chrome-devtools-frontend 1.0.1697998 → 1.0.1699061

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 (145) hide show
  1. package/.agents/skills/devtools-verification/SKILL.md +19 -0
  2. package/.agents/skills/{repro-flaky-tests → fix-tests}/SKILL.md +2 -2
  3. package/.agents/skills/version-control/SKILL.md +21 -9
  4. package/front_end/core/platform/HostRuntime.ts +12 -0
  5. package/front_end/core/platform/api/HostRuntime.ts +13 -0
  6. package/front_end/core/platform/browser/HostRuntime.ts +34 -0
  7. package/front_end/core/platform/node/HostRuntime.ts +40 -0
  8. package/front_end/core/sdk/AnimationModel.ts +12 -98
  9. package/front_end/core/sdk/CSSModel.ts +4 -2
  10. package/front_end/core/sdk/CSSPropertyParser.ts +4 -4
  11. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +4 -44
  12. package/front_end/core/sdk/Connections.ts +6 -1
  13. package/front_end/core/sdk/ConsoleModel.ts +1 -12
  14. package/front_end/core/sdk/DOMModel.ts +6 -56
  15. package/front_end/core/sdk/DebuggerModel.ts +9 -8
  16. package/front_end/core/sdk/NetworkManager.ts +2 -2
  17. package/front_end/core/sdk/NetworkRequest.ts +1 -1
  18. package/front_end/core/sdk/OverlayModel.ts +11 -12
  19. package/front_end/core/sdk/PageFunctions.ts +187 -0
  20. package/front_end/core/sdk/Resource.ts +1 -1
  21. package/front_end/core/sdk/RuntimeModel.ts +1 -20
  22. package/front_end/core/sdk/Script.ts +21 -0
  23. package/front_end/core/sdk/SourceMap.ts +15 -2
  24. package/front_end/core/sdk/SourceMapCache.ts +13 -10
  25. package/front_end/core/sdk/SourceMapManager.ts +11 -9
  26. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +109 -15
  27. package/front_end/core/sdk/SourceMapScopesInfo.ts +8 -2
  28. package/front_end/entrypoints/heap_snapshot_worker/ContextAnalyzer.ts +635 -0
  29. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshot.ts +26 -12
  30. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotLoader.ts +16 -2
  31. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotWorkerDispatcher.ts +4 -4
  32. package/front_end/entrypoints/heap_snapshot_worker/heap_snapshot_worker.ts +2 -0
  33. package/front_end/entrypoints/shell/shell.ts +1 -1
  34. package/front_end/models/ai_assistance/DOMHelpers.ts +95 -0
  35. package/front_end/models/ai_assistance/EvaluateAction.ts +5 -42
  36. package/front_end/models/ai_assistance/ExtensionScope.ts +2 -31
  37. package/front_end/models/ai_assistance/agents/AiAgent.ts +2 -1
  38. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +3 -4
  39. package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
  40. package/front_end/models/ai_assistance/contexts/DOMNodeContext.ts +10 -7
  41. package/front_end/models/ai_assistance/contexts/FileContext.ts +2 -10
  42. package/front_end/models/ai_assistance/tools/GetSourceContent.ts +3 -2
  43. package/front_end/models/ai_assistance/tools/ListSources.ts +21 -14
  44. package/front_end/models/bindings/CompilerScriptMapping.ts +3 -10
  45. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +48 -17
  46. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -2
  47. package/front_end/models/bindings/StylesSourceMapping.ts +1 -1
  48. package/front_end/models/comment_manager/CD4ABridge.ts +55 -12
  49. package/front_end/models/comment_manager/CommentManager.ts +46 -91
  50. package/front_end/models/comment_manager/CommentThread.ts +145 -0
  51. package/front_end/models/comment_manager/comment_manager.ts +2 -0
  52. package/front_end/models/emulation/DeviceModeModel.ts +54 -1
  53. package/front_end/models/extensions/ExtensionAPI.ts +8 -6
  54. package/front_end/models/extensions/ExtensionEndpoint.ts +11 -8
  55. package/front_end/models/extensions/HostUrlPattern.ts +4 -3
  56. package/front_end/models/extensions/LanguageExtensionEndpoint.ts +3 -2
  57. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +2 -1
  58. package/front_end/models/formatter/FormatterWorkerPool.ts +4 -6
  59. package/front_end/models/heap_snapshot/HeapSnapshotModel.ts +55 -0
  60. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +18 -14
  61. package/front_end/models/issues_manager/CookieIssue.ts +0 -13
  62. package/front_end/models/issues_manager/IssuesManager.ts +5 -0
  63. package/front_end/models/issues_manager/WebInstallIssue.ts +63 -0
  64. package/front_end/models/issues_manager/descriptions/webInstallManifestMissingId.md +3 -0
  65. package/front_end/models/issues_manager/descriptions/webInstallManifestMissingNameOrShortName.md +3 -0
  66. package/front_end/models/issues_manager/descriptions/webInstallManifestParsingOrNetworkError.md +3 -0
  67. package/front_end/models/issues_manager/descriptions/webInstallNoManifest.md +3 -0
  68. package/front_end/models/issues_manager/descriptions/webInstallStartUrlInvalid.md +3 -0
  69. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  70. package/front_end/models/source_map_scopes/NamesResolver.ts +11 -3
  71. package/front_end/models/source_map_scopes/ScopeChainModel.ts +10 -6
  72. package/front_end/models/workspace/UISourceCode.ts +20 -0
  73. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +281 -3
  74. package/front_end/panels/accessibility/accessibilityAnnouncementRecordingView.css +85 -0
  75. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +5 -8
  76. package/front_end/panels/ai_assistance/components/AIv2MarkdownRenderer.ts +4 -10
  77. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  78. package/front_end/panels/application/CrashReportContextView.ts +1 -1
  79. package/front_end/panels/application/IndexedDBViews.ts +1 -1
  80. package/front_end/panels/application/KeyValueStorageItemsView.ts +1 -0
  81. package/front_end/panels/application/components/AdsView.ts +63 -1
  82. package/front_end/panels/application/components/adsView.css +11 -1
  83. package/front_end/{ui/comments → panels/common}/CommentThreadWidget.ts +5 -20
  84. package/front_end/{ui/comments → panels/common}/CommentsOverlayWidget.ts +39 -62
  85. package/front_end/{ui/comments → panels/common}/CommentsStatusBarPill.ts +5 -5
  86. package/front_end/panels/common/ExtensionServer.ts +2 -1
  87. package/front_end/{ui/comments → panels/common}/commentThreadWidget.css +0 -12
  88. package/front_end/{ui/comments → panels/common}/commentsOverlay.css +2 -1
  89. package/front_end/{ui/comments/comments-meta.ts → panels/common/common-meta.ts} +11 -11
  90. package/front_end/panels/common/common.ts +3 -0
  91. package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +3 -3
  92. package/front_end/panels/elements/CSSRuleValidator.ts +1 -1
  93. package/front_end/panels/elements/ElementsPanel.ts +9 -5
  94. package/front_end/panels/elements/ElementsTreeElement.ts +11 -3
  95. package/front_end/panels/elements/ElementsTreeOutline.ts +69 -74
  96. package/front_end/panels/elements/PropertiesWidget.ts +11 -10
  97. package/front_end/panels/elements/PropertyRenderer.ts +29 -25
  98. package/front_end/panels/elements/StylePropertyTreeElement.ts +6 -6
  99. package/front_end/panels/elements/elementsTreeOutline.css +11 -5
  100. package/front_end/panels/emulation/DeviceModeToolbar.ts +2 -3
  101. package/front_end/panels/event_listeners/EventListenersView.ts +33 -44
  102. package/front_end/panels/lighthouse/LighthouseController.ts +1 -1
  103. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +6 -7
  104. package/front_end/panels/linear_memory_inspector/components/linearMemoryInspector.css +5 -1
  105. package/front_end/panels/linear_memory_inspector/linear_memory_inspector-meta.ts +1 -1
  106. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +1 -1
  107. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +1 -1
  108. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +1 -1
  109. package/front_end/panels/recorder/StepView.ts +19 -4
  110. package/front_end/panels/recorder/TimelineSection.ts +1 -1
  111. package/front_end/panels/recorder/stepView.css +1 -0
  112. package/front_end/panels/security/SecurityPanel.ts +5 -5
  113. package/front_end/panels/security/SecurityPanelSidebar.ts +2 -11
  114. package/front_end/panels/settings/BackendLinkingSettingsTab.ts +2 -1
  115. package/front_end/panels/settings/EditFileSystemView.ts +1 -1
  116. package/front_end/panels/sources/CallStackSidebarPane.ts +1 -1
  117. package/front_end/panels/sources/DebuggerPlugin.ts +2 -2
  118. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +11 -16
  119. package/front_end/panels/sources/sources-meta.ts +1 -1
  120. package/front_end/panels/timeline/components/DetailsView.ts +3 -3
  121. package/front_end/panels/timeline/components/OriginMap.ts +1 -1
  122. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -3
  123. package/front_end/panels/timeline/overlays/OverlaysImpl.ts +0 -3
  124. package/front_end/third_party/chromium/README.chromium +1 -1
  125. package/front_end/ui/comments/CommentOverlayManager.ts +28 -110
  126. package/front_end/ui/comments/comments.ts +0 -6
  127. package/front_end/ui/components/buttons/Button.ts +19 -1
  128. package/front_end/ui/legacy/ContextMenu.ts +16 -1
  129. package/front_end/ui/legacy/PlusButton.ts +5 -6
  130. package/front_end/ui/legacy/Treeoutline.ts +6 -0
  131. package/front_end/ui/legacy/Widget.ts +33 -0
  132. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +3 -0
  133. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +14 -4
  134. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +36 -32
  135. package/front_end/ui/visual_logging/KnownContextValues.ts +4 -0
  136. package/package.json +1 -1
  137. package/.agents/skills/devtools-ci/SKILL.md +0 -65
  138. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +0 -143
  139. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +0 -104
  140. package/.agents/skills/fixing-skipped-tests/SKILL.md +0 -32
  141. package/.agents/skills/gerrit-cli/SKILL.md +0 -74
  142. package/.agents/skills/gerrit-cli/references/commands.md +0 -169
  143. package/.agents/skills/gerrit-cli/references/workflows.md +0 -63
  144. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +0 -85
  145. package/front_end/models/issues_manager/descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +0 -4
