chrome-devtools-frontend 1.0.1013237 → 1.0.1013875
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/docs/workflows.md +28 -0
- package/front_end/core/i18n/locales/en-US.json +4 -1
- package/front_end/core/i18n/locales/en-XL.json +4 -1
- package/front_end/models/issues_manager/DeprecationIssue.ts +14 -2
- package/front_end/models/javascript_metadata/DOMPinnedProperties.ts +1754 -0
- package/front_end/models/source_map_scopes/NamesResolver.ts +48 -19
- package/front_end/models/workspace/UISourceCode.ts +1 -1
- package/front_end/panels/sources/TabbedEditorContainer.ts +16 -4
- package/front_end/ui/components/linear_memory_inspector/ValueInterpreterDisplay.ts +6 -6
- package/front_end/ui/components/linear_memory_inspector/valueInterpreterDisplay.css +8 -0
- package/package.json +1 -1
- package/scripts/devtools_paths.js +1 -1
- package/scripts/webidl-properties/config.js +279 -0
- package/scripts/webidl-properties/get-props.js +139 -0
- package/scripts/webidl-properties/index.js +85 -0
- package/scripts/webidl-properties/package.json +9 -0
- package/scripts/webidl-properties/tests.js +118 -0
- package/scripts/webidl-properties/util.js +81 -0
@@ -17,14 +17,17 @@ interface CachedScopeMap {
|
|
17
17
|
const scopeToCachedIdentifiersMap = new WeakMap<SDK.DebuggerModel.ScopeChainEntry, CachedScopeMap>();
|
18
18
|
const cachedMapByCallFrame = new WeakMap<SDK.DebuggerModel.CallFrame, Map<string, string>>();
|
19
19
|
|
20
|
-
export class
|
20
|
+
export class IdentifierPositions {
|
21
21
|
name: string;
|
22
|
-
lineNumber: number;
|
23
|
-
|
24
|
-
constructor(name: string, lineNumber: number, columnNumber: number) {
|
22
|
+
positions: {lineNumber: number, columnNumber: number}[];
|
23
|
+
|
24
|
+
constructor(name: string, positions: {lineNumber: number, columnNumber: number}[] = []) {
|
25
25
|
this.name = name;
|
26
|
-
this.
|
27
|
-
|
26
|
+
this.positions = positions;
|
27
|
+
}
|
28
|
+
|
29
|
+
addPosition(lineNumber: number, columnNumber: number): void {
|
30
|
+
this.positions.push({lineNumber, columnNumber});
|
28
31
|
}
|
29
32
|
}
|
30
33
|
|
@@ -66,7 +69,7 @@ const computeScopeTree = async function(functionScope: SDK.DebuggerModel.ScopeCh
|
|
66
69
|
|
67
70
|
export const scopeIdentifiers = async function(
|
68
71
|
functionScope: SDK.DebuggerModel.ScopeChainEntry|null, scope: SDK.DebuggerModel.ScopeChainEntry): Promise<{
|
69
|
-
freeVariables:
|
72
|
+
freeVariables: IdentifierPositions[], boundVariables: IdentifierPositions[],
|
70
73
|
}|null> {
|
71
74
|
if (!functionScope) {
|
72
75
|
return null;
|
@@ -132,24 +135,33 @@ export const scopeIdentifiers = async function(
|
|
132
135
|
continue;
|
133
136
|
}
|
134
137
|
|
138
|
+
const identifier = new IdentifierPositions(variable.name);
|
135
139
|
for (const offset of variable.offsets) {
|
136
140
|
const start = offset + slide;
|
137
141
|
cursor.resetTo(start);
|
138
|
-
|
142
|
+
identifier.addPosition(cursor.lineNumber(), cursor.columnNumber());
|
139
143
|
}
|
144
|
+
boundVariables.push(identifier);
|
140
145
|
}
|
141
146
|
|
142
147
|
// Compute free variables by collecting all the ancestor variables that are used in |containingScope|.
|
143
148
|
const freeVariables = [];
|
144
149
|
for (const ancestor of ancestorScopes) {
|
145
150
|
for (const ancestorVariable of ancestor.variables) {
|
151
|
+
let identifier = null;
|
146
152
|
for (const offset of ancestorVariable.offsets) {
|
147
153
|
if (offset >= containingScope.start && offset < containingScope.end) {
|
154
|
+
if (!identifier) {
|
155
|
+
identifier = new IdentifierPositions(ancestorVariable.name);
|
156
|
+
}
|
148
157
|
const start = offset + slide;
|
149
158
|
cursor.resetTo(start);
|
150
|
-
|
159
|
+
identifier.addPosition(cursor.lineNumber(), cursor.columnNumber());
|
151
160
|
}
|
152
161
|
}
|
162
|
+
if (identifier) {
|
163
|
+
freeVariables.push(identifier);
|
164
|
+
}
|
153
165
|
}
|
154
166
|
}
|
155
167
|
return {boundVariables, freeVariables};
|
@@ -187,17 +199,33 @@ const resolveScope =
|
|
187
199
|
// missing identifier names from SourceMap ranges.
|
188
200
|
const promises: Promise<void>[] = [];
|
189
201
|
|
190
|
-
const resolveEntry = (id:
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
202
|
+
const resolveEntry = (id: IdentifierPositions, handler: (sourceName: string) => void): void => {
|
203
|
+
// First see if we have a source map entry with a name for the identifier.
|
204
|
+
for (const position of id.positions) {
|
205
|
+
const entry = sourceMap.findEntry(position.lineNumber, position.columnNumber);
|
206
|
+
if (entry && entry.name) {
|
207
|
+
handler(entry.name);
|
208
|
+
return;
|
209
|
+
}
|
210
|
+
}
|
211
|
+
// If there is no entry with the name field, try to infer the name from the source positions.
|
212
|
+
async function resolvePosition(): Promise<void> {
|
213
|
+
if (!sourceMap) {
|
214
|
+
return;
|
215
|
+
}
|
216
|
+
// Let us find the first non-empty mapping of |id| and return that. Ideally, we would
|
217
|
+
// try to compute all the mappings and only use the mapping if all the non-empty
|
218
|
+
// mappings agree. However, that can be expensive for identifiers with many uses,
|
219
|
+
// so we iterate sequentially, stopping at the first non-empty mapping.
|
220
|
+
for (const position of id.positions) {
|
221
|
+
const sourceName = await resolveSourceName(script, sourceMap, id.name, position, textCache);
|
196
222
|
if (sourceName) {
|
197
223
|
handler(sourceName);
|
224
|
+
return;
|
198
225
|
}
|
199
|
-
}
|
226
|
+
}
|
200
227
|
}
|
228
|
+
promises.push(resolvePosition());
|
201
229
|
};
|
202
230
|
|
203
231
|
const functionScope = findFunctionScope();
|
@@ -229,10 +257,11 @@ const resolveScope =
|
|
229
257
|
return await cachedScopeMap.mappingPromise;
|
230
258
|
|
231
259
|
async function resolveSourceName(
|
232
|
-
script: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap,
|
260
|
+
script: SDK.Script.Script, sourceMap: SDK.SourceMap.SourceMap, name: string,
|
261
|
+
position: {lineNumber: number, columnNumber: number},
|
233
262
|
textCache: Map<string, TextUtils.Text.Text>): Promise<string|null> {
|
234
|
-
const startEntry = sourceMap.findEntry(
|
235
|
-
const endEntry = sourceMap.findEntry(
|
263
|
+
const startEntry = sourceMap.findEntry(position.lineNumber, position.columnNumber);
|
264
|
+
const endEntry = sourceMap.findEntry(position.lineNumber, position.columnNumber + name.length);
|
236
265
|
if (!startEntry || !endEntry || !startEntry.sourceURL || startEntry.sourceURL !== endEntry.sourceURL ||
|
237
266
|
!startEntry.sourceLineNumber || !startEntry.sourceColumnNumber || !endEntry.sourceLineNumber ||
|
238
267
|
!endEntry.sourceColumnNumber) {
|
@@ -131,7 +131,7 @@ export class UISourceCode extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
|
|
131
131
|
// DevTools UI to be the same script. For now this is just the url but this
|
132
132
|
// is likely to change in the future.
|
133
133
|
canononicalScriptId(): string {
|
134
|
-
return this.urlInternal
|
134
|
+
return `${this.contentTypeInternal.name()},${this.urlInternal}`;
|
135
135
|
}
|
136
136
|
|
137
137
|
parentURL(): Platform.DevToolsPath.UrlString {
|
@@ -73,7 +73,8 @@ export class TabbedEditorContainer extends Common.ObjectWrapper.ObjectWrapper<Ev
|
|
73
73
|
private readonly files: Map<string, Workspace.UISourceCode.UISourceCode>;
|
74
74
|
private readonly previouslyViewedFilesSetting: Common.Settings.Setting<SerializedHistoryItem[]>;
|
75
75
|
private readonly history: History;
|
76
|
-
private readonly uriToUISourceCode: Map<
|
76
|
+
private readonly uriToUISourceCode: Map<Platform.DevToolsPath.UrlString, Workspace.UISourceCode.UISourceCode>;
|
77
|
+
private readonly idToUISourceCode: Map<string, Workspace.UISourceCode.UISourceCode>;
|
77
78
|
private currentFileInternal!: Workspace.UISourceCode.UISourceCode|null;
|
78
79
|
private currentView!: UI.Widget.Widget|null;
|
79
80
|
private scrollTimer?: number;
|
@@ -104,6 +105,7 @@ export class TabbedEditorContainer extends Common.ObjectWrapper.ObjectWrapper<Ev
|
|
104
105
|
this.previouslyViewedFilesSetting = setting;
|
105
106
|
this.history = History.fromObject(this.previouslyViewedFilesSetting.get());
|
106
107
|
this.uriToUISourceCode = new Map();
|
108
|
+
this.idToUISourceCode = new Map();
|
107
109
|
}
|
108
110
|
|
109
111
|
private onBindingCreated(event: Common.EventTarget.EventTargetEvent<Persistence.Persistence.PersistenceBinding>):
|
@@ -356,12 +358,13 @@ export class TabbedEditorContainer extends Common.ObjectWrapper.ObjectWrapper<Ev
|
|
356
358
|
private canonicalUISourceCode(uiSourceCode: Workspace.UISourceCode.UISourceCode):
|
357
359
|
Workspace.UISourceCode.UISourceCode {
|
358
360
|
// Check if we have already a UISourceCode for this url
|
359
|
-
const existingSourceCode = this.
|
361
|
+
const existingSourceCode = this.idToUISourceCode.get(uiSourceCode.canononicalScriptId());
|
360
362
|
if (existingSourceCode) {
|
361
363
|
// Ignore incoming uiSourceCode, we already have this file.
|
362
364
|
return existingSourceCode;
|
363
365
|
}
|
364
|
-
this.
|
366
|
+
this.idToUISourceCode.set(uiSourceCode.canononicalScriptId(), uiSourceCode);
|
367
|
+
this.uriToUISourceCode.set(uiSourceCode.url(), uiSourceCode);
|
365
368
|
return uiSourceCode;
|
366
369
|
}
|
367
370
|
|
@@ -420,6 +423,9 @@ export class TabbedEditorContainer extends Common.ObjectWrapper.ObjectWrapper<Ev
|
|
420
423
|
if (this.uriToUISourceCode.get(uiSourceCode.url()) === uiSourceCode) {
|
421
424
|
this.uriToUISourceCode.delete(uiSourceCode.url());
|
422
425
|
}
|
426
|
+
if (this.idToUISourceCode.get(uiSourceCode.canononicalScriptId()) === uiSourceCode) {
|
427
|
+
this.idToUISourceCode.delete(uiSourceCode.canononicalScriptId());
|
428
|
+
}
|
423
429
|
}
|
424
430
|
this.tabbedPane.closeTabs(tabIds);
|
425
431
|
}
|
@@ -591,7 +597,13 @@ export class TabbedEditorContainer extends Common.ObjectWrapper.ObjectWrapper<Ev
|
|
591
597
|
this.uriToUISourceCode.delete(k);
|
592
598
|
}
|
593
599
|
}
|
594
|
-
//
|
600
|
+
// Remove from map under old id if it has changed.
|
601
|
+
for (const [k, v] of this.idToUISourceCode) {
|
602
|
+
if (v === uiSourceCode && k !== v.canononicalScriptId()) {
|
603
|
+
this.idToUISourceCode.delete(k);
|
604
|
+
}
|
605
|
+
}
|
606
|
+
// Ensure it is mapped under current url and id.
|
595
607
|
this.canonicalUISourceCode(uiSourceCode);
|
596
608
|
}
|
597
609
|
|
@@ -153,10 +153,10 @@ export class ValueInterpreterDisplay extends HTMLElement {
|
|
153
153
|
// Disabled until https://crbug.com/1079231 is fixed.
|
154
154
|
// clang-format off
|
155
155
|
return html`
|
156
|
-
<span class="value-type-cell-no-mode value-type-cell">${i18n.i18n.lockedString(type)}</span>
|
156
|
+
<span class="value-type-cell-no-mode value-type-cell selectable-text">${i18n.i18n.lockedString(type)}</span>
|
157
157
|
<div class="value-type-cell">
|
158
158
|
<div class="value-type-value-with-link" data-value="true">
|
159
|
-
<span>${unsignedValue}</span>
|
159
|
+
<span class="selectable-text">${unsignedValue}</span>
|
160
160
|
${
|
161
161
|
html`
|
162
162
|
<button class="jump-to-button" data-jump="true" title=${buttonTitle} ?disabled=${jumpDisabled}
|
@@ -179,7 +179,7 @@ export class ValueInterpreterDisplay extends HTMLElement {
|
|
179
179
|
// Disabled until https://crbug.com/1079231 is fixed.
|
180
180
|
// clang-format off
|
181
181
|
return html`
|
182
|
-
<span class="value-type-cell">${i18n.i18n.lockedString(type)}</span>
|
182
|
+
<span class="value-type-cell selectable-text">${i18n.i18n.lockedString(type)}</span>
|
183
183
|
<div>
|
184
184
|
<select title=${i18nString(UIStrings.changeValueTypeMode)}
|
185
185
|
data-mode-settings="true"
|
@@ -206,7 +206,7 @@ export class ValueInterpreterDisplay extends HTMLElement {
|
|
206
206
|
const showSignedAndUnsigned =
|
207
207
|
signedValue !== unsignedValue && mode !== ValueTypeMode.Hexadecimal && mode !== ValueTypeMode.Octal;
|
208
208
|
|
209
|
-
const unsignedRendered = html`<span class="value-type-cell" title=${
|
209
|
+
const unsignedRendered = html`<span class="value-type-cell selectable-text" title=${
|
210
210
|
i18nString(UIStrings.unsignedValue)} data-value="true">${unsignedValue}</span>`;
|
211
211
|
if (!showSignedAndUnsigned) {
|
212
212
|
return unsignedRendered;
|
@@ -214,8 +214,8 @@ export class ValueInterpreterDisplay extends HTMLElement {
|
|
214
214
|
|
215
215
|
// Some values are too long to show in one line, we're putting them into the next line.
|
216
216
|
const showInMultipleLines = type === ValueType.Int32 || type === ValueType.Int64;
|
217
|
-
const signedRendered =
|
218
|
-
|
217
|
+
const signedRendered = html`<span class="selectable-text" data-value="true" title=${
|
218
|
+
i18nString(UIStrings.signedValue)}>${signedValue}</span>`;
|
219
219
|
|
220
220
|
if (showInMultipleLines) {
|
221
221
|
return html`
|
package/package.json
CHANGED
@@ -51,7 +51,7 @@ function isInChromiumDirectory() {
|
|
51
51
|
const normalizedPath = PATH_TO_EXECUTED_FILE.split(path.sep).join('/');
|
52
52
|
const devtoolsPath = 'src/third_party/devtools-frontend';
|
53
53
|
const isInChromium = normalizedPath.includes(devtoolsPath);
|
54
|
-
const potentialChromiumDir = PATH_TO_EXECUTED_FILE.substring(0,
|
54
|
+
const potentialChromiumDir = PATH_TO_EXECUTED_FILE.substring(0, normalizedPath.indexOf(devtoolsPath));
|
55
55
|
const result = {isInChromium, chromiumDirectory: potentialChromiumDir};
|
56
56
|
_lookUpCaches.set('chromium', result);
|
57
57
|
return result;
|
@@ -0,0 +1,279 @@
|
|
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
|
+
/**
|
6
|
+
* All the specs relevant for generating the DOM pinned properties dataset.
|
7
|
+
* These strings represent file names. A list of all file names can be found at
|
8
|
+
* https://github.com/w3c/webref/tree/main/tr/idl or via the @webref/idl API.
|
9
|
+
*
|
10
|
+
* These spec names are included in the generated dataset. To keep it small,
|
11
|
+
* the values are bitwise or'd.
|
12
|
+
*/
|
13
|
+
export const SPECS = {
|
14
|
+
'html': 1,
|
15
|
+
'dom': 2,
|
16
|
+
'uievents': 4,
|
17
|
+
'pointerevents': 8,
|
18
|
+
'cssom': 16,
|
19
|
+
'wai-aria': 32
|
20
|
+
};
|
21
|
+
|
22
|
+
/**
|
23
|
+
* All the "global" attributes as defined in the DOM specification.
|
24
|
+
* Used to annotate the extracted properties from the WebIDL types.
|
25
|
+
*/
|
26
|
+
export const GLOBAL_ATTRIBUTES = new Set([
|
27
|
+
// https://html.spec.whatwg.org/multipage/dom.html#global-attributes
|
28
|
+
'accesskey', 'autocapitalize', 'autofocus', 'contenteditable', 'dir', 'draggable', 'enterkeyhint',
|
29
|
+
'hidden', 'inputmode', 'is', 'itemid', 'itemprop', 'itemref', 'itemscope',
|
30
|
+
'itemtype', 'lang', 'nonce', 'spellcheck', 'style', 'tabindex', 'title',
|
31
|
+
'translate',
|
32
|
+
]);
|
33
|
+
|
34
|
+
/**
|
35
|
+
* The "applicable" members for certain "states" that WebIDL types can be in.
|
36
|
+
* In other words, some members are "valid" only valid in certain situations:
|
37
|
+
* for example, with the HTML input element, the set of valid members are
|
38
|
+
* determined by the "type" attribute.
|
39
|
+
*/
|
40
|
+
export const VALID_MEMBERS = {
|
41
|
+
// https://html.spec.whatwg.org/multipage/input.html#input-type-attr-summary
|
42
|
+
HTMLInputElement: {
|
43
|
+
'[type=hidden]': new Set([
|
44
|
+
'autocomplete',
|
45
|
+
'value',
|
46
|
+
]),
|
47
|
+
'[type=text]': new Set([
|
48
|
+
'autocomplete',
|
49
|
+
'dirname',
|
50
|
+
'list',
|
51
|
+
'maxlength',
|
52
|
+
'minlength',
|
53
|
+
'pattern',
|
54
|
+
'placeholder',
|
55
|
+
'readonly',
|
56
|
+
'required',
|
57
|
+
'size',
|
58
|
+
'value',
|
59
|
+
'list',
|
60
|
+
'selectionstart',
|
61
|
+
'selectionend',
|
62
|
+
'selectiondirection',
|
63
|
+
]),
|
64
|
+
'[type=search]': new Set([
|
65
|
+
'autocomplete',
|
66
|
+
'dirname',
|
67
|
+
'list',
|
68
|
+
'maxlength',
|
69
|
+
'minlength',
|
70
|
+
'pattern',
|
71
|
+
'placeholder',
|
72
|
+
'readonly',
|
73
|
+
'required',
|
74
|
+
'size',
|
75
|
+
'value',
|
76
|
+
'list',
|
77
|
+
'selectionstart',
|
78
|
+
'selectionend',
|
79
|
+
'selectiondirection',
|
80
|
+
]),
|
81
|
+
'[type=url]': new Set([
|
82
|
+
'autocomplete',
|
83
|
+
'list',
|
84
|
+
'maxlength',
|
85
|
+
'minlength',
|
86
|
+
'pattern',
|
87
|
+
'placeholder',
|
88
|
+
'readonly',
|
89
|
+
'required',
|
90
|
+
'size',
|
91
|
+
'value',
|
92
|
+
'list',
|
93
|
+
'selectionstart',
|
94
|
+
'selectionend',
|
95
|
+
'selectiondirection',
|
96
|
+
]),
|
97
|
+
'[type=tel]': new Set([
|
98
|
+
'autocomplete',
|
99
|
+
'list',
|
100
|
+
'maxlength',
|
101
|
+
'minlength',
|
102
|
+
'pattern',
|
103
|
+
'placeholder',
|
104
|
+
'readonly',
|
105
|
+
'required',
|
106
|
+
'size',
|
107
|
+
'value',
|
108
|
+
'list',
|
109
|
+
'selectionstart',
|
110
|
+
'selectionend',
|
111
|
+
'selectiondirection',
|
112
|
+
]),
|
113
|
+
'[type=email]': new Set([
|
114
|
+
'autocomplete',
|
115
|
+
'list',
|
116
|
+
'maxlength',
|
117
|
+
'minlength',
|
118
|
+
'multiple',
|
119
|
+
'pattern',
|
120
|
+
'placeholder',
|
121
|
+
'readonly',
|
122
|
+
'required',
|
123
|
+
'size',
|
124
|
+
'value',
|
125
|
+
'list',
|
126
|
+
]),
|
127
|
+
'[type=password]': new Set([
|
128
|
+
'autocomplete',
|
129
|
+
'maxlength',
|
130
|
+
'minlength',
|
131
|
+
'pattern',
|
132
|
+
'placeholder',
|
133
|
+
'readonly',
|
134
|
+
'required',
|
135
|
+
'size',
|
136
|
+
'value',
|
137
|
+
'selectionstart',
|
138
|
+
'selectionend',
|
139
|
+
'selectiondirection',
|
140
|
+
]),
|
141
|
+
'[type=date]': new Set([
|
142
|
+
'autocomplete',
|
143
|
+
'list',
|
144
|
+
'max',
|
145
|
+
'min',
|
146
|
+
'readonly',
|
147
|
+
'required',
|
148
|
+
'step',
|
149
|
+
'value',
|
150
|
+
'valueasdate',
|
151
|
+
'valueasnumber',
|
152
|
+
'list',
|
153
|
+
]),
|
154
|
+
'[type=month]': new Set([
|
155
|
+
'autocomplete',
|
156
|
+
'list',
|
157
|
+
'max',
|
158
|
+
'min',
|
159
|
+
'readonly',
|
160
|
+
'required',
|
161
|
+
'step',
|
162
|
+
'value',
|
163
|
+
'valueasdate',
|
164
|
+
'valueasnumber',
|
165
|
+
'list',
|
166
|
+
]),
|
167
|
+
'[type=week]': new Set([
|
168
|
+
'autocomplete',
|
169
|
+
'list',
|
170
|
+
'max',
|
171
|
+
'min',
|
172
|
+
'readonly',
|
173
|
+
'required',
|
174
|
+
'step',
|
175
|
+
'value',
|
176
|
+
'valueasdate',
|
177
|
+
'valueasnumber',
|
178
|
+
'list',
|
179
|
+
]),
|
180
|
+
'[type=time]': new Set([
|
181
|
+
'autocomplete',
|
182
|
+
'list',
|
183
|
+
'max',
|
184
|
+
'min',
|
185
|
+
'readonly',
|
186
|
+
'required',
|
187
|
+
'step',
|
188
|
+
'value',
|
189
|
+
'valueasdate',
|
190
|
+
'valueasnumber',
|
191
|
+
'list',
|
192
|
+
]),
|
193
|
+
'[type=datetime-local]': new Set([
|
194
|
+
'autocomplete',
|
195
|
+
'list',
|
196
|
+
'max',
|
197
|
+
'min',
|
198
|
+
'readonly',
|
199
|
+
'required',
|
200
|
+
'step',
|
201
|
+
'value',
|
202
|
+
'valueasnumber',
|
203
|
+
'list',
|
204
|
+
]),
|
205
|
+
'[type=number]': new Set([
|
206
|
+
'autocomplete',
|
207
|
+
'list',
|
208
|
+
'max',
|
209
|
+
'min',
|
210
|
+
'placeholder',
|
211
|
+
'readonly',
|
212
|
+
'required',
|
213
|
+
'step',
|
214
|
+
'value',
|
215
|
+
'valueasnumber',
|
216
|
+
'list',
|
217
|
+
]),
|
218
|
+
'[type=range]': new Set([
|
219
|
+
'autocomplete',
|
220
|
+
'list',
|
221
|
+
'max',
|
222
|
+
'min',
|
223
|
+
'step',
|
224
|
+
'value',
|
225
|
+
'valueasnumber',
|
226
|
+
'list',
|
227
|
+
]),
|
228
|
+
'[type=color]': new Set([
|
229
|
+
'autocomplete',
|
230
|
+
'list',
|
231
|
+
'value',
|
232
|
+
]),
|
233
|
+
'[type=checkbox]': new Set([
|
234
|
+
'checked',
|
235
|
+
'required',
|
236
|
+
'checked',
|
237
|
+
'value',
|
238
|
+
]),
|
239
|
+
'[type=radio]': new Set([
|
240
|
+
'checked',
|
241
|
+
'required',
|
242
|
+
'checked',
|
243
|
+
'value',
|
244
|
+
]),
|
245
|
+
'[type=file]': new Set([
|
246
|
+
'accept',
|
247
|
+
'multiple',
|
248
|
+
'required',
|
249
|
+
'files',
|
250
|
+
'value',
|
251
|
+
]),
|
252
|
+
'[type=submit]': new Set([
|
253
|
+
'formaction',
|
254
|
+
'formenctype',
|
255
|
+
'formmethod',
|
256
|
+
'formnovalidate',
|
257
|
+
'formtarget',
|
258
|
+
'value',
|
259
|
+
]),
|
260
|
+
'[type=image]': new Set([
|
261
|
+
'alt',
|
262
|
+
'formaction',
|
263
|
+
'formenctype',
|
264
|
+
'formmethod',
|
265
|
+
'formnovalidate',
|
266
|
+
'formtarget',
|
267
|
+
'height',
|
268
|
+
'src',
|
269
|
+
'width',
|
270
|
+
'value',
|
271
|
+
]),
|
272
|
+
'[type=reset]': new Set([
|
273
|
+
'value',
|
274
|
+
]),
|
275
|
+
'[type=button]': new Set([
|
276
|
+
'value',
|
277
|
+
]),
|
278
|
+
},
|
279
|
+
};
|
@@ -0,0 +1,139 @@
|
|
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 {GLOBAL_ATTRIBUTES, SPECS, VALID_MEMBERS} from './config.js';
|
6
|
+
import {merge} from './util.js';
|
7
|
+
|
8
|
+
/**
|
9
|
+
* All the members relevant for generating the DOM pinned properties dataset
|
10
|
+
* from WebIDL interfaces, mixins and dictionaries.
|
11
|
+
*/
|
12
|
+
const ACCEPTED_MEMBER_TYPES = new Set(['attribute', 'field']);
|
13
|
+
|
14
|
+
/**
|
15
|
+
* Generates the DOM pinned properties dataset.
|
16
|
+
*
|
17
|
+
* @param {array} specs A list of specs. Each spec specifies its name and
|
18
|
+
* all the idl definitions it contains.
|
19
|
+
* @returns {object} output An object with WebIDL type names as keys and their
|
20
|
+
* WebIDL properties and inheritance/include chains as values.
|
21
|
+
*/
|
22
|
+
export function getIDLProps(specs, output = {}) {
|
23
|
+
for (const spec of specs) {
|
24
|
+
transform(spec, output);
|
25
|
+
}
|
26
|
+
return output;
|
27
|
+
}
|
28
|
+
|
29
|
+
function transform({name, idls}, output = {}) {
|
30
|
+
const makeEntry = () => ({
|
31
|
+
inheritance: null,
|
32
|
+
includes: [],
|
33
|
+
props: {},
|
34
|
+
});
|
35
|
+
|
36
|
+
for (const idl of idls) {
|
37
|
+
switch (idl.type) {
|
38
|
+
case 'interface':
|
39
|
+
case 'interface mixin':
|
40
|
+
case 'dictionary': {
|
41
|
+
output[idl.name] = output[idl.name] ?? makeEntry();
|
42
|
+
let props = idl.members?.filter(member => ACCEPTED_MEMBER_TYPES.has(member.type));
|
43
|
+
props = props?.map(member => [member.name, {global: GLOBAL_ATTRIBUTES.has(member.name), specs: [name]}, ]);
|
44
|
+
merge(output[idl.name], {
|
45
|
+
inheritance: idl.inheritance,
|
46
|
+
props: Object.fromEntries(props),
|
47
|
+
});
|
48
|
+
break;
|
49
|
+
}
|
50
|
+
case 'includes': {
|
51
|
+
output[idl.target] = output[idl.target] ?? makeEntry();
|
52
|
+
merge(output[idl.target], {
|
53
|
+
includes: [idl.includes],
|
54
|
+
});
|
55
|
+
break;
|
56
|
+
}
|
57
|
+
case 'callback':
|
58
|
+
case 'callback interface':
|
59
|
+
case 'enum':
|
60
|
+
case 'typedef':
|
61
|
+
case 'namespace': {
|
62
|
+
break;
|
63
|
+
}
|
64
|
+
default: {
|
65
|
+
console.warn('Skipping unknown WebIDL type', idl.type);
|
66
|
+
}
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Adds additional metadata to the DOM pinned properties dataset.
|
73
|
+
*
|
74
|
+
* Currently only adds information about which properties are valid based on
|
75
|
+
* some state, such as for the HTMLInputElement. See `VALID_MEMBERS`.
|
76
|
+
*
|
77
|
+
* @param {*} output
|
78
|
+
*/
|
79
|
+
export function addMetadata(output) {
|
80
|
+
for (const [key, value] of Object.entries(output)) {
|
81
|
+
const rule = VALID_MEMBERS[key];
|
82
|
+
if (!rule) {
|
83
|
+
continue;
|
84
|
+
}
|
85
|
+
const states = Object.entries(rule).map(([selector, allowlist]) => {
|
86
|
+
const valid = Object.entries(value.props).filter(([prop]) => allowlist.has(prop.toLowerCase()));
|
87
|
+
return [selector, Object.fromEntries(valid)];
|
88
|
+
});
|
89
|
+
value.states = Object.fromEntries(states);
|
90
|
+
}
|
91
|
+
return output;
|
92
|
+
}
|
93
|
+
|
94
|
+
/**
|
95
|
+
* Minimizes the DOM pinned properties dataset to remove the bits of data that
|
96
|
+
* don't contain information. For example, empty inheritance/includes chains.
|
97
|
+
*
|
98
|
+
* This should be done right at the end, before writing into the output file, to
|
99
|
+
* allow for certain diagnostics (such as finding "missing types").
|
100
|
+
*
|
101
|
+
* @param {*} output
|
102
|
+
* @returns {object}
|
103
|
+
*/
|
104
|
+
export function minimize(output) {
|
105
|
+
for (const [key, value] of Object.entries(output)) {
|
106
|
+
if (!value.inheritance) {
|
107
|
+
// Remove empty inheritance chains.
|
108
|
+
delete value.inheritance;
|
109
|
+
}
|
110
|
+
if (!value.includes.length) {
|
111
|
+
// Remove empty include chains.
|
112
|
+
delete value.includes;
|
113
|
+
}
|
114
|
+
const props = Object.entries(value.props);
|
115
|
+
if (!props.length) {
|
116
|
+
// Remove empty 'prop' lists.
|
117
|
+
delete value.props;
|
118
|
+
} else {
|
119
|
+
for (const [, value] of props) {
|
120
|
+
if (!value.global) {
|
121
|
+
// Remove the 'global' flag if it's false.
|
122
|
+
delete value.global;
|
123
|
+
}
|
124
|
+
if (value.specs.length === 1 && value.specs[0] === 'html') {
|
125
|
+
// Remove the 'specs' list if it's just "html".
|
126
|
+
delete value.specs;
|
127
|
+
} else {
|
128
|
+
// Combine multiple spec names into a single value.
|
129
|
+
value.specs = value.specs.reduce((acc, name) => acc | SPECS[name], 0);
|
130
|
+
}
|
131
|
+
}
|
132
|
+
}
|
133
|
+
// Remove the entire entry if there's nothing left after the cleanup above.
|
134
|
+
if (!Object.entries(value).length) {
|
135
|
+
delete output[key];
|
136
|
+
}
|
137
|
+
}
|
138
|
+
return output;
|
139
|
+
}
|