chrome-devtools-frontend 1.0.1661063 → 1.0.1662289
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/.agents/skills/evaluate-ai-css-completion/SKILL.md +19 -9
- package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +44 -16
- package/.agents/skills/migrate-chromium-test/SKILL.md +7 -2
- package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +7 -1
- package/docs/ui_engineering.md +75 -42
- package/front_end/core/common/SettingRegistration.ts +0 -5
- package/front_end/core/platform/ArrayUtilities.ts +10 -0
- package/front_end/foundation/README.md +86 -0
- package/front_end/generated/InspectorBackendCommands.ts +4 -5
- package/front_end/generated/protocol.ts +20 -38
- package/front_end/models/bindings/CompilerScriptMapping.ts +5 -4
- package/front_end/models/bindings/ContentProviderBasedProject.ts +2 -3
- package/front_end/models/bindings/DebuggerLanguagePlugins.ts +4 -3
- package/front_end/models/bindings/DefaultScriptMapping.ts +3 -3
- package/front_end/models/bindings/NetworkProject.ts +10 -1
- package/front_end/models/bindings/ResourceMapping.ts +4 -3
- package/front_end/models/bindings/ResourceScriptMapping.ts +2 -1
- package/front_end/models/bindings/SASSSourceMapping.ts +4 -3
- package/front_end/models/bindings/StylesSourceMapping.ts +3 -3
- package/front_end/models/extensions/RecorderExtensionEndpoint.ts +7 -5
- package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +14 -5
- package/front_end/models/issues_manager/CheckFormsIssuesTrigger.ts +8 -9
- package/front_end/models/issues_manager/RelatedIssue.ts +10 -8
- package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
- package/front_end/models/logs/RequestResolver.ts +2 -2
- package/front_end/models/persistence/AutomaticFileSystemWorkspaceBinding.ts +0 -5
- package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +1 -1
- package/front_end/models/trace/handlers/FramesHandler.ts +4 -7
- package/front_end/models/trace/handlers/ModelHandlers.ts +0 -1
- package/front_end/models/trace/handlers/RendererHandler.ts +2 -15
- package/front_end/models/trace/handlers/Threads.ts +4 -11
- package/front_end/models/trace/insights/DOMSize.ts +1 -1
- package/front_end/models/workspace/WorkspaceImpl.ts +1 -10
- package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -44
- package/front_end/panels/application/CookieItemsView.ts +1 -1
- package/front_end/panels/application/FrameDetailsView.ts +1 -1
- package/front_end/panels/application/ServiceWorkersView.ts +494 -411
- package/front_end/panels/application/application.ts +0 -6
- package/front_end/panels/application/components/components.ts +0 -2
- package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +1 -1
- package/front_end/panels/application/serviceWorkersView.css +40 -4
- package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +147 -123
- package/front_end/panels/common/ExtensionServer.ts +3 -3
- package/front_end/panels/console/ConsoleView.ts +1 -1
- package/front_end/panels/emulation/DeviceModeView.ts +139 -70
- package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
- package/front_end/panels/lighthouse/LighthouseProtocolService.ts +53 -12
- package/front_end/panels/network/RequestPayloadView.ts +77 -39
- package/front_end/panels/network/components/ResponseHeaderSection.ts +5 -3
- package/front_end/panels/profiler/HeapSnapshotView.ts +2 -2
- package/front_end/panels/recorder/RecorderController.ts +213 -49
- package/front_end/panels/recorder/RecorderPanel.ts +1 -1
- package/front_end/panels/sources/SourcesNavigator.ts +3 -2
- package/front_end/panels/sources/SourcesView.ts +0 -17
- package/front_end/panels/sources/UISourceCodeFrame.ts +42 -0
- package/front_end/panels/sources/sources.ts +0 -2
- package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -17
- package/front_end/panels/timeline/ThreadAppender.ts +0 -106
- package/front_end/third_party/chromium/README.chromium +1 -1
- package/front_end/ui/components/report_view/ReportView.ts +11 -5
- package/front_end/ui/components/report_view/report.css +4 -1
- package/front_end/ui/legacy/TextPrompt.ts +49 -1
- package/front_end/ui/legacy/UIUtils.ts +11 -1
- package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +2 -2
- package/front_end/ui/legacy/textPrompt.css +10 -0
- package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
- package/package.json +1 -1
- package/front_end/models/trace/handlers/AuctionWorkletsHandler.ts +0 -183
- package/front_end/panels/application/InterestGroupStorageModel.ts +0 -81
- package/front_end/panels/application/InterestGroupStorageView.ts +0 -151
- package/front_end/panels/application/InterestGroupTreeElement.ts +0 -65
- package/front_end/panels/application/components/InterestGroupAccessGrid.ts +0 -135
- package/front_end/panels/application/components/interestGroupAccessGrid.css +0 -27
- package/front_end/panels/application/interestGroupStorageView.css +0 -9
- package/front_end/panels/sources/InplaceFormatterEditorAction.ts +0 -149
|
@@ -69,12 +69,12 @@ import * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_compl
|
|
|
69
69
|
|
|
70
70
|
## Step 2: Launch Chrome with Local DevTools
|
|
71
71
|
|
|
72
|
-
Use the `npm start` script to build DevTools, launch Chrome Canary with remote debugging, and
|
|
72
|
+
Use the `npm start` script to build DevTools, launch Chrome Canary with remote debugging, and disable watch mode (so we build manually instead).
|
|
73
73
|
|
|
74
74
|
You must specify a persistent user data directory so you can log in once and reuse the session:
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
npm start -- --browser=canary --remote-debugging-port=9222 --user-data-dir=/tmp/devtools-ai-evaluate-css-completion
|
|
77
|
+
npm start -- --no-watch --browser=canary --remote-debugging-port=9222 --user-data-dir=/tmp/devtools-ai-evaluate-css-completion
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
> [!IMPORTANT]
|
|
@@ -82,14 +82,18 @@ npm start -- --browser=canary --remote-debugging-port=9222 --user-data-dir=/tmp/
|
|
|
82
82
|
> After launching Chrome, you MUST print the following checklist to the user and wait for their explicit confirmation before running the evaluation script:
|
|
83
83
|
> 1. Sign in to Chrome with your corporate account in the new window.
|
|
84
84
|
> 2. Open DevTools, go to **Settings** (gear icon) > **AI Innovations**, and ensure **Code Completions** is enabled.
|
|
85
|
-
> 3.
|
|
86
|
-
|
|
87
|
-
* Keep this process running (it will watch and rebuild changes).
|
|
85
|
+
> 3. Focus the DevTools window and press `Cmd + Option + I` (on macOS) or `Ctrl + Shift + I` (on Windows/Linux) to open DevTools-on-DevTools (inspecting DevTools itself). Go to the Network tab of the second DevTools instance and check the **Disable cache** checkbox. This ensures your local builds are loaded instead of cached versions when reloaded.
|
|
86
|
+
> 4. Ask the user to reply when they are ready.
|
|
88
87
|
|
|
89
88
|
## Step 3: Configure Test Cases
|
|
90
89
|
|
|
91
90
|
Before running the evaluation, configure your test cases directly in `front_end/entrypoints/devtools_app/devtools_app.ts` inside the `getCases()` method of the patch you applied in Step 1.
|
|
92
91
|
|
|
92
|
+
After modifying the test cases, manually run a build to compile the changes:
|
|
93
|
+
```bash
|
|
94
|
+
autoninja -C out/Default
|
|
95
|
+
```
|
|
96
|
+
|
|
93
97
|
For example, to test spacing sensitivity, update the returned array:
|
|
94
98
|
```typescript
|
|
95
99
|
getCases() {
|
|
@@ -118,12 +122,18 @@ Run the Puppeteer script to execute the evaluation:
|
|
|
118
122
|
node .agents/skills/evaluate-ai-css-completion/scripts/evaluate.js
|
|
119
123
|
```
|
|
120
124
|
|
|
121
|
-
> [!IMPORTANT]
|
|
122
|
-
> **Instructions for the Agent:**
|
|
123
|
-
> Since the evaluation script runs asynchronously as a background task, you MUST use the `schedule` tool to check the log file of the evaluation task every 10 seconds until it completes. Do not poll using status checks in a loop.
|
|
124
|
-
|
|
125
125
|
The script will connect to the DevTools instance, trigger completions, and output the results.
|
|
126
126
|
|
|
127
|
+
### Debugging and Diagnostics
|
|
128
|
+
|
|
129
|
+
If you get 0% trigger rate or unexpected errors, check the following:
|
|
130
|
+
|
|
131
|
+
1. **Browser Logs:** The evaluation script forwards browser console logs (prefixed with `BROWSER LOG:`). Look for these in your terminal.
|
|
132
|
+
2. **Host Config:** The script prints the DevTools `Host Config` at the start. Verify that `"devToolsAiCodeCompletionStyles": { "enabled": true }` is present.
|
|
133
|
+
3. **GCA/AIDA Logs:** The script automatically enables verbose AIDA logging. Look for `GCA request succeeded:` in the logs:
|
|
134
|
+
* If the response has `"usageMetadata": { "candidatesTokenCount": X }` (where X > 0) but the `candidates` array is missing or empty, the backend generated tokens but they were filtered out (e.g., due to safety classifiers or recitation blocks).
|
|
135
|
+
4. **Context Requirement:** The CSS model is sensitive to context. A minimal prompt like `h1 { font-s` might not trigger AIDA. Ensure your test cases have enough context in the `prefix` (e.g., several preceding CSS rules) or pass them via `additionalFiles` to simulate a larger stylesheet.
|
|
136
|
+
|
|
127
137
|
## Step 5: Revert the Patch
|
|
128
138
|
|
|
129
139
|
Once the evaluation is complete and you have recorded the results, revert the patch in `devtools_app.ts`:
|
|
@@ -27,8 +27,20 @@ async function runEvaluation() {
|
|
|
27
27
|
}
|
|
28
28
|
console.log('DevTools URL:', devtoolsPage.url());
|
|
29
29
|
|
|
30
|
+
devtoolsPage.on('console', msg => console.log('BROWSER LOG:', msg.text()));
|
|
31
|
+
|
|
30
32
|
console.log('Reloading DevTools to ensure patch is loaded...');
|
|
31
|
-
|
|
33
|
+
try {
|
|
34
|
+
await devtoolsPage.setCacheEnabled(false);
|
|
35
|
+
await devtoolsPage.reload({waitUntil: 'domcontentloaded', timeout: 10000});
|
|
36
|
+
} catch (e) {
|
|
37
|
+
console.log('Reload warning (proceeding anyway):', e.message);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
await devtoolsPage.evaluate(() => localStorage.setItem('debugAiCodeCompletionEnabled', 'true'));
|
|
41
|
+
await devtoolsPage.evaluate(() => localStorage.setItem('debugAiServicesEnabled', 'true'));
|
|
42
|
+
await devtoolsPage.evaluate(() => localStorage.setItem('debugAiAssistancePanelEnabled', 'true'));
|
|
43
|
+
await devtoolsPage.evaluate(() => localStorage.setItem('aiAssistanceStructuredLogEnabled', 'true'));
|
|
32
44
|
|
|
33
45
|
console.log('Waiting for DevTools to initialize and hook to be available...');
|
|
34
46
|
try {
|
|
@@ -38,29 +50,45 @@ async function runEvaluation() {
|
|
|
38
50
|
}
|
|
39
51
|
|
|
40
52
|
console.log('Fetching test cases from DevTools...');
|
|
53
|
+
const hostConfig = await devtoolsPage.evaluate(() => self.testCss.getHostConfig());
|
|
54
|
+
console.log('Host Config:', JSON.stringify(hostConfig, null, 2));
|
|
41
55
|
const testCases = await devtoolsPage.evaluate(() => self.testCss.getCases());
|
|
42
|
-
console.log(`Found ${testCases.length} test cases.`);
|
|
56
|
+
console.log(`Found ${testCases.length} test cases. Running each case 5 times.`);
|
|
43
57
|
|
|
44
|
-
let
|
|
58
|
+
let totalQueries = 0;
|
|
45
59
|
for (const tc of testCases) {
|
|
46
60
|
console.log(`\nRunning: ${tc.name}`);
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
let successCount = 0;
|
|
62
|
+
const suggestions = new Set();
|
|
63
|
+
|
|
64
|
+
for (let run = 1; run <= 5; run++) {
|
|
65
|
+
// Delay to avoid rate limiting
|
|
66
|
+
await new Promise(resolve => setTimeout(resolve, 1200));
|
|
67
|
+
try {
|
|
68
|
+
const result = await devtoolsPage.evaluate(async (url, prefix, suffix, additionalFiles) => {
|
|
69
|
+
return await self.testCss.evaluate(url, prefix, suffix, additionalFiles);
|
|
70
|
+
}, tc.url, tc.prefix, tc.suffix, tc.additionalFiles);
|
|
49
71
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
72
|
+
if (result.hasSuggestion) {
|
|
73
|
+
successCount++;
|
|
74
|
+
for (const s of result.suggestions) {
|
|
75
|
+
suggestions.add(s);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
} catch (e) {
|
|
79
|
+
console.error(` [Run ${run}] Failed:`, e.message);
|
|
80
|
+
}
|
|
54
81
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
totalQueries++;
|
|
83
|
+
if (totalQueries % 20 === 0) {
|
|
84
|
+
console.log('[INFO] Pausing for 8 seconds to prevent rate limiting...');
|
|
85
|
+
await new Promise(resolve => setTimeout(resolve, 8000));
|
|
86
|
+
}
|
|
58
87
|
}
|
|
59
88
|
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
console.log('
|
|
63
|
-
await new Promise(resolve => setTimeout(resolve, 10000));
|
|
89
|
+
console.log(` Result: [Success Rate: ${successCount}/5]`);
|
|
90
|
+
if (suggestions.size > 0) {
|
|
91
|
+
console.log(' Unique Suggestions:', Array.from(suggestions));
|
|
64
92
|
}
|
|
65
93
|
}
|
|
66
94
|
|
|
@@ -24,6 +24,8 @@ description: Use when migrating Chromium layout tests to DevTools unit tests
|
|
|
24
24
|
- If the test requires rendering, use `renderElementIntoDOM` to attach elements to a test container.
|
|
25
25
|
- If testing legacy components that require internal tokens or have private methods, try to use public APIs or events instead. If impossible, consider using structural typing workarounds or updating the component to be more testable.
|
|
26
26
|
- If tests require a repetitive setup extract it into a test helper function at the top of the file.
|
|
27
|
+
- Code comments must be full sentences ending with a period, except when the entire comment is a URL.
|
|
28
|
+
- Do not include comments of the form `// Replaces web test: http/tests/devtools/persistence/automapping-absolute-paths.js`. Since we plan to remove migrated web tests, these are not useful references.
|
|
27
29
|
|
|
28
30
|
4. **Update BUILD.gn**:
|
|
29
31
|
|
|
@@ -40,7 +42,10 @@ description: Use when migrating Chromium layout tests to DevTools unit tests
|
|
|
40
42
|
- If the newly added test does not test any logic from the component but just the getters from the mocked data explain to the user that this test does not add value and it's better to close it without adding the test. Provide a command without executing it to close the issue as "Fixed".
|
|
41
43
|
- If the newly added test uses too extensive mocks of CDP objects and methods explain to the user that the test should be foundation e2e test instead of unit test and we won't migrate it. Provide a command without executing it to close the issue as "Won't Fix (Infeasible)".
|
|
42
44
|
|
|
43
|
-
7.
|
|
45
|
+
7. Uploading a CL with your changes using the `version-control` skill. And **STRICTLY** follow the following additional template
|
|
46
|
+
- **Title**: `[test-migration] Migrating <chromium-test-name> to unit test`
|
|
47
|
+
- **Body**: `Migrated <link-to-chromium-test> to <devtools-test-name>`
|
|
48
|
+
- **Footer**: Use `Fixed: <issue number>` trailer instead of `Bug: <issue number>`.
|
|
44
49
|
|
|
45
50
|
## Example Conversion
|
|
46
51
|
|
|
@@ -69,7 +74,7 @@ import {describeWithEnvironment} from '../../../../testing/EnvironmentHelpers.js
|
|
|
69
74
|
describeWithEnvironment('DataGrid', () => {
|
|
70
75
|
it('tests long text in datagrid', () => {
|
|
71
76
|
// ... setup grid ...
|
|
72
|
-
// Use assertions instead of printing results
|
|
77
|
+
// Use assertions instead of printing results.
|
|
73
78
|
assert.strictEqual(keyElement.textContent.length, 1500);
|
|
74
79
|
});
|
|
75
80
|
});
|
|
@@ -110,7 +110,13 @@ existing element containers.
|
|
|
110
110
|
* The Gerrit Code Review UI cannot identify moved blocks of unchanged code.
|
|
111
111
|
* If blocks of code need to be reordered, create a "prefactoring" change that first extracts the code that needs to be reordered into a named helper functions and pause the migration, waiting user confirmation.
|
|
112
112
|
|
|
113
|
-
12. **
|
|
113
|
+
12. **Micro-Commit Strategy**:
|
|
114
|
+
|
|
115
|
+
* Do not attempt to convert every helper method and UI component in a single monolithic commit. Break the template extraction into **small, self-contained micro-commits** (e.g., migrating one helper method, toolbar button, or list item render at a time).
|
|
116
|
+
* Verify with build and run tests after each micro-commit to guarantee zero regressions and 100% visual parity before moving to the next compartment.
|
|
117
|
+
* Provide the exact list of files and commit message when presenting progress so the change history remains atomic and easy to review.
|
|
118
|
+
|
|
119
|
+
13. **Wait for confirmation**:
|
|
114
120
|
|
|
115
121
|
* Wait for an explicit confirmation from the user before proceeding to the
|
|
116
122
|
next step.
|
package/docs/ui_engineering.md
CHANGED
|
@@ -222,25 +222,38 @@ This section provides a series of examples for migrating from imperative DOM man
|
|
|
222
222
|
|
|
223
223
|
## Setting `className` on `this.element`
|
|
224
224
|
|
|
225
|
-
Instead of setting `className` directly on `this.element`, define the component's structure declaratively using a lit-html template.
|
|
225
|
+
Instead of setting `className` directly on `this.element`, define the component's structure declaratively using a lit-html template. When classes or attributes (`jslog`) must be set directly on `target` itself (for example when `target` is positioned externally via `.positionAt()`), use the `container` option in DevTools' custom `render()` (`front_end/ui/lit/render.ts`).
|
|
226
226
|
|
|
227
227
|
**Before:**
|
|
228
228
|
```typescript
|
|
229
229
|
class SomeWidget extends UI.Widget.Widget {
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
230
|
+
constructor(element?: HTMLElement) {
|
|
231
|
+
super(element, {jslog: `${VisualLogging.deviceModeRuler().track({click: true})}`});
|
|
232
|
+
this.element.className = 'some-class';
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
235
|
```
|
|
236
236
|
|
|
237
|
-
**After:**
|
|
237
|
+
**After (when creating an inner wrapper):**
|
|
238
238
|
```typescript
|
|
239
|
-
|
|
240
239
|
export const DEFAULT_VIEW = (input, _output, target) => {
|
|
241
240
|
render(html`
|
|
242
241
|
<div class="some-class"></div>`,
|
|
243
|
-
target
|
|
242
|
+
target);
|
|
243
|
+
};
|
|
244
|
+
```
|
|
245
|
+
|
|
246
|
+
**After (when classes or attributes must be applied directly to `target` itself):**
|
|
247
|
+
```typescript
|
|
248
|
+
export const DEFAULT_VIEW = (input, _output, target) => {
|
|
249
|
+
render(html`
|
|
250
|
+
<div class="some-content">...</div>`,
|
|
251
|
+
target, {
|
|
252
|
+
container: {
|
|
253
|
+
classes: ['some-class'],
|
|
254
|
+
attributes: {jslog: VisualLogging.deviceModeRuler().track({click: true})},
|
|
255
|
+
},
|
|
256
|
+
});
|
|
244
257
|
};
|
|
245
258
|
```
|
|
246
259
|
|
|
@@ -268,7 +281,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
268
281
|
<div>
|
|
269
282
|
<div></div>
|
|
270
283
|
</div>`,
|
|
271
|
-
target
|
|
284
|
+
target);
|
|
272
285
|
};
|
|
273
286
|
```
|
|
274
287
|
|
|
@@ -296,7 +309,7 @@ class SomeWidget extends UI.Widget.Widget {
|
|
|
296
309
|
export const DEFAULT_VIEW = (input, _output, target) => {
|
|
297
310
|
render(html`
|
|
298
311
|
<div class="some-class" aria-label="some-label">some-text</div>`,
|
|
299
|
-
target
|
|
312
|
+
target);
|
|
300
313
|
};
|
|
301
314
|
```
|
|
302
315
|
|
|
@@ -326,7 +339,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
326
339
|
<div>
|
|
327
340
|
<div class="some-class container" @click=${this.onClick.bind(this)}></div>
|
|
328
341
|
</div>`,
|
|
329
|
-
target
|
|
342
|
+
target);
|
|
330
343
|
};
|
|
331
344
|
```
|
|
332
345
|
|
|
@@ -353,7 +366,7 @@ class SomeWidget extends UI.Widget.Widget {
|
|
|
353
366
|
export const DEFAULT_VIEW = (input, _output, target) => {
|
|
354
367
|
render(html`
|
|
355
368
|
<div style="width:100%; margin-left:10px"></div>`,
|
|
356
|
-
target
|
|
369
|
+
target);
|
|
357
370
|
};
|
|
358
371
|
```
|
|
359
372
|
|
|
@@ -383,7 +396,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
383
396
|
<div>
|
|
384
397
|
<div class="some-class"></div>
|
|
385
398
|
</div>`,
|
|
386
|
-
target
|
|
399
|
+
target);
|
|
387
400
|
};
|
|
388
401
|
```
|
|
389
402
|
|
|
@@ -411,7 +424,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
411
424
|
<div>
|
|
412
425
|
<span class="some-class">some-text</span>
|
|
413
426
|
</div>`,
|
|
414
|
-
target
|
|
427
|
+
target);
|
|
415
428
|
};
|
|
416
429
|
```
|
|
417
430
|
|
|
@@ -450,7 +463,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
450
463
|
</devtools-toolbar-input>
|
|
451
464
|
</devtools-toolbar>
|
|
452
465
|
</div>`,
|
|
453
|
-
target
|
|
466
|
+
target);
|
|
454
467
|
};
|
|
455
468
|
```
|
|
456
469
|
|
|
@@ -483,7 +496,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
483
496
|
aria-label="accessible-placeholder" style="flex-grow:0.5; flex-shrink:1"></devtools-toolbar-input>
|
|
484
497
|
</devtools-toolbar>
|
|
485
498
|
</div>`,
|
|
486
|
-
target
|
|
499
|
+
target);
|
|
487
500
|
};
|
|
488
501
|
```
|
|
489
502
|
|
|
@@ -523,7 +536,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
523
536
|
<span><div style="font-size: 12px;">💫</div></span>
|
|
524
537
|
</devtools-adorner>
|
|
525
538
|
</div>`,
|
|
526
|
-
target
|
|
539
|
+
target);
|
|
527
540
|
};
|
|
528
541
|
```
|
|
529
542
|
|
|
@@ -558,7 +571,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
558
571
|
.jslogContext=${'edit-name'}></devtools-button>
|
|
559
572
|
</devtools-toolbar>
|
|
560
573
|
</div>`,
|
|
561
|
-
target
|
|
574
|
+
target);
|
|
562
575
|
};
|
|
563
576
|
```
|
|
564
577
|
|
|
@@ -618,7 +631,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
618
631
|
<input type="text" placeholder="some-placeholder" value="some-value"
|
|
619
632
|
?disabled=${!this.enabled} checked>
|
|
620
633
|
</div>`,
|
|
621
|
-
target
|
|
634
|
+
target);
|
|
622
635
|
};
|
|
623
636
|
```
|
|
624
637
|
|
|
@@ -661,7 +674,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
661
674
|
<devtools-button class="some-class" title=${i18nString(UIStrings.someTitle)} @click=${onClick}
|
|
662
675
|
.jslogContext=${'some-button'} .variant=${Buttons.Button.Variant.PRIMARY}>Some button</devtools-button>
|
|
663
676
|
</div>`,
|
|
664
|
-
target
|
|
677
|
+
target);
|
|
665
678
|
};
|
|
666
679
|
```
|
|
667
680
|
|
|
@@ -689,7 +702,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
689
702
|
<div>
|
|
690
703
|
<div class="some-class">some-text</div>
|
|
691
704
|
</div>`,
|
|
692
|
-
target
|
|
705
|
+
target);
|
|
693
706
|
};
|
|
694
707
|
```
|
|
695
708
|
|
|
@@ -760,7 +773,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
760
773
|
<devtools-icon name="checkmark"
|
|
761
774
|
style="color:var(--icon-checkmark-green); width:14px; height:14px"></devtools-icon>
|
|
762
775
|
</div>`,
|
|
763
|
-
target
|
|
776
|
+
target);
|
|
764
777
|
};
|
|
765
778
|
```
|
|
766
779
|
|
|
@@ -811,7 +824,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
811
824
|
<devtools-checkbox ${bindToSetting(this.someOtherSetting)}>${this.someOtherSetting.title()}</devtools-checkbox>
|
|
812
825
|
</devtools-toolbar>
|
|
813
826
|
</div>`,
|
|
814
|
-
target
|
|
827
|
+
target);
|
|
815
828
|
};
|
|
816
829
|
```
|
|
817
830
|
|
|
@@ -844,7 +857,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
844
857
|
<devtools-button ${bindToAction('elements.refresh-event-listeners')}></devtools-button>
|
|
845
858
|
</devtools-toolbar>
|
|
846
859
|
</div>`,
|
|
847
|
-
target
|
|
860
|
+
target);
|
|
848
861
|
};
|
|
849
862
|
```
|
|
850
863
|
|
|
@@ -878,7 +891,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
878
891
|
</devtools-checkbox>
|
|
879
892
|
</devtools-toolbar>
|
|
880
893
|
</div>`,
|
|
881
|
-
target
|
|
894
|
+
target);
|
|
882
895
|
};
|
|
883
896
|
```
|
|
884
897
|
|
|
@@ -910,7 +923,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
910
923
|
@change=${this.someToolbarComboBoxClicked.bind(this)}></select>
|
|
911
924
|
</devtools-toolbar>
|
|
912
925
|
</div>`,
|
|
913
|
-
target
|
|
926
|
+
target);
|
|
914
927
|
};
|
|
915
928
|
```
|
|
916
929
|
|
|
@@ -970,7 +983,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
970
983
|
<div class="toolbar-spacer"></div>
|
|
971
984
|
</devtools-toolbar>
|
|
972
985
|
</div>`,
|
|
973
|
-
target
|
|
986
|
+
target);
|
|
974
987
|
};
|
|
975
988
|
```
|
|
976
989
|
|
|
@@ -1001,7 +1014,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1001
1014
|
<div>
|
|
1002
1015
|
<iframe sandbox tabindex="-1"></iframe>
|
|
1003
1016
|
</div>`,
|
|
1004
|
-
target
|
|
1017
|
+
target);
|
|
1005
1018
|
};
|
|
1006
1019
|
```
|
|
1007
1020
|
|
|
@@ -1030,7 +1043,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1030
1043
|
<input class="harmony-input add-source-map" spellcheck="false" type="text"
|
|
1031
1044
|
jslog=${VisualLogging.textField('url').track({keydown: 'Enter', change: true})}>
|
|
1032
1045
|
</div>`,
|
|
1033
|
-
target
|
|
1046
|
+
target);
|
|
1034
1047
|
};
|
|
1035
1048
|
```
|
|
1036
1049
|
|
|
@@ -1098,7 +1111,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1098
1111
|
</table>
|
|
1099
1112
|
</devtools-data-grid>
|
|
1100
1113
|
</div>`,
|
|
1101
|
-
target
|
|
1114
|
+
target);
|
|
1102
1115
|
};
|
|
1103
1116
|
```
|
|
1104
1117
|
|
|
@@ -1182,7 +1195,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1182
1195
|
</devtools-split-view>
|
|
1183
1196
|
</devtools-split-view>
|
|
1184
1197
|
</div>`,
|
|
1185
|
-
target
|
|
1198
|
+
target);
|
|
1186
1199
|
};
|
|
1187
1200
|
```
|
|
1188
1201
|
|
|
@@ -1233,7 +1246,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1233
1246
|
<div role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0.5"
|
|
1234
1247
|
aria-valuetext="50% done"></div>
|
|
1235
1248
|
</div>`,
|
|
1236
|
-
target
|
|
1249
|
+
target);
|
|
1237
1250
|
};
|
|
1238
1251
|
```
|
|
1239
1252
|
|
|
@@ -1265,15 +1278,20 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1265
1278
|
header: i18nString(UIStrings.nothingToSeeHere), text: this.explanation,
|
|
1266
1279
|
link: 'http://www.google.com',})}
|
|
1267
1280
|
</div>`,
|
|
1268
|
-
target
|
|
1281
|
+
target);
|
|
1269
1282
|
};
|
|
1270
1283
|
```
|
|
1271
1284
|
|
|
1272
|
-
## Migrating `TextPrompt`
|
|
1285
|
+
## Migrating `TextPrompt` and `InplaceEditor`
|
|
1273
1286
|
|
|
1274
|
-
Replace imperative `TextPrompt` creation with the declarative `<devtools-prompt>` component, providing completions via `<datalist>`.
|
|
1287
|
+
Replace imperative `TextPrompt` or `UI.InplaceEditor` creation with the declarative `<devtools-prompt>` component, providing completions via `<datalist>`.
|
|
1275
1288
|
|
|
1276
|
-
|
|
1289
|
+
### Critical Rules for `<devtools-prompt>`:
|
|
1290
|
+
1. **Prompt Owns the Content (`<slot>`)**: When `editing` is `false`, `<devtools-prompt>` renders `<span class="entrypoint"><slot></slot></span>` in shadow DOM. Therefore, `<devtools-prompt>` **must contain the display text/title directly as a child** (`<devtools-prompt ...>${title}</devtools-prompt>`) and live inside the element container (e.g., inside `<devtools-checkbox>`).
|
|
1291
|
+
2. **Do Not Conditionally Swap Elements**: Do NOT render `<devtools-prompt>` conditionally *in place of* a `<span>` or text node (`isEditing ? html`<devtools-prompt>...` : html`<span>${title}</span>``). Mount `<devtools-prompt>` unconditionally, holding the text content at all times, and only toggle its boolean property `?editing=${item === input.editingItem}`.
|
|
1292
|
+
3. **Conditional Input Styling**: Because `<devtools-prompt>` is present in both display and editing modes, any input-mode borders or background classes must be applied conditionally (e.g., `class=${Directives.classMap({'condition-input': isEditing})}`) so non-editing rows remain unbordered.
|
|
1293
|
+
|
|
1294
|
+
**Before (`InplaceEditor` or imperative `TextPrompt`):**
|
|
1277
1295
|
```typescript
|
|
1278
1296
|
class SomeWidget extends UI.Widget.Widget {
|
|
1279
1297
|
constructor() {
|
|
@@ -1286,10 +1304,15 @@ class SomeWidget extends UI.Widget.Widget {
|
|
|
1286
1304
|
const promptElement = this.contentElement.createChild('span');
|
|
1287
1305
|
prompt.attach(promptElement);
|
|
1288
1306
|
}
|
|
1307
|
+
|
|
1308
|
+
#startEditing(item: string, element: HTMLElement): void {
|
|
1309
|
+
const config = new UI.InplaceEditor.Config(this.#commit.bind(this), this.#cancel.bind(this), element);
|
|
1310
|
+
UI.InplaceEditor.InplaceEditor.startEditing(element, config);
|
|
1311
|
+
}
|
|
1289
1312
|
}
|
|
1290
1313
|
```
|
|
1291
1314
|
|
|
1292
|
-
**After:**
|
|
1315
|
+
**After (Declarative `<devtools-prompt>` inside Lit):**
|
|
1293
1316
|
```typescript
|
|
1294
1317
|
export const DEFAULT_VIEW = (input, _output, target) => {
|
|
1295
1318
|
render(html`
|
|
@@ -1298,9 +1321,19 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1298
1321
|
<option>completion1</option>
|
|
1299
1322
|
<option>completion2</option>
|
|
1300
1323
|
</datalist>
|
|
1301
|
-
<devtools-
|
|
1324
|
+
<devtools-checkbox .checked=${input.enabled}>
|
|
1325
|
+
<devtools-prompt
|
|
1326
|
+
value=${input.item}
|
|
1327
|
+
completions="my-completions"
|
|
1328
|
+
?editing=${input.item === input.editingItem}
|
|
1329
|
+
class=${Directives.classMap({'prompt-editing-border': input.item === input.editingItem})}
|
|
1330
|
+
@commit=${(e: CustomEvent) => input.onCommit(input.item, e.detail)}
|
|
1331
|
+
@cancel=${() => input.onCancel(input.item)}>
|
|
1332
|
+
${input.title}
|
|
1333
|
+
</devtools-prompt>
|
|
1334
|
+
</devtools-checkbox>
|
|
1302
1335
|
</div>`,
|
|
1303
|
-
target
|
|
1336
|
+
target);
|
|
1304
1337
|
};
|
|
1305
1338
|
```
|
|
1306
1339
|
|
|
@@ -1346,7 +1379,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1346
1379
|
</ul>
|
|
1347
1380
|
`}></devtools-tree>
|
|
1348
1381
|
</div>`,
|
|
1349
|
-
target
|
|
1382
|
+
target);
|
|
1350
1383
|
};
|
|
1351
1384
|
```
|
|
1352
1385
|
|
|
@@ -1372,7 +1405,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
|
|
|
1372
1405
|
</ul>
|
|
1373
1406
|
`}></devtools-tree>
|
|
1374
1407
|
</div>`,
|
|
1375
|
-
target
|
|
1408
|
+
target);
|
|
1376
1409
|
};
|
|
1377
1410
|
```
|
|
1378
1411
|
|
|
@@ -273,11 +273,6 @@ export interface SettingRegistration {
|
|
|
273
273
|
* of the setting to be changed only if the user set it.
|
|
274
274
|
*/
|
|
275
275
|
userActionCondition?: string;
|
|
276
|
-
/**
|
|
277
|
-
* The name of the experiment a setting is associated with. Enabling and disabling the declared
|
|
278
|
-
* experiment will enable and disable the setting respectively.
|
|
279
|
-
*/
|
|
280
|
-
experiment?: Root.ExperimentNames.ExperimentName;
|
|
281
276
|
/**
|
|
282
277
|
* A condition is a function that will make the setting available if it
|
|
283
278
|
* returns true, and not available, otherwise. Make sure that objects you
|
|
@@ -152,6 +152,16 @@ export function lowerBound<S, T, A extends S[]>(
|
|
|
152
152
|
return r;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
/**
|
|
156
|
+
* Inserts a value into a sorted array in O(n) time (O(log n) search using the provided comparator and O(n) insertion).
|
|
157
|
+
* Returns the index at which the value was inserted.
|
|
158
|
+
*/
|
|
159
|
+
export function insertWithComparator<T>(array: T[], value: T, comparator: (a: T, b: T) => number): number {
|
|
160
|
+
const index = lowerBound(array, value, comparator);
|
|
161
|
+
array.splice(index, 0, value);
|
|
162
|
+
return index;
|
|
163
|
+
}
|
|
164
|
+
|
|
155
165
|
/**
|
|
156
166
|
* Returns the index of the element closest to the needle that is greater than
|
|
157
167
|
* it. Assumes that the provided array is sorted.
|
|
@@ -8,3 +8,89 @@ As such, allowed use of APIs is restricted to what is available in both runtimes
|
|
|
8
8
|
A `DevToolsUniverse` is a concrete, encapsulated instance of "foundation" scoped
|
|
9
9
|
to a single root CDP target. It is valid to create multiple `DevToolsUniverse`
|
|
10
10
|
instances simultaneously.
|
|
11
|
+
|
|
12
|
+
## Architecture Shortcomings & Code Health Projects
|
|
13
|
+
|
|
14
|
+
The following is a prioritized list of architectural shortcomings and refactoring projects aimed at reducing coupling, eliminating global state, and improving the modularity of the DevTools foundation.
|
|
15
|
+
|
|
16
|
+
### Priority 1: Move `models/text_utils` to `core/text_utils`
|
|
17
|
+
* **Problem**: 22 files in `front_end/core/sdk` depend directly on `front_end/models/text_utils` for basic text structures (layering violation).
|
|
18
|
+
* **Rationale**: `text_utils` contains fundamental, non-business-logic utilities (`TextRange`, `TextCursor`, `ContentData`, `ContentProvider`) that represent the base data structures of the editor, debugger, and network panels. It only depends on `core/common` and `core/platform`. Moving it to `core` is a natural fit and resolves the majority of `core` -> `models` layering violations.
|
|
19
|
+
* **Action**:
|
|
20
|
+
1. Move the directory `front_end/models/text_utils` to `front_end/core/text_utils`.
|
|
21
|
+
2. Update all imports across the codebase.
|
|
22
|
+
|
|
23
|
+
### Priority 2: Break Circular Dependencies in Bindings (`ResourceMapping` <-> Bindings)
|
|
24
|
+
* **Problem**: Bidirectional dependencies between `ResourceMapping` and `DebuggerWorkspaceBinding`/`CSSWorkspaceBinding` prevent isolated testing.
|
|
25
|
+
* **Rationale**: Decoupling `ResourceMapping` from concrete bindings allows better testing and modularity.
|
|
26
|
+
* **Action**: Introduce abstract listener interfaces in `ResourceMapping` (Dependency Inversion). `DebuggerWorkspaceBinding` and `CSSWorkspaceBinding` should implement these interfaces and register themselves, allowing `ResourceMapping` to invoke them without depending on concrete classes.
|
|
27
|
+
|
|
28
|
+
### Priority 3: Move `IgnoreListManager` from `workspace` to `bindings`
|
|
29
|
+
* **Problem**: `models/workspace` depends on `core/sdk` via `IgnoreListManager.ts`.
|
|
30
|
+
* **Rationale**: Ignore-listing is conceptually a debugger-binding concern, not a basic workspace/file-system concern.
|
|
31
|
+
* **Action**:
|
|
32
|
+
1. Remove the convenience helper `UISourceCode.isIgnoreListed()` (and related methods in `UILocation`) from `models/workspace`.
|
|
33
|
+
2. Update callers to query `IgnoreListManager` directly.
|
|
34
|
+
3. Move `IgnoreListManager.ts` to `models/bindings`.
|
|
35
|
+
4. This removes the value-level dependency of `workspace` on `sdk`.
|
|
36
|
+
|
|
37
|
+
### Priority 4: Decouple `Workspace.Project` from `SDK.Target`
|
|
38
|
+
* **Problem**: `Workspace.Project` exposes `target()`, coupling the VFS to runtime targets.
|
|
39
|
+
* **Rationale**: A workspace should be able to exist without knowing about browser targets.
|
|
40
|
+
* **Action**:
|
|
41
|
+
1. Remove `target()` from the `Project` interface and `ProjectStore` in `front_end/models/workspace/WorkspaceImpl.ts`.
|
|
42
|
+
2. In the `bindings` module, maintain a private `WeakMap<Workspace.Project, SDK.Target.Target>` to associate projects with targets.
|
|
43
|
+
3. Update `NetworkProject.targetForUISourceCode(uiSourceCode)` to look up the target from this `WeakMap`.
|
|
44
|
+
|
|
45
|
+
### Priority 5: Decouple `core/sdk` from `models/formatter` (AST Scope Fallback)
|
|
46
|
+
* **Problem**: `SDK/SourceMap.ts` depends on `models/formatter` for fallback scope calculation.
|
|
47
|
+
* **Rationale**: `SDK` should not depend on high-level formatting/parsing worker pools.
|
|
48
|
+
* **Action**:
|
|
49
|
+
1. Remove the fallback scope calculation logic from `SourceMap.ts` and expose a clean API (e.g., `setScopesInfo(...)`).
|
|
50
|
+
2. Move the AST fallback scope calculation logic to `front_end/models/bindings` (e.g., in `DebuggerWorkspaceBinding`), which coordinates script and source map loading.
|
|
51
|
+
3. Have the bindings layer calculate the fallback scopes and attach them to the `SourceMap` object.
|
|
52
|
+
|
|
53
|
+
### Priority 6: Refactor Mapping Engines & Inner Helpers in Bindings
|
|
54
|
+
* **Problem**: Engines and helpers query global `DebuggerWorkspaceBinding.instance()` directly.
|
|
55
|
+
* **Rationale**: High coupling hinders testing and isolation.
|
|
56
|
+
* **Action**: Eliminate direct global singleton queries. Update helpers to access parent mapping engines or pass references via constructors.
|
|
57
|
+
|
|
58
|
+
### Priority 7: Universe-scoped Embedder Host and HostConfig
|
|
59
|
+
* **Problem**: `InspectorFrontendHostInstance` and `Root.Runtime.hostConfig` are treated as global mutable singletons. Core classes (`Connections`, `IsolatedFileSystemManager`, `FileManager`) and UI components access them directly, leading to global state pollution and state leaks in tests.
|
|
60
|
+
* **Rationale**: The host interface and its configuration represent the boundary between DevTools and the Chromium browser. To support multiple isolated `DevToolsUniverse` instances, both the host interface and host configuration must be scoped to the universe context.
|
|
61
|
+
* **Action**:
|
|
62
|
+
1. Store the scoped `InspectorFrontendHostAPI` (wrapped in `ScopedInspectorFrontendHost`) in the `DevToolsUniverse` context.
|
|
63
|
+
2. Refactor core classes (`Connections`, `IsolatedFileSystemManager`, `FileManager`, `UserMetrics`) to receive the scoped host via constructor injection.
|
|
64
|
+
3. Introduce a scoped `HostConfigProvider` service in the `Universe` context to manage `HostConfig` fetching and caching.
|
|
65
|
+
4. Move polling and configuration update logic from `HostConfigTracker` in `AidaClient.ts` to `HostConfigProvider`.
|
|
66
|
+
5. Provide a compatibility Proxy for `Root.Runtime.hostConfig` to allow gradual migration of the 200+ call sites.
|
|
67
|
+
6. Remove the global side-effect initialization in `InspectorFrontendHost.ts`.
|
|
68
|
+
|
|
69
|
+
### Priority 8: View-Level Dependency Injection (Universe-ification)
|
|
70
|
+
* **Problem**: UI panels query global `.instance()` for bindings, causing state leaks in tests.
|
|
71
|
+
* **Rationale**: DevTools is transitioning to a scoped "Universe" architecture where dependencies are injected. UI panels should receive their dependencies rather than querying globals.
|
|
72
|
+
* **Action**:
|
|
73
|
+
1. Expose bindings on `Universe` and inject them via the view loading pipeline.
|
|
74
|
+
2. Leverage the view loading pipeline (`-meta.ts` files) to pass these bindings to panel constructors upon initialization, avoiding global calls in UI panels.
|
|
75
|
+
|
|
76
|
+
### Priority 9: Decouple `persistence` from `bindings`
|
|
77
|
+
* **Problem**: `models/persistence` depends on `models/bindings` to check if a target is Node.js.
|
|
78
|
+
* **Rationale**: `persistence` (which maps network files to local files) should ideally only depend on `workspace` and not on the complex debugger/bindings layer.
|
|
79
|
+
* **Action**: Introduce a capability/property on `Workspace.Project` (e.g., `isNodeProject`) to avoid direct target queries.
|
|
80
|
+
|
|
81
|
+
### Priority 10: Move `SourceFrameIssuesManager` to `bindings`
|
|
82
|
+
* **Problem**: `models/issues_manager` depends on `workspace` and `bindings` only for placing issue badges.
|
|
83
|
+
* **Rationale**: Showing badges on source frames is a binding/presentation concern, similar to showing console messages. `issues_manager` itself should be a clean, independent leaf module.
|
|
84
|
+
* **Action**: Move `SourceFrameIssuesManager.ts` to `models/bindings/` and remove `workspace` and `bindings` dependencies from `issues_manager/BUILD.gn`.
|
|
85
|
+
|
|
86
|
+
### Priority 11: Decouple UI via Interfaces / Revealers
|
|
87
|
+
* **Problem**: General UI components (like `Linkifier`) depend on heavy concrete bindings.
|
|
88
|
+
* **Rationale**: UI components should not be tightly coupled to complex mapping logic.
|
|
89
|
+
* **Action**: Use light interfaces or `Common.Revealer`.
|
|
90
|
+
|
|
91
|
+
### Priority 12: Decouple Core Modules from `i18n` (UI Localization)
|
|
92
|
+
* **Problem**: Core infra/parser modules import `i18n` for localized strings.
|
|
93
|
+
* **Rationale**: Business logic and core infrastructure should not be tied to UI localization libraries.
|
|
94
|
+
* **Action**:
|
|
95
|
+
1. Refactor parsers to return structured error data and let UI handle localization.
|
|
96
|
+
2. Update registries to use string IDs/keys.
|