chrome-devtools-frontend 1.0.1698733 → 1.0.1699061

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 (59) hide show
  1. package/.agents/skills/devtools-verification/SKILL.md +19 -0
  2. package/.agents/skills/{repro-flaky-tests → fix-tests}/SKILL.md +2 -2
  3. package/front_end/core/sdk/DOMModel.ts +3 -5
  4. package/front_end/core/sdk/NetworkManager.ts +2 -2
  5. package/front_end/core/sdk/OverlayModel.ts +11 -12
  6. package/front_end/core/sdk/SourceMapScopeChainEntry.ts +84 -32
  7. package/front_end/models/emulation/DeviceModeModel.ts +1 -1
  8. package/front_end/models/issues_manager/CookieIssue.ts +0 -13
  9. package/front_end/models/source_map_scopes/NamesResolver.ts +11 -3
  10. package/front_end/models/source_map_scopes/ScopeChainModel.ts +10 -6
  11. package/front_end/panels/accessibility/AccessibilityAnnouncementRecordingView.ts +281 -3
  12. package/front_end/panels/accessibility/accessibilityAnnouncementRecordingView.css +85 -0
  13. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +1 -2
  14. package/front_end/panels/animation/AnimationTimeline.ts +1 -1
  15. package/front_end/panels/application/CrashReportContextView.ts +1 -1
  16. package/front_end/panels/application/IndexedDBViews.ts +1 -1
  17. package/front_end/panels/application/KeyValueStorageItemsView.ts +1 -0
  18. package/front_end/panels/application/components/AdsView.ts +63 -1
  19. package/front_end/panels/application/components/adsView.css +11 -1
  20. package/front_end/panels/common/CommentThreadWidget.ts +1 -16
  21. package/front_end/panels/common/commentThreadWidget.css +0 -12
  22. package/front_end/panels/elements/CSSRuleValidator.ts +1 -1
  23. package/front_end/panels/elements/ElementsPanel.ts +4 -4
  24. package/front_end/panels/elements/ElementsTreeElement.ts +7 -2
  25. package/front_end/panels/elements/ElementsTreeOutline.ts +14 -26
  26. package/front_end/panels/elements/elementsTreeOutline.css +4 -4
  27. package/front_end/panels/lighthouse/LighthouseController.ts +1 -1
  28. package/front_end/panels/mobile_throttling/NetworkPanelIndicator.ts +1 -1
  29. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +1 -1
  30. package/front_end/panels/mobile_throttling/ThrottlingSettingsTab.ts +1 -1
  31. package/front_end/panels/recorder/StepView.ts +19 -4
  32. package/front_end/panels/recorder/TimelineSection.ts +1 -1
  33. package/front_end/panels/settings/BackendLinkingSettingsTab.ts +2 -1
  34. package/front_end/panels/settings/EditFileSystemView.ts +1 -1
  35. package/front_end/panels/sources/CallStackSidebarPane.ts +1 -1
  36. package/front_end/panels/sources/DebuggerPlugin.ts +1 -1
  37. package/front_end/panels/timeline/components/DetailsView.ts +3 -3
  38. package/front_end/panels/timeline/components/OriginMap.ts +1 -1
  39. package/front_end/panels/timeline/components/insights/BaseInsightComponent.ts +2 -3
  40. package/front_end/third_party/chromium/README.chromium +1 -1
  41. package/front_end/ui/components/buttons/Button.ts +19 -1
  42. package/front_end/ui/legacy/ContextMenu.ts +16 -1
  43. package/front_end/ui/legacy/PlusButton.ts +5 -6
  44. package/front_end/ui/legacy/Treeoutline.ts +2 -0
  45. package/front_end/ui/legacy/Widget.ts +33 -0
  46. package/front_end/ui/legacy/components/cookie_table/CookiesTable.ts +3 -0
  47. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +14 -4
  48. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +22 -25
  49. package/front_end/ui/visual_logging/KnownContextValues.ts +3 -0
  50. package/package.json +1 -1
  51. package/.agents/skills/devtools-ci/SKILL.md +0 -65
  52. package/.agents/skills/evaluate-ai-css-completion/SKILL.md +0 -143
  53. package/.agents/skills/evaluate-ai-css-completion/scripts/evaluate.js +0 -104
  54. package/.agents/skills/fixing-skipped-tests/SKILL.md +0 -32
  55. package/.agents/skills/gerrit-cli/SKILL.md +0 -74
  56. package/.agents/skills/gerrit-cli/references/commands.md +0 -169
  57. package/.agents/skills/gerrit-cli/references/workflows.md +0 -63
  58. package/.agents/skills/gerrit-cli/scripts/import_upstream.py +0 -85
  59. package/front_end/models/issues_manager/descriptions/cookieExcludeBlockedWithinRelatedWebsiteSet.md +0 -4