@@ -44,6 +44,7 @@ import {ConsoleModel} from './ConsoleModel.js';
44
44
  import {CSSModel} from './CSSModel.js';
45
45
  import type {FrameManager} from './FrameManager.js';
46
46
  import {OverlayModel} from './OverlayModel.js';
47
+ import {focusInPage, scrollIntoViewInPage, toggleClassAndInjectStyleRule} from './PageFunctions.js';
47
48
  import {RemoteObject} from './RemoteObject.js';
48
49
  import {Events as ResourceTreeModelEvents, type ResourceTreeFrame, ResourceTreeModel} from './ResourceTreeModel.js';
49
50
  import {RuntimeModel} from './RuntimeModel.js';
@@ -1127,47 +1128,6 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
1127
1128
  [{value: pseudoElementName}, {value: !hidden}]);
1128
1129
  object.release();
1129
1130
  this.setMarker('hidden-marker', hidden ? null : true);
1130
-
1131
- function toggleClassAndInjectStyleRule(this: Element, pseudoElementName: string|null, hidden: boolean): void {
1132
- const classNamePrefix = '__web-inspector-hide';
1133
- const classNameSuffix = '-shortcut__';
1134
- const styleTagId = '__web-inspector-hide-shortcut-style__';
1135
- const pseudoElementNameEscaped = pseudoElementName ? pseudoElementName.replace(/[\(\)\:]/g, '_') : '';
1136
- const className = classNamePrefix + pseudoElementNameEscaped + classNameSuffix;
1137
- this.classList.toggle(className, hidden);
1138
-
1139
- let localRoot: Element|HTMLHeadElement = this;
1140
- while (localRoot.parentNode) {
1141
- localRoot = (localRoot.parentNode as Element);
1142
- }
1143
- if (localRoot.nodeType === Node.DOCUMENT_NODE) {
1144
- localRoot = document.head;
1145
- }
1146
-
1147
- let style = localRoot.querySelector('style#' + styleTagId);
1148
- if (!style) {
1149
- const selectors = [];
1150
- selectors.push('.__web-inspector-hide-shortcut__');
1151
- selectors.push('.__web-inspector-hide-shortcut__ *');
1152
- const selector = selectors.join(', ');
1153
- const ruleBody = ' visibility: hidden !important;';
1154
- const rule = '\n' + selector + '\n{\n' + ruleBody + '\n}\n';
1155
-
1156
- style = document.createElement('style');
1157
- style.id = styleTagId;
1158
- style.textContent = rule;
1159
-
1160
- localRoot.appendChild(style);
1161
- }
1162
-
1163
- // In addition to putting them on the element we want to hide, we will
1164
- // also add pseudo element classes to the style element to keep track of
1165
- // which pseudo elements we have style rules for.
1166
- if (pseudoElementName && !style.classList.contains(className)) {
1167
- style.classList.add(className);
1168
- style.textContent = `.${className}${pseudoElementName}, ${style.textContent}`;
1169
- }
1170
- }
1171
1131
  }
