chrome-devtools-frontend 1.0.1716465 → 1.0.1717709

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 (209) hide show
  1. package/.agents/skills/devtools-model-management/SKILL.md +4 -4
  2. package/.agents/skills/devtools-setting-migration/SKILL.md +1 -1
  3. package/.agents/skills/devtools-testing-guidance/SKILL.md +4 -2
  4. package/.agents/skills/devtools-verification/SKILL.md +9 -5
  5. package/.agents/skills/fix-tests/SKILL.md +1 -1
  6. package/.agents/skills/foundation-test-migration/SKILL.md +2 -2
  7. package/.agents/skills/ui-eng-vision-local-lit-renderer/SKILL.md +57 -9
  8. package/.agents/skills/ui-eng-vision-logic-consolidator/SKILL.md +27 -3
  9. package/.agents/skills/ui-eng-vision-orchestrator/SKILL.md +32 -2
  10. package/.agents/skills/ui-eng-vision-test-scaffolder/SKILL.md +39 -10
  11. package/.agents/skills/ui-eng-vision-widget-promoter/SKILL.md +4 -4
  12. package/.agents/skills/ui-widgets/SKILL.md +8 -8
  13. package/docs/README.md +1 -1
  14. package/docs/contributing/README.md +3 -3
  15. package/docs/cookbook/dependencies.md +1 -1
  16. package/docs/get_the_code.md +35 -26
  17. package/docs/ui_engineering.md +34 -0
  18. package/dt +7 -0
  19. package/dt.bat +8 -0
  20. package/front_end/core/common/ParsedURL.ts +1 -1
  21. package/front_end/core/common/Settings.ts +5 -0
  22. package/front_end/core/host/UserMetricsEnums.ts +1 -0
  23. package/front_end/core/sdk/CSSModel.ts +21 -0
  24. package/front_end/core/sdk/CSSPropertyParser.ts +11 -2
  25. package/front_end/core/sdk/DOMModel.ts +1 -1
  26. package/front_end/core/sdk/DebuggerModel.ts +3 -0
  27. package/front_end/core/sdk/EmulationModel.ts +40 -39
  28. package/front_end/core/sdk/NetworkManager.ts +1 -0
  29. package/front_end/core/sdk/NetworkRequest.ts +21 -0
  30. package/front_end/core/sdk/PageResourceLoader.ts +141 -21
  31. package/front_end/core/sdk/ResourceTreeModel.ts +34 -10
  32. package/front_end/core/sdk/RuntimeModel.ts +8 -3
  33. package/front_end/devtools_compatibility.js +1 -0
  34. package/front_end/generated/Deprecation.ts +1 -8
  35. package/front_end/generated/InspectorBackendCommands.ts +2 -1
  36. package/front_end/generated/protocol-mapping.d.ts +8 -1
  37. package/front_end/generated/protocol-proxy-api.d.ts +7 -1
  38. package/front_end/generated/protocol.ts +9 -1
  39. package/front_end/models/ai_assistance/AiAgent2.ts +11 -5
  40. package/front_end/models/ai_assistance/AiConversation.ts +3 -2
  41. package/front_end/models/ai_assistance/AiUtils.ts +5 -2
  42. package/front_end/models/ai_assistance/README.md +2 -2
  43. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +9 -4
  44. package/front_end/models/ai_assistance/agents/AiAgent.ts +247 -121
  45. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +9 -5
  46. package/front_end/models/ai_assistance/agents/NetworkAgent.snapshot.txt +2 -2
  47. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +1 -1
  48. package/front_end/models/ai_assistance/contexts/RequestContext.snapshot.txt +2 -2
  49. package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +2 -2
  50. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.ts +9 -6
  51. package/front_end/models/ai_assistance/performance/AIQueries.ts +2 -1
  52. package/front_end/models/ai_assistance/tools/ExecuteJavaScript.ts +62 -23
  53. package/front_end/models/ai_assistance/tools/RunLighthouse.ts +6 -3
  54. package/front_end/models/ai_assistance/tools/Tool.ts +5 -1
  55. package/front_end/models/bindings/CompilerScriptMapping.ts +40 -8
  56. package/front_end/models/bindings/ResourceScriptMapping.ts +22 -3
  57. package/front_end/models/comment_manager/CommentThread.ts +13 -0
  58. package/front_end/models/computed_style/ComputedStyleModel.ts +1 -0
  59. package/front_end/models/issues_manager/GenericIssue.ts +18 -0
  60. package/front_end/models/issues_manager/descriptions/genericGeolocationPromptWithoutUserGesture.md +5 -0
  61. package/front_end/models/javascript_metadata/NativeFunctions.ts +0 -4
  62. package/front_end/models/lighthouse/RunTypes.ts +16 -0
  63. package/front_end/models/stack_trace/StackTrace.ts +10 -0
  64. package/front_end/models/stack_trace/StackTraceImpl.ts +15 -0
  65. package/front_end/models/stack_trace/StackTraceModel.ts +24 -3
  66. package/front_end/models/trace/Processor.ts +7 -9
  67. package/front_end/models/trace/extras/StackTraceForEvent.ts +1 -2
  68. package/front_end/models/trace/extras/TraceTree.ts +1 -1
  69. package/front_end/models/trace/handlers/AnimationFramesHandler.ts +4 -3
  70. package/front_end/models/trace/handlers/AsyncJSCallsHandler.ts +10 -9
  71. package/front_end/models/trace/handlers/ImagePaintingHandler.ts +6 -6
  72. package/front_end/models/trace/handlers/InitiatorsHandler.ts +6 -6
  73. package/front_end/models/trace/handlers/InvalidationsHandler.ts +2 -4
  74. package/front_end/models/trace/handlers/LayerTreeHandler.ts +3 -3
  75. package/front_end/models/trace/handlers/MetaHandler.ts +1 -1
  76. package/front_end/models/trace/handlers/NetworkRequestsHandler.ts +5 -5
  77. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +9 -9
  78. package/front_end/models/trace/handlers/ScriptsHandler.ts +2 -2
  79. package/front_end/models/trace/handlers/UserInteractionsHandler.ts +1 -1
  80. package/front_end/models/trace/handlers/WarningsHandler.ts +3 -3
  81. package/front_end/models/trace/handlers/helpers.ts +8 -5
  82. package/front_end/models/trace/helpers/Timing.ts +20 -26
  83. package/front_end/models/trace/helpers/Trace.ts +41 -14
  84. package/front_end/models/trace/insights/DuplicatedJavaScript.ts +12 -2
  85. package/front_end/models/trace/lantern/core/NetworkAnalyzer.ts +15 -1
  86. package/front_end/models/trace/types/TraceEvents.ts +213 -143
  87. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +46 -0
  88. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +23 -2
  89. package/front_end/panels/ai_assistance/README.md +3 -1
  90. package/front_end/panels/ai_assistance/components/ChatMessage.ts +1 -1
  91. package/front_end/panels/application/AppManifestView.ts +27 -8
  92. package/front_end/panels/application/serviceWorkersView.css +1 -1
  93. package/front_end/panels/comments/CommentsPane.ts +88 -35
  94. package/front_end/panels/comments/comments-meta.ts +1 -1
  95. package/front_end/panels/comments/commentsPane.css +42 -1
  96. package/front_end/panels/common/CommentThreadWidget.ts +67 -25
  97. package/front_end/panels/common/CommentsOverlayWidget.ts +13 -0
  98. package/front_end/panels/elements/DOMTreeElementWidget.ts +508 -747
  99. package/front_end/panels/elements/DOMTreeWidget.ts +418 -502
  100. package/front_end/panels/elements/PropertyRenderer.ts +103 -10
  101. package/front_end/panels/elements/StylePropertiesSection.ts +1 -1
  102. package/front_end/panels/elements/StylePropertyTreeElement.ts +107 -18
  103. package/front_end/panels/elements/stylePropertiesTreeOutline.css +6 -1
  104. package/front_end/panels/emulation/deviceModeView.css +3 -3
  105. package/front_end/panels/layer_viewer/Layers3DView.ts +6 -6
  106. package/front_end/panels/lighthouse/LighthouseController.ts +28 -21
  107. package/front_end/panels/lighthouse/LighthousePanel.ts +5 -6
  108. package/front_end/panels/lighthouse/LighthouseProtocolService.ts +27 -16
  109. package/front_end/panels/recorder/recordingView.css +1 -1
  110. package/front_end/panels/screencast/ScreencastApp.ts +2 -2
  111. package/front_end/panels/screencast/ScreencastView.ts +76 -45
  112. package/front_end/panels/settings/SettingsScreen.ts +6 -1
  113. package/front_end/panels/sources/BreakpointEditDialog.ts +3 -6
  114. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  115. package/front_end/panels/sources/SourcesPanel.ts +511 -374
  116. package/front_end/panels/sources/sourcesPanel.css +6 -31
  117. package/front_end/panels/timeline/CountersGraph.ts +6 -5
  118. package/front_end/panels/timeline/Initiators.ts +1 -1
  119. package/front_end/panels/timeline/TimelineFlameChartView.ts +10 -19
  120. package/front_end/panels/timeline/TimelineMiniMap.ts +11 -19
  121. package/front_end/panels/timeline/TimelinePanel.ts +90 -43
  122. package/front_end/panels/timeline/TimelineUIUtils.ts +2 -2
  123. package/front_end/panels/timeline/components/BreadcrumbsUI.ts +109 -87
  124. package/front_end/panels/timeline/components/LayoutShiftDetails.ts +3 -3
  125. package/front_end/panels/timeline/components/breadcrumbsUI.css +43 -24
  126. package/front_end/panels/timeline/overlays/components/TimeRangeOverlay.ts +21 -11
  127. package/front_end/panels/timeline/utils/EntryNodes.ts +1 -1
  128. package/front_end/third_party/chromium/README.chromium +1 -1
  129. package/front_end/third_party/puppeteer/README.chromium +2 -2
  130. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.d.ts +42 -12
  131. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +154 -26
  132. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPRequest.d.ts +47 -7
  133. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPRequest.d.ts.map +1 -1
  134. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPRequest.js +56 -4
  135. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/HTTPRequest.js.map +1 -1
  136. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts +6 -4
  137. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.d.ts.map +1 -1
  138. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js +5 -2
  139. package/front_end/third_party/puppeteer/package/lib/puppeteer/api/Page.js.map +1 -1
  140. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPRequest.d.ts +3 -3
  141. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPRequest.d.ts.map +1 -1
  142. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/HTTPRequest.js.map +1 -1
  143. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Page.d.ts +2 -1
  144. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Page.d.ts.map +1 -1
  145. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Page.js +23 -11
  146. package/front_end/third_party/puppeteer/package/lib/puppeteer/bidi/Page.js.map +1 -1
  147. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPRequest.d.ts +3 -3
  148. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPRequest.d.ts.map +1 -1
  149. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPRequest.js +1 -2
  150. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/HTTPRequest.js.map +1 -1
  151. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.d.ts +13 -7
  152. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  153. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.js +26 -11
  154. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/NetworkManager.js.map +1 -1
  155. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Page.d.ts +2 -1
  156. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Page.d.ts.map +1 -1
  157. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/Page.js.map +1 -1
  158. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.d.ts.map +1 -1
  159. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.js +69 -13
  160. package/front_end/third_party/puppeteer/package/lib/puppeteer/cdp/TargetManager.js.map +1 -1
  161. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/ConnectOptions.d.ts +10 -0
  162. package/front_end/third_party/puppeteer/package/lib/puppeteer/common/ConnectOptions.d.ts.map +1 -1
  163. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.d.ts +3 -3
  164. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js +3 -3
  165. package/front_end/third_party/puppeteer/package/lib/puppeteer/revisions.js.map +1 -1
  166. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.d.ts +1 -1
  167. package/front_end/third_party/puppeteer/package/lib/puppeteer/util/version.js +1 -1
  168. package/front_end/third_party/puppeteer/package/lib/types.d.ts +42 -12
  169. package/front_end/third_party/puppeteer/package/package.json +4 -4
  170. package/front_end/third_party/puppeteer/package/src/api/HTTPRequest.ts +103 -11
  171. package/front_end/third_party/puppeteer/package/src/api/Page.ts +11 -6
  172. package/front_end/third_party/puppeteer/package/src/bidi/HTTPRequest.ts +4 -4
  173. package/front_end/third_party/puppeteer/package/src/bidi/Page.ts +26 -14
  174. package/front_end/third_party/puppeteer/package/src/cdp/HTTPRequest.ts +7 -7
  175. package/front_end/third_party/puppeteer/package/src/cdp/NetworkManager.ts +40 -18
  176. package/front_end/third_party/puppeteer/package/src/cdp/Page.ts +2 -3
  177. package/front_end/third_party/puppeteer/package/src/cdp/TargetManager.ts +87 -14
  178. package/front_end/third_party/puppeteer/package/src/common/ConnectOptions.ts +10 -0
  179. package/front_end/third_party/puppeteer/package/src/revisions.ts +3 -3
  180. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  181. package/front_end/third_party/source-map-scopes-codec/README.chromium +2 -2
  182. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/decode/decode.d.ts +2 -0
  183. package/front_end/third_party/source-map-scopes-codec/package/_dist/src/decode/decode.d.ts.map +1 -1
  184. package/front_end/third_party/source-map-scopes-codec/package/deno.json +1 -1
  185. package/front_end/third_party/source-map-scopes-codec/package/package.json +1 -1
  186. package/front_end/third_party/source-map-scopes-codec/package/src/codec.js +2 -2
  187. package/front_end/third_party/source-map-scopes-codec/package/src/codec.ts +2 -2
  188. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js +19 -1
  189. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.js.map +1 -1
  190. package/front_end/third_party/source-map-scopes-codec/package/src/decode/decode.ts +26 -4
  191. package/front_end/ui/comments/CommentAnchorResolver.ts +2 -0
  192. package/front_end/ui/components/buttons/Button.ts +9 -1
  193. package/front_end/ui/components/buttons/button.css +4 -0
  194. package/front_end/ui/components/helpers/scheduled-render.ts +1 -1
  195. package/front_end/ui/legacy/GlassPane.ts +1 -1
  196. package/front_end/ui/legacy/SplitWidget.ts +10 -9
  197. package/front_end/ui/legacy/Toolbar.ts +1 -107
  198. package/front_end/ui/legacy/UIUtils.ts +133 -11
  199. package/front_end/ui/legacy/Widget.ts +5 -1
  200. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +5 -2
  201. package/front_end/ui/legacy/components/object_ui/objectPopover.css +1 -1
  202. package/front_end/ui/legacy/components/perf_ui/FlameChart.ts +47 -15
  203. package/front_end/ui/legacy/components/perf_ui/TimelineGrid.ts +1 -1
  204. package/front_end/ui/legacy/components/utils/Linkifier.ts +2 -2
  205. package/front_end/ui/legacy/theme_support/ThemeSupport.ts +6 -5
  206. package/front_end/ui/visual_logging/KnownContextValues.ts +44 -0
  207. package/front_end/ui/visual_logging/LoggingDriver.ts +4 -4
  208. package/package.json +3 -3
  209. package/.agents/skills/devtools-ux-writing-refactor/SKILL.md +0 -143
