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 +1 -1
- package/front_end/core/sdk/DOMModel.ts +6 -2
- package/front_end/panels/common/DOMLinkifier.ts +12 -1
- package/front_end/panels/elements/AdoptedStyleSheetTreeElement.ts +6 -1
- package/front_end/panels/elements/ElementsPanel.ts +28 -9
- package/front_end/panels/elements/ElementsTreeOutline.ts +22 -2
- package/front_end/panels/elements/StylePropertiesSection.ts +10 -0
- package/front_end/panels/elements/elements-meta.ts +1 -0
- package/front_end/panels/emulation/DeviceModeToolbar.ts +49 -60
- package/front_end/panels/timeline/TimelineUIUtils.ts +52 -31
- package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +11 -0
- package/front_end/ui/legacy/treeoutline.css +1 -1
- package/package.json +1 -1
package/WATCHLISTS
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
|
|
27
27
|
'WATCHLISTS': {
|
|
28
28
|
'all': ['devtools-reviews+devtools@chromium.org'],
|
|
29
|
-
'chromedriver': [
|
|
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
|
|
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
|
|
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(
|
|
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
|
-
|
|
1314
|
-
reveal(
|
|
1315
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -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.
|
|
271
|
-
this.
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
this.
|
|
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
|
|
302
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
334
|
+
mainToolbar.appendToolbarItem(this.uaItem);
|
|
349
335
|
|
|
350
336
|
this.throttlingConditionsItem =
|
|
351
337
|
MobileThrottling.ThrottlingManager.throttlingManager().createMobileThrottlingButton();
|
|
352
338
|
this.throttlingConditionsItem.turnShrinkable();
|
|
353
|
-
|
|
339
|
+
mainToolbar.appendToolbarItem(this.throttlingConditionsItem);
|
|
354
340
|
const saveDataItem = MobileThrottling.ThrottlingManager.throttlingManager().createSaveDataOverrideSelector();
|
|
355
341
|
saveDataItem.turnShrinkable();
|
|
356
|
-
|
|
357
|
-
}
|
|
342
|
+
mainToolbar.appendToolbarItem(saveDataItem);
|
|
358
343
|
|
|
359
|
-
|
|
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
|
-
|
|
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
|
-
|
|
352
|
+
mainToolbar.appendToolbarItem(this.spanButton);
|
|
369
353
|
|
|
370
354
|
// Show posture toolbar menu for foldable devices.
|
|
371
|
-
|
|
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
|
-
|
|
361
|
+
mainToolbar.appendToolbarItem(this.postureItem);
|
|
362
|
+
|
|
363
|
+
return mainToolbar;
|
|
378
364
|
}
|
|
379
365
|
|
|
380
|
-
private
|
|
381
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1729
|
-
|
|
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
|
-
|
|
1772
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2509
|
-
|
|
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