chrome-devtools-frontend 1.0.1539972 → 1.0.1541169

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 (74) hide show
  1. package/eslint.config.mjs +167 -151
  2. package/front_end/core/common/Revealer.ts +5 -0
  3. package/front_end/core/host/InspectorFrontendHost.ts +10 -10
  4. package/front_end/core/sdk/NetworkManager.ts +16 -11
  5. package/front_end/core/sdk/sdk-meta.ts +0 -35
  6. package/front_end/entrypoints/shell/shell.ts +1 -0
  7. package/front_end/entrypoints/trace_app/trace_app.ts +1 -0
  8. package/front_end/generated/InspectorBackendCommands.ts +6 -3
  9. package/front_end/generated/SupportedCSSProperties.js +13 -0
  10. package/front_end/generated/protocol.ts +58 -2
  11. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +121 -56
  12. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +104 -62
  13. package/front_end/models/ai_assistance/performance/AIQueries.ts +56 -2
  14. package/front_end/{panels/issues → models/issues_manager}/IssueAggregator.ts +83 -65
  15. package/front_end/models/issues_manager/issues_manager.ts +2 -0
  16. package/front_end/models/trace/Processor.ts +5 -4
  17. package/front_end/models/trace/insights/types.ts +1 -1
  18. package/front_end/models/trace/types/TraceEvents.ts +1 -1
  19. package/front_end/models/workspace/IgnoreListManager.ts +41 -47
  20. package/front_end/models/workspace/workspace-meta.ts +40 -0
  21. package/front_end/panels/animation/AnimationTimeline.ts +4 -4
  22. package/front_end/panels/animation/AnimationUI.ts +28 -34
  23. package/front_end/panels/elements/ElementsTreeElement.ts +37 -9
  24. package/front_end/panels/elements/LayoutPane.ts +2 -2
  25. package/front_end/panels/elements/components/AdornerManager.ts +9 -9
  26. package/front_end/panels/elements/layoutPane.css +5 -9
  27. package/front_end/panels/event_listeners/EventListenersView.ts +1 -1
  28. package/front_end/panels/explain/components/ConsoleInsight.ts +498 -449
  29. package/front_end/panels/issues/AffectedResourcesView.ts +3 -4
  30. package/front_end/panels/issues/CorsIssueDetailsView.ts +1 -2
  31. package/front_end/panels/issues/IssueView.ts +1 -1
  32. package/front_end/panels/issues/IssuesPane.ts +12 -15
  33. package/front_end/panels/issues/issues.ts +0 -2
  34. package/front_end/panels/network/NetworkDataGridNode.ts +2 -1
  35. package/front_end/panels/network/RequestConditionsDrawer.ts +149 -46
  36. package/front_end/panels/network/RequestTimingView.ts +13 -8
  37. package/front_end/panels/network/network-meta.ts +11 -0
  38. package/front_end/panels/settings/emulation/components/userAgentClientHintsForm.css +1 -1
  39. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  40. package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +1 -1
  41. package/front_end/panels/sources/breakpointsView.css +1 -1
  42. package/front_end/panels/sources/sourcesPanel.css +2 -2
  43. package/front_end/panels/timeline/TimelineFlameChartView.ts +3 -3
  44. package/front_end/panels/timeline/TimelinePanel.ts +3 -3
  45. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +16 -10
  46. package/front_end/panels/timeline/components/SidebarSingleInsightSet.ts +2 -0
  47. package/front_end/third_party/chromium/README.chromium +1 -1
  48. package/front_end/ui/components/markdown_view/MarkdownView.ts +1 -0
  49. package/front_end/ui/components/snackbars/Snackbars.docs.ts +46 -0
  50. package/front_end/ui/{components/docs/context_menu/basic.ts → legacy/ContextMenu.docs.ts} +58 -25
  51. package/front_end/ui/legacy/UIUtils.ts +2 -1
  52. package/front_end/ui/legacy/components/inline_editor/BezierEditor.ts +1 -1
  53. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +105 -92
  54. package/front_end/ui/legacy/components/perf_ui/TimelineOverviewPane.ts +3 -3
  55. package/front_end/ui/legacy/components/perf_ui/pieChart.css +1 -1
  56. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -1
  57. package/front_end/ui/legacy/inspectorCommon.css +3 -2
  58. package/mcp/mcp.ts +15 -1
  59. package/package.json +2 -1
  60. package/front_end/ui/components/docs/context_menu/basic.html +0 -45
  61. package/front_end/ui/components/docs/linkifier/simple-url.html +0 -25
  62. package/front_end/ui/components/docs/linkifier/simple-url.ts +0 -25
  63. package/front_end/ui/components/docs/panel_feedback/basic.html +0 -25
  64. package/front_end/ui/components/docs/panel_feedback/basic.ts +0 -21
  65. package/front_end/ui/components/docs/panel_feedback/button.html +0 -25
  66. package/front_end/ui/components/docs/panel_feedback/button.ts +0 -19
  67. package/front_end/ui/components/docs/panel_introduction_steps/basic.html +0 -25
  68. package/front_end/ui/components/docs/panel_introduction_steps/basic.ts +0 -28
  69. package/front_end/ui/components/docs/perf_piechart/basic-with-legend.html +0 -20
  70. package/front_end/ui/components/docs/perf_piechart/basic-with-legend.ts +0 -20
  71. package/front_end/ui/components/docs/perf_piechart/basic-without-legend.html +0 -20
  72. package/front_end/ui/components/docs/perf_piechart/basic-without-legend.ts +0 -18
  73. package/front_end/ui/components/docs/snackbars/basic.html +0 -17
  74. package/front_end/ui/components/docs/snackbars/basic.ts +0 -50
