chrome-devtools-frontend 1.0.1596535 → 1.0.1597624

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 (101) hide show
  1. package/agents/prompts/ui-widgets.md +7 -8
  2. package/docs/ui_engineering.md +10 -11
  3. package/front_end/core/host/AidaClient.ts +4 -0
  4. package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
  5. package/front_end/core/host/UserMetrics.ts +12 -0
  6. package/front_end/core/root/Runtime.ts +5 -0
  7. package/front_end/core/sdk/CPUThrottlingManager.ts +14 -13
  8. package/front_end/core/sdk/CSSMatchedStyles.ts +2 -0
  9. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +28 -0
  10. package/front_end/core/sdk/PageResourceLoader.ts +22 -1
  11. package/front_end/devtools_compatibility.js +2 -1
  12. package/front_end/models/ai_assistance/AiConversation.ts +29 -8
  13. package/front_end/models/ai_assistance/ChangeManager.ts +16 -0
  14. package/front_end/models/ai_assistance/ExtensionScope.ts +11 -3
  15. package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +127 -0
  16. package/front_end/models/ai_assistance/agents/AiAgent.ts +26 -3
  17. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +1 -1
  18. package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +11 -8
  19. package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +24 -0
  20. package/front_end/models/ai_assistance/agents/StylingAgent.ts +323 -16
  21. package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
  22. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +27 -0
  23. package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +21 -0
  24. package/front_end/models/greendev/Prototypes.ts +7 -1
  25. package/front_end/models/trace/Processor.ts +1 -0
  26. package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -0
  27. package/front_end/models/trace/insights/CharacterSet.ts +172 -0
  28. package/front_end/models/trace/insights/Models.ts +1 -0
  29. package/front_end/models/trace/insights/types.ts +1 -0
  30. package/front_end/models/trace/types/TraceEvents.ts +17 -0
  31. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +51 -36
  32. package/front_end/panels/ai_assistance/PatchWidget.ts +6 -6
  33. package/front_end/panels/ai_assistance/components/ChatMessage.ts +93 -74
  34. package/front_end/panels/ai_assistance/components/ChatView.ts +6 -11
  35. package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +18 -9
  36. package/front_end/panels/ai_assistance/components/StylingAgentMarkdownRenderer.ts +200 -0
  37. package/front_end/panels/ai_assistance/components/chatMessage.css +11 -8
  38. package/front_end/panels/application/AppManifestView.ts +3 -4
  39. package/front_end/panels/application/DeviceBoundSessionsView.ts +18 -22
  40. package/front_end/panels/application/FrameDetailsView.ts +9 -15
  41. package/front_end/panels/application/OriginTrialTreeView.ts +2 -3
  42. package/front_end/panels/application/ReportingApiView.ts +13 -17
  43. package/front_end/panels/application/ServiceWorkerCacheViews.ts +1 -1
  44. package/front_end/panels/application/components/BackForwardCacheView.ts +3 -3
  45. package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -3
  46. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +3 -2
  47. package/front_end/panels/changes/ChangesView.ts +6 -4
  48. package/front_end/panels/common/ExtensionServer.ts +15 -0
  49. package/front_end/panels/console/ConsolePinPane.ts +3 -3
  50. package/front_end/panels/coverage/CoverageListView.ts +1 -1
  51. package/front_end/panels/css_overview/CSSOverviewPanel.ts +11 -15
  52. package/front_end/panels/developer_resources/DeveloperResourcesView.ts +3 -5
  53. package/front_end/panels/elements/ElementsTreeElement.ts +55 -47
  54. package/front_end/panels/elements/ElementsTreeOutline.ts +149 -28
  55. package/front_end/panels/elements/EventListenersWidget.ts +3 -2
  56. package/front_end/panels/elements/StandaloneStylesContainer.ts +21 -6
  57. package/front_end/panels/elements/StylePropertyTreeElement.ts +49 -4
  58. package/front_end/panels/layer_viewer/Layers3DView.ts +5 -4
  59. package/front_end/panels/lighthouse/LighthousePanel.ts +8 -0
  60. package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +5 -6
  61. package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +6 -11
  62. package/front_end/panels/network/RequestCookiesView.ts +3 -4
  63. package/front_end/panels/network/RequestInitiatorView.ts +7 -5
  64. package/front_end/panels/network/RequestResponseView.ts +10 -15
  65. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +3 -4
  66. package/front_end/panels/recorder/components/RecordingView.ts +31 -36
  67. package/front_end/panels/recorder/components/StepEditor.ts +6 -7
  68. package/front_end/panels/search/SearchView.ts +2 -3
  69. package/front_end/panels/settings/SettingsScreen.ts +3 -2
  70. package/front_end/panels/settings/WorkspaceSettingsTab.ts +2 -5
  71. package/front_end/panels/timeline/components/LiveMetricsView.ts +5 -8
  72. package/front_end/panels/timeline/components/insights/Cache.ts +8 -10
  73. package/front_end/panels/timeline/components/insights/CharacterSet.ts +38 -0
  74. package/front_end/panels/timeline/components/insights/DOMSize.ts +16 -20
  75. package/front_end/panels/timeline/components/insights/DocumentLatency.ts +2 -6
  76. package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +3 -4
  77. package/front_end/panels/timeline/components/insights/FontDisplay.ts +3 -4
  78. package/front_end/panels/timeline/components/insights/ForcedReflow.ts +5 -7
  79. package/front_end/panels/timeline/components/insights/INPBreakdown.ts +3 -4
  80. package/front_end/panels/timeline/components/insights/ImageDelivery.ts +3 -4
  81. package/front_end/panels/timeline/components/insights/ImageRef.ts +2 -4
  82. package/front_end/panels/timeline/components/insights/InsightRenderer.ts +2 -0
  83. package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +5 -7
  84. package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +2 -4
  85. package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +3 -4
  86. package/front_end/panels/timeline/components/insights/ModernHTTP.ts +3 -4
  87. package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +7 -11
  88. package/front_end/panels/timeline/components/insights/NodeLink.ts +2 -4
  89. package/front_end/panels/timeline/components/insights/RenderBlocking.ts +3 -4
  90. package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +7 -10
  91. package/front_end/panels/timeline/components/insights/ThirdParties.ts +5 -7
  92. package/front_end/panels/timeline/components/insights/insights.ts +2 -0
  93. package/front_end/panels/web_audio/WebAudioView.ts +3 -4
  94. package/front_end/ui/components/settings/SettingCheckbox.ts +2 -0
  95. package/front_end/ui/legacy/UIUtils.ts +5 -5
  96. package/front_end/ui/legacy/Widget.ts +33 -2
  97. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +3 -3
  98. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +8 -8
  99. package/front_end/ui/visual_logging/Debugging.ts +0 -32
  100. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  101. package/package.json +1 -1