@@ -27,9 +27,18 @@ To build, follow these steps:
27
27
  ```bash
28
28
  cd devtools-frontend
29
29
  gclient sync
30
- npm run build
30
+ ./dt build
31
31
  ```
32
32
 
33
+ Scripts from `package.json` are run with `./dt <script> [args]` (on Windows:
34
+ `.\dt <script> [args]`), a launcher for `scripts/tools/dt.py`. Run it without arguments
35
+ to list the available scripts. Arguments after the script name are passed to the script
36
+ as is, so no `--` is needed.
37
+
38
+ `npm run <script> -- [args]` works too if you have npm installed, e.g.
39
+ `npm run test -- foo` is the same as `./dt test foo`. AI agents should use `./dt`, not
40
+ `npm run`.
41
+
33
42
  The resulting build artifacts can be found in `out/Default/gen/front_end`.
34
43
 
35
44
  The build tools generally assume `Default` as the target (and `out/Default` as the
@@ -37,7 +46,7 @@ build directory). You can pass `-t <name>` (or `--target=<name>`) to use a diffe
37
46
  target. For example
38
47
 
39
48
  ```bash
40
- npm run build -- -t Debug
49
+ ./dt build -t Debug
41
50
  ```
42
51
 
43
52
  will build in `out/Debug` instead of `out/Default`. If the directory doesn't exist,