@@ -1454,3 +1454,36 @@ Node.prototype.removeChildren = function(): void {
1454
1454
  }
1455
1455
  return originalRemoveChildren.call(this);
1456
1456
  };
1457
+
1458
+ export interface WrapperWidgetParams {
1459
+ widget: Widget;
1460
+ }
1461
+
1462
+ export class WrapperWidget extends Widget {
1463
+ #widget: Widget|null = null;
1464
+ constructor(element: HTMLElement, _deps: never[], params?: WrapperWidgetParams) {
1465
+ super(element);
1466
+ this.element.style.setProperty('display', 'contents');
1467
+ if (params?.widget) {
1468
+ this.widget = params.widget;
1469
+ }
1470
+ }
1471
+
1472
+ set widget(widget: Widget|null) {
1473
+ if (this.#widget === widget) {
1474
+ return;
1475
+ }
1476
+ if (this.#widget) {
1477
+ this.#widget.detach();
1478
+ }
1479
+ this.#widget = widget;
1480
+ if (this.#widget) {
1481
+ this.#widget.show(this.element, undefined, /* suppressOrphanWidgetError */ true);
1482
+ }
1483
+ }
1484
+ override focus(): void {
1485
+ if (this.#widget) {
1486
+ this.#widget.focus();
1487
+ }
1488
+ }
1489
+ }
@@ -54,6 +54,7 @@ export interface ViewInput {
54
54
  data: CookieData[];
55
55
  selectedKey?: string;
56
56
  editable?: boolean;
57
+ deletable?: boolean;
57
58
  renderInline?: boolean;
58
59
  portBindingEnabled?: boolean;
59
60
  schemeBindingEnabled?: boolean;
@@ -233,6 +234,7 @@ export class CookiesTable extends UI.Widget.VBox {
233
234
  id="cookies-table"
234
235
  striped
235
236
  ?inline=${input.renderInline}
237
+ ?deletable=${input.deletable}
236
238
  @create=${(e: CustomEvent<CookieData>) => input.onCreate(e.detail)}
237
239
  @refresh=${input.onRefresh}
238
240
  @deselect=${() => input.onSelect(undefined)}
@@ -448,6 +450,7 @@ export class CookiesTable extends UI.Widget.VBox {
448
450
  data: this.data,
449
451
  selectedKey: this.selectedKey,
450
452
  editable: this.#editable,
453
+ deletable: Boolean(this.#deleteCallback),
451
454
  renderInline: this.renderInline,
452
455
  schemeBindingEnabled: this.schemeBindingEnabled,
453
456
  portBindingEnabled: this.portBindingEnabled,
@@ -48,6 +48,8 @@ const DUMMY_COLUMN_ID = 'dummy'; // SortableDataGrid.create requires at least o
48
48
  * visible rows are layed out and sorting is provided out of the box.
49
49
  *
50
50
  * @property filters Set of text filters to be applied to the data grid.
51
+ * @attribute deletable If true, rows can be deleted through the context menu or the Delete
52
+ * key, which dispatches a `delete` event on the corresponding <tr> element.
51
53
  * @attribute inline If true, the data grid will render inline instead of taking a full container height.
52
54
  * @attribute resize Column resize method, one of 'nearest' (default), 'first' or 'last'.
53
55
  * @attribute striped If true, the data grid will have striped rows.
@@ -58,7 +60,7 @@ type DataGridElementNode = SortableNode|DynamicHeightNode;
58
60
  const elementToNode = new WeakMap<Element, DataGridElementNode>();
59
61
 
60
62
  export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate {
61
- static readonly observedAttributes: string[] = ['striped', 'name', 'inline', 'resize', 'highlight'];
63
+ static readonly observedAttributes: string[] = ['striped', 'name', 'inline', 'resize', 'highlight', 'deletable'];
62
64
 
63
65
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
64
66
  #dataGrid!: DataGridImpl<any>;
@@ -220,6 +222,9 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
220
222
  this.#revealHighlightedNode(Number(newValue));
221
223
  });
222
224
  break;
225
+ case 'deletable':
226
+ this.#dataGrid.deleteCallback = this.deletable ? this.#deleteCallback.bind(this) : undefined;
227
+ break;
223
228
  }
224
229
  }
225
230
 
@@ -231,6 +236,14 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
231
236
  return hasBooleanAttribute(this, 'striped');
232
237
  }
233
238
 