1172
1132
 
1173
1133
  isToggledToHidden(): boolean {
@@ -1329,8 +1289,8 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
1329
1289
  return node;
1330
1290
  }
1331
1291
 
1332
- async callFunction<T, U extends string|number>(fn: (this: HTMLElement, ...args: U[]) => T, args: U[] = []):
1333
- Promise<{value: T}|null> {
1292
+ async callFunction<T, U extends string|number, This = never>(fn: (this: This, ...args: U[]) => T,
1293
+ args: U[] = []): Promise<{value: T}|null> {
1334
1294
  const object = await this.resolveToObject();
1335
1295
  if (!object) {
1336
1296
  return null;
@@ -1359,16 +1319,10 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
1359
1319
  return;
1360
1320
  }
1361
1321
 
1362
- const result = await node.callFunction(scrollIntoViewInPage);
1363
- if (!result) {
1364
- return;
1365
- }
1366
-
1322
+ // Highlight synchronously before scrolling to avoid out-of-order highlights
1323
+ // if asynchronous scroll calls resolve late during rapid navigation.
1367
1324
  node.highlightForTwoSeconds();
1368
-
1369
- function scrollIntoViewInPage(this: Element): void {
1370
- this.scrollIntoViewIfNeeded(true);
1371
- }
1325
+ await node.callFunction(scrollIntoViewInPage);
1372
1326
  }
1373
1327
 
1374
1328
  async focus(): Promise<void> {
@@ -1383,10 +1337,6 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
1383
1337
 
1384
1338
  node.highlightForTwoSeconds();
1385
1339
  await this.#domModel.target().pageAgent().invoke_bringToFront();
1386
-
1387
- function focusInPage(this: HTMLElement): void {
1388
- this.focus();
1389
- }
1390
1340
  }
1391
1341
 
1392
1342
  simpleSelector(): string {
@@ -23,7 +23,7 @@ import {
23
23
  pauseOnExceptionEnabledSettingDescriptor,
24
24
  pauseOnUncaughtExceptionSettingDescriptor,
25
25
  } from './SDKSettings.js';
26
- import {SourceMap} from './SourceMap.js';
26
+ import {SourceMap, SourceMapProvenance} from './SourceMap.js';
27
27
  import {SourceMapManager} from './SourceMapManager.js';
28
28
  import {Capability, type Target} from './Target.js';
29
29
 
@@ -189,10 +189,10 @@ export class DebuggerModel extends SDKModel<EventTypes> {
189
189
  this.agent = target.debuggerAgent();
190
190
  this.#runtimeModel = (target.model(RuntimeModel) as RuntimeModel);
191
191
 
192
- this.#sourceMapManager =
193
- new SourceMapManager(target,
194
- (compiledURL, sourceMappingURL, payload, script) => new SourceMap(
195
- compiledURL, sourceMappingURL, payload, target.targetManager().getConsole(), script));
192
+ this.#sourceMapManager = new SourceMapManager(
193
+ target,
194
+ (compiledURL, sourceMappingURL, payload, script, provenance) => new SourceMap(
195
+ compiledURL, sourceMappingURL, payload, target.targetManager().getConsole(), script, provenance));
196
196
 
197
197
  const settings = this.target().targetManager().settings;
198
198
  this.#pauseOnExceptionEnabledSetting = settings.resolve(pauseOnExceptionEnabledSettingDescriptor);
@@ -719,7 +719,7 @@ export class DebuggerModel extends SDKModel<EventTypes> {
719
719
 
720
720
  if ((!selectedDebugSymbol || selectedDebugSymbol.type === Protocol.Debugger.DebugSymbolsType.SourceMap) &&
721
721
  script.sourceMapURL && !hasSyntaxError) {
722
- this.#sourceMapManager.attachSourceMap(script, script.sourceURL, script.sourceMapURL);
722
+ this.#sourceMapManager.attachSourceMap(script, script.sourceURL, script.sourceMapURL, SourceMapProvenance.CDP);
723
723
  }
724
724
 
725
725
  const isDiscardable = hasSyntaxError && script.isAnonymousScript();
@@ -730,11 +730,12 @@ export class DebuggerModel extends SDKModel<EventTypes> {
730
730
  return script;
731
731
  }
732
732
 
733
- setSourceMapURL(script: Script, newSourceMapURL: Platform.DevToolsPath.UrlString): void {
733
+ setSourceMapURL(script: Script, newSourceMapURL: Platform.DevToolsPath.UrlString,
734
+ provenance: SourceMapProvenance): void {
734
735
  // Detach any previous source map from the `script` first.
735
736
  this.#sourceMapManager.detachSourceMap(script);
736
737
  script.sourceMapURL = newSourceMapURL;
737
- this.#sourceMapManager.attachSourceMap(script, script.sourceURL, script.sourceMapURL);
738
+ this.#sourceMapManager.attachSourceMap(script, script.sourceURL, script.sourceMapURL, provenance);
738
739
  }
739
740
 
740
741
  async setDebugInfoURL(script: Script, _externalURL: Platform.DevToolsPath.UrlString): Promise<void> {
@@ -93,14 +93,14 @@ const UIStrings = {
93
93
  * @example {GET} PH2
94
94
  * @example {https://example.com} PH3
95
95
  */
96
- sFailedLoadingSS: '{PH1} failed loading: {PH2} "{PH3}".',
96
+ sFailedLoadingSS: '{PH1} failed loading: {PH2} "{PH3}"',
97
97
  /**
98
98
  * @description Console message when a request finished loading.
99
99
  * @example {XHR} PH1
100
100
  * @example {GET} PH2
101
101
  * @example {https://example.com} PH3
102
102
  */
103
- sFinishedLoadingSS: '{PH1} finished loading: {PH2} "{PH3}".',
103
+ sFinishedLoadingSS: '{PH1} finished loading: {PH2} "{PH3}"',
104
104
  /**
105
105
  * @description One of direct socket connection statuses.
106
106
  */
@@ -1566,7 +1566,7 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
1566
1566
  return this.#signedExchangeInfo;
1567
1567
  }
1568
1568
 
1569
- async populateImageSource(image: HTMLImageElement): Promise<void> {
1569
+ async populateImageSource(image: {src: string}): Promise<void> {
1570
1570
  const contentData = await this.requestContentData();
1571
1571
  if (TextUtils.ContentData.ContentData.isError(contentData)) {
1572
1572
  return;
@@ -342,13 +342,13 @@ export class OverlayModel extends SDKModel<EventTypes> implements ProtocolProxyA
342
342
  // overlay, so that it is not cleared by the highlight
343
343
  return;
344
344
  }
345
- clearTimeout(this.#hideHighlightTimeout);
346
- this.#hideHighlightTimeout = undefined;
345
+ clearTimeout(this.#hideHighlightTimeout);
346
+ this.#hideHighlightTimeout = undefined;
347
347
 
348
- const highlightConfig = this.buildHighlightConfig(mode);
349
- if (typeof showInfo !== 'undefined') {
350
- highlightConfig.showInfo = showInfo;
351
- }
348
+ const highlightConfig = this.buildHighlightConfig(mode);
349
+ if (typeof showInfo !== 'undefined') {
350
+ highlightConfig.showInfo = showInfo;
351
+ }
352
352
  this.#highlighter.highlightInOverlay(data, highlightConfig);
353
353
  }
354
354
 
@@ -496,12 +496,11 @@ export class OverlayModel extends SDKModel<EventTypes> implements ProtocolProxyA
496
496
  }
497
497
 
498
498
  private delayedHideHighlight(delay: number): void {
499
- if (this.#hideHighlightTimeout === undefined) {
500
- this.#hideHighlightTimeout = globalThis.setTimeout(
501
- () => this.highlightInOverlay({clear: true}),
502
- delay,
503
- );
504
- }
499
+ clearTimeout(this.#hideHighlightTimeout);
500
+ this.#hideHighlightTimeout = globalThis.setTimeout(
501
+ () => this.highlightInOverlay({clear: true}),
502
+ delay,
503
+ );
505
504
  }
506
505
 
507
506
  highlightFrame(frameId: Protocol.Page.FrameId): void {
@@ -0,0 +1,187 @@
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 Protocol from '../../generated/protocol.js';
6
+
7
+ /**
8
+ * Functions in this file are serialized via `.toString()` and evaluated in the
9
+ * inspected page context over CDP (e.g. `Runtime.callFunctionOn`). Because they
10
+ * execute in the inspected browser environment, they use standard DOM types.
11
+ */
12
+
13
+ export function scrollListenerInPage(this: HTMLElement|Document, id: number, reportScrollPositionBindingName: string,
14
+ scrollListenerNameInPage: string): void {
15
+ if ('scrollingElement' in this && !this.scrollingElement) {
16
+ return;
17
+ }
18
+
19
+ const scrollingElement = ('scrollingElement' in this ? this.scrollingElement : this) as HTMLElement;
20
+ // @ts-expect-error We're setting a custom field on `Element` or `Document` for retaining the function on the page.
21
+ this[scrollListenerNameInPage] = () => {
22
+ // @ts-expect-error `reportScrollPosition` binding is injected to the page before calling the function.
23
+ globalThis[reportScrollPositionBindingName](
24
+ JSON.stringify({scrollTop: scrollingElement.scrollTop, scrollLeft: scrollingElement.scrollLeft, id}));
25
+ };
26
+
27
+ // @ts-expect-error We've already defined the function used below.
28
+ this.addEventListener('scroll', this[scrollListenerNameInPage], true);
29
+ }
30
+
31
+ export function removeScrollListenerInPage(this: HTMLElement|Document, scrollListenerNameInPage: string): void {
32
+ // @ts-expect-error We've already set this custom field while adding scroll listener.
33
+ this.removeEventListener('scroll', this[scrollListenerNameInPage]);
34
+ // @ts-expect-error We've already set this custom field while adding scroll listener.
35
+ delete this[scrollListenerNameInPage];
36
+ }
37
+
38
+ export function scrollTopInPage(this: Element|Document): number {
39
+ if ('scrollingElement' in this) {
40
+ if (!this.scrollingElement) {
41
+ return 0;
42
+ }
43
+
44
+ return this.scrollingElement.scrollTop;
45
+ }
46
+ return this.scrollTop;
47
+ }
48
+
49
+ export function scrollLeftInPage(this: Element|Document): number {
50
+ if ('scrollingElement' in this) {
51
+ if (!this.scrollingElement) {
52
+ return 0;
53
+ }
54
+
55
+ return this.scrollingElement.scrollLeft;
56
+ }
57
+ return this.scrollLeft;
58
+ }
59
+
60
+ export function setScrollTopInPage(this: Element|Document, offsetInPage: number): void {
61
+ if ('scrollingElement' in this) {
62
+ if (!this.scrollingElement) {
63
+ return;
64
+ }
65
+
66
+ this.scrollingElement.scrollTop = offsetInPage;
67
+ } else {
68
+ this.scrollTop = offsetInPage;
69
+ }
70
+ }
71
+
72
+ export function setScrollLeftInPage(this: Element|Document, offsetInPage: number): void {
73
+ if ('scrollingElement' in this) {
74
+ if (!this.scrollingElement) {
75
+ return;
76
+ }
77
+
78
+ this.scrollingElement.scrollLeft = offsetInPage;
79
+ } else {
80
+ this.scrollLeft = offsetInPage;
81
+ }
82
+ }
83
+
84
+ export function verticalScrollRangeInPage(this: Element|Document): number {
85
+ if ('scrollingElement' in this) {
86
+ if (!this.scrollingElement) {
87
+ return 0;
88
+ }
89
+
90
+ return this.scrollingElement.scrollHeight - this.scrollingElement.clientHeight;
91
+ }
92
+
93
+ return this.scrollHeight - this.clientHeight;
94
+ }
95
+
96
+ export function horizontalScrollRangeInPage(this: Element|Document): number {
97
+ if ('scrollingElement' in this) {
98
+ if (!this.scrollingElement) {
99
+ return 0;
100
+ }
101
+
102
+ return this.scrollingElement.scrollWidth - this.scrollingElement.clientWidth;
103
+ }
104
+
105
+ return this.scrollWidth - this.clientWidth;
106
+ }
107
+
108
+ export function toggleClassAndInjectStyleRule(this: Element, pseudoElementName: string|null, hidden: boolean): void {
109
+ const classNamePrefix = '__web-inspector-hide';
110
+ const classNameSuffix = '-shortcut__';
111
+ const styleTagId = '__web-inspector-hide-shortcut-style__';
112
+ const pseudoElementNameEscaped = pseudoElementName ? pseudoElementName.replace(/[\(\)\:]/g, '_') : '';
113
+ const className = classNamePrefix + pseudoElementNameEscaped + classNameSuffix;
114
+ this.classList.toggle(className, hidden);
115
+
116
+ let localRoot: Element|HTMLHeadElement = this;
117
+ while (localRoot.parentNode) {
118
+ localRoot = (localRoot.parentNode as Element);
119
+ }
120
+ if (localRoot.nodeType === Node.DOCUMENT_NODE) {
121
+ localRoot = document.head;
122
+ }
123
+
124
+ let style = localRoot.querySelector('style#' + styleTagId);
125
+ if (!style) {
126
+ const selectors = [];
127
+ selectors.push('.__web-inspector-hide-shortcut__');
128
+ selectors.push('.__web-inspector-hide-shortcut__ *');
129
+ const selector = selectors.join(', ');
130
+ const ruleBody = ' visibility: hidden !important;';
131
+ const rule = '\n' + selector + '\n{\n' + ruleBody + '\n}\n';
132
+
133
+ style = document.createElement('style');
134
+ style.id = styleTagId;
135
+ style.textContent = rule;
136
+
137
+ localRoot.appendChild(style);
138
+ }
139
+
140
+ // In addition to putting them on the element we want to hide, we will
141
+ // also add pseudo element classes to the style element to keep track of
142
+ // which pseudo elements we have style rules for.
143
+ if (pseudoElementName && !style.classList.contains(className)) {
144
+ style.classList.add(className);
145
+ style.textContent = `.${className}${pseudoElementName}, ${style.textContent}`;
146
+ }
147
+ }
148
+
149
+ export function scrollIntoViewInPage(this: Element): void {
150
+ this.scrollIntoViewIfNeeded(true);
151
+ }
152
+
153
+ export function focusInPage(this: HTMLElement): void {
154
+ this.focus();
155
+ }
156
+
157
+ export function toStringForClipboard(this: Object, data: {
158
+ subtype: string|undefined,
159
+ indent: string,
160
+ }): string|undefined {
161
+ const subtype = data.subtype;
162
+ const indent = data.indent;
163
+
164
+ if (subtype === 'node') {
165
+ return this instanceof Element ? this.outerHTML : undefined;
166
+ }
167
+ if (subtype && typeof this === 'undefined') {
168
+ return String(subtype);
169
+ }
170
+ try {
171
+ return JSON.stringify(this, null, indent);
172
+ } catch {
173
+ return String(this);
174
+ }
175
+ }
176
+
177
+ export function saveVariable(this: Window, value: Protocol.Runtime.CallArgument): string {
178
+ const prefix = 'temp';
179
+ let index = 1;
180
+ while ((prefix + index) in this) {
181
+ ++index;
182
+ }
183
+ const name = prefix + index;
184
+ // @ts-expect-error Assignment to global object
185
+ this[name] = value;
186
+ return name;
187
+ }
@@ -199,7 +199,7 @@ export class Resource implements TextUtils.ContentProvider.ContentProvider {
199
199
  return TextUtils.TextUtils.performSearchInSearchMatches(result.result || [], query, caseSensitive, isRegex);
200
200
  }
201
201
 
202
- async populateImageSource(image: HTMLImageElement): Promise<void> {
202
+ async populateImageSource(image: {src: string}): Promise<void> {
203
203
  const contentData = await this.requestContentData();
204
204
  if (TextUtils.ContentData.ContentData.isError(contentData)) {
205
205
  return;
@@ -10,6 +10,7 @@ import type * as Platform from '../platform/platform.js';
10
10
 
11
11
  import {DebuggerModel, type FunctionDetails} from './DebuggerModel.js';
12
12
  import {HeapProfilerModel} from './HeapProfilerModel.js';
13
+ import {toStringForClipboard} from './PageFunctions.js';
13
14
  import {
14
15
  RemoteFunction,
15
16
  RemoteObject,
@@ -313,26 +314,6 @@ export class RuntimeModel extends SDKModel<EventTypes> {
313
314
  }])
314
315
  .then(Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText.bind(
315
316
  Host.InspectorFrontendHost.InspectorFrontendHostInstance));
316
-
317
- function toStringForClipboard(this: Object, data: {
318
- subtype: string,
319
- indent: string,
320
- }): string|undefined {
321
- const subtype = data.subtype;
322
- const indent = data.indent;
323
-
324
- if (subtype === 'node') {
325
- return this instanceof Element ? this.outerHTML : undefined;
326
- }
327
- if (subtype && typeof this === 'undefined') {
328
- return String(subtype);
329
- }
330
- try {
331
- return JSON.stringify(this, null, indent);
332
- } catch {
333
- return String(this);
334
- }
335
- }
336
317
  }
337
318
 
338
319
  private async queryObjectsRequested(object: RemoteObject, executionContextId?: number): Promise<void> {
@@ -43,6 +43,7 @@ import type {FrameAssociated} from './FrameAssociated.js';
43
43
  import type {PageResourceLoadInitiator} from './PageResourceLoader.js';
44
44
  import {ResourceTreeModel} from './ResourceTreeModel.js';
45
45
  import type {ExecutionContext} from './RuntimeModel.js';
46
+ import {SecurityOrigin} from './SecurityOrigin.js';
46
47
  import type {DebugId, SourceMap} from './SourceMap.js';
47
48
  import type {Target} from './Target.js';
48
49
 
@@ -88,6 +89,7 @@ export class Script implements TextUtils.ContentProvider.ContentProvider, FrameA
88
89
  readonly #language: string|null;
89
90
  #contentPromise: Promise<TextUtils.ContentData.ContentDataOrError>|null;
90
91
  readonly #embedderName: Platform.DevToolsPath.UrlString|null;
92
+ readonly #securityOrigin: SecurityOrigin;
91
93
  readonly isModule: boolean|null;
92
94
  readonly buildId: string|null;
93
95
  constructor(debuggerModel: DebuggerModel, scriptId: Protocol.Runtime.ScriptId,
@@ -119,12 +121,31 @@ export class Script implements TextUtils.ContentProvider.ContentProvider, FrameA
119
121
  this.#language = scriptLanguage;
120
122
  this.#contentPromise = null;
121
123
  this.#embedderName = embedderName;
124
+ this.#securityOrigin = SecurityOrigin.create(this.#embedderName ?? '');
122
125
  }
123
126
 
124
127
  embedderName(): Platform.DevToolsPath.UrlString|null {
125
128
  return this.#embedderName;
126
129
  }
127
130
 
131
+ /**
132
+ * Returns the security origin of the script derived exclusively from its
133
+ * embedder/network URL (`#embedderName`), or a unique opaque origin if the
134
+ * script has no valid network provenance (e.g. `eval()` or buffer-based Wasm).
135
+ *
136
+ * Security note: Do NOT fall back to `this.sourceURL` or
137
+ * `this.target().inspectedSecurityOrigin()`:
138
+ * - `sourceURL` is overwritten by `//# sourceURL=` comments, allowing a script
139
+ * to spoof an arbitrary origin.
140
+ * - Third-party scripts (`<script src="https://attacker.example/...">`) run in
141
+ * the same target/frame as the main page; falling back to the target's
142
+ * origin would allow them to launder their origin by dynamically evaluating
143
+ * code via `eval()` or `WebAssembly.instantiate(buffer)`.
144
+ */
145
+ securityOrigin(): SecurityOrigin {
146
+ return this.#securityOrigin;
147
+ }
148
+
128
149
  target(): Target {
129
150
  return this.debuggerModel.target();
130
151
  }
@@ -130,6 +130,12 @@ interface SourceInfo {
130
130
  reverseMappings: number[]|null;
131
131
  }
132
132
 
133
+ export const enum SourceMapProvenance {
134
+ CDP = 'cdp',
135
+ EXTENSION = 'extension',
136
+ USER = 'user',
137
+ }
138
+
133
139
  export class SourceMap {
134
140
  static retainRawSourceMaps = false;
135
141
 
@@ -149,13 +155,15 @@ export class SourceMap {
149
155
 
150
156
  #scopesFallbackPromise?: Promise<void>;
151
157
  readonly #console: Common.Console.Console;
158
+ readonly #provenance: SourceMapProvenance;
152
159
 
153
160
  /**
154
161
  * Implements Source Map V3 model. See https://github.com/google/closure-compiler/wiki/Source-Maps
155
162
  * for format description.
156
163
  */
157
164
  constructor(compiledURL: Platform.DevToolsPath.UrlString, sourceMappingURL: Platform.DevToolsPath.UrlString,
158
- payload: SourceMapV3, console: Common.Console.Console, script?: Script) {
165
+ payload: SourceMapV3, console: Common.Console.Console, script?: Script,
166
+ provenance: SourceMapProvenance = SourceMapProvenance.CDP) {
159
167
  this.#json = payload;
160
168
  this.#script = script;
161
169
  this.#compiledURL = compiledURL;
@@ -163,6 +171,7 @@ export class SourceMap {
163
171
  this.#baseURL = (Common.ParsedURL.schemeIs(sourceMappingURL, 'data:')) ? compiledURL : sourceMappingURL;
164
172
  this.#debugId = 'debugId' in payload ? (payload.debugId as DebugId | undefined) : undefined;
165
173
  this.#console = console;
174
+ this.#provenance = provenance;
166
175
 
167
176
  if ('sections' in this.#json) {
168
177
  if (this.#json.sections.find(section => 'url' in section)) {
@@ -172,6 +181,10 @@ export class SourceMap {
172
181
  this.eachSection(this.parseSources.bind(this));
173
182
  }
174
183
 
184
+ provenance(): SourceMapProvenance {
185
+ return this.#provenance;
186
+ }
187
+
175
188
  json(): SourceMapV3|null {
176
189
  return this.#json;
177
190
  }
@@ -823,7 +836,7 @@ export class SourceMap {
823
836
 
824
837
  resolveScopeChain(frame: CallFrame): ScopeChainEntry[]|null {
825
838
  this.#ensureSourceMapProcessed();
826
- if (this.#scopesInfo === null) {
839
+ if (this.#provenance === SourceMapProvenance.USER || !this.#scopesInfo?.hasVariablesAndBindings()) {
827
840
  return null;
828
841
  }
829
842
 
@@ -2,15 +2,14 @@
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
4
 
5
- import type * as Platform from '../platform/platform.js';
5
+ import * as Platform from '../platform/platform.js';
6
6
 
7
7
  import type {DebugId, SourceMapV3} from './SourceMap.js';
8
8
 
9
9
  /** A thin wrapper around the Cache API to store source map JSONs keyed on Debug IDs */
10
10
  export class SourceMapCache {
11
11
  static create(): SourceMapCache {
12
- if (typeof window === 'undefined') {
13
- // TODO(crbug.com/451502260): Move this behind a `HostRuntime` interface.
12
+ if (!Platform.HostRuntime.HOST_RUNTIME.getCacheStorage()) {
14
13
  return IN_MEMORY_INSTANCE as unknown as
15
14
  SourceMapCache; // TS doesn't like that our in-memory class doesn't have the same private fields.
16
15
  }
@@ -22,7 +21,7 @@ export class SourceMapCache {
22
21
  }
23
22
 
24
23
  readonly #name: string;
25
- #cachePromise?: Promise<Cache>;
24
+ #cachePromise?: Promise<Platform.HostRuntime.CacheEntry>;
26
25
 
27
26
  private constructor(name: string) {
28
27
  this.#name = name;
@@ -30,21 +29,25 @@ export class SourceMapCache {
30
29
 
31
30
  async set(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString, sourceMap: SourceMapV3): Promise<void> {
32
31
  const cache = await this.#cache();
33
- await cache.put(SourceMapCache.#urlForDebugId(debugId, securityOrigin), new Response(JSON.stringify(sourceMap)));
32
+ await cache?.put(SourceMapCache.#urlForDebugId(debugId, securityOrigin), new Response(JSON.stringify(sourceMap)));
34
33
  }
35
34
 
36
35
  async get(debugId: DebugId, securityOrigin: Platform.DevToolsPath.UrlString): Promise<SourceMapV3|null> {
37
36
  const cache = await this.#cache();
38
- const response = await cache.match(SourceMapCache.#urlForDebugId(debugId, securityOrigin));
39
- return await response?.json() ?? null;
37
+ const response = await cache?.match(SourceMapCache.#urlForDebugId(debugId, securityOrigin));
38
+ return (await response?.json() as SourceMapV3 | undefined) ?? null;
40
39
  }
41
40
 
42
- async #cache(): Promise<Cache> {
41
+ async #cache(): Promise<Platform.HostRuntime.CacheEntry|undefined> {
43
42
  if (this.#cachePromise) {
44
43
  return await this.#cachePromise;
45
44
  }
46
45
 
47
- this.#cachePromise = window.caches.open(this.#name);
46
+ const cacheStorage = Platform.HostRuntime.HOST_RUNTIME.getCacheStorage();
47
+ if (!cacheStorage) {
48
+ return undefined;
49
+ }
50
+ this.#cachePromise = cacheStorage.open(this.#name);
48
51
  return await this.#cachePromise;
49
52
  }
50
53
 
@@ -54,7 +57,7 @@ export class SourceMapCache {
54
57
  }
55
58
 
56
59
  async disposeForTest(): Promise<void> {
57
- await window.caches.delete(this.#name);
60
+ await Platform.HostRuntime.HOST_RUNTIME.getCacheStorage()?.delete(this.#name);
58
61
  }
59
62
  }
60
63