chrome-devtools-frontend 1.0.1674545 → 1.0.1675430

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 (45) hide show
  1. package/.agents/skills/devtools-ux-writing-refactor/SKILL.md +26 -9
  2. package/front_end/core/common/ParsedURL.ts +5 -1
  3. package/front_end/core/common/SettingRegistration.ts +29 -8
  4. package/front_end/core/common/Settings.ts +0 -11
  5. package/front_end/core/root/Runtime.ts +4 -0
  6. package/front_end/core/sdk/TargetManager.ts +18 -0
  7. package/front_end/entrypoints/main/MainImpl.ts +1 -0
  8. package/front_end/foundation/Universe.ts +44 -29
  9. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +15 -5
  10. package/front_end/models/emulation/DeviceModeModel.ts +60 -71
  11. package/front_end/models/emulation/EmulatedDevices.ts +18 -66
  12. package/front_end/models/persistence/AutomaticFileSystemManager.ts +2 -2
  13. package/front_end/models/persistence/IsolatedFileSystemManager.ts +20 -29
  14. package/front_end/models/workspace/FileManager.ts +13 -6
  15. package/front_end/panels/application/WebMCPView.ts +13 -5
  16. package/front_end/panels/application/components/AdsView.ts +24 -1
  17. package/front_end/panels/application/components/adsView.css +27 -21
  18. package/front_end/panels/autofill/AutofillView.ts +12 -12
  19. package/front_end/panels/autofill/autofill-meta.ts +2 -2
  20. package/front_end/panels/elements/ElementsTreeElement.ts +550 -327
  21. package/front_end/panels/elements/ElementsTreeOutline.ts +1 -1
  22. package/front_end/panels/js_timeline/js_timeline-meta.ts +8 -8
  23. package/front_end/panels/layer_viewer/LayerDetailsView.ts +30 -30
  24. package/front_end/panels/layer_viewer/LayerTreeOutline.ts +6 -6
  25. package/front_end/panels/layer_viewer/LayerViewHost.ts +1 -1
  26. package/front_end/panels/layer_viewer/Layers3DView.ts +11 -11
  27. package/front_end/panels/layer_viewer/PaintProfilerView.ts +8 -8
  28. package/front_end/panels/layer_viewer/TransformController.ts +3 -3
  29. package/front_end/panels/layer_viewer/layer_viewer-meta.ts +11 -11
  30. package/front_end/panels/mobile_throttling/ThrottlingManager.ts +1 -58
  31. package/front_end/panels/mobile_throttling/mobile_throttling.ts +0 -10
  32. package/front_end/panels/protocol_monitor/ProtocolMonitor.ts +7 -0
  33. package/front_end/panels/settings/emulation/DevicesSettingsTab.ts +1 -2
  34. package/front_end/third_party/chromium/README.chromium +1 -1
  35. package/front_end/ui/comments/CommentAnchorResolver.ts +132 -0
  36. package/front_end/ui/comments/comments.ts +9 -0
  37. package/front_end/ui/legacy/components/data_grid/DataGridElement.ts +40 -0
  38. package/front_end/ui/legacy/components/utils/Linkifier.ts +1 -0
  39. package/front_end/ui/settings/SettingUIRegistration.ts +4 -0
  40. package/front_end/ui/visual_logging/Debugging.ts +1 -5
  41. package/front_end/ui/visual_logging/KnownContextValues.ts +1 -0
  42. package/front_end/ui/visual_logging/LoggingConfig.ts +21 -0
  43. package/front_end/ui/visual_logging/visual_logging.ts +9 -0
  44. package/package.json +1 -1
  45. package/front_end/panels/mobile_throttling/MobileThrottlingSelector.ts +0 -92
