chrome-devtools-frontend 1.0.1660788 → 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.
Files changed (177) hide show
  1. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +143 -0
  2. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +104 -0
  3. package/.agents/skills/migrate-chromium-test/SKILL.md +7 -2
  4. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +7 -1
  5. package/docs/ui_engineering.md +75 -42
  6. package/eslint.config.mjs +7 -0
  7. package/extension-api/ExtensionAPI.d.ts +88 -0
  8. package/front_end/core/common/SettingRegistration.ts +0 -5
  9. package/front_end/core/host/InspectorFrontendHost.ts +1 -0
  10. package/front_end/core/host/InspectorFrontendHostStub.ts +2 -0
  11. package/front_end/core/platform/ArrayUtilities.ts +10 -0
  12. package/front_end/core/platform/StringUtilities.ts +20 -4
  13. package/front_end/core/sdk/CPUThrottlingManager.ts +2 -0
  14. package/front_end/core/sdk/DOMDebuggerModel.ts +2 -0
  15. package/front_end/core/sdk/DOMModel.ts +1 -0
  16. package/front_end/core/sdk/DebuggerModel.ts +4 -4
  17. package/front_end/core/sdk/EventBreakpointsModel.ts +2 -0
  18. package/front_end/core/sdk/FrameManager.ts +1 -0
  19. package/front_end/core/sdk/IsolateManager.ts +1 -0
  20. package/front_end/core/sdk/NetworkManager.ts +2 -0
  21. package/front_end/core/sdk/NetworkRequest.ts +1 -0
  22. package/front_end/core/sdk/PageResourceLoader.ts +2 -0
  23. package/front_end/core/sdk/SourceMap.ts +5 -5
  24. package/front_end/core/sdk/SourceMapManager.ts +3 -2
  25. package/front_end/core/sdk/TargetManager.ts +4 -0
  26. package/front_end/entrypoints/main/MainImpl.ts +2 -1
  27. package/front_end/foundation/README.md +86 -0
  28. package/front_end/generated/InspectorBackendCommands.ts +4 -5
  29. package/front_end/generated/protocol.ts +20 -38
  30. package/front_end/models/bindings/CompilerScriptMapping.ts +5 -5
  31. package/front_end/models/bindings/ContentProviderBasedProject.ts +2 -3
  32. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +3 -3
  33. package/front_end/models/bindings/DefaultScriptMapping.ts +3 -3
  34. package/front_end/models/bindings/ResourceMapping.ts +3 -3
  35. package/front_end/models/bindings/ResourceScriptMapping.ts +2 -2
  36. package/front_end/models/bindings/SASSSourceMapping.ts +3 -3
  37. package/front_end/models/bindings/TempFile.ts +8 -3
  38. package/front_end/models/extensions/ExtensionAPI.ts +110 -46
  39. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +7 -5
  40. package/front_end/models/formatter/ScriptFormatter.ts +8 -11
  41. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +14 -5
  42. package/front_end/models/issues_manager/CheckFormsIssuesTrigger.ts +8 -9
  43. package/front_end/models/issues_manager/CookieIssue.ts +21 -9
  44. package/front_end/models/issues_manager/Issue.ts +3 -4
  45. package/front_end/models/issues_manager/IssueAggregator.ts +0 -9
  46. package/front_end/models/issues_manager/IssueResolver.ts +2 -2
  47. package/front_end/models/issues_manager/IssuesManager.ts +136 -137
  48. package/front_end/models/issues_manager/RelatedIssue.ts +10 -8
  49. package/front_end/models/issues_manager/issues_manager.ts +0 -2
  50. package/front_end/models/javascript_metadata/NativeFunctions.js +1 -1
  51. package/front_end/models/logs/RequestResolver.ts +2 -2
  52. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +12 -9
  53. package/front_end/models/trace/handlers/FramesHandler.ts +4 -7
  54. package/front_end/models/trace/handlers/ModelHandlers.ts +0 -1
  55. package/front_end/models/trace/handlers/RendererHandler.ts +2 -15
  56. package/front_end/models/trace/handlers/Threads.ts +4 -11
  57. package/front_end/models/trace/insights/DOMSize.ts +1 -1
  58. package/front_end/models/workspace_diff/WorkspaceDiff.ts +10 -4
  59. package/front_end/panels/ai_assistance/components/ChatMessage.ts +2 -1
  60. package/front_end/panels/application/ApplicationPanelSidebar.ts +0 -44
  61. package/front_end/panels/application/CookieItemsView.ts +1 -1
  62. package/front_end/panels/application/ServiceWorkersView.ts +494 -411
  63. package/front_end/panels/application/WebMCPView.ts +38 -43
  64. package/front_end/panels/application/application.ts +0 -6
  65. package/front_end/panels/application/components/AdsView.ts +201 -30
  66. package/front_end/panels/application/components/adsView.css +25 -0
  67. package/front_end/panels/application/components/components.ts +0 -2
  68. package/front_end/panels/application/preloading/components/PreloadingDetailsReportView.ts +1 -1
  69. package/front_end/panels/application/preloading/components/RuleSetDetailsView.ts +2 -2
  70. package/front_end/panels/application/serviceWorkersView.css +40 -4
  71. package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +147 -123
  72. package/front_end/panels/common/ExtensionServer.ts +3 -3
  73. package/front_end/panels/console/ConsoleView.ts +3 -2
  74. package/front_end/panels/console/PromptBuilder.ts +2 -1
  75. package/front_end/panels/emulation/DeviceModeView.ts +164 -113
  76. package/front_end/panels/issues/AffectedResourcesView.ts +1 -1
  77. package/front_end/panels/issues/IssueView.ts +0 -2
  78. package/front_end/panels/issues/IssuesPane.ts +1 -8
  79. package/front_end/panels/js_timeline/js_timeline-meta.ts +3 -2
  80. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +53 -12
  81. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorController.ts +3 -3
  82. package/front_end/panels/linear_memory_inspector/LinearMemoryInspectorPane.ts +5 -5
  83. package/front_end/panels/linear_memory_inspector/components/LinearMemoryHighlightChipList.ts +4 -4
  84. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +2 -2
  85. package/front_end/panels/linear_memory_inspector/components/LinearMemoryNavigator.ts +6 -6
  86. package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +3 -3
  87. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterDisplay.ts +8 -9
  88. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterDisplayUtils.ts +2 -2
  89. package/front_end/panels/linear_memory_inspector/components/ValueInterpreterSettings.ts +1 -1
  90. package/front_end/panels/linear_memory_inspector/linear_memory_inspector-meta.ts +2 -2
  91. package/front_end/panels/network/RequestPayloadView.ts +92 -52
  92. package/front_end/panels/network/RequestTimingView.ts +95 -127
  93. package/front_end/panels/network/components/ResponseHeaderSection.ts +5 -3
  94. package/front_end/panels/profiler/HeapSnapshotView.ts +3 -3
  95. package/front_end/panels/profiler/WritableProfileHeader.ts +2 -2
  96. package/front_end/panels/recorder/{components/ControlButton.ts → ControlButton.ts} +9 -10
  97. package/front_end/panels/recorder/{components/CreateRecordingView.ts → CreateRecordingView.ts} +48 -47
  98. package/front_end/panels/recorder/{components/ExtensionView.ts → ExtensionView.ts} +8 -8
  99. package/front_end/panels/recorder/RecorderController.ts +302 -127
  100. package/front_end/panels/recorder/RecorderPanel.ts +1 -1
  101. package/front_end/panels/recorder/{components/RecordingListView.ts → RecordingListView.ts} +19 -20
  102. package/front_end/panels/recorder/{components/RecordingView.ts → RecordingView.ts} +88 -88
  103. package/front_end/panels/recorder/{components/ReplaySection.ts → ReplaySection.ts} +11 -11
  104. package/front_end/panels/recorder/{components/SelectorPicker.ts → SelectorPicker.ts} +13 -13
  105. package/front_end/panels/recorder/{components/StepEditor.ts → StepEditor.ts} +15 -15
  106. package/front_end/panels/recorder/{components/StepView.ts → StepView.ts} +10 -10
  107. package/front_end/panels/recorder/{components/TimelineSection.ts → TimelineSection.ts} +2 -2
  108. package/front_end/panels/recorder/recorder.ts +23 -1
  109. package/front_end/panels/recorder/util/util.ts +113 -0
  110. package/front_end/panels/sources/SourcesView.ts +0 -17
  111. package/front_end/panels/sources/TabbedEditorContainer.ts +1 -1
  112. package/front_end/panels/sources/UISourceCodeFrame.ts +42 -0
  113. package/front_end/panels/sources/sources.ts +0 -2
  114. package/front_end/panels/timeline/CompatibilityTracksAppender.ts +0 -17
  115. package/front_end/panels/timeline/IsolateSelector.ts +10 -8
  116. package/front_end/panels/timeline/ThreadAppender.ts +0 -106
  117. package/front_end/panels/timeline/TimelinePanel.ts +18 -7
  118. package/front_end/panels/timeline/TimelineUIUtils.ts +144 -129
  119. package/front_end/panels/timeline/timeline-meta.ts +3 -2
  120. package/front_end/third_party/chromium/README.chromium +1 -1
  121. package/front_end/ui/components/report_view/ReportView.ts +11 -5
  122. package/front_end/ui/components/report_view/report.css +4 -1
  123. package/front_end/ui/legacy/TextPrompt.ts +49 -1
  124. package/front_end/ui/legacy/Treeoutline.ts +3 -0
  125. package/front_end/ui/legacy/UIUtils.ts +11 -1
  126. package/front_end/ui/legacy/Widget.ts +14 -15
  127. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +2 -2
  128. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +11 -7
  129. package/front_end/ui/legacy/components/source_frame/SourceFrame.ts +2 -1
  130. package/front_end/ui/legacy/components/utils/JSPresentationUtils.ts +10 -1
  131. package/front_end/ui/legacy/textPrompt.css +10 -0
  132. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  133. package/package.json +1 -1
  134. package/front_end/models/issues_manager/AttributionReportingIssue.ts +0 -247
  135. package/front_end/models/issues_manager/descriptions/arInsecureContext.md +0 -7
  136. package/front_end/models/issues_manager/descriptions/arInvalidInfoHeader.md +0 -5
  137. package/front_end/models/issues_manager/descriptions/arInvalidRegisterOsSourceHeader.md +0 -5
  138. package/front_end/models/issues_manager/descriptions/arInvalidRegisterOsTriggerHeader.md +0 -5
  139. package/front_end/models/issues_manager/descriptions/arInvalidRegisterSourceHeader.md +0 -5
  140. package/front_end/models/issues_manager/descriptions/arInvalidRegisterTriggerHeader.md +0 -5
  141. package/front_end/models/issues_manager/descriptions/arNavigationRegistrationUniqueScopeAlreadySet.md +0 -5
  142. package/front_end/models/issues_manager/descriptions/arNavigationRegistrationWithoutTransientUserActivation.md +0 -6
  143. package/front_end/models/issues_manager/descriptions/arNoRegisterOsSourceHeader.md +0 -5
  144. package/front_end/models/issues_manager/descriptions/arNoRegisterOsTriggerHeader.md +0 -5
  145. package/front_end/models/issues_manager/descriptions/arNoRegisterSourceHeader.md +0 -5
  146. package/front_end/models/issues_manager/descriptions/arNoRegisterTriggerHeader.md +0 -5
  147. package/front_end/models/issues_manager/descriptions/arNoWebOrOsSupport.md +0 -4
  148. package/front_end/models/issues_manager/descriptions/arOsSourceIgnored.md +0 -18
  149. package/front_end/models/issues_manager/descriptions/arOsTriggerIgnored.md +0 -19
  150. package/front_end/models/issues_manager/descriptions/arPermissionPolicyDisabled.md +0 -8
  151. package/front_end/models/issues_manager/descriptions/arSourceAndTriggerHeaders.md +0 -9
  152. package/front_end/models/issues_manager/descriptions/arSourceIgnored.md +0 -13
  153. package/front_end/models/issues_manager/descriptions/arTriggerIgnored.md +0 -12
  154. package/front_end/models/issues_manager/descriptions/arUntrustworthyReportingOrigin.md +0 -10
  155. package/front_end/models/issues_manager/descriptions/arWebAndOsHeaders.md +0 -11
  156. package/front_end/models/trace/handlers/AuctionWorkletsHandler.ts +0 -183
  157. package/front_end/panels/application/InterestGroupStorageModel.ts +0 -81
  158. package/front_end/panels/application/InterestGroupStorageView.ts +0 -151
  159. package/front_end/panels/application/InterestGroupTreeElement.ts +0 -65
  160. package/front_end/panels/application/components/InterestGroupAccessGrid.ts +0 -135
  161. package/front_end/panels/application/components/interestGroupAccessGrid.css +0 -27
  162. package/front_end/panels/application/interestGroupStorageView.css +0 -9
  163. package/front_end/panels/issues/AttributionReportingIssueDetailsView.ts +0 -182
  164. package/front_end/panels/recorder/components/components.ts +0 -25
  165. package/front_end/panels/recorder/components/util.ts +0 -116
  166. package/front_end/panels/sources/InplaceFormatterEditorAction.ts +0 -149
  167. /package/front_end/panels/recorder/{components/controlButton.css → controlButton.css} +0 -0
  168. /package/front_end/panels/recorder/{components/createRecordingView.css → createRecordingView.css} +0 -0
  169. /package/front_end/panels/recorder/{components/extensionView.css → extensionView.css} +0 -0
  170. /package/front_end/panels/recorder/{components/recordingListView.css → recordingListView.css} +0 -0
  171. /package/front_end/panels/recorder/{components/recordingView.css → recordingView.css} +0 -0
  172. /package/front_end/panels/recorder/{components/replaySection.css → replaySection.css} +0 -0
  173. /package/front_end/panels/recorder/{components/selectorPicker.css → selectorPicker.css} +0 -0
  174. /package/front_end/panels/recorder/{components/startView.css → startView.css} +0 -0
  175. /package/front_end/panels/recorder/{components/stepEditor.css → stepEditor.css} +0 -0
  176. /package/front_end/panels/recorder/{components/stepView.css → stepView.css} +0 -0
  177. /package/front_end/panels/recorder/{components/timelineSection.css → timelineSection.css} +0 -0
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: evaluate-ai-css-completion
3
+ description: Expose a temporary evaluation hook in DevTools and run a Puppeteer script to validate CSS code completion trigger rates.
4
+ ---
5
+
6
+ # Evaluate CSS Completion Skill
7
+
8
+ This skill allows you to temporarily expose an evaluation hook in DevTools to measure CSS code completion trigger rates using a Puppeteer script.
9
+
10
+ > [!WARNING]
11
+ > **NEVER commit the `devtools_app.ts` patch.** It is only for local evaluation. Always revert it before uploading your CL.
12
+
13
+ ## Step 1: Apply the Temporary Patch
14
+
15
+ Modify `front_end/entrypoints/devtools_app/devtools_app.ts` to expose the global `testCssCompletion` hook.
16
+
17
+ Add the following code at the end of `front_end/entrypoints/devtools_app/devtools_app.ts`:
18
+
19
+ ```typescript
20
+ // --- TEMPORARY EVALUATION HOOK ---
21
+ // TEMPORARY PATCH - REMOVE BEFORE COMMIT
22
+
23
+ import * as Host from '../../core/host/host.js';
24
+ import * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
25
+
26
+ (self as any).testCss = {
27
+ getCases() {
28
+ return [
29
+ {
30
+ name: 'Generic CSS Test Case',
31
+ url: null,
32
+ prefix: 'h1 { font-s',
33
+ suffix: ' }',
34
+ },
35
+ // Add more test cases here
36
+ ];
37
+ },
38
+
39
+ async evaluate(uiSourceCodeUrl: string | null, prefix: string, suffix: string, additionalFiles?: any[]) {
40
+ const aidaClient = new Host.AidaClient.AidaClient();
41
+ const completion = new AiCodeCompletion.AiCodeCompletion.AiCodeCompletion(
42
+ {aidaClient},
43
+ AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES
44
+ );
45
+
46
+ const formattedAdditionalFiles = additionalFiles?.map(f => ({
47
+ path: f.path || f.name,
48
+ content: f.content || f.text,
49
+ included_reason: f.included_reason ?? Host.AidaClient.Reason.RELATED_FILE,
50
+ }));
51
+
52
+ const result = await completion.completeCode(
53
+ prefix,
54
+ suffix,
55
+ prefix.length,
56
+ Host.AidaClient.AidaInferenceLanguage.CSS,
57
+ formattedAdditionalFiles
58
+ );
59
+
60
+ return {
61
+ hasSuggestion: result.response !== null && result.response.generatedSamples.length > 0,
62
+ suggestions: result.response?.generatedSamples.map(s => s.generationString) ?? [],
63
+ injectedFiles: formattedAdditionalFiles?.map(f => f.path) ?? [],
64
+ };
65
+ }
66
+ };
67
+ // ----------------------------------
68
+ ```
69
+
70
+ ## Step 2: Launch Chrome with Local DevTools
71
+
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
+
74
+ You must specify a persistent user data directory so you can log in once and reuse the session:
75
+
76
+ ```bash
77
+ npm start -- --no-watch --browser=canary --remote-debugging-port=9222 --user-data-dir=/tmp/devtools-ai-evaluate-css-completion
78
+ ```
79
+
80
+ > [!IMPORTANT]
81
+ > **Instructions for the Agent:**
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
+ > 1. Sign in to Chrome with your corporate account in the new window.
84
+ > 2. Open DevTools, go to **Settings** (gear icon) > **AI Innovations**, and ensure **Code Completions** is enabled.
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.
87
+
88
+ ## Step 3: Configure Test Cases
89
+
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.
91
+
92
+ After modifying the test cases, manually run a build to compile the changes:
93
+ ```bash
94
+ autoninja -C out/Default
95
+ ```
96
+
97
+ For example, to test spacing sensitivity, update the returned array:
98
+ ```typescript
99
+ getCases() {
100
+ return [
101
+ {
102
+ name: 'With CSS Context',
103
+ url: null,
104
+ prefix: 'h1 { font-s',
105
+ suffix: ' }',
106
+ additionalFiles: [
107
+ {
108
+ path: 'other.css',
109
+ content: 'body { color: red; }',
110
+ }
111
+ ]
112
+ }
113
+ ];
114
+ }
115
+ ```
116
+
117
+ ## Step 4: Run the Evaluation Script
118
+
119
+ Run the Puppeteer script to execute the evaluation:
120
+
121
+ ```bash
122
+ node .agents/skills/evaluate-ai-css-completion/scripts/evaluate.js
123
+ ```
124
+
125
+ The script will connect to the DevTools instance, trigger completions, and output the results.
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
+
137
+ ## Step 5: Revert the Patch
138
+
139
+ Once the evaluation is complete and you have recorded the results, revert the patch in `devtools_app.ts`:
140
+
141
+ ```bash
142
+ git checkout front_end/entrypoints/devtools_app/devtools_app.ts
143
+ ```
@@ -0,0 +1,104 @@
1
+ // Copyright 2026 The Chromium Authors
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ /* eslint-disable no-console */
6
+
7
+ const puppeteer = require('puppeteer-core');
8
+
9
+ async function runEvaluation() {
10
+ console.log('Connecting to Chrome...');
11
+ const browser = await puppeteer.connect({browserURL: 'http://127.0.0.1:9222', defaultViewport: null});
12
+
13
+ try {
14
+ console.log('Opening test page (example.com)...');
15
+ const testPage = await browser.newPage();
16
+ await testPage.goto('https://example.com');
17
+
18
+ console.log('Waiting for DevTools target...');
19
+ const devtoolsTarget = await browser.waitForTarget(t => {
20
+ return t.url().startsWith('devtools://') || t.url().includes('inspector.html');
21
+ }, {timeout: 10000});
22
+
23
+ console.log('Connecting to DevTools page...');
24
+ const devtoolsPage = await devtoolsTarget.page();
25
+ if (!devtoolsPage) {
26
+ throw new Error('Could not get DevTools page object. Make sure DevTools is undocked or opened in a tab.');
27
+ }
28
+ console.log('DevTools URL:', devtoolsPage.url());
29
+
30
+ devtoolsPage.on('console', msg => console.log('BROWSER LOG:', msg.text()));
31
+
32
+ console.log('Reloading DevTools to ensure patch is loaded...');
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'));
44
+
45
+ console.log('Waiting for DevTools to initialize and hook to be available...');
46
+ try {
47
+ await devtoolsPage.waitForFunction(() => typeof self.testCss?.getCases === 'function', {timeout: 15000});
48
+ } catch {
49
+ throw new Error('testCss.getCases hook not found. Did you apply the patch and build? Is this the local build?');
50
+ }
51
+
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));
55
+ const testCases = await devtoolsPage.evaluate(() => self.testCss.getCases());
56
+ console.log(`Found ${testCases.length} test cases. Running each case 5 times.`);
57
+
58
+ let totalQueries = 0;
59
+ for (const tc of testCases) {
60
+ console.log(`\nRunning: ${tc.name}`);
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);
71
+
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
+ }
81
+
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
+ }
87
+ }
88
+
89
+ console.log(` Result: [Success Rate: ${successCount}/5]`);
90
+ if (suggestions.size > 0) {
91
+ console.log(' Unique Suggestions:', Array.from(suggestions));
92
+ }
93
+ }
94
+
95
+ // Cleanup tabs
96
+ await devtoolsPage.close();
97
+ await testPage.close();
98
+ } finally {
99
+ console.log('Disconnecting from Chrome...');
100
+ await browser.disconnect();
101
+ }
102
+ }
103
+
104
+ runEvaluation().catch(console.error);
@@ -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. Upload a CL with your changes using the `version-control` skill. Make sure the CL description has a `Fixed: <issue number>` trailer instead of `Bug: <issue number>`.
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. **Wait for confirmation**:
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.
@@ -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
- constructor() {
231
- super();
232
- this.element.className = 'some-class';
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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, {host: input});
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
- **Before:**
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-prompt completions="my-completions" editing></devtools-prompt>
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, {host: input});
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, {host: input});
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, {host: input});
1408
+ target);
1376
1409
  };
1377
1410
  ```
1378
1411
 
package/eslint.config.mjs CHANGED
@@ -691,6 +691,13 @@ export default defineConfig([
691
691
  'lit/quoted-expressions': ['error', 'never'],
692
692
  },
693
693
  },
694
+ {
695
+ name: 'Front-end core files',
696
+ files: ['front_end/core/**/*.ts'],
697
+ rules: {
698
+ '@devtools/no-instance-of-migrated-singletons': 'error',
699
+ },
700
+ },
694
701
  {
695
702
  name: 'Front-end meta files',
696
703
  files: ['front_end/**/*-meta.ts'],