chrome-devtools-frontend 1.0.1006211 → 1.0.1007307
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/gni/devtools_grd_files.gni +2 -0
- package/config/gni/devtools_image_files.gni +2 -0
- package/front_end/Images/src/ic_sources_authored.svg +5 -0
- package/front_end/Images/src/ic_sources_deployed.svg +5 -0
- package/front_end/core/i18n/locales/af.json +202 -43
- package/front_end/core/i18n/locales/am.json +201 -42
- package/front_end/core/i18n/locales/ar.json +201 -42
- package/front_end/core/i18n/locales/as.json +201 -42
- package/front_end/core/i18n/locales/az.json +201 -42
- package/front_end/core/i18n/locales/be.json +201 -42
- package/front_end/core/i18n/locales/bg.json +201 -42
- package/front_end/core/i18n/locales/bn.json +201 -42
- package/front_end/core/i18n/locales/bs.json +201 -42
- package/front_end/core/i18n/locales/ca.json +204 -45
- package/front_end/core/i18n/locales/cs.json +201 -42
- package/front_end/core/i18n/locales/cy.json +201 -42
- package/front_end/core/i18n/locales/da.json +201 -42
- package/front_end/core/i18n/locales/de.json +201 -42
- package/front_end/core/i18n/locales/el.json +201 -42
- package/front_end/core/i18n/locales/en-GB.json +199 -40
- package/front_end/core/i18n/locales/en-US.json +43 -1
- package/front_end/core/i18n/locales/en-XL.json +43 -1
- package/front_end/core/i18n/locales/es-419.json +201 -42
- package/front_end/core/i18n/locales/es.json +201 -42
- package/front_end/core/i18n/locales/et.json +201 -42
- package/front_end/core/i18n/locales/eu.json +202 -43
- package/front_end/core/i18n/locales/fa.json +201 -42
- package/front_end/core/i18n/locales/fi.json +201 -42
- package/front_end/core/i18n/locales/fil.json +201 -42
- package/front_end/core/i18n/locales/fr-CA.json +201 -42
- package/front_end/core/i18n/locales/fr.json +203 -44
- package/front_end/core/i18n/locales/gl.json +203 -44
- package/front_end/core/i18n/locales/gu.json +201 -42
- package/front_end/core/i18n/locales/he.json +201 -42
- package/front_end/core/i18n/locales/hi.json +201 -42
- package/front_end/core/i18n/locales/hr.json +201 -42
- package/front_end/core/i18n/locales/hu.json +201 -42
- package/front_end/core/i18n/locales/hy.json +201 -42
- package/front_end/core/i18n/locales/id.json +201 -42
- package/front_end/core/i18n/locales/is.json +201 -42
- package/front_end/core/i18n/locales/it.json +203 -44
- package/front_end/core/i18n/locales/ja.json +203 -44
- package/front_end/core/i18n/locales/ka.json +201 -42
- package/front_end/core/i18n/locales/kk.json +201 -42
- package/front_end/core/i18n/locales/km.json +201 -42
- package/front_end/core/i18n/locales/kn.json +201 -42
- package/front_end/core/i18n/locales/ko.json +201 -42
- package/front_end/core/i18n/locales/ky.json +201 -42
- package/front_end/core/i18n/locales/lo.json +201 -42
- package/front_end/core/i18n/locales/lt.json +201 -42
- package/front_end/core/i18n/locales/lv.json +201 -42
- package/front_end/core/i18n/locales/mk.json +201 -42
- package/front_end/core/i18n/locales/ml.json +201 -42
- package/front_end/core/i18n/locales/mn.json +201 -42
- package/front_end/core/i18n/locales/mr.json +201 -42
- package/front_end/core/i18n/locales/ms.json +201 -42
- package/front_end/core/i18n/locales/my.json +201 -42
- package/front_end/core/i18n/locales/ne.json +201 -42
- package/front_end/core/i18n/locales/nl.json +201 -42
- package/front_end/core/i18n/locales/no.json +201 -42
- package/front_end/core/i18n/locales/or.json +201 -42
- package/front_end/core/i18n/locales/pa.json +201 -42
- package/front_end/core/i18n/locales/pl.json +201 -42
- package/front_end/core/i18n/locales/pt-PT.json +201 -42
- package/front_end/core/i18n/locales/pt.json +201 -42
- package/front_end/core/i18n/locales/ro.json +201 -42
- package/front_end/core/i18n/locales/ru.json +203 -44
- package/front_end/core/i18n/locales/si.json +201 -42
- package/front_end/core/i18n/locales/sk.json +201 -42
- package/front_end/core/i18n/locales/sl.json +201 -42
- package/front_end/core/i18n/locales/sq.json +201 -42
- package/front_end/core/i18n/locales/sr-Latn.json +201 -42
- package/front_end/core/i18n/locales/sr.json +201 -42
- package/front_end/core/i18n/locales/sv.json +201 -42
- package/front_end/core/i18n/locales/sw.json +201 -42
- package/front_end/core/i18n/locales/ta.json +201 -42
- package/front_end/core/i18n/locales/te.json +202 -43
- package/front_end/core/i18n/locales/th.json +201 -42
- package/front_end/core/i18n/locales/tr.json +201 -42
- package/front_end/core/i18n/locales/uk.json +201 -42
- package/front_end/core/i18n/locales/ur.json +201 -42
- package/front_end/core/i18n/locales/uz.json +201 -42
- package/front_end/core/i18n/locales/vi.json +201 -42
- package/front_end/core/i18n/locales/zh-HK.json +201 -42
- package/front_end/core/i18n/locales/zh-TW.json +201 -42
- package/front_end/core/i18n/locales/zh.json +201 -42
- package/front_end/core/i18n/locales/zu.json +201 -42
- package/front_end/core/sdk/CPUThrottlingManager.ts +54 -0
- package/front_end/core/sdk/DebuggerModel.ts +12 -3
- package/front_end/core/sdk/EmulationModel.ts +7 -0
- package/front_end/core/sdk/NetworkManager.ts +6 -2
- package/front_end/devtools_compatibility.js +1 -0
- package/front_end/entrypoints/formatter_worker/FormatterActions.ts +1 -0
- package/front_end/entrypoints/formatter_worker/ScopeParser.ts +12 -10
- package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +4 -0
- package/front_end/generated/InspectorBackendCommands.js +3 -0
- package/front_end/generated/protocol-mapping.d.ts +2 -0
- package/front_end/generated/protocol-proxy-api.d.ts +3 -0
- package/front_end/generated/protocol.ts +7 -0
- package/front_end/models/formatter/FormatterWorkerPool.ts +6 -0
- package/front_end/models/issues_manager/DeprecationIssue.ts +1 -1
- package/front_end/models/javascript_metadata/JavaScriptMetadata.ts +13 -20
- package/front_end/models/javascript_metadata/NativeFunctions.js +1237 -3962
- package/front_end/models/source_map_scopes/NamesResolver.ts +206 -73
- package/front_end/models/workspace/UISourceCode.ts +7 -0
- package/front_end/panels/accessibility/axBreadcrumbs.css +2 -2
- package/front_end/panels/application/components/BackForwardCacheView.ts +16 -0
- package/front_end/panels/lighthouse/LighthouseStartView.ts +7 -5
- package/front_end/panels/lighthouse/LighthouseStartViewFR.ts +70 -49
- package/front_end/panels/mobile_throttling/ThrottlingManager.ts +113 -4
- package/front_end/panels/network/components/RequestHeadersView.css +31 -3
- package/front_end/panels/network/components/RequestHeadersView.ts +126 -3
- package/front_end/panels/sources/NavigatorView.ts +141 -40
- package/front_end/panels/sources/SourcesPanel.ts +8 -0
- package/front_end/panels/sources/TabbedEditorContainer.ts +2 -2
- package/front_end/panels/sources/sources-meta.ts +6 -0
- package/front_end/panels/timeline/TimelinePanel.ts +27 -4
- package/front_end/panels/timeline/timelinePanel.css +8 -0
- package/front_end/ui/components/text_editor/javascript.ts +12 -14
- package/front_end/ui/legacy/Treeoutline.ts +5 -2
- package/package.json +1 -1
- package/scripts/hosted_mode/server.js +14 -1
- package/scripts/javascript_natives/helpers.js +26 -7
- package/scripts/javascript_natives/index.js +4 -3
- package/scripts/javascript_natives/tests.js +2 -2
@@ -2,7 +2,6 @@
|
|
2
2
|
// Use of this source code is governed by a BSD-style license that can be
|
3
3
|
// found in the LICENSE file.
|
4
4
|
|
5
|
-
import * as Platform from '../../core/platform/platform.js';
|
6
5
|
import * as SDK from '../../core/sdk/sdk.js';
|
7
6
|
import * as Bindings from '../bindings/bindings.js';
|
8
7
|
import * as Formatter from '../formatter/formatter.js';
|
@@ -12,7 +11,7 @@ import * as Protocol from '../../generated/protocol.js';
|
|
12
11
|
|
13
12
|
interface CachedScopeMap {
|
14
13
|
sourceMap: SDK.SourceMap.SourceMap|null;
|
15
|
-
|
14
|
+
mappingPromise: Promise<{variableMapping: Map<string, string>, thisMapping: string|null}>;
|
16
15
|
}
|
17
16
|
|
18
17
|
const scopeToCachedIdentifiersMap = new WeakMap<SDK.DebuggerModel.ScopeChainEntry, CachedScopeMap>();
|
@@ -29,61 +28,138 @@ export class Identifier {
|
|
29
28
|
}
|
30
29
|
}
|
31
30
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
const
|
37
|
-
|
38
|
-
|
39
|
-
return [];
|
31
|
+
const computeScopeTree = async function(functionScope: SDK.DebuggerModel.ScopeChainEntry): Promise<{
|
32
|
+
scopeTree: Formatter.FormatterWorkerPool.ScopeTreeNode, text: TextUtils.Text.Text, slide: number,
|
33
|
+
}|null> {
|
34
|
+
const functionStartLocation = functionScope.startLocation();
|
35
|
+
const functionEndLocation = functionScope.endLocation();
|
36
|
+
if (!functionStartLocation || !functionEndLocation) {
|
37
|
+
return null;
|
40
38
|
}
|
41
|
-
const script =
|
42
|
-
if (!script || !script.sourceMapURL || script !==
|
43
|
-
return
|
39
|
+
const script = functionStartLocation.script();
|
40
|
+
if (!script || !script.sourceMapURL || script !== functionEndLocation.script()) {
|
41
|
+
return null;
|
44
42
|
}
|
45
43
|
const {content} = await script.requestContent();
|
46
44
|
if (!content) {
|
47
|
-
return
|
45
|
+
return null;
|
48
46
|
}
|
49
47
|
|
50
48
|
const text = new TextUtils.Text.Text(content);
|
51
49
|
const scopeRange = new TextUtils.TextRange.TextRange(
|
52
|
-
|
50
|
+
functionStartLocation.lineNumber, functionStartLocation.columnNumber, functionEndLocation.lineNumber,
|
51
|
+
functionEndLocation.columnNumber);
|
53
52
|
const scopeText = text.extract(scopeRange);
|
54
53
|
const scopeStart = text.toSourceRange(scopeRange).offset;
|
55
54
|
const prefix = 'function fui';
|
56
|
-
const
|
57
|
-
|
58
|
-
|
59
|
-
const cursor = new TextUtils.TextCursor.TextCursor(text.lineEndings());
|
60
|
-
for (const id of identifiers) {
|
61
|
-
if (id.offset < prefix.length) {
|
62
|
-
continue;
|
63
|
-
}
|
64
|
-
const start = scopeStart + id.offset - prefix.length;
|
65
|
-
cursor.resetTo(start);
|
66
|
-
result.push(new Identifier(id.name, cursor.lineNumber(), cursor.columnNumber()));
|
55
|
+
const scopeTree = await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptScopeTree(prefix + scopeText);
|
56
|
+
if (!scopeTree) {
|
57
|
+
return null;
|
67
58
|
}
|
68
|
-
return
|
59
|
+
return {scopeTree, text, slide: scopeStart - prefix.length};
|
69
60
|
};
|
70
61
|
|
71
|
-
export const
|
72
|
-
|
73
|
-
|
62
|
+
export const scopeIdentifiers = async function(
|
63
|
+
functionScope: SDK.DebuggerModel.ScopeChainEntry|null, scope: SDK.DebuggerModel.ScopeChainEntry): Promise<{
|
64
|
+
freeVariables: Identifier[], boundVariables: Identifier[],
|
65
|
+
}|null> {
|
66
|
+
if (!functionScope) {
|
74
67
|
return null;
|
75
68
|
}
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
69
|
+
|
70
|
+
const startLocation = scope.startLocation();
|
71
|
+
const endLocation = scope.endLocation();
|
72
|
+
if (!startLocation || !endLocation) {
|
73
|
+
return null;
|
74
|
+
}
|
75
|
+
|
76
|
+
// Parse the function scope to get the scope tree.
|
77
|
+
const scopeTreeAndStart = await computeScopeTree(functionScope);
|
78
|
+
if (!scopeTreeAndStart) {
|
79
|
+
return null;
|
80
|
+
}
|
81
|
+
const {scopeTree, text, slide} = scopeTreeAndStart;
|
82
|
+
|
83
|
+
// Compute the offset within the scope tree coordinate space.
|
84
|
+
const scopeOffsets = {
|
85
|
+
start: text.offsetFromPosition(startLocation.lineNumber, startLocation.columnNumber) - slide,
|
86
|
+
end: text.offsetFromPosition(endLocation.lineNumber, endLocation.columnNumber) - slide,
|
87
|
+
};
|
88
|
+
|
89
|
+
if (!contains(scopeTree, scopeOffsets)) {
|
90
|
+
return null;
|
91
|
+
}
|
92
|
+
|
93
|
+
// Find the corresponding scope in the scope tree.
|
94
|
+
let containingScope = scopeTree;
|
95
|
+
const ancestorScopes = [];
|
96
|
+
while (true) {
|
97
|
+
let childFound = false;
|
98
|
+
for (const child of containingScope.children) {
|
99
|
+
if (contains(child, scopeOffsets)) {
|
100
|
+
// We found a nested containing scope, continue with search there.
|
101
|
+
ancestorScopes.push(containingScope);
|
102
|
+
containingScope = child;
|
103
|
+
childFound = true;
|
104
|
+
break;
|
105
|
+
}
|
106
|
+
// Sanity check: |scope| should not straddle any of the scopes in the tree. That is:
|
107
|
+
// Either |scope| is disjoint from |child| or |child| must be inside |scope|.
|
108
|
+
// (Or the |scope| is inside |child|, but that case is covered above.)
|
109
|
+
if (!disjoint(scopeOffsets, child) && !contains(scopeOffsets, child)) {
|
110
|
+
console.error('Wrong nesting of scopes');
|
111
|
+
return null;
|
112
|
+
}
|
113
|
+
}
|
114
|
+
if (!childFound) {
|
115
|
+
// We found the deepest scope in the tree that contains our scope chain entry.
|
116
|
+
break;
|
81
117
|
}
|
82
118
|
}
|
83
|
-
|
119
|
+
|
120
|
+
// Now we have containing scope. Collect all the scope variables.
|
121
|
+
const boundVariables = [];
|
122
|
+
const cursor = new TextUtils.TextCursor.TextCursor(text.lineEndings());
|
123
|
+
for (const variable of containingScope.variables) {
|
124
|
+
// Skip the fixed-kind variable (i.e., 'this' or 'arguments') if we only found their "definition"
|
125
|
+
// without any uses.
|
126
|
+
if (variable.kind === Formatter.FormatterWorkerPool.DefinitionKind.Fixed && variable.offsets.length <= 1) {
|
127
|
+
continue;
|
128
|
+
}
|
129
|
+
|
130
|
+
for (const offset of variable.offsets) {
|
131
|
+
const start = offset + slide;
|
132
|
+
cursor.resetTo(start);
|
133
|
+
boundVariables.push(new Identifier(variable.name, cursor.lineNumber(), cursor.columnNumber()));
|
134
|
+
}
|
135
|
+
}
|
136
|
+
|
137
|
+
// Compute free variables by collecting all the ancestor variables that are used in |containingScope|.
|
138
|
+
const freeVariables = [];
|
139
|
+
for (const ancestor of ancestorScopes) {
|
140
|
+
for (const ancestorVariable of ancestor.variables) {
|
141
|
+
for (const offset of ancestorVariable.offsets) {
|
142
|
+
if (offset >= containingScope.start && offset < containingScope.end) {
|
143
|
+
const start = offset + slide;
|
144
|
+
cursor.resetTo(start);
|
145
|
+
freeVariables.push(new Identifier(ancestorVariable.name, cursor.lineNumber(), cursor.columnNumber()));
|
146
|
+
}
|
147
|
+
}
|
148
|
+
}
|
149
|
+
}
|
150
|
+
return {boundVariables, freeVariables};
|
151
|
+
|
152
|
+
function contains(scope: {start: number, end: number}, candidate: {start: number, end: number}): boolean {
|
153
|
+
return (scope.start <= candidate.start) && (scope.end >= candidate.end);
|
154
|
+
}
|
155
|
+
function disjoint(scope: {start: number, end: number}, other: {start: number, end: number}): boolean {
|
156
|
+
return (scope.end <= other.start) || (other.end <= scope.start);
|
157
|
+
}
|
84
158
|
};
|
85
159
|
|
86
|
-
|
160
|
+
const resolveScope =
|
161
|
+
async(scope: SDK.DebuggerModel
|
162
|
+
.ScopeChainEntry): Promise<{variableMapping: Map<string, string>, thisMapping: string | null}> => {
|
87
163
|
let cachedScopeMap = scopeToCachedIdentifiersMap.get(scope);
|
88
164
|
const script = scope.callFrame().script;
|
89
165
|
const sourceMap = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance().sourceMapForScript(script);
|
@@ -93,33 +169,59 @@ export const resolveScope = async(scope: SDK.DebuggerModel.ScopeChainEntry): Pro
|
|
93
169
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
94
170
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration)
|
95
171
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
96
|
-
const identifiersPromise =
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
const
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
172
|
+
const identifiersPromise =
|
173
|
+
(async(): Promise<{variableMapping: Map<string, string>, thisMapping: string | null}> => {
|
174
|
+
const variableMapping = new Map<string, string>();
|
175
|
+
let thisMapping = null;
|
176
|
+
|
177
|
+
if (!sourceMap) {
|
178
|
+
return {variableMapping, thisMapping};
|
179
|
+
}
|
180
|
+
const textCache = new Map<string, TextUtils.Text.Text>();
|
181
|
+
// Extract as much as possible from SourceMap and resolve
|
182
|
+
// missing identifier names from SourceMap ranges.
|
183
|
+
const promises: Promise<void>[] = [];
|
184
|
+
|
185
|
+
const resolveEntry = (id: Identifier, handler: (sourceName: string) => void): void => {
|
186
|
+
const entry = sourceMap.findEntry(id.lineNumber, id.columnNumber);
|
187
|
+
if (entry && entry.name) {
|
188
|
+
handler(entry.name);
|
189
|
+
} else {
|
190
|
+
promises.push(resolveSourceName(script, sourceMap, id, textCache).then(sourceName => {
|
191
|
+
if (sourceName) {
|
192
|
+
handler(sourceName);
|
193
|
+
}
|
194
|
+
}));
|
195
|
+
}
|
196
|
+
};
|
197
|
+
|
198
|
+
const functionScope = findFunctionScope();
|
199
|
+
const parsedVariables = await scopeIdentifiers(functionScope, scope);
|
200
|
+
if (!parsedVariables) {
|
201
|
+
return {variableMapping, thisMapping};
|
202
|
+
}
|
203
|
+
for (const id of parsedVariables.boundVariables) {
|
204
|
+
resolveEntry(id, sourceName => {
|
205
|
+
// Let use ignore 'this' mappings - those are handled separately.
|
206
|
+
if (sourceName !== 'this') {
|
207
|
+
variableMapping.set(id.name, sourceName);
|
111
208
|
}
|
112
|
-
})
|
209
|
+
});
|
113
210
|
}
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
211
|
+
for (const id of parsedVariables.freeVariables) {
|
212
|
+
resolveEntry(id, sourceName => {
|
213
|
+
if (sourceName === 'this') {
|
214
|
+
thisMapping = id.name;
|
215
|
+
}
|
216
|
+
});
|
217
|
+
}
|
218
|
+
await Promise.all(promises).then(getScopeResolvedForTest());
|
219
|
+
return {variableMapping, thisMapping};
|
220
|
+
})();
|
221
|
+
cachedScopeMap = {sourceMap, mappingPromise: identifiersPromise};
|
222
|
+
scopeToCachedIdentifiersMap.set(scope, {sourceMap, mappingPromise: identifiersPromise});
|
121
223
|
}
|
122
|
-
return await cachedScopeMap.
|
224
|
+
return await cachedScopeMap.mappingPromise;
|
123
225
|
|
124
226
|
async function resolveSourceName(
|
125
227
|
script: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap, id: Identifier,
|
@@ -152,6 +254,39 @@ export const resolveScope = async(scope: SDK.DebuggerModel.ScopeChainEntry): Pro
|
|
152
254
|
const originalIdentifier = text.extract(sourceTextRange).trim();
|
153
255
|
return /[a-zA-Z0-9_$]+/.test(originalIdentifier) ? originalIdentifier : null;
|
154
256
|
}
|
257
|
+
|
258
|
+
function findFunctionScope(): SDK.DebuggerModel.ScopeChainEntry|null {
|
259
|
+
// First find the scope in the callframe's scope chain and then find the containing function scope (closure or local).
|
260
|
+
const scopeChain = scope.callFrame().scopeChain();
|
261
|
+
let scopeIndex = 0;
|
262
|
+
for (scopeIndex; scopeIndex < scopeChain.length; scopeIndex++) {
|
263
|
+
if (scopeChain[scopeIndex] === scope) {
|
264
|
+
break;
|
265
|
+
}
|
266
|
+
}
|
267
|
+
for (scopeIndex; scopeIndex < scopeChain.length; scopeIndex++) {
|
268
|
+
const kind = scopeChain[scopeIndex].type();
|
269
|
+
if (kind === Protocol.Debugger.ScopeType.Local || kind === Protocol.Debugger.ScopeType.Closure) {
|
270
|
+
break;
|
271
|
+
}
|
272
|
+
}
|
273
|
+
return scopeIndex === scopeChain.length ? null : scopeChain[scopeIndex];
|
274
|
+
}
|
275
|
+
};
|
276
|
+
|
277
|
+
export const resolveScopeChain =
|
278
|
+
async function(callFrame: SDK.DebuggerModel.CallFrame|null): Promise<SDK.DebuggerModel.ScopeChainEntry[]|null> {
|
279
|
+
if (!callFrame) {
|
280
|
+
return null;
|
281
|
+
}
|
282
|
+
const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
|
283
|
+
if (pluginManager) {
|
284
|
+
const scopeChain = await pluginManager.resolveScopeChain(callFrame);
|
285
|
+
if (scopeChain) {
|
286
|
+
return scopeChain;
|
287
|
+
}
|
288
|
+
}
|
289
|
+
return callFrame.scopeChain();
|
155
290
|
};
|
156
291
|
|
157
292
|
export const allVariablesInCallFrame = async(callFrame: SDK.DebuggerModel.CallFrame): Promise<Map<string, string>> => {
|
@@ -163,8 +298,8 @@ export const allVariablesInCallFrame = async(callFrame: SDK.DebuggerModel.CallFr
|
|
163
298
|
const scopeChain = callFrame.scopeChain();
|
164
299
|
const nameMappings = await Promise.all(scopeChain.map(resolveScope));
|
165
300
|
const reverseMapping = new Map<string, string>();
|
166
|
-
for (const
|
167
|
-
for (const [compiledName, originalName] of
|
301
|
+
for (const {variableMapping} of nameMappings) {
|
302
|
+
for (const [compiledName, originalName] of variableMapping) {
|
168
303
|
if (originalName && !reverseMapping.has(originalName)) {
|
169
304
|
reverseMapping.set(originalName, compiledName);
|
170
305
|
}
|
@@ -234,15 +369,13 @@ export const resolveThisObject =
|
|
234
369
|
return callFrame.thisObject();
|
235
370
|
}
|
236
371
|
|
237
|
-
const
|
238
|
-
|
239
|
-
if (!thisMappings || thisMappings.size !== 1) {
|
372
|
+
const {thisMapping} = await resolveScope(scopeChain[0]);
|
373
|
+
if (!thisMapping) {
|
240
374
|
return callFrame.thisObject();
|
241
375
|
}
|
242
376
|
|
243
|
-
const [expression] = thisMappings.values();
|
244
377
|
const result = await callFrame.evaluate(({
|
245
|
-
expression,
|
378
|
+
expression: thisMapping,
|
246
379
|
objectGroup: 'backtrace',
|
247
380
|
includeCommandLineAPI: false,
|
248
381
|
silent: true,
|
@@ -322,7 +455,7 @@ export class RemoteObject extends SDK.RemoteObject.RemoteObject {
|
|
322
455
|
async getAllProperties(accessorPropertiesOnly: boolean, generatePreview: boolean):
|
323
456
|
Promise<SDK.RemoteObject.GetPropertiesResult> {
|
324
457
|
const allProperties = await this.object.getAllProperties(accessorPropertiesOnly, generatePreview);
|
325
|
-
const
|
458
|
+
const {variableMapping} = await resolveScope(this.scope);
|
326
459
|
|
327
460
|
const properties = allProperties.properties;
|
328
461
|
const internalProperties = allProperties.internalProperties;
|
@@ -330,7 +463,7 @@ export class RemoteObject extends SDK.RemoteObject.RemoteObject {
|
|
330
463
|
if (properties) {
|
331
464
|
for (let i = 0; i < properties.length; ++i) {
|
332
465
|
const property = properties[i];
|
333
|
-
const name =
|
466
|
+
const name = variableMapping.get(property.name) || properties[i].name;
|
334
467
|
if (!property.value) {
|
335
468
|
continue;
|
336
469
|
}
|
@@ -343,7 +476,7 @@ export class RemoteObject extends SDK.RemoteObject.RemoteObject {
|
|
343
476
|
}
|
344
477
|
|
345
478
|
async setPropertyValue(argumentName: string|Protocol.Runtime.CallArgument, value: string): Promise<string|undefined> {
|
346
|
-
const
|
479
|
+
const {variableMapping} = await resolveScope(this.scope);
|
347
480
|
|
348
481
|
let name;
|
349
482
|
if (typeof argumentName === 'string') {
|
@@ -353,8 +486,8 @@ export class RemoteObject extends SDK.RemoteObject.RemoteObject {
|
|
353
486
|
}
|
354
487
|
|
355
488
|
let actualName: string = name;
|
356
|
-
for (const compiledName of
|
357
|
-
if (
|
489
|
+
for (const compiledName of variableMapping.keys()) {
|
490
|
+
if (variableMapping.get(compiledName) === name) {
|
358
491
|
actualName = compiledName;
|
359
492
|
break;
|
360
493
|
}
|
@@ -127,6 +127,13 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
127
127
|
return this.urlInternal;
|
128
128
|
}
|
129
129
|
|
130
|
+
// Identifier used for deduplicating scripts that are considered by the
|
131
|
+
// DevTools UI to be the same script. For now this is just the url but this
|
132
|
+
// is likely to change in the future.
|
133
|
+
canononicalScriptId(): string {
|
134
|
+
return this.urlInternal;
|
135
|
+
}
|
136
|
+
|
130
137
|
parentURL(): Platform.DevToolsPath.UrlString {
|
131
138
|
return this.parentURLInternal;
|
132
139
|
}
|
@@ -96,11 +96,11 @@
|
|
96
96
|
}
|
97
97
|
|
98
98
|
.ax-breadcrumbs .ax-node:not(.inspected):hover {
|
99
|
-
background-color: var(--color-background-elevation-
|
99
|
+
background-color: var(--color-background-elevation-1);
|
100
100
|
}
|
101
101
|
|
102
102
|
.ax-breadcrumbs .ax-node:not(.inspected):focus {
|
103
|
-
background-color: var(--color-background-elevation-
|
103
|
+
background-color: var(--color-background-elevation-2);
|
104
104
|
}
|
105
105
|
|
106
106
|
.ax-breadcrumbs .ax-node.inspected:focus * {
|
@@ -94,6 +94,10 @@ const UIStrings = {
|
|
94
94
|
* @description Link Text about explanation of back/forward cache
|
95
95
|
*/
|
96
96
|
learnMore: 'Learn more: back/forward cache eligibility',
|
97
|
+
/**
|
98
|
+
* @description Link Text about unload handler
|
99
|
+
*/
|
100
|
+
neverUseUnload: 'Learn more: Never use unload handler',
|
97
101
|
/**
|
98
102
|
* @description Explanation for 'pending support' items which prevent the page from being eligible
|
99
103
|
* for back/forward cache.
|
@@ -536,6 +540,17 @@ export class BackForwardCacheView extends HTMLElement {
|
|
536
540
|
`;
|
537
541
|
}
|
538
542
|
|
543
|
+
#maybeRenderDeepLinkToUnload(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation): LitHtml.LitTemplate {
|
544
|
+
if (explanation.reason === Protocol.Page.BackForwardCacheNotRestoredReason.UnloadHandlerExistsInMainFrame ||
|
545
|
+
explanation.reason === Protocol.Page.BackForwardCacheNotRestoredReason.UnloadHandlerExistsInSubFrame) {
|
546
|
+
return LitHtml.html`
|
547
|
+
<x-link href="https://web.dev/bfcache/#never-use-the-unload-event" class="link">
|
548
|
+
${i18nString(UIStrings.neverUseUnload)}
|
549
|
+
</x-link>`;
|
550
|
+
}
|
551
|
+
return LitHtml.nothing;
|
552
|
+
}
|
553
|
+
|
539
554
|
#renderReason(explanation: Protocol.Page.BackForwardCacheNotRestoredExplanation, frames: string[]|undefined):
|
540
555
|
LitHtml.TemplateResult {
|
541
556
|
// clang-format off
|
@@ -554,6 +569,7 @@ export class BackForwardCacheView extends HTMLElement {
|
|
554
569
|
</div>
|
555
570
|
<div>
|
556
571
|
${NotRestoredReasonDescription[explanation.reason].name()}
|
572
|
+
${this.#maybeRenderDeepLinkToUnload(explanation)}
|
557
573
|
${this.#maybeRenderReasonContext(explanation)}
|
558
574
|
</div>` :
|
559
575
|
LitHtml.nothing}
|
@@ -7,7 +7,7 @@ import type * as Common from '../../core/common/common.js';
|
|
7
7
|
import * as i18n from '../../core/i18n/i18n.js';
|
8
8
|
import * as UI from '../../ui/legacy/legacy.js';
|
9
9
|
|
10
|
-
import type {LighthouseController} from './LighthouseController.js';
|
10
|
+
import type {LighthouseController, Preset} from './LighthouseController.js';
|
11
11
|
import {Events, Presets, RuntimeSettings} from './LighthouseController.js';
|
12
12
|
import {RadioSetting} from './RadioSetting.js';
|
13
13
|
|
@@ -42,10 +42,11 @@ const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseStartView.
|
|
42
42
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
43
43
|
export class StartView extends UI.Widget.Widget {
|
44
44
|
protected controller: LighthouseController;
|
45
|
-
|
45
|
+
protected readonly settingsToolbarInternal: UI.Toolbar.Toolbar;
|
46
46
|
protected startButton!: HTMLButtonElement;
|
47
47
|
protected helpText?: Element;
|
48
48
|
protected warningText?: Element;
|
49
|
+
protected checkboxes: Array<{preset: Preset, checkbox: UI.Toolbar.ToolbarCheckbox}> = [];
|
49
50
|
private shouldConfirm?: boolean;
|
50
51
|
|
51
52
|
constructor(controller: LighthouseController) {
|
@@ -84,7 +85,7 @@ export class StartView extends UI.Widget.Widget {
|
|
84
85
|
UI.ARIAUtils.setAccessibleName(control.element, label);
|
85
86
|
}
|
86
87
|
|
87
|
-
|
88
|
+
protected populateRuntimeSettingAsToolbarCheckbox(settingName: string, toolbar: UI.Toolbar.Toolbar): void {
|
88
89
|
const runtimeSetting = RuntimeSettings.find(item => item.setting.name === settingName);
|
89
90
|
if (!runtimeSetting || !runtimeSetting.title) {
|
90
91
|
throw new Error(`${settingName} is not a setting with a title`);
|
@@ -109,15 +110,16 @@ export class StartView extends UI.Widget.Widget {
|
|
109
110
|
|
110
111
|
// Populate the categories
|
111
112
|
const categoryFormElements = fragment.$('categories-form-elements') as HTMLElement;
|
112
|
-
categoryFormElements.textContent = '';
|
113
113
|
const pluginFormElements = fragment.$('plugins-form-elements') as HTMLElement;
|
114
|
-
|
114
|
+
|
115
|
+
this.checkboxes = [];
|
115
116
|
for (const preset of Presets) {
|
116
117
|
const formElements = preset.plugin ? pluginFormElements : categoryFormElements;
|
117
118
|
preset.setting.setTitle(preset.title());
|
118
119
|
const checkbox = new UI.Toolbar.ToolbarSettingCheckbox(preset.setting, preset.description());
|
119
120
|
const row = formElements.createChild('div', 'vbox lighthouse-launcher-row');
|
120
121
|
row.appendChild(checkbox.element);
|
122
|
+
this.checkboxes.push({preset, checkbox});
|
121
123
|
if (mode && !preset.supportedModes.includes(mode)) {
|
122
124
|
checkbox.setEnabled(false);
|
123
125
|
checkbox.setIndeterminate(true);
|
@@ -44,60 +44,22 @@ const str_ = i18n.i18n.registerUIStrings('panels/lighthouse/LighthouseStartViewF
|
|
44
44
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
45
45
|
|
46
46
|
export class StartViewFR extends StartView {
|
47
|
-
|
48
|
-
super.render();
|
49
|
-
this.refresh();
|
50
|
-
}
|
51
|
-
|
52
|
-
private createStartButton(mode: string): HTMLButtonElement {
|
53
|
-
let buttonLabel: Platform.UIString.LocalizedString;
|
54
|
-
let callback: () => void;
|
47
|
+
changeFormMode?: (mode: string) => void;
|
55
48
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
Events.RequestLighthouseTimespanStart,
|
61
|
-
/* keyboardInitiated */ this.startButton.matches(':focus-visible'),
|
62
|
-
);
|
63
|
-
};
|
64
|
-
} else if (mode === 'snapshot') {
|
65
|
-
buttonLabel = i18nString(UIStrings.analyzeSnapshot);
|
66
|
-
callback = (): void => {
|
67
|
-
this.controller.dispatchEventToListeners(
|
68
|
-
Events.RequestLighthouseStart,
|
69
|
-
/* keyboardInitiated */ this.startButton.matches(':focus-visible'),
|
70
|
-
);
|
71
|
-
};
|
72
|
-
} else {
|
73
|
-
buttonLabel = i18nString(UIStrings.analyzeNavigation);
|
74
|
-
callback = (): void => {
|
75
|
-
this.controller.dispatchEventToListeners(
|
76
|
-
Events.RequestLighthouseStart,
|
77
|
-
/* keyboardInitiated */ this.startButton.matches(':focus-visible'),
|
78
|
-
);
|
79
|
-
};
|
80
|
-
}
|
81
|
-
|
82
|
-
return UI.UIUtils.createTextButton(
|
83
|
-
buttonLabel,
|
84
|
-
callback,
|
85
|
-
/* className */ '',
|
86
|
-
/* primary */ true,
|
87
|
-
);
|
88
|
-
}
|
49
|
+
protected render(): void {
|
50
|
+
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.legacy_navigation', this.settingsToolbarInternal);
|
51
|
+
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.clear_storage', this.settingsToolbarInternal);
|
52
|
+
this.populateRuntimeSettingAsToolbarCheckbox('lighthouse.throttling', this.settingsToolbarInternal);
|
89
53
|
|
90
|
-
refresh(): void {
|
91
54
|
const {mode} = this.controller.getFlags();
|
92
|
-
|
93
|
-
this.startButton = this.createStartButton(mode);
|
55
|
+
this.populateStartButton(mode);
|
94
56
|
|
95
57
|
const fragment = UI.Fragment.Fragment.build`
|
96
58
|
<form class="lighthouse-start-view-fr">
|
97
59
|
<header class="hbox">
|
98
60
|
<div class="lighthouse-logo"></div>
|
99
61
|
<div class="lighthouse-title">${i18nString(UIStrings.generateLighthouseReport)}</div>
|
100
|
-
<div class="lighthouse-start-button-container">${this.startButton}</div>
|
62
|
+
<div class="lighthouse-start-button-container" $="start-button-container">${this.startButton}</div>
|
101
63
|
</header>
|
102
64
|
<div $="help-text" class="lighthouse-help-text hidden"></div>
|
103
65
|
<div class="lighthouse-options hbox">
|
@@ -127,16 +89,75 @@ export class StartViewFR extends StartView {
|
|
127
89
|
this.helpText = fragment.$('help-text');
|
128
90
|
this.warningText = fragment.$('warning-text');
|
129
91
|
|
130
|
-
// The previous radios are removed later and don't exist on the new fragment yet.
|
131
|
-
this.populateFormControls(fragment, mode);
|
132
|
-
|
133
|
-
// Populate the Lighthouse mode
|
134
92
|
const modeFormElements = fragment.$('mode-form-elements');
|
135
93
|
this.populateRuntimeSettingAsRadio('lighthouse.mode', i18nString(UIStrings.mode), modeFormElements);
|
136
94
|
|
95
|
+
// The previous radios are removed later and don't exist on the new fragment yet.
|
96
|
+
this.populateFormControls(fragment, mode);
|
97
|
+
|
137
98
|
this.contentElement.textContent = '';
|
138
99
|
this.contentElement.append(fragment.element());
|
139
100
|
|
101
|
+
this.refresh();
|
102
|
+
}
|
103
|
+
|
104
|
+
private populateStartButton(mode: string): void {
|
105
|
+
let buttonLabel: Platform.UIString.LocalizedString;
|
106
|
+
let callback: () => void;
|
107
|
+
|
108
|
+
if (mode === 'timespan') {
|
109
|
+
buttonLabel = i18nString(UIStrings.startTimespan);
|
110
|
+
callback = (): void => {
|
111
|
+
this.controller.dispatchEventToListeners(
|
112
|
+
Events.RequestLighthouseTimespanStart,
|
113
|
+
/* keyboardInitiated */ this.startButton.matches(':focus-visible'),
|
114
|
+
);
|
115
|
+
};
|
116
|
+
} else if (mode === 'snapshot') {
|
117
|
+
buttonLabel = i18nString(UIStrings.analyzeSnapshot);
|
118
|
+
callback = (): void => {
|
119
|
+
this.controller.dispatchEventToListeners(
|
120
|
+
Events.RequestLighthouseStart,
|
121
|
+
/* keyboardInitiated */ this.startButton.matches(':focus-visible'),
|
122
|
+
);
|
123
|
+
};
|
124
|
+
} else {
|
125
|
+
buttonLabel = i18nString(UIStrings.analyzeNavigation);
|
126
|
+
callback = (): void => {
|
127
|
+
this.controller.dispatchEventToListeners(
|
128
|
+
Events.RequestLighthouseStart,
|
129
|
+
/* keyboardInitiated */ this.startButton.matches(':focus-visible'),
|
130
|
+
);
|
131
|
+
};
|
132
|
+
}
|
133
|
+
|
134
|
+
const startButtonContainer = this.contentElement.querySelector('.lighthouse-start-button-container');
|
135
|
+
if (startButtonContainer) {
|
136
|
+
startButtonContainer.textContent = '';
|
137
|
+
this.startButton = UI.UIUtils.createTextButton(
|
138
|
+
buttonLabel,
|
139
|
+
callback,
|
140
|
+
/* className */ '',
|
141
|
+
/* primary */ true,
|
142
|
+
);
|
143
|
+
startButtonContainer.append(this.startButton);
|
144
|
+
}
|
145
|
+
}
|
146
|
+
|
147
|
+
refresh(): void {
|
148
|
+
const {mode} = this.controller.getFlags();
|
149
|
+
this.populateStartButton(mode);
|
150
|
+
|
151
|
+
for (const {checkbox, preset} of this.checkboxes) {
|
152
|
+
if (preset.supportedModes.includes(mode)) {
|
153
|
+
checkbox.setEnabled(true);
|
154
|
+
checkbox.setIndeterminate(false);
|
155
|
+
} else {
|
156
|
+
checkbox.setEnabled(false);
|
157
|
+
checkbox.setIndeterminate(true);
|
158
|
+
}
|
159
|
+
}
|
160
|
+
|
140
161
|
// Ensure the correct layout is used after refresh.
|
141
162
|
this.onResize();
|
142
163
|
}
|