chrome-devtools-frontend 1.0.1026160 → 1.0.1027447
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/config/owner/LIGHTHOUSE_OWNERS +1 -1
- package/docs/triage_guidelines.md +1 -122
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/i18n/locales/en-US.json +24 -0
- package/front_end/core/i18n/locales/en-XL.json +24 -0
- package/front_end/core/root/Runtime.ts +1 -0
- package/front_end/core/sdk/NetworkManager.ts +24 -3
- package/front_end/core/sdk/ResourceTreeModel.ts +1 -1
- package/front_end/core/sdk/SourceMap.ts +22 -6
- package/front_end/core/sdk/sdk-meta.ts +7 -0
- package/front_end/devtools_compatibility.js +1 -0
- package/front_end/entrypoints/main/MainImpl.ts +5 -0
- package/front_end/generated/ARIAProperties.js +723 -723
- package/front_end/generated/SupportedCSSProperties.js +2835 -2835
- package/front_end/generated/protocol.ts +4 -0
- package/front_end/legacy_test_runner/axe_core_test_runner/axe_core_test_runner.js +1 -1
- package/front_end/legacy_test_runner/sources_test_runner/DebuggerTestRunner.js +1 -1
- package/front_end/models/bindings/CompilerScriptMapping.ts +1 -1
- package/front_end/models/bindings/DebuggerLanguagePlugins.ts +38 -11
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +7 -1
- package/front_end/models/bindings/IgnoreListManager.ts +35 -22
- package/front_end/models/issues_manager/descriptions/clientHintMetaTagAllowListInvalidOrigin.md +1 -1
- package/front_end/models/issues_manager/descriptions/clientHintMetaTagModifiedHTML.md +1 -1
- package/front_end/models/text_utils/TextRange.ts +8 -0
- package/front_end/models/timeline_model/TimelineModel.ts +18 -1
- package/front_end/panels/accessibility/ARIAAttributesView.ts +2 -0
- package/front_end/panels/console/consoleView.css +4 -0
- package/front_end/panels/elements/ElementsTreeOutline.ts +10 -16
- package/front_end/panels/elements/TopLayerContainer.ts +16 -22
- package/front_end/panels/elements/components/ElementsBreadcrumbs.ts +45 -50
- package/front_end/panels/lighthouse/LighthouseController.ts +3 -0
- package/front_end/panels/lighthouse/LighthousePanel.ts +2 -0
- package/front_end/panels/security/SecurityPanel.ts +52 -0
- package/front_end/panels/security/originView.css +1 -1
- package/front_end/panels/settings/FrameworkIgnoreListSettingsTab.ts +16 -0
- package/front_end/panels/sources/CallStackSidebarPane.ts +2 -3
- package/front_end/panels/sources/DebuggerPlugin.ts +8 -2
- package/front_end/panels/sources/navigatorTree.css +3 -3
- package/front_end/panels/timeline/TimelineFlameChartDataProvider.ts +1 -1
- package/front_end/ui/components/docs/building-ui-documentation/CreatingComponents.md +172 -1
- package/front_end/ui/components/panel_feedback/PreviewToggle.ts +15 -16
- package/front_end/ui/components/panel_feedback/previewToggle.css +13 -15
- package/front_end/ui/components/text_editor/TextEditor.ts +3 -0
- package/front_end/ui/legacy/ARIAUtils.ts +1 -75
- package/front_end/ui/legacy/ListControl.ts +4 -0
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +13 -3
- package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +5 -4
- package/front_end/ui/legacy/inspectorCommon.css +0 -9
- package/package.json +1 -1
@@ -236,6 +236,10 @@ export namespace Accessibility {
|
|
236
236
|
* This `Node`'s role, whether explicit or implicit.
|
237
237
|
*/
|
238
238
|
role?: AXValue;
|
239
|
+
/**
|
240
|
+
* This `Node`'s Chrome raw role.
|
241
|
+
*/
|
242
|
+
chromeRole?: AXValue;
|
239
243
|
/**
|
240
244
|
* The accessible name for this `Node`.
|
241
245
|
*/
|
@@ -133,7 +133,7 @@ const DEFAULT_CONFIG = {
|
|
133
133
|
// ignored by the 'aria-valid-attr' rule.
|
134
134
|
// This should be removed after axe-core is updated.
|
135
135
|
// See: https://github.com/dequelabs/axe-core/issues/1457
|
136
|
-
{id: 'aria-valid-attr', options: ['aria-placeholder']}
|
136
|
+
{id: 'aria-valid-attr', options: ['aria-placeholder', 'aria-description']}
|
137
137
|
],
|
138
138
|
runOnly: {type: 'tags', values: {include: ['wcag2a', 'best-practice'], exclude: ['experimental']}}
|
139
139
|
};
|
@@ -291,7 +291,7 @@ SourcesTestRunner.captureStackTraceIntoString = async function(callFrames, async
|
|
291
291
|
const script = location.script();
|
292
292
|
const uiLocation = await self.Bindings.debuggerWorkspaceBinding.rawLocationToUILocation(location);
|
293
293
|
const isFramework =
|
294
|
-
uiLocation ? self.Bindings.ignoreListManager.
|
294
|
+
uiLocation ? self.Bindings.ignoreListManager.isUserIgnoreListedURL(uiLocation.uiSourceCode.url()) : false;
|
295
295
|
|
296
296
|
if (options.dropFrameworkCallFrames && isFramework) {
|
297
297
|
continue;
|
@@ -247,7 +247,7 @@ export class CompilerScriptMapping implements DebuggerSourceMapping {
|
|
247
247
|
const sourceMap = event.data.sourceMap;
|
248
248
|
await this.removeStubUISourceCode(script);
|
249
249
|
|
250
|
-
if (IgnoreListManager.instance().
|
250
|
+
if (IgnoreListManager.instance().isUserIgnoreListedURL(script.sourceURL, script.isContentScript())) {
|
251
251
|
this.sourceMapAttachedForTest(sourceMap);
|
252
252
|
return;
|
253
253
|
}
|
@@ -656,15 +656,23 @@ class SourceScopeRemoteObject extends SDK.RemoteObject.RemoteObjectImpl {
|
|
656
656
|
}
|
657
657
|
|
658
658
|
for (const variable of this.variables) {
|
659
|
-
let sourceVar;
|
659
|
+
let sourceVar: SDK.RemoteObject.RemoteObject|undefined;
|
660
660
|
try {
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
665
|
-
|
666
|
-
|
667
|
-
|
661
|
+
const evalResult = await this.#plugin.evaluate(variable.name, getRawLocation(this.#callFrame), this.stopId);
|
662
|
+
if (evalResult) {
|
663
|
+
sourceVar = new ExtensionRemoteObject(this.#callFrame, evalResult, this.#plugin);
|
664
|
+
}
|
665
|
+
// For backwards compatibility, fall back to the legacy API if the plugin doesn't define the new one.
|
666
|
+
// TODO(crbug.com/1342848) Remove
|
667
|
+
if (!sourceVar) {
|
668
|
+
sourceVar = await getValueTreeForExpression(this.#callFrame, this.#plugin, variable.name, ({
|
669
|
+
generatePreview: false,
|
670
|
+
includeCommandLineAPI: true,
|
671
|
+
objectGroup: 'backtrace',
|
672
|
+
returnByValue: false,
|
673
|
+
silent: false,
|
674
|
+
} as SDK.RuntimeModel.EvaluationOptions));
|
675
|
+
}
|
668
676
|
} catch (e) {
|
669
677
|
console.warn(e);
|
670
678
|
sourceVar = new SDK.RemoteObject.LocalJSONObject(undefined);
|
@@ -918,7 +926,7 @@ export class DebuggerLanguagePluginManager implements
|
|
918
926
|
error: string,
|
919
927
|
}|null> {
|
920
928
|
const {script} = callFrame;
|
921
|
-
const {expression} = options;
|
929
|
+
const {expression, returnByValue, throwOnSideEffect} = options;
|
922
930
|
const {plugin} = await this.rawModuleIdAndPluginForScript(script);
|
923
931
|
if (!plugin) {
|
924
932
|
return null;
|
@@ -929,9 +937,20 @@ export class DebuggerLanguagePluginManager implements
|
|
929
937
|
return null;
|
930
938
|
}
|
931
939
|
|
940
|
+
if (returnByValue) {
|
941
|
+
return {error: 'Cannot return by value'};
|
942
|
+
}
|
943
|
+
if (throwOnSideEffect) {
|
944
|
+
return {error: 'Cannot guarantee side-effect freedom'};
|
945
|
+
}
|
946
|
+
|
932
947
|
try {
|
933
|
-
const object = await
|
934
|
-
|
948
|
+
const object = await plugin.evaluate(expression, location, this.stopIdForCallFrame(callFrame));
|
949
|
+
if (!object) {
|
950
|
+
const object = await getValueTreeForExpression(callFrame, plugin, expression, options);
|
951
|
+
return {object, exceptionDetails: undefined};
|
952
|
+
}
|
953
|
+
return {object: new ExtensionRemoteObject(callFrame, object, plugin), exceptionDetails: undefined};
|
935
954
|
} catch (error) {
|
936
955
|
if (error instanceof FormattingError) {
|
937
956
|
const {exception: object, exceptionDetails} = error;
|
@@ -1542,4 +1561,12 @@ class ModelData {
|
|
1542
1561
|
export interface DebuggerLanguagePlugin extends Chrome.DevTools.LanguageExtensionPlugin {
|
1543
1562
|
name: string;
|
1544
1563
|
handleScript(script: SDK.Script.Script): boolean;
|
1564
|
+
|
1565
|
+
// These are optional in the public interface for compatibility purposes, but ExtensionAPI handles the missing
|
1566
|
+
// functions gracefully, so we can mark them non-optional here.
|
1567
|
+
// TODO(crbug.com/1342848) Remove
|
1568
|
+
evaluate(expression: string, context: Chrome.DevTools.RawLocation, stopId: unknown):
|
1569
|
+
Promise<Chrome.DevTools.RemoteObject|null>;
|
1570
|
+
getProperties(objectId: Chrome.DevTools.RemoteObjectId): Promise<Chrome.DevTools.PropertyDescriptor[]>;
|
1571
|
+
releaseObject(objectId: Chrome.DevTools.RemoteObjectId): Promise<void>;
|
1545
1572
|
}
|
@@ -509,7 +509,13 @@ export class Location extends LiveLocationWithPool {
|
|
509
509
|
|
510
510
|
async isIgnoreListed(): Promise<boolean> {
|
511
511
|
const uiLocation = await this.uiLocation();
|
512
|
-
|
512
|
+
if (!uiLocation) {
|
513
|
+
return false;
|
514
|
+
}
|
515
|
+
const manager = this.rawLocation.debuggerModel.sourceMapManager();
|
516
|
+
const script = this.rawLocation.script();
|
517
|
+
const map = script ? await manager.sourceMapForClientPromise(script) : null;
|
518
|
+
return IgnoreListManager.instance().isUserOrSourceMapIgnoreListedUISourceCode(uiLocation.uiSourceCode, map);
|
513
519
|
}
|
514
520
|
}
|
515
521
|
|
@@ -28,6 +28,9 @@ export class IgnoreListManager implements SDK.TargetManager.SDKModelObserver<SDK
|
|
28
28
|
Common.Settings.Settings.instance()
|
29
29
|
.moduleSetting('skipContentScripts')
|
30
30
|
.addChangeListener(this.patternChanged.bind(this));
|
31
|
+
Common.Settings.Settings.instance()
|
32
|
+
.moduleSetting('automaticallyIgnoreListKnownThirdPartyScripts')
|
33
|
+
.addChangeListener(this.patternChanged.bind(this));
|
31
34
|
|
32
35
|
this.#listeners = new Set();
|
33
36
|
|
@@ -97,21 +100,33 @@ export class IgnoreListManager implements SDK.TargetManager.SDKModelObserver<SDK
|
|
97
100
|
return debuggerModel.setBlackboxPatterns(patterns);
|
98
101
|
}
|
99
102
|
|
100
|
-
|
103
|
+
isUserOrSourceMapIgnoreListedUISourceCode(
|
104
|
+
uiSourceCode: Workspace.UISourceCode.UISourceCode, sourceMap: SDK.SourceMap.SourceMap|null): boolean {
|
101
105
|
const projectType = uiSourceCode.project().type();
|
102
106
|
const isContentScript = projectType === Workspace.Workspace.projectTypes.ContentScripts;
|
103
|
-
if (
|
107
|
+
if (this.skipContentScripts && isContentScript) {
|
104
108
|
return true;
|
105
109
|
}
|
106
110
|
const url = this.uiSourceCodeURL(uiSourceCode);
|
107
|
-
return url ? this.
|
111
|
+
return url ? this.isUserOrSourceMapIgnoreListedURL(url, sourceMap) : false;
|
112
|
+
}
|
113
|
+
|
114
|
+
isUserOrSourceMapIgnoreListedURL(url: Platform.DevToolsPath.UrlString, sourceMap: SDK.SourceMap.SourceMap|null):
|
115
|
+
boolean {
|
116
|
+
if (this.isUserIgnoreListedURL(url)) {
|
117
|
+
return true;
|
118
|
+
}
|
119
|
+
if (this.automaticallyIgnoreListKnownThirdPartyScripts && sourceMap?.hasIgnoreListHint(url)) {
|
120
|
+
return true;
|
121
|
+
}
|
122
|
+
return false;
|
108
123
|
}
|
109
124
|
|
110
|
-
|
125
|
+
isUserIgnoreListedURL(url: Platform.DevToolsPath.UrlString, isContentScript?: boolean): boolean {
|
111
126
|
if (this.#isIgnoreListedURLCache.has(url)) {
|
112
127
|
return Boolean(this.#isIgnoreListedURLCache.get(url));
|
113
128
|
}
|
114
|
-
if (isContentScript &&
|
129
|
+
if (isContentScript && this.skipContentScripts) {
|
115
130
|
return true;
|
116
131
|
}
|
117
132
|
const regex = this.getSkipStackFramesPatternSetting().asRegExp();
|
@@ -137,8 +152,9 @@ export class IgnoreListManager implements SDK.TargetManager.SDKModelObserver<SDK
|
|
137
152
|
|
138
153
|
private async updateScriptRanges(script: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap|null): Promise<void> {
|
139
154
|
let hasIgnoreListedMappings = false;
|
140
|
-
if (!IgnoreListManager.instance().
|
141
|
-
hasIgnoreListedMappings =
|
155
|
+
if (!IgnoreListManager.instance().isUserIgnoreListedURL(script.sourceURL, script.isContentScript())) {
|
156
|
+
hasIgnoreListedMappings =
|
157
|
+
sourceMap?.sourceURLs().some(url => this.isUserOrSourceMapIgnoreListedURL(url, sourceMap)) ?? false;
|
142
158
|
}
|
143
159
|
if (!hasIgnoreListedMappings) {
|
144
160
|
if (scriptToRange.get(script) && await script.setBlackboxedRanges([])) {
|
@@ -152,21 +168,10 @@ export class IgnoreListManager implements SDK.TargetManager.SDKModelObserver<SDK
|
|
152
168
|
return;
|
153
169
|
}
|
154
170
|
|
155
|
-
const
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
if (mappings[0].lineNumber !== 0 || mappings[0].columnNumber !== 0) {
|
160
|
-
newRanges.push({lineNumber: 0, columnNumber: 0});
|
161
|
-
currentIgnoreListed = true;
|
162
|
-
}
|
163
|
-
for (const mapping of mappings) {
|
164
|
-
if (mapping.sourceURL && currentIgnoreListed !== this.isIgnoreListedURL(mapping.sourceURL)) {
|
165
|
-
newRanges.push({lineNumber: mapping.lineNumber, columnNumber: mapping.columnNumber});
|
166
|
-
currentIgnoreListed = !currentIgnoreListed;
|
167
|
-
}
|
168
|
-
}
|
169
|
-
}
|
171
|
+
const newRanges =
|
172
|
+
sourceMap
|
173
|
+
.findRanges(srcURL => this.isUserOrSourceMapIgnoreListedURL(srcURL, sourceMap), {isStartMatching: true})
|
174
|
+
.flatMap(range => [range.start, range.end]);
|
170
175
|
|
171
176
|
const oldRanges = scriptToRange.get(script) || [];
|
172
177
|
if (!isEqual(oldRanges, newRanges) && await script.setBlackboxedRanges(newRanges)) {
|
@@ -210,6 +215,14 @@ export class IgnoreListManager implements SDK.TargetManager.SDKModelObserver<SDK
|
|
210
215
|
}
|
211
216
|
}
|
212
217
|
|
218
|
+
get skipContentScripts(): boolean {
|
219
|
+
return Common.Settings.Settings.instance().moduleSetting('skipContentScripts').get();
|
220
|
+
}
|
221
|
+
|
222
|
+
get automaticallyIgnoreListKnownThirdPartyScripts(): boolean {
|
223
|
+
return Common.Settings.Settings.instance().moduleSetting('automaticallyIgnoreListKnownThirdPartyScripts').get();
|
224
|
+
}
|
225
|
+
|
213
226
|
ignoreListContentScripts(): void {
|
214
227
|
Common.Settings.Settings.instance().moduleSetting('skipContentScripts').set(true);
|
215
228
|
}
|
@@ -230,6 +230,14 @@ export class TextRange {
|
|
230
230
|
}
|
231
231
|
return this.startLine < lineNumber && lineNumber < this.endLine;
|
232
232
|
}
|
233
|
+
|
234
|
+
get start(): {lineNumber: number, columnNumber: number} {
|
235
|
+
return {lineNumber: this.startLine, columnNumber: this.startColumn};
|
236
|
+
}
|
237
|
+
|
238
|
+
get end(): {lineNumber: number, columnNumber: number} {
|
239
|
+
return {lineNumber: this.endLine, columnNumber: this.endColumn};
|
240
|
+
}
|
233
241
|
}
|
234
242
|
|
235
243
|
export class SourceRange {
|
@@ -94,6 +94,18 @@ const UIStrings = {
|
|
94
94
|
*@description Title of an auction worklet in the timeline flame chart of the Performance panel
|
95
95
|
*/
|
96
96
|
unknownWorklet: 'Auction Worklet',
|
97
|
+
|
98
|
+
/**
|
99
|
+
*@description Title of control thread of a service process for an auction worklet in the timeline flame chart of the Performance panel
|
100
|
+
*/
|
101
|
+
workletService: 'Auction Worklet Service',
|
102
|
+
|
103
|
+
/**
|
104
|
+
*@description Title of control thread of a service process for an auction worklet with known URL in the timeline flame chart of the Performance panel
|
105
|
+
* @example {https://google.com} PH1
|
106
|
+
*/
|
107
|
+
workletServiceS: 'Auction Worklet Service — {PH1}',
|
108
|
+
|
97
109
|
};
|
98
110
|
const str_ = i18n.i18n.registerUIStrings('models/timeline_model/TimelineModel.ts', UIStrings);
|
99
111
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
@@ -535,7 +547,8 @@ export class TimelineModelImpl {
|
|
535
547
|
} else {
|
536
548
|
let urlForOther: Platform.DevToolsPath.UrlString|null = null;
|
537
549
|
let workletTypeForOther: WorkletType = WorkletType.NotWorklet;
|
538
|
-
if (thread.name() === TimelineModelImpl.AuctionWorkletThreadName
|
550
|
+
if (thread.name() === TimelineModelImpl.AuctionWorkletThreadName ||
|
551
|
+
thread.name() === TimelineModelImpl.UtilityMainThreadName) {
|
539
552
|
if (typeof workletUrl !== 'boolean') {
|
540
553
|
urlForOther = workletUrl;
|
541
554
|
}
|
@@ -822,6 +835,9 @@ export class TimelineModelImpl {
|
|
822
835
|
} else if (thread.name() === TimelineModelImpl.AuctionWorkletThreadName) {
|
823
836
|
track.url = url || Platform.DevToolsPath.EmptyUrlString;
|
824
837
|
track.name = TimelineModelImpl.nameAuctionWorklet(workletType, url);
|
838
|
+
} else if (workletType !== WorkletType.NotWorklet && thread.name() === TimelineModelImpl.UtilityMainThreadName) {
|
839
|
+
track.url = url || Platform.DevToolsPath.EmptyUrlString;
|
840
|
+
track.name = url ? i18nString(UIStrings.workletServiceS, {PH1: url}) : i18nString(UIStrings.workletService);
|
825
841
|
}
|
826
842
|
this.tracksInternal.push(track);
|
827
843
|
|
@@ -1700,6 +1716,7 @@ export namespace TimelineModelImpl {
|
|
1700
1716
|
export const WorkerThreadNameLegacy = 'DedicatedWorker Thread';
|
1701
1717
|
export const RendererMainThreadName = 'CrRendererMain';
|
1702
1718
|
export const BrowserMainThreadName = 'CrBrowserMain';
|
1719
|
+
export const UtilityMainThreadName = 'CrUtilityMain';
|
1703
1720
|
export const AuctionWorkletThreadName = 'AuctionV8HelperThread';
|
1704
1721
|
|
1705
1722
|
export const DevToolsMetadataEvent = {
|
@@ -229,6 +229,7 @@ const ATTRIBUTES = new Set<string>([
|
|
229
229
|
'aria-activedescendant',
|
230
230
|
'aria-atomic',
|
231
231
|
'aria-autocomplete',
|
232
|
+
'aria-braillelabel',
|
232
233
|
'aria-brailleroledescription',
|
233
234
|
'aria-busy',
|
234
235
|
'aria-checked',
|
@@ -239,6 +240,7 @@ const ATTRIBUTES = new Set<string>([
|
|
239
240
|
'aria-controls',
|
240
241
|
'aria-current',
|
241
242
|
'aria-describedby',
|
243
|
+
'aria-description',
|
242
244
|
'aria-details',
|
243
245
|
'aria-disabled',
|
244
246
|
'aria-dropeffect',
|
@@ -453,6 +453,8 @@ export class ElementsTreeOutline extends
|
|
453
453
|
}
|
454
454
|
}
|
455
455
|
|
456
|
+
this.createTopLayerContainer();
|
457
|
+
|
456
458
|
if (selectedNode) {
|
457
459
|
this.revealAndSelectNode(selectedNode, true);
|
458
460
|
}
|
@@ -1173,24 +1175,16 @@ export class ElementsTreeOutline extends
|
|
1173
1175
|
}
|
1174
1176
|
|
1175
1177
|
return new Promise<void>(resolve => {
|
1176
|
-
|
1177
|
-
|
1178
|
-
|
1179
|
-
|
1180
|
-
|
1181
|
-
|
1182
|
-
.then(() => {
|
1183
|
-
if (treeElement.node().nodeName() === 'BODY') {
|
1184
|
-
this.createTopLayerContainer(treeElement);
|
1185
|
-
}
|
1186
|
-
});
|
1178
|
+
treeElement.node().getChildNodes(() => {
|
1179
|
+
populatedTreeElements.add(treeElement);
|
1180
|
+
this.updateModifiedParentNode(treeElement.node());
|
1181
|
+
resolve();
|
1182
|
+
});
|
1183
|
+
});
|
1187
1184
|
}
|
1188
1185
|
|
1189
|
-
createTopLayerContainer(
|
1190
|
-
|
1191
|
-
this.topLayerContainer = new TopLayerContainer(bodyElement);
|
1192
|
-
}
|
1193
|
-
this.topLayerContainer.updateBody(bodyElement);
|
1186
|
+
createTopLayerContainer(): void {
|
1187
|
+
this.topLayerContainer = new TopLayerContainer(this);
|
1194
1188
|
void this.topLayerContainer.throttledUpdateTopLayerElements();
|
1195
1189
|
}
|
1196
1190
|
|
@@ -23,29 +23,18 @@ const str_ = i18n.i18n.registerUIStrings('panels/elements/TopLayerContainer.ts',
|
|
23
23
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
24
24
|
|
25
25
|
export class TopLayerContainer extends UI.TreeOutline.TreeElement {
|
26
|
-
|
27
|
-
domModel: SDK.DOMModel.DOMModel;
|
26
|
+
domContainer: ElementsTreeOutline.ElementsTreeOutline;
|
28
27
|
currentTopLayerElements: Set<ElementsTreeElement>;
|
29
|
-
bodyElement: ElementsTreeElement;
|
30
28
|
topLayerUpdateThrottler: Common.Throttler.Throttler;
|
31
29
|
#inserted = false;
|
32
30
|
|
33
|
-
constructor(
|
31
|
+
constructor(domContainer: ElementsTreeOutline.ElementsTreeOutline) {
|
34
32
|
super('#top-layer');
|
35
|
-
this.
|
36
|
-
this.domModel = bodyElement.node().domModel();
|
37
|
-
this.treeOutline = null;
|
33
|
+
this.domContainer = domContainer;
|
38
34
|
this.currentTopLayerElements = new Set();
|
39
35
|
this.topLayerUpdateThrottler = new Common.Throttler.Throttler(1);
|
40
36
|
}
|
41
37
|
|
42
|
-
updateBody(bodyElement: ElementsTreeElement): void {
|
43
|
-
if (this.bodyElement !== bodyElement) {
|
44
|
-
this.#inserted = false;
|
45
|
-
}
|
46
|
-
this.bodyElement = bodyElement;
|
47
|
-
}
|
48
|
-
|
49
38
|
async throttledUpdateTopLayerElements(): Promise<void> {
|
50
39
|
await this.topLayerUpdateThrottler.schedule(() => this.updateTopLayerElements());
|
51
40
|
}
|
@@ -55,7 +44,13 @@ export class TopLayerContainer extends UI.TreeOutline.TreeElement {
|
|
55
44
|
this.removeCurrentTopLayerElementsAdorners();
|
56
45
|
this.currentTopLayerElements = new Set();
|
57
46
|
|
58
|
-
const
|
47
|
+
const domModel = this.domContainer.rootDOMNode?.domModel();
|
48
|
+
if (!domModel) {
|
49
|
+
this.hidden = true;
|
50
|
+
return;
|
51
|
+
}
|
52
|
+
|
53
|
+
const newTopLayerElementsIDs = await domModel.getTopLayerElements();
|
59
54
|
if (!newTopLayerElementsIDs || newTopLayerElementsIDs.length === 0) {
|
60
55
|
this.hidden = true;
|
61
56
|
return;
|
@@ -63,12 +58,12 @@ export class TopLayerContainer extends UI.TreeOutline.TreeElement {
|
|
63
58
|
|
64
59
|
let topLayerElementIndex = 0;
|
65
60
|
for (let i = 0; i < newTopLayerElementsIDs.length; i++) {
|
66
|
-
const topLayerDOMNode =
|
61
|
+
const topLayerDOMNode = domModel.idToDOMNode.get(newTopLayerElementsIDs[i]);
|
67
62
|
if (topLayerDOMNode && topLayerDOMNode.nodeName() !== '::backdrop') {
|
68
63
|
const topLayerElementShortcut = new SDK.DOMModel.DOMNodeShortcut(
|
69
|
-
|
64
|
+
domModel.target(), topLayerDOMNode.backendNodeId(), 0, topLayerDOMNode.nodeName());
|
70
65
|
const topLayerElementRepresentation = new ElementsTreeOutline.ShortcutTreeElement(topLayerElementShortcut);
|
71
|
-
const topLayerTreeElement = this.
|
66
|
+
const topLayerTreeElement = this.domContainer.treeElementByNode.get(topLayerDOMNode);
|
72
67
|
if (!topLayerTreeElement) {
|
73
68
|
continue;
|
74
69
|
}
|
@@ -78,11 +73,10 @@ export class TopLayerContainer extends UI.TreeOutline.TreeElement {
|
|
78
73
|
this.currentTopLayerElements.add(topLayerTreeElement);
|
79
74
|
this.appendChild(topLayerElementRepresentation);
|
80
75
|
// Add the element's backdrop if previous top layer element is a backdrop.
|
81
|
-
const previousTopLayerDOMNode =
|
82
|
-
(i > 0) ? this.domModel.idToDOMNode.get(newTopLayerElementsIDs[i - 1]) : undefined;
|
76
|
+
const previousTopLayerDOMNode = (i > 0) ? domModel.idToDOMNode.get(newTopLayerElementsIDs[i - 1]) : undefined;
|
83
77
|
if (previousTopLayerDOMNode && previousTopLayerDOMNode.nodeName() === '::backdrop') {
|
84
78
|
const backdropElementShortcut = new SDK.DOMModel.DOMNodeShortcut(
|
85
|
-
|
79
|
+
domModel.target(), previousTopLayerDOMNode.backendNodeId(), 0, previousTopLayerDOMNode.nodeName());
|
86
80
|
const backdropElementRepresentation = new ElementsTreeOutline.ShortcutTreeElement(backdropElementShortcut);
|
87
81
|
topLayerElementRepresentation.appendChild(backdropElementRepresentation);
|
88
82
|
}
|
@@ -91,7 +85,7 @@ export class TopLayerContainer extends UI.TreeOutline.TreeElement {
|
|
91
85
|
|
92
86
|
this.hidden = topLayerElementIndex <= 0;
|
93
87
|
if (!this.hidden && !this.#inserted) {
|
94
|
-
this.
|
88
|
+
this.domContainer.appendChild(this);
|
95
89
|
this.#inserted = true;
|
96
90
|
}
|
97
91
|
}
|
@@ -45,6 +45,7 @@ export class ElementsBreadcrumbs extends HTMLElement {
|
|
45
45
|
static readonly litTagName = LitHtml.literal`devtools-elements-breadcrumbs`;
|
46
46
|
readonly #shadow = this.attachShadow({mode: 'open'});
|
47
47
|
readonly #resizeObserver = new ResizeObserver(() => this.#checkForOverflowOnResize());
|
48
|
+
readonly #renderBound = this.#render.bind(this);
|
48
49
|
|
49
50
|
#crumbsData: readonly DOMNode[] = [];
|
50
51
|
#selectedDOMNode: Readonly<DOMNode>|null = null;
|
@@ -61,7 +62,7 @@ export class ElementsBreadcrumbs extends HTMLElement {
|
|
61
62
|
this.#selectedDOMNode = data.selectedNode;
|
62
63
|
this.#crumbsData = data.crumbs;
|
63
64
|
this.#userHasManuallyScrolled = false;
|
64
|
-
void this.#
|
65
|
+
void ComponentHelpers.ScheduledRender.scheduleRender(this, this.#renderBound);
|
65
66
|
}
|
66
67
|
|
67
68
|
disconnectedCallback(): void {
|
@@ -104,12 +105,6 @@ export class ElementsBreadcrumbs extends HTMLElement {
|
|
104
105
|
}
|
105
106
|
}
|
106
107
|
|
107
|
-
async #update(): Promise<void> {
|
108
|
-
await this.#render();
|
109
|
-
this.#engageResizeObserver();
|
110
|
-
void this.#ensureSelectedNodeIsVisible();
|
111
|
-
}
|
112
|
-
|
113
108
|
#onCrumbMouseMove(node: DOMNode): () => void {
|
114
109
|
return (): void => node.highlightNode();
|
115
110
|
}
|
@@ -267,53 +262,53 @@ export class ElementsBreadcrumbs extends HTMLElement {
|
|
267
262
|
`;
|
268
263
|
}
|
269
264
|
|
270
|
-
|
265
|
+
#render(): void {
|
271
266
|
const crumbs = crumbsToRender(this.#crumbsData, this.#selectedDOMNode);
|
272
267
|
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
<
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
// clang-format on
|
314
|
-
});
|
268
|
+
// Disabled until https://crbug.com/1079231 is fixed.
|
269
|
+
// clang-format off
|
270
|
+
LitHtml.render(LitHtml.html`
|
271
|
+
<nav class="crumbs" aria-label=${i18nString(UIStrings.breadcrumbs)}>
|
272
|
+
${this.#renderOverflowButton('left', this.#userScrollPosition === 'start')}
|
273
|
+
|
274
|
+
<div class="crumbs-window" @scroll=${this.#onCrumbsWindowScroll}>
|
275
|
+
<ul class="crumbs-scroll-container">
|
276
|
+
${crumbs.map(crumb => {
|
277
|
+
const crumbClasses = {
|
278
|
+
crumb: true,
|
279
|
+
selected: crumb.selected,
|
280
|
+
};
|
281
|
+
// eslint-disable-next-line rulesdir/ban_a_tags_in_lit_html
|
282
|
+
return LitHtml.html`
|
283
|
+
<li class=${LitHtml.Directives.classMap(crumbClasses)}
|
284
|
+
data-node-id=${crumb.node.id}
|
285
|
+
data-crumb="true"
|
286
|
+
>
|
287
|
+
<a href="#"
|
288
|
+
draggable=false
|
289
|
+
class="crumb-link"
|
290
|
+
@click=${this.#onCrumbClick(crumb.node)}
|
291
|
+
@mousemove=${this.#onCrumbMouseMove(crumb.node)}
|
292
|
+
@mouseleave=${this.#onCrumbMouseLeave(crumb.node)}
|
293
|
+
@focus=${this.#onCrumbFocus(crumb.node)}
|
294
|
+
@blur=${this.#onCrumbBlur(crumb.node)}
|
295
|
+
><${NodeText.NodeText.NodeText.litTagName} data-node-title=${crumb.title.main} .data=${{
|
296
|
+
nodeTitle: crumb.title.main,
|
297
|
+
nodeId: crumb.title.extras.id,
|
298
|
+
nodeClasses: crumb.title.extras.classes,
|
299
|
+
} as NodeText.NodeText.NodeTextData}></${NodeText.NodeText.NodeText.litTagName}></a>
|
300
|
+
</li>`;
|
301
|
+
})}
|
302
|
+
</ul>
|
303
|
+
</div>
|
304
|
+
${this.#renderOverflowButton('right', this.#userScrollPosition === 'end')}
|
305
|
+
</nav>
|
306
|
+
`, this.#shadow, { host: this });
|
307
|
+
// clang-format on
|
315
308
|
|
316
309
|
void this.#checkForOverflow();
|
310
|
+
this.#engageResizeObserver();
|
311
|
+
void this.#ensureSelectedNodeIsVisible();
|
317
312
|
}
|
318
313
|
|
319
314
|
async #ensureSelectedNodeIsVisible(): Promise<void> {
|
@@ -309,6 +309,9 @@ export class LighthouseController extends Common.ObjectWrapper.ObjectWrapper<Eve
|
|
309
309
|
return '';
|
310
310
|
}
|
311
311
|
const usageData = await mainTarget.storageAgent().invoke_getUsageAndQuota({origin});
|
312
|
+
if (usageData.getError()) {
|
313
|
+
return '';
|
314
|
+
}
|
312
315
|
const locations = usageData.usageBreakdown.filter(usage => usage.usage)
|
313
316
|
.map(usage => STORAGE_TYPE_NAMES.get(usage.storageType))
|
314
317
|
.map(i18nStringFn => i18nStringFn ? i18nStringFn() : undefined)
|
@@ -308,6 +308,8 @@ export class LighthousePanel extends UI.Panel.Panel {
|
|
308
308
|
const reportContainer = this.auditResultsElement.createChild('div', 'lh-vars lh-root lh-devtools');
|
309
309
|
// @ts-ignore Expose LHR on DOM for e2e tests
|
310
310
|
reportContainer._lighthouseResultForTesting = lighthouseResult;
|
311
|
+
// @ts-ignore Expose Artifacts on DOM for e2e tests
|
312
|
+
reportContainer._lighthouseArtifactsForTesting = artifacts;
|
311
313
|
|
312
314
|
const dom = new LighthouseReport.DOM(this.auditResultsElement.ownerDocument as Document, reportContainer);
|
313
315
|
const renderer = new LighthouseReportRenderer(dom) as LighthouseReport.ReportRenderer;
|