chrome-devtools-frontend 1.0.963415 → 1.0.965113
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 +6 -0
- package/docs/resource_management.md +119 -0
- package/docs/workflows.md +7 -0
- package/front_end/core/i18n/locales/en-US.json +23 -2
- package/front_end/core/i18n/locales/en-XL.json +23 -2
- package/front_end/core/sdk/DOMModel.ts +2 -2
- package/front_end/generated/InspectorBackendCommands.js +8 -4
- package/front_end/generated/protocol-mapping.d.ts +12 -1
- package/front_end/generated/protocol-proxy-api.d.ts +11 -1
- package/front_end/generated/protocol.ts +27 -12
- package/front_end/models/javascript_metadata/NativeFunctions.js +7480 -4147
- package/front_end/models/workspace/UISourceCode.ts +4 -5
- package/front_end/panels/animation/AnimationUI.ts +2 -1
- package/front_end/panels/application/ApplicationPanelSidebar.ts +41 -1
- package/front_end/panels/application/InterestGroupStorageModel.ts +87 -0
- package/front_end/panels/application/InterestGroupStorageView.ts +112 -0
- package/front_end/panels/application/InterestGroupTreeElement.ts +61 -0
- package/front_end/panels/application/application.ts +4 -0
- package/front_end/panels/application/components/BackForwardCacheStrings.ts +1 -1
- package/front_end/panels/application/components/InterestGroupAccessGrid.ts +149 -0
- package/front_end/panels/application/components/components.ts +2 -0
- package/front_end/panels/application/components/interestGroupAccessGrid.css +26 -0
- package/front_end/panels/application/interestGroupStorageView.css +13 -0
- package/front_end/panels/elements/StylesSidebarPane.ts +6 -4
- package/front_end/panels/sources/UISourceCodeFrame.ts +7 -0
- package/front_end/ui/legacy/components/inline_editor/cssLength.css +1 -0
- package/front_end/ui/legacy/themeColors.css +1 -1
- package/package.json +1 -1
- package/scripts/build/devtools_plugin.js +103 -0
- package/scripts/build/ninja/{rollup.gni → bundle.gni} +2 -2
- package/scripts/build/ninja/devtools_entrypoint.gni +8 -8
- package/scripts/build/rollup.config.js +3 -93
- package/scripts/devtools_paths.js +3 -2
- package/scripts/javascript_natives/helpers.js +211 -0
- package/scripts/javascript_natives/index.js +57 -194
- package/scripts/javascript_natives/package.json +8 -3
- package/scripts/javascript_natives/test.d.ts +9 -0
- package/scripts/javascript_natives/tests.js +195 -0
- package/scripts/whitespaces.txt +1 -0
@@ -165,12 +165,14 @@ const UIStrings = {
|
|
165
165
|
newStyleRule: 'New Style Rule',
|
166
166
|
/**
|
167
167
|
*@description Text that is announced by the screen reader when the user focuses on an input field for entering the name of a CSS property in the Styles panel
|
168
|
+
*@example {margin} PH1
|
168
169
|
*/
|
169
|
-
cssPropertyName: '`CSS` property name',
|
170
|
+
cssPropertyName: '`CSS` property name: {PH1}',
|
170
171
|
/**
|
171
172
|
*@description Text that is announced by the screen reader when the user focuses on an input field for entering the value of a CSS property in the Styles panel
|
173
|
+
*@example {10px} PH1
|
172
174
|
*/
|
173
|
-
cssPropertyValue: '`CSS` property value',
|
175
|
+
cssPropertyValue: '`CSS` property value: {PH1}',
|
174
176
|
/**
|
175
177
|
*@description Text that is announced by the screen reader when the user focuses on an input field for editing the name of a CSS selector in the Styles panel
|
176
178
|
*/
|
@@ -3091,7 +3093,7 @@ export class StylesSidebarPropertyRenderer {
|
|
3091
3093
|
|
3092
3094
|
renderName(): Element {
|
3093
3095
|
const nameElement = document.createElement('span');
|
3094
|
-
UI.ARIAUtils.setAccessibleName(nameElement, i18nString(UIStrings.cssPropertyName));
|
3096
|
+
UI.ARIAUtils.setAccessibleName(nameElement, i18nString(UIStrings.cssPropertyName, {PH1: this.propertyName}));
|
3095
3097
|
nameElement.className = 'webkit-css-property';
|
3096
3098
|
nameElement.textContent = this.propertyName;
|
3097
3099
|
nameElement.normalize();
|
@@ -3100,7 +3102,7 @@ export class StylesSidebarPropertyRenderer {
|
|
3100
3102
|
|
3101
3103
|
renderValue(): Element {
|
3102
3104
|
const valueElement = document.createElement('span');
|
3103
|
-
UI.ARIAUtils.setAccessibleName(valueElement, i18nString(UIStrings.cssPropertyValue));
|
3105
|
+
UI.ARIAUtils.setAccessibleName(valueElement, i18nString(UIStrings.cssPropertyValue, {PH1: this.propertyValue}));
|
3104
3106
|
valueElement.className = 'value';
|
3105
3107
|
if (!this.propertyValue) {
|
3106
3108
|
return valueElement;
|
@@ -364,9 +364,16 @@ export class UISourceCodeFrame extends
|
|
364
364
|
this.unloadUISourceCode();
|
365
365
|
this.persistenceBinding = binding;
|
366
366
|
this.initializeUISourceCode();
|
367
|
+
this.reloadMessages();
|
367
368
|
this.reloadPlugins();
|
368
369
|
}
|
369
370
|
|
371
|
+
private reloadMessages(): void {
|
372
|
+
const messages = [...this.allMessages()];
|
373
|
+
const {editor} = this.textEditor;
|
374
|
+
editor.dispatch({effects: setRowMessages.of(RowMessages.create(messages))});
|
375
|
+
}
|
376
|
+
|
370
377
|
private updateStyle(): void {
|
371
378
|
this.setEditable(this.canEditSourceInternal());
|
372
379
|
}
|
@@ -260,7 +260,7 @@
|
|
260
260
|
--color-error-background: hsl(0deg 100% 8%);
|
261
261
|
--color-checkbox-accent-color: rgb(255 165 0);
|
262
262
|
/* Colors for styling inputs */
|
263
|
-
--color-input-outline: rgb(
|
263
|
+
--color-input-outline: rgb(60 64 67);
|
264
264
|
--color-input-outline-active: rgb(138 180 248);
|
265
265
|
--color-input-outline-error: rgb(242 139 130);
|
266
266
|
--color-input-outline-disabled: rgba(189 193 198 / 20%);
|
package/package.json
CHANGED
@@ -0,0 +1,103 @@
|
|
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
|
+
// @ts-check
|
6
|
+
|
7
|
+
const path = require('path');
|
8
|
+
|
9
|
+
/**
|
10
|
+
* `path.dirname` does not include trailing slashes. If we would always
|
11
|
+
* use `path.dirname` and then later perform comparisons on the paths that
|
12
|
+
* it returns, we could run into false positives. For example, given the
|
13
|
+
* the following two paths:
|
14
|
+
*
|
15
|
+
* front_end/timeline_model/TimelineModel.js
|
16
|
+
* front_end/timeline/Timeline.js
|
17
|
+
*
|
18
|
+
* And that would have the following values for `path.dirname`:
|
19
|
+
*
|
20
|
+
* front_end/timeline_model
|
21
|
+
* front_end/timeline
|
22
|
+
*
|
23
|
+
* If we would do a simple `.startswith` on the `path.dirname` of both of
|
24
|
+
* these paths, then the first path would start with the dirname of the
|
25
|
+
* second. However, they *are* part of different folders. To fix that problem,
|
26
|
+
* we need to force a path separator after each folder. That makes sure we
|
27
|
+
* and up with the following comparison of path dirnames:
|
28
|
+
*
|
29
|
+
* front_end/timeline_model/
|
30
|
+
* front_end/timeline/
|
31
|
+
*
|
32
|
+
* Now, the first path does *not* start with the second one, as expected.
|
33
|
+
*
|
34
|
+
* @param {string} file
|
35
|
+
* @return {string}
|
36
|
+
*/
|
37
|
+
function dirnameWithSeparator(file) {
|
38
|
+
return path.dirname(file) + path.sep;
|
39
|
+
}
|
40
|
+
|
41
|
+
function devtoolsPlugin(source, importer) {
|
42
|
+
if (!importer) {
|
43
|
+
return null;
|
44
|
+
}
|
45
|
+
const currentDirectory = path.normalize(dirnameWithSeparator(importer));
|
46
|
+
const importedFilelocation = path.normalize(path.join(currentDirectory, source));
|
47
|
+
const importedFileDirectory = dirnameWithSeparator(importedFilelocation);
|
48
|
+
|
49
|
+
// Generated files are part of other directories, as they are only imported once
|
50
|
+
if (path.basename(importedFileDirectory) === 'generated') {
|
51
|
+
return null;
|
52
|
+
}
|
53
|
+
|
54
|
+
// An import is considered external (and therefore a separate
|
55
|
+
// bundle) if its filename matches its immediate parent's folder
|
56
|
+
// name (without the extension). For example:
|
57
|
+
// import * as Components from './components/components.js' = external
|
58
|
+
// import * as UI from '../ui/ui.js' = external
|
59
|
+
// import * as LitHtml from '../third_party/lit-html/lit-html.js' = external
|
60
|
+
// import {DataGrid} from './components/DataGrid.js' = not external
|
61
|
+
// import * as Components from './components/foo.js' = not external
|
62
|
+
|
63
|
+
// Note that we can't do a simple check for only `third_party`, as in Chromium
|
64
|
+
// our full path is `third_party/devtools-frontend/src/`, which thus *always*
|
65
|
+
// includes third_party. It also not possible to use the current directory
|
66
|
+
// as a check for the import, as the import will be different in Chromium and
|
67
|
+
// would therefore not match the path of `__dirname`.
|
68
|
+
// These should be removed because the new heuristic _should_ deal with these
|
69
|
+
// e.g. it'll pick up third_party/lit-html/lit-html.js is its own entrypoint
|
70
|
+
|
71
|
+
// Puppeteer has dynamic imports in its build gated on an ifNode
|
72
|
+
// flag, but our Rollup config doesn't know about that and tries
|
73
|
+
// to parse dynamic import('fs'). Let's ignore Puppeteer for now.
|
74
|
+
// The long term plan is probably for Puppeteer to ship a web
|
75
|
+
// bundle anyway. See go/pptr-agnostify for details.
|
76
|
+
if (importedFileDirectory.includes(path.join('front_end', 'third_party', 'puppeteer'))) {
|
77
|
+
return null;
|
78
|
+
}
|
79
|
+
|
80
|
+
// The CodeMirror addons look like bundles (addon/comment/comment.js) but are not.
|
81
|
+
if (importedFileDirectory.includes(path.join('front_end', 'third_party', 'codemirror', 'package'))) {
|
82
|
+
return null;
|
83
|
+
}
|
84
|
+
|
85
|
+
// The LightHouse bundle shouldn't be processed by `terser` again, as it is uniquely built
|
86
|
+
if (importedFilelocation.includes(path.join('front_end', 'third_party', 'lighthouse', 'lighthouse-dt-bundle.js'))) {
|
87
|
+
return {
|
88
|
+
id: importedFilelocation,
|
89
|
+
external: true,
|
90
|
+
};
|
91
|
+
}
|
92
|
+
|
93
|
+
const importedFileName = path.basename(importedFilelocation, '.js');
|
94
|
+
const importedFileParentDirectory = path.basename(path.dirname(importedFilelocation));
|
95
|
+
const isExternal = importedFileName === importedFileParentDirectory;
|
96
|
+
|
97
|
+
return {
|
98
|
+
id: importedFilelocation,
|
99
|
+
external: isExternal,
|
100
|
+
};
|
101
|
+
}
|
102
|
+
|
103
|
+
module.exports = {devtoolsPlugin};
|
@@ -5,9 +5,9 @@
|
|
5
5
|
import("./node.gni")
|
6
6
|
import("./vars.gni")
|
7
7
|
|
8
|
-
template("
|
8
|
+
template("bundle") {
|
9
9
|
assert(defined(invoker.entrypoint),
|
10
|
-
"You must define the 'entrypoint' for a
|
10
|
+
"You must define the 'entrypoint' for a bundle target")
|
11
11
|
|
12
12
|
node_action(target_name) {
|
13
13
|
script = "node_modules/rollup/dist/bin/rollup"
|
@@ -4,9 +4,9 @@
|
|
4
4
|
|
5
5
|
import("//build/toolchain/rbe.gni")
|
6
6
|
import("../../../third_party/typescript/typescript.gni")
|
7
|
+
import("./bundle.gni")
|
7
8
|
import("./copy.gni")
|
8
9
|
import("./node.gni")
|
9
|
-
import("./rollup.gni")
|
10
10
|
import("./vars.gni")
|
11
11
|
|
12
12
|
# This defines an entrypoint for DevTools, which uses Rollup
|
@@ -19,7 +19,7 @@ import("./vars.gni")
|
|
19
19
|
# import in `core/root/Runtime.js`.
|
20
20
|
template("devtools_entrypoint") {
|
21
21
|
assert(defined(invoker.entrypoint),
|
22
|
-
"You must define the 'entrypoint' for a
|
22
|
+
"You must define the 'entrypoint' for a bundle target")
|
23
23
|
|
24
24
|
_entrypoint_target_name = "devtools_entrypoint-" + target_name
|
25
25
|
|
@@ -79,7 +79,7 @@ template("devtools_entrypoint") {
|
|
79
79
|
# make sure that the GN dependency graph correctly corresponds
|
80
80
|
# to that. Therefore, the graph looks like so:
|
81
81
|
#
|
82
|
-
# copy -> ts_library -> fix-tsconfig ->
|
82
|
+
# copy -> ts_library -> fix-tsconfig -> bundle
|
83
83
|
#
|
84
84
|
_entrypoint_output_file_name = get_path_info(invoker.entrypoint, "name")
|
85
85
|
_output_file_name =
|
@@ -88,7 +88,7 @@ template("devtools_entrypoint") {
|
|
88
88
|
_prebundle_output_file_name = "$_output_file_name.js"
|
89
89
|
|
90
90
|
_copy_target_name = _entrypoint_target_name + "-copy"
|
91
|
-
|
91
|
+
_bundle_target_name = _entrypoint_target_name + "-bundle"
|
92
92
|
_prebundle_target_name = _entrypoint_target_name + "-tsconfig"
|
93
93
|
|
94
94
|
node_action(_copy_target_name) {
|
@@ -127,12 +127,12 @@ template("devtools_entrypoint") {
|
|
127
127
|
}
|
128
128
|
}
|
129
129
|
|
130
|
-
|
130
|
+
bundle(_bundle_target_name) {
|
131
131
|
entrypoint = _prebundle_output_file_name
|
132
132
|
|
133
133
|
output_file_location = _entrypoint_gen_file_location
|
134
134
|
|
135
|
-
# Since
|
135
|
+
# Since bundles both the entrypoint and the files it imports,
|
136
136
|
# we have to make sure that, when you change a file it imports, we
|
137
137
|
# retrigger Rollup. Since the `invoker.deps` is a reference to the
|
138
138
|
# `ts_library` that compiles all of the files it imports, we have to
|
@@ -161,8 +161,8 @@ template("devtools_entrypoint") {
|
|
161
161
|
]
|
162
162
|
|
163
163
|
public_deps = [
|
164
|
+
":$_bundle_target_name",
|
164
165
|
":$_prebundle_target_name",
|
165
|
-
":$_rollup_target_name",
|
166
166
|
]
|
167
167
|
|
168
168
|
outputs = [ "$target_gen_dir/$_entrypoint_output_file_name.d.ts" ]
|
@@ -171,8 +171,8 @@ template("devtools_entrypoint") {
|
|
171
171
|
_devtools_entrypoint_deps += [ ":$_generated_declaration_target_name" ]
|
172
172
|
} else {
|
173
173
|
_devtools_entrypoint_deps += [
|
174
|
+
":$_bundle_target_name",
|
174
175
|
":$_prebundle_target_name",
|
175
|
-
":$_rollup_target_name",
|
176
176
|
]
|
177
177
|
}
|
178
178
|
}
|
@@ -5,41 +5,10 @@
|
|
5
5
|
// @ts-check
|
6
6
|
|
7
7
|
import {defaultStrategy} from 'minify-html-literals/src/strategy'; // eslint-disable-line rulesdir/es_modules_import
|
8
|
-
import * as path from 'path';
|
9
8
|
import minifyHTML from 'rollup-plugin-minify-html-template-literals';
|
10
9
|
import {terser} from 'rollup-plugin-terser';
|
11
10
|
|
12
|
-
|
13
|
-
* `path.dirname` does not include trailing slashes. If we would always
|
14
|
-
* use `path.dirname` and then later perform comparisons on the paths that
|
15
|
-
* it returns, we could run into false positives. For example, given the
|
16
|
-
* the following two paths:
|
17
|
-
*
|
18
|
-
* front_end/timeline_model/TimelineModel.js
|
19
|
-
* front_end/timeline/Timeline.js
|
20
|
-
*
|
21
|
-
* And that would have the following values for `path.dirname`:
|
22
|
-
*
|
23
|
-
* front_end/timeline_model
|
24
|
-
* front_end/timeline
|
25
|
-
*
|
26
|
-
* If we would do a simple `.startswith` on the `path.dirname` of both of
|
27
|
-
* these paths, then the first path would start with the dirname of the
|
28
|
-
* second. However, they *are* part of different folders. To fix that problem,
|
29
|
-
* we need to force a path separator after each folder. That makes sure we
|
30
|
-
* and up with the following comparison of path dirnames:
|
31
|
-
*
|
32
|
-
* front_end/timeline_model/
|
33
|
-
* front_end/timeline/
|
34
|
-
*
|
35
|
-
* Now, the first path does *not* start with the second one, as expected.
|
36
|
-
*
|
37
|
-
* @param {string} file
|
38
|
-
* @return {string}
|
39
|
-
*/
|
40
|
-
function dirnameWithSeparator(file) {
|
41
|
-
return path.dirname(file) + path.sep;
|
42
|
-
}
|
11
|
+
const devtools_plugin = require('./devtools_plugin.js');
|
43
12
|
|
44
13
|
/**
|
45
14
|
* @type {import("minify-html-literals").Strategy<import("html-minifier").Options, unknown>}
|
@@ -91,67 +60,8 @@ export default commandLineArgs => ({
|
|
91
60
|
{
|
92
61
|
name: 'devtools-plugin',
|
93
62
|
resolveId(source, importer) {
|
94
|
-
|
95
|
-
|
96
|
-
}
|
97
|
-
const currentDirectory = path.normalize(dirnameWithSeparator(importer));
|
98
|
-
const importedFilelocation = path.normalize(path.join(currentDirectory, source));
|
99
|
-
const importedFileDirectory = dirnameWithSeparator(importedFilelocation);
|
100
|
-
|
101
|
-
// Generated files are part of other directories, as they are only imported once
|
102
|
-
if (path.basename(importedFileDirectory) === 'generated') {
|
103
|
-
return null;
|
104
|
-
}
|
105
|
-
|
106
|
-
// An import is considered external (and therefore a separate
|
107
|
-
// bundle) if its filename matches its immediate parent's folder
|
108
|
-
// name (without the extension). For example:
|
109
|
-
// import * as Components from './components/components.js' = external
|
110
|
-
// import * as UI from '../ui/ui.js' = external
|
111
|
-
// import * as LitHtml from '../third_party/lit-html/lit-html.js' = external
|
112
|
-
// import {DataGrid} from './components/DataGrid.js' = not external
|
113
|
-
// import * as Components from './components/foo.js' = not external
|
114
|
-
|
115
|
-
// Note that we can't do a simple check for only `third_party`, as in Chromium
|
116
|
-
// our full path is `third_party/devtools-frontend/src/`, which thus *always*
|
117
|
-
// includes third_party. It also not possible to use the current directory
|
118
|
-
// as a check for the import, as the import will be different in Chromium and
|
119
|
-
// would therefore not match the path of `__dirname`.
|
120
|
-
// These should be removed because the new heuristic _should_ deal with these
|
121
|
-
// e.g. it'll pick up third_party/lit-html/lit-html.js is its own entrypoint
|
122
|
-
|
123
|
-
// Puppeteer has dynamic imports in its build gated on an ifNode
|
124
|
-
// flag, but our Rollup config doesn't know about that and tries
|
125
|
-
// to parse dynamic import('fs'). Let's ignore Puppeteer for now.
|
126
|
-
// The long term plan is probably for Puppeteer to ship a web
|
127
|
-
// bundle anyway. See go/pptr-agnostify for details.
|
128
|
-
if (importedFileDirectory.includes(path.join('front_end', 'third_party', 'puppeteer'))) {
|
129
|
-
return null;
|
130
|
-
}
|
131
|
-
|
132
|
-
// The CodeMirror addons look like bundles (addon/comment/comment.js) but are not.
|
133
|
-
if (importedFileDirectory.includes(path.join('front_end', 'third_party', 'codemirror', 'package'))) {
|
134
|
-
return null;
|
135
|
-
}
|
136
|
-
|
137
|
-
// The LightHouse bundle shouldn't be processed by `terser` again, as it is uniquely built
|
138
|
-
if (importedFilelocation.includes(
|
139
|
-
path.join('front_end', 'third_party', 'lighthouse', 'lighthouse-dt-bundle.js'))) {
|
140
|
-
return {
|
141
|
-
id: importedFilelocation,
|
142
|
-
external: true,
|
143
|
-
};
|
144
|
-
}
|
145
|
-
|
146
|
-
const importedFileName = path.basename(importedFilelocation, '.js');
|
147
|
-
const importedFileParentDirectory = path.basename(path.dirname(importedFilelocation));
|
148
|
-
const isExternal = importedFileName === importedFileParentDirectory;
|
149
|
-
|
150
|
-
return {
|
151
|
-
id: importedFilelocation,
|
152
|
-
external: isExternal,
|
153
|
-
};
|
154
|
-
}
|
63
|
+
return devtools_plugin.devtoolsPlugin(source, importer);
|
64
|
+
},
|
155
65
|
},
|
156
66
|
]
|
157
67
|
});
|
@@ -59,8 +59,9 @@ function isInChromiumDirectory() {
|
|
59
59
|
}
|
60
60
|
|
61
61
|
const normalizedPath = PATH_TO_EXECUTED_FILE.split(path.sep).join('/');
|
62
|
-
const
|
63
|
-
const
|
62
|
+
const devtoolsPath = 'src/third_party/devtools-frontend';
|
63
|
+
const isInChromium = normalizedPath.includes(devtoolsPath);
|
64
|
+
const potentialChromiumDir = PATH_TO_EXECUTED_FILE.substring(0, PATH_TO_EXECUTED_FILE.indexOf(devtoolsPath));
|
64
65
|
const result = {isInChromium, chromiumDirectory: potentialChromiumDir};
|
65
66
|
_lookUpCaches.set('chromium', result);
|
66
67
|
return result;
|
@@ -0,0 +1,211 @@
|
|
1
|
+
// Copyright 2021 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
|
+
// @ts-check
|
6
|
+
|
7
|
+
import * as fs from 'fs';
|
8
|
+
import * as path from 'path';
|
9
|
+
import {fileURLToPath} from 'url';
|
10
|
+
|
11
|
+
/** @type {Map<string, Map<string, string[][]>>} */
|
12
|
+
const methods = new Map();
|
13
|
+
|
14
|
+
export function clearState() {
|
15
|
+
methods.clear();
|
16
|
+
}
|
17
|
+
|
18
|
+
export function parseTSFunction(func, node) {
|
19
|
+
if (!func.name.escapedText) {
|
20
|
+
return;
|
21
|
+
}
|
22
|
+
|
23
|
+
const args = func.parameters
|
24
|
+
.map(p => {
|
25
|
+
let text = p.name.escapedText;
|
26
|
+
if (p.questionToken) {
|
27
|
+
text = '?' + text;
|
28
|
+
}
|
29
|
+
if (p.dotDotDotToken) {
|
30
|
+
text = '...' + text;
|
31
|
+
}
|
32
|
+
return text;
|
33
|
+
})
|
34
|
+
.filter(x => x !== 'this');
|
35
|
+
storeMethod(node.name.text, func.name.escapedText, args);
|
36
|
+
}
|
37
|
+
|
38
|
+
/**
|
39
|
+
* @param {WebIDL2.IDLRootType} thing
|
40
|
+
* */
|
41
|
+
export function walkRoot(thing) {
|
42
|
+
switch (thing.type) {
|
43
|
+
case 'interface':
|
44
|
+
walkInterface(thing);
|
45
|
+
break;
|
46
|
+
case 'interface mixin':
|
47
|
+
case 'namespace':
|
48
|
+
walkMembers(thing);
|
49
|
+
break;
|
50
|
+
}
|
51
|
+
}
|
52
|
+
|
53
|
+
/**
|
54
|
+
* @param {WebIDL2.InterfaceType} thing
|
55
|
+
* */
|
56
|
+
function walkInterface(thing) {
|
57
|
+
thing.members.forEach(member => {
|
58
|
+
switch (member.type) {
|
59
|
+
case 'constructor':
|
60
|
+
storeMethod('Window', thing.name, member.arguments.map(argName));
|
61
|
+
break;
|
62
|
+
case 'operation':
|
63
|
+
handleOperation(member);
|
64
|
+
}
|
65
|
+
});
|
66
|
+
const namedConstructor = thing.extAttrs.find(extAttr => extAttr.name === 'NamedConstructor');
|
67
|
+
if (namedConstructor && namedConstructor.arguments) {
|
68
|
+
storeMethod('Window', namedConstructor.rhs.value, namedConstructor.arguments.map(argName));
|
69
|
+
}
|
70
|
+
}
|
71
|
+
|
72
|
+
/**
|
73
|
+
* @param {WebIDL2.NamespaceType | WebIDL2.InterfaceMixinType} thing
|
74
|
+
* */
|
75
|
+
function walkMembers(thing) {
|
76
|
+
thing.members.forEach(member => {
|
77
|
+
if (member.type === 'operation') {
|
78
|
+
handleOperation(member);
|
79
|
+
}
|
80
|
+
});
|
81
|
+
}
|
82
|
+
|
83
|
+
/**
|
84
|
+
* @param {WebIDL2.OperationMemberType} member
|
85
|
+
* */
|
86
|
+
function handleOperation(member) {
|
87
|
+
storeMethod(
|
88
|
+
member.special === 'static' ? (parent.name + 'Constructor') : member.parent.name, member.name,
|
89
|
+
member.arguments.map(argName));
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* @param {WebIDL2.Argument} a
|
94
|
+
* */
|
95
|
+
function argName(a) {
|
96
|
+
let name = a.name;
|
97
|
+
if (a.optional) {
|
98
|
+
name = '?' + name;
|
99
|
+
}
|
100
|
+
if (a.variadic) {
|
101
|
+
name = '...' + name;
|
102
|
+
}
|
103
|
+
return name;
|
104
|
+
}
|
105
|
+
|
106
|
+
/**
|
107
|
+
* @param {string} parent
|
108
|
+
* @param {string} name
|
109
|
+
* @param {Array<string>} args
|
110
|
+
* */
|
111
|
+
function storeMethod(parent, name, args) {
|
112
|
+
if (!methods.has(name)) {
|
113
|
+
methods.set(name, new Map());
|
114
|
+
}
|
115
|
+
const method = methods.get(name);
|
116
|
+
if (!method.has(parent)) {
|
117
|
+
method.set(parent, []);
|
118
|
+
}
|
119
|
+
method.get(parent).push(args);
|
120
|
+
}
|
121
|
+
|
122
|
+
export function postProcess(dryRun = false) {
|
123
|
+
for (const name of methods.keys()) {
|
124
|
+
// We use the set jsonParents to track the set of different signatures across parent for this function name.
|
125
|
+
// If all signatures are identical, we leave out the parent and emit a single NativeFunction entry without receiver.
|
126
|
+
const jsonParents = new Set();
|
127
|
+
for (const [parent, signatures] of methods.get(name)) {
|
128
|
+
signatures.sort((a, b) => a.length - b.length);
|
129
|
+
const filteredSignatures = [];
|
130
|
+
for (const signature of signatures) {
|
131
|
+
const smallerIndex = filteredSignatures.findIndex(smaller => startsTheSame(smaller, signature));
|
132
|
+
if (smallerIndex !== -1) {
|
133
|
+
filteredSignatures[smallerIndex] = (signature.map((arg, index) => {
|
134
|
+
const otherArg = filteredSignatures[smallerIndex][index];
|
135
|
+
if (otherArg) {
|
136
|
+
return otherArg.length > arg.length ? otherArg : arg;
|
137
|
+
}
|
138
|
+
if (arg.startsWith('?') || arg.startsWith('...')) {
|
139
|
+
return arg;
|
140
|
+
}
|
141
|
+
return '?' + arg;
|
142
|
+
}));
|
143
|
+
} else {
|
144
|
+
filteredSignatures.push(signature);
|
145
|
+
}
|
146
|
+
}
|
147
|
+
|
148
|
+
function startsTheSame(smaller, bigger) {
|
149
|
+
for (let i = 0; i < smaller.length; i++) {
|
150
|
+
const withoutQuestion = str => /[\?]?(.*)/.exec(str)[1];
|
151
|
+
if (withoutQuestion(smaller[i]) !== withoutQuestion(bigger[i])) {
|
152
|
+
return false;
|
153
|
+
}
|
154
|
+
}
|
155
|
+
return true;
|
156
|
+
}
|
157
|
+
|
158
|
+
methods.get(name).set(parent, filteredSignatures);
|
159
|
+
jsonParents.add(JSON.stringify(filteredSignatures));
|
160
|
+
}
|
161
|
+
// If all parents had the same signature for this name, we put a `*` as parent for this entry.
|
162
|
+
if (jsonParents.size === 1) {
|
163
|
+
methods.set(name, new Map([['*', JSON.parse(jsonParents.values().next().value)]]));
|
164
|
+
}
|
165
|
+
for (const [parent, signatures] of methods.get(name)) {
|
166
|
+
if (signatures.length === 1 && !signatures[0].length) {
|
167
|
+
methods.get(name).delete(parent);
|
168
|
+
}
|
169
|
+
}
|
170
|
+
if (methods.get(name).size === 0) {
|
171
|
+
methods.delete(name);
|
172
|
+
}
|
173
|
+
}
|
174
|
+
const functions = [];
|
175
|
+
for (const [name, method] of methods) {
|
176
|
+
if (method.has('*')) {
|
177
|
+
// All parents had the same signature so we emit an entry without receiver.
|
178
|
+
functions.push({name, signatures: method.get('*')});
|
179
|
+
} else {
|
180
|
+
for (const [parent, signatures] of method) {
|
181
|
+
if (parent.endsWith('Constructor')) {
|
182
|
+
functions.push(
|
183
|
+
{name, signatures, static: true, receiver: parent.substring(0, parent.length - 'Constructor'.length)});
|
184
|
+
} else {
|
185
|
+
functions.push({name, signatures, receiver: parent});
|
186
|
+
}
|
187
|
+
}
|
188
|
+
}
|
189
|
+
}
|
190
|
+
const output = `export const NativeFunctions = [\n${
|
191
|
+
functions
|
192
|
+
.map(
|
193
|
+
entry =>
|
194
|
+
` {\n${Object.entries(entry).map(kv => ` ${kv[0]}: ${JSON.stringify(kv[1])}`).join(',\n')}\n }`)
|
195
|
+
.join(',\n')}\n];`;
|
196
|
+
|
197
|
+
if (dryRun) {
|
198
|
+
return output;
|
199
|
+
}
|
200
|
+
|
201
|
+
fs.writeFileSync(
|
202
|
+
(new URL('../../front_end/models/javascript_metadata/NativeFunctions.js', import.meta.url)).pathname,
|
203
|
+
`// Copyright 2020 The Chromium Authors. All rights reserved.
|
204
|
+
// Use of this source code is governed by a BSD-style license that can be
|
205
|
+
// found in the LICENSE file.
|
206
|
+
// Generated from ${
|
207
|
+
path.relative(path.join(fileURLToPath(import.meta.url), '..', '..'), fileURLToPath(import.meta.url))}
|
208
|
+
|
209
|
+
${output}
|
210
|
+
`);
|
211
|
+
}
|