chrome-devtools-frontend 1.0.1661063 → 1.0.1662965

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 (131) hide show
  1. package/.agents/skills/devtools-unicode-escaping/SKILL.md +64 -0
  2. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +19 -9
  3. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +44 -16
  4. package/.agents/skills/migrate-chromium-test/SKILL.md +32 -2
  5. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +7 -1
  6. package/docs/ui_engineering.md +75 -42
  7. package/front_end/core/common/SettingRegistration.ts +0 -10
  8. package/front_end/core/common/Settings.ts +69 -3
  9. package/front_end/core/platform/ArrayUtilities.ts +10 -0
  10. package/front_end/core/platform/StringUtilities.ts +38 -6
  11. package/front_end/core/sdk/ServiceWorkerCacheModel.ts +0 -10
  12. package/front_end/core/sdk/StorageBucketsModel.ts +0 -10
  13. package/front_end/core/sdk/sdk-meta.ts +0 -1
  14. package/front_end/entrypoints/inspector_main/OutermostTargetSelector.ts +0 -13
  15. package/front_end/entrypoints/inspector_main/RenderingOptions.ts +8 -3
  16. package/front_end/entrypoints/inspector_main/inspector_main-meta.ts +3 -3
  17. package/front_end/entrypoints/main/MainImpl.ts +7 -2
  18. package/front_end/foundation/README.md +86 -0
  19. package/front_end/foundation/Universe.ts +16 -0
  20. package/front_end/generated/InspectorBackendCommands.ts +6 -19
  21. package/front_end/generated/protocol-mapping.d.ts +0 -53
  22. package/front_end/generated/protocol-proxy-api.d.ts +0 -46
  23. package/front_end/generated/protocol.ts +20 -186
  24. package/front_end/models/ai_assistance/AiAgent2.ts +3 -3
  25. package/front_end/models/ai_assistance/AiConversation.ts +21 -10
  26. package/front_end/models/ai_assistance/AiHistoryStorage.ts +21 -10
  27. package/front_end/models/ai_assistance/ChangeManager.ts +2 -2
  28. package/front_end/models/ai_assistance/ExtensionScope.ts +6 -2
  29. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +1 -1
  30. package/front_end/models/ai_assistance/agents/AiAgent.ts +8 -1
  31. package/front_end/models/ai_assistance/agents/StylingAgent.ts +1 -1
  32. package/front_end/models/badges/UserBadges.ts +31 -17
  33. package/front_end/models/bindings/CompilerScriptMapping.ts +5 -4
  34. package/front_end/models/bindings/ContentProviderBasedProject.ts +2 -3
  35. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +4 -3
  36. package/front_end/models/bindings/DefaultScriptMapping.ts +3 -3
  37. package/front_end/models/bindings/NetworkProject.ts +22 -1
  38. package/front_end/models/bindings/ResourceMapping.ts +4 -3
  39. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -1
  40. package/front_end/models/bindings/SASSSourceMapping.ts +4 -3
  41. package/front_end/models/bindings/StylesSourceMapping.ts +3 -3
  42. package/front_end/models/emulation/DeviceModeModel.ts +67 -5
  43. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +7 -5
  44. package/front_end/models/extensions/RecorderPluginManager.ts +10 -6
  45. package/front_end/models/har/Log.snapshot.txt +193 -0
  46. package/front_end/models/har/Log.ts +3 -2
  47. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +14 -5
  48. package/front_end/models/issues_manager/CheckFormsIssuesTrigger.ts +8 -9
  49. package/front_end/models/issues_manager/RelatedIssue.ts +10 -8
  50. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  51. package/front_end/models/logs/RequestResolver.ts +2 -2
  52. package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +0 -5
  53. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +8 -6
  54. package/front_end/models/source_map_scopes/NamesResolver.ts +36 -30
  55. package/front_end/models/stack_trace/StackTrace.ts +17 -0
  56. package/front_end/models/trace/handlers/FramesHandler.ts +4 -7
  57. package/front_end/models/trace/handlers/ModelHandlers.ts +0 -1
  58. package/front_end/models/trace/handlers/RendererHandler.ts +2 -15
  59. package/front_end/models/trace/handlers/Threads.ts +4 -11
  60. package/front_end/models/trace/insights/DOMSize.ts +1 -1
  61. package/front_end/models/workspace/WorkspaceImpl.ts +1 -10
  62. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -44
  63. package/front_end/panels/application/CookieItemsView.ts +1 -1
  64. package/front_end/panels/application/FrameDetailsView.ts +1 -1
  65. package/front_end/panels/application/IndexedDBModel.ts +0 -10
  66. package/front_end/panels/application/IndexedDBViews.ts +240 -164
  67. package/front_end/panels/application/ServiceWorkersView.ts +494 -411
  68. package/front_end/panels/application/SharedStorageModel.ts +0 -10
  69. package/front_end/panels/application/application.ts +0 -6
  70. package/front_end/panels/application/components/AdsView.ts +23 -0
  71. package/front_end/panels/application/components/SharedStorageAccessGrid.ts +1 -1
  72. package/front_end/panels/application/components/adsView.css +21 -1
  73. package/front_end/panels/application/components/components.ts +0 -2
  74. package/front_end/panels/application/indexedDBViews.css +1 -1
  75. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +1 -1
  76. package/front_end/panels/application/serviceWorkersView.css +40 -4
  77. package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +147 -123
  78. package/front_end/panels/common/ExtensionServer.ts +3 -3
  79. package/front_end/panels/console/ConsoleView.ts +1 -1
  80. package/front_end/panels/emulation/DeviceModeView.ts +139 -70
  81. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  82. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +53 -12
  83. package/front_end/panels/network/NetworkDataGridNode.ts +43 -1
  84. package/front_end/panels/network/RequestInitiatorView.ts +24 -2
  85. package/front_end/panels/network/RequestPayloadView.ts +77 -39
  86. package/front_end/panels/network/components/ResponseHeaderSection.ts +5 -3
  87. package/front_end/panels/network/networkLogView.css +5 -0
  88. package/front_end/panels/network/requestInitiatorViewTree.css +5 -0
  89. package/front_end/panels/profiler/HeapProfileView.ts +198 -79
  90. package/front_end/panels/profiler/HeapSnapshotView.ts +2 -2
  91. package/front_end/panels/recorder/README.md +1 -2
  92. package/front_end/panels/recorder/RecorderPanel.ts +1749 -17
  93. package/front_end/panels/recorder/recorder.ts +0 -2
  94. package/front_end/panels/settings/AISettingsTab.ts +13 -0
  95. package/front_end/panels/sources/SourcesNavigator.ts +3 -2
  96. package/front_end/panels/sources/SourcesView.ts +0 -17
  97. package/front_end/panels/sources/UISourceCodeFrame.ts +42 -0
  98. package/front_end/panels/sources/sources.ts +0 -2
  99. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -17
  100. package/front_end/panels/timeline/ThreadAppender.ts +0 -106
  101. package/front_end/third_party/chromium/README.chromium +1 -1
  102. package/front_end/ui/components/report_view/ReportView.ts +11 -5
  103. package/front_end/ui/components/report_view/report.css +4 -1
  104. package/front_end/ui/components/spinners/Spinner.ts +29 -32
  105. package/front_end/ui/components/spinners/spinner.css +32 -84
  106. package/front_end/ui/legacy/TextPrompt.ts +49 -1
  107. package/front_end/ui/legacy/Treeoutline.ts +14 -6
  108. package/front_end/ui/legacy/UIUtils.ts +36 -11
  109. package/front_end/ui/legacy/Widget.ts +17 -10
  110. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +2 -2
  111. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +36 -7
  112. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +63 -68
  113. package/front_end/ui/legacy/components/object_ui/RemoteObjectPreviewFormatter.ts +1 -1
  114. package/front_end/ui/legacy/components/object_ui/objectValue.css +2 -1
  115. package/front_end/ui/legacy/inspectorCommon.css +4 -4
  116. package/front_end/ui/legacy/textPrompt.css +10 -0
  117. package/front_end/ui/lit/lit.ts +1 -0
  118. package/front_end/ui/lit/render.ts +44 -10
  119. package/front_end/ui/lit/strip-whitespace.ts +23 -2
  120. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  121. package/package.json +1 -1
  122. package/front_end/models/trace/handlers/AuctionWorkletsHandler.ts +0 -183
  123. package/front_end/panels/application/InterestGroupStorageModel.ts +0 -81
  124. package/front_end/panels/application/InterestGroupStorageView.ts +0 -151
  125. package/front_end/panels/application/InterestGroupTreeElement.ts +0 -65
  126. package/front_end/panels/application/components/InterestGroupAccessGrid.ts +0 -135
  127. package/front_end/panels/application/components/interestGroupAccessGrid.css +0 -27
  128. package/front_end/panels/application/interestGroupStorageView.css +0 -9
  129. package/front_end/panels/recorder/RecorderController.ts +0 -1595
  130. package/front_end/panels/sources/InplaceFormatterEditorAction.ts +0 -149
  131. /package/front_end/panels/recorder/{recorderController.css → recorderPanel.css} +0 -0
