chrome-devtools-frontend 1.0.1016605 → 1.0.1017091

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.
@@ -654,6 +654,7 @@ grd_files_debug_sources = [
654
654
  "front_end/core/sdk/ServiceWorkerManager.js",
655
655
  "front_end/core/sdk/SourceMap.js",
656
656
  "front_end/core/sdk/SourceMapManager.js",
657
+ "front_end/core/sdk/StorageKeyManager.js",
657
658
  "front_end/core/sdk/Target.js",
658
659
  "front_end/core/sdk/TargetManager.js",
659
660
  "front_end/core/sdk/TracingManager.js",
@@ -961,6 +962,7 @@ grd_files_debug_sources = [
961
962
  "front_end/panels/elements/StylePropertyTreeElement.js",
962
963
  "front_end/panels/elements/StylePropertyUtils.js",
963
964
  "front_end/panels/elements/StylesSidebarPane.js",
965
+ "front_end/panels/elements/TopLayerContainer.js",
964
966
  "front_end/panels/elements/classesPaneWidget.css.js",
965
967
  "front_end/panels/elements/components/AccessibilityTreeNode.js",
966
968
  "front_end/panels/elements/components/AdornerManager.js",
@@ -5159,6 +5159,9 @@
5159
5159
  "panels/elements/StylesSidebarPane.ts | unknownPropertyName": {
5160
5160
  "message": "Unknown property name"
5161
5161
  },
5162
+ "panels/elements/TopLayerContainer.ts | topLayer": {
5163
+ "message": "top-layer"
5164
+ },
5162
5165
  "panels/emulation/DeviceModeToolbar.ts | addDevicePixelRatio": {
5163
5166
  "message": "Add device pixel ratio"
5164
5167
  },
@@ -5159,6 +5159,9 @@
5159
5159
  "panels/elements/StylesSidebarPane.ts | unknownPropertyName": {
5160
5160
  "message": "Ûńk̂ńôẃn̂ ṕr̂óp̂ér̂t́ŷ ńâḿê"
5161
5161
  },
5162
+ "panels/elements/TopLayerContainer.ts | topLayer": {
5163
+ "message": "t̂óp̂-ĺâýêŕ"
5164
+ },
5162
5165
  "panels/emulation/DeviceModeToolbar.ts | addDevicePixelRatio": {
5163
5166
  "message": "Âd́d̂ d́êv́îćê ṕîx́êĺ r̂át̂íô"
5164
5167
  },
@@ -1410,6 +1410,10 @@ export class DOMModel extends SDKModel<EventTypes> {
1410
1410
  this.scheduleMutationEvent(node);
1411
1411
  }
1412
1412
 
1413
+ topLayerElementsUpdated(): void {
1414
+ this.dispatchEventToListeners(Events.TopLayerElementsChanged);
1415
+ }
1416
+
1413
1417
  pseudoElementRemoved(parentId: Protocol.DOM.NodeId, pseudoElementId: Protocol.DOM.NodeId): void {
1414
1418
  const parent = this.idToDOMNode.get(parentId);
1415
1419
  if (!parent) {
@@ -1511,6 +1515,10 @@ export class DOMModel extends SDKModel<EventTypes> {
1511
1515
  return this.agent.invoke_querySelectorAll({nodeId, selector}).then(({nodeIds}) => nodeIds);
1512
1516
  }
1513
1517
 
1518
+ getTopLayerElements(): Promise<Protocol.DOM.NodeId[]|null> {
1519
+ return this.agent.invoke_getTopLayerElements().then(({nodeIds}) => nodeIds);
1520
+ }
1521
+
1514
1522
  markUndoableState(minorChange?: boolean): void {
1515
1523
  void DOMModelUndoStack.instance().markUndoableState(this, minorChange || false);
1516
1524
  }
@@ -1574,6 +1582,7 @@ export enum Events {
1574
1582
  ChildNodeCountUpdated = 'ChildNodeCountUpdated',
1575
1583
  DistributedNodesChanged = 'DistributedNodesChanged',
1576
1584
  MarkersChanged = 'MarkersChanged',
1585
+ TopLayerElementsChanged = 'TopLayerElementsChanged',
1577
1586
  }
1578
1587
 
1579
1588
  export type EventTypes = {
@@ -1587,6 +1596,7 @@ export type EventTypes = {
1587
1596
  [Events.ChildNodeCountUpdated]: DOMNode,
1588
1597
  [Events.DistributedNodesChanged]: DOMNode,
1589
1598
  [Events.MarkersChanged]: DOMNode,
1599
+ [Events.TopLayerElementsChanged]: void,
1590
1600
  };
1591
1601
 
1592
1602
  class DOMDispatcher implements ProtocolProxyApi.DOMDispatcher {
@@ -1652,6 +1662,7 @@ class DOMDispatcher implements ProtocolProxyApi.DOMDispatcher {
1652
1662
  }
1653
1663
 
1654
1664
  topLayerElementsUpdated(): void {
1665
+ this.#domModel.topLayerElementsUpdated();
1655
1666
  }
1656
1667
  }
1657
1668
 
@@ -50,10 +50,13 @@ import {Capability} from './Target.js';
50
50
  import {SDKModel} from './SDKModel.js';
51
51
  import {TargetManager} from './TargetManager.js';
52
52
  import {SecurityOriginManager} from './SecurityOriginManager.js';
53
+ import {StorageKeyManager} from './StorageKeyManager.js';
53
54
 
54
55
  export class ResourceTreeModel extends SDKModel<EventTypes> {
55
56
  readonly agent: ProtocolProxyApi.PageApi;
57
+ readonly storageAgent: ProtocolProxyApi.StorageApi;
56
58
  readonly #securityOriginManager: SecurityOriginManager;
59
+ readonly #storageKeyManager: StorageKeyManager;
57
60
  readonly framesInternal: Map<string, ResourceTreeFrame>;
58
61
  #cachedResourcesProcessed: boolean;
59
62
  #pendingReloadOptions: {
@@ -75,8 +78,10 @@ export class ResourceTreeModel extends SDKModel<EventTypes> {
75
78
  networkManager.addEventListener(NetworkManagerEvents.RequestUpdateDropped, this.onRequestUpdateDropped, this);
76
79
  }
77
80
  this.agent = target.pageAgent();
81
+ this.storageAgent = target.storageAgent();
78
82
  void this.agent.invoke_enable();
79
83
  this.#securityOriginManager = (target.model(SecurityOriginManager) as SecurityOriginManager);
84
+ this.#storageKeyManager = (target.model(StorageKeyManager) as StorageKeyManager);
80
85
  this.#pendingBackForwardCacheNotUsedEvents = new Set<Protocol.Page.BackForwardCacheNotUsedEvent>();
81
86
  this.#pendingPrerenderAttemptCompletedEvents = new Set<Protocol.Page.PrerenderAttemptCompletedEvent>();
82
87
  target.registerPageDispatcher(new PageDispatcher(this));
@@ -129,6 +134,14 @@ export class ResourceTreeModel extends SDKModel<EventTypes> {
129
134
  }
130
135
  }
131
136
 
137
+ async storageKeyForFrame(frameId: Protocol.Page.FrameId): Promise<string|null> {
138
+ const response = await this.storageAgent.invoke_getStorageKeyForFrame({frameId: frameId});
139
+ if (response.getError() === 'Frame tree node for given frame not found') {
140
+ return null;
141
+ }
142
+ return response.storageKey;
143
+ }
144
+
132
145
  domModel(): DOMModel {
133
146
  return this.target().model(DOMModel) as DOMModel;
134
147
  }
@@ -164,6 +177,7 @@ export class ResourceTreeModel extends SDKModel<EventTypes> {
164
177
  }
165
178
  this.dispatchEventToListeners(Events.FrameAdded, frame);
166
179
  this.updateSecurityOrigins();
180
+ void this.updateStorageKeys();
167
181
  }
168
182
 
169
183
  frameAttached(
@@ -233,6 +247,7 @@ export class ResourceTreeModel extends SDKModel<EventTypes> {
233
247
  this.target().setInspectedURL(frame.url);
234
248
  }
235
249
  this.updateSecurityOrigins();
250
+ void this.updateStorageKeys();
236
251
  }
237
252
 
238
253
  documentOpened(framePayload: Protocol.Page.Frame): void {
@@ -265,6 +280,7 @@ export class ResourceTreeModel extends SDKModel<EventTypes> {
265
280
  frame.remove(isSwap);
266
281
  }
267
282
  this.updateSecurityOrigins();
283
+ void this.updateStorageKeys();
268
284
  }
269
285
 
270
286
  private onRequestFinished(event: Common.EventTarget.EventTargetEvent<NetworkRequest>): void {
@@ -520,6 +536,26 @@ export class ResourceTreeModel extends SDKModel<EventTypes> {
520
536
  };
521
537
  }
522
538
 
539
+ private async getStorageKeyData(): Promise<StorageKeyData> {
540
+ const storageKeys = new Set<string>();
541
+ let mainStorageKey: string|null = null;
542
+
543
+ for (const {isMainFrame, storageKey} of await Promise.all(
544
+ [...this.framesInternal.values()].map(async f => f.storageKey.then(k => ({
545
+ isMainFrame: f.isMainFrame(),
546
+ storageKey: k,
547
+ }))))) {
548
+ if (isMainFrame) {
549
+ mainStorageKey = storageKey;
550
+ }
551
+ if (storageKey) {
552
+ storageKeys.add(storageKey);
553
+ }
554
+ }
555
+
556
+ return {storageKeys: storageKeys, mainStorageKey: mainStorageKey};
557
+ }
558
+
523
559
  private updateSecurityOrigins(): void {
524
560
  const data = this.getSecurityOriginData();
525
561
  this.#securityOriginManager.setMainSecurityOrigin(
@@ -527,6 +563,16 @@ export class ResourceTreeModel extends SDKModel<EventTypes> {
527
563
  this.#securityOriginManager.updateSecurityOrigins(data.securityOrigins);
528
564
  }
529
565
 
566
+ private async updateStorageKeys(): Promise<void> {
567
+ const data = await this.getStorageKeyData();
568
+ this.#storageKeyManager.setMainStorageKey(data.mainStorageKey || '');
569
+ this.#storageKeyManager.updateStorageKeys(data.storageKeys);
570
+ }
571
+
572
+ async getMainStorageKey(): Promise<string|null> {
573
+ return this.mainFrame ? this.mainFrame.storageKey : null;
574
+ }
575
+
530
576
  getMainSecurityOrigin(): string|null {
531
577
  const data = this.getSecurityOriginData();
532
578
  return data.mainSecurityOrigin || data.unreachableMainSecurityOrigin;
@@ -626,6 +672,7 @@ export class ResourceTreeFrame {
626
672
  #urlInternal: Platform.DevToolsPath.UrlString;
627
673
  #domainAndRegistryInternal: string;
628
674
  #securityOriginInternal: string|null;
675
+ #storageKeyInternal?: Promise<string|null>;
629
676
  #unreachableUrlInternal: Platform.DevToolsPath.UrlString;
630
677
  #adFrameStatusInternal?: Protocol.Page.AdFrameStatus;
631
678
  #secureContextType: Protocol.Page.SecureContextType|null;
@@ -758,6 +805,13 @@ export class ResourceTreeFrame {
758
805
  return this.#securityOriginInternal;
759
806
  }
760
807
 
808
+ get storageKey(): Promise<string|null> {
809
+ if (!this.#storageKeyInternal) {
810
+ this.#storageKeyInternal = this.#model.storageKeyForFrame(this.#idInternal);
811
+ }
812
+ return this.#storageKeyInternal;
813
+ }
814
+
761
815
  unreachableUrl(): Platform.DevToolsPath.UrlString {
762
816
  return this.#unreachableUrlInternal;
763
817
  }
@@ -1122,3 +1176,8 @@ export interface SecurityOriginData {
1122
1176
  mainSecurityOrigin: string|null;
1123
1177
  unreachableMainSecurityOrigin: string|null;
1124
1178
  }
1179
+
1180
+ export interface StorageKeyData {
1181
+ storageKeys: Set<string>;
1182
+ mainStorageKey: string|null;
1183
+ }
@@ -268,7 +268,7 @@ export class Script implements TextUtils.ContentProvider.ContentProvider, FrameA
268
268
  return {status: Protocol.Debugger.SetScriptSourceResponseStatus.Ok};
269
269
  }
270
270
  const response = await this.debuggerModel.target().debuggerAgent().invoke_setScriptSource(
271
- {scriptId: this.scriptId, scriptSource: newSource});
271
+ {scriptId: this.scriptId, scriptSource: newSource, allowTopFrameEditing: true});
272
272
  if (response.getError()) {
273
273
  // Something went seriously wrong, like the V8 inspector no longer knowing about this script without
274
274
  // shutting down the Debugger agent etc.
@@ -81,5 +81,5 @@ export type EventTypes = {
81
81
  [Events.MainSecurityOriginChanged]: MainSecurityOriginChangedEvent,
82
82
  };
83
83
 
84
- // TODO(jarhar): this is the only usage of Capability.None. Do something about it!
84
+ // TODO(jarhar): this is the one of the two usages of Capability.None. Do something about it!
85
85
  SDKModel.register(SecurityOriginManager, {capabilities: Capability.None, autostart: false});
@@ -0,0 +1,71 @@
1
+ // Copyright 2022 The Chromium Authors. All rights reserved.
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 {Target} from './Target.js';
6
+ import {Capability} from './Target.js';
7
+ import {SDKModel} from './SDKModel.js';
8
+
9
+ export class StorageKeyManager extends SDKModel<EventTypes> {
10
+ #mainStorageKeyInternal: string;
11
+ #storageKeysInternal: Set<string>;
12
+ constructor(target: Target) {
13
+ super(target);
14
+
15
+ this.#mainStorageKeyInternal = '';
16
+ this.#storageKeysInternal = new Set();
17
+ }
18
+
19
+ updateStorageKeys(storageKeys: Set<string>): void {
20
+ const oldStorageKeys = this.#storageKeysInternal;
21
+ this.#storageKeysInternal = storageKeys;
22
+
23
+ for (const storageKey of oldStorageKeys) {
24
+ if (!this.#storageKeysInternal.has(storageKey)) {
25
+ this.dispatchEventToListeners(Events.StorageKeyRemoved, storageKey);
26
+ }
27
+ }
28
+
29
+ for (const storageKey of this.#storageKeysInternal) {
30
+ if (!oldStorageKeys.has(storageKey)) {
31
+ this.dispatchEventToListeners(Events.StorageKeyAdded, storageKey);
32
+ }
33
+ }
34
+ }
35
+
36
+ storageKeys(): string[] {
37
+ return [...this.#storageKeysInternal];
38
+ }
39
+
40
+ mainStorageKey(): string {
41
+ return this.#mainStorageKeyInternal;
42
+ }
43
+
44
+ setMainStorageKey(storageKey: string): void {
45
+ this.#mainStorageKeyInternal = storageKey;
46
+ this.dispatchEventToListeners(Events.MainStorageKeyChanged, {
47
+ mainStorageKey: this.#mainStorageKeyInternal,
48
+ });
49
+ }
50
+ }
51
+
52
+ // TODO(crbug.com/1167717): Make this a const enum again
53
+ // eslint-disable-next-line rulesdir/const_enum
54
+ export enum Events {
55
+ StorageKeyAdded = 'StorageKeyAdded',
56
+ StorageKeyRemoved = 'StorageKeyRemoved',
57
+ MainStorageKeyChanged = 'MainStorageKeyChanged',
58
+ }
59
+
60
+ export interface MainStorageKeyChangedEvent {
61
+ mainStorageKey: string;
62
+ }
63
+
64
+ export type EventTypes = {
65
+ [Events.StorageKeyAdded]: string,
66
+ [Events.StorageKeyRemoved]: string,
67
+ [Events.MainStorageKeyChanged]: MainStorageKeyChangedEvent,
68
+ };
69
+
70
+ // TODO(jarhar): this is the one of the two usages of Capability.None. Do something about it!
71
+ SDKModel.register(StorageKeyManager, {capabilities: Capability.None, autostart: false});
@@ -72,6 +72,7 @@ import * as ServiceWorkerCacheModel from './ServiceWorkerCacheModel.js';
72
72
  import * as ServiceWorkerManager from './ServiceWorkerManager.js';
73
73
  import * as SourceMap from './SourceMap.js';
74
74
  import * as SourceMapManager from './SourceMapManager.js';
75
+ import * as StorageKeyManager from './StorageKeyManager.js';
75
76
  import * as Target from './Target.js';
76
77
  import * as TargetManager from './TargetManager.js';
77
78
  import * as TracingManager from './TracingManager.js';
@@ -141,6 +142,7 @@ export {
141
142
  ServiceWorkerManager,
142
143
  SourceMap,
143
144
  SourceMapManager,
145
+ StorageKeyManager,
144
146
  Target,
145
147
  TargetManager,
146
148
  TracingManager,
@@ -312,8 +312,9 @@ export class ScopeVariableAnalysis {
312
312
  break;
313
313
  case 'Property':
314
314
  if (node.shorthand) {
315
- console.assert(node.value === node.key);
316
315
  console.assert(node.value.type === 'Identifier');
316
+ console.assert(node.key.type === 'Identifier');
317
+ console.assert((node.value as Acorn.ESTree.Identifier).name === (node.key as Acorn.ESTree.Identifier).name);
317
318
  this.#addVariable((node.value as Acorn.ESTree.Identifier).name, node.value.start, DefinitionKind.None, true);
318
319
  } else {
319
320
  if (node.computed) {
@@ -469,8 +470,9 @@ export class ScopeVariableAnalysis {
469
470
  case 'Property':
470
471
  // This is AssignmentProperty inside an object pattern.
471
472
  if (node.shorthand) {
472
- console.assert(node.value === node.key);
473
473
  console.assert(node.value.type === 'Identifier');
474
+ console.assert(node.key.type === 'Identifier');
475
+ console.assert((node.value as Acorn.ESTree.Identifier).name === (node.key as Acorn.ESTree.Identifier).name);
474
476
  this.#addVariable((node.value as Acorn.ESTree.Identifier).name, node.value.start, definitionKind, true);
475
477
  } else {
476
478
  if (node.computed) {
@@ -603,6 +603,7 @@ export class ApplicationPanelSidebar extends UI.Widget.VBox implements SDK.Targe
603
603
 
604
604
  private addDOMStorage(domStorage: DOMStorage): void {
605
605
  console.assert(!this.domStorageTreeElements.get(domStorage));
606
+ console.assert(Boolean(domStorage.storageKey) || Boolean(domStorage.securityOrigin));
606
607
 
607
608
  const domStorageTreeElement = new DOMStorageTreeElement(this.panel, domStorage);
608
609
  this.domStorageTreeElements.set(domStorage, domStorageTreeElement);
@@ -1422,7 +1423,10 @@ export class DOMStorageTreeElement extends ApplicationPanelTreeElement {
1422
1423
  private readonly domStorage: DOMStorage;
1423
1424
  constructor(storagePanel: ResourcesPanel, domStorage: DOMStorage) {
1424
1425
  super(
1425
- storagePanel, domStorage.securityOrigin ? domStorage.securityOrigin : i18nString(UIStrings.localFiles), false);
1426
+ storagePanel,
1427
+ domStorage.securityOrigin ? domStorage.securityOrigin :
1428
+ (domStorage.storageKey ? domStorage.storageKey : i18nString(UIStrings.localFiles)),
1429
+ false);
1426
1430
  this.domStorage = domStorage;
1427
1431
  const icon = UI.Icon.Icon.create('mediumicon-table', 'resource-tree-item');
1428
1432
  this.setLeadingIcons([icon]);
@@ -38,13 +38,15 @@ import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
38
38
 
39
39
  export class DOMStorage extends Common.ObjectWrapper.ObjectWrapper<DOMStorage.EventTypes> {
40
40
  private readonly model: DOMStorageModel;
41
- private readonly securityOriginInternal: string;
41
+ private readonly securityOriginInternal: string|null;
42
+ private readonly storageKeyInternal: string|null;
42
43
  private readonly isLocalStorageInternal: boolean;
43
44
 
44
- constructor(model: DOMStorageModel, securityOrigin: string, isLocalStorage: boolean) {
45
+ constructor(model: DOMStorageModel, securityOrigin: string, storageKey: string, isLocalStorage: boolean) {
45
46
  super();
46
47
  this.model = model;
47
48
  this.securityOriginInternal = securityOrigin;
49
+ this.storageKeyInternal = storageKey;
48
50
  this.isLocalStorageInternal = isLocalStorage;
49
51
  }
50
52
 
@@ -52,14 +54,46 @@ export class DOMStorage extends Common.ObjectWrapper.ObjectWrapper<DOMStorage.Ev
52
54
  return {securityOrigin: securityOrigin, isLocalStorage: isLocalStorage};
53
55
  }
54
56
 
57
+ static storageIdWithSecurityOrigin(securityOrigin: string, isLocalStorage: boolean): Protocol.DOMStorage.StorageId {
58
+ return {securityOrigin: securityOrigin, isLocalStorage: isLocalStorage};
59
+ }
60
+
61
+ static storageIdWithStorageKey(storageKey: string, isLocalStorage: boolean): Protocol.DOMStorage.StorageId {
62
+ return {storageKey: storageKey, isLocalStorage: isLocalStorage};
63
+ }
64
+
65
+ get idWithSecurityOrigin(): Protocol.DOMStorage.StorageId {
66
+ let securityOrigin = '';
67
+ if (this.securityOriginInternal) {
68
+ securityOrigin = this.securityOriginInternal;
69
+ }
70
+ return DOMStorage.storageIdWithSecurityOrigin(securityOrigin, this.isLocalStorageInternal);
71
+ }
72
+
73
+ get idWithStorageKey(): Protocol.DOMStorage.StorageId {
74
+ let storageKey = '';
75
+ if (this.storageKeyInternal) {
76
+ storageKey = this.storageKeyInternal;
77
+ }
78
+ return DOMStorage.storageIdWithStorageKey(storageKey, this.isLocalStorageInternal);
79
+ }
80
+
55
81
  get id(): Protocol.DOMStorage.StorageId {
56
- return DOMStorage.storageId(this.securityOriginInternal, this.isLocalStorageInternal);
82
+ // TODO(crbug.com/1313434) Prioritize storageKey once everything is ready
83
+ if (this.securityOriginInternal) {
84
+ return this.idWithSecurityOrigin;
85
+ }
86
+ return this.idWithStorageKey;
57
87
  }
58
88
 
59
- get securityOrigin(): string {
89
+ get securityOrigin(): string|null {
60
90
  return this.securityOriginInternal;
61
91
  }
62
92
 
93
+ get storageKey(): string|null {
94
+ return this.storageKeyInternal;
95
+ }
96
+
63
97
  get isLocalStorage(): boolean {
64
98
  return this.isLocalStorageInternal;
65
99
  }
@@ -116,6 +150,7 @@ export namespace DOMStorage {
116
150
 
117
151
  export class DOMStorageModel extends SDK.SDKModel.SDKModel<EventTypes> {
118
152
  private readonly securityOriginManager: SDK.SecurityOriginManager.SecurityOriginManager|null;
153
+ private readonly storageKeyManagerInternal: SDK.StorageKeyManager.StorageKeyManager|null;
119
154
  private storagesInternal: {
120
155
  [x: string]: DOMStorage,
121
156
  };
@@ -126,10 +161,15 @@ export class DOMStorageModel extends SDK.SDKModel.SDKModel<EventTypes> {
126
161
  super(target);
127
162
 
128
163
  this.securityOriginManager = target.model(SDK.SecurityOriginManager.SecurityOriginManager);
164
+ this.storageKeyManagerInternal = target.model(SDK.StorageKeyManager.StorageKeyManager);
129
165
  this.storagesInternal = {};
130
166
  this.agent = target.domstorageAgent();
131
167
  }
132
168
 
169
+ get storageKeyManagerForTest(): SDK.StorageKeyManager.StorageKeyManager|null {
170
+ return this.storageKeyManagerInternal;
171
+ }
172
+
133
173
  enable(): void {
134
174
  if (this.enabled) {
135
175
  return;
@@ -146,6 +186,16 @@ export class DOMStorageModel extends SDK.SDKModel.SDKModel<EventTypes> {
146
186
  this.addOrigin(securityOrigin);
147
187
  }
148
188
  }
189
+ if (this.storageKeyManagerInternal) {
190
+ this.storageKeyManagerInternal.addEventListener(
191
+ SDK.StorageKeyManager.Events.StorageKeyAdded, this.storageKeyAdded, this);
192
+ this.storageKeyManagerInternal.addEventListener(
193
+ SDK.StorageKeyManager.Events.StorageKeyRemoved, this.storageKeyRemoved, this);
194
+
195
+ for (const storageKey of this.storageKeyManagerInternal.storageKeys()) {
196
+ this.addStorageKey(storageKey);
197
+ }
198
+ }
149
199
  void this.agent.invoke_enable();
150
200
 
151
201
  this.enabled = true;
@@ -156,7 +206,7 @@ export class DOMStorageModel extends SDK.SDKModel.SDKModel<EventTypes> {
156
206
  return;
157
207
  }
158
208
  for (const isLocal of [true, false]) {
159
- const key = this.storageKey(origin, isLocal);
209
+ const key = this.keyForSecurityOrigin(origin, isLocal);
160
210
  const storage = this.storagesInternal[key];
161
211
  if (!storage) {
162
212
  return;
@@ -167,10 +217,30 @@ export class DOMStorageModel extends SDK.SDKModel.SDKModel<EventTypes> {
167
217
  this.addOrigin(origin);
168
218
  }
169
219
 
220
+ clearForStorageKey(storageKey: string): void {
221
+ if (!this.enabled) {
222
+ return;
223
+ }
224
+ for (const isLocal of [true, false]) {
225
+ const key = this.keyForStorageKey(storageKey, isLocal);
226
+ const storage = this.storagesInternal[key];
227
+ if (!storage) {
228
+ return;
229
+ }
230
+ storage.clear();
231
+ }
232
+ this.removeStorageKey(storageKey);
233
+ this.addStorageKey(storageKey);
234
+ }
235
+
170
236
  private securityOriginAdded(event: Common.EventTarget.EventTargetEvent<string>): void {
171
237
  this.addOrigin(event.data);
172
238
  }
173
239
 
240
+ private storageKeyAdded(event: Common.EventTarget.EventTargetEvent<string>): void {
241
+ this.addStorageKey(event.data);
242
+ }
243
+
174
244
  private addOrigin(securityOrigin: string): void {
175
245
  const parsed = new Common.ParsedURL.ParsedURL(securityOrigin);
176
246
  // These are "opaque" origins which are not supposed to support DOM storage.
@@ -179,21 +249,67 @@ export class DOMStorageModel extends SDK.SDKModel.SDKModel<EventTypes> {
179
249
  }
180
250
 
181
251
  for (const isLocal of [true, false]) {
182
- const key = this.storageKey(securityOrigin, isLocal);
252
+ const key = this.keyForSecurityOrigin(securityOrigin, isLocal);
183
253
  console.assert(!this.storagesInternal[key]);
184
- const storage = new DOMStorage(this, securityOrigin, isLocal);
254
+ if (this.duplicateExists(key)) {
255
+ continue;
256
+ }
257
+ const storage = new DOMStorage(this, securityOrigin, '', isLocal);
258
+ this.storagesInternal[key] = storage;
259
+ this.dispatchEventToListeners(Events.DOMStorageAdded, storage);
260
+ }
261
+ }
262
+
263
+ private addStorageKey(storageKey: string): void {
264
+ for (const isLocal of [true, false]) {
265
+ const key = this.keyForStorageKey(storageKey, isLocal);
266
+ console.assert(!this.storagesInternal[key]);
267
+ if (this.duplicateExists(key)) {
268
+ continue;
269
+ }
270
+ const storage = new DOMStorage(this, '', storageKey, isLocal);
185
271
  this.storagesInternal[key] = storage;
186
272
  this.dispatchEventToListeners(Events.DOMStorageAdded, storage);
187
273
  }
188
274
  }
189
275
 
276
+ private duplicateExists(key: string): boolean {
277
+ const parsedKey = JSON.parse(key);
278
+ for (const storageInternal in this.storagesInternal) {
279
+ const parsedStorageInternalKey = JSON.parse(storageInternal);
280
+ if (parsedKey.isLocalStorage === parsedStorageInternalKey.isLocalStorage) {
281
+ if (parsedKey.storageKey?.slice(0, -1) === parsedStorageInternalKey.securityOrigin ||
282
+ parsedKey.securityOrigin === parsedStorageInternalKey.storageKey?.slice(0, -1)) {
283
+ return true;
284
+ }
285
+ }
286
+ }
287
+ return false;
288
+ }
289
+
190
290
  private securityOriginRemoved(event: Common.EventTarget.EventTargetEvent<string>): void {
191
291
  this.removeOrigin(event.data);
192
292
  }
193
293
 
294
+ private storageKeyRemoved(event: Common.EventTarget.EventTargetEvent<string>): void {
295
+ this.removeStorageKey(event.data);
296
+ }
297
+
194
298
  private removeOrigin(securityOrigin: string): void {
195
299
  for (const isLocal of [true, false]) {
196
- const key = this.storageKey(securityOrigin, isLocal);
300
+ const key = this.keyForSecurityOrigin(securityOrigin, isLocal);
301
+ const storage = this.storagesInternal[key];
302
+ if (!storage) {
303
+ continue;
304
+ }
305
+ delete this.storagesInternal[key];
306
+ this.dispatchEventToListeners(Events.DOMStorageRemoved, storage);
307
+ }
308
+ }
309
+
310
+ private removeStorageKey(storageKey: string): void {
311
+ for (const isLocal of [true, false]) {
312
+ const key = this.keyForStorageKey(storageKey, isLocal);
197
313
  const storage = this.storagesInternal[key];
198
314
  if (!storage) {
199
315
  continue;
@@ -203,8 +319,20 @@ export class DOMStorageModel extends SDK.SDKModel.SDKModel<EventTypes> {
203
319
  }
204
320
  }
205
321
 
206
- private storageKey(securityOrigin: string, isLocalStorage: boolean): string {
207
- return JSON.stringify(DOMStorage.storageId(securityOrigin, isLocalStorage));
322
+ private storageKey(securityOrigin: string, storageKey: string, isLocalStorage: boolean): string {
323
+ // TODO(crbug.com/1313434) Prioritize storageKey once everything is ready
324
+ if (securityOrigin) {
325
+ return JSON.stringify(DOMStorage.storageIdWithSecurityOrigin(securityOrigin, isLocalStorage));
326
+ }
327
+ return JSON.stringify(DOMStorage.storageIdWithStorageKey(storageKey, isLocalStorage));
328
+ }
329
+
330
+ private keyForSecurityOrigin(securityOrigin: string, isLocalStorage: boolean): string {
331
+ return this.storageKey(securityOrigin, '', isLocalStorage);
332
+ }
333
+
334
+ private keyForStorageKey(storageKey: string, isLocalStorage: boolean): string {
335
+ return this.storageKey('', storageKey, isLocalStorage);
208
336
  }
209
337
 
210
338
  domStorageItemsCleared(storageId: Protocol.DOMStorage.StorageId): void {
@@ -147,6 +147,7 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
147
147
  private reportView: UI.ReportView.ReportView;
148
148
  private target: SDK.Target.Target|null;
149
149
  private securityOrigin: string|null;
150
+ private storageKey: string|null;
150
151
  private settings: Map<Protocol.Storage.StorageType, Common.Settings.Setting<boolean>>;
151
152
  private includeThirdPartyCookiesSetting: Common.Settings.Setting<boolean>;
152
153
  private quotaRow: HTMLElement;
@@ -180,6 +181,7 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
180
181
  this.reportView.show(this.contentElement);
181
182
  this.target = null;
182
183
  this.securityOrigin = null;
184
+ this.storageKey = null;
183
185
 
184
186
  this.settings = new Map();
185
187
  for (const type of AllStorageTypes) {
@@ -277,6 +279,11 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
277
279
  securityOriginManager.mainSecurityOrigin(), securityOriginManager.unreachableMainSecurityOrigin());
278
280
  securityOriginManager.addEventListener(
279
281
  SDK.SecurityOriginManager.Events.MainSecurityOriginChanged, this.originChanged, this);
282
+ const storageKeyManager =
283
+ target.model(SDK.StorageKeyManager.StorageKeyManager) as SDK.StorageKeyManager.StorageKeyManager;
284
+ this.updateStorageKey(storageKeyManager.mainStorageKey());
285
+ storageKeyManager.addEventListener(
286
+ SDK.StorageKeyManager.Events.MainStorageKeyChanged, this.storageKeyChanged, this);
280
287
  }
281
288
 
282
289
  targetRemoved(target: SDK.Target.Target): void {
@@ -287,6 +294,10 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
287
294
  SDK.SecurityOriginManager.SecurityOriginManager;
288
295
  securityOriginManager.removeEventListener(
289
296
  SDK.SecurityOriginManager.Events.MainSecurityOriginChanged, this.originChanged, this);
297
+ const storageKeyManager =
298
+ target.model(SDK.StorageKeyManager.StorageKeyManager) as SDK.StorageKeyManager.StorageKeyManager;
299
+ storageKeyManager.removeEventListener(
300
+ SDK.StorageKeyManager.Events.MainStorageKeyChanged, this.storageKeyChanged, this);
290
301
  }
291
302
 
292
303
  private originChanged(
@@ -295,6 +306,12 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
295
306
  this.updateOrigin(mainSecurityOrigin, unreachableMainSecurityOrigin);
296
307
  }
297
308
 
309
+ private storageKeyChanged(
310
+ event: Common.EventTarget.EventTargetEvent<SDK.StorageKeyManager.MainStorageKeyChangedEvent>): void {
311
+ const {mainStorageKey} = event.data;
312
+ this.updateStorageKey(mainStorageKey);
313
+ }
314
+
298
315
  private updateOrigin(mainOrigin: string, unreachableMainOrigin: string|null): void {
299
316
  const oldOrigin = this.securityOrigin;
300
317
  if (unreachableMainOrigin) {
@@ -313,6 +330,20 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
313
330
  void this.doUpdate();
314
331
  }
315
332
 
333
+ private updateStorageKey(mainStorageKey: string): void {
334
+ const oldStorageKey = this.storageKey;
335
+
336
+ this.storageKey = mainStorageKey;
337
+ this.reportView.setSubtitle(mainStorageKey);
338
+
339
+ if (oldStorageKey !== this.storageKey) {
340
+ this.quotaOverrideControlRow.classList.add('hidden');
341
+ this.quotaOverrideCheckbox.checkboxElement.checked = false;
342
+ this.quotaOverrideErrorMessage.textContent = '';
343
+ }
344
+ void this.doUpdate();
345
+ }
346
+
316
347
  private async applyQuotaOverrideFromInputField(): Promise<void> {
317
348
  if (!this.target || !this.securityOrigin) {
318
349
  this.quotaOverrideErrorMessage.textContent = i18nString(UIStrings.internalError);
@@ -375,7 +406,12 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
375
406
 
376
407
  if (this.target) {
377
408
  const includeThirdPartyCookies = this.includeThirdPartyCookiesSetting.get();
378
- StorageView.clear(this.target, this.securityOrigin, selectedStorageTypes, includeThirdPartyCookies);
409
+ // TODO(crbug.com/1313434) Prioritize storageKey once everything is ready
410
+ if (this.securityOrigin) {
411
+ StorageView.clear(this.target, this.securityOrigin, selectedStorageTypes, includeThirdPartyCookies);
412
+ } else if (this.storageKey) {
413
+ StorageView.clearByStorageKey(this.target, this.storageKey, selectedStorageTypes);
414
+ }
379
415
  }
380
416
 
381
417
  this.clearButton.disabled = true;
@@ -436,6 +472,20 @@ export class StorageView extends UI.ThrottledWidget.ThrottledWidget {
436
472
  }
437
473
  }
438
474
 
475
+ static clearByStorageKey(target: SDK.Target.Target, storageKey: string, selectedStorageTypes: string[]): void {
476
+ // TODO(crbug.com/1313434) Invoke protocol `clear` once it ready for storageKey
477
+
478
+ const set = new Set(selectedStorageTypes);
479
+ const hasAll = set.has(Protocol.Storage.StorageType.All);
480
+
481
+ if (set.has(Protocol.Storage.StorageType.Local_storage) || hasAll) {
482
+ const storageModel = target.model(DOMStorageModel);
483
+ if (storageModel) {
484
+ storageModel.clearForStorageKey(storageKey);
485
+ }
486
+ }
487
+ }
488
+
439
489
  async doUpdate(): Promise<void> {
440
490
  if (!this.securityOrigin || !this.target) {
441
491
  this.quotaRow.textContent = '';
@@ -556,6 +606,14 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
556
606
  return false;
557
607
  }
558
608
 
609
+ private async clear(target: SDK.Target.Target, resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel):
610
+ Promise<void> {
611
+ const storageKey = await resourceTreeModel.getMainStorageKey();
612
+ if (storageKey) {
613
+ StorageView.clearByStorageKey(target, storageKey, AllStorageTypes);
614
+ }
615
+ }
616
+
559
617
  private handleClear(includeThirdPartyCookies: boolean): boolean {
560
618
  const target = SDK.TargetManager.TargetManager.instance().mainTarget();
561
619
  if (!target) {
@@ -566,11 +624,12 @@ export class ActionDelegate implements UI.ActionRegistration.ActionDelegate {
566
624
  return false;
567
625
  }
568
626
  const securityOrigin = resourceTreeModel.getMainSecurityOrigin();
569
- if (!securityOrigin) {
570
- return false;
627
+ // TODO(crbug.com/1313434) Prioritize storageKey functionality once everything is ready
628
+ if (securityOrigin) {
629
+ StorageView.clear(target, securityOrigin, AllStorageTypes, includeThirdPartyCookies);
630
+ return true;
571
631
  }
572
-
573
- StorageView.clear(target, securityOrigin, AllStorageTypes, includeThirdPartyCookies);
632
+ void this.clear(target, resourceTreeModel);
574
633
  return true;
575
634
  }
576
635
  }
@@ -294,7 +294,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
294
294
  if (node.isAdFrameNode()) {
295
295
  const config = ElementsComponents.AdornerManager.getRegisteredAdorner(
296
296
  ElementsComponents.AdornerManager.RegisteredAdorners.AD);
297
- const adorner = this.adorn(config, this.tagTypeContext);
297
+ const adorner = this.adorn(config);
298
298
  UI.Tooltip.Tooltip.install(adorner, i18nString(UIStrings.thisFrameWasIdentifiedAsAnAd));
299
299
  }
300
300
  }
@@ -1962,17 +1962,22 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
1962
1962
  }
1963
1963
 
1964
1964
  // TODO: add unit tests for adorner-related methods after component and TypeScript works are done
1965
- adorn({name}: {name: string}, context: OpeningTagContext): Adorners.Adorner.Adorner {
1966
- const adornerContent = document.createElement('span');
1967
- adornerContent.textContent = name;
1965
+ adorn({name}: {name: string}, content?: HTMLElement): Adorners.Adorner.Adorner {
1966
+ let adornerContent = content;
1967
+ if (!adornerContent) {
1968
+ adornerContent = document.createElement('span');
1969
+ adornerContent.textContent = name;
1970
+ }
1968
1971
  const adorner = new Adorners.Adorner.Adorner();
1969
1972
  adorner.data = {
1970
1973
  name,
1971
1974
  content: adornerContent,
1972
1975
  };
1973
- context.adorners.push(adorner);
1974
- ElementsPanel.instance().registerAdorner(adorner);
1975
- this.updateAdorners(context);
1976
+ if (isOpeningTag(this.tagTypeContext)) {
1977
+ this.tagTypeContext.adorners.push(adorner);
1978
+ ElementsPanel.instance().registerAdorner(adorner);
1979
+ this.updateAdorners(this.tagTypeContext);
1980
+ }
1976
1981
  return adorner;
1977
1982
  }
1978
1983
 
@@ -2010,14 +2015,16 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2010
2015
  }
2011
2016
  }
2012
2017
 
2013
- removeAllAdorners(context: OpeningTagContext): void {
2014
- for (const adorner of context.adorners) {
2015
- ElementsPanel.instance().deregisterAdorner(adorner);
2016
- adorner.remove();
2017
- }
2018
+ removeAllAdorners(): void {
2019
+ if (isOpeningTag(this.tagTypeContext)) {
2020
+ for (const adorner of this.tagTypeContext.adorners) {
2021
+ ElementsPanel.instance().deregisterAdorner(adorner);
2022
+ adorner.remove();
2023
+ }
2018
2024
 
2019
- context.adorners = [];
2020
- this.updateAdorners(context);
2025
+ this.tagTypeContext.adorners = [];
2026
+ this.updateAdorners(this.tagTypeContext);
2027
+ }
2021
2028
  }
2022
2029
 
2023
2030
  private updateAdorners(context: OpeningTagContext): void {
@@ -2098,7 +2105,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2098
2105
 
2099
2106
  const config = ElementsComponents.AdornerManager.getRegisteredAdorner(
2100
2107
  ElementsComponents.AdornerManager.RegisteredAdorners.GRID);
2101
- const adorner = this.adorn(config, context);
2108
+ const adorner = this.adorn(config);
2102
2109
  adorner.classList.add('grid');
2103
2110
 
2104
2111
  const onClick = (((): void => {
@@ -2135,7 +2142,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2135
2142
  }
2136
2143
  const config = ElementsComponents.AdornerManager.getRegisteredAdorner(
2137
2144
  ElementsComponents.AdornerManager.RegisteredAdorners.SCROLL_SNAP);
2138
- const adorner = this.adorn(config, context);
2145
+ const adorner = this.adorn(config);
2139
2146
  adorner.classList.add('scroll-snap');
2140
2147
 
2141
2148
  const onClick = (((): void => {
@@ -2174,7 +2181,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2174
2181
  }
2175
2182
  const config = ElementsComponents.AdornerManager.getRegisteredAdorner(
2176
2183
  ElementsComponents.AdornerManager.RegisteredAdorners.FLEX);
2177
- const adorner = this.adorn(config, context);
2184
+ const adorner = this.adorn(config);
2178
2185
  adorner.classList.add('flex');
2179
2186
 
2180
2187
  const onClick = (((): void => {
@@ -2213,7 +2220,7 @@ export class ElementsTreeElement extends UI.TreeOutline.TreeElement {
2213
2220
  }
2214
2221
  const config = ElementsComponents.AdornerManager.getRegisteredAdorner(
2215
2222
  ElementsComponents.AdornerManager.RegisteredAdorners.CONTAINER);
2216
- const adorner = this.adorn(config, context);
2223
+ const adorner = this.adorn(config);
2217
2224
  adorner.classList.add('container');
2218
2225
 
2219
2226
  const onClick = (((): void => {
@@ -43,6 +43,7 @@ import {ElementsPanel} from './ElementsPanel.js';
43
43
  import {ElementsTreeElement, InitialChildrenLimit} from './ElementsTreeElement.js';
44
44
  import elementsTreeOutlineStyles from './elementsTreeOutline.css.js';
45
45
  import {ImagePreviewPopover} from './ImagePreviewPopover.js';
46
+ import {TopLayerContainer} from './TopLayerContainer.js';
46
47
 
47
48
  import type {MarkerDecoratorRegistration} from './MarkerDecorator.js';
48
49
 
@@ -100,6 +101,7 @@ export class ElementsTreeOutline extends
100
101
  private treeElementBeingDragged?: ElementsTreeElement;
101
102
  private dragOverTreeElement?: ElementsTreeElement;
102
103
  private updateModifiedNodesTimeout?: number;
104
+ private topLayerContainer?: TopLayerContainer;
103
105
 
104
106
  constructor(omitRootDOMNode?: boolean, selectEnabled?: boolean, hideGutter?: boolean) {
105
107
  super();
@@ -1001,6 +1003,7 @@ export class ElementsTreeOutline extends
1001
1003
  domModel.addEventListener(SDK.DOMModel.Events.DocumentUpdated, this.documentUpdated, this);
1002
1004
  domModel.addEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.childNodeCountUpdated, this);
1003
1005
  domModel.addEventListener(SDK.DOMModel.Events.DistributedNodesChanged, this.distributedNodesChanged, this);
1006
+ domModel.addEventListener(SDK.DOMModel.Events.TopLayerElementsChanged, this.topLayerElementsChanged, this);
1004
1007
  }
1005
1008
 
1006
1009
  unwireFromDOMModel(domModel: SDK.DOMModel.DOMModel): void {
@@ -1013,6 +1016,7 @@ export class ElementsTreeOutline extends
1013
1016
  domModel.removeEventListener(SDK.DOMModel.Events.DocumentUpdated, this.documentUpdated, this);
1014
1017
  domModel.removeEventListener(SDK.DOMModel.Events.ChildNodeCountUpdated, this.childNodeCountUpdated, this);
1015
1018
  domModel.removeEventListener(SDK.DOMModel.Events.DistributedNodesChanged, this.distributedNodesChanged, this);
1019
+ domModel.removeEventListener(SDK.DOMModel.Events.TopLayerElementsChanged, this.topLayerElementsChanged, this);
1016
1020
  elementsTreeOutlineByDOMModel.delete(domModel);
1017
1021
  }
1018
1022
 
@@ -1164,13 +1168,41 @@ export class ElementsTreeOutline extends
1164
1168
  return Promise.resolve();
1165
1169
  }
1166
1170
 
1167
- return new Promise(resolve => {
1168
- treeElement.node().getChildNodes(() => {
1169
- populatedTreeElements.add(treeElement);
1170
- this.updateModifiedParentNode(treeElement.node());
1171
- resolve();
1172
- });
1173
- });
1171
+ return new Promise<void>(resolve => {
1172
+ treeElement.node().getChildNodes(() => {
1173
+ populatedTreeElements.add(treeElement);
1174
+ this.updateModifiedParentNode(treeElement.node());
1175
+ resolve();
1176
+ });
1177
+ })
1178
+ .then(() => {
1179
+ if (treeElement.node().nodeName() === 'BODY') {
1180
+ void this.createTopLayerContainer(treeElement);
1181
+ }
1182
+ });
1183
+ }
1184
+
1185
+ async createTopLayerContainer(bodyElement: ElementsTreeElement): Promise<void> {
1186
+ if (!this.topLayerContainer) {
1187
+ this.topLayerContainer = new TopLayerContainer(bodyElement);
1188
+ }
1189
+ this.topLayerContainer.updateBody(bodyElement);
1190
+ await this.updateTopLayerContainer();
1191
+ }
1192
+
1193
+ async updateTopLayerContainer(): Promise<void> {
1194
+ if (this.topLayerContainer) {
1195
+ const bodyElement = this.topLayerContainer.bodyElement;
1196
+ if (!bodyElement.children().includes(this.topLayerContainer) && !this.topLayerContainer.parent &&
1197
+ !this.topLayerContainer.treeOutline) {
1198
+ bodyElement.insertChild(this.topLayerContainer, bodyElement.childCount() - 1);
1199
+ }
1200
+ this.topLayerContainer.removeChildren();
1201
+ const topLayerElementsExists = await this.topLayerContainer.addTopLayerElementsAsChildren();
1202
+ if (!topLayerElementsExists) {
1203
+ bodyElement.removeChild(this.topLayerContainer);
1204
+ }
1205
+ }
1174
1206
  }
1175
1207
 
1176
1208
  private createElementTreeElement(node: SDK.DOMModel.DOMNode, isClosingTag?: boolean): ElementsTreeElement {
@@ -1318,7 +1350,7 @@ export class ElementsTreeOutline extends
1318
1350
  }
1319
1351
 
1320
1352
  insertChildElement(
1321
- treeElement: ElementsTreeElement, child: SDK.DOMModel.DOMNode, index: number,
1353
+ treeElement: ElementsTreeElement|TopLayerContainer, child: SDK.DOMModel.DOMNode, index: number,
1322
1354
  isClosingTag?: boolean): ElementsTreeElement {
1323
1355
  const newElement = this.createElementTreeElement(child, isClosingTag);
1324
1356
  treeElement.insertChild(newElement, index);
@@ -1427,6 +1459,10 @@ export class ElementsTreeOutline extends
1427
1459
  }
1428
1460
  }
1429
1461
 
1462
+ private async topLayerElementsChanged(): Promise<void> {
1463
+ await this.updateTopLayerContainer();
1464
+ }
1465
+
1430
1466
  private static treeOutlineSymbol = Symbol('treeOutline');
1431
1467
  }
1432
1468
 
@@ -0,0 +1,97 @@
1
+ // Copyright 2022 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+ import * as i18n from '../../core/i18n/i18n.js';
5
+ import * as SDK from '../../core/sdk/sdk.js';
6
+ import * as UI from '../../ui/legacy/legacy.js';
7
+
8
+ import * as ElementsComponents from './components/components.js';
9
+ import * as ElementsTreeOutline from './ElementsTreeOutline.js';
10
+
11
+ import type {ElementsTreeElement} from './ElementsTreeElement.js';
12
+
13
+ const UIStrings = {
14
+ /**
15
+ * @description Top layer is rendered closest to the user within a viewport, therefore its elements always appear on top of all other content
16
+ */
17
+ topLayer: 'top-layer',
18
+ };
19
+
20
+ const str_ = i18n.i18n.registerUIStrings('panels/elements/TopLayerContainer.ts', UIStrings);
21
+ const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
22
+
23
+ export class TopLayerContainer extends UI.TreeOutline.TreeElement {
24
+ treeOutline: ElementsTreeOutline.ElementsTreeOutline|null;
25
+ domModel: SDK.DOMModel.DOMModel;
26
+ currentTopLayerElements: Set<ElementsTreeElement>;
27
+ bodyElement: ElementsTreeElement;
28
+
29
+ constructor(bodyElement: ElementsTreeElement) {
30
+ super('#top-layer');
31
+ this.bodyElement = bodyElement;
32
+ this.domModel = bodyElement.node().domModel();
33
+ this.treeOutline = null;
34
+ this.currentTopLayerElements = new Set();
35
+ }
36
+
37
+ updateBody(bodyElement: ElementsTreeElement): void {
38
+ this.bodyElement = bodyElement;
39
+ }
40
+
41
+ async addTopLayerElementsAsChildren(): Promise<boolean> {
42
+ this.removeCurrentTopLayerElementsAdorners();
43
+ this.currentTopLayerElements = new Set();
44
+ const newTopLayerElementsIDs = await this.domModel.getTopLayerElements();
45
+ if (newTopLayerElementsIDs === null) {
46
+ return false;
47
+ }
48
+ let topLayerElementIndex = 0;
49
+ if (newTopLayerElementsIDs) {
50
+ for (const elementID of newTopLayerElementsIDs) {
51
+ const topLayerDOMNode = this.domModel.idToDOMNode.get(elementID);
52
+ // Will need to add support for backdrop in the future.
53
+ if (topLayerDOMNode && topLayerDOMNode.nodeName() !== '::backdrop') {
54
+ topLayerElementIndex++;
55
+ const topLayerElementShortcut = new SDK.DOMModel.DOMNodeShortcut(
56
+ this.domModel.target(), topLayerDOMNode.backendNodeId(), 0, topLayerDOMNode.nodeName());
57
+ const topLayerTreeElement = this.treeOutline?.treeElementByNode.get(topLayerDOMNode);
58
+ const topLayerElementRepresentation = new ElementsTreeOutline.ShortcutTreeElement(topLayerElementShortcut);
59
+ if (topLayerTreeElement && !this.currentTopLayerElements.has(topLayerTreeElement)) {
60
+ this.appendChild(topLayerElementRepresentation);
61
+ this.addTopLayerAdorner(topLayerTreeElement, topLayerElementRepresentation, topLayerElementIndex);
62
+ this.currentTopLayerElements.add(topLayerTreeElement);
63
+ }
64
+ }
65
+ }
66
+ }
67
+ return topLayerElementIndex > 0;
68
+ }
69
+
70
+ private removeCurrentTopLayerElementsAdorners(): void {
71
+ for (const topLayerElement of this.currentTopLayerElements) {
72
+ topLayerElement.removeAllAdorners();
73
+ }
74
+ }
75
+
76
+ private addTopLayerAdorner(
77
+ element: ElementsTreeElement, topLayerElementRepresentation: ElementsTreeOutline.ShortcutTreeElement,
78
+ topLayerElementIndex: number): void {
79
+ const config = ElementsComponents.AdornerManager.getRegisteredAdorner(
80
+ ElementsComponents.AdornerManager.RegisteredAdorners.TOP_LAYER);
81
+ const adornerContent = document.createElement('span');
82
+ adornerContent.textContent = ` top-layer (${topLayerElementIndex}) `;
83
+ const adorner = element?.adorn(config, adornerContent);
84
+ if (adorner) {
85
+ const onClick = (((): void => {
86
+ topLayerElementRepresentation.revealAndSelect();
87
+ }) as EventListener);
88
+ adorner.addInteraction(onClick, {
89
+ isToggle: false,
90
+ shouldPropagateOnKeydown: false,
91
+ ariaLabelDefault: i18nString(UIStrings.topLayer),
92
+ ariaLabelActive: i18nString(UIStrings.topLayer),
93
+ });
94
+ adorner.addEventListener('mousedown', e => e.consume(), false);
95
+ }
96
+ }
97
+ }
@@ -30,6 +30,7 @@ export enum RegisteredAdorners {
30
30
  SCROLL_SNAP = 'scroll-snap',
31
31
  CONTAINER = 'container',
32
32
  SLOT = 'slot',
33
+ TOP_LAYER = 'top-layer',
33
34
  }
34
35
 
35
36
  // This enum-like const object serves as the authoritative registry for all the
@@ -72,6 +73,12 @@ export function getRegisteredAdorner(which: RegisteredAdorners): RegisteredAdorn
72
73
  category: AdornerCategories.LAYOUT,
73
74
  enabledByDefault: true,
74
75
  };
76
+ case RegisteredAdorners.TOP_LAYER:
77
+ return {
78
+ name: 'top-layer',
79
+ category: AdornerCategories.LAYOUT,
80
+ enabledByDefault: true,
81
+ };
75
82
  }
76
83
  }
77
84
 
@@ -9,6 +9,7 @@ import './DOMLinkifier.js';
9
9
  import './DOMPath.js';
10
10
  import './ElementsSidebarPane.js';
11
11
  import './ElementsTreeElement.js';
12
+ import './TopLayerContainer.js';
12
13
  import './ElementsTreeOutline.js';
13
14
  import './EventListenersWidget.js';
14
15
  import './MarkerDecorator.js';
@@ -25,6 +26,7 @@ import './ElementsPanel.js';
25
26
  import './ClassesPaneWidget.js';
26
27
  import './ElementStatePaneWidget.js';
27
28
  import './ElementsTreeElementHighlighter.js';
29
+ import './TopLayerContainer.js';
28
30
 
29
31
  import * as ClassesPaneWidget from './ClassesPaneWidget.js';
30
32
  import * as ColorSwatchPopoverIcon from './ColorSwatchPopoverIcon.js';
@@ -53,6 +55,7 @@ import * as StylePropertyHighlighter from './StylePropertyHighlighter.js';
53
55
  import * as StylePropertyTreeElement from './StylePropertyTreeElement.js';
54
56
  import * as StylePropertyUtils from './StylePropertyUtils.js';
55
57
  import * as StylesSidebarPane from './StylesSidebarPane.js';
58
+ import * as TopLayerContainer from './TopLayerContainer.js';
56
59
 
57
60
  export {
58
61
  ClassesPaneWidget,
@@ -82,4 +85,5 @@ export {
82
85
  StylePropertyTreeElement,
83
86
  StylePropertyUtils,
84
87
  StylesSidebarPane,
88
+ TopLayerContainer,
85
89
  };
@@ -319,6 +319,6 @@ export class OpenLinearMemoryInspector extends UI.Widget.VBox implements UI.Cont
319
319
  private async openMemoryInspector(obj: SDK.RemoteObject.RemoteObject): Promise<void> {
320
320
  const controller = LinearMemoryInspector.LinearMemoryInspectorController.LinearMemoryInspectorController.instance();
321
321
  Host.userMetrics.linearMemoryInspectorRevealedFrom(Host.UserMetrics.LinearMemoryInspectorRevealedFrom.ContextMenu);
322
- void controller.openInspectorView(obj, 0);
322
+ void controller.openInspectorView(obj);
323
323
  }
324
324
  }
@@ -77,17 +77,16 @@ async function getBufferFromObject(obj: SDK.RemoteObject.RemoteObject): Promise<
77
77
  return new SDK.RemoteObject.RemoteArrayBuffer(obj);
78
78
  }
79
79
 
80
+ export function isDWARFMemoryObject(obj: SDK.RemoteObject.RemoteObject): boolean {
81
+ return obj instanceof Bindings.DebuggerLanguagePlugins.ValueNode && obj.inspectableAddress !== undefined;
82
+ }
83
+
80
84
  export function isMemoryObjectProperty(obj: SDK.RemoteObject.RemoteObject): boolean {
81
85
  const isWasmOrBuffer = obj.type === 'object' && obj.subtype && ACCEPTED_MEMORY_TYPES.includes(obj.subtype);
82
- if (isWasmOrBuffer) {
86
+ if (isWasmOrBuffer || isDWARFMemoryObject(obj)) {
83
87
  return true;
84
88
  }
85
89
 
86
- const isWasmDWARF = obj instanceof Bindings.DebuggerLanguagePlugins.ValueNode;
87
- if (isWasmDWARF) {
88
- return obj.inspectableAddress !== undefined;
89
- }
90
-
91
90
  return false;
92
91
  }
93
92
 
@@ -388,11 +388,13 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
388
388
  }
389
389
 
390
390
  static appendMemoryIcon(element: Element, obj: SDK.RemoteObject.RemoteObject): void {
391
- // We show the memory icon only on ArrayBuffer and WebAssembly.Memory instances.
391
+ // We show the memory icon only on ArrayBuffer, WebAssembly.Memory and DWARF memory instances.
392
392
  // TypedArrays DataViews are also supported, but showing the icon next to their
393
393
  // previews is quite a significant visual overhead, and users can easily get to
394
394
  // their buffers and open the memory inspector from there.
395
- if (!LinearMemoryInspector.LinearMemoryInspectorController.isMemoryObjectProperty(obj)) {
395
+ const arrayBufferOrWasmMemory =
396
+ (obj.type === 'object' && (obj.subtype === 'arraybuffer' || obj.subtype === 'webassemblymemory'));
397
+ if (!arrayBufferOrWasmMemory && !LinearMemoryInspector.LinearMemoryInspectorController.isDWARFMemoryObject(obj)) {
396
398
  return;
397
399
  }
398
400
  const memoryIcon = new IconButton.Icon.Icon();
@@ -408,7 +410,7 @@ export class ObjectPropertiesSection extends UI.TreeOutline.TreeOutlineInShadow
408
410
  const controller =
409
411
  LinearMemoryInspector.LinearMemoryInspectorController.LinearMemoryInspectorController.instance();
410
412
  Host.userMetrics.linearMemoryInspectorRevealedFrom(Host.UserMetrics.LinearMemoryInspectorRevealedFrom.MemoryIcon);
411
- void controller.openInspectorView(obj, 0);
413
+ void controller.openInspectorView(obj);
412
414
  };
413
415
 
414
416
  UI.Tooltip.Tooltip.install(memoryIcon, 'Reveal in Memory Inspector panel');
package/package.json CHANGED
@@ -55,5 +55,5 @@
55
55
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
56
56
  "watch": "vpython third_party/node/node.py --output scripts/watch_build.js"
57
57
  },
58
- "version": "1.0.1016605"
58
+ "version": "1.0.1017091"
59
59
  }