chrome-devtools-frontend 1.0.1690116 → 1.0.1690531

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.
Files changed (146) hide show
  1. package/.gitmodules +4 -0
  2. package/front_end/core/root/Runtime.ts +1 -0
  3. package/front_end/core/sdk/NetworkRequest.ts +50 -4
  4. package/front_end/core/sdk/NetworkRequestAccess.ts +172 -0
  5. package/front_end/core/sdk/SecurityOrigin.ts +25 -0
  6. package/front_end/core/sdk/sdk.ts +2 -0
  7. package/front_end/generated/SupportedCSSProperties.ts +1 -1
  8. package/front_end/models/ai_assistance/AiAgent2.ts +4 -1
  9. package/front_end/models/ai_assistance/AiConversation.ts +29 -18
  10. package/front_end/panels/ai_assistance/components/chatMessage.css +3 -1
  11. package/front_end/panels/application/components/BackForwardCacheView.ts +1 -1
  12. package/front_end/panels/application/components/EndpointsGrid.ts +2 -1
  13. package/front_end/panels/application/components/PermissionsPolicySection.ts +1 -1
  14. package/front_end/panels/application/components/ServiceWorkerRouterView.ts +1 -1
  15. package/front_end/panels/application/components/TrustTokensView.ts +1 -1
  16. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +2 -1
  17. package/front_end/panels/application/preloading/components/RuleSetDetailsView.ts +2 -1
  18. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +1 -1
  19. package/front_end/panels/common/CPUThrottlingOption.ts +12 -6
  20. package/front_end/panels/common/DOMLinkifier.ts +1 -1
  21. package/front_end/panels/common/ExtensionView.ts +2 -1
  22. package/front_end/panels/console/ConsolePinPane.ts +4 -2
  23. package/front_end/panels/console/ConsoleSidebar.ts +6 -2
  24. package/front_end/panels/console/PromptBuilder.ts +1 -1
  25. package/front_end/panels/coverage/CoverageListView.ts +1 -1
  26. package/front_end/panels/coverage/CoverageModel.ts +6 -4
  27. package/front_end/panels/css_overview/CSSOverviewCompletedView.ts +9 -9
  28. package/front_end/panels/css_overview/CSSOverviewPanel.ts +3 -3
  29. package/front_end/panels/css_overview/CSSOverviewProcessingView.ts +2 -2
  30. package/front_end/panels/css_overview/CSSOverviewSidebarPanel.ts +3 -3
  31. package/front_end/panels/css_overview/CSSOverviewStartView.ts +3 -3
  32. package/front_end/panels/developer_resources/DeveloperResourcesListView.ts +1 -1
  33. package/front_end/panels/elements/ComputedStyleWidget.ts +1 -1
  34. package/front_end/panels/elements/ElementsTreeElement.ts +4 -3
  35. package/front_end/panels/elements/ElementsTreeOutline.ts +2 -2
  36. package/front_end/panels/elements/MetricsSidebarPane.ts +1 -1
  37. package/front_end/panels/elements/PropertyNameCategories.ts +1 -1
  38. package/front_end/panels/elements/ShortcutTreeElement.ts +2 -1
  39. package/front_end/panels/elements/StandaloneStylesContainer.ts +1 -1
  40. package/front_end/panels/elements/StylePropertiesSection.ts +1 -1
  41. package/front_end/panels/elements/StylesSidebarPane.ts +3 -2
  42. package/front_end/panels/elements/components/StylePropertyEditor.ts +160 -148
  43. package/front_end/panels/emulation/MediaQueryInspector.ts +2 -1
  44. package/front_end/panels/issues/AffectedLazyLoadImagesView.ts +1 -1
  45. package/front_end/panels/linear_memory_inspector/components/linearMemoryInspector.css +2 -2
  46. package/front_end/panels/linear_memory_inspector/components/linearMemoryNavigator.css +1 -1
  47. package/front_end/panels/linear_memory_inspector/components/linearMemoryValueInterpreter.css +5 -5
  48. package/front_end/panels/linear_memory_inspector/components/linearMemoryViewer.css +6 -6
  49. package/front_end/panels/linear_memory_inspector/components/valueInterpreterDisplay.css +6 -6
  50. package/front_end/panels/linear_memory_inspector/components/valueInterpreterSettings.css +2 -2
  51. package/front_end/panels/network/NetworkDataGridNode.ts +11 -9
  52. package/front_end/panels/network/NetworkLogView.ts +4 -1
  53. package/front_end/panels/network/RequestConditionsDrawer.ts +2 -2
  54. package/front_end/panels/network/RequestHeadersView.ts +1 -1
  55. package/front_end/panels/network/RequestPayloadView.ts +1 -1
  56. package/front_end/panels/network/RequestResponseView.ts +1 -1
  57. package/front_end/panels/network/RequestTimingView.ts +1 -1
  58. package/front_end/panels/network/ShowMoreDetailsWidget.ts +1 -1
  59. package/front_end/panels/network/components/ResponseHeaderSection.ts +1 -1
  60. package/front_end/panels/sources/BreakpointEditDialog.ts +1 -1
  61. package/front_end/panels/sources/CallStackSidebarPane.ts +1 -1
  62. package/front_end/panels/sources/DebuggerPausedMessage.ts +8 -6
  63. package/front_end/panels/sources/ProfilePlugin.ts +2 -1
  64. package/front_end/panels/sources/ScopeChainSidebarPane.ts +1 -1
  65. package/front_end/panels/timeline/IsolateSelector.ts +1 -1
  66. package/front_end/panels/timeline/LayoutShiftsTrackAppender.ts +1 -1
  67. package/front_end/panels/timeline/NetworkTrackAppender.ts +1 -1
  68. package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +1 -1
  69. package/front_end/panels/timeline/TimelineHistoryManager.ts +1 -1
  70. package/front_end/panels/timeline/TimelinePaintProfilerView.ts +7 -3
  71. package/front_end/panels/timeline/TimelinePanel.ts +3 -3
  72. package/front_end/panels/timeline/TimelineTreeView.ts +1 -1
  73. package/front_end/panels/timeline/TimelineUIUtils.ts +4 -4
  74. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +4 -3
  75. package/front_end/panels/timeline/components/LiveMetricsView.ts +1 -1
  76. package/front_end/panels/timeline/components/NetworkRequestDetails.ts +25 -24
  77. package/front_end/panels/timeline/components/RelatedInsightChips.ts +4 -3
  78. package/front_end/panels/timeline/components/Sidebar.ts +1 -1
  79. package/front_end/panels/timeline/components/SidebarAnnotationsTab.ts +4 -3
  80. package/front_end/panels/timeline/utils/Helpers.ts +1 -1
  81. package/front_end/panels/timeline/utils/ImageCache.ts +5 -3
  82. package/front_end/panels/web_audio/WebAudioView.ts +3 -3
  83. package/front_end/panels/webauthn/WebauthnPane.ts +5 -11
  84. package/front_end/third_party/acorn/acorn.ts +3 -3
  85. package/front_end/third_party/chromium/README.chromium +1 -1
  86. package/front_end/third_party/chromium/ahem/ahem.d.ts +1 -0
  87. package/front_end/third_party/csp_evaluator/package/csp.ts +4 -4
  88. package/front_end/third_party/csp_evaluator/package/parser.ts +3 -1
  89. package/front_end/third_party/csp_evaluator/package/utils.ts +1 -1
  90. package/front_end/third_party/diff/diff_match_patch.d.ts +10 -0
  91. package/front_end/third_party/json5/json5.ts +2 -2
  92. package/front_end/ui/components/buttons/floatingButton.css +25 -0
  93. package/front_end/ui/components/srgb_overlay/SrgbOverlay.ts +1 -1
  94. package/front_end/ui/components/suggestion_input/SuggestionInput.ts +45 -25
  95. package/front_end/ui/legacy/ContextMenu.ts +2 -2
  96. package/front_end/ui/legacy/DropTarget.ts +6 -1
  97. package/front_end/ui/legacy/EmptyWidget.ts +1 -1
  98. package/front_end/ui/legacy/GlassPane.ts +1 -1
  99. package/front_end/ui/legacy/ShortcutRegistry.ts +2 -2
  100. package/front_end/ui/legacy/SplitWidget.ts +2 -1
  101. package/front_end/ui/legacy/StackedPane.ts +1 -1
  102. package/front_end/ui/legacy/TargetCrashedScreen.ts +1 -1
  103. package/front_end/ui/legacy/TextPrompt.ts +2 -1
  104. package/front_end/ui/legacy/Toolbar.ts +1 -1
  105. package/front_end/ui/legacy/Treeoutline.ts +13 -37
  106. package/front_end/ui/legacy/UIUtils.ts +6 -3
  107. package/front_end/ui/legacy/ViewManager.ts +1 -1
  108. package/front_end/ui/legacy/Widget.ts +1 -1
  109. package/front_end/ui/legacy/components/color_picker/Spectrum.ts +7 -1
  110. package/front_end/ui/legacy/components/data_grid/DataGrid.ts +1 -1
  111. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +3 -2
  112. package/front_end/ui/legacy/components/inline_editor/AnimationTimingModel.ts +1 -1
  113. package/front_end/ui/legacy/components/inline_editor/BezierEditor.ts +48 -44
  114. package/front_end/ui/legacy/components/inline_editor/CSSAngleUtils.ts +1 -1
  115. package/front_end/ui/legacy/components/object_ui/CustomPreviewComponent.ts +141 -106
  116. package/front_end/ui/legacy/components/object_ui/ObjectPopoverHelper.ts +1 -1
  117. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +10 -10
  118. package/front_end/ui/legacy/components/perf_ui/pieChart.css +7 -5
  119. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +8 -5
  120. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +1 -1
  121. package/front_end/ui/legacy/components/utils/Linkifier.ts +5 -5
  122. package/front_end/ui/legacy/inspectorCommon.css +11 -0
  123. package/front_end/ui/visual_logging/Debugging.ts +2 -1
  124. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -1
  125. package/front_end/ui/visual_logging/LoggingDriver.ts +3 -3
  126. package/front_end/ui/visual_logging/LoggingEvents.ts +5 -5
  127. package/front_end/ui/visual_logging/LoggingState.ts +1 -1
  128. package/front_end/ui/visual_logging/visual_logging.ts +67 -56
  129. package/package.json +2 -1
  130. package/front_end/third_party/acorn/acorn-tsconfig.json +0 -12
  131. package/front_end/third_party/acorn/estree-tsconfig.json +0 -8
  132. package/front_end/third_party/chromium/client-variations/client-variations-tsconfig.json +0 -8
  133. package/front_end/third_party/codemirror/codemirror-legacy-types-tsconfig.json +0 -8
  134. package/front_end/third_party/codemirror/codemirror-tsconfig.json +0 -15
  135. package/front_end/third_party/codemirror.next/bundle-tsconfig.json +0 -24
  136. package/front_end/third_party/intl-messageformat/intl-messageformat-tsconfig.json +0 -16
  137. package/front_end/third_party/json5/json5-tsconfig.json +0 -8
  138. package/front_end/third_party/legacy-javascript/legacy-javascript-tsconfig.json +0 -8
  139. package/front_end/third_party/lighthouse/lighthouse-tsconfig.json +0 -11
  140. package/front_end/third_party/lighthouse/report/bundle-tsconfig.json +0 -8
  141. package/front_end/third_party/lit/lit-tsconfig.json +0 -15
  142. package/front_end/third_party/marked/marked-tsconfig.json +0 -8
  143. package/front_end/third_party/puppeteer/puppeteer-tsconfig.json +0 -191
  144. package/front_end/third_party/third-party-web/third-party-web-tsconfig.json +0 -8
  145. package/front_end/third_party/wasmparser/wasmparser-tsconfig.json +0 -9
  146. package/front_end/third_party/web-vitals/web-vitals-tsconfig.json +0 -47
