chrome-devtools-frontend 1.0.1590494 → 1.0.1592129
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/ui_engineering.md +34 -1
- package/front_end/core/common/Gzip.ts +17 -2
- package/front_end/core/host/AidaClient.ts +38 -15
- package/front_end/core/host/DispatchHttpRequestClient.ts +7 -4
- package/front_end/core/host/InspectorFrontendHostAPI.ts +2 -0
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/root/ExperimentNames.ts +1 -0
- package/front_end/core/root/Runtime.ts +5 -0
- package/front_end/core/sdk/CSSMetadata.ts +18 -0
- package/front_end/core/sdk/EmulationModel.ts +6 -1
- package/front_end/core/sdk/NetworkManager.ts +49 -2
- package/front_end/core/sdk/NetworkRequest.ts +4 -0
- package/front_end/core/sdk/RuntimeModel.ts +2 -1
- package/front_end/core/sdk/sdk-meta.ts +26 -0
- package/front_end/entrypoints/inspector_main/RenderingOptions.ts +34 -5
- package/front_end/entrypoints/main/MainImpl.ts +8 -0
- package/front_end/generated/InspectorBackendCommands.ts +1 -0
- package/front_end/generated/protocol-mapping.d.ts +10 -0
- package/front_end/generated/protocol-proxy-api.d.ts +8 -0
- package/front_end/generated/protocol.ts +6 -2
- package/front_end/models/ai_assistance/AiConversation.ts +9 -0
- package/front_end/models/ai_assistance/AiHistoryStorage.ts +1 -0
- package/front_end/models/ai_assistance/agents/AiAgent.ts +6 -1
- package/front_end/models/ai_assistance/agents/BreakpointDebuggerAgent.ts +722 -0
- package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +1 -1
- package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +12 -12
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +7 -7
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +7 -7
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +1 -1
- package/front_end/models/greendev/Prototypes.ts +7 -1
- package/front_end/models/javascript_metadata/NativeFunctions.js +5 -1
- package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +3 -3
- package/front_end/models/trace/helpers/Network.ts +1 -1
- package/front_end/models/trace/insights/NetworkDependencyTree.ts +1 -1
- package/front_end/models/trace/insights/RenderBlocking.ts +5 -5
- package/front_end/models/trace/lantern/metrics/FirstContentfulPaint.ts +6 -6
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +63 -0
- package/front_end/panels/ai_assistance/README.md +14 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +1 -4
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +45 -27
- package/front_end/panels/ai_assistance/components/chatInput.css +0 -8
- package/front_end/panels/ai_assistance/components/chatMessage.css +6 -0
- package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +25 -46
- package/front_end/panels/common/AiCodeGenerationTeaser.ts +9 -4
- package/front_end/panels/console/ConsoleViewMessage.ts +100 -4
- package/front_end/panels/elements/ComputedStyleWidget.ts +46 -18
- package/front_end/panels/elements/ElementsPanel.ts +2 -2
- package/front_end/panels/elements/StyleEditorWidget.ts +10 -2
- package/front_end/panels/elements/StylePropertyTreeElement.ts +1 -1
- package/front_end/panels/elements/StylesContainer.ts +2 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +8 -0
- package/front_end/panels/network/NetworkDataGridNode.ts +1 -1
- package/front_end/panels/network/NetworkLogViewColumns.ts +1 -1
- package/front_end/panels/network/RequestPayloadView.ts +7 -6
- package/front_end/panels/network/RequestTimingView.ts +3 -3
- package/front_end/panels/search/SearchResultsPane.ts +2 -1
- package/front_end/panels/settings/SettingsScreen.ts +3 -2
- package/front_end/panels/settings/WorkspaceSettingsTab.ts +1 -1
- package/front_end/panels/sources/BreakpointsView.ts +1 -1
- package/front_end/panels/sources/CallStackSidebarPane.ts +1 -1
- package/front_end/panels/timeline/NetworkTrackAppender.ts +1 -1
- package/front_end/panels/timeline/components/IgnoreListSetting.ts +0 -1
- package/front_end/panels/timeline/components/NetworkRequestDetails.ts +4 -4
- package/front_end/panels/timeline/components/NetworkRequestTooltip.ts +2 -2
- package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +4 -4
- package/front_end/third_party/chromium/README.chromium +4 -2
- package/front_end/third_party/lighthouse/lighthouse-dt-bundle.js +5 -1
- package/front_end/third_party/lighthouse/locales/en-GB.json +2 -2
- package/front_end/third_party/lighthouse/locales/en-US.json +2 -2
- package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +1 -1
- package/front_end/ui/legacy/Treeoutline.ts +39 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +31 -8
- package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +10 -3
- package/front_end/ui/legacy/components/source_frame/XMLView.ts +20 -5
- package/front_end/ui/visual_logging/Debugging.ts +51 -34
- package/front_end/ui/visual_logging/KnownContextValues.ts +7 -1
- package/front_end/ui/visual_logging/LoggingEvents.ts +5 -2
- package/package.json +1 -1
|
@@ -171,7 +171,7 @@ addElementAnnotation and specify an annotation reason.
|
|
|
171
171
|
- CRITICAL: Each time you describe a network request as being problematic you MUST call the function
|
|
172
172
|
addNetworkRequestAnnotation and specify an annotation reason.
|
|
173
173
|
- CRITICAL: If you spot ANY of the following problems:
|
|
174
|
-
- Render
|
|
174
|
+
- Render-blocking elements/network requests.
|
|
175
175
|
- Significant long task (especially on main thread).
|
|
176
176
|
- Layout shifts (e.g. due to unsized images).
|
|
177
177
|
... then you MUST call addNetworkRequestAnnotation for ALL network requests and addaddElementAnnotation for all
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import * as AgentProject from './AgentProject.js';
|
|
6
6
|
import * as AiAgent from './agents/AiAgent.js';
|
|
7
|
+
import * as BreakpointDebuggerAgent from './agents/BreakpointDebuggerAgent.js';
|
|
7
8
|
import * as ContextSelectionAgent from './agents/ContextSelectionAgent.js';
|
|
8
9
|
import * as FileAgent from './agents/FileAgent.js';
|
|
9
10
|
import * as NetworkAgent from './agents/NetworkAgent.js';
|
|
@@ -39,6 +40,7 @@ export {
|
|
|
39
40
|
AiHistoryStorage,
|
|
40
41
|
AIQueries,
|
|
41
42
|
AiUtils,
|
|
43
|
+
BreakpointDebuggerAgent,
|
|
42
44
|
BuiltInAi,
|
|
43
45
|
ChangeManager,
|
|
44
46
|
ContextSelectionAgent,
|
package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt
CHANGED
|
@@ -25,7 +25,7 @@ Status code: 200
|
|
|
25
25
|
MIME Type: image/webp
|
|
26
26
|
Protocol: h2
|
|
27
27
|
Priority: High
|
|
28
|
-
Render
|
|
28
|
+
Render-blocking: No
|
|
29
29
|
From a service worker: No
|
|
30
30
|
Initiators (root request to the request that directly loaded this one): file:///Users/cjamcl/surge/misc/display-dpr/index.html
|
|
31
31
|
Response headers
|
|
@@ -86,7 +86,7 @@ Status code: 200
|
|
|
86
86
|
MIME Type: image/svg+xml
|
|
87
87
|
Protocol: unknown
|
|
88
88
|
Priority: VeryHigh
|
|
89
|
-
Render
|
|
89
|
+
Render-blocking: No
|
|
90
90
|
From a service worker: No
|
|
91
91
|
Initiators (root request to the request that directly loaded this one): none
|
|
92
92
|
|
|
@@ -130,15 +130,15 @@ We can break this time down into the 2 phases that combine to make the LCP time:
|
|
|
130
130
|
- https://web.dev/articles/optimize-lcp
|
|
131
131
|
=== end content
|
|
132
132
|
|
|
133
|
-
Title: PerformanceInsightFormatter Render
|
|
133
|
+
Title: PerformanceInsightFormatter Render-blocking requests tells the LLM if there are no render-blocking requests
|
|
134
134
|
Content:
|
|
135
|
-
## Insight Title: Render
|
|
135
|
+
## Insight Title: Render-blocking requests
|
|
136
136
|
|
|
137
137
|
## Insight Summary:
|
|
138
|
-
This insight identifies network requests that were render
|
|
138
|
+
This insight identifies network requests that were render-blocking. Render-blocking requests are impactful because they are deemed critical to the page and therefore the browser stops rendering the page until it has dealt with these resources. For this insight make sure you fully inspect the details of each render-blocking network request and prioritize your suggestions to the user based on the impact of each render-blocking request.
|
|
139
139
|
|
|
140
140
|
## Detailed analysis:
|
|
141
|
-
There are no network requests that are render
|
|
141
|
+
There are no network requests that are render-blocking.
|
|
142
142
|
|
|
143
143
|
## Estimated savings: FCP 0 ms, LCP 0 ms
|
|
144
144
|
|
|
@@ -148,15 +148,15 @@ There are no network requests that are render blocking.
|
|
|
148
148
|
- https://web.dev/articles/optimize-lcp
|
|
149
149
|
=== end content
|
|
150
150
|
|
|
151
|
-
Title: PerformanceInsightFormatter Render
|
|
151
|
+
Title: PerformanceInsightFormatter Render-blocking requests serializes the correct details
|
|
152
152
|
Content:
|
|
153
|
-
## Insight Title: Render
|
|
153
|
+
## Insight Title: Render-blocking requests
|
|
154
154
|
|
|
155
155
|
## Insight Summary:
|
|
156
|
-
This insight identifies network requests that were render
|
|
156
|
+
This insight identifies network requests that were render-blocking. Render-blocking requests are impactful because they are deemed critical to the page and therefore the browser stops rendering the page until it has dealt with these resources. For this insight make sure you fully inspect the details of each render-blocking network request and prioritize your suggestions to the user based on the impact of each render-blocking request.
|
|
157
157
|
|
|
158
158
|
## Detailed analysis:
|
|
159
|
-
Here is a list of the network requests that were render
|
|
159
|
+
Here is a list of the network requests that were render-blocking on this page and their duration:
|
|
160
160
|
|
|
161
161
|
|
|
162
162
|
Network requests data:
|
|
@@ -210,7 +210,7 @@ MIME Type: image/jpeg
|
|
|
210
210
|
Protocol: http/1.1
|
|
211
211
|
Initial priority: Low
|
|
212
212
|
Final priority: High
|
|
213
|
-
Render
|
|
213
|
+
Render-blocking: No
|
|
214
214
|
From a service worker: No
|
|
215
215
|
Initiators (root request to the request that directly loaded this one): http://localhost:8787/lcp-discovery-delay/index.html, http://localhost:8787/lcp-discovery-delay/app.js
|
|
216
216
|
Response headers
|
|
@@ -276,7 +276,7 @@ Status code: 200
|
|
|
276
276
|
MIME Type: text/html
|
|
277
277
|
Protocol: http/1.1
|
|
278
278
|
Priority: VeryHigh
|
|
279
|
-
Render
|
|
279
|
+
Render-blocking: No
|
|
280
280
|
From a service worker: No
|
|
281
281
|
Initiators (root request to the request that directly loaded this one): none
|
|
282
282
|
Response headers
|
|
@@ -182,8 +182,8 @@ export class PerformanceInsightFormatter {
|
|
|
182
182
|
return [{title: 'How do I optimize my network dependency tree?'}];
|
|
183
183
|
case 'RenderBlocking':
|
|
184
184
|
return [
|
|
185
|
-
{title: 'Show me the most impactful render
|
|
186
|
-
{title: 'How can I reduce the number of render
|
|
185
|
+
{title: 'Show me the most impactful render-blocking requests that I should focus on'},
|
|
186
|
+
{title: 'How can I reduce the number of render-blocking requests?'}
|
|
187
187
|
];
|
|
188
188
|
case 'SlowCSSSelector':
|
|
189
189
|
return [{title: 'How can I optimize my CSS to increase the performance of CSS selectors?'}];
|
|
@@ -769,18 +769,18 @@ ${requestSummary}`;
|
|
|
769
769
|
}
|
|
770
770
|
|
|
771
771
|
/**
|
|
772
|
-
* Create an AI prompt string out of the Render
|
|
773
|
-
* @param insight The Render
|
|
772
|
+
* Create an AI prompt string out of the Render-blocking Insight model to use with Ask AI.
|
|
773
|
+
* @param insight The Render-blocking Model to query.
|
|
774
774
|
* @returns a string formatted for sending to Ask AI.
|
|
775
775
|
*/
|
|
776
776
|
formatRenderBlockingInsight(insight: Trace.Insights.Models.RenderBlocking.RenderBlockingInsightModel): string {
|
|
777
777
|
const requestSummary = this.#traceFormatter.formatNetworkRequests(insight.renderBlockingRequests);
|
|
778
778
|
|
|
779
779
|
if (requestSummary.length === 0) {
|
|
780
|
-
return 'There are no network requests that are render
|
|
780
|
+
return 'There are no network requests that are render-blocking.';
|
|
781
781
|
}
|
|
782
782
|
|
|
783
|
-
return `Here is a list of the network requests that were render
|
|
783
|
+
return `Here is a list of the network requests that were render-blocking on this page and their duration:
|
|
784
784
|
|
|
785
785
|
${requestSummary}`;
|
|
786
786
|
}
|
|
@@ -1136,7 +1136,7 @@ It is important that all of these checks pass to minimize the delay between the
|
|
|
1136
1136
|
3. The maximum of 4 preconnects should be respected.
|
|
1137
1137
|
- Opportunities to add [preconnect] for a faster loading experience.`;
|
|
1138
1138
|
case 'RenderBlocking':
|
|
1139
|
-
return 'This insight identifies network requests that were render
|
|
1139
|
+
return 'This insight identifies network requests that were render-blocking. Render-blocking requests are impactful because they are deemed critical to the page and therefore the browser stops rendering the page until it has dealt with these resources. For this insight make sure you fully inspect the details of each render-blocking network request and prioritize your suggestions to the user based on the impact of each render-blocking request.';
|
|
1140
1140
|
case 'SlowCSSSelector':
|
|
1141
1141
|
return `This insight identifies CSS selectors that are slowing down your page's rendering performance.`;
|
|
1142
1142
|
case 'ThirdParties':
|
package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt
CHANGED
|
@@ -615,8 +615,8 @@ Available insights:
|
|
|
615
615
|
- insight name: RenderBlocking
|
|
616
616
|
description: Requests are blocking the page's initial render, which may delay LCP. [Deferring or inlining](https://developer.chrome.com/docs/performance/insights/render-blocking) can move these network requests out of the critical path.
|
|
617
617
|
relevant trace bounds: {min: 171608170438, max: 171608877165}
|
|
618
|
-
example question: Show me the most impactful render
|
|
619
|
-
example question: How can I reduce the number of render
|
|
618
|
+
example question: Show me the most impactful render-blocking requests that I should focus on
|
|
619
|
+
example question: How can I reduce the number of render-blocking requests?
|
|
620
620
|
|
|
621
621
|
## insight set id: NAVIGATION_1
|
|
622
622
|
|
|
@@ -639,8 +639,8 @@ Available insights:
|
|
|
639
639
|
- insight name: RenderBlocking
|
|
640
640
|
description: Requests are blocking the page's initial render, which may delay LCP. [Deferring or inlining](https://developer.chrome.com/docs/performance/insights/render-blocking) can move these network requests out of the critical path.
|
|
641
641
|
relevant trace bounds: {min: 171614330544, max: 171615043224}
|
|
642
|
-
example question: Show me the most impactful render
|
|
643
|
-
example question: How can I reduce the number of render
|
|
642
|
+
example question: Show me the most impactful render-blocking requests that I should focus on
|
|
643
|
+
example question: How can I reduce the number of render-blocking requests?
|
|
644
644
|
=== end content
|
|
645
645
|
|
|
646
646
|
Title: PerformanceTraceFormatter formatTraceSummary deals with CrUX manager errors
|
|
@@ -1064,7 +1064,7 @@ Status code: 200
|
|
|
1064
1064
|
MIME Type: text/html
|
|
1065
1065
|
Protocol: http/1.1
|
|
1066
1066
|
Priority: VeryHigh
|
|
1067
|
-
Render
|
|
1067
|
+
Render-blocking: No
|
|
1068
1068
|
From a service worker: No
|
|
1069
1069
|
Initiators (root request to the request that directly loaded this one): none
|
|
1070
1070
|
Response headers
|
|
@@ -1094,7 +1094,7 @@ Status code: 200
|
|
|
1094
1094
|
MIME Type: text/css
|
|
1095
1095
|
Protocol: unknown
|
|
1096
1096
|
Priority: VeryHigh
|
|
1097
|
-
Render
|
|
1097
|
+
Render-blocking: Yes
|
|
1098
1098
|
From a service worker: No
|
|
1099
1099
|
Initiators (root request to the request that directly loaded this one): https://chromedevtools.github.io/performance-stories/lcp-large-image/index.html
|
|
1100
1100
|
Response headers
|
|
@@ -1134,7 +1134,7 @@ Status code: 200
|
|
|
1134
1134
|
MIME Type: text/css
|
|
1135
1135
|
Protocol: unknown
|
|
1136
1136
|
Priority: VeryHigh
|
|
1137
|
-
Render
|
|
1137
|
+
Render-blocking: Yes
|
|
1138
1138
|
From a service worker: No
|
|
1139
1139
|
Initiators (root request to the request that directly loaded this one): https://chromedevtools.github.io/performance-stories/lcp-large-image/index.html
|
|
1140
1140
|
Response headers
|
|
@@ -675,7 +675,7 @@ Status code: ${statusCode}
|
|
|
675
675
|
MIME Type: ${mimeType}
|
|
676
676
|
Protocol: ${protocol}
|
|
677
677
|
${priorityLines.join('\n')}
|
|
678
|
-
Render
|
|
678
|
+
Render-blocking: ${renderBlocking ? 'Yes' : 'No'}
|
|
679
679
|
From a service worker: ${fromServiceWorker ? 'Yes' : 'No'}
|
|
680
680
|
Initiators (root request to the request that directly loaded this one): ${initiatorUrls.join(', ') || 'none'}
|
|
681
681
|
${NetworkRequestFormatter.formatHeaders('Response headers', responseHeaders ?? [], true)}`;
|
|
@@ -10,6 +10,7 @@ let instance: Prototypes|null = null;
|
|
|
10
10
|
export interface GreenDevSettings {
|
|
11
11
|
aiAnnotations: Common.Settings.Setting<boolean>;
|
|
12
12
|
copyToGemini: Common.Settings.Setting<boolean>;
|
|
13
|
+
breakpointDebuggerAgent: Common.Settings.Setting<boolean>;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export class Prototypes {
|
|
@@ -42,7 +43,12 @@ export class Prototypes {
|
|
|
42
43
|
);
|
|
43
44
|
const copyToGemini =
|
|
44
45
|
settings.createSetting('greendev-copy-to-gemini-enabled', false, Common.Settings.SettingStorageType.LOCAL);
|
|
46
|
+
const breakpointDebuggerAgent = settings.createSetting(
|
|
47
|
+
'greendev-breakpoint-debugger-agent-enabled',
|
|
48
|
+
false,
|
|
49
|
+
Common.Settings.SettingStorageType.LOCAL,
|
|
50
|
+
);
|
|
45
51
|
|
|
46
|
-
return {aiAnnotations, copyToGemini};
|
|
52
|
+
return {aiAnnotations, copyToGemini, breakpointDebuggerAgent};
|
|
47
53
|
}
|
|
48
54
|
}
|
|
@@ -6716,7 +6716,7 @@ export const NativeFunctions = [
|
|
|
6716
6716
|
signatures: [["?options"]]
|
|
6717
6717
|
},
|
|
6718
6718
|
{
|
|
6719
|
-
name: "
|
|
6719
|
+
name: "createValueRange",
|
|
6720
6720
|
signatures: [["start","end"]]
|
|
6721
6721
|
},
|
|
6722
6722
|
{
|
|
@@ -7021,6 +7021,10 @@ export const NativeFunctions = [
|
|
|
7021
7021
|
name: "Highlight",
|
|
7022
7022
|
signatures: [["...initRanges"]]
|
|
7023
7023
|
},
|
|
7024
|
+
{
|
|
7025
|
+
name: "CanvasPaintEvent",
|
|
7026
|
+
signatures: [["type","?eventInitDict"]]
|
|
7027
|
+
},
|
|
7024
7028
|
{
|
|
7025
7029
|
name: "getElementTransform",
|
|
7026
7030
|
signatures: [["element","draw_transform"]]
|
|
@@ -512,9 +512,9 @@ export async function finalize(): Promise<void> {
|
|
|
512
512
|
const requestingFrameUrl =
|
|
513
513
|
Helpers.Trace.activeURLForFrameAtTime(frame, finalSendRequest.ts, rendererProcessesByFrame) || '';
|
|
514
514
|
|
|
515
|
-
// A resource that is preloaded (and not marked as render
|
|
516
|
-
// become render
|
|
517
|
-
// this case, we take the render
|
|
515
|
+
// A resource that is preloaded (and not marked as render-blocking) can
|
|
516
|
+
// become render-blocked later via a PreloadRenderBlockingStatusChange. In
|
|
517
|
+
// this case, we take the render-blocking value of the last
|
|
518
518
|
// PreloadRenderBlockingStatusChange for this request.
|
|
519
519
|
const preloadRenderBlockingStatusChange =
|
|
520
520
|
request.preloadRenderBlockingStatusChange?.at(-1)?.args.data.renderBlocking;
|
|
@@ -12,7 +12,7 @@ import type {RenderBlocking, SyntheticNetworkRequest} from '../types/TraceEvents
|
|
|
12
12
|
// browser.
|
|
13
13
|
// 3. Therefore, from a developer's point of view, there is nothing more they
|
|
14
14
|
// can do if they've put `async` on, and within the context of Insights, we
|
|
15
|
-
// shouldn't report an async script as render
|
|
15
|
+
// shouldn't report an async script as render-blocking.
|
|
16
16
|
// In the future we may want to consider suggesting the use of `defer` over
|
|
17
17
|
// `async`, as it doesn't have this concern, but for now we'll allow `async`
|
|
18
18
|
// and not report it as an issue.
|
|
@@ -194,7 +194,7 @@ function isCritical(request: Types.Events.SyntheticNetworkRequest, context: Insi
|
|
|
194
194
|
return false;
|
|
195
195
|
}
|
|
196
196
|
|
|
197
|
-
// Iframes are considered High Priority but they are not render
|
|
197
|
+
// Iframes are considered High Priority but they are not render-blocking
|
|
198
198
|
const isIframe = request.args.data.resourceType === Protocol.Network.ResourceType.Document &&
|
|
199
199
|
request.args.data.frame !== context.frameId;
|
|
200
200
|
|
|
@@ -24,7 +24,7 @@ export const UIStrings = {
|
|
|
24
24
|
/**
|
|
25
25
|
* @description Title of an insight that provides the user with the list of network requests that blocked and therefore slowed down the page rendering and becoming visible to the user.
|
|
26
26
|
*/
|
|
27
|
-
title: 'Render
|
|
27
|
+
title: 'Render-blocking requests',
|
|
28
28
|
/**
|
|
29
29
|
* @description Text to describe that there are requests blocking rendering, which may affect LCP.
|
|
30
30
|
*/
|
|
@@ -42,7 +42,7 @@ export const UIStrings = {
|
|
|
42
42
|
/**
|
|
43
43
|
* @description Text status indicating that no requests blocked the initial render of a navigation
|
|
44
44
|
*/
|
|
45
|
-
noRenderBlocking: 'No render
|
|
45
|
+
noRenderBlocking: 'No render-blocking requests for this navigation',
|
|
46
46
|
} as const;
|
|
47
47
|
|
|
48
48
|
const str_ = i18n.i18n.registerUIStrings('models/trace/insights/RenderBlocking.ts', UIStrings);
|
|
@@ -154,7 +154,7 @@ function computeSavings(
|
|
|
154
154
|
if (requestIdToWastedMs.size) {
|
|
155
155
|
metricSavings.FCP = estimateSavingsWithGraphs(deferredNodeIds, context.lantern);
|
|
156
156
|
|
|
157
|
-
// In most cases, render
|
|
157
|
+
// In most cases, render-blocking resources only affect LCP if LCP isn't an image.
|
|
158
158
|
if (!hasImageLCP(data, context)) {
|
|
159
159
|
metricSavings.LCP = metricSavings.FCP;
|
|
160
160
|
}
|
|
@@ -209,12 +209,12 @@ export function generateInsight(
|
|
|
209
209
|
continue;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
// If a request is marked `in_body_parser_blocking` it should only be considered render
|
|
212
|
+
// If a request is marked `in_body_parser_blocking` it should only be considered render-blocking if it is a
|
|
213
213
|
// high enough priority. Some requests (e.g. scripts) are not marked as high priority if they are fetched
|
|
214
214
|
// after a non-preloaded image. (See "early" definition in https://web.dev/articles/fetch-priority)
|
|
215
215
|
//
|
|
216
216
|
// There are edge cases and exceptions (e.g. priority hints) but this gives us the best approximation
|
|
217
|
-
// of render
|
|
217
|
+
// of render-blocking resources in the document body.
|
|
218
218
|
if (req.args.data.renderBlocking === 'in_body_parser_blocking') {
|
|
219
219
|
const priority = req.args.data.priority;
|
|
220
220
|
const isScript = req.args.data.resourceType === Protocol.Network.ResourceType.Script;
|
|
@@ -51,8 +51,8 @@ class FirstContentfulPaint extends Metric {
|
|
|
51
51
|
const cpuNodes: Graph.CPUNode[] = [];
|
|
52
52
|
graph.traverse(node => {
|
|
53
53
|
if (node.type === Graph.BaseNode.types.CPU) {
|
|
54
|
-
// A task is *possibly* render
|
|
55
|
-
// We use startTime here because the paint event can be *inside* the task that was render
|
|
54
|
+
// A task is *possibly* render-blocking if it *started* before cutoffTimestamp.
|
|
55
|
+
// We use startTime here because the paint event can be *inside* the task that was render-blocking.
|
|
56
56
|
if (node.startTime <= cutoffTimestamp) {
|
|
57
57
|
cpuNodes.push(node);
|
|
58
58
|
}
|
|
@@ -69,7 +69,7 @@ class FirstContentfulPaint extends Metric {
|
|
|
69
69
|
|
|
70
70
|
cpuNodes.sort((a, b) => a.startTime - b.startTime);
|
|
71
71
|
|
|
72
|
-
// A script is *possibly* render
|
|
72
|
+
// A script is *possibly* render-blocking if it finished loading before cutoffTimestamp.
|
|
73
73
|
const possiblyRenderBlockingScriptUrls = Metric.getScriptUrls(graph, node => {
|
|
74
74
|
// The optimistic LCP treatNodeAsRenderBlocking fn wants to exclude some images in the graph,
|
|
75
75
|
// but here it only receives scripts to evaluate. It's a no-op in this case, but it will
|
|
@@ -77,7 +77,7 @@ class FirstContentfulPaint extends Metric {
|
|
|
77
77
|
return node.endTime <= cutoffTimestamp && treatNodeAsRenderBlocking(node);
|
|
78
78
|
});
|
|
79
79
|
|
|
80
|
-
// A script is *definitely not* render
|
|
80
|
+
// A script is *definitely not* render-blocking if its EvaluateScript task started after cutoffTimestamp.
|
|
81
81
|
const definitelyNotRenderBlockingScriptUrls = new Set<string>();
|
|
82
82
|
const renderBlockingCpuNodeIds = new Set<string>();
|
|
83
83
|
for (const url of possiblyRenderBlockingScriptUrls) {
|
|
@@ -156,7 +156,7 @@ class FirstContentfulPaint extends Metric {
|
|
|
156
156
|
return false;
|
|
157
157
|
}
|
|
158
158
|
|
|
159
|
-
// Lastly, build up the FCP graph of all nodes we consider render
|
|
159
|
+
// Lastly, build up the FCP graph of all nodes we consider render-blocking
|
|
160
160
|
return treatNodeAsRenderBlocking(node);
|
|
161
161
|
}
|
|
162
162
|
// If it's a CPU node, just check if it was blocking.
|
|
@@ -168,7 +168,7 @@ class FirstContentfulPaint extends Metric {
|
|
|
168
168
|
dependencyGraph: Graph.Node<T>, processedNavigation: Types.Simulation.ProcessedNavigation): Graph.Node<T> {
|
|
169
169
|
return this.getFirstPaintBasedGraph(dependencyGraph, {
|
|
170
170
|
cutoffTimestamp: processedNavigation.timestamps.firstContentfulPaint,
|
|
171
|
-
// In the optimistic graph we exclude resources that appeared to be render
|
|
171
|
+
// In the optimistic graph we exclude resources that appeared to be render-blocking but were
|
|
172
172
|
// initiated by a script. While they typically have a very high importance and tend to have a
|
|
173
173
|
// significant impact on the page's content, these resources don't technically block rendering.
|
|
174
174
|
treatNodeAsRenderBlocking: node => node.hasRenderBlockingPriority() && node.initiatorType !== 'script',
|
|
@@ -278,6 +278,13 @@ async function getEmptyStateSuggestions(conversation?: AiAssistanceModel.AiConve
|
|
|
278
278
|
{title: 'What performance issues exist with my page?', jslogContext: 'performance-default'},
|
|
279
279
|
];
|
|
280
280
|
}
|
|
281
|
+
case AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT: {
|
|
282
|
+
return [
|
|
283
|
+
{title: 'Why did the code pause here?'},
|
|
284
|
+
{title: 'What function does this breakpoint belong to?'},
|
|
285
|
+
{title: 'Why is this error thrown?'},
|
|
286
|
+
];
|
|
287
|
+
}
|
|
281
288
|
|
|
282
289
|
case AiAssistanceModel.AiHistoryStorage.ConversationType.NONE: {
|
|
283
290
|
return [
|
|
@@ -504,6 +511,14 @@ function createFileContext(file: Workspace.UISourceCode.UISourceCode|null): AiAs
|
|
|
504
511
|
return new AiAssistanceModel.FileAgent.FileContext(file);
|
|
505
512
|
}
|
|
506
513
|
|
|
514
|
+
function createBreakpointContext(uiLocation: Workspace.UISourceCode.UILocation|null):
|
|
515
|
+
AiAssistanceModel.BreakpointDebuggerAgent.BreakpointContext|null {
|
|
516
|
+
if (!uiLocation) {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
return new AiAssistanceModel.BreakpointDebuggerAgent.BreakpointContext(uiLocation);
|
|
520
|
+
}
|
|
521
|
+
|
|
507
522
|
function createRequestContext(request: SDK.NetworkRequest.NetworkRequest|null):
|
|
508
523
|
AiAssistanceModel.NetworkAgent.RequestContext|null {
|
|
509
524
|
if (!request) {
|
|
@@ -564,6 +579,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
564
579
|
#selectedElement: AiAssistanceModel.StylingAgent.NodeContext|null = null;
|
|
565
580
|
#selectedPerformanceTrace: AiAssistanceModel.PerformanceAgent.PerformanceTraceContext|null = null;
|
|
566
581
|
#selectedRequest: AiAssistanceModel.NetworkAgent.RequestContext|null = null;
|
|
582
|
+
#selectedBreakpoint: AiAssistanceModel.BreakpointDebuggerAgent.BreakpointContext|null = null;
|
|
567
583
|
// Messages displayed in the `ChatView` component.
|
|
568
584
|
#messages: Message[] = [];
|
|
569
585
|
|
|
@@ -804,6 +820,10 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
804
820
|
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING;
|
|
805
821
|
} else if (isNetworkPanelVisible && hostConfig.devToolsAiAssistanceNetworkAgent?.enabled) {
|
|
806
822
|
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.NETWORK;
|
|
823
|
+
} else if (
|
|
824
|
+
isSourcesPanelVisible &&
|
|
825
|
+
this.#conversation?.type === AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT) {
|
|
826
|
+
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT;
|
|
807
827
|
} else if (isSourcesPanelVisible && hostConfig.devToolsAiAssistanceFileAgent?.enabled) {
|
|
808
828
|
targetConversationType = AiAssistanceModel.AiHistoryStorage.ConversationType.FILE;
|
|
809
829
|
} else if (isPerformancePanelVisible && hostConfig.devToolsAiAssistancePerformanceAgent?.enabled) {
|
|
@@ -895,6 +915,27 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
895
915
|
this.requestUpdate();
|
|
896
916
|
}
|
|
897
917
|
|
|
918
|
+
async handleBreakpointConversation(uiLocation: Workspace.UISourceCode.UILocation): Promise<void> {
|
|
919
|
+
const context = new AiAssistanceModel.BreakpointDebuggerAgent.BreakpointContext(uiLocation);
|
|
920
|
+
this.#selectedBreakpoint = context;
|
|
921
|
+
const conversation = new AiAssistanceModel.AiConversation.AiConversation(
|
|
922
|
+
AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT,
|
|
923
|
+
[],
|
|
924
|
+
undefined,
|
|
925
|
+
false,
|
|
926
|
+
this.#aidaClient,
|
|
927
|
+
this.#changeManager,
|
|
928
|
+
false,
|
|
929
|
+
this.#handlePerformanceRecordAndReload.bind(this),
|
|
930
|
+
this.#handleInspectElement.bind(this),
|
|
931
|
+
NetworkPanel.NetworkPanel.NetworkPanel.instance().networkLogView.timeCalculator(),
|
|
932
|
+
);
|
|
933
|
+
this.#updateConversationState(conversation);
|
|
934
|
+
this.#conversation?.setContext(context);
|
|
935
|
+
this.requestUpdate();
|
|
936
|
+
await UI.ViewManager.ViewManager.instance().showView(AiAssistancePanel.panelName);
|
|
937
|
+
}
|
|
938
|
+
|
|
898
939
|
override wasShown(): void {
|
|
899
940
|
super.wasShown();
|
|
900
941
|
this.#viewOutput.chatView?.restoreScrollPosition();
|
|
@@ -907,6 +948,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
907
948
|
this.#selectedPerformanceTrace =
|
|
908
949
|
createPerformanceTraceContext(UI.Context.Context.instance().flavor(AiAssistanceModel.AIContext.AgentFocus));
|
|
909
950
|
this.#selectedFile = createFileContext(UI.Context.Context.instance().flavor(Workspace.UISourceCode.UISourceCode));
|
|
951
|
+
this.#selectedBreakpoint =
|
|
952
|
+
createBreakpointContext(UI.Context.Context.instance().flavor(Workspace.UISourceCode.UILocation));
|
|
910
953
|
this.#updateConversationState(this.#conversation);
|
|
911
954
|
|
|
912
955
|
this.#aiAssistanceEnabledSetting?.addChangeListener(this.requestUpdate, this);
|
|
@@ -921,6 +964,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
921
964
|
AiAssistanceModel.AIContext.AgentFocus, this.#handlePerformanceTraceFlavorChange);
|
|
922
965
|
UI.Context.Context.instance().addFlavorChangeListener(
|
|
923
966
|
Workspace.UISourceCode.UISourceCode, this.#handleUISourceCodeFlavorChange);
|
|
967
|
+
UI.Context.Context.instance().addFlavorChangeListener(
|
|
968
|
+
Workspace.UISourceCode.UILocation, this.#handleBreakpointFlavorChange);
|
|
924
969
|
|
|
925
970
|
UI.ViewManager.ViewManager.instance().addEventListener(
|
|
926
971
|
UI.ViewManager.Events.VIEW_VISIBILITY_CHANGED, this.#selectDefaultAgentIfNeeded, this);
|
|
@@ -1050,6 +1095,17 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1050
1095
|
this.#updateConversationState(this.#conversation);
|
|
1051
1096
|
};
|
|
1052
1097
|
|
|
1098
|
+
#handleBreakpointFlavorChange =
|
|
1099
|
+
(ev: Common.EventTarget.EventTargetEvent<Workspace.UISourceCode.UILocation>): void => {
|
|
1100
|
+
const newBreakpoint = ev.data;
|
|
1101
|
+
|
|
1102
|
+
if (!newBreakpoint || this.#selectedBreakpoint?.getItem() === newBreakpoint) {
|
|
1103
|
+
return;
|
|
1104
|
+
}
|
|
1105
|
+
this.#selectedBreakpoint = new AiAssistanceModel.BreakpointDebuggerAgent.BreakpointContext(newBreakpoint);
|
|
1106
|
+
this.#updateConversationState(this.#conversation);
|
|
1107
|
+
};
|
|
1108
|
+
|
|
1053
1109
|
#getChangeSummary(): string|undefined {
|
|
1054
1110
|
if (!isAiAssistancePatchingEnabled() || !this.#conversation || this.#conversation?.isReadOnly) {
|
|
1055
1111
|
return;
|
|
@@ -1146,6 +1202,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1146
1202
|
|
|
1147
1203
|
return lockedString(UIStringsNotTranslate.inputPlaceholderForPerformanceWithNoRecording);
|
|
1148
1204
|
}
|
|
1205
|
+
case AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT:
|
|
1206
|
+
return lockedString(UIStringsNotTranslate.inputPlaceholderForNoContext);
|
|
1149
1207
|
case AiAssistanceModel.AiHistoryStorage.ConversationType.NONE:
|
|
1150
1208
|
if (AiAssistanceModel.AiUtils.isGeminiBranding()) {
|
|
1151
1209
|
return lockedString(UIStringsNotTranslate.inputPlaceholderForNoContextBranded);
|
|
@@ -1186,6 +1244,7 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1186
1244
|
}
|
|
1187
1245
|
return lockedString(UIStringsNotTranslate.inputDisclaimerForPerformance);
|
|
1188
1246
|
|
|
1247
|
+
case AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT:
|
|
1189
1248
|
case AiAssistanceModel.AiHistoryStorage.ConversationType.NONE:
|
|
1190
1249
|
if (noLogging) {
|
|
1191
1250
|
return lockedString(UIStringsNotTranslate.inputDisclaimerForNoContextEnterpriseNoLogging);
|
|
@@ -1441,6 +1500,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1441
1500
|
return this.#selectedRequest;
|
|
1442
1501
|
case AiAssistanceModel.AiHistoryStorage.ConversationType.PERFORMANCE:
|
|
1443
1502
|
return this.#selectedPerformanceTrace;
|
|
1503
|
+
case AiAssistanceModel.AiHistoryStorage.ConversationType.BREAKPOINT:
|
|
1504
|
+
return this.#selectedBreakpoint;
|
|
1444
1505
|
case AiAssistanceModel.AiHistoryStorage.ConversationType.NONE:
|
|
1445
1506
|
case undefined:
|
|
1446
1507
|
return null;
|
|
@@ -1456,6 +1517,8 @@ export class AiAssistancePanel extends UI.Panel.Panel {
|
|
|
1456
1517
|
this.#selectedRequest = data;
|
|
1457
1518
|
} else if (data instanceof AiAssistanceModel.PerformanceAgent.PerformanceTraceContext) {
|
|
1458
1519
|
this.#selectedPerformanceTrace = data;
|
|
1520
|
+
} else if (data instanceof AiAssistanceModel.BreakpointDebuggerAgent.BreakpointContext) {
|
|
1521
|
+
this.#selectedBreakpoint = data;
|
|
1459
1522
|
}
|
|
1460
1523
|
|
|
1461
1524
|
void VisualLogging.logFunctionCall(`context-change-${this.#conversation?.type}`);
|
|
@@ -69,3 +69,17 @@ The key to the walkthrough feature lies in the `parts` array of a `ModelChatMess
|
|
|
69
69
|
- `text`: The markdown-formatted text of the answer.
|
|
70
70
|
|
|
71
71
|
A single `ModelChatMessage` will typically have **zero or more `StepPart`s** followed by **one `AnswerPart`**. This structure allows the UI to first show the final answer and provide the option to progressively disclose the "thinking" steps that led to it via the `WalkthroughView`.
|
|
72
|
+
|
|
73
|
+
## Understanding Step Loading States
|
|
74
|
+
|
|
75
|
+
There are two distinct `isLoading` properties related to the AI's response generation, serving different purposes:
|
|
76
|
+
|
|
77
|
+
1. **`step.isLoading`**: This property resides within a single `Step` object and indicates the immediate parsing status of that specific step from the AI's response stream. It becomes `true` when a new step begins (e.g., "Querying...") and is set to `false` almost immediately once the first piece of meaningful information for that step (like a "thought" or a "code" block) is received. Essentially, it's a short-lived internal parsing state.
|
|
78
|
+
|
|
79
|
+
2. **Conversation `isLoading`** (passed down to the `ChatMessage` component): This top-level property reflects the overall state of the entire conversation's response generation process. It is set to `true` as soon as the user submits a query and remains `true` until the AI has completed its entire thought process, including all steps, and has provided the final answer.
|
|
80
|
+
|
|
81
|
+
The spinner displayed next to a step in the UI is intentionally tied to the **conversation `isLoading`** property, not the individual `step.isLoading`. This design choice provides a better user experience by:
|
|
82
|
+
|
|
83
|
+
* **Providing Continuous Feedback**: Using the conversation's `isLoading` ensures a persistent visual indicator that the AI is actively working on the query, even if individual steps are quickly parsed. This prevents the UI from appearing unresponsive.
|
|
84
|
+
* **Avoiding a "Stuck" Feeling**: If the spinner were tied to `step.isLoading`, it would flicker on and off rapidly, potentially making the user feel that the AI has stopped processing or is stuck.
|
|
85
|
+
* **Clear Progress Visualization**: The spinner is dynamically moved to the *last* active step in the list as long as the overall conversation is loading. Once a step is completed, it receives a checkmark, and the spinner moves to the next active step.
|
|
@@ -321,10 +321,7 @@ export const DEFAULT_VIEW = (input: ViewInput, _output: ViewOutput, target: HTML
|
|
|
321
321
|
></devtools-button>`
|
|
322
322
|
: Lit.nothing}
|
|
323
323
|
<div
|
|
324
|
-
class
|
|
325
|
-
'resource-link': true,
|
|
326
|
-
'has-picker-behavior': input.conversationType === AiAssistanceModel.AiHistoryStorage.ConversationType.STYLING,
|
|
327
|
-
})}
|
|
324
|
+
class="resource-link"
|
|
328
325
|
>
|
|
329
326
|
${
|
|
330
327
|
input.selectedContext instanceof AiAssistanceModel.StylingAgent.NodeContext ?
|