chrome-devtools-frontend 1.0.1666631 → 1.0.1667564

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 (78) hide show
  1. package/front_end/core/host/AidaClient.ts +6 -4
  2. package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
  3. package/front_end/core/host/InspectorFrontendHostStub.ts +3 -3
  4. package/front_end/core/host/UserMetrics.ts +49 -1
  5. package/front_end/core/sdk/CSSMatchedStyles.ts +2 -5
  6. package/front_end/core/sdk/CSSPropertyParserMatchers.ts +12 -0
  7. package/front_end/core/sdk/Connections.ts +1 -1
  8. package/front_end/core/sdk/DOMModel.test.api.ts +104 -0
  9. package/front_end/core/sdk/DOMModel.ts +23 -11
  10. package/front_end/core/sdk/DebuggerModel.ts +9 -9
  11. package/front_end/core/sdk/IsolateManager.ts +1 -1
  12. package/front_end/core/sdk/NetworkManager.ts +105 -4
  13. package/front_end/core/sdk/OverlayModel.ts +15 -14
  14. package/front_end/core/sdk/RemoteObject.ts +1 -1
  15. package/front_end/devtools_compatibility.js +1 -0
  16. package/front_end/entrypoints/main/MainImpl.ts +5 -2
  17. package/front_end/entrypoints/main/SimpleApp.ts +9 -3
  18. package/front_end/foundation/Universe.ts +6 -0
  19. package/front_end/models/ai_assistance/contexts/PerformanceTraceContext.ts +23 -10
  20. package/front_end/models/computed_style/ComputedStyleModel.ts +4 -7
  21. package/front_end/models/crux-manager/CrUXManager.ts +4 -0
  22. package/front_end/models/extensions/ExtensionAPI.ts +3 -3
  23. package/front_end/models/heap_snapshot/HeapSnapshotProxy.ts +2 -2
  24. package/front_end/models/live-metrics/LiveMetrics.ts +59 -19
  25. package/front_end/models/live-metrics/web-vitals-injected/README.md +1 -1
  26. package/front_end/models/live-metrics/web-vitals-injected/spec/spec.ts +6 -1
  27. package/front_end/models/live-metrics/web-vitals-injected/web-vitals-injected.ts +11 -3
  28. package/front_end/models/logs/LogManager.ts +1 -1
  29. package/front_end/models/persistence/FileSystemWorkspaceBinding.ts +1 -1
  30. package/front_end/models/source_map_scopes/FunctionCodeResolver.ts +6 -7
  31. package/front_end/models/workspace_diff/WorkspaceDiff.ts +13 -14
  32. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +15 -7
  33. package/front_end/panels/application/BackgroundServiceView.ts +18 -20
  34. package/front_end/panels/autofill/AutofillView.ts +17 -17
  35. package/front_end/panels/autofill/autofillView.css +7 -8
  36. package/front_end/panels/browser_debugger/CategorizedBreakpointsSidebarPane.ts +31 -31
  37. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +15 -15
  38. package/front_end/panels/browser_debugger/XHRBreakpointsSidebarPane.ts +12 -12
  39. package/front_end/panels/browser_debugger/browser_debugger-meta.ts +27 -27
  40. package/front_end/panels/common/AiCodeCompletionDisclaimer.ts +14 -6
  41. package/front_end/panels/common/AiCodeCompletionSummaryToolbar.ts +14 -6
  42. package/front_end/panels/common/AiCodeCompletionTeaser.ts +13 -6
  43. package/front_end/panels/common/GeminiRebrandPromoDialog.ts +1 -1
  44. package/front_end/panels/console/ConsoleInsightTeaser.ts +13 -6
  45. package/front_end/panels/console/ConsoleView.ts +6 -6
  46. package/front_end/panels/console_counters/WarningErrorCounter.ts +4 -4
  47. package/front_end/panels/coverage/CoverageModel.ts +11 -1
  48. package/front_end/panels/elements/StylePropertiesSection.ts +50 -17
  49. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +18 -7
  50. package/front_end/panels/emulation/AdvancedApp.ts +18 -8
  51. package/front_end/panels/event_listeners/EventListenersView.ts +8 -8
  52. package/front_end/panels/explain/components/ConsoleInsight.ts +15 -7
  53. package/front_end/panels/network/NetworkLogView.ts +13 -14
  54. package/front_end/panels/screencast/ScreencastApp.ts +12 -6
  55. package/front_end/panels/screencast/ScreencastView.ts +37 -11
  56. package/front_end/panels/sensors/locationsSettingsTab.css +4 -0
  57. package/front_end/panels/settings/AISettingsTab.ts +13 -6
  58. package/front_end/panels/sources/DebuggerPlugin.ts +13 -2
  59. package/front_end/panels/timeline/components/LiveMetricsView.ts +14 -1
  60. package/front_end/panels/timeline/components/cpuThrottlingSelector.css +4 -0
  61. package/front_end/panels/timeline/components/liveMetricsView.css +28 -0
  62. package/front_end/panels/timeline/timeline-meta.ts +13 -0
  63. package/front_end/third_party/chromium/README.chromium +1 -1
  64. package/front_end/ui/components/menus/menu.css +4 -0
  65. package/front_end/ui/components/menus/menuItem.css +4 -0
  66. package/front_end/ui/components/menus/selectMenu.css +4 -0
  67. package/front_end/ui/components/menus/selectMenuButton.css +4 -0
  68. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +21 -10
  69. package/front_end/ui/components/text_editor/AiCodeGenerationProvider.ts +22 -10
  70. package/front_end/ui/components/text_editor/config.ts +7 -0
  71. package/front_end/ui/legacy/AppProvider.ts +2 -1
  72. package/front_end/ui/legacy/RootView.ts +8 -1
  73. package/front_end/ui/legacy/UniverseRequestEvent.ts +19 -0
  74. package/front_end/ui/legacy/Widget.ts +49 -6
  75. package/front_end/ui/legacy/inspectorCommon.css +1 -1
  76. package/front_end/ui/legacy/legacy.ts +2 -0
  77. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -0
  78. package/package.json +1 -1