@@ -51,14 +60,14 @@ to `false`:
51
60
  gn gen out/Fast --args="devtools_bundle=false"
52
61
  ```
53
62
 
54
- and use `npm run build -- -t Fast` to build this target.
63
+ and use `./dt build -t Fast` to build this target.
55
64
 
56
65
  ### Rebuilding automatically
57
66
 
58
67
  You can use
59
68
 
60
69
  ```bash
61
- npm run build -- --watch
70
+ ./dt build --watch
62
71
  ```
63
72
 
64
73
  to have the build script watch for changes in source files and automatically trigger
@@ -96,7 +105,7 @@ if you encounter any errors.
96
105
 
97
106
  ### Using a `.env` file for default script options
98
107
 
99
- Many scripts, like `npm run build` and `npm start`, accept command-line flags to configure their behavior (e.g., `-t <target>`, `--browser=<name>`).
108
+ Many scripts, like `build` and `start`, accept command-line flags to configure their behavior (e.g., `-t <target>`, `--browser=<name>`).
100
109
 
101
110
  To avoid typing these flags every time, you can set your preferred defaults in a .env file at the root of the devtools-frontend directory.
102
111
 
@@ -106,13 +115,13 @@ The `.env.template` file lists all supported variables. Copy it to `.env` to get
106
115
  cp .env.template .env
107
116
  ```
108
117
 
109
- As mentioned earlier, you might create a fast build target. Instead of always typing `npm run build -- -t Fast`, to remove the flag repetition you simple run `npm run build` you can set the following variable in your `.env` file:
118
+ As mentioned earlier, you might create a fast build target. Instead of always typing `./dt build -t Fast`, to remove the flag repetition you simple run `./dt build` you can set the following variable in your `.env` file:
110
119
 
111
120
  ```shell
112
121
  DEVTOOLS_TARGET=Fast
113
122
  ```
114
123
 
115
- Another example - by default running `npm start` auto-opens DevTools for new Tabs.
124
+ Another example - by default running `./dt start` auto-opens DevTools for new Tabs.
116
125
  You may want to disable this so fewer pop-up happen while debugging, usually done via `--no-open` flag. Or with `.env` set the following value:
117
126
 