@@ -11,7 +11,7 @@ Adhere strictly to the Model-View-Presenter (MVP) architecture.
11
11
  ### Presenter (`Widget`) Rules
12
12
 
13
13
  * Location: Co-located in the same file as its View.
14
- * MUST extend a base `UI.Widget` class (e.g., `UI.Widget.Widget`). Note that `UI.Widget.Widget` is not an `HTMLElement` and must be appended via `.show()` or `<devtools-widget>`.
14
+ * MUST extend a base `UI.Widget` class (e.g., `UI.Widget.Widget`). Note that `UI.Widget.Widget` is not an `HTMLElement` and must be appended via `.show()` or `UI.Widget.widget`
15
15
  * Constructor MUST assign the injected view function to a private `#view` field.
16
16
  * Constructor MUST call `super()`. If taking `element?: HTMLElement`, pass it to `super(element)`. `super(true)` is forbidden.
17
17
  * Styling MUST be handled within the View. `this.registerCSSFiles()` is forbidden.
@@ -50,10 +50,10 @@ const DEFAULT_VIEW = (input: ViewInput, output: ViewOutput, target: HTMLElement)
50
50
  // clang-format on
51
51
  ```
52
52
 
53
- ### Composition (`<devtools-widget>`)
53
+ ### Composition
54
54
 
55
- * To render a child widget, the parent's View MUST use `<devtools-widget>`.
56
- * Configuration is passed via the `.widgetConfig` property using `UI.Widget.widgetConfig()`.
55
+ * To render a child widget, the parent's View MUST use lit-html directive `UI.Widget.wiget`
56
+ * Configuration is passed via the parameters.
57
57
  * Properties passed from a parent MUST be declared as public fields on the child presenter class.
58
58
  * The framework automatically updates these properties and calls `requestUpdate()` on the child when the parent re-renders.
59
59
 
@@ -66,7 +66,7 @@ When migrating imperative components (extending `UI.VBox`, `UI.Panel`, or `HTMLE
66
66
  * Prefer extending `UI.Widget.Widget`.
67
67
  * **Special Case:** If the component *must* extend `UI.Panel.Panel` or `UI.Dialog.Dialog` (to retain specific functionality), you cannot use `requestUpdate()`. Instead, call `this.performUpdate()` directly on state changes.
68
68
  3. **State Migration:** Move DOM-stored state to private class fields.
69
- 4. **Update Usage:** Replace `new MyComponent()` instantiations with declarative `<devtools-widget .widgetConfig=...>` in parent templates.
69
+ 4. **Update Usage:** Replace `new MyComponent()` instantiations with declarative `widget(MyComponent, {params})` in parent templates.
70
70
  5. **Scoping Styles:** Ensure your CSS file uses the `@scope` block to prevent style leaks:
71
71
  ```css
72
72
  @scope to (devtools-widget > *) {
@@ -101,7 +101,7 @@ render(html`
101
101
 
102
102
  ### Legacy Interop & Refs
103
103
  * **Raw Elements:** Use `Lit.Directives.ref` to obtain a reference to a raw `HTMLElement` if needed for imperative APIs (e.g., `splitWidget.installResizer(element)`).