@@ -68,7 +68,7 @@ import {cloneCustomElement, ElementFocusRestorer} from './UIUtils.js';
68
68
  * @attribute value Sets the initial text value that's edited when editing starts. If not provided, the slot's inner text is used.
69
69
  */
70
70
  export class TextPromptElement extends HTMLElement {
71
- static readonly observedAttributes = ['editing', 'completions', 'placeholder', 'cancel-on-blur'];
71
+ static readonly observedAttributes = ['editing', 'completions', 'placeholder', 'cancel-on-blur', 'render-as-block'];
72
72
  static formAssociated = true;
73
73
  readonly #shadow = this.attachShadow({mode: 'open'});
74
74
  readonly #internals = this.attachInternals();
@@ -79,6 +79,7 @@ export class TextPromptElement extends HTMLElement {
79
79
  #completionObserver = new MutationObserver(this.#onMutate.bind(this));
80
80
  #validator?: (value: string) => null | Platform.UIString.LocalizedString;
81
81
  #cancelOnBlur = false;
82
+ #isEditing = false;
82
83
 
83
84
  constructor() {
84
85
  super();
@@ -101,6 +102,18 @@ export class TextPromptElement extends HTMLElement {
101
102
  return this.#cancelOnBlur;
102
103
  }
103
104
 
105
+ set renderAsBlock(renderAsBlock: boolean) {
106
+ if (renderAsBlock) {
107
+ this.setAttribute('render-as-block', '');
108
+ } else {
109
+ this.removeAttribute('render-as-block');
110
+ }
111
+ }
112
+
113
+ get renderAsBlock(): boolean {
114
+ return this.hasAttribute('render-as-block');
115
+ }
116
+
104
117
  #onMutate(changes: MutationRecord[]): void {
105
118
  const listId = this.getAttribute('completions');
106
119
  if (!listId) {
@@ -159,6 +172,17 @@ export class TextPromptElement extends HTMLElement {
159
172
  this.#completionObserver.disconnect();
160
173
  }
161
174
  break;
175
+ case 'render-as-block':
176
+ if (this.#isEditing) {
177
+ if (isTruthy(newValue)) {
178
+ this.#textPrompt.renderAsBlock();
179
+ this.#entrypoint.style.display = 'block';
180
+ } else {
181
+ this.#textPrompt.renderAsInlineBlock();
182
+ this.#entrypoint.style.display = 'inline';
183
+ }
184
+ }
185
+ break;
162
186
  }
163
187
  }
164
188
 
@@ -188,6 +212,7 @@ export class TextPromptElement extends HTMLElement {
188
212
  }
189
213
 
190
214
  #startEditing(): void {
215
+ this.#isEditing = true;
191
216
  const truncatedTextPlaceholder = this.getAttribute('placeholder');
192
217
  const placeholder = this.#entrypoint.createChild('span');
193
218
  const initialText = this.getAttribute('value') ?? this.#slot.deepInnerText();
@@ -198,6 +223,13 @@ export class TextPromptElement extends HTMLElement {
198
223
  }
199
224
  this.#slot.remove();
200
225
 
226
+ if (this.renderAsBlock) {
227
+ this.#textPrompt.renderAsBlock();
228
+ this.#entrypoint.style.display = 'block';
229
+ } else {
230
+ this.#textPrompt.renderAsInlineBlock();
231
+ this.#entrypoint.style.display = 'inline';
232
+ }
201
233
  const proxy =
202
234
  this.#textPrompt.attachAndStartEditing(placeholder, e => this.#done(e, /* commit=*/ !this.#cancelOnBlur));
203
235
  proxy.addEventListener('keydown', this.#editingValueKeyDown.bind(this));
@@ -206,6 +238,7 @@ export class TextPromptElement extends HTMLElement {
206
238
  }
207
239
 
208
240
  #stopEditing(): void {
241
+ this.#isEditing = false;
209
242
  this.#entrypoint.removeChildren();
210
243
  this.#entrypoint.appendChild(this.#slot);
211
244
  this.#textPrompt.detach();
@@ -224,6 +257,9 @@ export class TextPromptElement extends HTMLElement {
224
257
  }
225
258
 
226
259
  #done(e: Event, commit: boolean): void {
260
+ if (!this.#isEditing) {
261
+ return;
262
+ }
227
263
  const target = e.target as HTMLElement;
228
264
  const text = target.textContent || '';
229
265
  if (commit) {
@@ -238,9 +274,11 @@ export class TextPromptElement extends HTMLElement {
238
274
  return;
239
275
  }
240
276
 
277
+ this.#isEditing = false;
241
278
  this.dispatchEvent(new TextPromptElement.CommitEvent(text));
242
279
  } else {
243
280
  this.#internals.setValidity({});
281
+ this.#isEditing = false;
244
282
  this.dispatchEvent(new TextPromptElement.CancelEvent());
245
283
  }
246
284
  e.consume();
@@ -365,6 +403,16 @@ export class TextPrompt extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
365
403
 
366
404
  renderAsBlock(): void {
367
405
  this.proxyElementDisplay = 'block';
406
+ if (this.proxyElement) {
407
+ (this.proxyElement as HTMLElement).style.display = 'block';
408
+ }
409
+ }
410
+
411
+ renderAsInlineBlock(): void {
412
+ this.proxyElementDisplay = 'inline-block';
413
+ if (this.proxyElement) {
414
+ (this.proxyElement as HTMLElement).style.display = 'inline-block';
415
+ }
368
416
  }
369
417
 
370
418
  /**
@@ -1864,7 +1864,7 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
1864
1864
  }
1865
1865
  }
1866
1866
 
1867
- protected override addNodes(nodes: NodeList|Node[], nextSibling?: Node|null): void {
1867
+ protected override addNodes(nodes: NodeList|Node[]): void {
1868
1868
  for (const node of getTreeNodes(nodes)) {
1869
1869
  if (TreeViewTreeElement.get(node)) {
1870
1870
  continue; // Not sure this can happen
@@ -1876,10 +1876,18 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
1876
1876
  if (parent.treeElement.childCount() === 0) {
1877
1877
  parent.treeElement.childrenListElement.classList.add(...parent.classes.values());
1878
1878
  }
1879
- while (nextSibling && nextSibling.nodeType !== Node.ELEMENT_NODE) {
1880
- nextSibling = nextSibling.nextSibling;
1879
+ let nextElement: TreeElement|null = null;
1880
+ for (let e: Element|null = node.nextElementSibling; e; e = e.nextElementSibling) {
1881
+ const nextTreeEl = TreeViewTreeElement.get(e);
1882
+ if (nextTreeEl) {
1883
+ nextElement = nextTreeEl;
1884
+ break;
1885
+ }
1886
+ if (e instanceof TreeElementWrapper && e.treeElement && e.treeElement.parent === parent.treeElement) {
1887
+ nextElement = e.treeElement;
1888
+ break;
1889
+ }
1881
1890
  }
1882
- const nextElement = nextSibling ? TreeViewTreeElement.get(nextSibling) : null;
1883
1891
  const index = nextElement ? parent.treeElement.indexOfChild(nextElement) : parent.treeElement.children().length;
1884
1892
  let treeElement;
1885
1893
  if (node instanceof HTMLLIElement) {
@@ -1906,12 +1914,12 @@ export class TreeViewElement extends HTMLElementWithLightDOMTemplate {
1906
1914
  }
1907
1915
  }
1908
1916
  }
1909
- for (const element of getStyleElements(nodes)) {
1917
+ for (const element of new Set(getStyleElements(nodes))) {
1910
1918
  this.#treeOutline.shadowRoot.appendChild(element.cloneNode(true));
1911
1919
  }
1912
1920
  }
1913
1921
 
1914
- protected override removeNodes(nodes: NodeList): void {
1922
+ protected override removeNodes(nodes: NodeList|Node[]): void {
1915
1923
  for (const node of getTreeNodes(nodes)) {
1916
1924
  if (node instanceof HTMLLIElement) {
1917
1925
  TreeViewTreeElement.get(node)?.remove();
@@ -1190,7 +1190,7 @@ export function setTitle(element: HTMLElement, title: string): void {
1190
1190
  }
1191
1191
 
1192
1192
  export class CheckboxLabel extends HTMLElement {
1193
- static readonly observedAttributes = ['checked', 'disabled', 'indeterminate', 'name', 'title', 'aria-label'];
1193
+ static readonly observedAttributes = ['checked', 'disabled', 'indeterminate', 'name', 'title', 'aria-label', 'small'];
1194
1194
 
1195
1195
  readonly #shadowRoot!: DocumentFragment;
1196
1196
  #checkboxElement!: HTMLInputElement;
@@ -1255,6 +1255,8 @@ export class CheckboxLabel extends HTMLElement {
1255
1255
  this.#textElement.title = newValue ?? '';
1256
1256
  } else if (name === 'aria-label') {
1257
1257
  this.#checkboxElement.ariaLabel = newValue;
1258
+ } else if (name === 'small') {
1259
+ this.#checkboxElement.classList.toggle('small', newValue !== null);
1258
1260
  }
1259
1261
  }
1260
1262
 
@@ -1282,6 +1284,14 @@ export class CheckboxLabel extends HTMLElement {
1282
1284
  this.toggleAttribute('checked', checked);
1283
1285
  }
1284
1286
 
1287
+ get small(): boolean {
1288
+ return this.hasAttribute('small');
1289
+ }
1290
+
1291
+ set small(small: boolean) {
1292
+ this.toggleAttribute('small', small);
1293
+ }
1294
+
1285
1295
  set disabled(disabled: boolean) {
1286
1296
  this.toggleAttribute('disabled', disabled);
1287
1297
  }
@@ -2114,10 +2124,6 @@ export class HTMLElementWithLightDOMTemplate extends HTMLElement {
2114
2124
  value['_$litType$'] === 1);
2115
2125
  }
2116
2126
 
2117
- function isLitDirective(value: unknown): value is {values: unknown[]} {
2118
- return Boolean(typeof value === 'object' && value && '_$litDirective$' in value && 'values' in value);
2119
- }
2120
-
2121
2127
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
2122
2128
  function isCallable(value: unknown): value is(...args: any[]) => any {
2123
2129
  // Native class constructors cannot be invoked without 'new', and we shouldn't attempt to wrap them.
@@ -2138,7 +2144,7 @@ export class HTMLElementWithLightDOMTemplate extends HTMLElement {
2138
2144
  HTMLElementWithLightDOMTemplate.patchLitTemplate(value);
2139
2145
  return value;
2140
2146
  }
2141
- if (isLitDirective(value)) {
2147
+ if (Lit.isLitDirective(value)) {
2142
2148
  for (let i = 0; i < value.values.length; i++) {
2143
2149
  const subvalue = value.values[i];
2144
2150
  if (isCallable(subvalue)) {
@@ -2176,9 +2182,28 @@ export class HTMLElementWithLightDOMTemplate extends HTMLElement {
2176
2182
 
2177
2183
  #onChange(mutationList: MutationRecord[]): void {
2178
2184
  this.onChange(mutationList);
2179
- for (const mutation of mutationList) {
2180
- this.removeNodes(mutation.removedNodes);
2181
- this.addNodes(mutation.addedNodes, mutation.nextSibling);
2185
+ const addedNodes = new Set<Node>();
2186
+ const removedNodes = new Set<Node>();
2187
+ for (let i = 0; i < mutationList.length; i++) {
2188
+ const mutation = mutationList[i];
2189
+ for (const node of mutation.addedNodes) {
2190
+ addedNodes.add(node);
2191
+ }
2192
+ for (const node of mutation.removedNodes) {
2193
+ removedNodes.add(node);
2194
+ }
2195
+ }
2196
+ if (removedNodes.size > 0) {
2197
+ this.removeNodes([...removedNodes]);
2198
+ }
2199
+
2200
+ const finalAddedNodes = [...addedNodes].filter(n => this.templateRoot.contains(n));
2201
+ if (finalAddedNodes.length > 0) {
2202
+ this.addNodes(finalAddedNodes);
2203
+ }
2204
+
2205
+ for (let i = 0; i < mutationList.length; i++) {
2206
+ const mutation = mutationList[i];
2182
2207
  this.updateNode(mutation.target, mutation.attributeName);
2183
2208
  }
2184
2209
  }
@@ -2189,10 +2214,10 @@ export class HTMLElementWithLightDOMTemplate extends HTMLElement {
2189
2214
  protected updateNode(_node: Node, _attributeName: string|null): void {
2190
2215
  }
2191
2216
 
2192
- protected addNodes(_nodes: NodeList|Node[], _nextSibling?: Node|null): void {
2217
+ protected addNodes(_nodes: NodeList|Node[]): void {
2193
2218
  }
2194
2219
 
2195
- protected removeNodes(_nodes: NodeList): void {
2220
+ protected removeNodes(_nodes: NodeList|Node[]): void {
2196
2221
  }
2197
2222
 
2198
2223
  static findCorrespondingElement(
@@ -76,16 +76,20 @@ export function widgetConfig<F extends WidgetFactory<AnyWidget>, ParamKeys exten
76
76
  let currentUpdateQueue: Map<AnyWidget, PromiseWithResolvers<void>>|null = null;
77
77
  const currentlyProcessed = new Set<AnyWidget>();
78
78
  let nextUpdateQueue = new Map<AnyWidget, PromiseWithResolvers<void>>();
79
- let pendingAnimationFrame: number|null = null;
79
+ const pendingAnimationFrames = new WeakMap<Window, number>();
80
80
  let overallUpdatePromise: PromiseWithResolvers<void>|null = null;
81
81
 
82
82
  function enqueueIntoNextUpdateQueue(widget: AnyWidget): Promise<void> {
83
83
  const scheduledUpdate = nextUpdateQueue.get(widget) ?? Promise.withResolvers<void>();
84
84
  nextUpdateQueue.delete(widget);
85
85
  nextUpdateQueue.set(widget, scheduledUpdate);
86
- if (pendingAnimationFrame === null) {
87
- const widgetWindow = widget.contentElement.window() || window;
88
- pendingAnimationFrame = widgetWindow.requestAnimationFrame(runNextUpdate);
86
+ const widgetWindow = widget.contentElement.window() || window;
87
+ if (!pendingAnimationFrames.has(widgetWindow)) {
88
+ const frameId = widgetWindow.requestAnimationFrame(() => {
89
+ pendingAnimationFrames.delete(widgetWindow);
90
+ runNextUpdate();
91
+ });
92
+ pendingAnimationFrames.set(widgetWindow, frameId);
89
93
  }
90
94
  return scheduledUpdate.promise;
91
95
  }
@@ -121,14 +125,13 @@ function cancelUpdate(widget: AnyWidget): void {
121
125
 
122
126
  function resolveOverallUpdatePromise(): void {
123
127
  if (currentlyProcessed.size === 0 && (!currentUpdateQueue || currentUpdateQueue.size === 0) &&
124
- nextUpdateQueue.size === 0 && !pendingAnimationFrame && overallUpdatePromise) {
128
+ nextUpdateQueue.size === 0 && overallUpdatePromise) {
125
129
  overallUpdatePromise.resolve();
126
130
  overallUpdatePromise = null;
127
131
  }
128
132
  }
129
133
 
130
134
  function runNextUpdate(): void {
131
- pendingAnimationFrame = null;
132
135
  if (!currentUpdateQueue) {
133
136
  currentUpdateQueue = nextUpdateQueue;
134
137
  nextUpdateQueue = new Map();
@@ -155,9 +158,13 @@ function runNextUpdate(): void {
155
158
  const nextUpdate = nextUpdateQueue.get(widget);
156
159
  if (nextUpdate) {
157
160
  void nextUpdate.promise.then(resolve);
158
- if (pendingAnimationFrame === null) {
159
- const widgetWindow = widget.contentElement.window() || window;
160
- pendingAnimationFrame = widgetWindow.requestAnimationFrame(runNextUpdate);
161
+ const widgetWindow = widget.contentElement.window() || window;
162
+ if (!pendingAnimationFrames.has(widgetWindow)) {
163
+ const frameId = widgetWindow.requestAnimationFrame(() => {
164
+ pendingAnimationFrames.delete(widgetWindow);
165
+ runNextUpdate();
166
+ });
167
+ pendingAnimationFrames.set(widgetWindow, frameId);
161
168
  }
162
169
  } else {
163
170
  resolve();
@@ -578,7 +585,7 @@ export class Widget<ContentTypeT extends HTMLElement|DocumentFragment = HTMLElem
578
585
  }
579
586
 
580
587
  static get allUpdatesComplete(): Promise<void> {
581
- if (!pendingAnimationFrame && !currentUpdateQueue && currentlyProcessed.size === 0) {
588
+ if (nextUpdateQueue.size === 0 && !currentUpdateQueue && currentlyProcessed.size === 0) {
582
589
  return Promise.resolve();
583
590
  }
584
591
  if (!overallUpdatePromise) {
@@ -688,10 +688,10 @@ export class CookiesTable extends UI.Widget.VBox {
688
688
  ]);
689
689
  void Common.Revealer.reveal(requestFilter);
690
690
  }, {jslogContext: 'show-requests-with-this-cookie'});
691
- if (IssuesManager.RelatedIssue.hasIssues(cookie)) {
691
+ if (IssuesManager.RelatedIssue.hasIssues(cookie, IssuesManager.IssuesManager.IssuesManager.instance())) {
692
692
  contextMenu.revealSection().appendItem(i18nString(UIStrings.showIssueAssociatedWithThis), () => {
693
693
  // TODO(chromium:1077719): Just filter for the cookie instead of revealing one of the associated issues.
694
- void IssuesManager.RelatedIssue.reveal(cookie);
694
+ void IssuesManager.RelatedIssue.reveal(cookie, IssuesManager.IssuesManager.IssuesManager.instance());
695
695
  }, {jslogContext: 'show-issue-associated-with-this'});
696
696
  }
697
697
  }
@@ -71,6 +71,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
71
71
  #hideableColumns = new Set<string>();
72
72
  #hiddenColumns = new Set<string>();
73
73
  #usedCreationNode: DataGridElementNode|null = null;
74
+ #sortingChangedScheduled = false;
74
75
 
75
76
  constructor() {
76
77
  super();
@@ -247,6 +248,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
247
248
  const sort = column.getAttribute('sort') === 'descending' ? Order.Descending :
248
249
  column.getAttribute('sort') === 'ascending' ? Order.Ascending :
249
250
  undefined;
251
+ const disclosure = hasBooleanAttribute(column, 'disclosure');
250
252
  const columnDescriptor = {
251
253
  id,
252
254
  title: title as Platform.UIString.LocalizedString,
@@ -259,6 +261,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
259
261
  weight,
260
262
  editable,
261
263
  dataType,
264
+ disclosure,
262
265
  };
263
266
  this.#dataGrid.addColumn(columnDescriptor);
264
267
  this.#columns.push(columnDescriptor);
@@ -289,7 +292,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
289
292
  return false;
290
293
  }
291
294
 
292
- #getDataRows(nodes: NodeList): HTMLElement[] {
295
+ #getDataRows(nodes: NodeList|Node[]): HTMLElement[] {
293
296
  return [...nodes]
294
297
  .flatMap(node => {
295
298
  if (node instanceof HTMLTableRowElement) {
@@ -303,7 +306,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
303
306
  .filter(node => node.querySelector('td') && !hasBooleanAttribute(node, 'placeholder'));
304
307
  }
305
308
 
306
- #getStyleElements(nodes: NodeList): HTMLElement[] {
309
+ #getStyleElements(nodes: NodeList|Node[]): HTMLElement[] {
307
310
  return [...nodes].flatMap(node => {
308
311
  if (node instanceof HTMLStyleElement) {
309
312
  return [node];
@@ -325,10 +328,16 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
325
328
  return null;
326
329
  }
327
330
 
328
- override addNodes(nodes: NodeList): void {
331
+ override addNodes(nodes: NodeList|Node[]): void {
329
332
  for (const element of this.#getDataRows(nodes)) {
333
+ if (getNode(element)) {
334
+ continue;
335
+ }
330
336
  const parentRow = element.parentElement?.closest('td')?.closest('tr');
331
337
  const parentDataGridNode = parentRow ? getNode(parentRow) : undefined;
338
+ if (parentRow && !parentDataGridNode) {
339
+ continue;
340
+ }
332
341
  const parentNode = parentDataGridNode || this.#dataGrid.rootNode();
333
342
  const nextNode = this.#findNextExistingNode(element);
334
343
  const index = nextNode ? parentNode.children.indexOf(nextNode) : parentNode.children.length;
@@ -352,14 +361,17 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
352
361
  if (hasBooleanAttribute(element, 'highlighted')) {
353
362
  node.setHighlighted(true);
354
363
  }
364
+ if (hasBooleanAttribute(element, 'expanded')) {
365
+ node.expand();
366
+ }
355
367
  }
356
- for (const element of this.#getStyleElements(nodes)) {
368
+ for (const element of new Set(this.#getStyleElements(nodes))) {
357
369
  this.#shadowRoot.appendChild(element.cloneNode(true));
358
370
  }
359
- this.#dataGrid.dispatchEventToListeners(DataGridEvents.SORTING_CHANGED);
371
+ this.#scheduleSortingChanged();
360
372
  }
361
373
 
362
- override removeNodes(nodes: NodeList): void {
374
+ override removeNodes(nodes: NodeList|Node[]): void {
363
375
  for (const element of this.#getDataRows(nodes)) {
364
376
  const node = getNode(element);
365
377
  if (node) {
@@ -387,6 +399,12 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
387
399
  dataGridNode.setInactive(hasBooleanAttribute(dataRow, 'inactive'));
388
400
  } else if (attributeName === 'highlighted') {
389
401
  dataGridNode.setHighlighted(hasBooleanAttribute(dataRow, 'highlighted'));
402
+ } else if (attributeName === 'expanded') {
403
+ if (hasBooleanAttribute(dataRow, 'expanded')) {
404
+ dataGridNode.expand();
405
+ } else {
406
+ dataGridNode.collapse();
407
+ }
390
408
  } else {
391
409
  this.#updateHasChildren(dataGridNode, dataRow);
392
410
  dataGridNode.refresh();
@@ -394,6 +412,17 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
394
412
  }
395
413
  }
396
414
 
415
+ #scheduleSortingChanged(): void {
416
+ if (this.#sortingChangedScheduled) {
417
+ return;
418
+ }
419
+ this.#sortingChangedScheduled = true;
420
+ queueMicrotask(() => {
421
+ this.#sortingChangedScheduled = false;
422
+ this.#dataGrid.dispatchEventToListeners(DataGridEvents.SORTING_CHANGED);
423
+ });
424
+ }
425
+
397
426
  deselectRow(): void {
398
427
  this.#dataGrid.selectedNode?.deselect();
399
428
  }
@@ -430,7 +459,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
430
459
  // However, if we have nodes added, that will trigger a sort anyway so we
431
460
  // don't need to re-sort again.
432
461
  if (this.#dataGrid.sortColumnId() !== null && !hadAddedNodes) {
433
- this.#dataGrid.dispatchEventToListeners(DataGridEvents.SORTING_CHANGED);
462
+ this.#scheduleSortingChanged();
434
463
  }
435
464
  }
436
465