118
127
  ```shell
@@ -154,43 +163,43 @@ You can run a [build](#Build) of DevTools frontend in a pre-built Chrome or Chro
154
163
  - Use the downloaded Chrome for Testing binary in `third_party/chrome`.
155
164
  - Use the latest Chrome Canary. This includes any DevTools features that are only available in regular Chrome builds (`is_official_build` + `is_chrome_branded`), such as GenAI-related features.
156
165
 
157
- #### Using `npm start` script (recommended)
166
+ #### Using the `start` script (recommended)
158
167
 
159
168
  With Chromium 136, we added (back) a `start` script that can be used to easily launch DevTools with pre-built [Chrome
160
169
  for Testing](https://developer.chrome.com/blog/chrome-for-testing) or [Chrome Canary](https://www.google.com/chrome/canary/).
161
170
  It'll also take care of automatically enabling/disabling experimental features that are actively being worked on. Use
162
171
 
163
172
  ```bash
164
- npm start
173
+ ./dt start
165
174
  ```
166
175
 
167
176
  to build DevTools front-end in `out/Default` (you can change this to `out/foo` by passing `--target=foo` if needed),
168
177
  and open Chrome for Testing (in `third_party/chrome`) with the custom DevTools front-end. This will also monitor the
169
178
  source files for changes while Chrome is running and automatically trigger a rebuild whenever source files change.
170
179
 
171
- By default, `npm start` will automatically open DevTools for every new tab, you can use
180
+ By default, `./dt start` will automatically open DevTools for every new tab, you can use
172
181
 
173
182
  ```bash
174
- npm start -- --no-open
183
+ ./dt start --no-open
175
184
  ```
176
185
 
177
186
  to disable this behavior. You can also use
178
187
 
179
188
  ```bash
180
- npm start -- --browser=canary
189
+ ./dt start --browser=canary
181
190
  ```
182
191
 
183
192
  to run in Chrome Canary instead of Chrome for Testing; this requires you to install Chrome Canary manually first
184
193
  (Googlers can install `google-chrome-canary` on gLinux). And finally use
185
194
 
186
195
  ```bash
187
- npm start -- http://www.example.com
196
+ ./dt start http://www.example.com
188
197
  ```
189
198
 
190
199
  to automatically open `http://www.example.com` in the newly spawned Chrome tab. Use
191
200
 
192
201
  ```bash
193
- npm start -- --verbose
202
+ ./dt start --verbose
194
203
  ```
195
204
 
196
205
  to enable verbose logging, which among other things, also prints all output from Chrome to the terminal, which is
@@ -198,38 +207,38 @@ otherwise suppressed.
198
207
 
199
208
  ##### Controlling the feature set
200
209
 
201
- By default `npm start` will enable a bunch of experimental features (related to DevTools) that are considered ready for teamfood.
210
+ By default `./dt start` will enable a bunch of experimental features (related to DevTools) that are considered ready for teamfood.
202
211
  To also enable experimental features that aren't yet considered sufficiently stable to enable them by default for the team, run:
203
212
 
204
213
  ```bash
205
214
  # Long version
206
- npm start -- --unstable-features
215
+ ./dt start --unstable-features
207
216
 
208
217
  # Short version
209
- npm start -- -u
218
+ ./dt start -u
210
219
  ```
211
220
 
212
221
  Just like with Chrome itself, you can also control the set of enabled and disabled features using
213
222
 
214
223
  ```bash
215
- npm start -- --enable-features=DevToolsWellKnown
216
- npm start -- --disable-features=DevToolsWellKnown --enable-features=DevToolsFreestyler:multimodal/true
224
+ ./dt start --enable-features=DevToolsWellKnown
225
+ ./dt start --disable-features=DevToolsWellKnown --enable-features=DevToolsFreestyler:multimodal/true
217
226
  ```
218
227
 
219
228
  which you can use to override the default feature set.
220
229
 
221
230
  ##### Remote debugging
222
231
 
223
- The `npm start` command also supports launching Chrome for remote debugging via
232
+ The `./dt start` command also supports launching Chrome for remote debugging via
224
233
 
225
234
  ```bash
226
- npm start -- --remote-debugging-port=9222
235
+ ./dt start --remote-debugging-port=9222
227
236
  ```
228
237
 
229
238
  or
230
239
 
231
240
  ```bash
232
- npm start -- --browser=canary --remote-debugging-port=9222 --user-data-dir=\`mktemp -d`
241
+ ./dt start --browser=canary --remote-debugging-port=9222 --user-data-dir=\`mktemp -d`
233
242
  ```
234
243
 
235
244
  Note that you have to also pass the `--user-data-dir` and point it to a non-standard profile directory (a freshly created
@@ -362,22 +371,22 @@ out/Default/chrome --custom-devtools-frontend=file://$(realpath out/Default/gen/
362
371
  afterwards, which can be quite a bit faster than building and linking the full
363
372
  Chromium binary.
364
373
 
365
- Alternatively you can use `npm start` from the DevTools sub folder (`third_party/devtools-frontend/src/`) with the browser set to `chromium`.
374
+ Alternatively you can use `./dt start` from the DevTools sub folder (`third_party/devtools-frontend/src/`) with the browser set to `chromium`.
366
375
  This will tell the command that you are in a Chromium checkout and try to find the correct browser executable path to resolve.
367
376
  Or alternately you can use a `.env` file, see [set up here](#using-a-env-file-for-default-script-options).
368
377
 
369
378
  ```bash
370
379
  cd third_party/devtools-frontend/src/
371
- npm start -- --browser=chromium
380
+ ./dt start --browser=chromium
372
381
  ```
373
382
 
374
383
  ### Testing
375
384
 
376
- To run the test suite, use `npm test` from within the DevTools front-end folder:
385
+ To run the test suite, use `./dt test` from within the DevTools front-end folder:
377
386
 
378
387
  ```bash
379
388
  cd third_party/devtools-frontend/src
380
- npm test
389
+ ./dt test
381
390
  ```
382
391
 
383
392
  ### Juggling the git submodules
@@ -906,6 +906,34 @@ export const DEFAULT_VIEW = (input, _output, target) => {
906
906
  };
907
907
  ```
908
908
 
