chrome-devtools-frontend 1.0.974080 → 1.0.975442
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 +5 -0
- package/config/gni/devtools_image_files.gni +1 -0
- package/front_end/Images/src/ic_layers_16x16.svg +11 -0
- package/front_end/core/host/UserMetrics.ts +2 -1
- package/front_end/core/i18n/locales/af.json +74 -29
- package/front_end/core/i18n/locales/am.json +74 -29
- package/front_end/core/i18n/locales/ar.json +74 -29
- package/front_end/core/i18n/locales/as.json +74 -29
- package/front_end/core/i18n/locales/az.json +74 -29
- package/front_end/core/i18n/locales/be.json +74 -29
- package/front_end/core/i18n/locales/bg.json +74 -29
- package/front_end/core/i18n/locales/bn.json +74 -29
- package/front_end/core/i18n/locales/bs.json +75 -30
- package/front_end/core/i18n/locales/ca.json +74 -29
- package/front_end/core/i18n/locales/cs.json +74 -29
- package/front_end/core/i18n/locales/cy.json +74 -29
- package/front_end/core/i18n/locales/da.json +74 -29
- package/front_end/core/i18n/locales/de.json +76 -31
- package/front_end/core/i18n/locales/el.json +76 -31
- package/front_end/core/i18n/locales/en-GB.json +74 -29
- package/front_end/core/i18n/locales/en-US.json +13 -1
- package/front_end/core/i18n/locales/en-XL.json +13 -1
- package/front_end/core/i18n/locales/es-419.json +74 -29
- package/front_end/core/i18n/locales/es.json +74 -29
- package/front_end/core/i18n/locales/et.json +74 -29
- package/front_end/core/i18n/locales/eu.json +76 -31
- package/front_end/core/i18n/locales/fa.json +74 -29
- package/front_end/core/i18n/locales/fi.json +75 -30
- package/front_end/core/i18n/locales/fil.json +74 -29
- package/front_end/core/i18n/locales/fr-CA.json +74 -29
- package/front_end/core/i18n/locales/fr.json +74 -29
- package/front_end/core/i18n/locales/gl.json +81 -36
- package/front_end/core/i18n/locales/gu.json +74 -29
- package/front_end/core/i18n/locales/he.json +74 -29
- package/front_end/core/i18n/locales/hi.json +74 -29
- package/front_end/core/i18n/locales/hr.json +74 -29
- package/front_end/core/i18n/locales/hu.json +74 -29
- package/front_end/core/i18n/locales/hy.json +74 -29
- package/front_end/core/i18n/locales/id.json +74 -29
- package/front_end/core/i18n/locales/is.json +74 -29
- package/front_end/core/i18n/locales/it.json +74 -29
- package/front_end/core/i18n/locales/ja.json +74 -29
- package/front_end/core/i18n/locales/ka.json +74 -29
- package/front_end/core/i18n/locales/kk.json +74 -29
- package/front_end/core/i18n/locales/km.json +74 -29
- package/front_end/core/i18n/locales/kn.json +74 -29
- package/front_end/core/i18n/locales/ko.json +74 -29
- package/front_end/core/i18n/locales/ky.json +74 -29
- package/front_end/core/i18n/locales/lo.json +74 -29
- package/front_end/core/i18n/locales/lt.json +74 -29
- package/front_end/core/i18n/locales/lv.json +74 -29
- package/front_end/core/i18n/locales/mk.json +75 -30
- package/front_end/core/i18n/locales/ml.json +74 -29
- package/front_end/core/i18n/locales/mn.json +74 -29
- package/front_end/core/i18n/locales/mr.json +74 -29
- package/front_end/core/i18n/locales/ms.json +74 -29
- package/front_end/core/i18n/locales/my.json +74 -29
- package/front_end/core/i18n/locales/ne.json +74 -29
- package/front_end/core/i18n/locales/nl.json +74 -29
- package/front_end/core/i18n/locales/no.json +74 -29
- package/front_end/core/i18n/locales/or.json +75 -30
- package/front_end/core/i18n/locales/pa.json +74 -29
- package/front_end/core/i18n/locales/pl.json +74 -29
- package/front_end/core/i18n/locales/pt-PT.json +74 -29
- package/front_end/core/i18n/locales/pt.json +74 -29
- package/front_end/core/i18n/locales/ro.json +74 -29
- package/front_end/core/i18n/locales/ru.json +78 -33
- package/front_end/core/i18n/locales/si.json +74 -29
- package/front_end/core/i18n/locales/sk.json +74 -29
- package/front_end/core/i18n/locales/sl.json +74 -29
- package/front_end/core/i18n/locales/sq.json +74 -29
- package/front_end/core/i18n/locales/sr-Latn.json +74 -29
- package/front_end/core/i18n/locales/sr.json +74 -29
- package/front_end/core/i18n/locales/sv.json +74 -29
- package/front_end/core/i18n/locales/sw.json +74 -29
- package/front_end/core/i18n/locales/ta.json +74 -29
- package/front_end/core/i18n/locales/te.json +75 -30
- package/front_end/core/i18n/locales/th.json +74 -29
- package/front_end/core/i18n/locales/tr.json +74 -29
- package/front_end/core/i18n/locales/uk.json +74 -29
- package/front_end/core/i18n/locales/ur.json +74 -29
- package/front_end/core/i18n/locales/uz.json +74 -29
- package/front_end/core/i18n/locales/vi.json +74 -29
- package/front_end/core/i18n/locales/zh-HK.json +74 -29
- package/front_end/core/i18n/locales/zh-TW.json +74 -29
- package/front_end/core/i18n/locales/zh.json +74 -29
- package/front_end/core/i18n/locales/zu.json +74 -29
- package/front_end/core/root/Runtime.ts +1 -0
- package/front_end/core/sdk/CSSLayer.ts +30 -0
- package/front_end/core/sdk/CSSModel.ts +5 -0
- package/front_end/core/sdk/CSSRule.ts +3 -0
- package/front_end/core/sdk/DOMModel.ts +3 -3
- package/front_end/core/sdk/sdk.ts +2 -0
- package/front_end/entrypoints/formatter_worker/FormatterWorker.ts +3 -0
- package/front_end/entrypoints/formatter_worker/Substitute.ts +536 -0
- package/front_end/entrypoints/formatter_worker/formatter_worker.ts +2 -0
- package/front_end/entrypoints/main/MainImpl.ts +5 -0
- package/front_end/generated/InspectorBackendCommands.js +11 -9
- package/front_end/generated/protocol-mapping.d.ts +13 -0
- package/front_end/generated/protocol-proxy-api.d.ts +15 -0
- package/front_end/generated/protocol.ts +71 -9
- package/front_end/models/issues_manager/IssuesManager.ts +6 -0
- package/front_end/models/javascript_metadata/NativeFunctions.js +87 -19
- package/front_end/panels/console/ConsolePrompt.ts +9 -4
- package/front_end/panels/elements/LayersWidget.ts +167 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +71 -3
- package/front_end/panels/elements/elements-meta.ts +15 -2
- package/front_end/panels/elements/elements.ts +2 -0
- package/front_end/panels/elements/layersWidget.css +28 -0
- package/front_end/panels/elements/stylesSidebarPane.css +4 -0
- package/front_end/panels/lighthouse/LighthouseController.ts +3 -3
- package/front_end/panels/timeline/timelineStatusDialog.css +1 -0
- package/front_end/third_party/acorn/estree-legacy.d.ts +1 -0
- package/front_end/ui/components/text_editor/cursor_tooltip.ts +2 -1
- package/front_end/ui/components/text_editor/javascript.ts +10 -3
- package/front_end/ui/legacy/Toolbar.ts +25 -13
- package/package.json +1 -1
- package/scripts/build/assert_grd.py +1 -1
- package/scripts/build/assert_third_party_readmes.py +1 -1
- package/scripts/build/build_inspector_overlay.py +1 -1
- package/scripts/build/code_generator_frontend.py +1 -1
- package/scripts/build/efficiently_recompile.py +1 -1
- package/scripts/build/generate_aria.py +2 -0
- package/scripts/build/generate_devtools_grd.py +1 -5
- package/scripts/build/generate_supported_css.py +6 -5
@@ -0,0 +1,30 @@
|
|
1
|
+
// Copyright 2022 The Chromium Authors. All rights reserved.
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
3
|
+
// found in the LICENSE file.
|
4
|
+
|
5
|
+
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
6
|
+
import type * as Protocol from '../../generated/protocol.js';
|
7
|
+
|
8
|
+
import type {CSSModel} from './CSSModel.js';
|
9
|
+
import {CSSQuery} from './CSSQuery.js';
|
10
|
+
|
11
|
+
export class CSSLayer extends CSSQuery {
|
12
|
+
static parseLayerPayload(cssModel: CSSModel, payload: Protocol.CSS.CSSLayer[]): CSSLayer[] {
|
13
|
+
return payload.map(supports => new CSSLayer(cssModel, supports));
|
14
|
+
}
|
15
|
+
|
16
|
+
constructor(cssModel: CSSModel, payload: Protocol.CSS.CSSLayer) {
|
17
|
+
super(cssModel);
|
18
|
+
this.reinitialize(payload);
|
19
|
+
}
|
20
|
+
|
21
|
+
reinitialize(payload: Protocol.CSS.CSSLayer): void {
|
22
|
+
this.text = payload.text;
|
23
|
+
this.range = payload.range ? TextUtils.TextRange.TextRange.fromObject(payload.range) : null;
|
24
|
+
this.styleSheetId = payload.styleSheetId;
|
25
|
+
}
|
26
|
+
|
27
|
+
active(): boolean {
|
28
|
+
return true;
|
29
|
+
}
|
30
|
+
}
|
@@ -266,6 +266,11 @@ export class CSSModel extends SDKModel<EventTypes> {
|
|
266
266
|
return medias ? CSSMedia.parseMediaArrayPayload(this, medias) : [];
|
267
267
|
}
|
268
268
|
|
269
|
+
async rootLayerPromise(nodeId: Protocol.DOM.NodeId): Promise<Protocol.CSS.CSSLayerData> {
|
270
|
+
const {rootLayer} = await this.agent.invoke_getLayersForNode({nodeId});
|
271
|
+
return rootLayer;
|
272
|
+
}
|
273
|
+
|
269
274
|
isEnabled(): boolean {
|
270
275
|
return this.#isEnabled;
|
271
276
|
}
|
@@ -6,6 +6,7 @@ import * as Protocol from '../../generated/protocol.js';
|
|
6
6
|
import * as TextUtils from '../../models/text_utils/text_utils.js';
|
7
7
|
|
8
8
|
import {CSSContainerQuery} from './CSSContainerQuery.js';
|
9
|
+
import {CSSLayer} from './CSSLayer.js';
|
9
10
|
import {CSSMedia} from './CSSMedia.js';
|
10
11
|
import {CSSSupports} from './CSSSupports.js';
|
11
12
|
|
@@ -101,6 +102,7 @@ export class CSSStyleRule extends CSSRule {
|
|
101
102
|
media: CSSMedia[];
|
102
103
|
containerQueries: CSSContainerQuery[];
|
103
104
|
supports: CSSSupports[];
|
105
|
+
layers: CSSLayer[];
|
104
106
|
wasUsed: boolean;
|
105
107
|
constructor(cssModel: CSSModel, payload: Protocol.CSS.CSSRule, wasUsed?: boolean) {
|
106
108
|
// TODO(crbug.com/1011811): Replace with spread operator or better types once Closure is gone.
|
@@ -111,6 +113,7 @@ export class CSSStyleRule extends CSSRule {
|
|
111
113
|
CSSContainerQuery.parseContainerQueriesPayload(cssModel, payload.containerQueries) :
|
112
114
|
[];
|
113
115
|
this.supports = payload.supports ? CSSSupports.parseSupportsPayload(cssModel, payload.supports) : [];
|
116
|
+
this.layers = payload.layers ? CSSLayer.parseLayerPayload(cssModel, payload.layers) : [];
|
114
117
|
this.wasUsed = wasUsed || false;
|
115
118
|
}
|
116
119
|
|
@@ -1243,10 +1243,10 @@ export class DOMModel extends SDKModel<EventTypes> {
|
|
1243
1243
|
|
1244
1244
|
documentUpdated(): void {
|
1245
1245
|
// If we have this.#pendingDocumentRequestPromise in flight,
|
1246
|
-
//
|
1247
|
-
const documentWasRequested = this.#
|
1246
|
+
// it will contain most recent result.
|
1247
|
+
const documentWasRequested = this.#pendingDocumentRequestPromise;
|
1248
1248
|
this.setDocument(null);
|
1249
|
-
if (this.parentModel() && documentWasRequested) {
|
1249
|
+
if (this.parentModel() && !documentWasRequested) {
|
1250
1250
|
void this.requestDocument();
|
1251
1251
|
}
|
1252
1252
|
}
|
@@ -24,6 +24,7 @@ import * as CPUProfilerModel from './CPUProfilerModel.js';
|
|
24
24
|
import * as CPUThrottlingManager from './CPUThrottlingManager.js';
|
25
25
|
import * as CSSContainerQuery from './CSSContainerQuery.js';
|
26
26
|
import * as CSSFontFace from './CSSFontFace.js';
|
27
|
+
import * as CSSLayer from './CSSLayer.js';
|
27
28
|
import * as CSSMatchedStyles from './CSSMatchedStyles.js';
|
28
29
|
import * as CSSMedia from './CSSMedia.js';
|
29
30
|
import * as CSSMetadata from './CSSMetadata.js';
|
@@ -92,6 +93,7 @@ export {
|
|
92
93
|
CPUThrottlingManager,
|
93
94
|
CSSContainerQuery,
|
94
95
|
CSSFontFace,
|
96
|
+
CSSLayer,
|
95
97
|
CSSMatchedStyles,
|
96
98
|
CSSMedia,
|
97
99
|
CSSMetadata,
|
@@ -42,6 +42,7 @@ import {HTMLFormatter} from './HTMLFormatter.js';
|
|
42
42
|
import {IdentityFormatter} from './IdentityFormatter.js';
|
43
43
|
import {JavaScriptFormatter} from './JavaScriptFormatter.js';
|
44
44
|
import {JSONFormatter} from './JSONFormatter.js';
|
45
|
+
import {computeSubstitution, applySubstitution} from './Substitute.js';
|
45
46
|
|
46
47
|
export interface Chunk {
|
47
48
|
// TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
|
@@ -304,3 +305,5 @@ export function argumentsList(content: string): string[] {
|
|
304
305
|
console.error = (): undefined => undefined;
|
305
306
|
}
|
306
307
|
})();
|
308
|
+
|
309
|
+
export {applySubstitution, computeSubstitution};
|
@@ -0,0 +1,536 @@
|
|
1
|
+
// Copyright 2022 The Chromium Authors. All rights reserved.
|
2
|
+
// Use of this source code is governed by a BSD-style license that can be
|
3
|
+
// found in the LICENSE file.
|
4
|
+
|
5
|
+
import * as Acorn from '../../third_party/acorn/acorn.js';
|
6
|
+
|
7
|
+
import {ECMA_VERSION} from './AcornTokenizer.js';
|
8
|
+
|
9
|
+
export interface Replacement {
|
10
|
+
from: string;
|
11
|
+
to: string;
|
12
|
+
offset: number;
|
13
|
+
isShorthandAssignmentProperty: boolean;
|
14
|
+
}
|
15
|
+
|
16
|
+
// Given an |expression| and a mapping from names to new names, the |computeSubstitution|
|
17
|
+
// function returns a list of replacements sorted by the offset. The function throws if
|
18
|
+
// it cannot parse the expression or the substitution is impossible to perform (for example
|
19
|
+
// if the substitution target is 'this' within a function, it would become bound there).
|
20
|
+
export function computeSubstitution(expression: string, nameMap: Map<string, string>): Replacement[] {
|
21
|
+
// Parse the expression and find variables and scopes.
|
22
|
+
const root = Acorn.parse(expression, {ecmaVersion: ECMA_VERSION, allowAwaitOutsideFunction: true, ranges: false}) as
|
23
|
+
Acorn.ESTree.Node;
|
24
|
+
const scopeVariables = new ScopeVariableAnalysis();
|
25
|
+
scopeVariables.processNode(root);
|
26
|
+
const freeVariables = scopeVariables.getFreeVariables();
|
27
|
+
const result: Replacement[] = [];
|
28
|
+
|
29
|
+
// Prepare the machinery for generating fresh names (to avoid variable captures).
|
30
|
+
const allNames = scopeVariables.getAllNames();
|
31
|
+
for (const rename of nameMap.values()) {
|
32
|
+
allNames.add(rename);
|
33
|
+
}
|
34
|
+
function getNewName(base: string): string {
|
35
|
+
let i = 1;
|
36
|
+
while (allNames.has(`${base}_${i}`)) {
|
37
|
+
i++;
|
38
|
+
}
|
39
|
+
const newName = `${base}_${i}`;
|
40
|
+
allNames.add(newName);
|
41
|
+
return newName;
|
42
|
+
}
|
43
|
+
|
44
|
+
// Perform the substitutions.
|
45
|
+
for (const [name, rename] of nameMap.entries()) {
|
46
|
+
const defUse = freeVariables.get(name);
|
47
|
+
if (!defUse) {
|
48
|
+
continue;
|
49
|
+
}
|
50
|
+
|
51
|
+
const binders = [];
|
52
|
+
for (const use of defUse) {
|
53
|
+
result.push({
|
54
|
+
from: name,
|
55
|
+
to: rename,
|
56
|
+
offset: use.offset,
|
57
|
+
isShorthandAssignmentProperty: use.isShorthandAssignmentProperty,
|
58
|
+
});
|
59
|
+
binders.push(...use.scope.findBinders(rename));
|
60
|
+
}
|
61
|
+
// If there is a capturing binder, rename the bound variable.
|
62
|
+
for (const binder of binders) {
|
63
|
+
if (binder.definitionKind === DefinitionKind.Fixed) {
|
64
|
+
// If the identifier is bound to a fixed name, such as 'this',
|
65
|
+
// then refuse to do the substitution.
|
66
|
+
throw new Error(`Cannot avoid capture of '${rename}'`);
|
67
|
+
}
|
68
|
+
const newName = getNewName(rename);
|
69
|
+
for (const use of binder.uses) {
|
70
|
+
result.push({
|
71
|
+
from: rename,
|
72
|
+
to: newName,
|
73
|
+
offset: use.offset,
|
74
|
+
isShorthandAssignmentProperty: use.isShorthandAssignmentProperty,
|
75
|
+
});
|
76
|
+
}
|
77
|
+
}
|
78
|
+
}
|
79
|
+
result.sort((l, r) => l.offset - r.offset);
|
80
|
+
return result;
|
81
|
+
}
|
82
|
+
|
83
|
+
export function applySubstitution(expression: string, replacements: Replacement[]): string {
|
84
|
+
const accumulator = [];
|
85
|
+
let last = 0;
|
86
|
+
for (const r of replacements) {
|
87
|
+
accumulator.push(expression.slice(last, r.offset));
|
88
|
+
let replacement = r.to;
|
89
|
+
if (r.isShorthandAssignmentProperty) {
|
90
|
+
// Let us expand the shorthand to full assignment.
|
91
|
+
replacement = `${r.from}: ${r.to}`;
|
92
|
+
}
|
93
|
+
accumulator.push(replacement);
|
94
|
+
last = r.offset + r.from.length;
|
95
|
+
}
|
96
|
+
accumulator.push(expression.slice(last));
|
97
|
+
return accumulator.join('');
|
98
|
+
}
|
99
|
+
|
100
|
+
interface Use {
|
101
|
+
offset: number;
|
102
|
+
scope: Scope;
|
103
|
+
isShorthandAssignmentProperty: boolean;
|
104
|
+
}
|
105
|
+
|
106
|
+
const enum DefinitionKind {
|
107
|
+
None = 0,
|
108
|
+
Let = 1,
|
109
|
+
Var = 2,
|
110
|
+
Fixed = 3,
|
111
|
+
}
|
112
|
+
|
113
|
+
interface VariableUses {
|
114
|
+
definitionKind: DefinitionKind;
|
115
|
+
uses: Use[];
|
116
|
+
}
|
117
|
+
|
118
|
+
class Scope {
|
119
|
+
readonly variables = new Map<string, VariableUses>();
|
120
|
+
readonly parent: Scope|null;
|
121
|
+
|
122
|
+
constructor(parent: Scope|null) {
|
123
|
+
this.parent = parent;
|
124
|
+
}
|
125
|
+
|
126
|
+
addVariable(name: string, offset: number, definitionKind: DefinitionKind, isShorthandAssignmentProperty: boolean):
|
127
|
+
void {
|
128
|
+
const variable = this.variables.get(name);
|
129
|
+
const use = {offset, scope: this, isShorthandAssignmentProperty};
|
130
|
+
if (!variable) {
|
131
|
+
this.variables.set(name, {definitionKind, uses: [use]});
|
132
|
+
return;
|
133
|
+
}
|
134
|
+
if (variable.definitionKind === DefinitionKind.None) {
|
135
|
+
variable.definitionKind = definitionKind;
|
136
|
+
}
|
137
|
+
variable.uses.push(use);
|
138
|
+
}
|
139
|
+
|
140
|
+
findBinders(name: string): VariableUses[] {
|
141
|
+
const result = [];
|
142
|
+
let scope: Scope|null = this;
|
143
|
+
while (scope !== null) {
|
144
|
+
const defUse = scope.variables.get(name);
|
145
|
+
if (defUse && defUse.definitionKind !== DefinitionKind.None) {
|
146
|
+
result.push(defUse);
|
147
|
+
}
|
148
|
+
scope = scope.parent;
|
149
|
+
}
|
150
|
+
return result;
|
151
|
+
}
|
152
|
+
|
153
|
+
#mergeChildDefUses(name: string, defUses: VariableUses): void {
|
154
|
+
const variable = this.variables.get(name);
|
155
|
+
if (!variable) {
|
156
|
+
this.variables.set(name, defUses);
|
157
|
+
return;
|
158
|
+
}
|
159
|
+
variable.uses.push(...defUses.uses);
|
160
|
+
if (defUses.definitionKind === DefinitionKind.Var) {
|
161
|
+
console.assert(variable.definitionKind !== DefinitionKind.Let);
|
162
|
+
if (variable.definitionKind === DefinitionKind.None) {
|
163
|
+
variable.definitionKind = defUses.definitionKind;
|
164
|
+
}
|
165
|
+
} else {
|
166
|
+
console.assert(defUses.definitionKind === DefinitionKind.None);
|
167
|
+
}
|
168
|
+
}
|
169
|
+
|
170
|
+
finalizeToParent(isFunctionScope: boolean): void {
|
171
|
+
if (!this.parent) {
|
172
|
+
console.error('Internal error: wrong nesting in scope analysis.');
|
173
|
+
throw new Error('Internal error');
|
174
|
+
}
|
175
|
+
|
176
|
+
// Move all unbound variables to the parent (also move var-bound variables
|
177
|
+
// if the parent is not a function).
|
178
|
+
const keysToRemove = [];
|
179
|
+
for (const [name, defUse] of this.variables.entries()) {
|
180
|
+
if (defUse.definitionKind === DefinitionKind.None ||
|
181
|
+
(defUse.definitionKind === DefinitionKind.Var && !isFunctionScope)) {
|
182
|
+
this.parent.#mergeChildDefUses(name, defUse);
|
183
|
+
keysToRemove.push(name);
|
184
|
+
}
|
185
|
+
}
|
186
|
+
keysToRemove.forEach(k => this.variables.delete(k));
|
187
|
+
}
|
188
|
+
}
|
189
|
+
|
190
|
+
class ScopeVariableAnalysis {
|
191
|
+
readonly #rootScope = new Scope(null);
|
192
|
+
readonly #allNames = new Set<string>();
|
193
|
+
#currentScope = this.#rootScope;
|
194
|
+
|
195
|
+
processNode(node: Acorn.ESTree.Node|null): void {
|
196
|
+
if (node === null) {
|
197
|
+
return;
|
198
|
+
}
|
199
|
+
switch (node.type) {
|
200
|
+
case 'AwaitExpression':
|
201
|
+
this.processNode(node.argument);
|
202
|
+
break;
|
203
|
+
case 'ArrayExpression':
|
204
|
+
node.elements.forEach(item => this.processNode(item));
|
205
|
+
break;
|
206
|
+
case 'ExpressionStatement':
|
207
|
+
this.processNode(node.expression);
|
208
|
+
break;
|
209
|
+
case 'Program':
|
210
|
+
console.assert(this.#currentScope === this.#rootScope);
|
211
|
+
node.body.forEach(item => this.processNode(item));
|
212
|
+
console.assert(this.#currentScope === this.#rootScope);
|
213
|
+
break;
|
214
|
+
case 'ArrayPattern':
|
215
|
+
node.elements.forEach(item => this.processNode(item));
|
216
|
+
break;
|
217
|
+
case 'ArrowFunctionExpression':
|
218
|
+
this.#pushScope();
|
219
|
+
node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.Var));
|
220
|
+
this.processNode(node.body);
|
221
|
+
this.#popScope(true);
|
222
|
+
break;
|
223
|
+
case 'AssignmentExpression':
|
224
|
+
case 'AssignmentPattern':
|
225
|
+
case 'BinaryExpression':
|
226
|
+
case 'LogicalExpression':
|
227
|
+
this.processNode(node.left);
|
228
|
+
this.processNode(node.right);
|
229
|
+
break;
|
230
|
+
case 'BlockStatement':
|
231
|
+
this.#pushScope();
|
232
|
+
node.body.forEach(this.processNode.bind(this));
|
233
|
+
this.#popScope(false);
|
234
|
+
break;
|
235
|
+
case 'CallExpression':
|
236
|
+
this.processNode(node.callee);
|
237
|
+
node.arguments.forEach(this.processNode.bind(this));
|
238
|
+
break;
|
239
|
+
case 'VariableDeclaration': {
|
240
|
+
const definitionKind = node.kind === 'var' ? DefinitionKind.Var : DefinitionKind.Let;
|
241
|
+
node.declarations.forEach(this.#processVariableDeclarator.bind(this, definitionKind));
|
242
|
+
break;
|
243
|
+
}
|
244
|
+
case 'CatchClause':
|
245
|
+
this.#pushScope();
|
246
|
+
this.#processNodeAsDefinition(DefinitionKind.Let, node.param);
|
247
|
+
node.body.body.forEach(this.processNode.bind(this));
|
248
|
+
this.#popScope(false);
|
249
|
+
break;
|
250
|
+
case 'ClassBody':
|
251
|
+
node.body.forEach(this.processNode.bind(this));
|
252
|
+
break;
|
253
|
+
case 'ClassDeclaration':
|
254
|
+
this.#processNodeAsDefinition(DefinitionKind.Let, node.id);
|
255
|
+
this.#pushScope();
|
256
|
+
this.processNode(node.superClass ?? null);
|
257
|
+
this.processNode(node.body);
|
258
|
+
this.#popScope(false);
|
259
|
+
break;
|
260
|
+
case 'ClassExpression':
|
261
|
+
this.#pushScope();
|
262
|
+
// Intentionally ignore the id.
|
263
|
+
this.processNode(node.superClass ?? null);
|
264
|
+
this.processNode(node.body);
|
265
|
+
this.#popScope(false);
|
266
|
+
break;
|
267
|
+
case 'ChainExpression':
|
268
|
+
this.processNode(node.expression);
|
269
|
+
break;
|
270
|
+
case 'ConditionalExpression':
|
271
|
+
this.processNode(node.test);
|
272
|
+
this.processNode(node.consequent);
|
273
|
+
this.processNode(node.alternate);
|
274
|
+
break;
|
275
|
+
case 'DoWhileStatement':
|
276
|
+
this.processNode(node.body);
|
277
|
+
this.processNode(node.test);
|
278
|
+
break;
|
279
|
+
case 'ForInStatement':
|
280
|
+
case 'ForOfStatement':
|
281
|
+
this.#pushScope();
|
282
|
+
this.processNode(node.left);
|
283
|
+
this.processNode(node.right);
|
284
|
+
this.processNode(node.body);
|
285
|
+
this.#popScope(false);
|
286
|
+
break;
|
287
|
+
case 'ForStatement':
|
288
|
+
this.#pushScope();
|
289
|
+
this.processNode(node.init ?? null);
|
290
|
+
this.processNode(node.test ?? null);
|
291
|
+
this.processNode(node.update ?? null);
|
292
|
+
this.processNode(node.body);
|
293
|
+
this.#popScope(false);
|
294
|
+
break;
|
295
|
+
case 'FunctionDeclaration':
|
296
|
+
this.#processNodeAsDefinition(DefinitionKind.Var, node.id);
|
297
|
+
this.#pushScope();
|
298
|
+
this.#addVariable('this', node.start, DefinitionKind.Fixed);
|
299
|
+
this.#addVariable('arguments', node.start, DefinitionKind.Fixed);
|
300
|
+
node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.Let));
|
301
|
+
this.processNode(node.body);
|
302
|
+
this.#popScope(true);
|
303
|
+
break;
|
304
|
+
case 'FunctionExpression':
|
305
|
+
// Id is intentionally ignored in function expressions.
|
306
|
+
this.#pushScope();
|
307
|
+
this.#addVariable('this', node.start, DefinitionKind.Fixed);
|
308
|
+
this.#addVariable('arguments', node.start, DefinitionKind.Fixed);
|
309
|
+
node.params.forEach(this.#processNodeAsDefinition.bind(this, DefinitionKind.Let));
|
310
|
+
this.processNode(node.body);
|
311
|
+
this.#popScope(true);
|
312
|
+
break;
|
313
|
+
case 'Identifier':
|
314
|
+
this.#addVariable(node.name, node.start);
|
315
|
+
break;
|
316
|
+
case 'IfStatement':
|
317
|
+
this.processNode(node.test);
|
318
|
+
this.processNode(node.consequent);
|
319
|
+
this.processNode(node.alternate ?? null);
|
320
|
+
break;
|
321
|
+
case 'LabeledStatement':
|
322
|
+
this.processNode(node.body);
|
323
|
+
break;
|
324
|
+
case 'MetaProperty':
|
325
|
+
break;
|
326
|
+
case 'MethodDefinition':
|
327
|
+
if (node.computed) {
|
328
|
+
this.processNode(node.key);
|
329
|
+
}
|
330
|
+
this.processNode(node.value);
|
331
|
+
break;
|
332
|
+
case 'NewExpression':
|
333
|
+
this.processNode(node.callee);
|
334
|
+
node.arguments.forEach(this.processNode.bind(this));
|
335
|
+
break;
|
336
|
+
case 'MemberExpression':
|
337
|
+
this.processNode(node.object);
|
338
|
+
if (node.computed) {
|
339
|
+
this.processNode(node.property);
|
340
|
+
}
|
341
|
+
break;
|
342
|
+
case 'ObjectExpression':
|
343
|
+
node.properties.forEach(this.processNode.bind(this));
|
344
|
+
break;
|
345
|
+
case 'ObjectPattern':
|
346
|
+
node.properties.forEach(this.processNode.bind(this));
|
347
|
+
break;
|
348
|
+
case 'PrivateIdentifier':
|
349
|
+
break;
|
350
|
+
case 'PropertyDefinition':
|
351
|
+
if (node.computed) {
|
352
|
+
this.processNode(node.key);
|
353
|
+
}
|
354
|
+
this.processNode(node.value ?? null);
|
355
|
+
break;
|
356
|
+
case 'Property':
|
357
|
+
if (node.shorthand) {
|
358
|
+
console.assert(node.value === node.key);
|
359
|
+
console.assert(node.value.type === 'Identifier');
|
360
|
+
this.#addVariable((node.value as Acorn.ESTree.Identifier).name, node.value.start, DefinitionKind.None, true);
|
361
|
+
} else {
|
362
|
+
if (node.computed) {
|
363
|
+
this.processNode(node.key);
|
364
|
+
}
|
365
|
+
this.processNode(node.value);
|
366
|
+
}
|
367
|
+
break;
|
368
|
+
case 'RestElement':
|
369
|
+
this.#processNodeAsDefinition(DefinitionKind.Let, node.argument);
|
370
|
+
break;
|
371
|
+
case 'ReturnStatement':
|
372
|
+
this.processNode(node.argument ?? null);
|
373
|
+
break;
|
374
|
+
case 'SequenceExpression':
|
375
|
+
node.expressions.forEach(this.processNode.bind(this));
|
376
|
+
break;
|
377
|
+
case 'SpreadElement':
|
378
|
+
this.processNode(node.argument);
|
379
|
+
break;
|
380
|
+
case 'SwitchCase':
|
381
|
+
this.processNode(node.test ?? null);
|
382
|
+
node.consequent.forEach(this.processNode.bind(this));
|
383
|
+
break;
|
384
|
+
case 'SwitchStatement':
|
385
|
+
this.processNode(node.discriminant);
|
386
|
+
node.cases.forEach(this.processNode.bind(this));
|
387
|
+
break;
|
388
|
+
case 'TaggedTemplateExpression':
|
389
|
+
this.processNode(node.tag);
|
390
|
+
this.processNode(node.quasi);
|
391
|
+
break;
|
392
|
+
case 'TemplateLiteral':
|
393
|
+
node.expressions.forEach(this.processNode.bind(this));
|
394
|
+
break;
|
395
|
+
case 'ThisExpression':
|
396
|
+
this.#addVariable('this', node.start);
|
397
|
+
break;
|
398
|
+
case 'ThrowStatement':
|
399
|
+
this.processNode(node.argument);
|
400
|
+
break;
|
401
|
+
case 'TryStatement':
|
402
|
+
this.processNode(node.block);
|
403
|
+
this.processNode(node.handler ?? null);
|
404
|
+
this.processNode(node.finalizer ?? null);
|
405
|
+
break;
|
406
|
+
case 'WithStatement':
|
407
|
+
this.processNode(node.object);
|
408
|
+
// TODO jarin figure how to treat the with body.
|
409
|
+
this.processNode(node.body);
|
410
|
+
break;
|
411
|
+
case 'YieldExpression':
|
412
|
+
this.processNode(node.argument ?? null);
|
413
|
+
break;
|
414
|
+
case 'UnaryExpression':
|
415
|
+
case 'UpdateExpression':
|
416
|
+
this.processNode(node.argument);
|
417
|
+
break;
|
418
|
+
case 'WhileStatement':
|
419
|
+
this.processNode(node.test);
|
420
|
+
this.processNode(node.body);
|
421
|
+
break;
|
422
|
+
|
423
|
+
// Ignore, no expressions involved.
|
424
|
+
case 'BreakStatement':
|
425
|
+
case 'ContinueStatement':
|
426
|
+
case 'DebuggerStatement':
|
427
|
+
case 'EmptyStatement':
|
428
|
+
case 'Literal':
|
429
|
+
case 'Super':
|
430
|
+
case 'TemplateElement':
|
431
|
+
break;
|
432
|
+
// Ignore, cannot be used outside of a module.
|
433
|
+
case 'ImportDeclaration':
|
434
|
+
case 'ImportDefaultSpecifier':
|
435
|
+
case 'ImportNamespaceSpecifier':
|
436
|
+
case 'ImportSpecifier':
|
437
|
+
case 'ImportExpression':
|
438
|
+
case 'ExportAllDeclaration':
|
439
|
+
case 'ExportDefaultDeclaration':
|
440
|
+
case 'ExportNamedDeclaration':
|
441
|
+
case 'ExportSpecifier':
|
442
|
+
break;
|
443
|
+
|
444
|
+
case 'VariableDeclarator':
|
445
|
+
console.error('Should not encounter VariableDeclarator in general traversal.');
|
446
|
+
break;
|
447
|
+
}
|
448
|
+
}
|
449
|
+
|
450
|
+
getFreeVariables(): Map<string, Use[]> {
|
451
|
+
const result = new Map<string, Use[]>();
|
452
|
+
for (const [name, defUse] of this.#rootScope.variables) {
|
453
|
+
if (defUse.definitionKind !== DefinitionKind.None) {
|
454
|
+
// Skip bound variables.
|
455
|
+
continue;
|
456
|
+
}
|
457
|
+
result.set(name, defUse.uses);
|
458
|
+
}
|
459
|
+
return result;
|
460
|
+
}
|
461
|
+
|
462
|
+
getAllNames(): Set<string> {
|
463
|
+
return this.#allNames;
|
464
|
+
}
|
465
|
+
|
466
|
+
#pushScope(): void {
|
467
|
+
this.#currentScope = new Scope(this.#currentScope);
|
468
|
+
}
|
469
|
+
|
470
|
+
#popScope(isFunctionContext: boolean): void {
|
471
|
+
if (this.#currentScope.parent === null) {
|
472
|
+
console.error('Internal error: wrong nesting in scope analysis.');
|
473
|
+
throw new Error('Internal error');
|
474
|
+
}
|
475
|
+
this.#currentScope.finalizeToParent(isFunctionContext);
|
476
|
+
this.#currentScope = this.#currentScope.parent;
|
477
|
+
}
|
478
|
+
|
479
|
+
#addVariable(
|
480
|
+
name: string, offset: number, definitionKind: DefinitionKind = DefinitionKind.None,
|
481
|
+
isShorthandAssignmentProperty: boolean = false): void {
|
482
|
+
this.#allNames.add(name);
|
483
|
+
this.#currentScope.addVariable(name, offset, definitionKind, isShorthandAssignmentProperty);
|
484
|
+
}
|
485
|
+
|
486
|
+
#processNodeAsDefinition(
|
487
|
+
definitionKind: DefinitionKind.Let|DefinitionKind.Var,
|
488
|
+
node: Acorn.ESTree.Pattern|Acorn.ESTree.AssignmentProperty|null): void {
|
489
|
+
if (node === null) {
|
490
|
+
return;
|
491
|
+
}
|
492
|
+
switch (node.type) {
|
493
|
+
case 'ArrayPattern':
|
494
|
+
node.elements.forEach(this.#processNodeAsDefinition.bind(this, definitionKind));
|
495
|
+
break;
|
496
|
+
case 'AssignmentPattern':
|
497
|
+
this.#processNodeAsDefinition(definitionKind, node.left);
|
498
|
+
this.processNode(node.right);
|
499
|
+
break;
|
500
|
+
case 'Identifier':
|
501
|
+
this.#addVariable(node.name, node.start, definitionKind);
|
502
|
+
break;
|
503
|
+
case 'MemberExpression':
|
504
|
+
this.processNode(node.object);
|
505
|
+
if (node.computed) {
|
506
|
+
this.processNode(node.property);
|
507
|
+
}
|
508
|
+
break;
|
509
|
+
case 'ObjectPattern':
|
510
|
+
node.properties.forEach(this.#processNodeAsDefinition.bind(this, definitionKind));
|
511
|
+
break;
|
512
|
+
case 'Property':
|
513
|
+
// This is AssignmentProperty inside an object pattern.
|
514
|
+
if (node.shorthand) {
|
515
|
+
console.assert(node.value === node.key);
|
516
|
+
console.assert(node.value.type === 'Identifier');
|
517
|
+
this.#addVariable((node.value as Acorn.ESTree.Identifier).name, node.value.start, definitionKind, true);
|
518
|
+
} else {
|
519
|
+
if (node.computed) {
|
520
|
+
this.processNode(node.key);
|
521
|
+
}
|
522
|
+
this.#processNodeAsDefinition(definitionKind, node.value);
|
523
|
+
}
|
524
|
+
break;
|
525
|
+
case 'RestElement':
|
526
|
+
this.#processNodeAsDefinition(definitionKind, node.argument);
|
527
|
+
break;
|
528
|
+
}
|
529
|
+
}
|
530
|
+
|
531
|
+
#processVariableDeclarator(
|
532
|
+
definitionKind: DefinitionKind.Let|DefinitionKind.Var, decl: Acorn.ESTree.VariableDeclarator): void {
|
533
|
+
this.#processNodeAsDefinition(definitionKind, decl.id);
|
534
|
+
this.processNode(decl.init ?? null);
|
535
|
+
}
|
536
|
+
}
|
@@ -16,6 +16,7 @@ import * as HTMLOutline from './HTMLOutline.js';
|
|
16
16
|
import * as JavaScriptFormatter from './JavaScriptFormatter.js';
|
17
17
|
import * as JavaScriptOutline from './JavaScriptOutline.js';
|
18
18
|
import * as JSONFormatter from './JSONFormatter.js';
|
19
|
+
import * as Substitute from './Substitute.js';
|
19
20
|
|
20
21
|
export {
|
21
22
|
CSSFormatter,
|
@@ -27,4 +28,5 @@ export {
|
|
27
28
|
JavaScriptFormatter,
|
28
29
|
JavaScriptOutline,
|
29
30
|
JSONFormatter,
|
31
|
+
Substitute,
|
30
32
|
};
|
@@ -384,6 +384,10 @@ export class MainImpl {
|
|
384
384
|
// New Lighthouse panel with timespan and snapshot mode
|
385
385
|
Root.Runtime.experiments.register('lighthousePanelFR', 'Use Lighthouse panel with timespan and snapshot modes');
|
386
386
|
|
387
|
+
// Tooling for CSS layers in Styles sidebar pane.
|
388
|
+
Root.Runtime.experiments.register(
|
389
|
+
Root.Runtime.ExperimentName.CSS_LAYERS, 'Tooling for CSS layers in the Styles pane');
|
390
|
+
|
387
391
|
Root.Runtime.experiments.enableExperimentsByDefault([
|
388
392
|
'sourceOrderViewer',
|
389
393
|
'hideIssuesFeature',
|
@@ -391,6 +395,7 @@ export class MainImpl {
|
|
391
395
|
Root.Runtime.ExperimentName.PRECISE_CHANGES,
|
392
396
|
'reportingApiDebugging',
|
393
397
|
Root.Runtime.ExperimentName.SYNC_SETTINGS,
|
398
|
+
Root.Runtime.ExperimentName.CSS_LAYERS,
|
394
399
|
]);
|
395
400
|
|
396
401
|
Root.Runtime.experiments.cleanUpStaleExperiments();
|