@@ -431,9 +431,13 @@ export class TraceProcessor extends EventTarget {
431
431
  #computeInsightSet(data: Handlers.Types.HandlerData, context: Insights.Types.InsightSetContext): void {
432
432
  const logger = context.options.logger;
433
433
 
434
+ if (!this.#insights) {
435
+ this.#insights = new Map();
436
+ }
437
+
434
438
  let id, urlString, navigation;
435
439
  if (context.navigation) {
436
- id = context.navigationId;
440
+ id = `NAVIGATION_${this.#insights.size}`;
437
441
  urlString = data.Meta.finalDisplayUrlByNavigationId.get(context.navigationId) ?? data.Meta.mainFrameURL;
438
442
  navigation = context.navigation;
439
443
  } else {
@@ -503,9 +507,6 @@ export class TraceProcessor extends EventTarget {
503
507
  bounds: context.bounds,
504
508
  model: insightSetModel,
505
509
  };
506
- if (!this.#insights) {
507
- this.#insights = new Map();
508
- }
509
510
  this.#insights.set(insightSet.id, insightSet);
510
511
  this.sortInsightSet(insightSet, context.options.metadata ?? null);
511
512
  }
@@ -110,7 +110,7 @@ export type PartialInsightModel<T> =
110
110
  * navigation (or the end of the trace).
111
111
  */
112
112
  export interface InsightSet {
113
- /** If for a navigation, this is the navigationId. Else it is Trace.Types.Events.NO_NAVIGATION. */
113
+ /** If for a navigation, this is of the form "NAVIGATION_(index)". Else it is Trace.Types.Events.NO_NAVIGATION. */
114
114
  id: Types.Events.NavigationId;
115
115
  /** The URL to show in the accordion list. */
116
116
  url: URL;
@@ -1001,7 +1001,7 @@ export const NO_NAVIGATION = 'NO_NAVIGATION';
1001
1001
  * portion of the trace for which we don't have any navigation event for (as it happeneded prior
1002
1002
  * to the trace start).
1003
1003
  */
1004
- export type NavigationId = string|typeof NO_NAVIGATION;
1004
+ export type NavigationId = string;
1005
1005
 
1006
1006
  /**
1007
1007
  * This is a synthetic Layout shift cluster. The rawSourceEvent is the worst layout shift event
@@ -50,51 +50,49 @@ export interface IgnoreListGeneralRules {
50
50
 
51
51
  export class IgnoreListManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements
52
52
  SDK.TargetManager.SDKModelObserver<SDK.DebuggerModel.DebuggerModel> {
53
- readonly #listeners: Set<() => void>;
54
- readonly #isIgnoreListedURLCache: Map<string, boolean>;
55
- readonly #contentScriptExecutionContexts: Set<string>;
53
+ readonly #settings: Common.Settings.Settings;
54
+ readonly #targetManager: SDK.TargetManager.TargetManager;
56
55
 
57
- private constructor() {
56
+ readonly #listeners = new Set<() => void>();
57
+ readonly #isIgnoreListedURLCache = new Map<string, boolean>();
58
+ readonly #contentScriptExecutionContexts = new Set<string>();
59
+
60
+ private constructor(settings: Common.Settings.Settings, targetManager: SDK.TargetManager.TargetManager) {
58
61
  super();
62
+ this.#settings = settings;
63
+ this.#targetManager = targetManager;
59
64
 
60
- SDK.TargetManager.TargetManager.instance().addModelListener(
65
+ this.#targetManager.addModelListener(
61
66
  SDK.DebuggerModel.DebuggerModel, SDK.DebuggerModel.Events.GlobalObjectCleared,
62
67
  this.clearCacheIfNeeded.bind(this), this);
63
- SDK.TargetManager.TargetManager.instance().addModelListener(
68
+ this.#targetManager.addModelListener(
64
69
  SDK.RuntimeModel.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextCreated, this.onExecutionContextCreated,
65
70
  this, {scoped: true});
66
- SDK.TargetManager.TargetManager.instance().addModelListener(
71
+ this.#targetManager.addModelListener(
67
72
  SDK.RuntimeModel.RuntimeModel, SDK.RuntimeModel.Events.ExecutionContextDestroyed,
68
73
  this.onExecutionContextDestroyed, this, {scoped: true});
69
- Common.Settings.Settings.instance()
70
- .moduleSetting('skip-stack-frames-pattern')
71
- .addChangeListener(this.patternChanged.bind(this));
72
- Common.Settings.Settings.instance()
73
- .moduleSetting('skip-content-scripts')
74
- .addChangeListener(this.patternChanged.bind(this));
75
- Common.Settings.Settings.instance()
76
- .moduleSetting('automatically-ignore-list-known-third-party-scripts')
77
- .addChangeListener(this.patternChanged.bind(this));
78
- Common.Settings.Settings.instance()
79
- .moduleSetting('enable-ignore-listing')
74
+ this.#settings.moduleSetting('skip-stack-frames-pattern').addChangeListener(this.patternChanged.bind(this));
75
+ this.#settings.moduleSetting('skip-content-scripts').addChangeListener(this.patternChanged.bind(this));
76
+ this.#settings.moduleSetting('automatically-ignore-list-known-third-party-scripts')
80
77
  .addChangeListener(this.patternChanged.bind(this));
81
- Common.Settings.Settings.instance()
82
- .moduleSetting('skip-anonymous-scripts')
83
- .addChangeListener(this.patternChanged.bind(this));
84
-
85
- this.#listeners = new Set();
86
- this.#isIgnoreListedURLCache = new Map();
87
- this.#contentScriptExecutionContexts = new Set();
78
+ this.#settings.moduleSetting('enable-ignore-listing').addChangeListener(this.patternChanged.bind(this));
79
+ this.#settings.moduleSetting('skip-anonymous-scripts').addChangeListener(this.patternChanged.bind(this));
88
80
 
89
- SDK.TargetManager.TargetManager.instance().observeModels(SDK.DebuggerModel.DebuggerModel, this);
81
+ this.#targetManager.observeModels(SDK.DebuggerModel.DebuggerModel, this);
90
82
  }
91
83
 
92
84
  static instance(opts: {
93
85
  forceNew: boolean|null,
94
- } = {forceNew: null}): IgnoreListManager {
86
+ settings?: Common.Settings.Settings,
87
+ targetManager?: SDK.TargetManager.TargetManager,
88
+ } = {
89
+ forceNew: null,
90
+ }): IgnoreListManager {
95
91
  const {forceNew} = opts;
96
92
  if (!ignoreListManagerInstance || forceNew) {
97
- ignoreListManagerInstance = new IgnoreListManager();
93
+ ignoreListManagerInstance = new IgnoreListManager(
94
+ opts.settings ?? Common.Settings.Settings.instance(),
95
+ opts.targetManager ?? SDK.TargetManager.TargetManager.instance());
98
96
  }
99
97
 
100
98
  return ignoreListManagerInstance;
@@ -135,8 +133,7 @@ export class IgnoreListManager extends Common.ObjectWrapper.ObjectWrapper<EventT
135
133
  if (this.isContentScript(event.data)) {
136
134
  this.#contentScriptExecutionContexts.add(event.data.uniqueId);
137
135
  if (this.skipContentScripts) {
138
- for (const debuggerModel of SDK.TargetManager.TargetManager.instance().models(
139
- SDK.DebuggerModel.DebuggerModel)) {
136
+ for (const debuggerModel of this.#targetManager.models(SDK.DebuggerModel.DebuggerModel)) {
140
137
  void this.updateIgnoredExecutionContexts(debuggerModel);
141
138
  }
142
139
  }
@@ -148,8 +145,7 @@ export class IgnoreListManager extends Common.ObjectWrapper.ObjectWrapper<EventT
148
145
  if (this.isContentScript(event.data)) {
149
146
  this.#contentScriptExecutionContexts.delete(event.data.uniqueId);
150
147
  if (this.skipContentScripts) {
151
- for (const debuggerModel of SDK.TargetManager.TargetManager.instance().models(
152
- SDK.DebuggerModel.DebuggerModel)) {
148
+ for (const debuggerModel of this.#targetManager.models(SDK.DebuggerModel.DebuggerModel)) {
153
149
  void this.updateIgnoredExecutionContexts(debuggerModel);
154
150
  }
155
151
  }
@@ -163,8 +159,7 @@ export class IgnoreListManager extends Common.ObjectWrapper.ObjectWrapper<EventT
163
159
  }
164
160
 
165
161
  private getSkipStackFramesPatternSetting(): Common.Settings.RegExpSetting {
166
- return Common.Settings.Settings.instance().moduleSetting('skip-stack-frames-pattern') as
167
- Common.Settings.RegExpSetting;
162
+ return this.#settings.moduleSetting('skip-stack-frames-pattern') as Common.Settings.RegExpSetting;
168
163
  }
169
164
 
170
165
  private setIgnoreListPatterns(debuggerModel: SDK.DebuggerModel.DebuggerModel): Promise<boolean> {
@@ -327,58 +322,57 @@ export class IgnoreListManager extends Common.ObjectWrapper.ObjectWrapper<EventT
327
322
  }
328
323
 
329
324
  get enableIgnoreListing(): boolean {
330
- return Common.Settings.Settings.instance().moduleSetting('enable-ignore-listing').get();
325
+ return this.#settings.moduleSetting('enable-ignore-listing').get();
331
326
  }
332
327
 
333
328
  set enableIgnoreListing(value: boolean) {
334
- Common.Settings.Settings.instance().moduleSetting('enable-ignore-listing').set(value);
329
+ this.#settings.moduleSetting('enable-ignore-listing').set(value);
335
330
  }
336
331
 
337
332
  get skipContentScripts(): boolean {
338
- return this.enableIgnoreListing && Common.Settings.Settings.instance().moduleSetting('skip-content-scripts').get();
333
+ return this.enableIgnoreListing && this.#settings.moduleSetting('skip-content-scripts').get();
339
334
  }
340
335
 
341
336
  get skipAnonymousScripts(): boolean {
342
- return this.enableIgnoreListing &&
343
- Common.Settings.Settings.instance().moduleSetting('skip-anonymous-scripts').get();
337
+ return this.enableIgnoreListing && this.#settings.moduleSetting('skip-anonymous-scripts').get();
344
338
  }
345
339
 
346
340
  get automaticallyIgnoreListKnownThirdPartyScripts(): boolean {
347
341
  return this.enableIgnoreListing &&
348
- Common.Settings.Settings.instance().moduleSetting('automatically-ignore-list-known-third-party-scripts').get();
342
+ this.#settings.moduleSetting('automatically-ignore-list-known-third-party-scripts').get();
349
343
  }
350
344
 
351
345
  ignoreListContentScripts(): void {
352
346
  if (!this.enableIgnoreListing) {
353
347
  this.enableIgnoreListing = true;
354
348
  }
355
- Common.Settings.Settings.instance().moduleSetting('skip-content-scripts').set(true);
349
+ this.#settings.moduleSetting('skip-content-scripts').set(true);
356
350
  }
357
351
 
358
352
  unIgnoreListContentScripts(): void {
359
- Common.Settings.Settings.instance().moduleSetting('skip-content-scripts').set(false);
353
+ this.#settings.moduleSetting('skip-content-scripts').set(false);
360
354
  }
361
355
 
362
356
  ignoreListAnonymousScripts(): void {
363
357
  if (!this.enableIgnoreListing) {
364
358
  this.enableIgnoreListing = true;
365
359
  }
366
- Common.Settings.Settings.instance().moduleSetting('skip-anonymous-scripts').set(true);
360
+ this.#settings.moduleSetting('skip-anonymous-scripts').set(true);
367
361
  }
368
362
 
369
363
  unIgnoreListAnonymousScripts(): void {
370
- Common.Settings.Settings.instance().moduleSetting('skip-anonymous-scripts').set(false);
364
+ this.#settings.moduleSetting('skip-anonymous-scripts').set(false);
371
365
  }
372
366
 
373
367
  ignoreListThirdParty(): void {
374
368
  if (!this.enableIgnoreListing) {
375
369
  this.enableIgnoreListing = true;
376
370
  }
377
- Common.Settings.Settings.instance().moduleSetting('automatically-ignore-list-known-third-party-scripts').set(true);
371
+ this.#settings.moduleSetting('automatically-ignore-list-known-third-party-scripts').set(true);
378
372
  }
379
373
 
380
374
  unIgnoreListThirdParty(): void {
381
- Common.Settings.Settings.instance().moduleSetting('automatically-ignore-list-known-third-party-scripts').set(false);
375
+ this.#settings.moduleSetting('automatically-ignore-list-known-third-party-scripts').set(false);
382
376
  }
383
377
 
384
378
  ignoreListURL(url: Platform.DevToolsPath.UrlString): void {
@@ -467,7 +461,7 @@ export class IgnoreListManager extends Common.ObjectWrapper.ObjectWrapper<EventT
467
461
  this.#isIgnoreListedURLCache.clear();
468
462
 
469
463
  const promises: Array<Promise<unknown>> = [];
470
- for (const debuggerModel of SDK.TargetManager.TargetManager.instance().models(SDK.DebuggerModel.DebuggerModel)) {
464
+ for (const debuggerModel of this.#targetManager.models(SDK.DebuggerModel.DebuggerModel)) {
471
465
  promises.push(this.setIgnoreListPatterns(debuggerModel));
472
466
  const sourceMapManager = debuggerModel.sourceMapManager();
473
467
  for (const script of debuggerModel.scripts()) {
@@ -0,0 +1,40 @@
1
+ // Copyright 2025 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
+
7
+ Common.Settings.registerSettingExtension({
8
+ storageType: Common.Settings.SettingStorageType.SYNCED,
9
+ settingName: 'skip-stack-frames-pattern',
10
+ settingType: Common.Settings.SettingType.REGEX,
11
+ defaultValue: '/node_modules/|^node:',
12
+ });
13
+
14
+ Common.Settings.registerSettingExtension({
15
+ storageType: Common.Settings.SettingStorageType.SYNCED,
16
+ settingName: 'skip-content-scripts',
17
+ settingType: Common.Settings.SettingType.BOOLEAN,
18
+ defaultValue: true,
19
+ });
20
+
21
+ Common.Settings.registerSettingExtension({
22
+ storageType: Common.Settings.SettingStorageType.SYNCED,
23
+ settingName: 'automatically-ignore-list-known-third-party-scripts',
24
+ settingType: Common.Settings.SettingType.BOOLEAN,
25
+ defaultValue: true,
26
+ });
27
+
28
+ Common.Settings.registerSettingExtension({
29
+ storageType: Common.Settings.SettingStorageType.SYNCED,
30
+ settingName: 'skip-anonymous-scripts',
31
+ settingType: Common.Settings.SettingType.BOOLEAN,
32
+ defaultValue: false,
33
+ });
34
+
35
+ Common.Settings.registerSettingExtension({
36
+ storageType: Common.Settings.SettingStorageType.SYNCED,
37
+ settingName: 'enable-ignore-listing',
38
+ settingType: Common.Settings.SettingType.BOOLEAN,
39
+ defaultValue: true,
40
+ });
@@ -219,6 +219,8 @@ const DEFAULT_TOOLBAR_VIEW: ToolbarView = (input: ToolbarViewInput, output: unde
219
219
  };
220
220
  // clang-format on
221
221
 
222
+ const DEFAULT_DURATION = 100;
223
+
222
224
  let animationTimelineInstance: AnimationTimeline;
223
225
  export class AnimationTimeline extends UI.Widget.VBox implements
224
226
  SDK.TargetManager.SDKModelObserver<SDK.AnimationModel.AnimationModel> {
@@ -233,7 +235,6 @@ export class AnimationTimeline extends UI.Widget.VBox implements
233
235
  #clearButton!: UI.Toolbar.ToolbarButton;
234
236
  #selectedGroup!: SDK.AnimationModel.AnimationGroup|null;
235
237
  #renderQueue!: AnimationUI[];
236
- #defaultDuration: number;
237
238
  #duration: number;
238
239
  #timelineControlsWidth: number;
239
240
  readonly #nodesMap: Map<number, NodeUI>;
@@ -297,8 +298,7 @@ export class AnimationTimeline extends UI.Widget.VBox implements
297
298
  i18nString(UIStrings.noEffectSelected), i18nString(UIStrings.selectAnEffectAboveToInspectAnd));
298
299
  noEffectSelectedPlaceholder.show(timelineHint);
299
300
 
300
- /** @constant */ this.#defaultDuration = 100;
301
- this.#duration = this.#defaultDuration;
301
+ this.#duration = DEFAULT_DURATION;
302
302
  this.#nodesMap = new Map();
303
303
  this.#uiAnimations = [];
304
304
  this.#groupBuffer = [];
@@ -614,7 +614,7 @@ export class AnimationTimeline extends UI.Widget.VBox implements
614
614
  this.#nodesMap.clear();
615
615
  this.#animationsMap.clear();
616
616
  this.#animationsContainer.removeChildren();
617
- this.#duration = this.#defaultDuration;
617
+ this.#duration = DEFAULT_DURATION;
618
618
  this.#timelineScrubber.classList.add('hidden');
619
619
  this.#gridHeader.classList.remove('scrubber-enabled');
620
620
  this.#selectedGroup = null;
@@ -32,10 +32,10 @@ const str_ = i18n.i18n.registerUIStrings('panels/animation/AnimationUI.ts', UISt
32
32
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
33
33
 
34
34
  interface CachedElement {
35
- group: HTMLElement|null;
36
- animationLine: HTMLElement|null;
37
- keyframePoints: Record<number, HTMLElement>;
38
- keyframeRender: Record<number, HTMLElement>;
35
+ group: SVGElement|null;
36
+ animationLine: SVGElement|null;
37
+ keyframePoints: Record<number, SVGElement>;
38
+ keyframeRender: Record<number, SVGElement>;
39
39
  }
40
40
 
41
41
  export class AnimationUI {
@@ -43,15 +43,15 @@ export class AnimationUI {
43
43
  #timeline: AnimationTimeline;
44
44
  #keyframes?: SDK.AnimationModel.KeyframeStyle[];
45
45
  #nameElement: HTMLElement;
46
- readonly #svg: Element;
47
- #activeIntervalGroup: Element;
48
- #cachedElements: CachedElement[];
49
- #movementInMs: number;
50
- #keyboardMovementRateMs: number;
46
+ readonly #svg: SVGElement;
47
+ #activeIntervalGroup: SVGGElement;
48
+ #cachedElements: CachedElement[] = [];
49
+ #movementInMs = 0;
50
+ #keyboardMovementRateMs = 50;
51
51
  #color: string;
52
52
  #node?: SDK.DOMModel.DOMNode|null;
53
- #delayLine?: Element;
54
- #endDelayLine?: Element;
53
+ #delayLine?: SVGLineElement;
54
+ #endDelayLine?: SVGLineElement;
55
55
  #tailGroup?: Element;
56
56
  #mouseEventType?: Events;
57
57
  #keyframeMoved?: number|null;
@@ -73,7 +73,7 @@ export class AnimationUI {
73
73
 
74
74
  this.#svg = UI.UIUtils.createSVGChild(parentElement, 'svg', 'animation-ui');
75
75
  this.#svg.setAttribute('height', Options.AnimationSVGHeight.toString());
76
- (this.#svg as HTMLElement).style.marginLeft = '-' + Options.AnimationMargin + 'px';
76
+ this.#svg.style.marginLeft = '-' + Options.AnimationMargin + 'px';
77
77
  this.#svg.addEventListener('contextmenu', this.onContextMenu.bind(this));
78
78
  this.#activeIntervalGroup = UI.UIUtils.createSVGChild(this.#svg, 'g');
79
79
  this.#activeIntervalGroup.setAttribute('jslog', `${VisualLogging.animationClip().track({drag: true})}`);
@@ -86,10 +86,6 @@ export class AnimationUI {
86
86
  this.#activeIntervalGroup, this.keydownMove.bind(this, Events.ANIMATION_DRAG, null));
87
87
  }
88
88
 
89
- this.#cachedElements = [];
90
-
91
- this.#movementInMs = 0;
92
- this.#keyboardMovementRateMs = 50;
93
89
  this.#color = AnimationUI.colorForAnimation(this.#animation);
94
90
  }
95
91
 
@@ -125,19 +121,19 @@ export class AnimationUI {
125
121
  this.#node = node;
126
122
  }
127
123
 
128
- private createLine(parentElement: HTMLElement, className: string): Element {
124
+ private createLine(parentElement: SVGElement, className: string): SVGLineElement {
129
125
  const line = UI.UIUtils.createSVGChild(parentElement, 'line', className);
130
126
  line.setAttribute('x1', Options.AnimationMargin.toString());
131
127
  line.setAttribute('y1', Options.AnimationHeight.toString());
132
128
  line.setAttribute('y2', Options.AnimationHeight.toString());
133
- (line as HTMLElement).style.stroke = this.#color;
129
+ line.style.stroke = this.#color;
134
130
  return line;
135
131
  }
136
132
 
137
- private drawAnimationLine(iteration: number, parentElement: HTMLElement): void {
133
+ private drawAnimationLine(iteration: number, parentElement: SVGElement): void {
138
134
  const cache = this.#cachedElements[iteration];
139
135
  if (!cache.animationLine) {
140
- cache.animationLine = (this.createLine(parentElement, 'animation-line') as HTMLElement);
136
+ cache.animationLine = this.createLine(parentElement, 'animation-line');
141
137
  }
142
138
  if (!cache.animationLine) {
143
139
  return;
@@ -147,7 +143,7 @@ export class AnimationUI {
147
143
  'x2', (this.duration() * this.#timeline.pixelTimeRatio() + Options.AnimationMargin).toFixed(2));
148
144
  }
149
145
 
150
- private drawDelayLine(parentElement: HTMLElement): void {
146
+ private drawDelayLine(parentElement: SVGElement): void {
151
147
  if (!this.#delayLine || !this.#endDelayLine) {
152
148
  this.#delayLine = this.createLine(parentElement, 'animation-delay-line');
153
149
  this.#endDelayLine = this.createLine(parentElement, 'animation-delay-line');
@@ -164,7 +160,7 @@ export class AnimationUI {
164
160
  this.#timeline.width(),
165
161
  (this.delayOrStartTime() + this.duration() * this.#animation.source().iterations()) *
166
162
  this.#timeline.pixelTimeRatio());
167
- (this.#endDelayLine as HTMLElement).style.transform = 'translateX(' + leftMargin.toFixed(2) + 'px)';
163
+ this.#endDelayLine.style.transform = 'translateX(' + leftMargin.toFixed(2) + 'px)';
168
164
  this.#endDelayLine.setAttribute('x1', margin.toString());
169
165
  this.#endDelayLine.setAttribute(
170
166
  'x2',
@@ -179,10 +175,8 @@ export class AnimationUI {
179
175
  return;
180
176
  }
181
177
 
182
- const circle =
183
- (UI.UIUtils.createSVGChild(
184
- parentElement, 'circle', keyframeIndex <= 0 ? 'animation-endpoint' : 'animation-keyframe-point') as
185
- HTMLElement);
178
+ const circle = UI.UIUtils.createSVGChild(
179
+ parentElement, 'circle', keyframeIndex <= 0 ? 'animation-endpoint' : 'animation-keyframe-point');
186
180
  circle.setAttribute('cx', x.toFixed(2));
187
181
  circle.setAttribute('cy', Options.AnimationHeight.toString());
188
182
  circle.style.stroke = this.#color;
@@ -221,10 +215,10 @@ export class AnimationUI {
221
215
  }
222
216
 
223
217
  private renderKeyframe(
224
- iteration: number, keyframeIndex: number, parentElement: HTMLElement, leftDistance: number, width: number,
218
+ iteration: number, keyframeIndex: number, parentElement: SVGElement, leftDistance: number, width: number,
225
219
  easing: string): void {
226
- function createStepLine(parentElement: HTMLElement, x: number, strokeColor: string): void {
227
- const line = (UI.UIUtils.createSVGChild(parentElement, 'line') as HTMLElement);
220
+ function createStepLine(parentElement: SVGElement, x: number, strokeColor: string): void {
221
+ const line = UI.UIUtils.createSVGChild(parentElement, 'line');
228
222
  line.setAttribute('x1', x.toString());
229
223
  line.setAttribute('x2', x.toString());
230
224
  line.setAttribute('y1', Options.AnimationMargin.toString());
@@ -237,7 +231,7 @@ export class AnimationUI {
237
231
  if (!cache[keyframeIndex]) {
238
232
  const svg = bezier ? UI.UIUtils.createSVGChild(parentElement, 'path', 'animation-keyframe') :
239
233
  UI.UIUtils.createSVGChild(parentElement, 'g', 'animation-keyframe-step');
240
- cache[keyframeIndex] = (svg as HTMLElement);
234
+ cache[keyframeIndex] = svg;
241
235
  }
242
236
  const group = cache[keyframeIndex];
243
237
  group.tabIndex = 0;
@@ -269,14 +263,14 @@ export class AnimationUI {
269
263
  const maxWidth = this.#timeline.width() - Options.AnimationMargin;
270
264
 
271
265
  this.#svg.setAttribute('width', (maxWidth + 2 * Options.AnimationMargin).toFixed(2));
272
- (this.#activeIntervalGroup as HTMLElement).style.transform =
266
+ this.#activeIntervalGroup.style.transform =
273
267
  'translateX(' + (this.delayOrStartTime() * this.#timeline.pixelTimeRatio()).toFixed(2) + 'px)';
274
268
 
275
269
  this.#nameElement.style.transform = 'translateX(' +
276
270
  (Math.max(this.delayOrStartTime(), 0) * this.#timeline.pixelTimeRatio() + Options.AnimationMargin).toFixed(2) +
277
271
  'px)';
278
272
  this.#nameElement.style.width = (this.duration() * this.#timeline.pixelTimeRatio()).toFixed(2) + 'px';
279
- this.drawDelayLine((this.#svg as HTMLElement));
273
+ this.drawDelayLine(this.#svg);
280
274
 
281
275
  if (this.#animation.type() === 'CSSTransition') {
282
276
  this.renderTransition();
@@ -307,7 +301,7 @@ export class AnimationUI {
307
301
  }
308
302
 
309
303
  private renderTransition(): void {
310
- const activeIntervalGroup = (this.#activeIntervalGroup as HTMLElement);
304
+ const activeIntervalGroup = this.#activeIntervalGroup;
311
305
  if (!this.#cachedElements[0]) {
312
306
  this.#cachedElements[0] = {animationLine: null, keyframePoints: {}, keyframeRender: {}, group: null};
313
307
  }
@@ -326,7 +320,7 @@ export class AnimationUI {
326
320
  animationLine: null,
327
321
  keyframePoints: {},
328
322
  keyframeRender: {},
329
- group: (UI.UIUtils.createSVGChild(parentElement, 'g') as HTMLElement),
323
+ group: UI.UIUtils.createSVGChild(parentElement, 'g'),
330
324
  };
331
325
  }
332
326
  const group = this.#cachedElements[iteration].group;
@@ -45,6 +45,7 @@ import type * as Elements from '../../models/elements/elements.js';
45
45
  import type * as IssuesManager from '../../models/issues_manager/issues_manager.js';
46
46
  import * as TextUtils from '../../models/text_utils/text_utils.js';
47
47
  import * as CodeMirror from '../../third_party/codemirror.next/codemirror.next.js';
48
+ import type {DirectiveResult} from '../../third_party/lit/lib/directive.js';
48
49
  import * as Adorners from '../../ui/components/adorners/adorners.js';
49
50
  import * as Buttons from '../../ui/components/buttons/buttons.js';
50
51
  import * as CodeHighlighter from '../../ui/components/code_highlighter/code_highlighter.js';
@@ -351,10 +352,13 @@ export function isOpeningTag(context: TagTypeContext): context is OpeningTagCont
351
352
  }
352
353
 
353
354
  export interface ViewInput {
355
+ showAdAdorner: boolean;
354
356
  adorners?: Set<Adorners.Adorner.Adorner>;
355
357
  nodeInfo?: DocumentFragment;
356
358
 
357
359
  onGutterClick: (e: Event) => void;
360
+ onAdornerAdded: (adorner: Adorners.Adorner.Adorner) => void;
361
+ onAdornerRemoved: (adorner: Adorners.Adorner.Adorner) => void;
358
362
  }
359
363
 
360
364
  export interface ViewOutput {
@@ -363,7 +367,23 @@ export interface ViewOutput {
363
367
  contentElement?: HTMLElement;
364
368
  }
365
369
 
370
+ function adornerRef(input: ViewInput): DirectiveResult<typeof Lit.Directives.RefDirective> {
371
+ let adorner: Adorners.Adorner.Adorner|undefined;
372
+ return ref((el?: Element) => {
373
+ if (adorner) {
374
+ input.onAdornerRemoved(adorner);
375
+ }
376
+ adorner = el as Adorners.Adorner.Adorner;
377
+ if (adorner) {
378
+ input.onAdornerAdded(adorner);
379
+ }
380
+ });
381
+ }
382
+
366
383
  export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement): void => {
384
+ const adAdornerConfig =
385
+ ElementsComponents.AdornerManager.getRegisteredAdorner(ElementsComponents.AdornerManager.RegisteredAdorners.AD);
386
+ const hasAdorners = input.adorners || input.showAdAdorner;
367
387
  // clang-format off
368
388
  render(html`
369
389
  <div ${ref(el => { output.contentElement = el as HTMLElement; })}>
@@ -372,8 +392,14 @@ export const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLE
372
392
  <devtools-icon name="dots-horizontal"></devtools-icon>
373
393
  <div class="hidden" ${ref(el => { output.decorationsElement = el as HTMLElement; })}></div>
374
394
  </div>
375
- ${input.adorners ? html`<div class="adorner-container ${input.adorners.size === 0 ? 'hidden': ''}">
376
- ${repeat(Array.from(input.adorners.values()).sort(adornerComparator), adorner => {
395
+ ${hasAdorners? html`<div class="adorner-container ${!hasAdorners ? 'hidden': ''}">
396
+ ${input.showAdAdorner ? html`<devtools-adorner
397
+ .data=${{name: adAdornerConfig.name, jslogContext: adAdornerConfig.name}}
398
+ aria-label=${i18nString(UIStrings.thisFrameWasIdentifiedAsAnAd)}
399
+ ${adornerRef(input)}>
400
+ <span>${adAdornerConfig.name}</span>
401
+ </devtools-adorner>` : nothing}
402
+ ${repeat(Array.from((input.adorners ?? new Set()).values()).sort(adornerComparator), adorner => {
377
403
  return adorner;
378
404
  })}
379
405
  </div>`: nothing}
@@ -442,13 +468,6 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
442
468
  };
443
469
  void this.updateStyleAdorners();
444
470
 
445
- if (node.isAdFrameNode()) {
446
- const config = ElementsComponents.AdornerManager.getRegisteredAdorner(
447
- ElementsComponents.AdornerManager.RegisteredAdorners.AD);
448
- const adorner = this.adorn(config);
449
- UI.Tooltip.Tooltip.install(adorner, i18nString(UIStrings.thisFrameWasIdentifiedAsAnAd));
450
- }
451
-
452
471
  void this.updateScrollAdorner();
453
472
  }
454
473
  this.expandAllButtonElement = null;
@@ -533,8 +552,17 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
533
552
  DEFAULT_VIEW(
534
553
  {
535
554
  adorners: !this.isClosingTag() ? this.#adorners : undefined,
555
+ showAdAdorner:
556
+ ElementsPanel.instance().isAdornerEnabled(ElementsComponents.AdornerManager.RegisteredAdorners.AD) &&
557
+ this.nodeInternal.isAdFrameNode(),
536
558
  nodeInfo: this.#nodeInfo,
537
559
  onGutterClick: this.showContextMenu.bind(this),
560
+ onAdornerAdded: adorner => {
561
+ ElementsPanel.instance().registerAdorner(adorner);
562
+ },
563
+ onAdornerRemoved: adorner => {
564
+ ElementsPanel.instance().deregisterAdorner(adorner);
565
+ },
538
566
  },
539
567
  this, this.listItemElement);
540
568
  }
@@ -312,7 +312,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
312
312
  </div>
313
313
  <div class="checkbox-settings">
314
314
  ${input.booleanSettings.map(setting =>
315
- html`<devtools-checkbox
315
+ html`<div><devtools-checkbox
316
316
  data-boolean-setting="true"
317
317
  class="checkbox-label"
318
318
  title=${setting.title}
@@ -320,7 +320,7 @@ const DEFAULT_VIEW: View = (input, output, target) => {
320
320
  @change=${(e: Event) => input.onBooleanSettingChange(setting, e)}
321
321
  jslog=${VisualLogging.toggle().track({click: true}).context(setting.name)}>
322
322
  ${setting.title}
323
- </devtools-checkbox>`)}
323
+ </devtools-checkbox></div>`)}
324
324
  </div>
325
325
  </div>
326
326
  ${input.gridElements ?
@@ -22,20 +22,20 @@ export interface RegisteredAdorner {
22
22
  }
23
23
 
24
24
  export enum RegisteredAdorners {
25
+ AD = 'ad',
26
+ CONTAINER = 'container',
27
+ FLEX = 'flex',
25
28
  GRID = 'grid',
26
- SUBGRID = 'subgrid',
27
29
  MASONRY = 'masonry',
28
- FLEX = 'flex',
29
- AD = 'ad',
30
+ MEDIA = 'media',
31
+ POPOVER = 'popover',
32
+ REVEAL = 'reveal',
33
+ SCROLL = 'scroll',
30
34
  SCROLL_SNAP = 'scroll-snap',
31
- STARTING_STYLE = 'starting-style',
32
- CONTAINER = 'container',
33
35
  SLOT = 'slot',
36
+ STARTING_STYLE = 'starting-style',
37
+ SUBGRID = 'subgrid',
34
38
  TOP_LAYER = 'top-layer',
35
- REVEAL = 'reveal',
36
- MEDIA = 'media',
37
- SCROLL = 'scroll',
38
- POPOVER = 'popover',
39
39
  }
40
40
 
41
41
  /**
@@ -44,18 +44,14 @@
44
44
  }
45
45
 
46
46
  .checkbox-settings {
47
- margin-top: 8px;
47
+ margin-top: var(--sys-size-5);
48
48
  display: grid;
49
49
  grid-template-columns: 1fr;
50
- gap: 5px;
51
- }
52
-
53
- .checkbox-settings devtools-checkbox {
54
- margin-bottom: 8px;
55
- }
50
+ gap: var(--sys-size-4);
56
51
 
57
- .checkbox-settings devtools-checkbox:last-child {
58
- margin-bottom: 0;
52
+ > div {
53
+ display: flex;
54
+ }
59
55
  }
60
56
 
61
57
  devtools-checkbox {
@@ -315,7 +315,7 @@ export class ObjectEventListenerBar extends UI.TreeOutline.TreeElement {
315
315
 
316
316
  const propertyValue = ObjectUI.ObjectPropertiesSection.ObjectPropertiesSection.createPropertyValue(
317
317
  object, /* wasThrown */ false, /* showPreview */ false);
318
- this.valueTitle = propertyValue.element;
318
+ this.valueTitle = propertyValue;
319
319
  title.appendChild(this.valueTitle);
320
320
 
321
321
  if (this.#eventListener.canRemove()) {