104
- * **Child Widgets:** Use `UI.Widget.widgetRef` to obtain the class instance of a child `<devtools-widget>` if you need to call methods on it directly (though declarative data flow is preferred).
104
+ * **Child Widgets:** Use `UI.Widget.widgetRef` to obtain the class instance of a child widget if you need to call methods on it directly (though declarative data flow is preferred).
105
105
 
106
106
  ### Dependencies
107
107
  The `DEFAULT_VIEW` is typically a module-level function. Ensure all dependencies (enums, constants, other components) are imported at the top of the file so they are available in the function scope.
@@ -236,8 +236,7 @@ const DEFAULT_VIEW = (input: ViewInput, output: undefined, target: HTMLElement):
236
236
  <button @click=${input.onTitleChange}>Change Child Title</button>
237
237
 
238
238
  <!-- Pass properties to the child widget. -->
239
- <devtools-widget .widgetConfig=${widgetConfig(MyExampleWidget, {title: input.title})}>
240
- </devtools-widget>
239
+ ${widget(MyExampleWidget, {title: input.title})}
241
240
  </div>
242
241
  `, target);
243
242
  };
@@ -40,11 +40,11 @@ To test the `DEFAULT_VIEW` function itself, we should use screenshot and e2e tes
40
40
 
41
41
  We should no longer use imperative API to update DOM. Instead we rely on orchestrated rendering of lit-html templates. The view function described above should be a call to lit-html `render`. The view function should be called from `UI.Widget`’s `performUpdate` method, which by default is scheduled using `requestAnimationFrame`.
42
42
 
43
- To embed another presenter (`UI.Widget`) in the lit-html template, use `<devtools-widget .widgetConfig=${widgetConfig(<class>, {foo: 1, bar: 2})}`
43
+ To embed another presenter (`UI.Widget`) in the lit-html template, use `widget(<class>, {foo: 1, bar: 2})`
44
44
 
45
45
  This will instantiate a `Widget` class with the web component as its `element` and, optionally, will set the properties provided in the second parameter. The widget won’t be re-instantiated on the subsequent template renders, but the properties would be updated. For this to work, the widget needs to accept `HTMLElement` as a sole constructor parameter and properties need to be public members or setters.
46
46
 
47
- For backwards compatibility, the first argument to `widgetConfig` can also be a factory function: `<devtools-widget .widgetConfig=${widgetConfig(element => new MyWidget(foo, bar, element))}>`. Similar to the class constructor version, `element` is the actual `<devtools-widget>` so the following two invocations of `widgetConfig` are equivalent: `widgetConfig(MyWidget)` and `widgetConfig(element => new MyWidget(element))`.
47
+ For backwards compatibility, the first argument to `widgetConfig` can also be a factory function: `widget(element => new MyWidget(foo, bar, element))`. Similar to the class constructor version, `element` is the actual `<devtools-widget>` so the following two invocations of `widgetConfig` are equivalent: `widget(MyWidget)` and `widget(element => new MyWidget(element))`.
48
48
 
49
49
  ## Styling
50
50
  To prevent style conflicts in widgets without relying on shadow DOM, we use the CSS [`@scope`](https://developer.mozilla.org/en-US/docs/Web/CSS/@scope) at-rule for style encapsulation. This ensures that styles defined for a widget do not leak out and affect other components.
@@ -83,12 +83,12 @@ render(html`
83
83
  <div class="container">
84
84
  <h3 class="title">My Widget</h3>
85
85
  ...
86
- <devtools-widget .widgetConfig=${widgetConfig(NestedWidget)}></devtools-widget>
86
+ ${widget(NestedWidget)}
87
87
  </div>
88
88
  `, this.element);
89
89
  ```
90
90
 
91
- In this example, the `.title` style will apply within the parent widget but will not leak into the nested `<devtools-widget>`. Because this convention relies on developer discipline, it is important to verify its correct application during code reviews.
91
+ In this example, the `.title` style will apply within the parent widget but will not leak into the nested widget. Because this convention relies on developer discipline, it is important to verify its correct application during code reviews.
92
92
 
93
93
  ## Examples
94
94
 
@@ -97,8 +97,8 @@ In this example, the `.title` style will apply within the parent widget but will
97
97
  <devtools-split-view>
98
98
  <devtools-widget slot="main" .widgetConfig=${widgetConfig(ElementsTree)}></devtools-widget>
99
99
  <devtools-tab-pane slot="sidebar">
100
- <devtools-widget .widgetConfig=${widgetConfig(StylesPane, {element: input.element})}></devtools-widget>
101
- <devtools-widget .widgetConfig=${widgetConfig(ComputedPane, {element: input.element})}></devtools-widget>
100
+ ${widget(StylesPane, {element: input.element})}
101
+ ${widget(ComputedPane, {element: input.element})}
102
102
  ...
103
103
  </devtools-tab-pane>
104
104
  </devtools-split-view>
@@ -109,8 +109,7 @@ In this example, the `.title` style will apply within the parent widget but will
109
109
  type View = (input: ViewInput, output: ViewOutput, target: HTMLElement) => void;