@@ -0,0 +1,132 @@
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
+ import * as VisualLogging from '../../ui/visual_logging/visual_logging.js';
6
+
7
+ export interface CommentAnchorSignature {
8
+ /** Visual logging tree path, e.g. "Panel: elements > Pane: styles > TreeOutline > TreeItem: color" */
9
+ vePath: string;
10
+ /** Normalized text content of the target node */
11
+ textSignature: string;
12
+ /** Text content of the parent container VE node for sibling disambiguation */
13
+ parentTextSignature?: string;
14
+ /** 0-indexed position among siblings sharing the same visual logging path and text signature */
15
+ siblingIndex?: number;
16
+ /** Optional backend RequestId for Network panel elements (`data-network-request-id`) */
17
+ networkRequestId?: string;
18
+ /** Optional backend NodeId for Elements panel DOM nodes (`data-backend-node-id`) */
19
+ backendNodeId?: number;
20
+ /** Optional 1-indexed line number for CodeMirror text editors */
21
+ editorLineNumber?: number;
22
+ /** Optional file path of the document displayed in the CodeMirror editor */
23
+ editorFilePath?: string;
24
+ }
25
+
26
+ export interface CommentThread {
27
+ id: string;
28
+ anchor: CommentAnchorSignature;
29
+ comments: Array<{
30
+ author: 'DEVELOPER' | 'AGENT',
31
+ text: string,
32
+ timestamp: number,
33
+ }>;
34
+ status: 'ACTIVE'|'RESOLVED';
35
+ changes?: Array<Record<string, unknown>>;
36
+ }
37
+
38
+ const IGNORED_MINOR_CONTROLS = new Set<number>([
39
+ VisualLogging.VisualElements.Action,
40
+ VisualLogging.VisualElements.Toggle,
41
+ VisualLogging.VisualElements.Close,
42
+ VisualLogging.VisualElements.Expand,
43
+ VisualLogging.VisualElements.ToggleSubpane,
44
+ VisualLogging.VisualElements.Toolbar,
45
+ ]);
46
+
47
+ export function closestAcrossShadow(element: Element, selector: string): Element|null {
48
+ let current: Element|null = element;
49
+ while (current) {
50
+ if (current.matches(selector)) {
51
+ return current;
52
+ }
53
+ current = current.parentElementOrShadowHost();
54
+ }
55
+ return null;
56
+ }
57
+
58
+ export function isNonEmptyItem(element: Element): boolean {
59
+ return element.deepTextContent().trim().length > 0;
60
+ }
61
+
62
+ export function isTabTitle(element: Element): boolean {
63
+ let current: Element|null = element;
64
+ while (current) {
65
+ if (VisualLogging.needsLogging(current)) {
66
+ try {
67
+ const config = VisualLogging.getLoggingConfig(current);
68
+ if (config.ve === VisualLogging.VisualElements.PanelTabHeader) {
69
+ return true;
70
+ }
71
+ } catch {
72
+ // Ignore
73
+ }
74
+ }
75
+ const role = current.getAttribute('role');
76
+ if (role === 'tab') {
77
+ return true;
78
+ }
79
+ if (current.classList.contains('tab-element') || current.classList.contains('tab-header')) {
80
+ return true;
81
+ }
82
+ current = current.parentElementOrShadowHost();
83
+ }
84
+ return false;
85
+ }
86
+
87
+ export function resolveCommentAnchorElement(element: Element): Element|null {
88
+ if (isTabTitle(element)) {
89
+ return null;
90
+ }
91
+ let target: Element|null = element;
92
+ let fallbackCandidate: Element|null = null;
93
+
94
+ while (target) {
95
+ const hasDomainId = target.hasAttribute('data-network-request-id') || target.hasAttribute('data-backend-node-id');
96
+
97
+ if (hasDomainId) {
98
+ return isNonEmptyItem(target) ? target : null;
99
+ }
100
+ if (VisualLogging.needsLogging(target)) {
101
+ try {
102
+ const config = VisualLogging.getLoggingConfig(target);
103
+ if (config.ve === VisualLogging.VisualElements.TableRow ||
104
+ config.ve === VisualLogging.VisualElements.TreeItem) {
105
+ return isNonEmptyItem(target) ? target : null;
106
+ }
107
+ if (!fallbackCandidate && !IGNORED_MINOR_CONTROLS.has(config.ve)) {
108
+ fallbackCandidate = target;
109
+ }
110
+ } catch {
111
+ // Ignore
112
+ }
113
+ }
114
+ target = target.parentElementOrShadowHost();
115
+ }
116
+
117
+ if (fallbackCandidate && isNonEmptyItem(fallbackCandidate)) {
118
+ return fallbackCandidate;
119
+ }
120
+ return null;
121
+ }
122
+
123
+ export function isElementVisible(element: Element): boolean {
124
+ if (!element.isConnected) {
125
+ return false;
126
+ }
127
+ if (!element.checkVisibility({checkOpacity: true, checkVisibilityCSS: true, contentVisibilityAuto: true})) {
128
+ return false;
129
+ }
130
+ const rect = element.getBoundingClientRect();
131
+ return rect.width > 0 && rect.height > 0;
132
+ }
@@ -0,0 +1,9 @@
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
+ import * as CommentAnchorResolver from './CommentAnchorResolver.js';
6
+
7
+ export {
8
+ CommentAnchorResolver,
9
+ };
@@ -3,6 +3,7 @@
3
3
  // found in the LICENSE file.
