chrome-devtools-frontend 1.0.1704172 → 1.0.1706026

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 (100) hide show
  1. package/AUTHORS +1 -0
  2. package/docs/architecture_of_devtools.md +1 -1
  3. package/docs/cookbook/localization.md +2 -2
  4. package/eslint.config.mjs +1 -0
  5. package/front_end/Images/src/nodejsHex.svg +28 -0
  6. package/front_end/core/common/README.md +4 -3
  7. package/front_end/core/sdk/CPUThrottlingManager.ts +14 -10
  8. package/front_end/core/sdk/DOMModel.ts +65 -20
  9. package/front_end/core/sdk/DebuggerModel.ts +0 -4
  10. package/front_end/core/sdk/PageResourceLoader.ts +12 -11
  11. package/front_end/core/sdk/SourceMap.ts +7 -5
  12. package/front_end/core/sdk/SourceMapFunctionRanges.ts +22 -37
  13. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +3 -3
  14. package/front_end/core/sdk/SourceMapScopesInfo.ts +50 -62
  15. package/front_end/entrypoints/formatter_actions/formatter_actions.ts +18 -0
  16. package/front_end/entrypoints/formatter_worker/Substitute.ts +9 -8
  17. package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +9 -4
  18. package/front_end/entrypoints/heap_snapshot_worker/ContextAnalyzer.ts +140 -20
  19. package/front_end/entrypoints/heap_snapshot_worker/HeapSnapshotLoader.ts +16 -5
  20. package/front_end/entrypoints/inspector_main/nodeIcon.css +1 -1
  21. package/front_end/foundation/Universe.ts +9 -0
  22. package/front_end/generated/InspectorBackendCommands.ts +4 -3
  23. package/front_end/generated/protocol.ts +17 -1
  24. package/front_end/models/ai_assistance/AiConversation.ts +16 -3
  25. package/front_end/models/ai_assistance/skills/accessibility.md +8 -7
  26. package/front_end/models/formatter/FormatterWorkerPool.ts +14 -4
  27. package/front_end/models/javascript_metadata/NativeFunctions.ts +6 -2
  28. package/front_end/models/source_map_scopes/NamesResolver.ts +69 -42
  29. package/front_end/models/source_map_scopes/ScopeChainModel.ts +13 -45
  30. package/front_end/models/source_map_scopes/ScopeChainResolver.ts +101 -0
  31. package/front_end/models/source_map_scopes/source_map_scopes.ts +2 -0
  32. package/front_end/panels/application/BackgroundServiceView.ts +1 -0
  33. package/front_end/panels/application/components/AdsView.ts +6 -6
  34. package/front_end/panels/application/components/BackForwardCacheView.ts +1 -1
  35. package/front_end/panels/console/ConsolePrompt.ts +2 -1
  36. package/front_end/panels/elements/CSSRuleValidator.ts +3 -0
  37. package/front_end/panels/elements/ClassesPaneWidget.ts +4 -1
  38. package/front_end/panels/elements/LayoutPane.ts +17 -20
  39. package/front_end/panels/network/NetworkLogViewColumns.ts +10 -3
  40. package/front_end/panels/network/ShowMoreDetailsWidget.ts +1 -1
  41. package/front_end/panels/security/SecurityPanel.ts +14 -7
  42. package/front_end/panels/sources/BreakpointEditDialog.ts +13 -1
  43. package/front_end/panels/sources/DebuggerPlugin.ts +69 -58
  44. package/front_end/panels/sources/EditingLocationHistoryManager.ts +6 -6
  45. package/front_end/panels/sources/OpenFileQuickOpen.ts +2 -2
  46. package/front_end/panels/sources/Plugin.ts +3 -2
  47. package/front_end/panels/sources/ResourceOriginPlugin.ts +14 -17
  48. package/front_end/panels/sources/ScopeChainSidebarPane.ts +2 -2
  49. package/front_end/panels/sources/SourcesPanel.ts +1 -1
  50. package/front_end/panels/sources/SourcesView.ts +25 -82
  51. package/front_end/panels/sources/TabbedEditorContainer.ts +75 -21
  52. package/front_end/panels/sources/UISourceCodeFrame.ts +22 -13
  53. package/front_end/panels/sources/sourcesView.css +17 -27
  54. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +1 -0
  55. package/front_end/third_party/chromium/README.chromium +1 -1
  56. package/front_end/third_party/source-map-scopes-codec/README.chromium +2 -2
  57. package/front_end/third_party/source-map-scopes-codec/package/CONTRIBUTING.md +14 -1
  58. package/front_end/third_party/source-map-scopes-codec/package/README.md +8 -6
  59. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/builder.d.ts +4 -1
  60. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/builder.d.ts.map +1 -1
  61. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/safe_builder.d.ts +3 -1
  62. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/builder/safe_builder.d.ts.map +1 -1
  63. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/decode/decode.d.ts.map +1 -1
  64. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/scopes.d.ts +4 -3
  65. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/scopes.d.ts.map +1 -1
  66. package/front_end/third_party/source-map-scopes-codec/package/deno.json +6 -2
  67. package/front_end/third_party/source-map-scopes-codec/package/package.json +1 -1
  68. package/front_end/third_party/source-map-scopes-codec/package/src/builder/builder.js +18 -2
  69. package/front_end/third_party/source-map-scopes-codec/package/src/builder/builder.js.map +1 -1
  70. package/front_end/third_party/source-map-scopes-codec/package/src/builder/builder.ts +22 -3
  71. package/front_end/third_party/source-map-scopes-codec/package/src/builder/safe_builder.js +33 -5
  72. package/front_end/third_party/source-map-scopes-codec/package/src/builder/safe_builder.js.map +1 -1
  73. package/front_end/third_party/source-map-scopes-codec/package/src/builder/safe_builder.ts +41 -5
  74. package/front_end/third_party/source-map-scopes-codec/package/src/codec.js +0 -2
  75. package/front_end/third_party/source-map-scopes-codec/package/src/codec.js.map +1 -1
  76. package/front_end/third_party/source-map-scopes-codec/package/src/codec.ts +4 -3
  77. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +99 -36
  78. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js.map +1 -1
  79. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.ts +143 -49
  80. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encode.js +3 -1
  81. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encode.js.map +1 -1
  82. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encode.ts +3 -1
  83. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js +40 -18
  84. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.js.map +1 -1
  85. package/front_end/third_party/source-map-scopes-codec/package/src/encode/encoder.ts +51 -21
  86. package/front_end/third_party/source-map-scopes-codec/package/src/scopes.ts +4 -3
  87. package/front_end/third_party/source-map-scopes-codec/package/src/vlq.js +18 -4
  88. package/front_end/third_party/source-map-scopes-codec/package/src/vlq.js.map +1 -1
  89. package/front_end/third_party/source-map-scopes-codec/package/src/vlq.ts +18 -4
  90. package/front_end/ui/comments/CommentOverlayManager.ts +23 -10
  91. package/front_end/ui/components/text_editor/javascript.ts +128 -52
  92. package/front_end/ui/legacy/README.md +12 -12
  93. package/front_end/ui/legacy/SearchableView.ts +125 -74
  94. package/front_end/ui/legacy/components/source_frame/FontView.ts +5 -5
  95. package/front_end/ui/legacy/components/source_frame/ImageView.ts +13 -13
  96. package/front_end/ui/legacy/components/source_frame/ResourceSourceFrame.ts +2 -6
  97. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +3 -2
  98. package/mcp/mcp.ts +1 -0
  99. package/package.json +1 -1
  100. package/front_end/Images/nodeIcon.avif +0 -0