909
+ ### Long-press action buttons
910
+
911
+ Imperative long-press action buttons (`UI.Toolbar.Toolbar.createLongPressActionButton`) are migrated declaratively by nesting secondary `<devtools-button>` elements inside `<devtools-button ${bindToAction('...')}>`. The parent button triggers the primary action on click, and holding the button down opens a menu presenting the nested buttons:
912
+
913
+ ```typescript
914
+ export const DEFAULT_VIEW = (input, _output, target) => {
915
+ const {bindToAction} = UI.UIUtils;
916
+ render(html`
917
+ <devtools-toolbar>
918
+ <devtools-button ${bindToAction('debugger.toggle-pause')}>
919
+ <devtools-button
920
+ .iconName=${'stop'}
921
+ .variant=${Buttons.Button.Variant.TOOLBAR}
922
+ .title=${i18nString(UIStrings.terminateCurrentJavascriptCall)}
923
+ @click=${input.onTerminateExecution}>
924
+ </devtools-button>
925
+ <devtools-button
926
+ .iconName=${'play'}
927
+ .variant=${Buttons.Button.Variant.TOOLBAR}
928
+ .title=${i18nString(UIStrings.resumeWithAllPausesBlockedForMs)}
929
+ @click=${input.onLongResume}>
930
+ </devtools-button>
931
+ </devtools-button>
932
+ </devtools-toolbar>`,
933
+ target);
934
+ };
935
+ ```
936
+
909
937
  ## Binding a setting to a checkbox
910
938
 
911
939
  Use the `bindToSetting` directive to bind a boolean setting to a `<devtools-checkbox>` component.
@@ -1244,6 +1272,12 @@ export const DEFAULT_VIEW = (input, _output, target) => {
1244
1272
  };