4
4
  /* eslint-disable @devtools/no-imperative-dom-api */
5
5
 
6
+ import type * as Common from '../../../../core/common/common.js';
6
7
  import type * as Platform from '../../../../core/platform/platform.js';
7
8
  import type * as TextUtils from '../../../../core/text_utils/text_utils.js';
8
9
  import * as Lit from '../../../lit/lit.js';
@@ -79,6 +80,39 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
79
80
  #hiddenColumns = new Set<string>();
80
81
  #usedCreationNode: DataGridElementNode|null = null;
81
82
  #sortingChangedScheduled = false;
83
+ #columnsVisibilitySetting?: Common.Settings.Setting<Record<string, {visible: boolean}>>;
84
+
85
+ set columnsVisibilitySetting(setting: Common.Settings.Setting<Record<string, {visible: boolean}>>) {
86
+ this.#columnsVisibilitySetting = setting;
87
+ this.#applyColumnVisibilitySetting();
88
+ }
89
+
90
+ get columnsVisibilitySetting(): Common.Settings.Setting<Record<string, {visible: boolean}>>|undefined {
91
+ return this.#columnsVisibilitySetting;
92
+ }
93
+
94
+ #applyColumnVisibilitySetting(): void {
95
+ if (!this.#columnsVisibilitySetting) {
96
+ return;
97
+ }
98
+ const settingValue = this.#columnsVisibilitySetting.get();
99
+ let changed = false;
100
+ for (const column of this.#columns) {
101
+ if (this.#hideableColumns.has(column.id) && settingValue[column.id]?.visible === false) {
102
+ if (!this.#hiddenColumns.has(column.id)) {
103
+ this.#hiddenColumns.add(column.id);
104
+ changed = true;
105
+ }
106
+ } else if (this.#hiddenColumns.has(column.id)) {
107
+ this.#hiddenColumns.delete(column.id);
108
+ changed = true;
109
+ }
110
+ }
111
+ if (changed) {
112
+ const visibleColumns = new Set(this.#columns.map(({id}) => id).filter(id => !this.#hiddenColumns.has(id)));
113
+ this.#dataGrid.setColumnsVisibility(visibleColumns);
114
+ }
115
+ }
82
116
 
83
117
  constructor() {
84
118
  super();
@@ -133,6 +167,11 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
133
167
  }
134
168
  this.#dataGrid.setColumnsVisibility(
135
169
  new Set(this.#columns.map(({id}) => id).filter(column => !this.#hiddenColumns.has(column))));
170
+ if (this.#columnsVisibilitySetting) {
171
+ const settingValue = this.#columnsVisibilitySetting.get();
172
+ settingValue[column.id] = {visible: !this.#hiddenColumns.has(column.id)};
173
+ this.#columnsVisibilitySetting.set(settingValue);
174
+ }
136
175
  }, {checked: !this.#hiddenColumns.has(column.id)});
137
176
  }
138
177
  }
@@ -320,6 +359,7 @@ export class DataGridElement extends UI.UIUtils.HTMLElementWithLightDOMTemplate
320
359
  const visibleColumns = new Set(this.#columns.map(({id}) => id).filter(id => !this.#hiddenColumns.has(id)));
321
360
  this.#dataGrid.setColumnsVisibility(visibleColumns);
322
361
  this.#dataGrid.setEditCallback(hasEditableColumn ? this.#editCallback.bind(this) : undefined, INTERNAL_TOKEN);
362
+ this.#applyColumnVisibilitySetting();
323
363
  }
324
364
 
325
365
  #needUpdateColumns(mutationList: MutationRecord[]): boolean {