@@ -5,6 +5,7 @@
5
5
  import '../../ui/components/spinners/spinners.js';
6
6
  import '../../ui/components/tooltips/tooltips.js';
7
7
 
8
+ import type * as Common from '../../core/common/common.js';
8
9
  import * as Host from '../../core/host/host.js';
9
10
  import * as i18n from '../../core/i18n/i18n.js';
10
11
  import * as Root from '../../core/root/root.js';
@@ -196,7 +197,8 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
196
197
  #panel?: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
197
198
 
198
199
  #aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
199
- #boundOnAidaAvailabilityChange: () => Promise<void>;
200
+ #boundOnAidaAvailabilityChange:
201
+ (ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>) => void;
200
202
 
201
203
  constructor(element?: HTMLElement, view: View = DEFAULT_SUMMARY_TOOLBAR_VIEW) {
202
204
  super(element);
@@ -249,14 +251,17 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
249
251
  this.requestUpdate();
250
252
  }
251
253
 
252
- async #onAidaAvailabilityChange(): Promise<void> {
253
- const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
254
- if (currentAidaAvailability !== this.#aidaAvailability) {
255
- this.#aidaAvailability = currentAidaAvailability;
254
+ #updateAidaAvailability(aidaAvailability: Host.AidaClient.AidaAccessPreconditions): void {
255
+ if (aidaAvailability !== this.#aidaAvailability) {
256
+ this.#aidaAvailability = aidaAvailability;
256
257
  this.requestUpdate();
257
258
  }
258
259
  }
259
260
 
261
+ #onAidaAvailabilityChange(ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>): void {
262
+ this.#updateAidaAvailability(ev.data);
263
+ }
264
+
260
265
  #onManageInSettingsTooltipClick(): void {
261
266
  this.#viewOutput.hideTooltip?.();
262
267
  void UI.ViewManager.ViewManager.instance().showView('chrome-ai');