package/.gitmodules CHANGED
@@ -1,3 +1,7 @@
1
+ [submodule "test/ai_evals/eval_data"]
2
+ path = test/ai_evals/eval_data
3
+ url = https://chrome-internal.googlesource.com/devtools/chrome-devtools-evals
4
+ gclient-condition = checkout_ai_evals == True
1
5
  [submodule "third_party/clang-format/script"]
2
6
  path = third_party/clang-format/script
3
7
  url = https://chromium.googlesource.com/external/github.com/llvm/llvm-project/clang/tools/clang-format
@@ -515,6 +515,7 @@ interface UseGcaApi {
515
515
 
516
516
  interface DevToolsAiV2Architecture {
517
517
  enabled: boolean;
518
+ userTier?: string;
518
519
  }
519
520
 
520
521
  interface DevToolsProtocolMonitor {
@@ -16,6 +16,7 @@ import {
16
16
  Events as NetworkManagerEvents,
17
17
  NetworkManager,
18
18
  } from './NetworkManager.js';
19
+ import {SecurityOrigin} from './SecurityOrigin.js';
19
20
  import {ServerSentEvents} from './ServerSentEvents.js';
20
21
  import {ServerTiming} from './ServerTiming.js';
21
22
  import {Type} from './Target.js';
@@ -388,6 +389,55 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
388
389
  return this.#backendRequestId;
389
390
  }
390
391
 
392
+ /**
393
+ * Returns the security origin of the target resource URL (`request.url()`) for this network request.
394
+ *
395
+ * For example, if an inspected document at `https://example.com/index.html` initiates a fetch
396
+ * to `https://api.github.com/users`, `requestURLSecurityOrigin()` returns the origin for
397
+ * `https://api.github.com`.
398
+ *
399
+ * For imported HAR files, the origin is mapped to an isolated virtual domain
400
+ * (`imported-har://${authority}`) to ensure recorded network traffic never collides with
401
+ * live web origins.
402
+ *
403
+ * @see {@link initiatorSecurityOrigin} to obtain the origin of the document that initiated the request.
404
+ */
405
+ requestURLSecurityOrigin(): SecurityOrigin {
406
+ return this.#resolveSecurityOrigin(this.#url);
407
+ }
408
+
409
+ /**
410
+ * Returns the security origin of the document or context (`request.documentURL`) that initiated
411
+ * this network request.
412
+ *
413
+ * For example, if an inspected document at `https://example.com/index.html` initiates a fetch
414
+ * to `https://api.github.com/users`, `initiatorSecurityOrigin()` returns the origin for
415
+ * `https://example.com`.
416
+ *
417
+ * If `documentURL` is empty, invalid, or an opaque context (such as a `data:` URL or sandboxed
418
+ * iframe), this returns a unique opaque `SecurityOrigin`.
419
+ *
420
+ * For imported HAR files, the origin is mapped to an isolated virtual domain
421
+ * (`imported-har://${authority}`) matching the imported initiating document.
422
+ *
423
+ * @see {@link requestURLSecurityOrigin} to obtain the origin of the target resource URL being requested.
424
+ */
425
+ initiatorSecurityOrigin(): SecurityOrigin {
426
+ return this.#resolveSecurityOrigin(this.#documentURL);
427
+ }
428
+
429
+ #resolveSecurityOrigin(url: Platform.DevToolsPath.UrlString): SecurityOrigin {
430
+ if (this.#isImportedHar) {
431
+ const parsed = Common.ParsedURL.ParsedURL.fromString(url);
432
+ if (!parsed || !parsed.host) {
433
+ return SecurityOrigin.createUniqueOpaque();
434
+ }
435
+ const authority = parsed.host + (parsed.port ? ':' + parsed.port : '');
436
+ return SecurityOrigin.create(`imported-har://${authority}`);
437
+ }
438
+ return SecurityOrigin.create(url);
439
+ }
440
+
391
441
  url(): Platform.DevToolsPath.UrlString {
392
442
  return this.#url;
393
443
  }