@@ -709,6 +709,7 @@ export class Linkifier extends Common.ObjectWrapper.ObjectWrapper<EventTypes> im
709
709
  if (text instanceof HTMLElement) {
710
710
  link.appendChild(text);
711
711
  } else if (bypassURLTrimming) {
712
+ link.removeChildren();
712
713
  link.classList.add('devtools-link-styled-trim');
713
714
  Linkifier.appendTextWithoutHashes(link, text);
714
715
  } else {
@@ -51,6 +51,7 @@ export function register(
51
51
  if (registeredSettings.has(settingName)) {
52
52
  throw new Error(`Duplicate setting name '${settingName}'`);
53
53
  }
54
+ Common.SettingRegistration.registerCategoryOrder(settingUIDescriptor.category, settingUIDescriptor.order);
54
55
  registeredSettings.set(settingName, {descriptor: settingDescriptor, uiDescriptor: settingUIDescriptor});
55
56
  }
56
57
 
@@ -98,5 +99,8 @@ export function resolve(settingDescriptor: Common.Settings.SettingDescriptor<unk
98
99
  }
99
100
 
100
101
  export function resetSettings(): void {
102
+ for (const {uiDescriptor} of registeredSettings.values()) {
103
+ Common.SettingRegistration.removeCategoryOrder(uiDescriptor.category, uiDescriptor.order);
104
+ }
101
105
  registeredSettings.clear();
102
106
  }
@@ -6,7 +6,7 @@
6
6
  import {assertNotNullOrUndefined} from '../../core/platform/platform.js';
7
7
 
8
8
  import type {Loggable} from './Loggable.js';
9
- import {type LoggingConfig, VisualElements} from './LoggingConfig.js';
9
+ import {elementKey, type LoggingConfig, VisualElements} from './LoggingConfig.js';
10
10
  import {getLoggingState, type LoggingState} from './LoggingState.js';
11
11
 
12
12
  let veDebuggingEnabled = false;
@@ -406,10 +406,6 @@ function processImpressionsForAdHocAnalysisDebugLog(states: LoggingState[]): voi
406
406
  }
407
407
  }
408
408
 