110
110
  const DEFAULT_VIEW = (input, output, target) => {
111
111
  render(html`
112
- <devtools-widget .widgetConfig=${widgetConfig(MetricsPane, {element: input.element})}>
113
- </devtools-widget>
112
+ ${widget(MetricsPane, {element: input.element})}
114
113
  <devtools-toolbar>
115
114
  <devtools-filter-input @change=${input.onFilter}></devtools-filter-input>
116
115
  <devtools-checkbox @change=${input.onShowAll}>Show All</devtools-checkbox>
@@ -1236,7 +1235,7 @@ export const DEFAULT_VIEW = (input, _output, target) => {
1236
1235
 
1237
1236
  ## Migrating `EmptyWidget`
1238
1237
 
1239
- Replace the imperative `EmptyWidget` with a declarative `<devtools-widget>` and configure it with `widgetConfig` to render an `EmptyWidget`.
1238
+ Replace the imperative `EmptyWidget` with a declarative `widget` and configure it to render an `EmptyWidget`.
1240
1239
 
1241
1240
  **Before:**
1242
1241
  ```typescript
@@ -1258,9 +1257,9 @@ class SomeWidget extends UI.Widget.Widget {
1258
1257
  export const DEFAULT_VIEW = (input, _output, target) => {
1259
1258
  render(html`
1260
1259
  <div>
1261
- <devtools-widget .widgetConfig=${widgetConfig(UI.EmptyWidget.EmptyWidget,{
1260
+ ${widget(UI.EmptyWidget.EmptyWidget,{
1262
1261
  header: i18nString(UIStrings.nothingToSeeHere), text: this.explanation,
1263
- link: 'http://www.google.com',})}></devtools-widget>
1262
+ link: 'http://www.google.com',})}
1264
1263
  </div>`,
1265
1264
  target, {host: input});
1266
1265
  };
@@ -133,6 +133,10 @@ export enum ClientFeature {
133
133
  CHROME_PATCH_AGENT = 12,
134
134
  // Chrome AI Assistance Performance Agent.
135
135
  CHROME_PERFORMANCE_FULL_AGENT = 24,
136
+ // Chrome Context Selection Agent.
137
+ CHROME_CONTEXT_SELECTION_AGENT = 25,
138
+ // Chrome Accessibility Agent
139
+ CHROME_ACCESSIBILITY_AGENT = 26,
136
140
 
137
141
  // Removed features (for reference).
138
142
  // Chrome AI Assistance Performance Insights Agent.
@@ -556,5 +556,6 @@ export const enum EnumeratedHistogram {
556
556
  LighthouseCategoryUsed = 'DevTools.LighthouseCategoryUsed',
557
557
  SwatchActivated = 'DevTools.SwatchActivated',
558
558
  BuiltInAiAvailability = 'DevTools.BuiltInAiAvailability',
559
+ ExtensionEvalTarget = 'DevTools.ExtensionEvalTarget',
559
560
  // LINT.ThenChange(/front_end/devtools_compatibility.js:EnumeratedHistogram)
560
561
  }
@@ -322,6 +322,11 @@ export class UserMetrics {
322
322
  InspectorFrontendHostInstance.recordPerformanceHistogram(
323
323
  'DevTools.Insights.ShortTeaserGenerationTime', timeInMilliseconds);
324
324
  }
325
+
326
+ extensionEvalTarget(target: ExtensionEvalTarget): void {
327
+ InspectorFrontendHostInstance.recordEnumeratedHistogram(
328
+ EnumeratedHistogram.ExtensionEvalTarget, target, ExtensionEvalTarget.MAX_VALUE);
329
+ }
325
330
  }
326
331
 
327
332
  /**
@@ -1264,3 +1269,10 @@ export const enum BuiltInAiAvailability {
1264
1269
  DISABLED_NO_GPU = 9,
1265
1270
  MAX_VALUE = 10,
1266
1271
  }
1272
+
1273
+ export const enum ExtensionEvalTarget {
1274
+ WEB_PAGE = 0,
1275
+ SAME_EXTENSION = 1,
1276
+ OTHER_EXTENSION = 2,
1277
+ MAX_VALUE = 3,
1278
+ }
@@ -476,6 +476,10 @@ export interface HostConfigAiAssistanceFileAgent {
476
476
  userTier: string;
477
477
  }
478
478
 
479
+ export interface HostConfigAiAssistanceAccessibilityAgent {
480
+ enabled: boolean;
481
+ }
482
+
479
483
  export interface HostConfigAiCodeCompletion {
480
484
  modelId: string;
481
485
  temperature: number;
@@ -632,6 +636,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
632
636
  devToolsAiAssistanceNetworkAgent: HostConfigAiAssistanceNetworkAgent,
633
637
  devToolsAiAssistanceFileAgent: HostConfigAiAssistanceFileAgent,
634
638
  devToolsAiAssistancePerformanceAgent: HostConfigAiAssistancePerformanceAgent,
639
+ devToolsAiAssistanceAccessibilityAgent: HostConfigAiAssistanceAccessibilityAgent,
635
640
  devToolsAiAssistanceV2: HostConfigAiAssistanceV2,
636
641
  devToolsAiCodeCompletion: HostConfigAiCodeCompletion,
637
642
  devToolsAiCodeGeneration: HostConfigAiCodeGeneration,
@@ -35,36 +35,37 @@ const str_ = i18n.i18n.registerUIStrings('core/sdk/CPUThrottlingManager.ts', UIS
35
35
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
36
36
  const i18nLazyString = i18n.i18n.getLazilyComputedLocalizedString.bind(undefined, str_);
37
37
 
38
- let throttlingManagerInstance: CPUThrottlingManager;
38
+ let throttlingManagerInstance: CPUThrottlingManager|undefined;
39
39
 
40
40
  export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<EventTypes> implements
41
41
  SDKModelObserver<EmulationModel> {
42
- readonly #targetManager: TargetManager;
43
42
  #cpuThrottlingOption: CPUThrottlingOption;
44
43
  #calibratedThrottlingSetting: Common.Settings.Setting<CalibratedCPUThrottling>;
45
44
  #hardwareConcurrency?: number;
46
45
  #pendingMainTargetPromise?: (r: number) => void;
47
46
 
48
- private constructor(settings: Common.Settings.Settings, targetManager: TargetManager) {
47
+ private constructor() {
49
48
  super();
50
- this.#targetManager = targetManager;
51
49
  this.#cpuThrottlingOption = NoThrottlingOption;
52
- this.#calibratedThrottlingSetting = settings.createSetting<CalibratedCPUThrottling>(
50
+ this.#calibratedThrottlingSetting = Common.Settings.Settings.instance().createSetting<CalibratedCPUThrottling>(
53
51
  'calibrated-cpu-throttling', {}, Common.Settings.SettingStorageType.GLOBAL);
54
52
  this.#calibratedThrottlingSetting.addChangeListener(this.#onCalibratedSettingChanged, this);
55
- targetManager.observeModels(EmulationModel, this);
53
+ TargetManager.instance().observeModels(EmulationModel, this);
56
54
  }
57
55
 
58
56
  static instance(opts: {forceNew: boolean|null} = {forceNew: null}): CPUThrottlingManager {
59
57
  const {forceNew} = opts;
60
58
  if (!throttlingManagerInstance || forceNew) {
61
- throttlingManagerInstance =
62
- new CPUThrottlingManager(Common.Settings.Settings.instance(), TargetManager.instance());
59
+ throttlingManagerInstance = new CPUThrottlingManager();
63
60
  }
64
61
 
65
62
  return throttlingManagerInstance;
66
63
  }
67
64
 
65
+ static removeInstance(): void {
66
+ throttlingManagerInstance = undefined;
67
+ }
68
+
68
69
  cpuThrottlingRate(): number {
69
70
  return this.#cpuThrottlingOption.rate();
70
71
  }
@@ -87,7 +88,7 @@ export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<Eve
87
88
  return;
88
89
  }
89
90
 
90
- for (const emulationModel of this.#targetManager.models(EmulationModel)) {
91
+ for (const emulationModel of TargetManager.instance().models(EmulationModel)) {
91
92
  void emulationModel.setCPUThrottlingRate(rate);
92
93
  }
93
94
  this.dispatchEventToListeners(Events.RATE_CHANGED, rate);
@@ -99,7 +100,7 @@ export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<Eve
99
100
  }
100
101
 
101
102
  this.#cpuThrottlingOption = option;
102
- for (const emulationModel of this.#targetManager.models(EmulationModel)) {
103
+ for (const emulationModel of TargetManager.instance().models(EmulationModel)) {
103
104
  void emulationModel.setCPUThrottlingRate(this.#cpuThrottlingOption.rate());
104
105
  }
105
106
  this.dispatchEventToListeners(Events.RATE_CHANGED, this.#cpuThrottlingOption.rate());
@@ -107,7 +108,7 @@ export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<Eve
107
108
 
108
109
  setHardwareConcurrency(concurrency: number): void {
109
110
  this.#hardwareConcurrency = concurrency;
110
- for (const emulationModel of this.#targetManager.models(EmulationModel)) {
111
+ for (const emulationModel of TargetManager.instance().models(EmulationModel)) {
111
112
  void emulationModel.setHardwareConcurrency(concurrency);
112
113
  }
113
114
  this.dispatchEventToListeners(Events.HARDWARE_CONCURRENCY_CHANGED, this.#hardwareConcurrency);
@@ -118,14 +119,14 @@ export class CPUThrottlingManager extends Common.ObjectWrapper.ObjectWrapper<Eve
118
119
  // target may error. So if we get any errors here at all, assume that we do
119
120
  // not have a target.
120
121
  try {
121
- return this.#targetManager.primaryPageTarget() !== null;
122
+ return TargetManager.instance().primaryPageTarget() !== null;
122
123
  } catch {
123
124
  return false;
124
125
  }
125
126
  }
126
127
 
127
128
  async getHardwareConcurrency(): Promise<number> {
128
- const target = this.#targetManager.primaryPageTarget();
129
+ const target = TargetManager.instance().primaryPageTarget();
129
130
  const existingCallback = this.#pendingMainTargetPromise;
130
131
 
131
132
  // If the main target hasn't attached yet, block callers until it appears.
@@ -21,6 +21,7 @@ import {
21
21
  BinOpMatcher,
22
22
  ColorMatcher,
23
23
  ColorMixMatcher,
24
+ ContrastColorMatcher,
24
25
  CustomFunctionMatcher,
25
26
  defaultValueForCSSType,
26
27
  EnvFunctionMatcher,
@@ -932,6 +933,7 @@ export class CSSMatchedStyles {
932
933
  new VariableMatcher(this, style),
933
934
  new ColorMatcher(() => computedStyles?.get('color') ?? null),
934
935
  new ColorMixMatcher(),
936
+ new ContrastColorMatcher(),
935
937
  new URLMatcher(),
936
938
  new AngleMatcher(),
937
939
  new LinkableNameMatcher(),
@@ -433,6 +433,34 @@ export class ColorMixMatcher extends matcherBase(ColorMixMatch) {
433
433
  }
434
434
  }
435
435
 
436
+ export class ContrastColorMatch implements Match {
437
+ constructor(readonly text: string, readonly node: CodeMirror.SyntaxNode, readonly color: CodeMirror.SyntaxNode[]) {
438
+ }
439
+ }
440
+
441
+ // clang-format off
442
+ export class ContrastColorMatcher extends matcherBase(ContrastColorMatch) {
443
+ // clang-format on
444
+ override accepts(propertyName: string): boolean {
445
+ return cssMetadata().isColorAwareProperty(propertyName);
446
+ }
447
+ override matches(node: CodeMirror.SyntaxNode, matching: BottomUpTreeMatching): ContrastColorMatch|null {
448
+ if (node.name !== 'CallExpression' || matching.ast.text(node.getChild('Callee')) !== 'contrast-color') {
449
+ return null;
450
+ }
451
+
452
+ if (matching.getComputedText(node) === '') {
453
+ return null;
454
+ }
455
+
456
+ const args = ASTUtils.callArgs(node);
457
+ if (args.length !== 1) {
458
+ return null;
459
+ }
460
+ return new ContrastColorMatch(matching.ast.text(node), node, args[0]);
461
+ }
462
+ }
463
+
436
464
  // clang-format off
437
465
  export class URLMatch implements Match {
438
466
  constructor(
@@ -334,6 +334,27 @@ export class PageResourceLoader extends Common.ObjectWrapper.ObjectWrapper<Event
334
334
  initiator.target;
335
335
  Host.userMetrics.developerResourceScheme(this.getDeveloperResourceScheme(parsedURL));
336
336
  if (eligibleForLoadFromTarget) {
337
+ let mustEnforceCSP = false;
338
+ const isHttp = parsedURL.scheme === 'http' || parsedURL.scheme === 'https';
339
+ if (isHttp && initiator.target) {
340
+ const networkManager = initiator.target.model(NetworkManager);
341
+ if (networkManager) {
342
+ let status = await networkManager.getSecurityIsolationStatus(initiator.frameId);
343
+ if (!status && initiator.frameId) {
344
+ status = await networkManager.getSecurityIsolationStatus(null);
345
+ }
346
+ if (status?.csp) {
347
+ for (const csp of status.csp) {
348
+ const directives = csp.effectiveDirectives;
349
+ if (directives.includes('connect-src') || directives.includes('default-src')) {
350
+ mustEnforceCSP = true;
351
+ break;
352
+ }
353
+ }
354
+ }
355
+ }
356
+ }
357
+
337
358
  try {
338
359
  Host.userMetrics.developerResourceLoaded(Host.UserMetrics.DeveloperResourceLoaded.LOAD_THROUGH_PAGE_VIA_TARGET);
339
360
  const result = await this.loadFromTarget(initiator.target, initiator.frameId, url, isBinary);
@@ -341,7 +362,7 @@ export class PageResourceLoader extends Common.ObjectWrapper.ObjectWrapper<Event
341
362
  } catch (e) {
342
363
  if (e instanceof Error) {
343
364
  Host.userMetrics.developerResourceLoaded(Host.UserMetrics.DeveloperResourceLoaded.LOAD_THROUGH_PAGE_FAILURE);
344
- if (e.message.includes('CSP violation')) {
365
+ if (mustEnforceCSP || e.message.includes('CSP violation')) {
345
366
  return {
346
367
  success: false,
347
368
  content: '',
@@ -444,7 +444,8 @@
444
444
  TimelineNavigationSettingState: 'DevTools.TimelineNavigationSettingState',
445
445
  SyncSetting: 'DevTools.SyncSetting',
446
446
  SwatchActivated: 'DevTools.SwatchActivated',
447
- BuiltInAiAvailability: 'DevTools.BuiltInAiAvailability'
447
+ BuiltInAiAvailability: 'DevTools.BuiltInAiAvailability',
448
+ ExtensionEvalTarget: 'DevTools.ExtensionEvalTarget'
448
449
  // LINT.ThenChange(/front_end/core/host/InspectorFrontendHostAPI.ts:EnumeratedHistogram)
449
450
  };
450
451
 
@@ -1,10 +1,10 @@
1
1
  // Copyright 2024 The Chromium Authors
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
-
4
+ import * as Common from '../../core/common/common.js';
5
5
  import * as Host from '../../core/host/host.js';
6
6
  import * as Root from '../../core/root/root.js';
7
- import type * as SDK from '../../core/sdk/sdk.js';
7
+ import * as SDK from '../../core/sdk/sdk.js';
8
8
  import type * as Trace from '../../models/trace/trace.js';
9
9
  import * as Greendev from '../greendev/greendev.js';
10
10
  import type * as NetworkTimeCalculator from '../network_time_calculator/network_time_calculator.js';
@@ -13,6 +13,7 @@ import {
13
13
  type AiAgent,
14
14
  type ContextDetail,
15
15
  type ConversationContext,
16
+ ErrorType,
16
17
  type MultimodalInput,
17
18
  type ResponseData,
18
19
  ResponseType,
@@ -162,6 +163,11 @@ export class AiConversation {
162
163
  return this.#contexts.at(0);
163
164
  }
164
165
 
166
+ getPendingMultimodalInput(): MultimodalInput|undefined {
167
+ const greenDevEmulationEnabled = Greendev.Prototypes.instance().isEnabled('emulationCapabilities');
168
+ return greenDevEmulationEnabled ? this.#agent.popPendingMultimodalInput() : undefined;
169
+ }
170
+
165
171
  #reconstructHistory(historyWithoutImages: ResponseData[]): ResponseData[] {
166
172
  const imageHistory = AiHistoryStorage.instance().getImageHistory();
167
173
  if (imageHistory && imageHistory.length > 0) {
@@ -308,6 +314,7 @@ export class AiConversation {
308
314
  performanceRecordAndReload: this.#performanceRecordAndReload,
309
315
  onInspectElement: this.#onInspectElement,
310
316
  networkTimeCalculator: this.#networkTimeCalculator,
317
+ allowedOrigin: this.allowedOrigin,
311
318
  history,
312
319
  };
313
320
  switch (type) {
@@ -364,12 +371,6 @@ export class AiConversation {
364
371
  void this.addHistoryItem(userQuery);
365
372
  yield userQuery;
366
373
 
367
- this.#setOriginIfEmpty(this.selectedContext?.getOrigin());
368
-
369
- if (this.isBlockedByOrigin) {
370
- throw new Error('Cross-origin context data should not be included');
371
- }
372
-
373
374
  yield* this.#runAgent(initialQuery, options);
374
375
  }
375
376
 
@@ -385,6 +386,15 @@ export class AiConversation {
385
386
  multimodalInput?: MultimodalInput,
386
387
  } = {},
387
388
  ): AsyncGenerator<ResponseData, void, void> {
389
+ this.#setOriginIfEmpty(this.selectedContext?.getOrigin());
390
+ if (this.isBlockedByOrigin) {
391
+ yield {
392
+ type: ResponseType.ERROR,
393
+ error: ErrorType.CROSS_ORIGIN,
394
+ };
395
+ return;
396
+ }
397
+
388
398
  function shouldAddToHistory(data: ResponseData): boolean {
389
399
  if (data.type === ResponseType.CONTEXT_CHANGE) {
390
400
  return false;
@@ -440,6 +450,17 @@ export class AiConversation {
440
450
  get type(): ConversationType {
441
451
  return this.#type;
442
452
  }
453
+
454
+ allowedOrigin = (): string|undefined => {
455
+ if (this.#origin) {
456
+ return this.#origin;
457
+ }
458
+ const target = SDK.TargetManager.TargetManager.instance().primaryPageTarget();
459
+ const inspectedURL = target?.inspectedURL();
460
+ this.#origin = inspectedURL ? new Common.ParsedURL.ParsedURL(inspectedURL).securityOrigin() : undefined;
461
+
462
+ return this.#origin;
463
+ };
443
464
  }
444
465
 
445
466
  function isAiAssistanceServerSideLoggingEnabled(): boolean {
@@ -9,6 +9,8 @@ import type * as Protocol from '../../generated/protocol.js';
9
9
 
10
10
  export interface Change {
11
11
  groupId: string;
12
+ // Optional turn ID to group changes from the same turn.
13
+ turnId?: number;
12
14
  // Optional about where in the source the selector was defined.
13
15
  sourceLocation?: string;
14
16
  // Selector used by the page or a simple selector as the fallback.
@@ -17,6 +19,7 @@ export interface Change {
17
19
  simpleSelector?: string;
18
20
  className: string;
19
21
  styles: Record<string, string>;
22
+ backendNodeId?: Protocol.DOM.BackendNodeId;
20
23
  }
21
24
 
22
25
  function formatStyles(styles: Record<string, string>, indent = 2): string {
@@ -101,6 +104,7 @@ export class ChangeManager {
101
104
  // it currently causes crashes in the Styles tab when duplicate selectors exist (crbug.com/393515428).
102
105
  // This workaround avoids that crash.
103
106
  existingChange.groupId = change.groupId;
107
+ existingChange.turnId = change.turnId;
104
108
  } else {
105
109
  changes.push({
106
110
  ...change,
@@ -122,6 +126,18 @@ export class ChangeManager {
122
126
  .join('\n\n');
123
127
  }
124
128
 
129
+ getChangedNodesForGroupId(groupId: string, turnId?: number): Protocol.DOM.BackendNodeId[] {
130
+ const nodes = new Set<Protocol.DOM.BackendNodeId>();
131
+ for (const changes of this.#stylesheetChanges.values()) {
132
+ for (const change of changes) {
133
+ if (change.groupId === groupId && change.backendNodeId && (turnId === undefined || change.turnId === turnId)) {
134
+ nodes.add(change.backendNodeId);
135
+ }
136
+ }
137
+ }
138
+ return Array.from(nodes);
139
+ }
140
+
125
141
  #formatChangesForInspectorStylesheet(changes: Change[]): string {
126
142
  return changes
127
143
  .map(change => {
@@ -22,6 +22,7 @@ interface ElementContext {
22
22
  selector: string;
23
23
  simpleSelector?: string;
24
24
  sourceLocation?: string;
25
+ backendNodeId?: Protocol.DOM.BackendNodeId;
25
26
  }
26
27
 
27
28
  /**
@@ -33,6 +34,7 @@ export class ExtensionScope {
33
34
  }) => Promise<void>> = [];
34
35
  #changeManager: ChangeManager;
35
36
  #agentId: string;
37
+ #turnId?: number;
36
38
  /** Don't use directly use the getter */
37
39
  #frameId?: Protocol.Page.FrameId|null;
38
40
  /** Don't use directly use the getter */
@@ -40,15 +42,15 @@ export class ExtensionScope {
40
42
 
41
43
  readonly #bindingMutex = new Common.Mutex.Mutex();
42
44
 
43
- constructor(changes: ChangeManager, agentId: string, selectedNode: SDK.DOMModel.DOMNode|null) {
45
+ constructor(changes: ChangeManager, agentId: string, selectedNode: SDK.DOMModel.DOMNode|null, turnId?: number) {
44
46
  this.#changeManager = changes;
45
47
  const frameId = selectedNode?.frameId();
46
48
  const target = selectedNode?.domModel().target();
47
49
  this.#agentId = agentId;
50
+ this.#turnId = turnId;
48
51
  this.#target = target;
49
52
  this.#frameId = frameId;
50
53
  }
51
-
52
54
  get target(): SDK.Target.Target {
53
55
  if (!this.#target) {
54
56
  throw new Error('Target is not found for executing code');
@@ -274,6 +276,7 @@ export class ExtensionScope {
274
276
  throw new Error('Node is not found');
275
277
  }
276
278
 
279
+ const backendNodeId = node.backendNodeId();
277
280
  try {
278
281
  const matchedStyles = await cssModel.getMatchedStyles(node.id);
279
282
 
@@ -297,6 +300,7 @@ export class ExtensionScope {
297
300
  selector,
298
301
  simpleSelector: ExtensionScope.getSelectorForNode(node),
299
302
  sourceLocation: ExtensionScope.getSourceLocation(styleRule),
303
+ backendNodeId,
300
304
  };
301
305
  } catch {
302
306
  // no-op to allow the fallback below to run.
@@ -305,6 +309,7 @@ export class ExtensionScope {
305
309
  // Fallback
306
310
  return {
307
311
  selector: ExtensionScope.getSelectorForNode(node),
312
+ backendNodeId,
308
313
  };
309
314
  }
310
315
 
@@ -337,7 +342,8 @@ export class ExtensionScope {
337
342
 
338
343
  let context: ElementContext = {
339
344
  // TODO: Should this a be a *?
340
- selector: ''
345
+ selector: '',
346
+ backendNodeId: undefined,
341
347
  };
342
348
  try {
343
349
  context = await this.#computeContextFromElement(element.object);
@@ -351,11 +357,13 @@ export class ExtensionScope {
351
357
  const sanitizedStyles = await this.sanitizedStyleChanges(context.selector, arg.styles);
352
358
  const styleChanges = await this.#changeManager.addChange(cssModel, this.frameId, {
353
359
  groupId: this.#agentId,
360
+ turnId: this.#turnId,
354
361
  sourceLocation: context.sourceLocation,
355
362
  selector: context.selector,
356
363
  simpleSelector: context.simpleSelector,
357
364
  className: arg.className,
358
365
  styles: sanitizedStyles,
366
+ backendNodeId: context.backendNodeId,
359
367
  });
360
368
  await this.#simpleEval(executionContext, `freestyler.respond(${id}, ${JSON.stringify(styleChanges)})`);
361
369
  } catch (error) {