chrome-devtools-frontend 1.0.1664496 → 1.0.1664619
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/devtools-ux-writing-refactor/SKILL.md +2 -2
- package/.agents/skills/repro-flaky-tests/SKILL.md +2 -2
- package/.agents/skills/version-control/SKILL.md +4 -3
- package/docs/ui_engineering.md +89 -0
- package/eslint.config.mjs +1 -2
- package/front_end/core/host/AidaClient.ts +19 -4
- package/front_end/core/host/InspectorFrontendHostStub.ts +1 -1
- package/front_end/core/host/ResourceLoader.ts +15 -15
- package/front_end/core/sdk/CSSMatchedStyles.ts +75 -0
- package/front_end/core/sdk/NetworkRequest.ts +4 -4
- package/front_end/entrypoints/main/MainImpl.ts +2 -1
- package/front_end/entrypoints/main/main-meta.ts +0 -15
- package/front_end/entrypoints/shell/shell.ts +1 -0
- package/front_end/entrypoints/trace_app/trace_app.ts +1 -0
- package/front_end/foundation/Universe.ts +1 -0
- package/front_end/models/ai_assistance/agents/AiAgent.ts +9 -1
- package/front_end/models/ai_assistance/data_formatters/FileFormatter.ts +11 -4
- package/front_end/models/ai_assistance/data_formatters/NetworkRequestFormatter.ts +8 -2
- package/front_end/models/ai_assistance/performance/AICallTree.ts +2 -2
- package/front_end/models/ai_assistance/tools/GetNetworkRequestDetails.ts +9 -2
- package/front_end/models/ai_assistance/tools/ListNetworkRequests.ts +2 -5
- package/front_end/models/badges/badges-meta.ts +27 -0
- package/front_end/models/bindings/CSSWorkspaceBinding.ts +4 -3
- package/front_end/models/bindings/DebuggerWorkspaceBinding.ts +4 -3
- package/front_end/models/bindings/PresentationConsoleMessageHelper.ts +18 -11
- package/front_end/models/bindings/ResourceMapping.ts +38 -32
- package/front_end/models/breakpoints/BreakpointManager.ts +2 -1
- package/front_end/models/har/Writer.ts +2 -2
- package/front_end/models/issues_manager/IssuesManager.ts +4 -2
- package/front_end/models/issues_manager/SourceFrameIssuesManager.ts +6 -2
- package/front_end/models/logs/logs-meta.ts +23 -16
- package/front_end/models/source_map_scopes/NamesResolver.ts +104 -86
- package/front_end/models/source_map_scopes/ScopeChainModel.ts +6 -2
- package/front_end/models/trace/handlers/UserTimingsHandler.ts +17 -6
- package/front_end/models/trace_source_maps_resolver/SourceMapsResolver.ts +2 -2
- package/front_end/models/workspace_diff/WorkspaceDiff.ts +12 -7
- package/front_end/panels/ai_assistance/ai_assistance.ts +1 -0
- package/front_end/panels/ai_assistance/components/ChatInput.ts +21 -17
- package/front_end/panels/ai_assistance/components/ChatMessage.ts +7 -0
- package/front_end/panels/ai_assistance/components/ImageResize.ts +74 -0
- package/front_end/panels/animation/animationTimeline.css +0 -2
- package/front_end/panels/changes/ChangesSidebar.ts +1 -1
- package/front_end/panels/changes/ChangesView.ts +3 -4
- package/front_end/panels/changes/CombinedDiffView.ts +2 -2
- package/front_end/panels/changes/changes-meta.ts +2 -2
- package/front_end/panels/common/aiCodeCompletionDisclaimer.css +0 -1
- package/front_end/panels/console/ConsolePrompt.ts +3 -1
- package/front_end/panels/elements/LayoutPane.ts +19 -9
- package/front_end/panels/elements/StylePropertiesSection.ts +33 -5
- package/front_end/panels/emulation/DeviceModeToolbar.ts +92 -32
- package/front_end/panels/emulation/DeviceModeView.ts +5 -4
- package/front_end/panels/emulation/MediaQueryInspector.ts +33 -17
- package/front_end/panels/network/NetworkLogViewColumns.ts +1 -1
- package/front_end/panels/network/networkTimingTable.css +2 -2
- package/front_end/panels/profiler/BottomUpProfileDataGrid.ts +0 -4
- package/front_end/panels/profiler/HeapProfileView.ts +53 -33
- package/front_end/panels/profiler/TopDownProfileDataGrid.ts +0 -4
- package/front_end/panels/profiler/profilesPanel.css +9 -13
- package/front_end/panels/sensors/LocationsSettingsTab.ts +245 -232
- package/front_end/panels/settings/SettingsScreen.ts +29 -24
- package/front_end/panels/sources/DebuggerPlugin.ts +6 -3
- package/front_end/panels/sources/ScopeChainSidebarPane.ts +3 -1
- package/front_end/panels/sources/WatchExpressionsSidebarPane.ts +3 -1
- package/front_end/panels/timeline/TimelineUIUtils.ts +2 -2
- package/front_end/panels/whats_new/ReleaseNoteText.ts +8 -8
- package/front_end/panels/whats_new/resources/WNDT.md +7 -8
- package/front_end/ui/components/highlighting/HighlightManager.ts +6 -1
- package/front_end/ui/components/settings/SettingCheckbox.ts +12 -7
- package/front_end/ui/components/text_editor/javascript.ts +3 -1
- package/front_end/ui/legacy/Toolbar.ts +4 -1
- package/front_end/ui/legacy/components/color_picker/spectrum.css +1 -2
- package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +24 -1
- package/front_end/ui/legacy/components/object_ui/JavaScriptREPL.ts +3 -1
- package/front_end/ui/legacy/components/quick_open/CommandMenu.ts +14 -11
- package/front_end/ui/legacy/components/settings_ui/SettingsUI.ts +14 -4
- package/front_end/ui/settings/SettingUIRegistration.ts +8 -3
- package/mcp/mcp.ts +1 -0
- package/package.json +4 -4
|
@@ -112,9 +112,9 @@ When all tests and presubmit checks pass, commit your changes and upload the CL
|
|
|
112
112
|
*(If you update an existing commit on this branch, use `git commit --amend`).*
|
|
113
113
|
|
|
114
114
|
2. **Upload the CL to Gerrit**
|
|
115
|
-
Upload the CL using `git cl upload`.
|
|
115
|
+
Upload the CL using `git cl upload -f`. Always pass `-f` (`--force`) to prevent `git cl upload` from prompting or opening an interactive text editor in background agent shells:
|
|
116
116
|
```bash
|
|
117
|
-
git cl upload -d --commit-description="Ensure consistent UI Strings in <folder_path>
|
|
117
|
+
git cl upload -f -d --commit-description="Ensure consistent UI Strings in <folder_path>
|
|
118
118
|
|
|
119
119
|
Summary of changes:
|
|
120
120
|
- <dynamically list specific words replaced, contractions adopted, or sentence case fixes>
|
|
@@ -43,7 +43,7 @@ The second bot should do the following:
|
|
|
43
43
|
- Ensure you use the instructions from the `devtools-version-control` skill to create and switch branches appropriately.
|
|
44
44
|
- Make a minor, harmless modification directly to the test file being investigated (e.g., adding a comment `// Trigger stressor bot`) to ensure the commit is not empty.
|
|
45
45
|
- Add this file (with `git add`) and commit it using a meaningful commit message (e.g., `git commit -m "Deflake <test name>"`). Ask the user which bug number to use for the changelist description.
|
|
46
|
-
- Upload this change using `git cl upload`. This creates a debugging CL that all agents (like the fix agent) will continue to work on.
|
|
46
|
+
- Upload this change using `git cl upload -f`. Always use `-f` (`--force`) so the upload runs non-interactively without opening a text editor or prompt. This creates a debugging CL that all agents (like the fix agent) will continue to work on.
|
|
47
47
|
- Note the <issue number> created during upload.
|
|
48
48
|
- To start the stressor bot, run this command (substituting `<test file>:exact_test_id` with the actual file and exact test ID):
|
|
49
49
|
`git cl try -B devtools-frontend/try -b e2e_stressor_linux -b e2e_stressor_win64 -b e2e_stressor_mac -p runner_args='<test file>:exact_test_id --repeat=100'`
|
|
@@ -81,7 +81,7 @@ The test fixing sub-agent should do the following:
|
|
|
81
81
|
- Compile and run the test locally, to make sure there are no obvious errors introduced.
|
|
82
82
|
- Commit the changes (to the branch created earlier). Make sure to list (in the changelist description) the test name, the fix, reasoning for why the test failed, why the fix fixes the issue and the failure rate (if non-zero). If you were not able to reproduce the error, state that the fix is speculative. Ask the user to provide the bug number to use in the changelist description.
|
|
83
83
|
- CRITICAL: Iff the 'local-repro' bot does not reproduce the error, the 'fix-test' agent MUST wait until the stressor bot finishes its run before uploading the fix to the server (so that the stressor try run isn't aborted prematurely). If the error reproduces locally, you can upload immediately.
|
|
84
|
-
- Once it is safe to do so, upload the change with `git cl upload`.
|
|
84
|
+
- Once it is safe to do so, upload the change with `git cl upload -f`.
|
|
85
85
|
|
|
86
86
|
## Results
|
|
87
87
|
|
|
@@ -60,8 +60,9 @@ git rebase-update
|
|
|
60
60
|
### Initial upload
|
|
61
61
|
When a CL is ready, upload it with:
|
|
62
62
|
```bash
|
|
63
|
-
git cl upload -d --commit-description="<description>"
|
|
63
|
+
git cl upload -f -d --commit-description="<description>"
|
|
64
64
|
```
|
|
65
|
+
* Always include `-f` (`--force`) so that `git cl upload` runs non-interactively without opening a text editor or prompting.
|
|
65
66
|
* Use the same writing style as the current committer
|
|
66
67
|
* Keep line length below 72
|
|
67
68
|
* Add a "Bug: <issue number>" or "Bug: None" trailer on a separate line.
|
|
@@ -70,7 +71,7 @@ git cl upload -d --commit-description="<description>"
|
|
|
70
71
|
### Subsequent upload
|
|
71
72
|
To upload an updated CL:
|
|
72
73
|
```bash
|
|
73
|
-
git cl upload -d -t "<one sentence patch set description>"
|
|
74
|
+
git cl upload -f -d -t "<one sentence patch set description>"
|
|
74
75
|
```
|
|
75
76
|
|
|
76
77
|
## Release and Roll Status
|
|
@@ -86,7 +87,7 @@ To check whether a DevTools commit (`devtools/devtools-frontend`) has rolled int
|
|
|
86
87
|
| Create new CL from main | `git new-branch <name>` |
|
|
87
88
|
| Create stacked CL | `git new-branch --upstream_current <name>` |
|
|
88
89
|
| Update current CL | `git commit --amend` |
|
|
89
|
-
| Upload to Gerrit | `git cl upload` |
|
|
90
|
+
| Upload to Gerrit | `git cl upload -f` |
|
|
90
91
|
| Change branch parent | `git reparent-branch <new-parent>` |
|
|
91
92
|
| Sync all branches | `git rebase-update` |
|
|
92
93
|
| Check release & roll status | Query `https://chromiumdash.appspot.com/fetch_commit?commit=<sha>` |
|
package/docs/ui_engineering.md
CHANGED
|
@@ -1515,3 +1515,92 @@ The component will automatically sort and merge the ranges provided.
|
|
|
1515
1515
|
|
|
1516
1516
|
In this example, the ranges `1,3` and `2,3` will be merged into `1,4`. The
|
|
1517
1517
|
ranges `10,2` and the current range `5,3` will also be highlighted.
|
|
1518
|
+
|
|
1519
|
+
## Migrating `UI.ListWidget.ListWidget`
|
|
1520
|
+
|
|
1521
|
+
Replace the legacy imperative `UI.ListWidget.ListWidget` (which rendered inline editing controls and required custom delegates) with the modern `<devtools-list>` component.
|
|
1522
|
+
|
|
1523
|
+
### Guidelines
|
|
1524
|
+
1. **Dialogue Modals for Complex Editors:** If the editor contains multiple input fields (e.g., location details, headers), show the editor in a modal card dialog powered by `UI.Dialog.Dialog`.
|
|
1525
|
+
2. **Inline Prompts for Trivial Editors:** If the editor is trivial (i.e., a single text input, such as URL pattern blocking rules), handle editing inline using a `<devtools-prompt>` component. In this case, clicking the item focuses the prompt and toggles `editing` mode inline, dispatching `@commit` and `@cancel` events to update the state. See `RequestConditionsDrawer` for a reference implementation.
|
|
1526
|
+
3. **Conditionally render list:** Use `<devtools-list>` and conditionally render it inside the card only if the list has elements (`input.locations.length > 0`). This ensures empty state layout rules are correctly applied and prevents extra spacing/gaps.
|
|
1527
|
+
4. **Declarative dialog views:** Define the edit/add modal content in a separate, customizable view function (e.g. `DEFAULT_DIALOG_VIEW`). Instantiating `UI.Dialog.Dialog` in the class is fine, but its content rendering should be driven declaratively by the dialog view function.
|
|
1528
|
+
5. **Common styles integration:** Ensure `{includeCommonStyles: true}` is passed when rendering elements into the DOM inside test setups to import proper layout variables and global typography styles (e.g. Roboto).
|
|
1529
|
+
|
|
1530
|
+
**Before:**
|
|
1531
|
+
```typescript
|
|
1532
|
+
class SettingsTab extends UI.Widget.VBox implements UI.ListWidget.Delegate<Location> {
|
|
1533
|
+
private readonly listWidget: UI.ListWidget.ListWidget<Location>;
|
|
1534
|
+
constructor() {
|
|
1535
|
+
super();
|
|
1536
|
+
this.listWidget = new UI.ListWidget.ListWidget(this);
|
|
1537
|
+
this.listWidget.show(this.element);
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
renderItem(item: Location, editable: boolean): Element {
|
|
1541
|
+
const element = document.createElement('div');
|
|
1542
|
+
element.createChild('span').textContent = item.title;
|
|
1543
|
+
return element;
|
|
1544
|
+
}
|
|
1545
|
+
|
|
1546
|
+
beginEdit(item: Location): UI.ListWidget.Editor<Location> {
|
|
1547
|
+
const editor = new UI.ListWidget.Editor<Location>();
|
|
1548
|
+
editor.createInput('title', 'text', 'Title', titleValidator);
|
|
1549
|
+
return editor;
|
|
1550
|
+
}
|
|
1551
|
+
|
|
1552
|
+
commitEdit(item: Location, editor: UI.ListWidget.Editor<Location>, isNew: boolean): void {
|
|
1553
|
+
item.title = editor.control('title').value;
|
|
1554
|
+
this.update();
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
```
|
|
1558
|
+
|
|
1559
|
+
**After:**
|
|
1560
|
+
```typescript
|
|
1561
|
+
export interface SettingsTabInput {
|
|
1562
|
+
items: Location[];
|
|
1563
|
+
onAddClicked: () => void;
|
|
1564
|
+
onEdit: (index: number) => void;
|
|
1565
|
+
onDelete: (index: number) => void;
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
export const DEFAULT_VIEW: View = (input, _output, target) => {
|
|
1569
|
+
render(html`
|
|
1570
|
+
<devtools-card heading="Locations">
|
|
1571
|
+
${input.items.length > 0 ? html`
|
|
1572
|
+
<devtools-list
|
|
1573
|
+
.editable=${true}
|
|
1574
|
+
.deletable=${true}
|
|
1575
|
+
@edit=${(e: Lists.List.ItemEditEvent) => input.onEdit(e.detail.index)}
|
|
1576
|
+
@delete=${(e: Lists.List.ItemRemoveEvent) => input.onDelete(e.detail.index)}>
|
|
1577
|
+
${input.items.map((item, index) => html`
|
|
1578
|
+
<div slot="slot-${index}">
|
|
1579
|
+
<span>${item.title}</span>
|
|
1580
|
+
</div>
|
|
1581
|
+
`)}
|
|
1582
|
+
</devtools-list>
|
|
1583
|
+
` : nothing}
|
|
1584
|
+
<devtools-button @click=${input.onAddClicked}>Add location</devtools-button>
|
|
1585
|
+
</devtools-card>
|
|
1586
|
+
`, target);
|
|
1587
|
+
};
|
|
1588
|
+
|
|
1589
|
+
export interface SettingsTabDialogInput {
|
|
1590
|
+
editingValues: {title: string};
|
|
1591
|
+
validationErrors: {title: string | null};
|
|
1592
|
+
onTitleInput: (val: string) => void;
|
|
1593
|
+
onCancel: () => void;
|
|
1594
|
+
onSave: () => void;
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
export const DEFAULT_DIALOG_VIEW = (input: SettingsTabDialogInput, target: HTMLElement) => {
|
|
1598
|
+
render(html`
|
|
1599
|
+
<div class="editor-container">
|
|
1600
|
+
<input type="text" .value=${input.editingValues.title} @input=${(e: Event) => input.onTitleInput((e.target as HTMLInputElement).value)}>
|
|
1601
|
+
<devtools-button @click=${input.onCancel}>Cancel</devtools-button>
|
|
1602
|
+
<devtools-button @click=${input.onSave}>Save</devtools-button>
|
|
1603
|
+
</div>
|
|
1604
|
+
`, target);
|
|
1605
|
+
};
|
|
1606
|
+
```
|
package/eslint.config.mjs
CHANGED
|
@@ -621,10 +621,9 @@ export default defineConfig([
|
|
|
621
621
|
],
|
|
622
622
|
'@devtools/l10n-i18nString-call-only-with-uistrings': 'error',
|
|
623
623
|
'@devtools/l10n-no-i18nString-calls-module-instantiation': 'error',
|
|
624
|
-
'@devtools/l10n-no-locked-or-placeholder-only-phrase': 'error',
|
|
625
624
|
'@devtools/l10n-no-uistrings-export': 'error',
|
|
626
625
|
'@devtools/l10n-no-unused-message': 'error',
|
|
627
|
-
'@devtools/l10n-
|
|
626
|
+
'@devtools/l10n-uistrings-text-style': 'error',
|
|
628
627
|
},
|
|
629
628
|
},
|
|
630
629
|
{
|
|
@@ -64,6 +64,7 @@ const AidaLanguageToMarkdown: Record<AidaInferenceLanguage, string> = {
|
|
|
64
64
|
export class AidaAbortError extends Error {}
|
|
65
65
|
export class AidaBlockError extends Error {}
|
|
66
66
|
export class AidaQuotaError extends Error {}
|
|
67
|
+
export class AidaPayloadTooLargeError extends Error {}
|
|
67
68
|
|
|
68
69
|
interface AiStream {
|
|
69
70
|
write: (data: string) => Promise<void>;
|
|
@@ -195,13 +196,16 @@ export class AidaClient {
|
|
|
195
196
|
return;
|
|
196
197
|
}
|
|
197
198
|
if ('error' in result && result.error) {
|
|
198
|
-
|
|
199
|
-
const detailStr = typeof result.detail === 'string' ? result.detail : '';
|
|
200
|
-
if (errorStr.toLowerCase().includes('quota') || detailStr.toLowerCase().includes('quota')) {
|
|
199
|
+
if (isQuotaError(result.error, result.detail)) {
|
|
201
200
|
stream.fail(new AidaQuotaError(
|
|
202
201
|
`Cannot send request: ${result.error}${result.detail ? ` ${result.detail}` : ''}`));
|
|
203
202
|
return;
|
|
204
203
|
}
|
|
204
|
+
if (isPayloadTooLargeError(result.error, result.detail)) {
|
|
205
|
+
stream.fail(new AidaPayloadTooLargeError(
|
|
206
|
+
`Cannot send request: ${result.error}${result.detail ? ` ${result.detail}` : ''}`));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
205
209
|
stream.fail(new Error(`Cannot send request: ${result.error}${result.detail ? ` ${result.detail}` : ''}`));
|
|
206
210
|
return;
|
|
207
211
|
}
|
|
@@ -261,9 +265,12 @@ export class AidaClient {
|
|
|
261
265
|
thoughtSignature: result.functionCallChunk.functionCall.thoughtSignature,
|
|
262
266
|
});
|
|
263
267
|
} else if ('error' in result) {
|
|
264
|
-
if (
|
|
268
|
+
if (isQuotaError(result.error)) {
|
|
265
269
|
throw new AidaQuotaError(`Server responded: ${JSON.stringify(result)}`);
|
|
266
270
|
}
|
|
271
|
+
if (isPayloadTooLargeError(result.error)) {
|
|
272
|
+
throw new AidaPayloadTooLargeError(`Server responded: ${JSON.stringify(result)}`);
|
|
273
|
+
}
|
|
267
274
|
throw new Error(`Server responded: ${JSON.stringify(result)}`);
|
|
268
275
|
} else {
|
|
269
276
|
throw new Error(`Unknown chunk result ${JSON.stringify(result)}`);
|
|
@@ -519,3 +526,11 @@ export const enum Events {
|
|
|
519
526
|
export interface EventTypes {
|
|
520
527
|
[Events.AIDA_AVAILABILITY_CHANGED]: void;
|
|
521
528
|
}
|
|
529
|
+
|
|
530
|
+
function isQuotaError(...inputs: Array<string|undefined>): boolean {
|
|
531
|
+
return inputs.some(input => input?.toLowerCase().includes('quota'));
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function isPayloadTooLargeError(...inputs: Array<string|undefined>): boolean {
|
|
535
|
+
return inputs.some(input => input?.toLowerCase().includes('payload size exceeds the limit'));
|
|
536
|
+
}
|
|
@@ -37,7 +37,7 @@ import {streamWrite as resourceLoaderStreamWrite} from './ResourceLoader.js';
|
|
|
37
37
|
|
|
38
38
|
const UIStrings = {
|
|
39
39
|
/**
|
|
40
|
-
* @description Document title in Inspector Frontend Host of the DevTools window
|
|
40
|
+
* @description Document title in Inspector Frontend Host of the DevTools window.
|
|
41
41
|
* @example {example.com} PH1
|
|
42
42
|
*/
|
|
43
43
|
devtoolsS: 'DevTools - {PH1}',
|
|
@@ -12,59 +12,59 @@ import type {LoadNetworkResourceResult} from './InspectorFrontendHostAPI.js';
|
|
|
12
12
|
|
|
13
13
|
const UIStrings = {
|
|
14
14
|
/**
|
|
15
|
-
* @description Name of an error category used in error messages
|
|
15
|
+
* @description Name of an error category used in error messages.
|
|
16
16
|
*/
|
|
17
17
|
systemError: 'System error',
|
|
18
18
|
/**
|
|
19
|
-
* @description Name of an error category used in error messages
|
|
19
|
+
* @description Name of an error category used in error messages.
|
|
20
20
|
*/
|
|
21
21
|
connectionError: 'Connection error',
|
|
22
22
|
/**
|
|
23
|
-
* @description Name of an error category used in error messages
|
|
23
|
+
* @description Name of an error category used in error messages.
|
|
24
24
|
*/
|
|
25
25
|
certificateError: 'Certificate error',
|
|
26
26
|
/**
|
|
27
|
-
* @description Name of an error category used in error messages
|
|
27
|
+
* @description Name of an error category used in error messages.
|
|
28
28
|
*/
|
|
29
29
|
httpError: 'HTTP error',
|
|
30
30
|
/**
|
|
31
|
-
* @description Name of an error category used in error messages
|
|
31
|
+
* @description Name of an error category used in error messages.
|
|
32
32
|
*/
|
|
33
33
|
cacheError: 'Cache error',
|
|
34
34
|
/**
|
|
35
|
-
* @description Name of an error category used in error messages
|
|
35
|
+
* @description Name of an error category used in error messages.
|
|
36
36
|
*/
|
|
37
|
-
signedExchangeError: 'Signed Exchange error',
|
|
37
|
+
signedExchangeError: '`Signed Exchange` error',
|
|
38
38
|
/**
|
|
39
|
-
* @description Name of an error category used in error messages
|
|
39
|
+
* @description Name of an error category used in error messages.
|
|
40
40
|
*/
|
|
41
41
|
ftpError: 'FTP error',
|
|
42
42
|
/**
|
|
43
|
-
* @description Name of an error category used in error messages
|
|
43
|
+
* @description Name of an error category used in error messages.
|
|
44
44
|
*/
|
|
45
45
|
certificateManagerError: 'Certificate manager error',
|
|
46
46
|
/**
|
|
47
|
-
* @description Name of an error category used in error messages
|
|
47
|
+
* @description Name of an error category used in error messages.
|
|
48
48
|
*/
|
|
49
49
|
dnsResolverError: 'DNS resolver error',
|
|
50
50
|
/**
|
|
51
|
-
* @description Name of an error category used in error messages
|
|
51
|
+
* @description Name of an error category used in error messages.
|
|
52
52
|
*/
|
|
53
53
|
unknownError: 'Unknown error',
|
|
54
54
|
/**
|
|
55
|
-
* @description Phrase used in error messages that carry a network error name
|
|
55
|
+
* @description Phrase used in error messages that carry a network error name.
|
|
56
56
|
* @example {404} PH1
|
|
57
57
|
* @example {net::ERR_INSUFFICIENT_RESOURCES} PH2
|
|
58
58
|
*/
|
|
59
59
|
httpErrorStatusCodeSS: 'HTTP error: status code {PH1}, {PH2}',
|
|
60
60
|
/**
|
|
61
|
-
* @description Name of an error category used in error messages
|
|
61
|
+
* @description Name of an error category used in error messages.
|
|
62
62
|
*/
|
|
63
63
|
invalidUrl: 'Invalid URL',
|
|
64
64
|
/**
|
|
65
|
-
* @description Name of an error category used in error messages
|
|
65
|
+
* @description Name of an error category used in error messages.
|
|
66
66
|
*/
|
|
67
|
-
decodingDataUrlFailed: 'Decoding
|
|
67
|
+
decodingDataUrlFailed: 'Decoding data URL failed',
|
|
68
68
|
} as const;
|
|
69
69
|
const str_ = i18n.i18n.registerUIStrings('core/host/ResourceLoader.ts', UIStrings);
|
|
70
70
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
|
@@ -886,6 +886,81 @@ export class CSSMatchedStyles {
|
|
|
886
886
|
return new Set(this.#customHighlightPseudoDOMCascades.keys());
|
|
887
887
|
}
|
|
888
888
|
|
|
889
|
+
/**
|
|
890
|
+
* Looks for a rule with the same selector chain as a specific parent rule of the current one.
|
|
891
|
+
* This finds a rule with the same guaranteed specificity, not necessarily THE parent rule,
|
|
892
|
+
* as the same selector string can be used on multiple rules in the same scope, in the same
|
|
893
|
+
* or different stylesheet.
|
|
894
|
+
*
|
|
895
|
+
* @param rule The (nested) rule whose parent rule selector should be matched
|
|
896
|
+
* @param nestingIndex Nesting depth of the parent selector to be matched, with 0 meaning direct parent, 1 grandparent rule and so on
|
|
897
|
+
* @returns A rule with the same selector chain and specificity as selected parent rule, if found. `null` otherwise
|
|
898
|
+
*/
|
|
899
|
+
findParentRule(rule: CSSStyleRule, nestingIndex: number): CSSStyleRule|null {
|
|
900
|
+
const selectorText = rule.nestingSelectors?.[nestingIndex];
|
|
901
|
+
if (!selectorText) {
|
|
902
|
+
return null;
|
|
903
|
+
}
|
|
904
|
+
const nestingSelectors = rule.nestingSelectors?.slice(nestingIndex + 1) ?? [];
|
|
905
|
+
|
|
906
|
+
const matchCascade = (cascade: DOMInheritanceCascade): CSSStyleRule|null => {
|
|
907
|
+
for (const style of cascade.styles()) {
|
|
908
|
+
if (this.isInherited(style)) {
|
|
909
|
+
continue;
|
|
910
|
+
}
|
|
911
|
+
const parentRule = style.parentRule;
|
|
912
|
+
if (!(parentRule instanceof CSSStyleRule)) {
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
if (parentRule.selectorText() !== selectorText) {
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
const ruleNestingSelectors = parentRule.nestingSelectors ?? [];
|
|
919
|
+
if (ruleNestingSelectors.length !== nestingSelectors.length) {
|
|
920
|
+
continue;
|
|
921
|
+
}
|
|
922
|
+
let matchesChain = true;
|
|
923
|
+
for (let i = 0; i < nestingSelectors.length; i++) {
|
|
924
|
+
if (ruleNestingSelectors[i] !== nestingSelectors[i]) {
|
|
925
|
+
matchesChain = false;
|
|
926
|
+
break;
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
if (matchesChain) {
|
|
930
|
+
return parentRule;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
return null;
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
if (this.#mainDOMCascade) {
|
|
937
|
+
const match = matchCascade(this.#mainDOMCascade);
|
|
938
|
+
if (match) {
|
|
939
|
+
return match;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
|
|
943
|
+
if (this.#pseudoDOMCascades) {
|
|
944
|
+
for (const cascade of this.#pseudoDOMCascades.values()) {
|
|
945
|
+
const match = matchCascade(cascade);
|
|
946
|
+
if (match) {
|
|
947
|
+
return match;
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
if (this.#customHighlightPseudoDOMCascades) {
|
|
953
|
+
for (const cascade of this.#customHighlightPseudoDOMCascades.values()) {
|
|
954
|
+
const match = matchCascade(cascade);
|
|
955
|
+
if (match) {
|
|
956
|
+
return match;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
return null;
|
|
962
|
+
}
|
|
963
|
+
|
|
889
964
|
nodeForStyle(style: CSSStyleDeclaration): DOMNode|null {
|
|
890
965
|
return this.#addedStyles.get(style) || this.#nodeForStyle.get(style) || null;
|
|
891
966
|
}
|
|
@@ -33,7 +33,7 @@ const UIStrings = {
|
|
|
33
33
|
/**
|
|
34
34
|
* @description Tooltip to explain why a cookie was blocked
|
|
35
35
|
*/
|
|
36
|
-
notOnPath: 'This cookie was blocked because its path was not an exact match for or a superdirectory of the request
|
|
36
|
+
notOnPath: 'This cookie was blocked because its path was not an exact match for or a superdirectory of the request URL’s path.',
|
|
37
37
|
/**
|
|
38
38
|
* @description Tooltip to explain why a cookie was blocked
|
|
39
39
|
*/
|
|
@@ -139,7 +139,7 @@ const UIStrings = {
|
|
|
139
139
|
/**
|
|
140
140
|
* @description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked.
|
|
141
141
|
*/
|
|
142
|
-
blockedReasonInvalidDomain: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because its Domain attribute was invalid with regards to the current host
|
|
142
|
+
blockedReasonInvalidDomain: 'This attempt to set a cookie via a `Set-Cookie` header was blocked because its Domain attribute was invalid with regards to the current host URL.',
|
|
143
143
|
/**
|
|
144
144
|
* @description Tooltip to explain why an attempt to set a cookie via a `Set-Cookie` HTTP header on a request's response was blocked.
|
|
145
145
|
*/
|
|
@@ -152,7 +152,7 @@ const UIStrings = {
|
|
|
152
152
|
* @description Text in Network Manager
|
|
153
153
|
* @example {https://example.com} PH1
|
|
154
154
|
*/
|
|
155
|
-
setcookieHeaderIsIgnoredIn: 'Set-Cookie header is ignored in response from
|
|
155
|
+
setcookieHeaderIsIgnoredIn: 'Set-Cookie header is ignored in response from URL: {PH1}. The combined size of the name and value must be less than or equal to 4096 characters.',
|
|
156
156
|
/**
|
|
157
157
|
* @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted.
|
|
158
158
|
*/
|
|
@@ -172,7 +172,7 @@ const UIStrings = {
|
|
|
172
172
|
/**
|
|
173
173
|
* @description Tooltip to explain why the cookie should have been blocked by third-party cookie phaseout but is exempted.
|
|
174
174
|
*/
|
|
175
|
-
exemptionReasonScheme: 'This cookie is allowed by the top-level
|
|
175
|
+
exemptionReasonScheme: 'This cookie is allowed by the top-level URL scheme',
|
|
176
176
|
} as const;
|
|
177
177
|
// clang-format on
|
|
178
178
|
|
|
@@ -460,7 +460,8 @@ export class MainImpl {
|
|
|
460
460
|
targetManager.addEventListener(
|
|
461
461
|
SDK.TargetManager.Events.SUSPEND_STATE_CHANGED, this.#onSuspendStateChanged.bind(this));
|
|
462
462
|
|
|
463
|
-
new Bindings.PresentationConsoleMessageHelper.PresentationConsoleMessageManager(targetManager
|
|
463
|
+
new Bindings.PresentationConsoleMessageHelper.PresentationConsoleMessageManager(targetManager,
|
|
464
|
+
this.#universe.workspace);
|
|
464
465
|
targetManager.setScopeTarget(targetManager.primaryPageTarget());
|
|
465
466
|
UI.Context.Context.instance().addFlavorChangeListener(SDK.Target.Target, ({data}) => {
|
|
466
467
|
const outermostTarget = data?.outermostTarget();
|
|
@@ -192,11 +192,6 @@ const UIStrings = {
|
|
|
192
192
|
* of saving settings to their Google account.
|
|
193
193
|
*/
|
|
194
194
|
saveSettings: 'Save `DevTools` settings to your `Google` account',
|
|
195
|
-
/**
|
|
196
|
-
* @description Label for a checkbox in the settings UI. Allows developers to opt-in/opt-out
|
|
197
|
-
* of receiving Google Developer Program (GDP) badges based on their activity in Chrome DevTools.
|
|
198
|
-
*/
|
|
199
|
-
earnBadges: 'Earn badges',
|
|
200
195
|
/**
|
|
201
196
|
* @description A command available in the command menu to perform searches, for example in the
|
|
202
197
|
* elements panel, as user types, rather than only when they press Enter.
|
|
@@ -795,16 +790,6 @@ Common.Settings.registerSettingExtension({
|
|
|
795
790
|
reloadRequired: true,
|
|
796
791
|
});
|
|
797
792
|
|
|
798
|
-
Common.Settings.registerSettingExtension({
|
|
799
|
-
category: Common.Settings.SettingCategory.ACCOUNT,
|
|
800
|
-
settingName: 'receive-gdp-badges',
|
|
801
|
-
settingType: Common.Settings.SettingType.BOOLEAN,
|
|
802
|
-
storageType: Common.Settings.SettingStorageType.SYNCED,
|
|
803
|
-
title: i18nLazyString(UIStrings.earnBadges),
|
|
804
|
-
defaultValue: false,
|
|
805
|
-
reloadRequired: true,
|
|
806
|
-
});
|
|
807
|
-
|
|
808
793
|
Common.Settings.registerSettingExtension({
|
|
809
794
|
storageType: Common.Settings.SettingStorageType.SYNCED,
|
|
810
795
|
settingName: 'user-shortcuts',
|
|
@@ -18,6 +18,7 @@ import '../../panels/settings/settings-meta.js';
|
|
|
18
18
|
import '../../panels/protocol_monitor/protocol_monitor-meta.js';
|
|
19
19
|
import '../../models/persistence/persistence-meta.js';
|
|
20
20
|
import '../../models/logs/logs-meta.js';
|
|
21
|
+
import '../../models/badges/badges-meta.js';
|
|
21
22
|
import '../main/main-meta.js';
|
|
22
23
|
import '../../ui/legacy/components/perf_ui/perf_ui-meta.js';
|
|
23
24
|
import '../../ui/legacy/components/quick_open/quick_open-meta.js';
|
|
@@ -13,6 +13,7 @@ import '../../models/workspace/workspace-meta.js';
|
|
|
13
13
|
import '../../Images/Images.js';
|
|
14
14
|
import '../../models/logs/logs-meta.js';
|
|
15
15
|
import '../../models/persistence/persistence-meta.js';
|
|
16
|
+
import '../../models/badges/badges-meta.js';
|
|
16
17
|
import '../../panels/browser_debugger/browser_debugger-meta.js';
|
|
17
18
|
import '../../panels/developer_resources/developer_resources-meta.js';
|
|
18
19
|
// panels/timeline depends on mobile_throttling for settings UI
|
|
@@ -38,6 +38,7 @@ export const enum ErrorType {
|
|
|
38
38
|
BLOCK = 'block',
|
|
39
39
|
CROSS_ORIGIN = 'cross-origin',
|
|
40
40
|
QUOTA = 'quota',
|
|
41
|
+
PAYLOAD_TOO_LARGE = 'payload-too-large',
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
export const enum MultimodalInputType {
|
|
@@ -806,6 +807,9 @@ export abstract class AiAgent<T> {
|
|
|
806
807
|
} else if (err instanceof Host.AidaClient.AidaQuotaError ||
|
|
807
808
|
(err instanceof Error && err.message.toLowerCase().includes('quota'))) {
|
|
808
809
|
error = ErrorType.QUOTA;
|
|
810
|
+
} else if (err instanceof Host.AidaClient.AidaPayloadTooLargeError ||
|
|
811
|
+
(err instanceof Error && /payload size exceeds the limit/i.test(err.message))) {
|
|
812
|
+
error = ErrorType.PAYLOAD_TOO_LARGE;
|
|
809
813
|
}
|
|
810
814
|
yield this.#createErrorResponse(error);
|
|
811
815
|
|
|
@@ -1096,7 +1100,11 @@ export abstract class AiAgent<T> {
|
|
|
1096
1100
|
request: structuredClone(request),
|
|
1097
1101
|
aidaResponse,
|
|
1098
1102
|
});
|
|
1099
|
-
|
|
1103
|
+
try {
|
|
1104
|
+
localStorage.setItem('aiAssistanceStructuredLog', JSON.stringify(this.#structuredLog));
|
|
1105
|
+
} catch (err) {
|
|
1106
|
+
console.warn('Failed to write to local storage "aiAssistanceStructuredLog":', err);
|
|
1107
|
+
}
|
|
1100
1108
|
}
|
|
1101
1109
|
}
|
|
1102
1110
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// found in the LICENSE file.
|
|
4
4
|
|
|
5
5
|
import * as Bindings from '../../bindings/bindings.js';
|
|
6
|
+
import * as Logs from '../../logs/logs.js';
|
|
6
7
|
import * as NetworkTimeCalculator from '../../network_time_calculator/network_time_calculator.js';
|
|
7
8
|
import type * as Workspace from '../../workspace/workspace.js';
|
|
8
9
|
|
|
@@ -51,12 +52,18 @@ export class FileFormatter {
|
|
|
51
52
|
|
|
52
53
|
#file: Workspace.UISourceCode.UISourceCode;
|
|
53
54
|
#debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding;
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
#networkLog: Logs.NetworkLog.NetworkLog;
|
|
56
|
+
constructor(
|
|
57
|
+
file: Workspace.UISourceCode.UISourceCode,
|
|
58
|
+
debuggerWorkspaceBinding: Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding =
|
|
59
|
+
Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance(),
|
|
60
|
+
networkLog: Logs.NetworkLog.NetworkLog = Logs.NetworkLog.NetworkLog.instance(),
|
|
61
|
+
) {
|
|
57
62
|
this.#file = file;
|
|
58
63
|
this.#debuggerWorkspaceBinding = debuggerWorkspaceBinding;
|
|
64
|
+
this.#networkLog = networkLog;
|
|
59
65
|
}
|
|
66
|
+
|
|
60
67
|
formatFile(): string {
|
|
61
68
|
const sourceMapDetails = FileFormatter.formatSourceMapDetails(this.#file, this.#debuggerWorkspaceBinding);
|
|
62
69
|
const lines = [
|
|
@@ -69,7 +76,7 @@ export class FileFormatter {
|
|
|
69
76
|
const calculator = new NetworkTimeCalculator.NetworkTransferTimeCalculator();
|
|
70
77
|
calculator.updateBoundaries(resource.request);
|
|
71
78
|
lines.push(`Request initiator chain:
|
|
72
|
-
${new NetworkRequestFormatter(resource.request, calculator).formatRequestInitiatorChain()}`);
|
|
79
|
+
${new NetworkRequestFormatter(resource.request, calculator, this.#networkLog).formatRequestInitiatorChain()}`);
|
|
73
80
|
}
|
|
74
81
|
lines.push(`File content:
|
|
75
82
|
${this.#formatFileContent()}`);
|
|
@@ -131,10 +131,16 @@ export class NetworkRequestFormatter {
|
|
|
131
131
|
return lines.length > 0 ? `${lines.join('\n')}\n` : '';
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
+
readonly #networkLog: Logs.NetworkLog.NetworkLog;
|
|
135
|
+
|
|
134
136
|
constructor(
|
|
135
|
-
request: SDK.NetworkRequest.NetworkRequest,
|
|
137
|
+
request: SDK.NetworkRequest.NetworkRequest,
|
|
138
|
+
calculator: NetworkTimeCalculator.NetworkTransferTimeCalculator,
|
|
139
|
+
networkLog: Logs.NetworkLog.NetworkLog = Logs.NetworkLog.NetworkLog.instance(),
|
|
140
|
+
) {
|
|
136
141
|
this.#request = request;
|
|
137
142
|
this.#calculator = calculator;
|
|
143
|
+
this.#networkLog = networkLog;
|
|
138
144
|
}
|
|
139
145
|
|
|
140
146
|
formatRequestHeaders(): string {
|
|
@@ -199,7 +205,7 @@ Request initiator chain:\n${this.formatRequestInitiatorChain()}`;
|
|
|
199
205
|
const allowedOrigin = Common.ParsedURL.ParsedURL.extractOrigin(this.#request.url());
|
|
200
206
|
let initiatorChain = '';
|
|
201
207
|
let lineStart = '- URL: ';
|
|
202
|
-
const graph =
|
|
208
|
+
const graph = this.#networkLog.initiatorGraphForRequest(this.#request);
|
|
203
209
|
|
|
204
210
|
for (const initiator of Array.from(graph.initiators).reverse()) {
|
|
205
211
|
initiatorChain = initiatorChain + lineStart +
|
|
@@ -33,6 +33,7 @@ export class AICallTree {
|
|
|
33
33
|
public selectedNode: Trace.Extras.TraceTree.Node|null,
|
|
34
34
|
public rootNode: Trace.Extras.TraceTree.TopDownRootNode,
|
|
35
35
|
public parsedTrace: Trace.TraceModel.ParsedTrace,
|
|
36
|
+
public workspace: Workspace.Workspace.WorkspaceImpl = Workspace.Workspace.WorkspaceImpl.instance(),
|
|
36
37
|
) {
|
|
37
38
|
}
|
|
38
39
|
|
|
@@ -336,8 +337,7 @@ export class AICallTree {
|
|
|
336
337
|
const selfTimeStr = roundToTenths(node.selfTime);
|
|
337
338
|
|
|
338
339
|
// 6. URL Index
|
|
339
|
-
const location = SourceMapsResolver.SourceMapsResolver.codeLocationForEntry(
|
|
340
|
-
parsedTrace, event, Workspace.Workspace.WorkspaceImpl.instance());
|
|
340
|
+
const location = SourceMapsResolver.SourceMapsResolver.codeLocationForEntry(parsedTrace, event, this.workspace);
|
|
341
341
|
const url = location?.url;
|
|
342
342
|
let urlIndexStr = '';
|
|
343
343
|
if (url) {
|
|
@@ -38,6 +38,12 @@ export class GetNetworkRequestDetailsTool implements
|
|
|
38
38
|
readonly description =
|
|
39
39
|
'Retrieves the full headers, timing, status, and body details of a specific network request by ID.';
|
|
40
40
|
|
|
41
|
+
readonly #networkLog?: Logs.NetworkLog.NetworkLog;
|
|
42
|
+
|
|
43
|
+
constructor(networkLog?: Logs.NetworkLog.NetworkLog) {
|
|
44
|
+
this.#networkLog = networkLog;
|
|
45
|
+
}
|
|
46
|
+
|
|
41
47
|
readonly parameters: Host.AidaClient.FunctionObjectParam<keyof GetNetworkRequestDetailsArgs> = {
|
|
42
48
|
type: Host.AidaClient.ParametersTypes.OBJECT,
|
|
43
49
|
description: 'Arguments for retrieving detailed information about a specific network request.',
|
|
@@ -81,7 +87,8 @@ export class GetNetworkRequestDetailsTool implements
|
|
|
81
87
|
};
|
|
82
88
|
}
|
|
83
89
|
|
|
84
|
-
const
|
|
90
|
+
const networkLog = this.#networkLog ?? Logs.NetworkLog.NetworkLog.instance();
|
|
91
|
+
const request = networkLog.requests().find(req => {
|
|
85
92
|
if (req.requestId() !== args.id) {
|
|
86
93
|
return false;
|
|
87
94
|
}
|
|
@@ -102,7 +109,7 @@ export class GetNetworkRequestDetailsTool implements
|
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
const calculator = new NetworkTimeCalculator.NetworkTransferTimeCalculator();
|
|
105
|
-
const formatter = new NetworkRequestFormatter(request, calculator);
|
|
112
|
+
const formatter = new NetworkRequestFormatter(request, calculator, networkLog);
|
|
106
113
|
const formattedDetails = await formatter.formatNetworkRequest();
|
|
107
114
|
|
|
108
115
|
return {
|