409
- function elementKey(config: LoggingConfig): string {
410
- return `${VisualElements[config.ve]}${config.context ? `: ${config.context}` : ''}`;
411
- }
412
-
413
409
  export function debugString(config: LoggingConfig): string {
414
410
  const components = [VisualElements[config.ve]];
415
411
  if (config.context) {
@@ -3238,6 +3238,7 @@ export const knownContextValues = new Set([
3238
3238
  'protocol',
3239
3239
  'protocol-handlers',
3240
3240
  'protocol-monitor',
3241
+ 'protocol-monitor-columns',
3241
3242
  'protocol-monitor-documentation',
3242
3243
  'protocol-monitor.add-custom-property',
3243
3244
  'protocol-monitor.add-parameter',
@@ -27,6 +27,27 @@ export interface LoggingConfig {
27
27
  parent?: string;
28
28
  }
29
29
 
30
+ export function elementKey(config: LoggingConfig): string {
31
+ return `${VisualElements[config.ve]}${config.context ? `: ${config.context}` : ''}`;
32
+ }
33
+
34
+ export function getVePath(element: Element): string {
35
+ const parts: string[] = [];
36
+ let current: Element|null = element;
37
+ while (current) {
38
+ if (needsLogging(current)) {
39
+ try {
40
+ const config = getLoggingConfig(current);
41
+ parts.unshift(elementKey(config));
42
+ } catch {
43
+ // Skip invalid logging configs
44
+ }
45
+ }
46
+ current = current.parentElementOrShadowHost();
47
+ }
48
+ return parts.join(' > ');
49
+ }
50
+
30
51
  export function needsLogging(element: Element): boolean {
31
52
  return element.hasAttribute(LOGGING_ATTRIBUTE);
32
53
  }
@@ -11,6 +11,15 @@ import * as NonDomState from './NonDomState.js';
11
11
 
12
12
  export type Loggable = LoggableModule.Loggable;
13
13
  export {DebugLoggingFormat, setVeDebuggingEnabled, setVeDebugLoggingEnabled} from './Debugging.js';
14
+ export {
15
+ elementKey,
16
+ getLoggingConfig,
17
+ getVePath,
18
+ type LoggingConfig,
19
+ needsLogging,
20
+ parseJsLog,
21
+ VisualElements,
22
+ } from './LoggingConfig.js';
14
23
  export {addDocument, startLogging, stopLogging} from './LoggingDriver.js';
15
24
  export {logImpressions, logSettingAccess, logFunctionCall} from './LoggingEvents.js';
16
25
  export const logClick = (loggable: Loggable, event: Event, options: {doubleClick?: boolean} = {}): void =>
package/package.json CHANGED
@@ -92,5 +92,5 @@
92
92
  "webidl2": "24.5.0",
93
93
  "yargs": "17.7.2"
94
94
  },
95
- "version": "1.0.1674545"
95
+ "version": "1.0.1675430"
96
96
  }
@@ -1,92 +0,0 @@
1
- // Copyright 2017 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
- import * as i18n from '../../core/i18n/i18n.js';
6
- import * as SDK from '../../core/sdk/sdk.js';
7
-
8
- import {throttlingManager} from './ThrottlingManager.js';
9
- import {
10
- ThrottlingPresets,
11
- type Conditions,
12
- type ConditionsList,
13
- type MobileThrottlingConditionsGroup,
14
- } from './ThrottlingPresets.js';
15
-
16
- const UIStrings = {
17
- /**
18
- * @description Mobile throttling is disabled. The user can select this option to run mobile
19
- * emulation at a normal speed instead of throttled.
20
- */
21
- disabled: 'Disabled',
22
- /**
23
- * @description Title for a group of pre-decided configuration options for mobile throttling. These
24
- * are useful default options that users might want.
25
- */
26
- presets: 'Presets',
27
- /**
28
- * @description Title for a group of advanced configuration options for mobile throttling, which
29
- * might not be applicable to every user or situation.
30
- */
31
- advanced: 'Advanced',
32
- } as const;
33
- const str_ = i18n.i18n.registerUIStrings('panels/mobile_throttling/MobileThrottlingSelector.ts', UIStrings);
34
- const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
35
-
36
- export class MobileThrottlingSelector {
37
- private readonly populateCallback: (arg0: MobileThrottlingConditionsGroup[]) => ConditionsList;
38
- private readonly selectCallback: (arg0: number) => void;
39
- private readonly options: ConditionsList;
40
-
41
- constructor(
42
- populateCallback: (arg0: MobileThrottlingConditionsGroup[]) => ConditionsList,
43
- selectCallback: (arg0: number) => void) {
44
- this.populateCallback = populateCallback;
45
- this.selectCallback = selectCallback;
46
- SDK.CPUThrottlingManager.CPUThrottlingManager.instance().addEventListener(
47
- SDK.CPUThrottlingManager.Events.RATE_CHANGED, this.conditionsChanged, this);
48
- SDK.NetworkManager.MultitargetNetworkManager.instance().addEventListener(
49
- SDK.NetworkManager.MultitargetNetworkManager.Events.CONDITIONS_CHANGED, this.conditionsChanged, this);
50
- this.options = this.populateOptions();
51
- this.conditionsChanged();
52
- }
53
-
54
- optionSelected(conditions: Conditions): void {
55
- SDK.NetworkManager.MultitargetNetworkManager.instance().setNetworkConditions(conditions.network);
56
- throttlingManager().setCPUThrottlingOption(conditions.cpuThrottlingOption);
57
- }
58
-
59
- private populateOptions(): ConditionsList {
60
- const disabledGroup = {
61
- title: i18nString(UIStrings.disabled),
62
- items: [ThrottlingPresets.getNoThrottlingConditions()],
63
- };
64
- const presetsGroup = {title: i18nString(UIStrings.presets), items: ThrottlingPresets.getMobilePresets()};
65
- const advancedGroup = {title: i18nString(UIStrings.advanced), items: ThrottlingPresets.getAdvancedMobilePresets()};
66
- return this.populateCallback([disabledGroup, presetsGroup, advancedGroup]);
67
- }
68
-
69
- private conditionsChanged(): void {
70
- this.populateOptions();
71
-
72
- const networkConditions = SDK.NetworkManager.MultitargetNetworkManager.instance().networkConditions();
73
- const cpuThrottlingOption = throttlingManager().cpuThrottlingOption();
74
- for (let index = 0; index < this.options.length; ++index) {
75
- const option = this.options[index];
76
- if (option && 'network' in option && option.network === networkConditions &&
77
- option.cpuThrottlingOption === cpuThrottlingOption) {
78
- this.selectCallback(index);
79
- return;
80
- }
81
- }
82
-
83
- const customConditions = ThrottlingPresets.getCustomConditions();
84
- for (let index = 0; index < this.options.length; ++index) {
85
- const item = this.options[index];
86
- if (item && item.title === customConditions.title && item.description === customConditions.description) {
87
- this.selectCallback(index);
88
- return;
89
- }
90
- }
91
- }
92
- }