239
+ set deletable(deletable: boolean) {
240
+ this.toggleAttribute('deletable', deletable);
241
+ }
242
+
243
+ get deletable(): boolean {
244
+ return hasBooleanAttribute(this, 'deletable');
245
+ }
246
+
234
247
  set inline(striped: boolean) {
235
248
  this.toggleAttribute('inline', striped);
236
249
  }
@@ -584,9 +597,6 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
584
597
  if (args[0] === 'refresh') {
585
598
  this.#dataGrid.refreshCallback = this.#refreshCallback.bind(this);
586
599
  }
587
- if (args[0] === 'delete') {
588
- this.#dataGrid.deleteCallback = this.#deleteCallback.bind(this);
589
- }
590
600
  }
591
601
 
592
602
  #refreshCallback(): void {
@@ -388,6 +388,10 @@ export class ObjectTreeExpansionTracker {
388
388
  }
389
389
  }
390
390
 
391
+ const ARRAY_LOAD_THRESHOLD = 100;
392
+ const ARRAY_BUCKET_THRESHOLD = 100;
393
+ const ARRAY_SPARSE_ITERATION_THRESHOLD = 250000;
394
+
391
395
  export abstract class ObjectTreeNodeBase extends Common.ObjectWrapper.ObjectWrapper<ObjectTreeNodeBase.EventTypes> {
392
396
  #children?: NodeChildren;
393
397
  protected readonly options: ObjectTreeOptions;
@@ -715,7 +719,7 @@ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
715
719
  }
716
720
 