@@ -279,7 +284,10 @@ export class AiCodeCompletionDisclaimer extends UI.Widget.Widget {
279
284
  super.wasShown();
280
285
  Host.AidaClient.HostConfigTracker.instance().addEventListener(
281
286
  Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
282
- void this.#onAidaAvailabilityChange();
287
+ const initialAvailability = Host.AidaClient.HostConfigTracker.instance().aidaAvailability;
288
+ if (initialAvailability !== undefined) {
289
+ this.#updateAidaAvailability(initialAvailability);
290
+ }
283
291
  }
284
292
 
285
293
  override willHide(): void {
@@ -6,6 +6,7 @@ import '../../ui/components/spinners/spinners.js';
6
6
  import '../../ui/components/tooltips/tooltips.js';
7
7
  import '../../ui/kit/kit.js';
8
8
 
9
+ import type * as Common from '../../core/common/common.js';
9
10
  import * as Host from '../../core/host/host.js';
10
11
  import * as i18n from '../../core/i18n/i18n.js';
11
12
  import type * as AiCodeCompletion from '../../models/ai_code_completion/ai_code_completion.js';
@@ -116,7 +117,8 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
116
117
  #panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
117
118
 
118
119
  #aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
119
- #boundOnAidaAvailabilityChange: () => Promise<void>;
120
+ #boundOnAidaAvailabilityChange:
121
+ (ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>) => void;
120
122
 
121
123
  constructor(props: AiCodeCompletionSummaryToolbarProps, view?: View) {
122
124
  super();
@@ -130,14 +132,17 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
130
132
  this.requestUpdate();
131
133
  }
132
134
 
133
- async #onAidaAvailabilityChange(): Promise<void> {
134
- const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
135
- if (currentAidaAvailability !== this.#aidaAvailability) {
136
- this.#aidaAvailability = currentAidaAvailability;
135
+ #updateAidaAvailability(aidaAvailability: Host.AidaClient.AidaAccessPreconditions): void {
136
+ if (aidaAvailability !== this.#aidaAvailability) {
137
+ this.#aidaAvailability = aidaAvailability;
137
138
  this.requestUpdate();
138
139
  }
139
140
  }
140
141
 
142
+ #onAidaAvailabilityChange(ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>): void {
143
+ this.#updateAidaAvailability(ev.data);
144
+ }
145
+
141
146
  setLoading(loading: boolean): void {
142
147
  this.#loading = loading;
143
148
  this.requestUpdate();
@@ -172,7 +177,10 @@ export class AiCodeCompletionSummaryToolbar extends UI.Widget.Widget {
172
177
  super.wasShown();
173
178
  Host.AidaClient.HostConfigTracker.instance().addEventListener(
174
179
  Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
175
- void this.#onAidaAvailabilityChange();
180
+ const initialAvailability = Host.AidaClient.HostConfigTracker.instance().aidaAvailability;
181
+ if (initialAvailability !== undefined) {
182
+ this.#updateAidaAvailability(initialAvailability);
183
+ }
176
184
  }
177
185
 
178
186
  override willHide(): void {
@@ -171,7 +171,8 @@ export class AiCodeCompletionTeaser extends UI.Widget.Widget {
171
171
  readonly #view: View;
172
172
 
173
173
  #aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
174
- #boundOnAidaAvailabilityChange: () => Promise<void>;
174
+ #boundOnAidaAvailabilityChange:
175
+ (ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>) => void;
175
176
  #boundOnAiCodeCompletionSettingChanged: () => void;
176
177
  #onDetach: () => void;
177
178
  #panel?: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
@@ -211,14 +212,17 @@ export class AiCodeCompletionTeaser extends UI.Widget.Widget {
211
212
  });
212
213
  }
213
214
 
214
- async #onAidaAvailabilityChange(): Promise<void> {
215
- const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
216
- if (currentAidaAvailability !== this.#aidaAvailability) {
217
- this.#aidaAvailability = currentAidaAvailability;
215
+ #updateAidaAvailability(aidaAvailability: Host.AidaClient.AidaAccessPreconditions): void {
216
+ if (aidaAvailability !== this.#aidaAvailability) {
217
+ this.#aidaAvailability = aidaAvailability;
218
218
  this.requestUpdate();
219
219
  }
220
220
  }
221
221
 
222
+ #onAidaAvailabilityChange(ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>): void {
223
+ this.#updateAidaAvailability(ev.data);
224
+ }
225
+
222
226
  #onAiCodeCompletionSettingChanged(): void {
223
227
  if (this.#aiCodeCompletionFreCompletedSetting.get() || this.#aiCodeCompletionTeaserDismissedSetting.get()) {
224
228
  this.detach();
@@ -322,7 +326,10 @@ export class AiCodeCompletionTeaser extends UI.Widget.Widget {
322
326
  Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
323
327
  this.#aiCodeCompletionFreCompletedSetting.addChangeListener(this.#boundOnAiCodeCompletionSettingChanged);
324
328
  this.#aiCodeCompletionTeaserDismissedSetting.addChangeListener(this.#boundOnAiCodeCompletionSettingChanged);
325
- void this.#onAidaAvailabilityChange();
329
+ const initialAvailability = Host.AidaClient.HostConfigTracker.instance().aidaAvailability;
330
+ if (initialAvailability !== undefined) {
331
+ this.#updateAidaAvailability(initialAvailability);
332
+ }
326
333
  }
327
334
 
328
335
  override willHide(): void {
@@ -177,7 +177,7 @@ export class GeminiRebrandPromoDialog extends UI.Widget.VBox {
177
177
  return;
178
178
  }
179
179
 
180
- const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
180
+ const currentAidaAvailability = Host.AidaClient.HostConfigTracker.instance().aidaAvailability;
181
181
  if (currentAidaAvailability !== Host.AidaClient.AidaAccessPreconditions.AVAILABLE) {
182
182
  return;
183
183
  }
@@ -409,7 +409,8 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
409
409
  #isSlow = false;
410
410
  #timeoutId: ReturnType<typeof setTimeout>|null = null;
411
411
  #aidaAvailability?: Host.AidaClient.AidaAccessPreconditions;
412
- #boundOnAidaAvailabilityChange: () => Promise<void>;
412
+ #boundOnAidaAvailabilityChange:
413
+ (ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>) => void;
413
414
  #boundOnDownloadProgressChange: (event: Common.EventTarget.EventTargetEvent<number>) => void;
414
415
  #boundOnSessionCreation: () => void;
415
416
  #downloadProgress: number|null = null;
@@ -448,14 +449,17 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
448
449
  return Common.Settings.Settings.instance().createLocalSetting('console-insights-onboarding-finished', true);
449
450
  }
450
451
 
451
- async #onAidaAvailabilityChange(): Promise<void> {
452
- const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
453
- if (currentAidaAvailability !== this.#aidaAvailability) {
454
- this.#aidaAvailability = currentAidaAvailability;
452
+ #updateAidaAvailability(aidaAvailability: Host.AidaClient.AidaAccessPreconditions): void {
453
+ if (aidaAvailability !== this.#aidaAvailability) {
454
+ this.#aidaAvailability = aidaAvailability;
455
455
  this.requestUpdate();
456
456
  }
457
457
  }
458
458
 
459
+ #onAidaAvailabilityChange(ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>): void {
460
+ this.#updateAidaAvailability(ev.data);
461
+ }
462
+
459
463
  #executeConsoleInsightAction(): void {
460
464
  UI.Context.Context.instance().setFlavor(ConsoleViewMessage, this.#consoleViewMessage);
461
465
  const action = UI.ActionRegistry.ActionRegistry.instance().getAction(EXPLAIN_TEASER_ACTION_ID);
@@ -731,7 +735,10 @@ export class ConsoleInsightTeaser extends UI.Widget.Widget {
731
735
  super.wasShown();
732
736
  Host.AidaClient.HostConfigTracker.instance().addEventListener(
733
737
  Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
734
- void this.#onAidaAvailabilityChange();
738
+ const initialAvailability = Host.AidaClient.HostConfigTracker.instance().aidaAvailability;
739
+ if (initialAvailability !== undefined) {
740
+ this.#updateAidaAvailability(initialAvailability);
741
+ }
735
742
  }
736
743
 
737
744
  override willHide(): void {
@@ -196,9 +196,9 @@ const UIStrings = {
196
196
  */
197
197
  copyVisibleStyledSelection: 'Copy visible styled selection',
198
198
  /**
199
- * @description Text to replay an XHR request.
199
+ * @description Text to resend a network request.
200
200
  */
201
- replayXhr: 'Replay XHR',
201
+ resend: 'Resend',
202
202
  /**
203
203
  * @description Text to indicate DevTools is writing to a file.
204
204
  */
@@ -1338,10 +1338,10 @@ export class ConsoleView extends UI.Widget.VBox implements
1338
1338
 
1339
1339
  if (consoleMessage) {
1340
1340
  const request = Logs.NetworkLog.NetworkLog.requestForConsoleMessage(consoleMessage);
1341
- if (request && SDK.NetworkManager.NetworkManager.canReplayRequest(request)) {
1342
- contextMenu.debugSection().appendItem(
1343
- i18nString(UIStrings.replayXhr), SDK.NetworkManager.NetworkManager.replayRequest.bind(null, request),
1344
- {jslogContext: 'replay-xhr'});
1341
+ if (request && SDK.NetworkManager.NetworkManager.canResendRequest(request)) {
1342
+ contextMenu.debugSection().appendItem(i18nString(UIStrings.resend),
1343
+ SDK.NetworkManager.NetworkManager.replayRequest.bind(null, request),
1344
+ {jslogContext: 'resend'});
1345
1345
  }
1346
1346
  }
1347
1347
 
@@ -19,20 +19,20 @@ const {html, nothing, render} = Lit;
19
19
 
20
20
  const UIStrings = {
21
21
  /**
22
- * @description The console error count in the Warning Error Counter shown in the main toolbar (top-left in DevTools). The error count refers to the number of errors currently present in the JavaScript console.
22
+ * @description The console error count in the Warning Error Counter shown in the main toolbar (top-left in DevTools). The error count refers to the number of errors currently present in the Console.
23
23
  */
24
24
  sErrors: '{n, plural, =1 {# error} other {# errors}}',
25
25
  /**
26
- * @description The console warning count in the Warning Error Counter shown in the main toolbar (top-left in DevTools). The warning count refers to the number of warnings currently present in the JavaScript console.
26
+ * @description The console warning count in the Warning Error Counter shown in the main toolbar (top-left in DevTools). The warning count refers to the number of warnings currently present in the Console.
27
27
  */
28
28
  sWarnings: '{n, plural, =1 {# warning} other {# warnings}}',
29
29
  /**
30
- * @description Tooltip shown for a main toolbar button that opens the Console panel
30
+ * @description Tooltip shown for a main toolbar button that opens the Console panel.
31
31
  * @example {2 errors, 1 warning} PH1
32
32
  */
33
33
  openConsoleToViewS: 'Open Console to view {PH1}',
34
34
  /**
35
- * @description Title for the issues count in the Issues Error Counter shown in the main toolbar (top-left in DevTools). The issues count refers to the number of issues in the issues tab.
35
+ * @description Title for the issues count in the Issues Error Counter shown in the main toolbar (top-left in DevTools). The issues count refers to the number of issues in the Issues tab.
36
36
  */
37
37
  openIssuesToView: '{n, plural, =1 {Open Issues to view # issue:} other {Open Issues to view # issues:}}',
38
38
  } as const;
@@ -61,6 +61,7 @@ export class CoverageModel extends SDK.SDKModel.SDKModel<EventTypes> {
61
61
  private sourceMapManager: SDK.SourceMapManager.SourceMapManager<SDK.Script.Script>|null;
62
62
  private willResolveSourceMaps: boolean;
63
63
  private processSourceMapBacklog: SourceMapObject[];
64
+ private isPolling: boolean;
64
65
 
65
66
  constructor(target: SDK.Target.Target) {
66
67
  super(target);
@@ -88,6 +89,7 @@ export class CoverageModel extends SDK.SDKModel.SDKModel<EventTypes> {
88
89
  this.performanceTraceRecording = false;
89
90
  this.willResolveSourceMaps = false;
90
91
  this.processSourceMapBacklog = [];
92
+ this.isPolling = false;
91
93
  }
92
94
 
93
95
  async start(jsCoveragePerBlock: boolean): Promise<boolean> {
@@ -184,22 +186,30 @@ export class CoverageModel extends SDK.SDKModel.SDKModel<EventTypes> {
184
186
  }
185
187
 
186
188
  async startPolling(): Promise<void> {
187
- if (this.currentPollPromise || this.suspensionState !== SuspensionState.ACTIVE) {
189
+ if (this.isPolling || this.suspensionState !== SuspensionState.ACTIVE) {
188
190
  return;
189
191
  }
192
+ this.isPolling = true;
190
193
  await this.pollLoop();
191
194
  }
192
195
 
193
196
  private async pollLoop(): Promise<void> {
194
197
  this.clearTimer();
198
+ if (!this.isPolling) {
199
+ return;
200
+ }
195
201
  this.currentPollPromise = this.pollAndCallback();
196
202
  await this.currentPollPromise;
203
+ if (!this.isPolling) {
204
+ return;
205
+ }
197
206
  if (this.suspensionState === SuspensionState.ACTIVE || this.performanceTraceRecording) {
198
207
  this.pollTimer = window.setTimeout(() => this.pollLoop(), COVERAGE_POLLING_PERIOD_MS);
199
208
  }
200
209
  }
201
210
 
202
211
  async stopPolling(): Promise<void> {
212
+ this.isPolling = false;
203
213
  this.clearTimer();
204
214
  await this.currentPollPromise;
205
215
  this.currentPollPromise = null;
@@ -154,7 +154,7 @@ export class StylePropertiesSection {
154
154
  private parentsComputedStyles: Map<string, string>|null;
155
155
  private computedStyleExtraFields: Protocol.CSS.ComputedStyleExtraFields|null;
156
156
  editable: boolean;
157
- private hoverTimer: number|null = null;
157
+ private hoverTimer: ReturnType<typeof setTimeout>|null = null;
158
158
  private willCauseCancelEditing = false;
159
159
  private forceShowAll = false;
160
160
  private readonly originalPropertiesCount: number;
@@ -275,7 +275,11 @@ export class StylePropertiesSection {
275
275
  this.selectorElement.classList.add('selector');
276
276
  this.selectorElement.textContent = headerText;
277
277
  selectorContainer.appendChild(this.selectorElement);
278
- this.selectorElement.addEventListener('mouseenter', () => this.onMouseEnterSelector(), false);
278
+ this.selectorElement.addEventListener('mouseenter', () => {
279
+ if (this.styleInternal.parentRule instanceof SDK.CSSRule.CSSStyleRule) {
280
+ this.onMouseEnterSelector(this.styleInternal.parentRule);
281
+ }
282
+ }, false);
279
283
  this.selectorElement.addEventListener('mouseleave', this.onMouseOutSelector.bind(this), false);
280
284
  this.#specificityTooltips = selectorContainer.createChild('span');
281
285
 
@@ -679,35 +683,31 @@ export class StylePropertiesSection {
679
683
  SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
680
684
  }
681
685
 
682
- private onMouseEnterSelector(ruleOrSelector?: SDK.CSSRule.CSSStyleRule|string): void {
686
+ private onMouseEnterSelector(rule: SDK.CSSRule.CSSStyleRule, nestingIndex?: number): void {
683
687
  if (this.hoverTimer) {
684
688
  clearTimeout(this.hoverTimer);
685
689
  }
686
- this.hoverTimer = window.setTimeout(this.highlight.bind(this, undefined, ruleOrSelector), 300);
690
+ const selectorList = constructResolvedSelector(rule, nestingIndex);
691
+ if (!selectorList) {
692
+ return;
693
+ }
694
+ this.hoverTimer = setTimeout(this.highlight.bind(this, undefined, selectorList), 300);
687
695
  }
688
696
 
689
697
  /**
690
698
  * Highlights the DOM node associated with this style section in the page overlay.
691
- * Use `ruleOrSelector` to highlight elements matching a specific parent/ancestor
692
- * rule or selector, or omit it to use the selector of the rule displayed in this section.
699
+ * Use `selectorList` to highlight elements matching a specific parent/ancestor
700
+ * rule or selector.
693
701
  *
694
702
  * @param mode Highlight mode (defaults to `'all'`).
695
- * @param ruleOrSelector Parent selector string, parent rule, or `undefined`.
703
+ * @param selectorList Parent selector string to highlight.
696
704
  */
697
- highlight(mode: string|undefined = 'all', ruleOrSelector?: SDK.CSSRule.CSSStyleRule|string): void {
705
+ highlight(mode: string|undefined = 'all', selectorList: string): void {
698
706
  SDK.OverlayModel.OverlayModel.hideDOMNodeHighlight(SDK.TargetManager.TargetManager.instance());
699
707
  const node = this.stylesContainer.node();
700
708
  if (!node) {
701
709
  return;
702
710
  }
703
- let selectorList: string|undefined;
704
- if (typeof ruleOrSelector === 'string') {
705
- selectorList = ruleOrSelector;
706
- } else if (ruleOrSelector instanceof SDK.CSSRule.CSSStyleRule) {
707
- selectorList = ruleOrSelector.selectorText();
708
- } else if (this.styleInternal.parentRule instanceof SDK.CSSRule.CSSStyleRule) {
709
- selectorList = this.styleInternal.parentRule.selectorText();
710
- }
711
711
  node.domModel().overlayModel().highlightInOverlay({node, selectorList}, mode);
712
712
  }
713
713
 
@@ -1164,7 +1164,7 @@ export class StylePropertiesSection {
1164
1164
  }
1165
1165
 
1166
1166
  const nestingElement = document.createElement('div');
1167
- nestingElement.addEventListener('mouseenter', () => this.onMouseEnterSelector(nestingSelector), false);
1167
+ nestingElement.addEventListener('mouseenter', () => this.onMouseEnterSelector(rule, nestingIndex), false);
1168
1168
  nestingElement.addEventListener('mouseleave', this.onMouseOutSelector.bind(this), false);
1169
1169
  nestingElement.textContent = `${nestingSelector} {`;
1170
1170
  return nestingElement;
@@ -2273,3 +2273,36 @@ export class HighlightPseudoStylePropertiesSection extends StylePropertiesSectio
2273
2273
  interface TreeElementParent {
2274
2274
  appendChild(child: UI.TreeOutline.TreeElement): void;
2275
2275
  }
2276
+
2277
+ export function constructResolvedSelector(rule: SDK.CSSRule.CSSRule|null, nestingIndex?: number): string|undefined {
2278
+ if (!(rule instanceof SDK.CSSRule.CSSStyleRule)) {
2279
+ return undefined;
2280
+ }
2281
+
2282
+ const nestingSelectors = rule.nestingSelectors;
2283
+ if (!nestingSelectors) {
2284
+ return nestingIndex === undefined ? rule.selectorText() : undefined;
2285
+ }
2286
+
2287
+ if (nestingIndex !== undefined && (nestingIndex < 0 || nestingIndex >= nestingSelectors.length)) {
2288
+ return undefined;
2289
+ }
2290
+
2291
+ const selectorText = nestingIndex !== undefined ? nestingSelectors[nestingIndex] : rule.selectorText();
2292
+
2293
+ const parentIndex = nestingIndex !== undefined ? nestingIndex + 1 : 0;
2294
+ const parentSelector = constructResolvedSelector(rule, parentIndex);
2295
+
2296
+ if (!parentSelector) {
2297
+ return selectorText;
2298
+ }
2299
+
2300
+ // Strip pseudo-elements (e.g. ::before) because pseudo-elements are invalid inside CSS :is(...).
2301
+ const sanitizedParent = parentSelector.replace(/::[a-zA-Z-]+/g, '').trim();
2302
+
2303
+ if (selectorText.includes('&')) {
2304
+ return selectorText.replaceAll('&', `:is(${sanitizedParent})`);
2305
+ }
2306
+
2307
+ return `:is(${sanitizedParent}) ${selectorText.trim()}`;
2308
+ }
@@ -29,17 +29,29 @@ export class StylesAiCodeCompletionProvider {
29
29
  sampleId?: number,
30
30
  }|null) => void;
31
31
 
32
- #boundOnUpdateAiCodeCompletionState = this.#updateAiCodeCompletionState.bind(this);
32
+ #boundOnAidaAvailabilityChange =
33
+ (ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>): void => {
34
+ this.#updateAiCodeCompletionStateWithAvailability(ev.data);
35
+ };
36
+ #boundOnSettingChange = (): void => {
37
+ const aidaAvailability = Host.AidaClient.HostConfigTracker.instance().aidaAvailability;
38
+ if (aidaAvailability !== undefined) {
39
+ this.#updateAiCodeCompletionStateWithAvailability(aidaAvailability);
40
+ }
41
+ };
33
42
 
34
43
  private constructor(aiCodeCompletionConfig: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig) {
35
44
  if (!AiCodeCompletion.AiCodeCompletion.AiCodeCompletion.isAiCodeCompletionStylesAvailable()) {
36
45
  throw new Error('AI code completion feature in Styles is not available.');
37
46
  }
38
47
  this.#aiCodeCompletionConfig = aiCodeCompletionConfig;
39
- Host.AidaClient.HostConfigTracker.instance().addEventListener(
40
- Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnUpdateAiCodeCompletionState);
41
- this.#aiCodeCompletionSetting.addChangeListener(this.#boundOnUpdateAiCodeCompletionState);
42
- void this.#updateAiCodeCompletionState();
48
+ Host.AidaClient.HostConfigTracker.instance().addEventListener(Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED,
49
+ this.#boundOnAidaAvailabilityChange);
50
+ this.#aiCodeCompletionSetting.addChangeListener(this.#boundOnSettingChange);
51
+ const initialAvailability = Host.AidaClient.HostConfigTracker.instance().aidaAvailability;
52
+ if (initialAvailability !== undefined) {
53
+ this.#updateAiCodeCompletionStateWithAvailability(initialAvailability);
54
+ }
43
55
  }
44
56
 
45
57
  static createInstance(aiCodeCompletionConfig: TextEditor.AiCodeCompletionProvider.AiCodeCompletionConfig):
@@ -78,8 +90,7 @@ export class StylesAiCodeCompletionProvider {
78
90
  this.#aiCodeCompletionConfig?.onFeatureDisabled();
79
91
  }
80
92
 
81
- async #updateAiCodeCompletionState(): Promise<void> {
82
- const aidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
93
+ #updateAiCodeCompletionStateWithAvailability(aidaAvailability: Host.AidaClient.AidaAccessPreconditions): void {
83
94
  const isAvailable = aidaAvailability === Host.AidaClient.AidaAccessPreconditions.AVAILABLE;
84
95
  const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance().locale;
85
96
  const isEnabled =
@@ -5,13 +5,14 @@
5
5
 
6
6
  import type * as Common from '../../core/common/common.js';
7
7
  import * as Host from '../../core/host/host.js';
8
+ import type * as Foundation from '../../foundation/foundation.js';
8
9
  import * as UI from '../../ui/legacy/legacy.js';
9
10
  import * as ThemeSupport from '../../ui/legacy/theme_support/theme_support.js';
10
11
 
11
12
  import {DeviceModeWrapper} from './DeviceModeWrapper.js';
12
13
  import {type Bounds, Events, InspectedPagePlaceholder} from './InspectedPagePlaceholder.js';
13
14
 
14
- let appInstance: AdvancedApp;
15
+ let appInstance: AdvancedApp|null = null;
15
16
 
16
17
  export class AdvancedApp implements UI.App.App {
17
18
  private rootSplitWidget!: UI.SplitWidget.SplitWidget;
@@ -20,8 +21,10 @@ export class AdvancedApp implements UI.App.App {
20
21
  private toolboxWindow?: Window|null;
21
22
  private toolboxRootView?: UI.RootView.RootView;
22
23
  private changingDockSide?: boolean;
24
+ readonly #universe: Foundation.Universe.Universe;
23
25
 
24
- constructor() {
26
+ constructor(universe: Foundation.Universe.Universe) {
27
+ this.#universe = universe;
25
28
  UI.DockController.DockController.instance().addEventListener(
26
29
  UI.DockController.Events.BEFORE_DOCK_SIDE_CHANGED, this.openToolboxWindow, this);
27
30
  }
@@ -29,15 +32,22 @@ export class AdvancedApp implements UI.App.App {
29
32
  /**
30
33
  * Note: it's used by toolbox.ts without real type checks.
31
34
  */
32
- static instance(): AdvancedApp {
35
+ static instance(universe?: Foundation.Universe.Universe): AdvancedApp {
33
36
  if (!appInstance) {
34
- appInstance = new AdvancedApp();
37
+ if (!universe) {
38
+ throw new Error('AdvancedApp.instance() requires a Universe on initial instantiation');
39
+ }
40
+ appInstance = new AdvancedApp(universe);
35
41
  }
36
42
  return appInstance;
37
43
  }
38
44
 
45
+ static removeInstance(): void {
46
+ appInstance = null;
47
+ }
48
+
39
49
  presentUI(document: Document): void {
40
- const rootView = new UI.RootView.RootView();
50
+ const rootView = new UI.RootView.RootView(this.#universe);
41
51
 
42
52
  this.rootSplitWidget =
43
53
  new UI.SplitWidget.SplitWidget(false, true, 'inspector-view.split-view-state', 555, 300, true);
@@ -85,7 +95,7 @@ export class AdvancedApp implements UI.App.App {
85
95
  UI.UIUtils.installComponentRootStyles(toolboxDocument.body);
86
96
  UI.ContextMenu.ContextMenu.installHandler(toolboxDocument);
87
97
 
88
- this.toolboxRootView = new UI.RootView.RootView();
98
+ this.toolboxRootView = new UI.RootView.RootView(this.#universe);
89
99
  this.toolboxRootView.attachToDocument(toolboxDocument);
90
100
 
91
101
  this.updateDeviceModeView();
@@ -200,7 +210,7 @@ export class AdvancedAppProvider implements UI.AppProvider.AppProvider {
200
210
  return advancedAppProviderInstance;
201
211
  }
202
212
 
203
- createApp(): UI.App.App {
204
- return AdvancedApp.instance();
213
+ createApp(universe: Foundation.Universe.Universe): UI.App.App {
214
+ return AdvancedApp.instance(universe);
205
215
  }
206
216
  }
@@ -21,31 +21,31 @@ import eventListenersViewStyles from './eventListenersView.css.js';
21
21
 
22
22
  const UIStrings = {
23
23
  /**
24
- * @description Empty holder text content in Event Listeners View of the Event Listener Debugging pane in the Sources panel
24
+ * @description Empty holder text content in Event Listeners view of the Event Listeners sidebar in the Sources panel.
25
25
  */
26
26
  noEventListeners: 'No event listeners',
27
27
  /**
28
- * @description Empty holder text content in Event Listeners View of the Event Listener Debugging pane in the Elements panel
28
+ * @description Empty holder text content in Event Listeners view of the Event Listeners sidebar in the Elements panel.
29
29
  */
30
30
  eventListenersExplanation: 'On this page you will find registered event listeners',
31
31
  /**
32
- * @description Delete button title in Event Listeners View of the Event Listener Debugging pane in the Sources panel
32
+ * @description Delete button title in Event Listeners view of the Event Listeners sidebar in the Sources panel.
33
33
  */
34
34
  deleteEventListener: 'Delete event listener',
35
35
  /**
36
- * @description Passive button text content in Event Listeners View of the Event Listener Debugging pane in the Sources panel
36
+ * @description Passive button text content in Event Listeners view of the Event Listeners sidebar in the Sources panel.
37
37
  */
38
- togglePassive: 'Toggle Passive',
38
+ togglePassive: 'Toggle passive',
39
39
  /**
40
- * @description Passive button title in Event Listeners View of the Event Listener Debugging pane in the Sources panel
40
+ * @description Passive button title in Event Listeners view of the Event Listeners sidebar in the Sources panel.
41
41
  */
42
42
  toggleWhetherEventListenerIs: 'Toggle whether event listener is passive or blocking',
43
43
  /**
44
- * @description A context menu item to reveal a node in the DOM tree of the Elements Panel
44
+ * @description A context menu item to reveal a node in the DOM tree of the Elements panel.
45
45
  */
46
46
  openInElementsPanel: 'Open in Elements panel',
47
47
  /**
48
- * @description Text in Event Listeners Widget of the Elements panel
48
+ * @description Text in Event Listeners widget of the Elements panel.
49
49
  */
50
50
  passive: 'Passive',
51
51
  } as const;
@@ -753,7 +753,8 @@ export type ViewFunction = typeof DEFAULT_VIEW;
753
753
 
754
754
  export class ConsoleInsight extends UI.Widget.Widget {
755
755
  static async create(promptBuilder: PublicPromptBuilder, aidaClient: PublicAidaClient): Promise<Lit.LitTemplate> {
756
- const aidaPreconditions = await Host.AidaClient.AidaClient.checkAccessPreconditions();
756
+ const aidaPreconditions = Host.AidaClient.HostConfigTracker.instance().aidaAvailability ??
757
+ await Host.AidaClient.AidaClient.checkAccessPreconditions();
757
758
  return html`<devtools-widget class="devtools-console-insight" ${
758
759
  widget(element => new ConsoleInsight(promptBuilder, aidaClient, aidaPreconditions, element))}>
759
760
  </devtools-widget>`;
@@ -780,7 +781,8 @@ export class ConsoleInsight extends UI.Widget.Widget {
780
781
 
781
782
  #consoleInsightsEnabledSetting: Common.Settings.Setting<boolean>|undefined;
782
783
  #aidaPreconditions: Host.AidaClient.AidaAccessPreconditions;
783
- #boundOnAidaAvailabilityChange: () => Promise<void>;
784
+ #boundOnAidaAvailabilityChange:
785
+ (ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>) => void;
784
786
  #marked: Marked.Marked.Marked;
785
787
 
786
788
  constructor(
@@ -885,7 +887,10 @@ export class ConsoleInsight extends UI.Widget.Widget {
885
887
  Host.AidaClient.HostConfigTracker.instance().addEventListener(
886
888
  Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
887
889
  // If AIDA availability has changed while the component was disconnected, we need to update.
888
- void this.#onAidaAvailabilityChange();
890
+ const initialAvailability = Host.AidaClient.HostConfigTracker.instance().aidaAvailability;
891
+ if (initialAvailability !== undefined) {
892
+ this.#updateAidaAvailability(initialAvailability);
893
+ }
889
894
  // The setting might have been turned on/off while the component was disconnected.
890
895
  // Update the state, unless the current state is already terminal (`INSIGHT` or `ERROR`).
891
896
  if (this.#state.type !== State.INSIGHT && this.#state.type !== State.ERROR) {
@@ -901,15 +906,18 @@ export class ConsoleInsight extends UI.Widget.Widget {
901
906
  Host.AidaClient.Events.AIDA_AVAILABILITY_CHANGED, this.#boundOnAidaAvailabilityChange);
902
907
  }
903
908
 
904
- async #onAidaAvailabilityChange(): Promise<void> {
905
- const currentAidaAvailability = await Host.AidaClient.AidaClient.checkAccessPreconditions();
906
- if (currentAidaAvailability !== this.#aidaPreconditions) {
907
- this.#aidaPreconditions = currentAidaAvailability;
909
+ #updateAidaAvailability(aidaAvailability: Host.AidaClient.AidaAccessPreconditions): void {
910
+ if (aidaAvailability !== this.#aidaPreconditions) {
911
+ this.#aidaPreconditions = aidaAvailability;
908
912
  this.#state = this.#getStateFromAidaAvailability();
909
913
  void this.#generateInsightIfNeeded();
910
914
  }
911
915
  }
912
916
 
917
+ #onAidaAvailabilityChange(ev: Common.EventTarget.EventTargetEvent<Host.AidaClient.AidaAccessPreconditions>): void {
918
+ this.#updateAidaAvailability(ev.data);
919
+ }
920
+
913
921
  #onConsoleInsightsSettingChanged(): void {
914
922
  if (this.#consoleInsightsEnabledSetting?.getIfNotDisabled() === true) {
915
923
  this.#getOnboardingCompletedSetting().set(true);