@@ -486,10 +536,6 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
486
536
  return this.#securityDetails;
487
537
  }
488
538
 
489
- securityOrigin(): string {
490
- return this.#parsedURL.securityOrigin();
491
- }
492
-
493
539
  setSecurityDetails(securityDetails: Protocol.Network.SecurityDetails): void {
494
540
  this.#securityDetails = securityDetails;
495
541
  }
@@ -0,0 +1,172 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import type * as NetworkRequest from './NetworkRequest.js';
6
+ import * as SecurityOrigin from './SecurityOrigin.js';
7
+
8
+ /**
9
+ * Specifies how a network request's response data (body and headers) can be accessed
10
+ * relative to the initiating context's security origin.
11
+ *
12
+ * DevTools features that inspect network requests (such as AI Assistance) must respect
13
+ * the browser Same-Origin Policy (SOP) and Cross-Origin Resource Sharing (CORS) rules:
14
+ * - Same-origin responses are fully readable by the initiating document.
15
+ * - Cross-origin responses with valid CORS headers (`Access-Control-Allow-Origin`) are readable.
16
+ * - Opaque cross-origin responses (such as `no-cors` fetches to third-party endpoints) are NOT
17
+ * readable by the page and must never have their response bodies or restricted headers exposed
18
+ * to unauthorized contexts (b/513821237).
19
+ */
20
+ export const enum ResponseAccessMode {
21
+ /** The request target URL is same-origin with the initiator context. */
22
+ SAME_ORIGIN = 'SAME_ORIGIN',
23
+ /** The request is cross-origin, but explicit CORS response headers permit access by the initiator. */
24
+ CORS_ALLOWED = 'CORS_ALLOWED',
25
+ /** The request is cross-origin without CORS authorization; response body and unexposed headers are restricted. */
26
+ OPAQUE_CROSS_ORIGIN = 'OPAQUE_CROSS_ORIGIN',
27
+ }
28
+
29
+ /**
30
+ * Standard CORS-safelisted response headers that are exposed on cross-origin requests
31
+ * without requiring explicit `Access-Control-Expose-Headers`.
32
+ *
33
+ * @see https://fetch.spec.whatwg.org/#cors-safelisted-response-header-name
34
+ */
35
+ export const CORS_SAFELISTED_RESPONSE_HEADERS: ReadonlySet<string> = new Set([
36
+ 'cache-control',
37
+ 'content-language',
38
+ 'content-length',
39
+ 'content-type',
40
+ 'expires',
41
+ 'last-modified',
42
+ 'pragma',
43
+ ]);
44
+
45
+ /**
46
+ * Placeholder text replacing response body content when cross-origin access is forbidden.
47
+ */
48
+ export const REDACTED_RESPONSE_BODY = '<redacted cross-origin response body>';
49
+
50
+ /**
51
+ * Returns whether a network request used credentials (cookies, authorization headers,
52
+ * or server-indicated credentials mode).
53
+ *
54
+ * @param request The network request to inspect.
55
+ * @returns True if the request included credentials or requires credentialed CORS.
56
+ * @see https://fetch.spec.whatwg.org/#credentials
57
+ */
58
+ export function isRequestCredentialed(request: NetworkRequest.NetworkRequest): boolean {
59
+ const hasAuthHeaders = Boolean(
60
+ request.requestHeaderValue('authorization') || request.requestHeaderValue('proxy-authorization'),
61
+ );
62
+ const hasCookies = request.includedRequestCookies().length > 0 || request.responseCookies.length > 0 ||
63
+ Boolean(request.requestHeaderValue('cookie')) || Boolean(request.responseHeaderValue('set-cookie'));
64
+ const hasAllowCredentials =
65
+ request.responseHeaderValue('access-control-allow-credentials')?.trim().toLowerCase() === 'true';
66
+
67
+ return hasAuthHeaders || hasCookies || hasAllowCredentials;
68
+ }
69
+
70
+ /**
71
+ * Evaluates the response access mode for a network request relative to an initiator origin.
72
+ *
73
+ * Evaluation rules:
74
+ * 1. If `initiatorOrigin` is omitted, the origin is derived from `request.documentURL`. If
75
+ * `documentURL` is invalid or opaque, access mode defaults to `OPAQUE_CROSS_ORIGIN`.
76
+ * 2. If the initiator origin is opaque (e.g. sandboxed iframe or `data:` URL), returns `OPAQUE_CROSS_ORIGIN`.
77
+ * 3. If the initiator origin is same-origin with the request URL, returns `SAME_ORIGIN`.
78
+ * 4. If Chrome's network stack flagged a CORS error (`corsErrorStatus`), returns `OPAQUE_CROSS_ORIGIN`.
79
+ * 5. If the server provided an `Access-Control-Allow-Origin` header:
80
+ * - Wildcard `*` grants `CORS_ALLOWED` only if the request does not include credentials. Under the
81
+ * Fetch specification, wildcard `*` is invalid for credentialed requests.
82
+ * - An explicit match against the initiator origin grants `CORS_ALLOWED`.
83
+ * 6. Otherwise, returns `OPAQUE_CROSS_ORIGIN`.
84
+ *
85
+ * @param request The network request being inspected.
86
+ * @param initiatorOrigin The security origin of the initiating context (e.g. page or conversation origin).
87
+ * @returns The evaluated `ResponseAccessMode`.
88
+ */
89
+ export function evaluateResponseAccessMode(
90
+ request: NetworkRequest.NetworkRequest,
91
+ initiatorOrigin?: SecurityOrigin.SecurityOrigin,
92
+ ): ResponseAccessMode {
93
+ const effectiveInitiatorOrigin = initiatorOrigin ?? SecurityOrigin.SecurityOrigin.create(request.documentURL);
94
+
95
+ // Opaque initiator contexts (e.g. data: URLs, sandboxed iframes) are never permitted
96
+ // to inspect cross-origin response content.
97
+ if (effectiveInitiatorOrigin.isOpaque()) {
98
+ return ResponseAccessMode.OPAQUE_CROSS_ORIGIN;
99
+ }
100
+
101
+ const resourceOrigin = SecurityOrigin.SecurityOrigin.create(request.url());
102
+ if (effectiveInitiatorOrigin.isSameOriginWith(resourceOrigin)) {
103
+ return ResponseAccessMode.SAME_ORIGIN;
104
+ }
105
+
106
+ // If the browser blocked the request with a CORS error, treat the response
107
+ // as opaque regardless of any Access-Control-Allow-Origin header value.
108
+ if (request.corsErrorStatus()) {
109
+ return ResponseAccessMode.OPAQUE_CROSS_ORIGIN;
110
+ }
111
+
112
+ const allowOriginHeader = request.responseHeaderValue('access-control-allow-origin')?.trim();
113
+ if (!allowOriginHeader) {
114
+ return ResponseAccessMode.OPAQUE_CROSS_ORIGIN;
115
+ }
116
+
117
+ const isCredentialed = isRequestCredentialed(request);
118
+
119
+ // Under the Fetch specification, wildcard '*' is forbidden from exposing responses if credentials are used.
120
+ if (allowOriginHeader === '*' && !isCredentialed) {
121
+ return ResponseAccessMode.CORS_ALLOWED;
122
+ }
123
+
124
+ if (allowOriginHeader.toLowerCase() === effectiveInitiatorOrigin.siteId().toLowerCase()) {
125
+ return ResponseAccessMode.CORS_ALLOWED;
126
+ }
127
+
128
+ return ResponseAccessMode.OPAQUE_CROSS_ORIGIN;
129
+ }
130
+
131
+ /**
132
+ * Filters the response headers of a network request based on its access mode.
133
+ *
134
+ * - `SAME_ORIGIN`: Returns all response headers.
135
+ * - `CORS_ALLOWED`: Returns CORS-safelisted headers plus headers exposed via
136
+ * `Access-Control-Expose-Headers`.
137
+ * - `OPAQUE_CROSS_ORIGIN`: Returns only CORS-safelisted response headers. Under the Fetch
138
+ * specification, opaque responses do not expose headers configured by `Access-Control-Expose-Headers`.
139
+ *
140
+ * @param request The network request whose headers are being filtered.
141
+ * @param accessMode The evaluated response access mode for this request.
142
+ * @returns Array of allowed header name/value pairs.
143
+ */
144
+ export function getFilterableResponseHeaders(
145
+ request: NetworkRequest.NetworkRequest,
146
+ accessMode: ResponseAccessMode,
147
+ ): Array<{name: string, value: string}> {
148
+ if (accessMode === ResponseAccessMode.SAME_ORIGIN) {
149
+ return request.responseHeaders;
150
+ }
151
+
152
+ if (accessMode === ResponseAccessMode.OPAQUE_CROSS_ORIGIN) {
153
+ return request.responseHeaders.filter(header => {
154
+ const lowerName = header.name.toLowerCase().trim();
155
+ return CORS_SAFELISTED_RESPONSE_HEADERS.has(lowerName);
156
+ });
157
+ }
158
+
159
+ // For CORS_ALLOWED requests, parse Access-Control-Expose-Headers.
160
+ const exposeHeadersValue = request.responseHeaderValue('access-control-expose-headers')?.trim() || '';
161
+ const exposedSet = new Set(
162
+ exposeHeadersValue.split(',').map(h => h.trim().toLowerCase()).filter(Boolean),
163
+ );
164
+
165
+ const isCredentialed = isRequestCredentialed(request);
166
+ const allowWildcardExposure = !isCredentialed && exposedSet.has('*');
167
+
168
+ return request.responseHeaders.filter(header => {
169
+ const lowerName = header.name.toLowerCase().trim();
170
+ return CORS_SAFELISTED_RESPONSE_HEADERS.has(lowerName) || allowWildcardExposure || exposedSet.has(lowerName);
171
+ });
172
+ }
@@ -30,6 +30,15 @@ const OPAQUE_PREFIXES = [
30
30
  'blob:null',
31
31
  ] as const;