717
721
  override async populateChildrenIfNeededImpl(): Promise<NodeChildren> {
718
- if (this.#range.count > ArrayGroupingTreeElement.bucketThreshold) {
722
+ if (this.#range.count > ARRAY_BUCKET_THRESHOLD) {
719
723
  const ranges = await arrayRangeGroups(this.object, this.#range.fromIndex, this.#range.toIndex);
720
724
  const arrayRanges = ranges?.ranges.map(
721
725
  ([fromIndex, toIndex, count]) => new ArrayGroupTreeNode(this.object, {fromIndex, toIndex, count}, this, {
@@ -729,7 +733,7 @@ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
729
733
  const result = await this.#object.callFunction(buildArrayFragment, [
730
734
  {value: this.#range.fromIndex},
731
735
  {value: this.#range.toIndex},
732
- {value: ArrayGroupingTreeElement.sparseIterationThreshold},
736
+ {value: ARRAY_SPARSE_ITERATION_THRESHOLD},
733
737
  ]);
734
738
  if (!result.object || result.wasThrown) {
735
739
  return {};
@@ -1240,9 +1244,6 @@ export class ObjectPropertiesSectionWidget extends UI.Widget.Widget {
1240
1244
  };
1241
1245
  }
1242
1246
 
1243
- /** @constant */
1244
- const ARRAY_LOAD_THRESHOLD = 100;
1245
-
1246
1247
  const maxRenderableStringLength = 10000;
1247
1248
 
1248
1249
  export const enum ObjectPropertiesMode {
@@ -1289,7 +1290,7 @@ export function populateObjectTreeContextMenu(
1289
1290
  {checked: object.includeNullOrUndefinedValues, jslogContext: 'show-all'});
1290
1291
  }
1291
1292
 
1292
- export interface ObjectTreeViewInput {
1293
+ interface ObjectTreeViewInput {
1293
1294
  renderAsSubtree: boolean;
1294
1295
  objectTree?: ObjectTree;
1295
1296
  linkifier?: Components.Linkifier.Linkifier;
@@ -1298,8 +1299,8 @@ export interface ObjectTreeViewInput {
1298
1299
  skipGettersAndSetters: boolean;
1299
1300
  onExpand: (expanded: boolean) => void;
1300
1301
  }
1301
- export type ObjectTreeView = (input: ObjectTreeViewInput, output: object, target: HTMLElement) => void;
1302
- export const OBJECT_TREE_DEFAULT_VIEW: ObjectTreeView = (input, output, target) => {
1302
+ type ObjectTreeView = (input: ObjectTreeViewInput, output: object, target: HTMLElement) => void;
1303
+ const OBJECT_TREE_DEFAULT_VIEW: ObjectTreeView = (input, output, target) => {
1303
1304
  const objectTree = input.objectTree;
1304
1305
  if (!objectTree) {
1305
1306
  render(nothing, target);
@@ -1454,7 +1455,7 @@ export function renderObjectTree(
1454
1455
  widgetRef(ObjectTreeWidget, () => {})}></ul>`;
1455
1456
  }
1456
1457
 
1457
- export interface ObjectPropertiesSectionViewInput {
1458
+ interface ObjectPropertiesSectionViewInput {
1458
1459
  objectTree: ObjectTree;
1459
1460
  title?: Element|TemplateResult;
1460
1461
  linkifier?: Components.Linkifier.Linkifier;
@@ -1464,8 +1465,8 @@ export interface ObjectPropertiesSectionViewInput {
1464
1465
  onExpand: (expanded: boolean) => void;
1465
1466
  }
1466
1467
 
1467
- export type ObjectPropertiesSectionView =
1468
- (input: ObjectPropertiesSectionViewInput, output: object, target: HTMLElement) => void;
1468
+ type ObjectPropertiesSectionView = (input: ObjectPropertiesSectionViewInput, output: object, target: HTMLElement) =>
1469
+ void;
1469
1470
 
1470
1471
  export const OBJECT_PROPERTIES_SECTION_DEFAULT_VIEW: ObjectPropertiesSectionView = (input, _output, target) => {
1471
1472
 
@@ -1674,9 +1675,9 @@ export interface ObjectPropertyViewInput {
1674
1675
  node: ObjectTreeNode;
1675
1676
  search?: UI.TreeOutline.TreeSearch<ObjectTreeNodeBase>;
1676
1677
  }
1677
- export type ObjectPropertyViewOutput = undefined;
1678
- export type ObjectPropertyView =
1679
- (input: ObjectPropertyViewInput, output: ObjectPropertyViewOutput, target: HTMLElement) => void;
1678
+ type ObjectPropertyViewOutput = undefined;
1679
+ type ObjectPropertyView = (input: ObjectPropertyViewInput, output: ObjectPropertyViewOutput, target: HTMLElement) =>
1680
+ void;
1680
1681
  export const OBJECT_PROPERTY_DEFAULT_VIEW: ObjectPropertyView = (input, _output, target) => {
1681
1682
  const {property} = input.node;
1682
1683
  const isInternalEntries = property.synthetic && input.node.name === '[[Entries]]';
@@ -1897,7 +1898,7 @@ export class ObjectPropertyWidget extends UI.Widget.Widget {
1897
1898
  }
1898
1899
  }
1899
1900
 
1900
- export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1901
+ class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1901
1902
  property: ObjectTreeNode;
1902
1903
  override toggleOnClick: boolean;
1903
1904
  private linkifier: Components.Linkifier.Linkifier|undefined;
@@ -2232,8 +2233,8 @@ async function arrayRangeGroups(object: SDK.RemoteObject.RemoteObject, fromIndex
2232
2233
  return await object.callFunctionJSON(packArrayRanges, [
2233
2234
  {value: fromIndex},
2234
2235
  {value: toIndex},
2235
- {value: ArrayGroupingTreeElement.bucketThreshold},
2236
- {value: ArrayGroupingTreeElement.sparseIterationThreshold},
2236
+ {value: ARRAY_BUCKET_THRESHOLD},
2237
+ {value: ARRAY_SPARSE_ITERATION_THRESHOLD},
2237
2238
  ]);
2238
2239
 
2239
2240
  /**
@@ -2361,7 +2362,7 @@ function buildArrayFragment(
2361
2362
  return result;
2362
2363
  }
2363
2364
 
2364
- export class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
2365
+ class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
2365
2366
  override toggleOnClick: boolean;
2366
2367
  private readonly linkifier: Components.Linkifier.Linkifier|undefined;
2367
2368
  readonly #child: ArrayGroupTreeNode;
@@ -2442,13 +2443,9 @@ export class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
2442
2443
  override onattach(): void {
2443
2444
  this.listItemElement.classList.add('object-properties-section-name');
2444
2445
  }
2445
-
2446
- // These should be module constants but they are modified by layout tests.
2447
- static bucketThreshold = 100;
2448
- static sparseIterationThreshold = 250000;
2449
2446
  }
2450
2447
 
2451
- export interface ExpandableTextViewInput {
2448
+ interface ExpandableTextViewInput {
2452
2449
  copyText: () => void;
2453
2450
  expandText: () => void;
2454
2451
  expanded: boolean;
@@ -2456,8 +2453,8 @@ export interface ExpandableTextViewInput {
2456
2453
  byteCount: number;
2457
2454
  text: string;
2458
2455
  }
2459
- export type ExpandableTextView = (input: ExpandableTextViewInput, output: object, target: HTMLElement) => void;
2460
- export const EXPANDABLE_TEXT_DEFAULT_VIEW: ExpandableTextView = (input, output, target) => {
2456
+ type ExpandableTextView = (input: ExpandableTextViewInput, output: object, target: HTMLElement) => void;
2457
+ const EXPANDABLE_TEXT_DEFAULT_VIEW: ExpandableTextView = (input, output, target) => {
2461
2458
  const totalBytesText = i18n.ByteUtilities.bytesToString(input.byteCount);
2462
2459
  const canExpand = input.text.length < ExpandableTextPropertyValue.MAX_DISPLAYABLE_TEXT_LENGTH;
2463
2460
  const onContextMenu = (e: Event): void => {
@@ -283,8 +283,11 @@ export const knownContextValues: Set<string> = new Set([
283
283
  'accept',
284
284
  'accept-execute-code',
285
285
  'access-control-allow-origin',
286
+ 'accessibility-announcements.filter-by-type',
286
287
  'accessibility-default',
287
288
  'accessibility-tree',
289
+ 'accessibility.clear-announcements',
290
+ 'accessibility.toggle-recording',
288
291
  'accessibility.view',
289
292
  'accuracy',
290
293
  'achromatopsia',
package/package.json CHANGED
@@ -90,5 +90,5 @@
90
90
  "yargs": "18.1.0",
91
91
  "yargs-unparser": "2.0.0"
92
92
  },
93
- "version": "1.0.1698733"
93
+ "version": "1.0.1699061"
94
94
  }
@@ -1,65 +0,0 @@
1
- ---
2
- name: devtools-ci
3
- description: >-
4
- Triage and analyze Chromium/DevTools LUCI build results and trybot failures,
5
- including high-level builder status, compile errors, and detailed test
6
- failures. Use this to inspect trybot buildbucket (bb) results for a patchset
7
- overview or to query ResultDB directly for comprehensive, actionable information
8
- about specific test regressions and infrastructure issues.
9
- ---
10
-
11
- # LUCI Build Triage & Analysis
12
-
13
- This skill provides access to specialized triage tools maintained within the
14
- `depot_tools` repository. These tools are used to inspect Buildbucket results
15
- and ResultDB test failures.
16
-
17
- ## Environment & Auth Discovery
18
-
19
- - **Environment**: Always execute these scripts using **`vpython3`** instead
20
- of `python3` to ensure all hermetic dependencies (like `httplib2`) are
21
- satisfied.
22
- - **Authentication**: Use `bb auth-info` to proactively check if you are
23
- logged into Buildbucket. If not, inform the user they must run `bb auth-login`.
24
-
25
- ## Locating the Tools
26
-
27
- The triage scripts and their detailed documentation are located relative to
28
- the `depot_tools` installation:
29
-
30
- 1. **Locate Depot Tools**: Run `which gerrit_client.py` to find your local
31
- `depot_tools` root.
32
- 2. **Navigation**:
33
- - **Buildbucket (`bb_wrapper.py`)**: Found in `<DEPOT_TOOLS_ROOT>/agents/skills/buildbucket/`
34
- - *Strengths*: High-level Build info (is it failing? what steps failed?),
35
- compile errors, and infra issues.
36
- - **Test Results (`luci_triage.py`)**: Found in `<DEPOT_TOOLS_ROOT>/agents/skills/luci-test-results/`
37
- - *Strengths*: Detailed Test info (specific test case failures in
38
- ResultDB).
39
-
40
- ## Troubleshooting
41
-
42
- - **"No Artifacts Found"**: This usually occurs if:
43
- 1. The build failed so early (e.g., bot setup) that no artifacts were produced.
44
- 2. The build is too old and logs have been purged.
45
- 3. There is an authentication issue.
46
- - **Fallback**: Try `bb log <build_id> <step_name>` to see the raw output
47
- for the failed step.
48
-
49
- ## Available Skills in Depot Tools
50
-
51
- Once you have located the `depot_tools` directory, read the following files
52
- to understand how to use each tool:
53
-
54
- - **`buildbucket`**: Used for high-level builder status and compile failures.
55
- - Read: `<DEPOT_TOOLS_ROOT>/agents/skills/buildbucket/SKILL.md`
56
- - **`luci-test-results`**: Used for detailed ResultDB test failure analysis.
57
- - Read: `<DEPOT_TOOLS_ROOT>/agents/skills/luci-test-results/SKILL.md`
58
-
59
- ## Usage at Runtime
60
-
61
- When you need to triage a build:
62
- 1. Locate `depot_tools` as described above.
63
- 2. Read the relevant `SKILL.md` file from `depot_tools`.
64
- 3. Follow the instructions in that file to execute the Python scripts (e.g.,
65
- `bb_wrapper.py` or `luci_triage.py`) using their absolute paths.
@@ -1,143 +0,0 @@
1
- ---
2
- name: evaluate-ai-css-completion
3
- description: Expose a temporary evaluation hook in DevTools and run a Puppeteer script to validate CSS code completion trigger rates.
4
- ---
5
-
6
- # Evaluate CSS Completion Skill
7
-
8
- This skill allows you to temporarily expose an evaluation hook in DevTools to measure CSS code completion trigger rates using a Puppeteer script.
9
-
10
- > [!WARNING]
11
- > **NEVER commit the `devtools_app.ts` patch.** It is only for local evaluation. Always revert it before uploading your CL.
12
-
13
- ## Step 1: Apply the Temporary Patch
14
-
15
- Modify `front_end/entrypoints/devtools_app/devtools_app.ts` to expose the global `testCssCompletion` hook.
16
-
17
- Add the following code at the end of `front_end/entrypoints/devtools_app/devtools_app.ts`:
18
-
19
- ```typescript
20
- // --- TEMPORARY EVALUATION HOOK ---
21
- // TEMPORARY PATCH - REMOVE BEFORE COMMIT
22
-
23
- import * as Host from '../../core/host/host.js';
24
- import * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
25
-
26
- (self as any).testCss = {
27
- getCases() {
28
- return [
29
- {
30
- name: 'Generic CSS Test Case',
31
- url: null,
32
- prefix: 'h1 { font-s',
33
- suffix: ' }',
34
- },
35
- // Add more test cases here
36
- ];
37
- },
38
-
39
- async evaluate(uiSourceCodeUrl: string | null, prefix: string, suffix: string, additionalFiles?: any[]) {
40
- const aidaClient = new Host.AidaClient.AidaClient();
41
- const completion = new AiCodeCompletion.AiCodeCompletion.AiCodeCompletion(
42
- {aidaClient},
43
- AiCodeCompletion.AiCodeCompletion.ContextFlavor.STYLES
44
- );
45
-
46
- const formattedAdditionalFiles = additionalFiles?.map(f => ({
47
- path: f.path || f.name,
48
- content: f.content || f.text,
49
- included_reason: f.included_reason ?? Host.AidaClient.Reason.RELATED_FILE,
50
- }));
51
-
52
- const result = await completion.completeCode(
53
- prefix,
54
- suffix,
55
- prefix.length,
56
- Host.AidaClient.AidaInferenceLanguage.CSS,
57
- formattedAdditionalFiles
58
- );
59
-
60
- return {
61
- hasSuggestion: result.response !== null && result.response.generatedSamples.length > 0,
62
- suggestions: result.response?.generatedSamples.map(s => s.generationString) ?? [],
63
- injectedFiles: formattedAdditionalFiles?.map(f => f.path) ?? [],
64
- };
65
- }
66
- };
67
- // ----------------------------------
68
- ```
69
-
70
- ## Step 2: Launch Chrome with Local DevTools
71
-
72
- Use the `npm start` script to build DevTools, launch Chrome Canary with remote debugging, and disable watch mode (so we build manually instead).
73
-
74
- You must specify a persistent user data directory so you can log in once and reuse the session:
75
-
76
- ```bash
77
- npm start -- --no-watch --browser=canary --remote-debugging-port=9222 --user-data-dir=/tmp/devtools-ai-evaluate-css-completion
78
- ```
79
-
80
- > [!IMPORTANT]
81
- > **Instructions for the Agent:**
82
- > After launching Chrome, you MUST print the following checklist to the user and wait for their explicit confirmation before running the evaluation script:
83
- > 1. Sign in to Chrome with your corporate account in the new window.
84
- > 2. Open DevTools, go to **Settings** (gear icon) > **AI Innovations**, and ensure **Code Completions** is enabled.
85
- > 3. Focus the DevTools window and press `Cmd + Option + I` (on macOS) or `Ctrl + Shift + I` (on Windows/Linux) to open DevTools-on-DevTools (inspecting DevTools itself). Go to the Network tab of the second DevTools instance and check the **Disable cache** checkbox. This ensures your local builds are loaded instead of cached versions when reloaded.
86
- > 4. Ask the user to reply when they are ready.
87
-
88
- ## Step 3: Configure Test Cases
89
-
90
- Before running the evaluation, configure your test cases directly in `front_end/entrypoints/devtools_app/devtools_app.ts` inside the `getCases()` method of the patch you applied in Step 1.
91
-
92
- After modifying the test cases, manually run a build to compile the changes:
93
- ```bash
94
- autoninja -C out/Default
95
- ```
96
-
97
- For example, to test spacing sensitivity, update the returned array:
98
- ```typescript
99
- getCases() {
100
- return [
101
- {
102
- name: 'With CSS Context',
103
- url: null,
104
- prefix: 'h1 { font-s',
105
- suffix: ' }',
106
- additionalFiles: [
107
- {
108
- path: 'other.css',
109
- content: 'body { color: red; }',
110
- }
111
- ]
112
- }
113
- ];
114
- }
115
- ```
116
-
117
- ## Step 4: Run the Evaluation Script
118
-
119
- Run the Puppeteer script to execute the evaluation:
120
-
121
- ```bash
122
- node .agents/skills/evaluate-ai-css-completion/scripts/evaluate.js
123
- ```
124
-
125
- The script will connect to the DevTools instance, trigger completions, and output the results.
126
-
127
- ### Debugging and Diagnostics
128
-
129
- If you get 0% trigger rate or unexpected errors, check the following:
130
-
131
- 1. **Browser Logs:** The evaluation script forwards browser console logs (prefixed with `BROWSER LOG:`). Look for these in your terminal.
132
- 2. **Host Config:** The script prints the DevTools `Host Config` at the start. Verify that `"devToolsAiCodeCompletionStyles": { "enabled": true }` is present.
133
- 3. **GCA/AIDA Logs:** The script automatically enables verbose AIDA logging. Look for `GCA request succeeded:` in the logs:
134
- * If the response has `"usageMetadata": { "candidatesTokenCount": X }` (where X > 0) but the `candidates` array is missing or empty, the backend generated tokens but they were filtered out (e.g., due to safety classifiers or recitation blocks).
135
- 4. **Context Requirement:** The CSS model is sensitive to context. A minimal prompt like `h1 { font-s` might not trigger AIDA. Ensure your test cases have enough context in the `prefix` (e.g., several preceding CSS rules) or pass them via `additionalFiles` to simulate a larger stylesheet.
136
-
137
- ## Step 5: Revert the Patch
138
-
139
- Once the evaluation is complete and you have recorded the results, revert the patch in `devtools_app.ts`:
140
-
141
- ```bash
142
- git checkout front_end/entrypoints/devtools_app/devtools_app.ts
143
- ```
@@ -1,104 +0,0 @@
1
- // Copyright 2026 The Chromium Authors
2
- // Use of this source code is governed by a BSD-style license that can be
3
- // found in the LICENSE file.
4
-
5
- /* eslint-disable no-console */
6
-
7
- const puppeteer = require('puppeteer-core');
8
-
9
- async function runEvaluation() {
10
- console.log('Connecting to Chrome...');
11
- const browser = await puppeteer.connect({browserURL: 'http://127.0.0.1:9222', defaultViewport: null});
12
-
13
- try {
14
- console.log('Opening test page (example.com)...');
15
- const testPage = await browser.newPage();
16
- await testPage.goto('https://example.com');
17
-
18
- console.log('Waiting for DevTools target...');
19
- const devtoolsTarget = await browser.waitForTarget(t => {
20
- return t.url().startsWith('devtools://') || t.url().includes('inspector.html');
21
- }, {timeout: 10000});
22
-
23
- console.log('Connecting to DevTools page...');
24
- const devtoolsPage = await devtoolsTarget.page();
25
- if (!devtoolsPage) {
26
- throw new Error('Could not get DevTools page object. Make sure DevTools is undocked or opened in a tab.');
27
- }
28
- console.log('DevTools URL:', devtoolsPage.url());
29
-
30
- devtoolsPage.on('console', msg => console.log('BROWSER LOG:', msg.text()));
31
-
32
- console.log('Reloading DevTools to ensure patch is loaded...');
33
- try {
34
- await devtoolsPage.setCacheEnabled(false);
35
- await devtoolsPage.reload({waitUntil: 'domcontentloaded', timeout: 10000});
36
- } catch (e) {
37
- console.log('Reload warning (proceeding anyway):', e.message);
38
- }
39
-
40
- await devtoolsPage.evaluate(() => localStorage.setItem('debugAiCodeCompletionEnabled', 'true'));
41
- await devtoolsPage.evaluate(() => localStorage.setItem('debugAiServicesEnabled', 'true'));
42
- await devtoolsPage.evaluate(() => localStorage.setItem('debugAiAssistancePanelEnabled', 'true'));
43
- await devtoolsPage.evaluate(() => localStorage.setItem('aiAssistanceStructuredLogEnabled', 'true'));
44
-
45
- console.log('Waiting for DevTools to initialize and hook to be available...');
46
- try {
47
- await devtoolsPage.waitForFunction(() => typeof self.testCss?.getCases === 'function', {timeout: 15000});
48
- } catch {
49
- throw new Error('testCss.getCases hook not found. Did you apply the patch and build? Is this the local build?');
50
- }
51
-
52
- console.log('Fetching test cases from DevTools...');
53
- const hostConfig = await devtoolsPage.evaluate(() => self.testCss.getHostConfig());
54
- console.log('Host Config:', JSON.stringify(hostConfig, null, 2));
55
- const testCases = await devtoolsPage.evaluate(() => self.testCss.getCases());
56
- console.log(`Found ${testCases.length} test cases. Running each case 5 times.`);
57
-
58
- let totalQueries = 0;
59
- for (const tc of testCases) {
60
- console.log(`\nRunning: ${tc.name}`);
61
- let successCount = 0;
62
- const suggestions = new Set();
63
-
64
- for (let run = 1; run <= 5; run++) {
65
- // Delay to avoid rate limiting
66
- await new Promise(resolve => setTimeout(resolve, 1200));
67
- try {
68
- const result = await devtoolsPage.evaluate(async (url, prefix, suffix, additionalFiles) => {
69
- return await self.testCss.evaluate(url, prefix, suffix, additionalFiles);
70
- }, tc.url, tc.prefix, tc.suffix, tc.additionalFiles);
71
-
72
- if (result.hasSuggestion) {
73
- successCount++;
74
- for (const s of result.suggestions) {
75
- suggestions.add(s);
76
- }
77
- }
78
- } catch (e) {
79
- console.error(` [Run ${run}] Failed:`, e.message);
80
- }
81
-
82
- totalQueries++;
83
- if (totalQueries % 20 === 0) {
84
- console.log('[INFO] Pausing for 8 seconds to prevent rate limiting...');
85
- await new Promise(resolve => setTimeout(resolve, 8000));
86
- }
87
- }
88
-
89
- console.log(` Result: [Success Rate: ${successCount}/5]`);
90
- if (suggestions.size > 0) {
91
- console.log(' Unique Suggestions:', Array.from(suggestions));
92
- }
93
- }
94
-
95
- // Cleanup tabs
96
- await devtoolsPage.close();
97
- await testPage.close();
98
- } finally {
99
- console.log('Disconnecting from Chrome...');
100
- await browser.disconnect();
101
- }
102
- }
103
-
104
- runEvaluation().catch(console.error);
@@ -1,32 +0,0 @@
1
- ---
2
- name: fixing-skipped-tests
3
- description: Use this skill when unskipping a test that was previously skipped.
4
- ---
5
- # Fixing Skipped Tests
6
-
7
- This skill outlines the workflow for fixing skipped tests in the DevTools codebase, typically given a Chromium bug link (e.g., `crbug.com/1234567`).
8
-
9
- ## Workflow
10
-
11
- 1. **Extract the Bug ID**
12
- - The input is typically a bug link like `crbug.com/<bugid>`. Extract the `<bugid>` from the link.
13
-
14
- 2. **Create a Branch**
15
- - Create a new branch named `fixed-<bugid>`.
16
- - Ensure you use the instructions from the `devtools-version-control` skill to create and switch branches appropriately.
17
-
18
- 3. **Search for the Skipped Tests**
19
- - Search the `test/TestExpectations` file for the `<bugid>` to find skipped tests.
20
- - Tests are no longer skipped using `.skip` in the code, but rather by having a `[ Skip ]` expectation in `test/TestExpectations`.
21
-
22
- 4. **Iterative Fix Process**
23
- - **Step 1:** Remove the `[ Skip ]` expectation for *one* of the tests associated with the bug in `test/TestExpectations` (or change it to `[ Pass Failure ]` if you are investigating flakiness).
24
- - **Step 2:** Re-run the specific test using its exact test ID (e.g., `npm run test path/to/foo.test.ts:test_name`) using the guidelines from the `devtools-verification` skill. If you are dealing with flaky tests, you can use the `--repeat=x` flag (e.g. `npm run test -- --repeat=20 path/to/foo.test.ts:test_name`) to run the test multiple times to reproduce the flakiness. This flag works for both E2E and unit tests.
25
- - **Step 3:** Analyze the test failure and make the necessary fixes to the code or the test itself.
26
- - **Step 4:** Re-run the test to ensure the fix is correct.
27
- - **Step 5:** Remove the `[crbug.com/<bugid>]` prefix from the test name string in the code if it is present.
28
- - **Step 6:** Remove any comments directly above the test in the code that explain the reason why the test was skipped (e.g., `// TODO(crbug...): Flaky`).
29
- - **Step 7:** Apply the fix to the remaining tests, un-skipping them one by one or in batches by modifying `test/TestExpectations`, and verifying until all are passing.
30
-
31
- 5. **Verify Full Build**
32
- - Before finishing, run the full verification process (TypeScript checks, linters, full test run) as required by the repository best practices.