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.
- package/agents/prompts/ui-widgets.md +7 -8
- package/docs/ui_engineering.md +10 -11
- package/front_end/core/host/AidaClient.ts +4 -0
- package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
- package/front_end/core/host/UserMetrics.ts +12 -0
- package/front_end/core/root/Runtime.ts +5 -0
- package/front_end/core/sdk/CPUThrottlingManager.ts +14 -13
- package/front_end/core/sdk/CSSMatchedStyles.ts +2 -0
- package/front_end/core/sdk/CSSPropertyParserMatchers.ts +28 -0
- package/front_end/core/sdk/PageResourceLoader.ts +22 -1
- package/front_end/devtools_compatibility.js +2 -1
- package/front_end/models/ai_assistance/AiConversation.ts +29 -8
- package/front_end/models/ai_assistance/ChangeManager.ts +16 -0
- package/front_end/models/ai_assistance/ExtensionScope.ts +11 -3
- package/front_end/models/ai_assistance/agents/AccessibilityAgent.ts +127 -0
- package/front_end/models/ai_assistance/agents/AiAgent.ts +26 -3
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.snapshot.txt +1 -1
- package/front_end/models/ai_assistance/agents/ContextSelectionAgent.ts +11 -8
- package/front_end/models/ai_assistance/agents/StylingAgent.snapshot.txt +24 -0
- package/front_end/models/ai_assistance/agents/StylingAgent.ts +323 -16
- package/front_end/models/ai_assistance/ai_assistance.ts +2 -0
- package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.ts +27 -0
- package/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.snapshot.txt +21 -0
- package/front_end/models/greendev/Prototypes.ts +7 -1
- package/front_end/models/trace/Processor.ts +1 -0
- package/front_end/models/trace/handlers/PageLoadMetricsHandler.ts +33 -0
- package/front_end/models/trace/insights/CharacterSet.ts +172 -0
- package/front_end/models/trace/insights/Models.ts +1 -0
- package/front_end/models/trace/insights/types.ts +1 -0
- package/front_end/models/trace/types/TraceEvents.ts +17 -0
- package/front_end/panels/ai_assistance/AiAssistancePanel.ts +51 -36
- package/front_end/panels/ai_assistance/PatchWidget.ts +6 -6
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +93 -74
- package/front_end/panels/ai_assistance/components/ChatView.ts +6 -11
- package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +18 -9
- package/front_end/panels/ai_assistance/components/StylingAgentMarkdownRenderer.ts +200 -0
- package/front_end/panels/ai_assistance/components/chatMessage.css +11 -8
- package/front_end/panels/application/AppManifestView.ts +3 -4
- package/front_end/panels/application/DeviceBoundSessionsView.ts +18 -22
- package/front_end/panels/application/FrameDetailsView.ts +9 -15
- package/front_end/panels/application/OriginTrialTreeView.ts +2 -3
- package/front_end/panels/application/ReportingApiView.ts +13 -17
- package/front_end/panels/application/ServiceWorkerCacheViews.ts +1 -1
- package/front_end/panels/application/components/BackForwardCacheView.ts +3 -3
- package/front_end/panels/application/preloading/components/UsedPreloadingView.ts +2 -3
- package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +3 -2
- package/front_end/panels/changes/ChangesView.ts +6 -4
- package/front_end/panels/common/ExtensionServer.ts +15 -0
- package/front_end/panels/console/ConsolePinPane.ts +3 -3
- package/front_end/panels/coverage/CoverageListView.ts +1 -1
- package/front_end/panels/css_overview/CSSOverviewPanel.ts +11 -15
- package/front_end/panels/developer_resources/DeveloperResourcesView.ts +3 -5
- package/front_end/panels/elements/ElementsTreeElement.ts +55 -47
- package/front_end/panels/elements/ElementsTreeOutline.ts +149 -28
- package/front_end/panels/elements/EventListenersWidget.ts +3 -2
- package/front_end/panels/elements/StandaloneStylesContainer.ts +21 -6
- package/front_end/panels/elements/StylePropertyTreeElement.ts +49 -4
- package/front_end/panels/layer_viewer/Layers3DView.ts +5 -4
- package/front_end/panels/lighthouse/LighthousePanel.ts +8 -0
- package/front_end/panels/linear_memory_inspector/components/LinearMemoryInspector.ts +5 -6
- package/front_end/panels/linear_memory_inspector/components/LinearMemoryValueInterpreter.ts +6 -11
- package/front_end/panels/network/RequestCookiesView.ts +3 -4
- package/front_end/panels/network/RequestInitiatorView.ts +7 -5
- package/front_end/panels/network/RequestResponseView.ts +10 -15
- package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +3 -4
- package/front_end/panels/recorder/components/RecordingView.ts +31 -36
- package/front_end/panels/recorder/components/StepEditor.ts +6 -7
- package/front_end/panels/search/SearchView.ts +2 -3
- package/front_end/panels/settings/SettingsScreen.ts +3 -2
- package/front_end/panels/settings/WorkspaceSettingsTab.ts +2 -5
- package/front_end/panels/timeline/components/LiveMetricsView.ts +5 -8
- package/front_end/panels/timeline/components/insights/Cache.ts +8 -10
- package/front_end/panels/timeline/components/insights/CharacterSet.ts +38 -0
- package/front_end/panels/timeline/components/insights/DOMSize.ts +16 -20
- package/front_end/panels/timeline/components/insights/DocumentLatency.ts +2 -6
- package/front_end/panels/timeline/components/insights/DuplicatedJavaScript.ts +3 -4
- package/front_end/panels/timeline/components/insights/FontDisplay.ts +3 -4
- package/front_end/panels/timeline/components/insights/ForcedReflow.ts +5 -7
- package/front_end/panels/timeline/components/insights/INPBreakdown.ts +3 -4
- package/front_end/panels/timeline/components/insights/ImageDelivery.ts +3 -4
- package/front_end/panels/timeline/components/insights/ImageRef.ts +2 -4
- package/front_end/panels/timeline/components/insights/InsightRenderer.ts +2 -0
- package/front_end/panels/timeline/components/insights/LCPBreakdown.ts +5 -7
- package/front_end/panels/timeline/components/insights/LCPDiscovery.ts +2 -4
- package/front_end/panels/timeline/components/insights/LegacyJavaScript.ts +3 -4
- package/front_end/panels/timeline/components/insights/ModernHTTP.ts +3 -4
- package/front_end/panels/timeline/components/insights/NetworkDependencyTree.ts +7 -11
- package/front_end/panels/timeline/components/insights/NodeLink.ts +2 -4
- package/front_end/panels/timeline/components/insights/RenderBlocking.ts +3 -4
- package/front_end/panels/timeline/components/insights/SlowCSSSelector.ts +7 -10
- package/front_end/panels/timeline/components/insights/ThirdParties.ts +5 -7
- package/front_end/panels/timeline/components/insights/insights.ts +2 -0
- package/front_end/panels/web_audio/WebAudioView.ts +3 -4
- package/front_end/ui/components/settings/SettingCheckbox.ts +2 -0
- package/front_end/ui/legacy/UIUtils.ts +5 -5
- package/front_end/ui/legacy/Widget.ts +33 -2
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +3 -3
- package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +8 -8
- package/front_end/ui/visual_logging/Debugging.ts +0 -32
- package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
- 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
|
|
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
|
|
53
|
+
### Composition
|
|
54
54
|
|
|
55
|
-
* To render a child widget, the parent's View MUST use
|
|
56
|
-
* Configuration is passed via the
|
|
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
|
|
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
|
|
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
|
-
|
|
240
|
-
</devtools-widget>
|
|
239
|
+
${widget(MyExampleWidget, {title: input.title})}
|
|
241
240
|
</div>
|
|
242
241
|
`, target);
|
|
243
242
|
};
|
package/docs/ui_engineering.md
CHANGED
|
@@ -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
|
|
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:
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
101
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
1260
|
+
${widget(UI.EmptyWidget.EmptyWidget,{
|
|
1262
1261
|
header: i18nString(UIStrings.nothingToSeeHere), text: this.explanation,
|
|
1263
|
-
link: 'http://www.google.com',})}
|
|
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(
|
|
47
|
+
private constructor() {
|
|
49
48
|
super();
|
|
50
|
-
this.#targetManager = targetManager;
|
|
51
49
|
this.#cpuThrottlingOption = NoThrottlingOption;
|
|
52
|
-
this.#calibratedThrottlingSetting =
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 =
|
|
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
|
|
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) {
|