chrome-devtools-frontend 1.0.1013298 → 1.0.1014346

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.
@@ -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 Identifier {
20
+ export class IdentifierPositions {
21
21
  name: string;
22
- lineNumber: number;
23
- columnNumber: number;
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.lineNumber = lineNumber;
27
- this.columnNumber = columnNumber;
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: Identifier[], boundVariables: Identifier[],
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
- boundVariables.push(new Identifier(variable.name, cursor.lineNumber(), cursor.columnNumber()));
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
- freeVariables.push(new Identifier(ancestorVariable.name, cursor.lineNumber(), cursor.columnNumber()));
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: Identifier, handler: (sourceName: string) => void): void => {
191
- const entry = sourceMap.findEntry(id.lineNumber, id.columnNumber);
192
- if (entry && entry.name) {
193
- handler(entry.name);
194
- } else {
195
- promises.push(resolveSourceName(script, sourceMap, id, textCache).then(sourceName => {
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, id: Identifier,
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(id.lineNumber, id.columnNumber);
235
- const endEntry = sourceMap.findEntry(id.lineNumber, id.columnNumber + id.name.length);
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 {
@@ -256,6 +256,7 @@ export class StartView extends UI.Widget.Widget {
256
256
  }
257
257
  wasShown(): void {
258
258
  super.wasShown();
259
+ this.controller.recomputePageAuditability();
259
260
  this.registerCSSFiles([lighthouseStartViewStyles]);
260
261
  }
261
262
  }
@@ -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<string, Workspace.UISourceCode.UISourceCode>;
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.uriToUISourceCode.get(uiSourceCode.canononicalScriptId());
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.uriToUISourceCode.set(uiSourceCode.canononicalScriptId(), uiSourceCode);
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
- // Ensure it is mapped under current url.
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
- html`<span data-value="true" title=${i18nString(UIStrings.signedValue)}>${signedValue}</span>`;
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`
@@ -61,3 +61,11 @@
61
61
  background-color: var(--color-details-hairline);
62
62
  margin: 0 4px;
63
63
  }
64
+
65
+ .selectable-text {
66
+ user-select: text;
67
+ }
68
+
69
+ .selectable-text::selection {
70
+ background-color: var(--legacy-item-selection-bg-color);
71
+ }
package/package.json CHANGED
@@ -55,5 +55,5 @@
55
55
  "unittest": "scripts/test/run_unittests.py --no-text-coverage",
56
56
  "watch": "vpython third_party/node/node.py --output scripts/watch_build.js"
57
57
  },
58
- "version": "1.0.1013298"
58
+ "version": "1.0.1014346"
59
59
  }
@@ -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, PATH_TO_EXECUTED_FILE.indexOf(devtoolsPath));
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;
@@ -19,6 +19,7 @@ module.exports = {
19
19
  noDefineCall: 'Could not find a defineComponent() call for the component {{ tagName }}.',
20
20
  defineCallNonLiteral: 'defineComponent() first argument must be a string literal.',
21
21
  staticLiteralInvalid: 'static readonly litTagName must use a literal string, with no interpolation.',
22
+ duplicateStaticLitTagName: 'found a duplicated litTagName: {{ tagName }}',
22
23
  litTagNameNotLiteral:
23
24
  'litTagName must be defined as a string passed in as LitHtml.literal`component-name`, but no tagged template was found.',
24
25
  staticLiteralNotReadonly: 'static litTagName must be readonly.'
@@ -90,8 +91,9 @@ module.exports = {
90
91
  });
91
92
  }
92
93
 
93
- /** @type {Set<{classNode: any, tagName: string}>} */
94
- const componentClassDefinitionLitTagNamesFound = new Set();
94
+ // Map of litTagName to the class node.
95
+ /** @type {Map<string, any}>} */
96
+ const componentClassDefinitionLitTagNameNodes = new Map();
95
97
 
96
98
  /** @type {Set<string>} */
97
99
  const defineComponentCallsFound = new Set();
@@ -147,8 +149,15 @@ module.exports = {
147
149
  // Grab the name of the component, e.g:
148
150
  // LitHtml.literal`devtools-foo` will pull "devtools-foo" out.
149
151
  const componentTagName = componentTagNameNode.value.quasi.quasis[0].value.cooked;
150
- componentClassDefinitionLitTagNamesFound.add(
151
- {tagName: componentTagName, classNode: componentClassDefinition});
152
+
153
+ // Now we ensure that we haven't found this tag name before. If we
154
+ // have, we have two components with the same litTagName property,
155
+ // which is an error.
156
+ if (componentClassDefinitionLitTagNameNodes.has(componentTagName)) {
157
+ context.report({node: componentClassDefinition, messageId: 'duplicateStaticLitTagName', data: {tagName: componentTagName}});
158
+ }
159
+
160
+ componentClassDefinitionLitTagNameNodes.set(componentTagName, componentClassDefinition);
152
161
  }
153
162
 
154
163
  // Find all defineComponent() calls and store the arguments to them.
@@ -177,8 +186,7 @@ module.exports = {
177
186
  }
178
187
  }
179
188
 
180
- for (const foundComponentClass of componentClassDefinitionLitTagNamesFound) {
181
- const {tagName, classNode} = foundComponentClass;
189
+ for (const [tagName, classNode] of componentClassDefinitionLitTagNameNodes) {
182
190
  // Check that each tagName has a matching entry in both other places we expect it.
183
191
  if (!defineComponentCallsFound.has(tagName)) {
184
192
  context.report({node: classNode, messageId: 'noDefineCall', data: {tagName}});
@@ -233,5 +233,28 @@ ruleTester.run('check_component_naming', rule, {
233
233
  filename: 'front_end/ui/components/Foo.ts',
234
234
  errors: [{messageId: 'noDefineCall', data: {tagName: 'devtools-bar'}}]
235
235
  },
236
+ {
237
+ // Multiple components in one file is valid.
238
+ // But here devtools-foo is fine, but devtools-bar has the wrong static tag name
239
+ code: `export class Foo extends HTMLElement {
240
+ static readonly litTagName = LitHtml.literal\`devtools-foo\`
241
+ }
242
+
243
+ export class Bar extends HTMLElement {
244
+ static readonly litTagName = LitHtml.literal\`devtools-foo\`
245
+ }
246
+
247
+ ComponentHelpers.CustomElements.defineComponent('devtools-foo', Foo);
248
+ ComponentHelpers.CustomElements.defineComponent('devtools-bar', Foo);
249
+
250
+ declare global {
251
+ interface HTMLElementTagNameMap {
252
+ 'devtools-foo': Foo
253
+ 'devtools-bar': Bar
254
+ }
255
+ }`,
256
+ filename: 'front_end/ui/components/Foo.ts',
257
+ errors: [{messageId: 'duplicateStaticLitTagName', data: {tagName: 'devtools-foo'}}]
258
+ },
236
259
  ]
237
260
  });
@@ -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
+ };