32
32
 
33
+ /**
34
+ * String prefixes for imported artifact schemes (e.g., HAR recordings, traces).
35
+ * Imported entities operate in an isolated origin domain (`imported-har://${domain}`)
36
+ * that never matches live web origins (`https://${domain}`).
37
+ */
38
+ export const IMPORTED_ORIGIN_PREFIXES: ReadonlySet<string> = new Set([
39
+ 'imported-har:',
40
+ ]);
41
+
33
42
  function isOpaqueUrlString(url: string): boolean {
34
43
  const lower = url.trim().toLowerCase();
35
44
  if (OPAQUE_EXACT_MATCHES.has(lower)) {
@@ -86,6 +95,22 @@ export class SecurityOrigin {
86
95
  return SecurityOrigin.createUniqueOpaque();
87
96
  }
88
97
 
98
+ // Custom imported schemes (e.g. `imported-har:`) are not supported by standard URL parsers.
99
+ // We extract the host to construct a domain-scoped origin (`imported-har://${host}`).
100
+ // This isolates imported artifacts from live web pages (`imported-har://example.com` != `https://example.com`),
101
+ // while enabling same-origin comparison between requests within the same imported session domain.
102
+ for (const prefix of IMPORTED_ORIGIN_PREFIXES) {
103
+ if (rawUrl.toLowerCase().startsWith(prefix)) {
104
+ const afterScheme = rawUrl.substring(prefix.length).replace(/^\/+/, '');
105
+ const slashIndex = afterScheme.indexOf('/');
106
+ const host = slashIndex === -1 ? afterScheme : afterScheme.substring(0, slashIndex);
107
+ if (!host) {
108
+ return SecurityOrigin.createUniqueOpaque();
109
+ }
110
+ return new SecurityOrigin({type: 'origin', value: `${prefix}//${host.toLowerCase()}`});
111
+ }
112
+ }
113
+
89
114
  if (rawUrl.toLowerCase().startsWith('file://')) {
90
115
  const parsed = Common.ParsedURL.ParsedURL.fromString(rawUrl as Platform.DevToolsPath.UrlString);
91
116
  if (!parsed) {
@@ -58,6 +58,7 @@ import * as LayerTreeBase from './LayerTreeBase.js';
58
58
  import * as LogModel from './LogModel.js';
59
59
  import * as NetworkManager from './NetworkManager.js';
60
60
  import * as NetworkRequest from './NetworkRequest.js';
61
+ import * as NetworkRequestAccess from './NetworkRequestAccess.js';
61
62
  import * as OverlayColorGenerator from './OverlayColorGenerator.js';
62
63
  import * as OverlayModel from './OverlayModel.js';
63
64
  import * as OverlayPersistentHighlighter from './OverlayPersistentHighlighter.js';
@@ -146,6 +147,7 @@ export {
146
147
  LogModel,
147
148
  NetworkManager,
148
149
  NetworkRequest,
150
+ NetworkRequestAccess,
149
151
  OverlayColorGenerator,
150
152
  OverlayModel,
151
153
  OverlayPersistentHighlighter,
@@ -3790,7 +3790,7 @@ export const generatedProperties: CSSProperty[] = [
3790
3790
  {
3791
3791
  "name": "margin-trim",
3792
3792
  "runtime_flag": "MarginTrim",
3793
- "runtime_flag_status": "experimental"
3793
+ "runtime_flag_status": "stable"
3794
3794
  },
3795
3795
  {
3796
3796
  "inherited": true,
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
 
5
5
  import * as Host from '../../core/host/host.js';
6
+ import * as Root from '../../core/root/root.js';
6
7
  import * as SDK from '../../core/sdk/sdk.js';
7
8
  import type * as LHModel from '../lighthouse/lighthouse.js';
8
9
  import type * as Trace from '../trace/trace.js';
@@ -78,7 +79,9 @@ export class AiAgent2 extends AiAgent<unknown> {
78
79
  // TODO: The static preamble is a placeholder and will eventually live server-side.
79
80
  readonly preamble: string = preamble;
80
81
  readonly clientFeature: Host.AidaClient.ClientFeature = Host.AidaClient.ClientFeature.CHROME_DEVTOOLS_V2_AGENT;
81
- readonly userTier: string = 'TESTERS';
82
+ get userTier(): string|undefined {
83
+ return Root.Runtime.hostConfig.devToolsAiV2Architecture?.userTier;
84
+ }
82
85
 
83
86
  #changes: ChangeManager;
84
87
  #execJs: typeof executeJsCode;
@@ -1,7 +1,6 @@
1
1
  // Copyright 2024 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
- import * as Common from '../../core/common/common.js';
5
4
  import * as Host from '../../core/host/host.js';
6
5
  import * as Platform from '../../core/platform/platform.js';
7
6
  import * as Root from '../../core/root/root.js';
@@ -46,14 +45,13 @@ export const NOT_FOUND_IMAGE_DATA = '';
46
45
  export const CONTEXT_TITLE = 'Analyzing data';
47
46
  const MAX_TITLE_LENGTH = 80;
48
47
  /**
49
- * List of page navigations that are allowed during an AI agent run.
50
- * These are page navigations triggered by agents themselves:
51
- * - `about://` : Navigated to before initiating a trace recording to ensure a clean state.
52
- * - `chrome://terms`: Navigated to by Lighthouse during its Back-Forward Cache
53
- * audit.
48
+ * Page URL prefixes permitted during an AI agent run.
49
+ * Agents trigger these navigations internally:
50
+ * - `about:blank`: Used before recording a performance trace to ensure a clean state.
51
+ * - `chrome://terms`: Used by Lighthouse during Back-Forward Cache audits.
54
52
  */
55
53
  export const ALLOWED_PAGE_NAVIGATIONS: Platform.DevToolsPath.UrlString[] = [
56
- Platform.DevToolsPath.urlString`about://`,
54
+ Platform.DevToolsPath.urlString`about:blank`,
57
55
  Platform.DevToolsPath.urlString`chrome://terms`,
58
56
  ];
59
57
 
@@ -431,14 +429,17 @@ export class AiConversation {
431
429
  } = {},
432
430
  ): AsyncGenerator<ResponseData, void, void> {
433
431
  this.#navigationOccurredDuringRun = false;
434
- const originAtRunStart = getPrimaryPageOrigin(this.#targetManager);
432
+ const originAtRunStart = getPrimaryPageSecurityOrigin(this.#targetManager);
435
433
  const listener = (): void => {
436
- // If an unexpected navigation to a different origin occurred
437
- // during processing the user's request, we don't want to allow
438
- // the agent to run any function calls and retrieve data from the new origin.
439
- // Performance agent and accessibility agent navigate to 'about://' or 'chrome://terms'
440
- const newOrigin = getPrimaryPageOrigin(this.#targetManager);
441
- if (originAtRunStart !== newOrigin && newOrigin && !ALLOWED_PAGE_NAVIGATIONS.includes(newOrigin)) {
434
+ // Prevent the agent from executing tools or reading data from an untrusted origin
435
+ // if the page navigates unexpectedly during execution.
436
+ const newInspectedURL = this.#targetManager.primaryPageTarget()?.inspectedURL();
437
+ const newOrigin = newInspectedURL ? SDK.SecurityOrigin.SecurityOrigin.create(newInspectedURL) : undefined;
438
+ const isSameOrigin = Boolean(originAtRunStart && newOrigin && originAtRunStart.isSameOriginWith(newOrigin));
439
+ const isAllowedNavigation =
440
+ Boolean(newInspectedURL && ALLOWED_PAGE_NAVIGATIONS.some(allowed => newInspectedURL.startsWith(allowed)));
441
+
442
+ if (!isSameOrigin && !isAllowedNavigation) {
442
443
  this.#navigationOccurredDuringRun = true;
443
444
  }
444
445
  };
@@ -551,6 +552,10 @@ export class AiConversation {
551
552
  return this.#type;
552
553
  }
553
554
 
555
+ /**
556
+ * Returns the permitted origin for agent tool execution, or blocks execution
557
+ * if an unapproved cross-origin navigation occurred during the current run.
558
+ */
554
559
  allowedOrigin = (): AllowedOriginResult => {
555
560
  if (this.#navigationOccurredDuringRun) {
556
561
  return {blocked: true};
@@ -558,7 +563,7 @@ export class AiConversation {
558
563
  if (this.#origin) {
559
564
  return {origin: this.origin};
560
565
  }
561
- this.#origin = getPrimaryPageOrigin(this.#targetManager);
566
+ this.#origin = getPrimaryPageSecurityOrigin(this.#targetManager);
562
567
 
563
568
  return {origin: this.origin};
564
569
  };
@@ -574,10 +579,16 @@ function isAiAssistanceServerSideLoggingAllowed(): boolean {
574
579
  return !Root.Runtime.hostConfig.aidaAvailability?.disallowLogging;
575
580
  }
576
581
 
577
- function getPrimaryPageOrigin(
582
+ /**
583
+ * Returns the security origin of the primary page target.
584
+ *
585
+ * @param targetManager Target manager used to locate the primary page target.
586
+ * @returns The parsed SecurityOrigin, or undefined if no target or inspected URL exists.
587
+ */
588
+ function getPrimaryPageSecurityOrigin(
578
589
  targetManager: SDK.TargetManager.TargetManager,
579
- ): Platform.DevToolsPath.UrlString|undefined {
590
+ ): SDK.SecurityOrigin.SecurityOrigin|undefined {
580
591
  const target = targetManager.primaryPageTarget();
581
592
  const inspectedURL = target?.inspectedURL();
582
- return inspectedURL ? new Common.ParsedURL.ParsedURL(inspectedURL).securityOrigin() : undefined;
593
+ return inspectedURL ? SDK.SecurityOrigin.SecurityOrigin.create(inspectedURL) : undefined;
583
594
  }
@@ -615,7 +615,9 @@
615
615
 
616
616
  .storage-breakdown-widget {
617
617
  display: flex;
618
- justify-content: center;
618
+ justify-content: flex-start;
619
+ min-width: fit-content;
619
620
  padding: var(--sys-size-4);
621
+ width: 100%;
620
622
  }
621
623
  }
@@ -460,7 +460,7 @@ export class BackForwardCacheView extends UI.Widget.Widget {
460
460
  #historyIndex = 0;
461
461
  #view: View;
462
462
 
463
- constructor(view = DEFAULT_VIEW) {
463
+ constructor(view: View = DEFAULT_VIEW) {
464
464
  super({useShadowDom: true, delegatesFocus: true});
465
465
  this.#view = view;
466
466
  }
@@ -71,7 +71,8 @@ export const DEFAULT_VIEW = (input: ViewInput, output: undefined, target: HTMLEl
71
71
  type View = typeof DEFAULT_VIEW;
72
72
 
73
73
  export class EndpointsGrid extends UI.Widget.Widget {
74
- endpoints = new Map<string, Protocol.Network.ReportingApiEndpoint[]>();
74
+ endpoints: Map<string, Protocol.Network.ReportingApiEndpoint[]> =
75
+ new Map<string, Protocol.Network.ReportingApiEndpoint[]>();
75
76
  #view: View;
76
77
 
77
78
  constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
@@ -218,7 +218,7 @@ export class PermissionsPolicySection extends UI.Widget.Widget {
218
218
  #showDetails = false;
219
219
  #view: View;
220
220
 
221
- constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
221
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
222
222
  super(element, {useShadowDom: false});
223
223
  this.#view = view;
224
224
  }
@@ -45,7 +45,7 @@ export class ServiceWorkerRouterView extends UI.Widget.Widget {
45
45
  #rules: SDK.ServiceWorkerManager.ServiceWorkerRouterRule[] = [];
46
46
  #view: View;
47
47
 
48
- constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
48
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
49
49
  super(element, {useShadowDom: true});
50
50
  this.#view = view;
51
51
  }
@@ -141,7 +141,7 @@ export class TrustTokensView extends UI.Widget.VBox {
141
141
  #tokens: Protocol.Storage.TrustTokens[] = [];
142
142
  #view: View;
143
143
 
144
- constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
144
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
145
145
  super(element, {useShadowDom: true});
146
146
  this.#view = view;
147
147
  }
@@ -443,7 +443,8 @@ export class PreloadingDetailsReportView extends UI.Widget.VBox {
443
443
  #data: PreloadingDetailsReportViewData = null;
444
444
  #view: typeof DEFAULT_VIEW;
445
445
 
446
- constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
446
+ constructor(element?: HTMLElement,
447
+ view: (input: ViewInput, _output: undefined, target: HTMLElement) => void = DEFAULT_VIEW) {
447
448
  super(element);
448
449
  this.#view = view;
449
450
  }
@@ -79,7 +79,8 @@ export class RuleSetDetailsView extends UI.Widget.VBox {
79
79
  #ruleSet: RuleSet|null = null;
80
80
  #shouldPrettyPrint = true;
81
81
 
82
- constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
82
+ constructor(element?: HTMLElement,
83
+ view: (input: ViewInput|null, _output: object, target: HTMLElement) => void = DEFAULT_VIEW) {
83
84
  super(element, {useShadowDom: true});
84
85
  this.#view = view;
85
86
  }
@@ -425,7 +425,7 @@ const DEFAULT_VIEW: View = (input, _output, target) => {
425
425
  export class UsedPreloadingView extends UI.Widget.VBox {
426
426
  readonly #view: View;
427
427
 
428
- constructor(view = DEFAULT_VIEW) {
428
+ constructor(view: View = DEFAULT_VIEW) {
429
429
  super({useShadowDom: true});
430
430
  this.#view = view;
431
431
  }
@@ -66,10 +66,14 @@ function makeFixedPresetThrottlingOption(rate: CPUThrottlingRates): CPUThrottlin
66
66
  };
67
67
  }
68
68
 
69
- export const NoThrottlingOption = makeFixedPresetThrottlingOption(CPUThrottlingRates.NO_THROTTLING);
70
- export const MidTierThrottlingOption = makeFixedPresetThrottlingOption(CPUThrottlingRates.MID_TIER_MOBILE);
71
- export const LowTierThrottlingOption = makeFixedPresetThrottlingOption(CPUThrottlingRates.LOW_TIER_MOBILE);
72
- export const ExtraSlowThrottlingOption = makeFixedPresetThrottlingOption(CPUThrottlingRates.EXTRA_SLOW);
69
+ export const NoThrottlingOption: CPUThrottlingOption =
70
+ makeFixedPresetThrottlingOption(CPUThrottlingRates.NO_THROTTLING);
71
+ export const MidTierThrottlingOption: CPUThrottlingOption =
72
+ makeFixedPresetThrottlingOption(CPUThrottlingRates.MID_TIER_MOBILE);
73
+ export const LowTierThrottlingOption: CPUThrottlingOption =
74
+ makeFixedPresetThrottlingOption(CPUThrottlingRates.LOW_TIER_MOBILE);
75
+ export const ExtraSlowThrottlingOption: CPUThrottlingOption =
76
+ makeFixedPresetThrottlingOption(CPUThrottlingRates.EXTRA_SLOW);
73
77
 
74
78
  function makeCalibratedThrottlingOption(calibratedDeviceType: CalibratedDeviceType): CPUThrottlingOption {
75
79
  const getSettingValue = (): number|CalibrationError|null => {
@@ -109,8 +113,10 @@ function makeCalibratedThrottlingOption(calibratedDeviceType: CalibratedDeviceTy
109
113
  };
110
114
  }
111
115
 
112
- export const CalibratedLowTierMobileThrottlingOption = makeCalibratedThrottlingOption('low-tier-mobile');
113
- export const CalibratedMidTierMobileThrottlingOption = makeCalibratedThrottlingOption('mid-tier-mobile');
116
+ export const CalibratedLowTierMobileThrottlingOption: CPUThrottlingOption =
117
+ makeCalibratedThrottlingOption('low-tier-mobile');
118
+ export const CalibratedMidTierMobileThrottlingOption: CPUThrottlingOption =
119
+ makeCalibratedThrottlingOption('mid-tier-mobile');
114
120
 
115
121
  export function calibrationErrorToString(error: CalibrationError): string {
116
122
  if (error === CalibrationError.DEVICE_TOO_WEAK) {
@@ -91,7 +91,7 @@ export class DOMNodeLink extends UI.Widget.Widget {
91
91
  #options: Options|undefined = undefined;
92
92
  #view: View;
93
93
 
94
- constructor(element?: HTMLElement, node?: SDK.DOMModel.DOMNode, options?: Options, view = DEFAULT_VIEW) {
94
+ constructor(element?: HTMLElement, node?: SDK.DOMModel.DOMNode, options?: Options, view: View = DEFAULT_VIEW) {
95
95
  super(element, {useShadowDom: true});
96
96
  this.element.classList.remove('vbox');
97
97
  this.#node = node;
@@ -41,7 +41,8 @@ export class ExtensionView extends UI.Widget.Widget {
41
41
  #frameIndex?: number;
42
42
 
43
43
  #view: typeof DEFAULT_VIEW;
44
- constructor(server: ExtensionServer, id: string, src: string, className: string, view = DEFAULT_VIEW) {
44
+ constructor(server: ExtensionServer, id: string, src: string, className: string,
45
+ view: (input: ViewInput, output: ViewOutput, target: HTMLElement) => void = DEFAULT_VIEW) {
45
46
  super();
46
47
  this.#view = view;
47
48
  this.#server = server;
@@ -109,7 +109,8 @@ export class ConsolePinPane extends UI.Widget.VBox {
109
109
  readonly #pinModel: ConsolePinModel;
110
110
  readonly #focusOut: () => void;
111
111
 
112
- constructor(focusOut: () => void, view = DEFAULT_PANE_VIEW) {
112
+ constructor(focusOut: () => void,
113
+ view: (input: PaneViewInput, _output: object, target: HTMLElement) => void = DEFAULT_PANE_VIEW) {
113
114
  super({useShadowDom: true});
114
115
  this.#focusOut = focusOut;
115
116
  this.#view = view;
@@ -294,7 +295,8 @@ export class ConsolePinPresenter extends UI.Widget.Widget {
294
295
  #deletePinIcon!: Buttons.Button.Button;
295
296
  readonly #selfXssWarningDisabledSetting: Common.Settings.Setting<boolean>;
296
297
 
297
- constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
298
+ constructor(element?: HTMLElement,
299
+ view: (input: ViewInput, output: ViewOutput, target: HTMLElement) => void = DEFAULT_VIEW) {
298
300
  super(element);
299
301
  this.#view = view;
300
302
 
@@ -117,7 +117,11 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
117
117
  };
118
118
 
119
119
  export class ConsoleFilterGroup {
120
- readonly urlGroups = new Map<string|null, {filter: ConsoleFilter, url: string|null, count: number}>();
120
+ readonly urlGroups: Map<string|null, {
121
+ filter: ConsoleFilter,
122
+ url: string|null,
123
+ count: number,
124
+ }> = new Map<string|null, {filter: ConsoleFilter, url: string|null, count: number}>();
121
125
  messageCount = 0;
122
126
  readonly name: GroupName;
123
127
  readonly filter: ConsoleFilter;
@@ -190,7 +194,7 @@ export class ConsoleSidebar extends ConsoleSidebarBase {
190
194
  Common.Settings.Settings.instance().createSetting<string|null>('console.sidebar-selected-filter', null);
191
195
  #selectedFilter = this.#groups.find(group => group.name === this.#selectedFilterSetting.get())?.filter;
192
196
 
193
- constructor(element?: HTMLElement, view = DEFAULT_VIEW) {
197
+ constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
194
198
  super(element, {
195
199
  useShadowDom: 'pure',
196
200
  });
@@ -203,7 +203,7 @@ export function lineWhitespace(line: string): string|null {
203
203
 
204
204
  export function formatRelatedCode(
205
205
  {text, columnNumber, lineNumber}: {text: string, columnNumber: number, lineNumber: number},
206
- maxCodeSize = MAX_CODE_SIZE): string {
206
+ maxCodeSize: number = MAX_CODE_SIZE): string {
207
207
  const lines = text.split('\n');
208
208
  if (lines[lineNumber].length >= maxCodeSize / 2) {
209
209
  const start = Math.max(columnNumber - maxCodeSize / 2, 0);