1245
1273
  ```
1246
1274
 
1275
+ ### Pre-existing Widget Instances (`WrapperWidget`)
1276
+ When embedding pre-existing `UI.Widget` instances that are created elsewhere (such as `UI.ViewManager` locations: `TabbedViewLocation.tabbedPane()`, `StackLocation.widget()`), render them declaratively using `${widget(UI.Widget.WrapperWidget, {widget: myWidgetInstance})}` instead of creating custom wrapper widget classes. `WrapperWidget` automatically forwards constraints and manages child widget attach/detach lifecycles.
1277
+
1278
+ ### Preserving `showMode` Settings
1279
+ When migrating a `SplitWidget` that persists sidebar visibility (e.g. via `splitWidget.showModeSetting()`), bind the setting's value to the `<devtools-split-view show=${input.showMode}>` property (`Both`, `OnlyMain`, or `OnlySidebar`) to preserve user settings and layout state across sessions.
1280
+
1247
1281
  ## Migrating `UI.ARIAUtils` helpers
1248
1282
 
1249
1283
  Replace calls to `UI.ARIAUtils` helper functions with the corresponding ARIA attributes directly in the lit-html template.
package/dt ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+ # Copyright 2026 The Chromium Authors
3
+ # Use of this source code is governed by a BSD-style license that can be
4
+ # found in the LICENSE file.
5
+ #
6
+ # Launcher for scripts/tools/dt.py, so it can be run as `./dt <script> [args]`.
7
+ exec vpython3 "$(dirname "$0")/scripts/tools/dt.py" "$@"
package/dt.bat ADDED
@@ -0,0 +1,8 @@
1
+ @echo off
2
+ rem Copyright 2026 The Chromium Authors
3
+ rem Use of this source code is governed by a BSD-style license that can be
4
+ rem found in the LICENSE file.
5
+ rem
6
+ rem Launcher for scripts\tools\dt.py, so it can be run as `dt <script> [args]`.
7
+ call vpython3 "%~dp0scripts\tools\dt.py" %*
8
+ exit /b %ERRORLEVEL%
@@ -334,7 +334,7 @@ export class ParsedURL {
334
334
  // 8 - ?fragment
335
335
  const schemeRegex = /([A-Za-z][A-Za-z0-9+.-]*):\/\//;
336
336
  const userRegex = /(?:([A-Za-z0-9\-._~%!$&'()*+,;=:]*)@)?/;
337
- const hostRegex = /((?:\[::\d?\])|(?:[^\s\/:]*))/;
337
+ const hostRegex = /((?:\[[0-9a-fA-F:.]*:[0-9a-fA-F:.]*\])|(?:[^\s\/:]*))/;
338
338
  const portRegex = /(?::([\d]+))?/;
339
339
  const pathRegex = /(\/[^#?]*)?/;
340
340
  const queryRegex = /(?:\?([^#]*))?/;
@@ -258,6 +258,11 @@ export class Settings {
258
258
  return setting;
259
259
  }
260
260
 
261
+ /** The event target shared by all settings. Change listeners are registered with the setting name as event type. */
262
+ get eventTargetForTest(): ObjectWrapper<GenericEvents> {
263
+ return this.#eventSupport;
264
+ }
265
+
261
266
  /**
262
267
  * Get setting via key, and create a new setting if the requested setting does not exist.
263
268
  * @param key kebab-case string ID
@@ -636,6 +636,7 @@ export enum IssueCreated {
636
636
  'GenericIssue::AutofillAndManualTextPolicyControlledFeaturesInfo' = 114,
637
637
  'GenericIssue::AutofillPolicyControlledFeatureInfo' = 115,
638
638
  'GenericIssue::ManualTextPolicyControlledFeatureInfo' = 116,
639
+ 'GenericIssue::GeolocationPromptWithoutUserGesture' = 117,
639
640
  'CorsIssue::InvalidLocalNetworkAccess' = 118,
640
641
  /* eslint-enable @typescript-eslint/naming-convention */
641
642
  MAX_VALUE = 119,
@@ -537,6 +537,24 @@ export class CSSModel extends SDKModel<EventTypes> {
537
537
  return true;
538
538
  }
539
539
 
540
+ forcePositionTryOption(node: DOMNode, index?: number): boolean {
541
+ const currentForcedIndex = node.marker<number>(PositionTryOptionMarker) ?? undefined;
542
+ if (currentForcedIndex === index) {
543
+ return false;
544
+ }
545
+ node.setMarker(PositionTryOptionMarker, index ?? null);
546
+ if (node.id === undefined) {
547
+ return false;
548
+ }
549
+ void this.agent.invoke_forcePositionTryOption({nodeId: node.id, index});
550
+ this.dispatchEventToListeners(Events.PositionTryOptionForced, node);
551
+ return true;
552
+ }
553
+
554
+ forcedPositionTryOption(node: DOMNode): number|undefined {
555
+ return node.marker<number>(PositionTryOptionMarker) ?? undefined;
556
+ }
557
+
540
558
  forcePseudoState(node: DOMNode, pseudoClass: string, enable: boolean): boolean {
541
559
  const forcedPseudoClasses = node.marker<string[]>(PseudoStateMarker) || [];
542
560
  const hasPseudoClass = forcedPseudoClasses.includes(pseudoClass);
@@ -1058,6 +1076,7 @@ export enum Events {
1058
1076
  MediaQueryResultChanged = 'MediaQueryResultChanged',
1059
1077
  ModelWasEnabled = 'ModelWasEnabled',
1060
1078
  ModelDisposed = 'ModelDisposed',
1079
+ PositionTryOptionForced = 'PositionTryOptionForced',
1061
1080
  PseudoStateForced = 'PseudoStateForced',
1062
1081
  StartingStylesStateForced = 'StartingStylesStateForced',
1063
1082
  StyleSheetAdded = 'StyleSheetAdded',
@@ -1087,6 +1106,7 @@ export interface EventTypes {
1087
1106
  [Events.MediaQueryResultChanged]: void;
1088
1107
  [Events.ModelWasEnabled]: void;
1089
1108
  [Events.ModelDisposed]: CSSModel;
1109
+ [Events.PositionTryOptionForced]: DOMNode;
1090
1110
  [Events.PseudoStateForced]: PseudoStateForcedEvent;
1091
1111
  [Events.StartingStylesStateForced]: DOMNode;
1092
1112
  [Events.StyleSheetAdded]: CSSStyleSheetHeader;
@@ -1096,6 +1116,7 @@ export interface EventTypes {
1096
1116
  }
1097
1117
 
1098
1118
  const PseudoStateMarker = 'pseudo-state-marker';
1119
+ const PositionTryOptionMarker = 'position-try-option-marker';
1099
1120
 
1100
1121
  export class Edit {
1101
1122
  styleSheetId: string;
@@ -341,7 +341,7 @@ class ComputedTextChunk {
341
341
  * original and computed slices as required.
342
342
  **/
343
343
  export class ComputedText {
344
- readonly #chunks: ComputedTextChunk[] = [];
344
+ #chunks: ComputedTextChunk[] = [];
345
345
  readonly text: string;
346
346
  readonly #topLevelValueCounts = new Map<string, number>();
347
347
 
@@ -356,6 +356,7 @@ export class ComputedText {
356
356
  }
357
357
 
358
358
  get chunkCount(): number {
359
+ this.#sortIfNecessary();
359
360
  return this.#chunks.length;
360
361
  }
361
362
 
@@ -363,6 +364,14 @@ export class ComputedText {
363
364
  if (this.#sorted) {
364
365
  return;
365
366
  }
367
+ // A match that was pushed more than once is superseded by its last push.
368
+ const lastPush = new Map<Match, ComputedTextChunk>();
369
+ for (const chunk of this.#chunks) {
370
+ lastPush.set(chunk.match, chunk);
371
+ }
372
+ if (lastPush.size < this.#chunks.length) {
373
+ this.#chunks = this.#chunks.filter(chunk => lastPush.get(chunk.match) === chunk);
374
+ }
366
375
  // Sort intervals by offset, with longer intervals first if the offset is identical.
367
376
  this.#chunks.sort((a, b) => {
368
377
  if (a.offset < b.offset) {
@@ -383,7 +392,7 @@ export class ComputedText {
383
392
  }
384
393
 
385
394
  // Add another substitutable match. The match will either be appended to the list of existing matches or it will
386
- // be substituted for the last match(es) if it encompasses them.
395
+ // be substituted for the last match(es) if it encompasses them. Pushing a match again supersedes its earlier push.
387
396
  push(match: Match, offset: number): void {
388
397
  function hasComputedText(match: Match): match is MatchWithComputedText {
389
398
  return Boolean(match.computedText);
@@ -1188,7 +1188,7 @@ export class DOMNode extends Common.ObjectWrapper.ObjectWrapper<DOMNodeEventType
1188
1188
  }
1189
1189
 
1190
1190
  marker<T>(name: string): T|null {
1191
- return this.#markers.get(name) as T || null;
1191
+ return (this.#markers.get(name) as T) ?? null;
1192
1192
  }
1193
1193
 
1194
1194
  getMarkerKeysForTest(): string[] {
@@ -959,6 +959,9 @@ export class DebuggerModel extends SDKModel<EventTypes> {
959
959
  this.#disableAsyncStackTracesSetting.removeChangeListener(this.asyncStackTracesStateChanged, this);
960
960
  this.#breakpointsActiveSetting.removeChangeListener(this.breakpointsActiveChanged, this);
961
961
  this.#jsSourceMapsEnabledSetting.removeChangeListener(this.jsSourceMapsStateChanged, this);
962
+ // Drop all scripts and their source maps, so they don't outlive the target in case something
963
+ // still holds on to this model (e.g. a console message with "Preserve log" enabled).
964
+ this.reset();
962
965
  }
963
966
 
964
967
  override async suspendModel(): Promise<void> {
@@ -4,7 +4,7 @@
4
4
 
5
5
  import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
6
6
  import * as Protocol from '../../generated/protocol.js';
7
- import type * as Common from '../common/common.js';
7
+ import * as Common from '../common/common.js';
8
8
 
9
9
  import {CSSModel} from './CSSModel.js';
10
10
  import type {MultitargetNetworkManager} from './NetworkManager.js';
@@ -59,7 +59,7 @@ export class EmulationModel extends SDKModel<EmulationModelEventTypes> implement
59
59
  #screenOrientationLocked: boolean;
60
60
  #lockedOrientation: Protocol.Emulation.ScreenOrientation|null;
61
61
  readonly #dataSaverSetting: Common.Settings.Setting<DataSaverOverride>;
62
- readonly #dataSaverChangeListener: () => void;
62
+ readonly #settingListeners: Common.EventTarget.EventDescriptor[] = [];
63
63
 
64
64
  constructor(target: Target) {
65
65
  super(target);
@@ -78,22 +78,22 @@ export class EmulationModel extends SDKModel<EmulationModelEventTypes> implement
78
78
 
79
79
  const settings = this.target().targetManager().settings;
80
80
  const disableJavascriptSetting = settings.resolve(javaScriptDisabledSettingDescriptor);
81
- disableJavascriptSetting.addChangeListener(
81
+ this.#settingListeners.push(disableJavascriptSetting.addChangeListener(
82
82
  async () =>
83
- await this.#emulationAgent.invoke_setScriptExecutionDisabled({value: disableJavascriptSetting.get()}));
83
+ await this.#emulationAgent.invoke_setScriptExecutionDisabled({value: disableJavascriptSetting.get()})));
84
84
  if (disableJavascriptSetting.get()) {
85
85
  void this.#emulationAgent.invoke_setScriptExecutionDisabled({value: true});
86
86
  }
87
87
 
88
88
  const touchSetting = settings.resolve(touchSettingDescriptor);
89
- touchSetting.addChangeListener(() => {
89
+ this.#settingListeners.push(touchSetting.addChangeListener(() => {
90
90
  const settingValue = touchSetting.get();
91
91
 
92
92
  void this.overrideEmulateTouch(settingValue === 'force');
93
- });
93
+ }));
94
94
 
95
95
  const idleDetectionSetting = settings.resolve(idleDetectionSettingDescriptor);
96
- idleDetectionSetting.addChangeListener(async () => {
96
+ this.#settingListeners.push(idleDetectionSetting.addChangeListener(async () => {
97
97
  const settingValue = idleDetectionSetting.get();
98
98
  if (settingValue === 'none') {
99
99
  await this.clearIdleOverride();
@@ -105,10 +105,10 @@ export class EmulationModel extends SDKModel<EmulationModelEventTypes> implement
105
105
  isScreenUnlocked: boolean,
106
106
  });
107
107
  await this.setIdleOverride(emulationParams);
108
- });
108
+ }));
109
109
 
110
110
  const cpuPressureDetectionSetting = settings.resolve(cpuPressureSettingDescriptor);
111
- cpuPressureDetectionSetting.addChangeListener(async () => {
111
+ this.#settingListeners.push(cpuPressureDetectionSetting.addChangeListener(async () => {
112
112
  const settingValue = cpuPressureDetectionSetting.get();
113
113
 
114
114
  if (settingValue === 'none') {
@@ -123,7 +123,7 @@ export class EmulationModel extends SDKModel<EmulationModelEventTypes> implement
123
123
  }
124
124
 
125
125
  await this.setPressureStateOverride(settingValue);
126
- });
126
+ }));
127
127
 
128
128
  const mediaTypeSetting = settings.resolve(emulatedCSSMediaSettingDescriptor);
129
129
  const mediaFeatureColorGamutSetting = settings.resolve(emulatedCSSMediaFeatureColorGamutSettingDescriptor);
@@ -152,67 +152,69 @@ export class EmulationModel extends SDKModel<EmulationModelEventTypes> implement
152
152
  ['prefers-reduced-motion', mediaFeaturePrefersReducedMotionSetting.get()],
153
153
  ['prefers-reduced-transparency', mediaFeaturePrefersReducedTransparencySetting.get()],
154
154
  ]);
155
- mediaTypeSetting.addChangeListener(() => {
155
+ this.#settingListeners.push(mediaTypeSetting.addChangeListener(() => {
156
156
  this.#mediaConfiguration.set('type', mediaTypeSetting.get());
157
157
  void this.updateCssMedia();
158
- });
159
- mediaFeatureColorGamutSetting.addChangeListener(() => {
158
+ }));
159
+ this.#settingListeners.push(mediaFeatureColorGamutSetting.addChangeListener(() => {
160
160
  this.#mediaConfiguration.set('color-gamut', mediaFeatureColorGamutSetting.get());
161
161
  void this.updateCssMedia();
162
- });
163
- mediaFeaturePrefersColorSchemeSetting.addChangeListener(() => {
162
+ }));
163
+ this.#settingListeners.push(mediaFeaturePrefersColorSchemeSetting.addChangeListener(() => {
164
164
  this.#mediaConfiguration.set('prefers-color-scheme', mediaFeaturePrefersColorSchemeSetting.get());
165
165
  void this.updateCssMedia();
166
- });
167
- mediaFeatureForcedColorsSetting.addChangeListener(() => {
166
+ }));
167
+ this.#settingListeners.push(mediaFeatureForcedColorsSetting.addChangeListener(() => {
168
168
  this.#mediaConfiguration.set('forced-colors', mediaFeatureForcedColorsSetting.get());
169
169
  void this.updateCssMedia();
170
- });
171
- mediaFeaturePrefersContrastSetting.addChangeListener(() => {
170
+ }));
171
+ this.#settingListeners.push(mediaFeaturePrefersContrastSetting.addChangeListener(() => {
172
172
  this.#mediaConfiguration.set('prefers-contrast', mediaFeaturePrefersContrastSetting.get());
173
173
  void this.updateCssMedia();
174
- });
175
- mediaFeaturePrefersReducedDataSetting.addChangeListener(() => {
174
+ }));
175
+ this.#settingListeners.push(mediaFeaturePrefersReducedDataSetting.addChangeListener(() => {
176
176
  this.#mediaConfiguration.set('prefers-reduced-data', mediaFeaturePrefersReducedDataSetting.get());
177
177
  void this.updateCssMedia();
178
- });
179
- mediaFeaturePrefersReducedMotionSetting.addChangeListener(() => {
178
+ }));
179
+ this.#settingListeners.push(mediaFeaturePrefersReducedMotionSetting.addChangeListener(() => {
180
180
  this.#mediaConfiguration.set('prefers-reduced-motion', mediaFeaturePrefersReducedMotionSetting.get());
181
181
  void this.updateCssMedia();
182
- });
183
- mediaFeaturePrefersReducedTransparencySetting.addChangeListener(() => {
182
+ }));
183
+ this.#settingListeners.push(mediaFeaturePrefersReducedTransparencySetting.addChangeListener(() => {
184
184
  this.#mediaConfiguration.set('prefers-reduced-transparency', mediaFeaturePrefersReducedTransparencySetting.get());
185
185
  void this.updateCssMedia();
186
- });
186
+ }));
187
187
  void this.updateCssMedia();
188
188
 
189
189
  const autoDarkModeSetting = settings.resolve(emulateAutoDarkModeSettingDescriptor);
190
- autoDarkModeSetting.addChangeListener(() => {
190
+ this.#settingListeners.push(autoDarkModeSetting.addChangeListener(() => {
191
191
  const enabled = autoDarkModeSetting.get();
192
192
  mediaFeaturePrefersColorSchemeSetting.set(enabled ? 'dark' : '');
193
193
  void this.emulateAutoDarkMode(enabled);
194
- });
194
+ }));
195
195
  if (autoDarkModeSetting.get()) {
196
196
  mediaFeaturePrefersColorSchemeSetting.set('dark');
197
197
  void this.emulateAutoDarkMode(true);
198
198
  }
199
199
 
200
200
  const visionDeficiencySetting = settings.resolve(emulatedVisionDeficiencySettingDescriptor);
201
- visionDeficiencySetting.addChangeListener(() => this.emulateVisionDeficiency(visionDeficiencySetting.get()));
201
+ this.#settingListeners.push(
202
+ visionDeficiencySetting.addChangeListener(() => this.emulateVisionDeficiency(visionDeficiencySetting.get())));
202
203
  if (visionDeficiencySetting.get()) {
203
204
  void this.emulateVisionDeficiency(visionDeficiencySetting.get());
204
205
  }
205
206
 
206
207
  const osTextScaleSetting = settings.resolve(emulatedOSTextScaleSettingDescriptor);
207
- osTextScaleSetting.addChangeListener(() => {
208
+ this.#settingListeners.push(osTextScaleSetting.addChangeListener(() => {
208
209
  void this.emulateOSTextScale(parseFloat(osTextScaleSetting.get()) || undefined);
209
- });
210
+ }));
210
211
  if (osTextScaleSetting.get()) {
211
212
  void this.emulateOSTextScale(parseFloat(osTextScaleSetting.get()) || undefined);
212
213
  }
213
214
 
214
215
  const localFontsDisabledSetting = settings.resolve(localFontsDisabledSettingDescriptor);
215
- localFontsDisabledSetting.addChangeListener(() => this.setLocalFontsDisabled(localFontsDisabledSetting.get()));
216
+ this.#settingListeners.push(
217
+ localFontsDisabledSetting.addChangeListener(() => this.setLocalFontsDisabled(localFontsDisabledSetting.get())));
216
218
  if (localFontsDisabledSetting.get()) {
217
219
  this.setLocalFontsDisabled(localFontsDisabledSetting.get());
218
220
  }
@@ -235,19 +237,18 @@ export class EmulationModel extends SDKModel<EmulationModelEventTypes> implement
235
237
  this.setDisabledImageTypes(types);
236
238
  };
237
239
 
238
- avifFormatDisabledSetting.addChangeListener(updateDisabledImageFormats);
239
- jpegXlFormatDisabledSetting.addChangeListener(updateDisabledImageFormats);
240
- webpFormatDisabledSetting.addChangeListener(updateDisabledImageFormats);
240
+ this.#settingListeners.push(avifFormatDisabledSetting.addChangeListener(updateDisabledImageFormats));
241
+ this.#settingListeners.push(jpegXlFormatDisabledSetting.addChangeListener(updateDisabledImageFormats));
242
+ this.#settingListeners.push(webpFormatDisabledSetting.addChangeListener(updateDisabledImageFormats));
241
243
 
242
244
  if (avifFormatDisabledSetting.get() || jpegXlFormatDisabledSetting.get() || webpFormatDisabledSetting.get()) {
243
245
  updateDisabledImageFormats();
244
246
  }
245
247
 
246
248
  this.#dataSaverSetting = settings.resolve(dataSaverSettingDescriptor);
247
- this.#dataSaverChangeListener = () => {
249
+ this.#settingListeners.push(this.#dataSaverSetting.addChangeListener(() => {
248
250
  void this.setDataSaverOverride(this.#dataSaverSetting.get());
249
- };
250
- this.#dataSaverSetting.addChangeListener(this.#dataSaverChangeListener, this);
251
+ }));
251
252
  if (this.#dataSaverSetting.get() !== DataSaverOverride.UNSET) {
252
253
  void this.setDataSaverOverride(this.#dataSaverSetting.get());
253
254
  }
@@ -266,7 +267,7 @@ export class EmulationModel extends SDKModel<EmulationModelEventTypes> implement
266
267
 
267
268
  override dispose(): void {
268
269
  super.dispose();
269
- this.#dataSaverSetting.removeChangeListener(this.#dataSaverChangeListener, this);
270
+ Common.EventTarget.removeEventListeners(this.#settingListeners);
270
271
  }
271
272
 
272
273
  setTouchEmulationAllowed(touchEmulationAllowed: boolean): void {
@@ -563,6 +563,7 @@ export class NetworkManager extends SDKModel<EventTypes> {
563
563
  const settings = this.target().targetManager().settings;
564
564
  settings.resolve(cacheDisabledSettingDescriptor).removeChangeListener(this.cacheDisabledSettingChanged, this);
565
565
  settings.resolve(preserveNetworkLogSettingDescriptor).removeChangeListener(this.preserveLogChanged, this);
566
+ this.#bypassServiceWorkerSetting.removeChangeListener(this.bypassServiceWorkerChanged, this);
566
567
  }
567
568
 
568
569
  private bypassServiceWorkerChanged(): void {
@@ -1506,6 +1506,27 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper<EventType
1506
1506
  return this.#contentData;
1507
1507
  }
1508
1508
 
1509
+ /**
1510
+ * Returns the response body without waiting for the request to finish loading.
1511
+ *
1512
+ * `requestContentData()` waits for `FINISHED_LOADING` when the request has no
1513
+ * content data provider. Some requests never finish: Chrome does not send
1514
+ * `loadingFinished` for a `Cache-Control: no-store` fetch() whose body the page
1515
+ * never reads, and synthetic requests without a provider are never finished at all.
1516
+ * Callers that must not block, such as AI context building, use this method instead.
1517
+ *
1518
+ * Requests with a content data provider (Local Overrides, Cache Storage, HAR
1519
+ * imports) resolve even when they have not finished.
1520
+ */
1521
+ requestContentDataIfAvailable(): Promise<TextUtils.ContentData.ContentDataOrError> {
1522
+ // A content data provider returns the body without waiting for the request
1523
+ // to finish, so only requests without a content data provider can block.
1524
+ if (!this.finished && !this.#contentDataProvider) {
1525
+ return Promise.resolve({error: 'Request has not finished loading'});
1526
+ }
1527
+ return this.requestContentData();
1528
+ }
1529
+
1509
1530
  setContentDataProvider(
1510
1531
  dataProvider: () => Promise<TextUtils.ContentData.ContentDataOrError>,
1511
1532
  ): void {