chrome-devtools-frontend 1.0.1569477 → 1.0.1570343

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.
package/WATCHLISTS CHANGED
@@ -26,7 +26,7 @@
26
26
 
27
27
  'WATCHLISTS': {
28
28
  'all': ['devtools-reviews+devtools@chromium.org'],
29
- 'chromedriver': ['johnchen+watch@chromium.org'],
29
+ 'chromedriver': [],
30
30
  'lighthouse': ['lighthouse-eng+devtools-cl@google.com'],
31
31
  'third_party': ['devtools-reviews+third_party@chromium.org'],
32
32
  },
@@ -882,7 +882,7 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
882
882
  }
883
883
 
884
884
  private toAdoptedStyleSheets(ids: Protocol.DOM.StyleSheetId[]): AdoptedStyleSheet[] {
885
- return ids.map(id => (new AdoptedStyleSheet(id, this.#domModel.cssModel())));
885
+ return ids.map(id => (new AdoptedStyleSheet(id, this)));
886
886
  }
887
887
 
888
888
  setAdoptedStyleSheets(ids: Protocol.DOM.StyleSheetId[]): void {
@@ -1280,7 +1280,11 @@ export class DOMDocument extends DOMNode {
1280
1280
  }
1281
1281
 
1282
1282
  export class AdoptedStyleSheet {
1283
- constructor(readonly id: Protocol.DOM.StyleSheetId, readonly cssModel: CSSModel) {
1283
+ constructor(readonly id: Protocol.DOM.StyleSheetId, readonly parent: DOMNode) {
1284
+ }
1285
+
1286
+ get cssModel(): CSSModel {
1287
+ return this.parent.domModel().cssModel();
1284
1288
  }
1285
1289
  }
1286
1290
 
@@ -5,6 +5,7 @@
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as i18n from '../../core/i18n/i18n.js';
7
7
  import * as SDK from '../../core/sdk/sdk.js';
8
+ import type * as Protocol from '../../generated/protocol.js';
8
9
  import * as UI from '../../ui/legacy/legacy.js';
9
10
  import {Directives, html, nothing, render} from '../../ui/lit/lit.js';
10
11
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
@@ -212,14 +213,16 @@ const DEFERRED_DEFAULT_VIEW: DeferredView = (input, _output, target: HTMLElement
212
213
  export class DeferredDOMNodeLink extends UI.Widget.Widget {
213
214
  #deferredNode: SDK.DOMModel.DeferredDOMNode|undefined = undefined;
214
215
  #options: Options|undefined = undefined;
216
+ #styleSheetId: Protocol.DOM.StyleSheetId|undefined = undefined;
215
217
  #view: DeferredView;
216
218
 
217
219
  constructor(
218
220
  element?: HTMLElement, deferredNode?: SDK.DOMModel.DeferredDOMNode, options?: Options,
219
- view: DeferredView = DEFERRED_DEFAULT_VIEW) {
221
+ styleSheetId?: Protocol.DOM.StyleSheetId, view: DeferredView = DEFERRED_DEFAULT_VIEW) {
220
222
  super(element, {useShadowDom: true});
221
223
  this.element.classList.remove('vbox');
222
224
  this.#deferredNode = deferredNode;
225
+ this.#styleSheetId = styleSheetId;
223
226
  this.#options = options;
224
227
  this.#view = view;
225
228
  this.performUpdate();
@@ -230,6 +233,14 @@ export class DeferredDOMNodeLink extends UI.Widget.Widget {
230
233
  preventKeyboardFocus: this.#options?.preventKeyboardFocus,
231
234
  onClick: () => {
232
235
  this.#deferredNode?.resolve?.(node => {
236
+ if (node && this.#styleSheetId) {
237
+ for (const adoptedStyle of node.adoptedStyleSheetsForNode) {
238
+ if (adoptedStyle.id === this.#styleSheetId) {
239
+ void Common.Revealer.reveal(adoptedStyle);
240
+ return;
241
+ }
242
+ }
243
+ }
233
244
  void Common.Revealer.reveal(node);
234
245
  void node?.scrollIntoView();
235
246
  });
@@ -9,11 +9,12 @@ import * as CodeHighlighter from '../../ui/components/code_highlighter/code_high
9
9
  import * as Components from '../../ui/legacy/components/utils/utils.js';
10
10
  import * as UI from '../../ui/legacy/legacy.js';
11
11
  import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
12
+ import {PanelUtils} from '../utils/utils.js';
12
13
 
13
14
  export class AdoptedStyleSheetTreeElement extends UI.TreeOutline.TreeElement {
14
15
  private eventListener: Common.EventTarget.EventDescriptor|null = null;
15
16
 
16
- constructor(private readonly adoptedStyleSheet: SDK.DOMModel.AdoptedStyleSheet) {
17
+ constructor(readonly adoptedStyleSheet: SDK.DOMModel.AdoptedStyleSheet) {
17
18
  super('');
18
19
  const header = adoptedStyleSheet.cssModel.styleSheetHeaderForId(adoptedStyleSheet.id);
19
20
  if (header) {
@@ -50,6 +51,10 @@ export class AdoptedStyleSheetTreeElement extends UI.TreeOutline.TreeElement {
50
51
  }
51
52
  treeElement.appendChild(new AdoptedStyleSheetContentsTreeElement(header));
52
53
  }
54
+
55
+ highlight(): void {
56
+ PanelUtils.highlightElement(this.listItemElement);
57
+ }
53
58
  }
54
59
 
55
60
  export class AdoptedStyleSheetContentsTreeElement extends UI.TreeOutline.TreeElement {
@@ -795,7 +795,7 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
795
795
  return this.#domTreeWidget.selectedDOMNode();
796
796
  }
797
797
 
798
- selectDOMNode(node: SDK.DOMModel.DOMNode, focus?: boolean): void {
798
+ selectDOMNode(node: SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet, focus?: boolean): void {
799
799
  this.#domTreeWidget.selectDOMNode(node, focus);
800
800
  }
801
801
 
@@ -899,6 +899,18 @@ export class ElementsPanel extends UI.Panel.Panel implements UI.SearchableView.S
899
899
  this.notFirstInspectElement = true;
900
900
  }
901
901
 
902
+ async revealAndSelectAdoptedStyleSheet(nodeToReveal: SDK.DOMModel.AdoptedStyleSheet, opts?: RevealAndSelectNodeOpts):
903
+ Promise<void> {
904
+ const {showPanel = true, focusNode = false} = opts ?? {};
905
+ this.omitDefaultSelection = true;
906
+
907
+ if (showPanel) {
908
+ await UI.ViewManager.ViewManager.instance().showView('elements', false, !focus);
909
+ }
910
+ this.selectDOMNode(nodeToReveal, focusNode);
911
+ delete this.omitDefaultSelection;
912
+ }
913
+
902
914
  private showUAShadowDOMChanged(): void {
903
915
  this.#domTreeWidget.reload();
904
916
  }
@@ -1309,10 +1321,12 @@ export class ContextMenuProvider implements
1309
1321
  }
1310
1322
  }
1311
1323
 
1312
- export class DOMNodeRevealer implements
1313
- Common.Revealer.Revealer<SDK.DOMModel.DOMNode|SDK.DOMModel.DeferredDOMNode|SDK.RemoteObject.RemoteObject> {
1314
- reveal(node: SDK.DOMModel.DOMNode|SDK.DOMModel.DeferredDOMNode|SDK.RemoteObject.RemoteObject, omitFocus?: boolean):
1315
- Promise<void> {
1324
+ export class DOMNodeRevealer implements Common.Revealer.Revealer<
1325
+ SDK.DOMModel.DOMNode|SDK.DOMModel.DeferredDOMNode|SDK.RemoteObject.RemoteObject|SDK.DOMModel.AdoptedStyleSheet> {
1326
+ reveal(
1327
+ node: SDK.DOMModel.DOMNode|SDK.DOMModel.DeferredDOMNode|SDK.RemoteObject.RemoteObject|
1328
+ SDK.DOMModel.AdoptedStyleSheet,
1329
+ omitFocus?: boolean): Promise<void> {
1316
1330
  const panel = ElementsPanel.instance();
1317
1331
  panel.pendingNodeReveal = true;
1318
1332
 
@@ -1332,7 +1346,7 @@ export class DOMNodeRevealer implements
1332
1346
 
1333
1347
  function revealPromise(
1334
1348
  resolve: () => void, reject: (arg0: Platform.UserVisibleError.UserVisibleError) => void): void {
1335
- if (node instanceof SDK.DOMModel.DOMNode) {
1349
+ if (node instanceof SDK.DOMModel.DOMNode || node instanceof SDK.DOMModel.AdoptedStyleSheet) {
1336
1350
  onNodeResolved((node));
1337
1351
  } else if (node instanceof SDK.DOMModel.DeferredDOMNode) {
1338
1352
  (node).resolve(checkDeferredDOMNodeThenReveal);
@@ -1346,14 +1360,15 @@ export class DOMNodeRevealer implements
1346
1360
  }
1347
1361
  }
1348
1362
 
1349
- function onNodeResolved(resolvedNode: SDK.DOMModel.DOMNode): void {
1363
+ function onNodeResolved(resolvedNode: SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet): void {
1350
1364
  panel.pendingNodeReveal = false;
1351
1365
 
1352
1366
  // A detached node could still have a parent and ownerDocument
1353
1367
  // properties, which means stepping up through the hierarchy to ensure
1354
1368
  // that the root node is the document itself. Any break implies
1355
1369
  // detachment.
1356
- let currentNode: SDK.DOMModel.DOMNode = resolvedNode;
1370
+ let currentNode: SDK.DOMModel.DOMNode =
1371
+ resolvedNode instanceof SDK.DOMModel.AdoptedStyleSheet ? resolvedNode.parent : resolvedNode;
1357
1372
  while (currentNode.parentNode) {
1358
1373
  currentNode = currentNode.parentNode;
1359
1374
  }
@@ -1367,7 +1382,11 @@ export class DOMNodeRevealer implements
1367
1382
  }
1368
1383
 
1369
1384
  if (resolvedNode) {
1370
- void panel.revealAndSelectNode(resolvedNode, {showPanel: true, focusNode: !omitFocus}).then(resolve);
1385
+ const opts: RevealAndSelectNodeOpts = {showPanel: true, focusNode: !omitFocus};
1386
+ const promise = resolvedNode instanceof SDK.DOMModel.AdoptedStyleSheet ?
1387
+ panel.revealAndSelectAdoptedStyleSheet(resolvedNode, opts) :
1388
+ panel.revealAndSelectNode(resolvedNode, opts);
1389
+ void promise.then(resolve);
1371
1390
  return;
1372
1391
  }
1373
1392
  const msg = i18nString(UIStrings.nodeCannotBeFoundInTheCurrent);
@@ -274,8 +274,12 @@ export class DOMTreeWidget extends UI.Widget.Widget {
274
274
  this.performUpdate();
275
275
  }
276
276
 
277
- selectDOMNode(node: SDK.DOMModel.DOMNode|null, focus?: boolean): void {
278
- this.#viewOutput?.elementsTreeOutline?.selectDOMNode(node, focus);
277
+ selectDOMNode(node: SDK.DOMModel.DOMNode|SDK.DOMModel.AdoptedStyleSheet|null, focus?: boolean): void {
278
+ if (node instanceof SDK.DOMModel.AdoptedStyleSheet) {
279
+ this.#viewOutput?.elementsTreeOutline?.highlightAdoptedStyleSheet(node);
280
+ } else {
281
+ this.#viewOutput?.elementsTreeOutline?.selectDOMNode(node, focus);
282
+ }
279
283
  }
280
284
 
281
285
  highlightNodeAttribute(node: SDK.DOMModel.DOMNode, attribute: string): void {
@@ -911,6 +915,22 @@ export class ElementsTreeOutline extends
911
915
  }
912
916
  }
913
917
 
918
+ highlightAdoptedStyleSheet(adoptedStyleSheet: SDK.DOMModel.AdoptedStyleSheet): void {
919
+ const parentNode = !this.includeRootDOMNode && adoptedStyleSheet.parent === this.rootDOMNode && this.rootDOMNode ?
920
+ this.rootElement() :
921
+ this.createTreeElementFor(adoptedStyleSheet.parent);
922
+ if (!parentNode) {
923
+ return;
924
+ }
925
+
926
+ for (const child of parentNode.children()) {
927
+ if (child instanceof AdoptedStyleSheetTreeElement && child.adoptedStyleSheet === adoptedStyleSheet) {
928
+ child.highlight();
929
+ return;
930
+ }
931
+ }
932
+ }
933
+
914
934
  editing(): boolean {
915
935
  const node = this.selectedDOMNode();
916
936
  if (!node) {
@@ -409,6 +409,16 @@ export class StylePropertiesSection {
409
409
  link.textContent = label;
410
410
  return link;
411
411
  }
412
+ if (rule && rule.style.styleSheetId && rule.treeScope) {
413
+ // Make link for adopted stylesheet.
414
+ const ownerNode = new SDK.DOMModel.DeferredDOMNode(rule.cssModelInternal.target(), rule.treeScope);
415
+ const link = document.createElement('devtools-widget') as
416
+ UI.Widget.WidgetElement<PanelsCommon.DOMLinkifier.DeferredDOMNodeLink>;
417
+ link.widgetConfig = UI.Widget.widgetConfig(
418
+ e => new PanelsCommon.DOMLinkifier.DeferredDOMNodeLink(e, ownerNode, undefined, rule.style.styleSheetId));
419
+ link.textContent = label;
420
+ return link;
421
+ }
412
422
  return null;
413
423
  }
414
424
 
@@ -617,6 +617,7 @@ Common.Revealer.registerRevealer({
617
617
  SDK.DOMModel.DOMNode,
618
618
  SDK.DOMModel.DeferredDOMNode,
619
619
  SDK.RemoteObject.RemoteObject,
620
+ SDK.DOMModel.AdoptedStyleSheet,
620
621
  ];
621
622
  },
622
623
  destination: Common.Revealer.RevealerDestination.ELEMENTS_PANEL,
@@ -239,39 +239,17 @@ export class DeviceModeToolbar {
239
239
  Common.Settings.Settings.instance().createSetting('emulation.auto-adjust-scale', true);
240
240
 
241
241
  this.lastMode = new Map();
242
-
243
- this.#element = document.createElement('div');
244
- this.#element.classList.add('device-mode-toolbar');
245
- this.#element.setAttribute('jslog', `${VisualLogging.toolbar('device-mode').track({resize: true})}`);
246
-
247
- const mainToolbar = this.#element.createChild('devtools-toolbar', 'main-toolbar');
248
- this.appendDeviceSelectMenu(mainToolbar);
249
-
250
242
  this.widthInput = new EmulationComponents.DeviceSizeInputElement.SizeInputElement(
251
243
  i18nString(UIStrings.width), {jslogContext: 'width'});
252
- this.widthInput.addEventListener('sizechanged', ({size: width}) => {
253
- if (this.autoAdjustScaleSetting.get()) {
254
- this.model.setWidthAndScaleToFit(width);
255
- } else {
256
- this.model.setWidth(width);
257
- }
258
- });
259
244
  this.heightInput = new EmulationComponents.DeviceSizeInputElement.SizeInputElement(
260
245
  i18nString(UIStrings.heightLeaveEmptyForFull), {jslogContext: 'height'});
261
- this.heightInput.addEventListener('sizechanged', ({size: height}) => {
262
- if (this.autoAdjustScaleSetting.get()) {
263
- this.model.setHeightAndScaleToFit(height);
264
- } else {
265
- this.model.setHeight(height);
266
- }
267
- });
268
- this.appendDimensionInputs(mainToolbar);
269
246
 
270
- this.appendDisplaySettings(mainToolbar);
271
- this.appendDevicePositionItems(mainToolbar);
272
- const optionsToolbar = this.#element.createChild('devtools-toolbar', 'device-mode-toolbar-options');
273
- optionsToolbar.wrappable = true;
274
- this.fillOptionsToolbar(optionsToolbar);
247
+ this.#element = document.createElement('div');
248
+ this.#element.classList.add('device-mode-toolbar');
249
+ this.#element.setAttribute('jslog', `${VisualLogging.toolbar('device-mode').track({resize: true})}`);
250
+
251
+ const mainToolbar = this.createMainToolbar();
252
+ const optionsToolbar = this.createOptionsToolbar();
275
253
 
276
254
  this.emulatedDevicesList = EmulationModel.EmulatedDevices.EmulatedDevicesList.instance();
277
255
  this.emulatedDevicesList.addEventListener(
@@ -298,37 +276,46 @@ export class DeviceModeToolbar {
298
276
  return element;
299
277
  }
300
278
 
301
- private appendDeviceSelectMenu(toolbar: UI.Toolbar.Toolbar): void {
302
- toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
279
+ private createMainToolbar(): UI.Toolbar.Toolbar {
280
+ const mainToolbar = this.#element.createChild('devtools-toolbar', 'main-toolbar');
281
+
282
+ mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
303
283
  this.deviceSelectItem =
304
284
  new UI.Toolbar.ToolbarMenuButton(this.appendDeviceMenuItems.bind(this), undefined, undefined, 'device');
305
285
  this.deviceSelectItem.turnShrinkable();
306
286
  this.deviceSelectItem.setDarkText();
307
- toolbar.appendToolbarItem(this.deviceSelectItem);
308
- }
309
-
310
- private appendDimensionInputs(toolbar: UI.Toolbar.Toolbar): void {
311
- toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.widthInput));
287
+ mainToolbar.appendToolbarItem(this.deviceSelectItem);
312
288
 
289
+ this.widthInput.addEventListener('sizechanged', ({size: width}) => {
290
+ if (this.autoAdjustScaleSetting.get()) {
291
+ this.model.setWidthAndScaleToFit(width);
292
+ } else {
293
+ this.model.setWidth(width);
294
+ }
295
+ });
296
+ this.heightInput.addEventListener('sizechanged', ({size: height}) => {
297
+ if (this.autoAdjustScaleSetting.get()) {
298
+ this.model.setHeightAndScaleToFit(height);
299
+ } else {
300
+ this.model.setHeight(height);
301
+ }
302
+ });
303
+ mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.widthInput));
313
304
  const xElement = document.createElement('div');
314
305
  xElement.classList.add('device-mode-x');
315
306
  xElement.textContent = '×';
316
307
  this.xItem = new UI.Toolbar.ToolbarItem(xElement);
317
- toolbar.appendToolbarItem(this.xItem);
318
-
319
- toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.heightInput));
320
- }
308
+ mainToolbar.appendToolbarItem(this.xItem);
309
+ mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.heightInput));
321
310
 
322
- private appendDisplaySettings(toolbar: UI.Toolbar.Toolbar): void {
323
- toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
311
+ mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
324
312
  this.scaleItem =
325
313
  new UI.Toolbar.ToolbarMenuButton(this.appendScaleMenuItems.bind(this), undefined, undefined, 'scale');
326
314
  setTitleForButton(this.scaleItem, i18nString(UIStrings.zoom));
327
315
  this.scaleItem.turnShrinkable();
328
316
  this.scaleItem.setDarkText();
329
- toolbar.appendToolbarItem(this.scaleItem);
330
-
331
- toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
317
+ mainToolbar.appendToolbarItem(this.scaleItem);
318
+ mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
332
319
 
333
320
  this.deviceScaleItem = new UI.Toolbar.ToolbarMenuButton(
334
321
  this.appendDeviceScaleMenuItems.bind(this), undefined, undefined, 'device-pixel-ratio');
@@ -336,53 +323,55 @@ export class DeviceModeToolbar {
336
323
  this.deviceScaleItem.setVisible(this.showDeviceScaleFactorSetting.get());
337
324
  setTitleForButton(this.deviceScaleItem, i18nString(UIStrings.devicePixelRatio));
338
325
  this.deviceScaleItem.setDarkText();
339
- toolbar.appendToolbarItem(this.deviceScaleItem);
340
-
341
- toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
326
+ mainToolbar.appendToolbarItem(this.deviceScaleItem);
327
+ mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
342
328
  this.uaItem =
343
329
  new UI.Toolbar.ToolbarMenuButton(this.appendUserAgentMenuItems.bind(this), undefined, undefined, 'device-type');
344
330
  this.uaItem.turnShrinkable();
345
331
  this.uaItem.setVisible(this.showUserAgentTypeSetting.get());
346
332
  setTitleForButton(this.uaItem, i18nString(UIStrings.deviceType));
347
333
  this.uaItem.setDarkText();
348
- toolbar.appendToolbarItem(this.uaItem);
334
+ mainToolbar.appendToolbarItem(this.uaItem);
349
335
 
350
336
  this.throttlingConditionsItem =
351
337
  MobileThrottling.ThrottlingManager.throttlingManager().createMobileThrottlingButton();
352
338
  this.throttlingConditionsItem.turnShrinkable();
353
- toolbar.appendToolbarItem(this.throttlingConditionsItem);
339
+ mainToolbar.appendToolbarItem(this.throttlingConditionsItem);
354
340
  const saveDataItem = MobileThrottling.ThrottlingManager.throttlingManager().createSaveDataOverrideSelector();
355
341
  saveDataItem.turnShrinkable();
356
- toolbar.appendToolbarItem(saveDataItem);
357
- }
342
+ mainToolbar.appendToolbarItem(saveDataItem);
358
343
 
359
- private appendDevicePositionItems(toolbar: UI.Toolbar.Toolbar): void {
360
- toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
344
+ mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
361
345
  this.modeButton = new UI.Toolbar.ToolbarButton('', 'screen-rotation', undefined, 'screen-rotation');
362
346
  this.modeButton.addEventListener(UI.Toolbar.ToolbarButton.Events.CLICK, this.modeMenuClicked, this);
363
- toolbar.appendToolbarItem(this.modeButton);
347
+ mainToolbar.appendToolbarItem(this.modeButton);
364
348
 
365
349
  // Show dual screen toolbar.
366
350
  this.spanButton = new UI.Toolbar.ToolbarButton('', 'device-fold', undefined, 'device-fold');
367
351
  this.spanButton.addEventListener(UI.Toolbar.ToolbarButton.Events.CLICK, this.spanClicked, this);
368
- toolbar.appendToolbarItem(this.spanButton);
352
+ mainToolbar.appendToolbarItem(this.spanButton);
369
353
 
370
354
  // Show posture toolbar menu for foldable devices.
371
- toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
355
+ mainToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
372
356
  this.postureItem = new UI.Toolbar.ToolbarMenuButton(
373
357
  this.appendDevicePostureItems.bind(this), undefined, undefined, 'device-posture');
374
358
  this.postureItem.turnShrinkable();
375
359
  this.postureItem.setDarkText();
376
360
  setTitleForButton(this.postureItem, i18nString(UIStrings.devicePosture));
377
- toolbar.appendToolbarItem(this.postureItem);
361
+ mainToolbar.appendToolbarItem(this.postureItem);
362
+
363
+ return mainToolbar;
378
364
  }
379
365
 
380
- private fillOptionsToolbar(toolbar: UI.Toolbar.Toolbar): void {
381
- toolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
366
+ private createOptionsToolbar(): UI.Toolbar.Toolbar {
367
+ const optionsToolbar = this.#element.createChild('devtools-toolbar', 'device-mode-toolbar-options');
368
+ optionsToolbar.wrappable = true;
369
+ optionsToolbar.appendToolbarItem(new UI.Toolbar.ToolbarItem(this.createEmptyToolbarElement()));
382
370
  const moreOptionsButton = new UI.Toolbar.ToolbarMenuButton(
383
371
  this.appendOptionsMenuItems.bind(this), true, undefined, 'more-options', 'dots-vertical');
384
372
  moreOptionsButton.setTitle(i18nString(UIStrings.moreOptions));
385
- toolbar.appendToolbarItem(moreOptionsButton);
373
+ optionsToolbar.appendToolbarItem(moreOptionsButton);
374
+ return optionsToolbar;
386
375
  }
387
376
 
388
377
  private appendDevicePostureItems(contextMenu: UI.ContextMenu.ContextMenu): void {
@@ -312,18 +312,10 @@ const UIStrings = {
312
312
  * @description Call site stack label in Timeline UIUtils of the Performance panel
313
313
  */
314
314
  idleCallbackRequested: 'Idle callback requested',
315
- /**
316
- * @description Stack label in Timeline UIUtils of the Performance panel
317
- */
318
- recalculationForced: 'Recalculation forced',
319
315
  /**
320
316
  * @description Call site stack label in Timeline UIUtils of the Performance panel
321
317
  */
322
318
  firstLayoutInvalidation: 'First layout invalidation',
323
- /**
324
- * @description Stack label in Timeline UIUtils of the Performance panel
325
- */
326
- layoutForced: 'Layout forced',
327
319
  /**
328
320
  * @description Label in front of CSS property (eg `opacity`) being animated or a CSS animation name (eg `layer-4-fade-in-out`)
329
321
  */
@@ -1725,20 +1717,8 @@ export class TimelineUIUtils {
1725
1717
  parsedTrace: Trace.TraceModel.ParsedTrace): Promise<void> {
1726
1718
  const {startTime} = Trace.Helpers.Timing.eventTimingsMilliSeconds(event);
1727
1719
  let initiatorStackLabel = i18nString(UIStrings.initiatorStackTrace);
1728
- let stackLabel = i18nString(UIStrings.functionStack);
1729
- const stackTraceForEvent = Trace.Extras.StackTraceForEvent.get(event, parsedTrace.data);
1730
- if (stackTraceForEvent?.callFrames.length || stackTraceForEvent?.description || stackTraceForEvent?.parent) {
1731
- contentHelper.addSection(i18nString(UIStrings.functionStack));
1732
- await contentHelper.createChildStackTraceElement(stackTraceForEvent);
1733
- // TODO(andoli): also build stack trace component for other events
1734
- // that have a stack trace using the StackTraceForEvent helper.
1735
- } else {
1736
- const stackTrace = Trace.Helpers.Trace.getZeroIndexedStackTraceInEventPayload(event);
1737
- if (stackTrace?.length) {
1738
- contentHelper.addSection(stackLabel);
1739
- await contentHelper.createChildStackTraceElement(TimelineUIUtils.stackTraceFromCallFrames(stackTrace));
1740
- }
1741
- }
1720
+ await contentHelper.appendFunctionStackTraceSection(event, parsedTrace);
1721
+
1742
1722
  switch (event.name) {
1743
1723
  case Trace.Types.Events.Name.TIMER_FIRE:
1744
1724
  initiatorStackLabel = i18nString(UIStrings.timerInstalled);
@@ -1751,11 +1731,9 @@ export class TimelineUIUtils {
1751
1731
  break;
1752
1732
  case Trace.Types.Events.Name.RECALC_STYLE:
1753
1733
  initiatorStackLabel = i18nString(UIStrings.firstInvalidated);
1754
- stackLabel = i18nString(UIStrings.recalculationForced);
1755
1734
  break;
1756
1735
  case Trace.Types.Events.Name.LAYOUT:
1757
1736
  initiatorStackLabel = i18nString(UIStrings.firstLayoutInvalidation);
1758
- stackLabel = i18nString(UIStrings.layoutForced);
1759
1737
  break;
1760
1738
  }
1761
1739
 
@@ -1768,8 +1746,9 @@ export class TimelineUIUtils {
1768
1746
  // and the time since the initiator (Pending For).
1769
1747
  const stackTrace = Trace.Helpers.Trace.getZeroIndexedStackTraceInEventPayload(initiator);
1770
1748
  if (stackTrace) {
1771
- contentHelper.addSection(initiatorStackLabel);
1772
- await contentHelper.createChildStackTraceElement(TimelineUIUtils.stackTraceFromCallFrames(stackTrace));
1749
+ const traceElement =
1750
+ await contentHelper.createChildStackTraceElement(TimelineUIUtils.stackTraceFromCallFrames(stackTrace));
1751
+ contentHelper.appendSectionWithBodyIfExists(initiatorStackLabel, {body: traceElement});
1773
1752
  }
1774
1753
 
1775
1754
  const link = this.createEntryLink(initiator);
@@ -2414,6 +2393,39 @@ export class TimelineDetailsContentHelper {
2414
2393
  this.fragment.appendChild(this.element);
2415
2394
  }
2416
2395
 
2396
+ /**
2397
+ * Creates a new section, but only if the provided `body` element is present,
2398
+ * otherwise it does nothing.
2399
+ */
2400
+ appendSectionWithBodyIfExists(title: string, options: {
2401
+ body: HTMLElement|null,
2402
+ swatchColor?: string,
2403
+ event?: Trace.Types.Events.Event,
2404
+ }): void {
2405
+ if (!options.body) {
2406
+ return;
2407
+ }
2408
+ this.addSection(title, options.swatchColor, options.event);
2409
+ this.tableElement.appendChild(options.body);
2410
+ }
2411
+
2412
+ /**
2413
+ * Generates a stack trace for the given event. If there is no stack data,
2414
+ * nothing is appended; you can safely call this without fearing that it will
2415
+ * create an empty section.
2416
+ */
2417
+ async appendFunctionStackTraceSection(
2418
+ event: Trace.Types.Events.Event,
2419
+ parsedTrace: Trace.TraceModel.ParsedTrace,
2420
+ ): Promise<void> {
2421
+ const stackTraceForEvent = Trace.Extras.StackTraceForEvent.get(event, parsedTrace.data);
2422
+ if (!stackTraceForEvent) {
2423
+ return;
2424
+ }
2425
+ const traceElement = await this.createChildStackTraceElement(stackTraceForEvent);
2426
+ this.appendSectionWithBodyIfExists(i18nString(UIStrings.functionStack), {body: traceElement});
2427
+ }
2428
+
2417
2429
  linkifier(): LegacyComponents.Linkifier.Linkifier|null {
2418
2430
  return this.#linkifier;
2419
2431
  }
@@ -2481,9 +2493,13 @@ export class TimelineDetailsContentHelper {
2481
2493
  this.appendElementRow(title, locationContent);
2482
2494
  }
2483
2495
 
2484
- async createChildStackTraceElement(runtimeStackTrace: Protocol.Runtime.StackTrace): Promise<void> {
2496
+ /**
2497
+ * Creates a stack trace element for the given trace, but checks if it
2498
+ * contains any entries, and discards it if it's empty.
2499
+ */
2500
+ async createChildStackTraceElement(runtimeStackTrace: Protocol.Runtime.StackTrace): Promise<HTMLElement|null> {
2485
2501
  if (!this.#linkifier) {
2486
- return;
2502
+ return null;
2487
2503
  }
2488
2504
 
2489
2505
  let callFrameContents;
@@ -2493,7 +2509,6 @@ export class TimelineDetailsContentHelper {
2493
2509
  callFrameContents = new LegacyComponents.JSPresentationUtils.StackTracePreviewContent(
2494
2510
  undefined, this.target ?? undefined, this.#linkifier, {tabStops: true, showColumnNumber: true});
2495
2511
  callFrameContents.stackTrace = stackTrace;
2496
- await callFrameContents.updateComplete;
2497
2512
  } else {
2498
2513
  // I _think_ this only happens during tests.
2499
2514
  // See "TimelineFlameChartView > shows the details for a selected main thread event".
@@ -2505,10 +2520,16 @@ export class TimelineDetailsContentHelper {
2505
2520
  {runtimeStackTrace, tabStops: true, showColumnNumber: true});
2506
2521
  }
2507
2522
 
2508
- const stackTraceElement =
2509
- this.tableElement.createChild('div', 'timeline-details-view-row timeline-details-stack-values');
2523
+ await callFrameContents.updateComplete;
2524
+ if (!callFrameContents.hasContent()) {
2525
+ return null;
2526
+ }
2527
+
2528
+ const stackTraceElement = document.createElement('div');
2529
+ stackTraceElement.classList.add('timeline-details-view-row', 'timeline-details-stack-values');
2510
2530
  callFrameContents.markAsRoot();
2511
2531
  callFrameContents.show(stackTraceElement);
2532
+ return stackTraceElement;
2512
2533
  }
2513
2534
  }
2514
2535
 
@@ -324,6 +324,11 @@ export class StackTracePreviewContent extends UI.Widget.Widget {
324
324
  #links: HTMLElement[] = [];
325
325
 
326
326
  readonly #table: HTMLElement;
327
+ /**
328
+ * Updated when we update to define if we have any rows for the StackTrace;
329
+ * allowing the caller to know if this element is empty or not.
330
+ */
331
+ #hasRows = false;
327
332
 
328
333
  constructor(element?: HTMLElement, target?: SDK.Target.Target, linkifier?: Linkifier, options?: Options) {
329
334
  super(element, {useShadowDom: true});
@@ -353,6 +358,10 @@ export class StackTracePreviewContent extends UI.Widget.Widget {
353
358
  this.performUpdate();
354
359
  }
355
360
 
361
+ hasContent(): boolean {
362
+ return this.#hasRows;
363
+ }
364
+
356
365
  override performUpdate(): void {
357
366
  if (!this.#linkifier) {
358
367
  return;
@@ -363,6 +372,7 @@ export class StackTracePreviewContent extends UI.Widget.Widget {
363
372
  if (this.#stackTrace) {
364
373
  const stackTraceRows = buildStackTraceRows(
365
374
  this.#stackTrace, this.#target ?? null, this.#linkifier, tabStops, this.#options.showColumnNumber);
375
+ this.#hasRows = stackTraceRows.length > 0;
366
376
  this.#links = renderStackTraceTable(this.#table, this.element, this.#options.expandable ?? false, stackTraceRows);
367
377
  return;
368
378
  }
@@ -373,6 +383,7 @@ export class StackTracePreviewContent extends UI.Widget.Widget {
373
383
  const stackTraceRows = buildStackTraceRowsForLegacyRuntimeStackTrace(
374
384
  runtimeStackTrace ?? {callFrames: []}, this.#target ?? null, this.#linkifier, tabStops, updateCallback,
375
385
  this.#options.showColumnNumber);
386
+ this.#hasRows = stackTraceRows.length > 0;
376
387
  this.#links = renderStackTraceTable(this.#table, this.element, this.#options.expandable ?? false, stackTraceRows);
377
388
  }
378
389
 
@@ -115,11 +115,11 @@ ol.tree-outline:not(.hide-selection-when-blurred) li.selected:focus {
115
115
 
116
116
  .tree-outline li::before {
117
117
  user-select: none;
118
+ mask-position: left center;
118
119
  mask-image: var(--image-file-arrow-collapse);
119
120
  background-color: var(--icon-default);
120
121
  content: "\A0\A0";
121
122
  text-shadow: none;
122
- margin-top: calc(-1 * var(--sys-size-2));
123
123
  height: var(--sys-size-8);
124
124
  width: var(--sys-size-8);
125
125
  }
package/package.json CHANGED
@@ -104,5 +104,5 @@
104
104
  "flat-cache": "6.1.12"
105
105
  }
106
106
  },
107
- "version": "1.0.1569477"
107
+ "version": "1.0.1570343"
108
108
  }