@@ -0,0 +1,101 @@
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 * as Common from '../../core/common/common.js';
6
+ import * as Root from '../../core/root/root.js';
7
+ import * as SDK from '../../core/sdk/sdk.js';
8
+ import type * as Bindings from '../bindings/bindings.js';
9
+
10
+ import {resolveScopeChain} from './NamesResolver.js';
11
+
12
+ /**
13
+ * Resolves and caches the scope chain for {@link SDK.DebuggerModel.CallFrame}s.
14
+ *
15
+ * There is one instance per `Universe`. Caching the resolved scope chain allows different consumers
16
+ * (e.g. the Scope view and the inline values in the editor) to share the exact same
17
+ * {@link SDK.DebuggerModel.ScopeChainEntry} and {@link SDK.RemoteObject.RemoteObject} instances for a
18
+ * given pause, without redundant CDP evaluations.
19
+ *
20
+ * The cache for a script is invalidated whenever debugging info (e.g. from a debugger language plugin)
21
+ * or a source map is attached to or detached from that script. The {@link Events.SCOPE_CHAIN_INVALIDATED}
22
+ * event is dispatched afterwards, so consumers can re-resolve the scope chain.
23
+ */
24
+ export class ScopeChainResolver extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements
25
+ SDK.TargetManager.SDKModelObserver<SDK.DebuggerModel.DebuggerModel> {
26
+ readonly #debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding;
27
+ readonly #cache = new WeakMap<SDK.Script.Script,
28
+ WeakMap<SDK.DebuggerModel.CallFrame, Promise<SDK.DebuggerModel.ScopeChainEntry[]>>>();
29
+
30
+ constructor(targetManager: SDK.TargetManager.TargetManager,
31
+ debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding) {
32
+ super();
33
+ this.#debuggerWorkspaceBinding = debuggerWorkspaceBinding;
34
+ targetManager.observeModels(SDK.DebuggerModel.DebuggerModel, this);
35
+ }
36
+
37
+ /**
38
+ * @deprecated Pass the `ScopeChainResolver` of the `Universe` via constructor instead.
39
+ */
40
+ static instance(): ScopeChainResolver {
41
+ // TODO(crbug.com/458180550): Remove once all callers receive the instance via constructor.
42
+ return Root.DevToolsContext.globalInstance().get(ScopeChainResolver);
43
+ }
44
+
45
+ modelAdded(debuggerModel: SDK.DebuggerModel.DebuggerModel): void {
46
+ debuggerModel.addEventListener(SDK.DebuggerModel.Events.DebugInfoAttached, this.#debugInfoAttached, this);
47
+ debuggerModel.sourceMapManager().addEventListener(SDK.SourceMapManager.Events.SourceMapAttached,
48
+ this.#sourceMapChanged, this);
49
+ debuggerModel.sourceMapManager().addEventListener(SDK.SourceMapManager.Events.SourceMapDetached,
50
+ this.#sourceMapChanged, this);
51
+ }
52
+
53
+ modelRemoved(debuggerModel: SDK.DebuggerModel.DebuggerModel): void {
54
+ debuggerModel.removeEventListener(SDK.DebuggerModel.Events.DebugInfoAttached, this.#debugInfoAttached, this);
55
+ debuggerModel.sourceMapManager().removeEventListener(SDK.SourceMapManager.Events.SourceMapAttached,
56
+ this.#sourceMapChanged, this);
57
+ debuggerModel.sourceMapManager().removeEventListener(SDK.SourceMapManager.Events.SourceMapDetached,
58
+ this.#sourceMapChanged, this);
59
+ }
60
+
61
+ /**
62
+ * Returns the (cached) resolved scope chain for `callFrame`. Repeated calls return the same promise
63
+ * until the cache for the call frame's script is invalidated.
64
+ */
65
+ resolveScopeChain(callFrame: SDK.DebuggerModel.CallFrame): Promise<SDK.DebuggerModel.ScopeChainEntry[]> {
66
+ let cacheForScript = this.#cache.get(callFrame.script);
67
+ if (!cacheForScript) {
68
+ cacheForScript = new WeakMap();
69
+ this.#cache.set(callFrame.script, cacheForScript);
70
+ }
71
+ let cachedPromise = cacheForScript.get(callFrame);
72
+ if (!cachedPromise) {
73
+ cachedPromise = resolveScopeChain(callFrame, this.#debuggerWorkspaceBinding);
74
+ cacheForScript.set(callFrame, cachedPromise);
75
+ }
76
+ return cachedPromise;
77
+ }
78
+
79
+ #invalidate(script: SDK.Script.Script): void {
80
+ this.#cache.delete(script);
81
+ this.dispatchEventToListeners(Events.SCOPE_CHAIN_INVALIDATED, script);
82
+ }
83
+
84
+ #debugInfoAttached(event: Common.EventTarget.EventTargetEvent<SDK.Script.Script>): void {
85
+ this.#invalidate(event.data);
86
+ }
87
+
88
+ #sourceMapChanged(event: Common.EventTarget
89
+ .EventTargetEvent<{client: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap}>): void {
90
+ this.#invalidate(event.data.client);
91
+ }
92
+ }
93
+
94
+ export const enum Events {
95
+ SCOPE_CHAIN_INVALIDATED = 'ScopeChainInvalidated',
96
+ }
97
+
98
+ export interface EventTypes {
99
+ /** Dispatched with the script for which the cached scope chains have been invalidated. */
100
+ [Events.SCOPE_CHAIN_INVALIDATED]: SDK.Script.Script;
101
+ }
@@ -5,9 +5,11 @@
5
5
  import * as FunctionCodeResolver from './FunctionCodeResolver.js';
6
6
  import * as NamesResolver from './NamesResolver.js';
7
7
  import * as ScopeChainModel from './ScopeChainModel.js';
8
+ import * as ScopeChainResolver from './ScopeChainResolver.js';
8
9
 
9
10
  export {
10
11
  FunctionCodeResolver,
11
12
  NamesResolver,
12
13
  ScopeChainModel,
14
+ ScopeChainResolver,
13
15
  };
@@ -120,6 +120,7 @@ const UIStrings = {
120
120
  * @description Inform users that DevTools are recording/waiting for events in the Periodic Background Sync tool of the Application panel
121
121
  * @example {Background Fetch} PH1
122
122
  */
123
+ // eslint-disable-next-line @devtools/l10n-uistrings-sentence-punctuation -- Concatenated with recordingSActivity in UI to form a multi-sentence message.
123
124
  devtoolsWillRecordAllSActivity: 'DevTools will record all {PH1} activity for up to 3 days, even when closed.',
124
125
  /**
125
126
  * @description Text in Background Service View of the Application panel to instruct the user on how to start a recording for
@@ -147,13 +147,13 @@ const UIStrings = {
147
147
  settings: 'Settings',
148
148
  /**
149
149
  * @description The name of a checkbox setting. This setting highlights the
150
- * rendering elements for ads that are found on the page.
150
+ * rendering elements for ad-related resources that are found on the page.
151
151
  */
152
- highlightAds: 'Highlight ads',
152
+ highlightAdRelatedResources: 'Highlight ad-related resources',
153
153
  /**
154
- * @description Explanation text for the 'Highlight ads' setting.
154
+ * @description Explanation text for the 'Highlight ad-related resources' setting.
155
155
  */
156
- highlightsElementsRedDetectedToBe: 'Highlights elements (red) detected to be ads',
156
+ highlightsElementsRedDetectedToBeAdRelated: 'Highlights elements (red) detected to be ad-related',
157
157
  /**
158
158
  * @description Text explaining that ad detection is not perfect.
159
159
  */
@@ -425,8 +425,8 @@ const DEFAULT_VIEW: View = (input, output, target) => {
425
425
  <devtools-checkbox class="setting-container small"
426
426
  ${bindToSetting(Common.Settings.Settings.instance().resolve(SDK.SDKSettings.showAdHighlightsSettingDescriptor))}>
427
427
  <div class="setting-text-container">
428
- <div class="setting-label">${i18nString(UIStrings.highlightAds)}</div>
429
- <div class="setting-explanation">${i18nString(UIStrings.highlightsElementsRedDetectedToBe)}</div>
428
+ <div class="setting-label">${i18nString(UIStrings.highlightAdRelatedResources)}</div>
429
+ <div class="setting-explanation">${i18nString(UIStrings.highlightsElementsRedDetectedToBeAdRelated)}</div>
430
430
  </div>
431
431
  </devtools-checkbox>
432
432
  <hr class="divider">
@@ -129,7 +129,7 @@ const UIStrings = {
129
129
  * 'm' is never less than 2.
130
130
  * @example {3} m
131
131
  */
132
- issuesInMultipleFrames: '{n, plural, =1 {# issue found in {m} frames.} other {# issues found in {m} frames.}}',
132
+ issuesInMultipleFrames: '{n, plural, =1 {# issue found in {m} frames} other {# issues found in {m} frames}}',
133
133
  /**
134
134
  * @description Shows the number of frames with a particular issue.
135
135
  */
@@ -459,7 +459,8 @@ export class ConsolePrompt extends ConsolePromptBase {
459
459
  ?.evaluateCommandInConsole(executionContext, message, expression, useCommandLineAPI);
460
460
  }
461
461
 
462
- private async substituteNames(expression: string, mapping: Array<Map<string, string|null>>): Promise<string> {
462
+ private async substituteNames(expression: string,
463
+ mapping: Formatter.FormatterWorkerPool.ScopeVariableMapping[]): Promise<string> {
463
464
  try {
464
465
  return await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptSubstitute(expression, mapping);
465
466
  } catch {
@@ -26,6 +26,7 @@ const UIStrings = {
26
26
  * @example {flex-wrap: nowrap} REASON_PROPERTY_DECLARATION_CODE
27
27
  * @example {align-content} AFFECTED_PROPERTY_DECLARATION_CODE
28
28
  */
29
+ // eslint-disable-next-line @devtools/l10n-uistrings-sentence-punctuation -- Two sentences displayed together in Styles pane
29
30
  ruleViolatedBySameElementRuleReason:
30
31
  'The {REASON_PROPERTY_DECLARATION_CODE} property prevents {AFFECTED_PROPERTY_DECLARATION_CODE} from having an effect.',
31
32
  /**
@@ -33,6 +34,7 @@ const UIStrings = {
33
34
  * @example {flex-wrap} PROPERTY_NAME
34
35
  * @example {nowrap} PROPERTY_VALUE
35
36
  */
37
+ // eslint-disable-next-line @devtools/l10n-uistrings-sentence-punctuation -- Two sentences displayed together in Styles pane
36
38
  ruleViolatedBySameElementRuleFix: 'Try setting {PROPERTY_NAME} to something other than {PROPERTY_VALUE}.',
37
39
  /**
38
40
  * @description The message shown in the Styles tab when the user hovers over a property declaration that has no effect due to not being a flex or grid container.
@@ -71,6 +73,7 @@ const UIStrings = {
71
73
  * @example {20} PH4
72
74
  * @example {Arial} PH5
73
75
  */
76
+ // eslint-disable-next-line @devtools/l10n-uistrings-sentence-punctuation -- Multiple warnings can be joined with a space in the UI to form a multi-sentence message
74
77
  fontVariationSettingsWarning:
75
78
  'Value for setting "{PH1}" {PH2} is outside the supported range [{PH3}, {PH4}] for font-family "{PH5}".',
76
79
  /**
@@ -359,7 +359,10 @@ export class ClassNamePrompt extends UI.TextPrompt.TextPrompt {
359
359
 
360
360
  let completions: string[] = await this.classNamesPromise;
361
361
  const classesMap = this.nodeClasses((selectedNode));
362
- const existingClasses = new Set(expression.split(/[,\s]/).map(className => className.trim()).filter(Boolean));
362
+ // Classes may be typed with a leading dot (e.g. `.btn`), while completions
363
+ // are raw class names, so normalize before de-duplicating.
364
+ const existingClasses =
365
+ new Set(expression.split(/[,\s]/).map(className => className.trim().replace(/^\./, '')).filter(Boolean));
363
366
  completions = completions.filter(value => !classesMap.get(value) && !existingClasses.has(value));
364
367
 
365
368
  if (prefix[0] === '.') {
@@ -366,15 +366,21 @@ const DEFAULT_VIEW: View = (input, output, target) => {
366
366
 
367
367
  type View = (input: ViewInput, output: object, element: HTMLElement) => void;
368
368
  export class LayoutPane extends UI.Widget.Widget {
369
- readonly #settings: readonly Setting[] = [];
369
+ readonly #settings: ReadonlyArray<Common.Settings.Setting<string|boolean>>;
370
370
  readonly #uaShadowDOMSetting: Common.Settings.Setting<boolean>;
371
371
  #domModels: SDK.DOMModel.DOMModel[];
372
372
  readonly #view: View;
373
373
 
374
374
  constructor(element?: HTMLElement, view: View = DEFAULT_VIEW) {
375
375
  super(element);
376
- this.#settings = this.#makeSettings();
377
- this.#uaShadowDOMSetting = Common.Settings.Settings.instance().moduleSetting('show-ua-shadow-dom');
376
+ const settings = Common.Settings.Settings.instance();
377
+ this.#settings = [
378
+ settings.resolve(SDK.SDKSettings.showGridLineLabelsSettingDescriptor),
379
+ settings.resolve(SDK.SDKSettings.showGridTrackSizesSettingDescriptor),
380
+ settings.resolve(SDK.SDKSettings.showGridAreasSettingDescriptor),
381
+ settings.resolve(SDK.SDKSettings.extendGridLinesSettingDescriptor),
382
+ ];
383
+ this.#uaShadowDOMSetting = settings.moduleSetting('show-ua-shadow-dom');
378
384
  this.#domModels = [];
379
385
  this.#view = view;
380
386
  }
@@ -445,10 +451,8 @@ export class LayoutPane extends UI.Widget.Widget {
445
451
  }
446
452
 
447
453
  #makeSettings(): Setting[] {
448
- const settings = [];
449
- for (const settingName
450
- of ['show-grid-line-labels', 'show-grid-track-sizes', 'show-grid-areas', 'extend-grid-lines']) {
451
- const setting = Common.Settings.Settings.instance().moduleSetting(settingName);
454
+ const settings: Setting[] = [];
455
+ for (const setting of this.#settings) {
452
456
  const settingValue = setting.get();
453
457
  const settingType = setting.type();
454
458
  if (!settingType) {
@@ -489,13 +493,13 @@ export class LayoutPane extends UI.Widget.Widget {
489
493
  }
490
494
 
491
495
  onSettingChanged(setting: string, value: string|boolean): void {
492
- Common.Settings.Settings.instance().moduleSetting(setting).set(value);
496
+ this.#settings.find(s => s.name === setting)?.set(value);
493
497
  }
494
498
 
495
499
  override wasShown(): void {
496
500
  super.wasShown();
497
501
  for (const setting of this.#settings) {
498
- Common.Settings.Settings.instance().moduleSetting(setting.name).addChangeListener(this.requestUpdate, this);
502
+ setting.addChangeListener(this.requestUpdate, this);
499
503
  }
500
504
  for (const domModel of this.#domModels) {
501
505
  this.modelRemoved(domModel);
@@ -510,7 +514,7 @@ export class LayoutPane extends UI.Widget.Widget {
510
514
  override willHide(): void {
511
515
  super.willHide();
512
516
  for (const setting of this.#settings) {
513
- Common.Settings.Settings.instance().moduleSetting(setting.name).removeChangeListener(this.requestUpdate, this);
517
+ setting.removeChangeListener(this.requestUpdate, this);
514
518
  }
515
519
  SDK.TargetManager.TargetManager.instance().unobserveModels(SDK.DOMModel.DOMModel, this);
516
520
  UI.Context.Context.instance().removeFlavorChangeListener(SDK.DOMModel.DOMNode, this.requestUpdate, this);
@@ -537,6 +541,7 @@ export class LayoutPane extends UI.Widget.Widget {
537
541
  }
538
542
 
539
543
  override async performUpdate(): Promise<void> {
544
+ const settings = this.#makeSettings();
540
545
  const input: ViewInput = {
541
546
  gridElements: gridNodesToElements(await this.#fetchGridNodes()),
542
547
  flexContainerElements: flexContainerNodesToElements(await this.#fetchFlexContainerNodes()),
@@ -547,22 +552,14 @@ export class LayoutPane extends UI.Widget.Widget {
547
552
  onMouseEnter: this.#onElementMouseEnter.bind(this),
548
553
  onElementToggle: this.#onElementToggle.bind(this),
549
554
  onBooleanSettingChange: this.#onBooleanSettingChange.bind(this),
550
- enumSettings: this.#getEnumSettings(),
551
- booleanSettings: this.#getBooleanSettings(),
555
+ enumSettings: settings.filter(isEnumSetting),
556
+ booleanSettings: settings.filter(isBooleanSetting),
552
557
  onSummaryKeyDown: this.#onSummaryKeyDown.bind(this),
553
558
  };
554
559
 
555
560
  this.#view(input, {}, this.contentElement);
556
561
  }
557
562
 
558
- #getEnumSettings(): EnumSetting[] {
559
- return this.#settings.filter(isEnumSetting);
560
- }
561
-
562
- #getBooleanSettings(): BooleanSetting[] {
563
- return this.#settings.filter(isBooleanSetting);
564
- }
565
-
566
563
  #onBooleanSettingChange(setting: BooleanSetting, event: Event): void {
567
564
  event.preventDefault();
568
565
  this.onSettingChanged(setting.name, (event.target as UI.UIUtils.CheckboxLabel).checked);
@@ -625,7 +625,7 @@ export class NetworkLogViewColumns {
625
625
  const setting = savedSettings[columnId];
626
626
  let columnConfig = this.columns.find(columnConfig => columnConfig.id === columnId);
627
627
  if (!columnConfig && setting.title) {
628
- columnConfig = this.addCustomHeader(setting.title, columnId) || undefined;
628
+ columnConfig = this.registerCustomHeader(setting.title, columnId) || undefined;
629
629
  }
630
630
 
631
631
  if (columnConfig) {
@@ -816,6 +816,15 @@ export class NetworkLogViewColumns {
816
816
  }
817
817
 
818
818
  private addCustomHeader(headerTitle: string, headerId?: string, index?: number): Descriptor|null {
819
+ const columnConfig = this.registerCustomHeader(headerTitle, headerId, index);
820
+ if (columnConfig) {
821
+ this.saveColumnsSettings();
822
+ this.updateColumns();
823
+ }
824
+ return columnConfig;
825
+ }
826
+
827
+ private registerCustomHeader(headerTitle: string, headerId?: string, index?: number): Descriptor|null {
819
828
  if (!headerId) {
820
829
  headerId = headerTitle;
821
830
  }
@@ -855,8 +864,6 @@ export class NetworkLogViewColumns {
855
864
  if (this.#dataGrid) {
856
865
  this.#dataGrid.addColumn(NetworkLogViewColumns.convertToDataGridDescriptor(columnConfig), index);
857
866
  }
858
- this.saveColumnsSettings();
859
- this.updateColumns();
860
867
  return columnConfig;
861
868
  }
862
869
 
@@ -35,7 +35,7 @@ export const DEFAULT_VIEW: View = (input, output, target) => {
35
35
  if (input.copy) {
36
36
  contextMenu.clipboardSection().appendItem(i18nString(UIStrings.copy), input.copy, {jslogContext: 'copy'});
37
37
  }
38
- if (!input.showMore) {
38
+ if (!input.showMore && input.text.length > MAX_LENGTH) {
39
39
  contextMenu.newSection().appendItem(i18nString(UIStrings.showMore), input.onToggle, {jslogContext: 'show-more'});
40
40
  }
41
41
  void contextMenu.show();
@@ -31,6 +31,7 @@ import {SecurityPanelSidebar} from './SecurityPanelSidebar.js';
31
31
 
32
32
  const {widget, widgetRef} = UI.Widget;
33
33
 
34
+ /* eslint-disable @devtools/l10n-uistrings-sentence-punctuation -- Kept with periods to avoid breaking Blink layout tests in chromium/src: http/tests/devtools/security/ */
34
35
  const UIStrings = {
35
36
  /**
36
37
  * @description Title of the overview section in the Security panel.
@@ -428,6 +429,7 @@ const UIStrings = {
428
429
  */
429
430
  enabled: 'enabled',
430
431
  } as const;
432
+ /* eslint-enable @devtools/l10n-uistrings-sentence-punctuation */
431
433
  const str_ = i18n.i18n.registerUIStrings('panels/security/SecurityPanel.ts', UIStrings);
432
434
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
433
435
 
@@ -1560,6 +1562,14 @@ function renderCertificateTransparencySection(input: CertificateTransparencySect
1560
1562
  // clang-format on
1561
1563
  }
1562
1564
 
1565
+ function renderNoteSection(loadedFromCache: boolean): TemplateResult {
1566
+ // clang-format off
1567
+ return html`
1568
+ ${loadedFromCache ? html`<div>${i18nString(UIStrings.thisResponseWasLoadedFromCache)}</div>` : nothing}
1569
+ <div>${i18nString(UIStrings.theSecurityDetailsAboveAreFrom)}</div>`;
1570
+ // clang-format on
1571
+ }
1572
+
1563
1573
  export class SecurityOriginView extends UI.Widget.VBox {
1564
1574
  readonly #origin: Platform.DevToolsPath.UrlString;
1565
1575
  readonly #titleSection: HTMLElement;
@@ -1584,16 +1594,13 @@ export class SecurityOriginView extends UI.Widget.VBox {
1584
1594
 
1585
1595
  const sctListLength = originState.securityDetails.signedCertificateTimestampList.length;
1586
1596
  const ctCompliance = originState.securityDetails.certificateTransparencyCompliance;
1587
- if (!sctListLength && ctCompliance === Protocol.Network.CertificateTransparencyCompliance.Unknown) {
1588
- return;
1597
+ if (sctListLength || ctCompliance !== Protocol.Network.CertificateTransparencyCompliance.Unknown) {
1598
+ this.#createCertificateTransparencySection(originState.securityDetails);
1589
1599
  }
1590
- this.#createCertificateTransparencySection(originState.securityDetails);
1591
1600
 
1592
1601
  const noteSection = this.element.createChild('div', 'origin-view-section origin-view-notes');
1593
- if (originState.loadedFromCache) {
1594
- noteSection.createChild('div').textContent = i18nString(UIStrings.thisResponseWasLoadedFromCache);
1595
- }
1596
- noteSection.createChild('div').textContent = i18nString(UIStrings.theSecurityDetailsAboveAreFrom);
1602
+ // eslint-disable-next-line @devtools/no-lit-render-outside-of-view
1603
+ render(renderNoteSection(originState.loadedFromCache), noteSection);
1597
1604
  } else if (originState.securityState === Protocol.Security.SecurityState.Secure) {
1598
1605
  // If the security state is secure but there are no security details,
1599
1606
  // this means that the origin is a non-cryptographic secure origin, e.g.
@@ -161,6 +161,7 @@ export class BreakpointEditDialog extends UI.Widget.Widget {
161
161
  #breakpointType: SDK.DebuggerModel.BreakpointType.LOGPOINT|SDK.DebuggerModel.BreakpointType.CONDITIONAL_BREAKPOINT =
162
162
  SDK.DebuggerModel.BreakpointType.CONDITIONAL_BREAKPOINT;
163
163
  #onFinish: (result: BreakpointEditDialogResult) => void = () => {};
164
+ #location?: () => Promise<SDK.DebuggerModel.Location|null>;
164
165
  #editor?: TextEditor.TextEditor.TextEditor;
165
166
  #state?: CodeMirror.EditorState;
166
167
 
@@ -207,6 +208,13 @@ export class BreakpointEditDialog extends UI.Widget.Widget {
207
208
  this.#onFinish = onFinish;
208
209
  this.requestUpdate();
209
210
  }
211
+ get location(): (() => Promise<SDK.DebuggerModel.Location|null>)|undefined {
212
+ return this.#location;
213
+ }
214
+ set location(location: (() => Promise<SDK.DebuggerModel.Location|null>)|undefined) {
215
+ this.#location = location;
216
+ this.requestUpdate();
217
+ }
210
218
 
211
219
  override focus(): void {
212
220
  void this.updateComplete.then(() => {
@@ -283,6 +291,9 @@ export class BreakpointEditDialog extends UI.Widget.Widget {
283
291
  },
284
292
  ];
285
293
 
294
+ const completionOptions: TextEditor.JavaScript.CompletionOptions = {
295
+ location: () => this.#location ? this.#location() : Promise.resolve(null),
296
+ };
286
297
  const editorConfig = [
287
298
  CodeMirror.javascript.javascriptLanguage,
288
299
  TextEditor.Config.baseConfiguration(this.oldCondition),
@@ -292,8 +303,9 @@ export class BreakpointEditDialog extends UI.Widget.Widget {
292
303
  TextEditor.Config.showCompletionHint,
293
304
  TextEditor.Config.conservativeCompletion,
294
305
  CodeMirror.javascript.javascriptLanguage.data.of({autocomplete}),
306
+ TextEditor.JavaScript.completion(completionOptions),
295
307
  CodeMirror.autocompletion(),
296
- TextEditor.JavaScript.argumentHints(),
308
+ TextEditor.JavaScript.argumentHints(completionOptions),
297
309
  ];
298
310
 
299
311
  this.#state = CodeMirror.EditorState.create({
@@ -611,7 +611,7 @@ export class DebuggerPlugin extends Plugin {
611
611
  tokenType === 'PropertyDefinition';
612
612
  }
613
613
 
614
- private getPopoverRequest(event: MouseEvent|KeyboardEvent): UI.PopoverHelper.PopoverRequest|null {
614
+ getPopoverRequest(event: MouseEvent|KeyboardEvent): UI.PopoverHelper.PopoverRequest|null {
615
615
  if (event instanceof KeyboardEvent) {
616
616
  return null;
617
617
  }
@@ -629,7 +629,6 @@ export class DebuggerPlugin extends Plugin {
629
629
  if (!debuggableFrame) {
630
630
  return null;
631
631
  }
632
- const selectedCallFrame = debuggableFrame.sdkFrame;
633
632
 
634
633
  let textPosition = editor.editor.posAtCoords(event);
635
634
  if (!textPosition) {
@@ -667,65 +666,17 @@ export class DebuggerPlugin extends Plugin {
667
666
  return {
668
667
  box,
669
668
  show: async (popover: UI.GlassPane.GlassPane) => {
670
- const scopeMappings = await this.#getScopeMappings(debuggableFrame) ?? [];
671
- const scopedVariable = findVariableInScopeMappings(evaluationText, highlightRange.from, scopeMappings);
672
- if (scopedVariable.found) {
673
- if (!scopedVariable.value) {
674
- return false;
675
- }
676
- objectPopoverHelper =
677
- await ObjectUI.ObjectPopoverHelper.ObjectPopoverHelper.buildObjectPopover(scopedVariable.value, popover);
678
- const potentiallyUpdatedCallFrame =
679
- UI.Context.Context.instance().flavor(StackTrace.StackTrace.DebuggableFrameFlavor);
680
- if (!objectPopoverHelper || debuggableFrame !== potentiallyUpdatedCallFrame) {
681
- objectPopoverHelper?.dispose();
682
- return false;
683
- }
684
- return true;
685
- }
686
-
687
- let resolvedText = '';
688
- if (selectedCallFrame.script.isJavaScript()) {
689
- const nameMap = await SourceMapScopes.NamesResolver.allVariablesInCallFrame(
690
- selectedCallFrame, Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance());
691
- try {
692
- resolvedText =
693
- await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptSubstitute(evaluationText, nameMap);
694
- } catch {
695
- }
696
- }
697
- // We use side-effect free debug-evaluate when the highlighted expression contains a
698
- // function/method call. Otherwise we allow side-effects. The motiviation here are
699
- // frameworks like Vue, that heavily use proxies for caching:
700
- //
701
- // * We deem a simple property access of a proxy as deterministic so it should be
702
- // successful even if V8 thinks its side-effecting.
703
- // * Explicit function calls on the other hand must be side-effect free. The canonical
704
- // example is hovering over {Math.random()} which would result in a different value
705
- // each time the user hovers over it.
706
- const throwOnSideEffect = highlightRange.containsSideEffects;
707
- const result = await selectedCallFrame.evaluate({
708
- expression: resolvedText || evaluationText,
709
- objectGroup: 'popover',
710
- includeCommandLineAPI: false,
711
- silent: true,
712
- returnByValue: false,
713
- generatePreview: false,
714
- throwOnSideEffect,
715
- });
716
- if (!result || 'error' in result || !result.object ||
717
- (result.object.type === 'object' && result.object.subtype === 'error')) {
669
+ const object = await this.#evaluateForPopover(debuggableFrame, highlightRange, evaluationText);
670
+ if (!object) {
718
671
  return false;
719
672
  }
720
673
  objectPopoverHelper =
721
- await ObjectUI.ObjectPopoverHelper.ObjectPopoverHelper.buildObjectPopover(result.object, popover);
674
+ await ObjectUI.ObjectPopoverHelper.ObjectPopoverHelper.buildObjectPopover(object, popover);
722
675
  const potentiallyUpdatedCallFrame =
723
676
  UI.Context.Context.instance().flavor(StackTrace.StackTrace.DebuggableFrameFlavor);
724
677
  if (!objectPopoverHelper || debuggableFrame !== potentiallyUpdatedCallFrame) {
725
678
  debuggerModel.runtimeModel().releaseObjectGroup('popover');
726
- if (objectPopoverHelper) {
727
- objectPopoverHelper.dispose();
728
- }
679
+ objectPopoverHelper?.dispose();
729
680
  return false;
730
681
  }
731
682
  const decoration = CodeMirror.Decoration.set(evalExpressionMark.range(highlightRange.from, highlightRange.to));
@@ -742,6 +693,52 @@ export class DebuggerPlugin extends Plugin {
742
693
  };
743
694
  }
744
695
 
696
+ async #evaluateForPopover(debuggableFrame: StackTrace.StackTrace.DebuggableFrameFlavor,
697
+ highlightRange: {from: number, to: number, containsSideEffects: boolean},
698
+ evaluationText: string): Promise<SDK.RemoteObject.RemoteObject|null> {
699
+ const scopeMappings = await this.#getScopeMappings(debuggableFrame) ?? [];
700
+ const scopedVariable = findVariableInScopeMappings(evaluationText, highlightRange.from, scopeMappings);
701
+ if (scopedVariable.found) {
702
+ return scopedVariable.value;
703
+ }
704
+
705
+ const selectedCallFrame = debuggableFrame.sdkFrame;
706
+ let resolvedText = '';
707
+ if (selectedCallFrame.script.isJavaScript()) {
708
+ const nameMap = await SourceMapScopes.NamesResolver.allVariablesInCallFrame(
709
+ selectedCallFrame, Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance());
710
+ try {
711
+ resolvedText =
712
+ await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptSubstitute(evaluationText, nameMap);
713
+ } catch {
714
+ }
715
+ }
716
+ // We use side-effect free debug-evaluate when the highlighted expression contains a
717
+ // function/method call. Otherwise we allow side-effects. The motivation here are
718
+ // frameworks like Vue, that heavily use proxies for caching:
719
+ //
720
+ // * We deem a simple property access of a proxy as deterministic so it should be
721
+ // successful even if V8 thinks its side-effecting.
722
+ // * Explicit function calls on the other hand must be side-effect free. The canonical
723
+ // example is hovering over {Math.random()} which would result in a different value
724
+ // each time the user hovers over it.
725
+ const throwOnSideEffect = highlightRange.containsSideEffects;
726
+ const result = await selectedCallFrame.evaluate({
727
+ expression: resolvedText || evaluationText,
728
+ objectGroup: 'popover',
729
+ includeCommandLineAPI: false,
730
+ silent: true,
731
+ returnByValue: false,
732
+ generatePreview: false,
733
+ throwOnSideEffect,
734
+ });
735
+ if (!result || 'error' in result || !result.object ||
736
+ (result.object.type === 'object' && result.object.subtype === 'error')) {
737
+ return null;
738
+ }
739
+ return result.object;
740
+ }
741
+
745
742
  private onEditorUpdate(update: CodeMirror.ViewUpdate): void {
746
743
  if (!update.changes.empty) {
747
744
  // If the document changed, adjust known breakpoint positions
@@ -863,6 +860,15 @@ export class DebuggerPlugin extends Plugin {
863
860
  dialog.oldCondition = oldCondition,
864
861
  dialog.breakpointType = isLogpointForDialog ? SDK.DebuggerModel.BreakpointType.LOGPOINT :
865
862
  SDK.DebuggerModel.BreakpointType.CONDITIONAL_BREAKPOINT;
863
+ dialog.location = async () => {
864
+ const uiLocation = breakpoint ? (breakpoint.getClosestResolvedLocation() ??
865
+ {lineNumber: breakpoint.lineNumber(), columnNumber: breakpoint.columnNumber()}) :
866
+ (location ?? await this.defaultBreakpointLocation(line));
867
+ const rawLocations =
868
+ await Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().uiLocationToRawLocations(
869
+ this.uiSourceCode, uiLocation.lineNumber, uiLocation.columnNumber);
870
+ return rawLocations[0] ?? null;
871
+ };
866
872
  dialog.onFinish = async result => {
867
873
  this.activeBreakpointDialog = null;
868
874
  this.#activeBreakpointEditRequest = undefined;
@@ -1002,8 +1008,8 @@ export class DebuggerPlugin extends Plugin {
1002
1008
 
1003
1009
  const uiPositionToEditorOffset = (lineNumber: number, columnNumber: number): number|null =>
1004
1010
  this.editor?.toOffset(this.transformer.uiLocationToEditorLocation(lineNumber, columnNumber)) ?? null;
1005
- const scopeChain = await SourceMapScopes.ScopeChainModel.ScopeChainModel.resolveScopeChain(
1006
- callFrame, Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance());
1011
+ const scopeChain =
1012
+ await SourceMapScopes.ScopeChainResolver.ScopeChainResolver.instance().resolveScopeChain(callFrame);
1007
1013
  const localOriginalScope = scopeChain
1008
1014
  .find((s): s is SDK.SourceMapScopeChainEntry.SourceMapScopeChainEntry =>
1009
1015
  s instanceof SDK.SourceMapScopeChainEntry.SourceMapScopeChainEntry &&
@@ -2184,8 +2190,7 @@ export async function computeScopeMappings(
2184
2190
  resolvedScopeChain?: SDK.DebuggerModel.ScopeChainEntry[]): Promise<ScopeMapping[]> {
2185
2191
  const scopeMappings: ScopeMapping[] = [];
2186
2192
  const scopeChain = resolvedScopeChain ??
2187
- await SourceMapScopes.ScopeChainModel.ScopeChainModel.resolveScopeChain(
2188
- callFrame, Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance());
2193
+ await SourceMapScopes.ScopeChainResolver.ScopeChainResolver.instance().resolveScopeChain(callFrame);
2189
2194
  const activeScopes =
2190
2195
  new Set(scopeChain.filter(s => s instanceof SDK.SourceMapScopeChainEntry.SourceMapScopeChainEntry)
2191
2196
  .map(s => s.originalScope()));
@@ -2397,6 +2402,12 @@ const evalExpression = defineStatefulDecoration();
2397
2402
  // Styling for plugin-local elements
2398
2403
 
2399
2404
  const theme = CodeMirror.EditorView.baseTheme({
2405
+ '&.source-frame-debugger-script': {
2406
+ backgroundColor: 'rgb(255 255 194 / 50%)',
2407
+ },
2408
+ '&dark.source-frame-debugger-script': {
2409
+ backgroundColor: 'rgb(61 61 0 / 50%)',
2410
+ },
2400
2411
  '.cm-line::selection': {
2401
2412
  backgroundColor: 'transparent',
2402
2413
  color: 'currentColor',