chrome-devtools-frontend 1.0.1541169 → 1.0.1542501

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 (69) hide show
  1. package/docs/get_the_code.md +9 -0
  2. package/front_end/Tests.js +6 -1
  3. package/front_end/core/common/Settings.ts +140 -106
  4. package/front_end/core/host/UserMetrics.ts +5 -0
  5. package/front_end/core/sdk/IOModel.ts +1 -4
  6. package/front_end/core/sdk/ServerSentEventsProtocol.ts +4 -0
  7. package/front_end/entrypoints/main/MainImpl.ts +18 -7
  8. package/front_end/foundation/README.md +10 -0
  9. package/front_end/foundation/Universe.ts +21 -0
  10. package/front_end/foundation/foundation.ts +7 -0
  11. package/front_end/generated/SupportedCSSProperties.js +42 -42
  12. package/front_end/models/ai_assistance/BuiltInAi.ts +2 -1
  13. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +44 -34
  14. package/front_end/models/ai_code_completion/AiCodeCompletion.ts +72 -31
  15. package/front_end/models/har/Importer.ts +14 -0
  16. package/front_end/models/issues_manager/IssuesManager.ts +0 -5
  17. package/front_end/models/javascript_metadata/NativeFunctions.js +0 -4
  18. package/front_end/models/trace/handlers/ScriptsHandler.ts +26 -0
  19. package/front_end/models/trace/types/TraceEvents.ts +1 -1
  20. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +117 -103
  21. package/front_end/panels/ai_assistance/components/ChatView.ts +7 -31
  22. package/front_end/panels/ai_assistance/components/MarkdownRendererWithCodeBlock.ts +1 -1
  23. package/front_end/panels/ai_assistance/components/PerformanceAgentMarkdownRenderer.ts +1 -1
  24. package/front_end/panels/ai_assistance/components/chatView.css +1 -1
  25. package/front_end/panels/console/ConsoleInsightTeaser.ts +5 -0
  26. package/front_end/panels/console/ConsolePrompt.ts +8 -1
  27. package/front_end/panels/sources/AiCodeCompletionPlugin.ts +17 -1
  28. package/front_end/panels/timeline/overlays/components/EntryLabelOverlay.ts +4 -1
  29. package/front_end/third_party/chromium/README.chromium +1 -1
  30. package/front_end/third_party/puppeteer/README.chromium +2 -2
  31. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  32. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js +3 -1
  33. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/cdp/NetworkManager.js.map +1 -1
  34. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.d.ts +2 -2
  35. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/revisions.js +2 -2
  36. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.d.ts +1 -1
  37. package/front_end/third_party/puppeteer/package/lib/cjs/puppeteer/util/version.js +1 -1
  38. package/front_end/third_party/puppeteer/package/lib/es5-iife/puppeteer-core-browser.js +4 -4
  39. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.d.ts.map +1 -1
  40. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js +3 -1
  41. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/cdp/NetworkManager.js.map +1 -1
  42. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.d.ts +2 -2
  43. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/revisions.js +2 -2
  44. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.d.ts +1 -1
  45. package/front_end/third_party/puppeteer/package/lib/esm/puppeteer/util/version.js +1 -1
  46. package/front_end/third_party/puppeteer/package/package.json +1 -1
  47. package/front_end/third_party/puppeteer/package/src/cdp/NetworkManager.ts +3 -1
  48. package/front_end/third_party/puppeteer/package/src/revisions.ts +2 -2
  49. package/front_end/third_party/puppeteer/package/src/util/version.ts +1 -1
  50. package/front_end/ui/components/markdown_view/MarkdownLinksMap.ts +0 -4
  51. package/front_end/ui/components/markdown_view/MarkdownView.docs.ts +95 -0
  52. package/front_end/ui/components/markdown_view/MarkdownView.ts +6 -7
  53. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +246 -13
  54. package/front_end/ui/components/text_editor/config.ts +1 -1
  55. package/front_end/ui/legacy/Widget.ts +13 -4
  56. package/front_end/ui/legacy/components/object_ui/ObjectPropertiesSection.ts +43 -33
  57. package/front_end/ui/visual_logging/KnownContextValues.ts +12 -0
  58. package/mcp/mcp.ts +2 -0
  59. package/package.json +1 -1
  60. package/front_end/models/issues_manager/UserReidentificationIssue.ts +0 -72
  61. package/front_end/models/issues_manager/descriptions/userReidentificationBlocked.md +0 -5
  62. package/front_end/ui/components/docs/markdown_image/basic.html +0 -19
  63. package/front_end/ui/components/docs/markdown_image/basic.ts +0 -38
  64. package/front_end/ui/components/docs/markdown_link/basic.html +0 -17
  65. package/front_end/ui/components/docs/markdown_link/basic.ts +0 -19
  66. package/front_end/ui/components/docs/markdown_view/basic.html +0 -25
  67. package/front_end/ui/components/docs/markdown_view/basic.ts +0 -67
  68. package/front_end/ui/components/docs/markdown_view/code-block.html +0 -30
  69. package/front_end/ui/components/docs/markdown_view/code-block.ts +0 -71
@@ -5,7 +5,7 @@
5
5
  import * as Common from '../../../core/common/common.js';
6
6
  import * as Host from '../../../core/host/host.js';
7
7
  import * as i18n from '../../../core/i18n/i18n.js';
8
- import * as Root from '../../../core/root/root.js';
8
+ import * as AiCodeCompletion from '../../../models/ai_code_completion/ai_code_completion.js';
9
9
  import * as PanelCommon from '../../../panels/common/common.js';
10
10
  import * as CodeMirror from '../../../third_party/codemirror.next/codemirror.next.js';
11
11
  import * as UI from '../../legacy/legacy.js';
@@ -13,9 +13,12 @@ import * as VisualLogging from '../../visual_logging/visual_logging.js';
13
13
 
14
14
  import {AiCodeCompletionTeaserPlaceholder} from './AiCodeCompletionTeaserPlaceholder.js';
15
15
  import {
16
+ acceptAiAutoCompleteSuggestion,
16
17
  aiAutoCompleteSuggestion,
17
18
  aiAutoCompleteSuggestionState,
19
+ hasActiveAiSuggestion,
18
20
  setAiAutoCompleteSuggestion,
21
+ showCompletionHint,
19
22
  } from './config.js';
20
23
  import type {TextEditor} from './TextEditor.js';
21
24
 
@@ -45,15 +48,18 @@ export interface AiCodeCompletionConfig {
45
48
  onFeatureDisabled: () => void;
46
49
  onSuggestionAccepted: () => void;
47
50
  onRequestTriggered: () => void;
51
+ // TODO(b/445394511): Move exposing citations to onSuggestionAccepted
48
52
  onResponseReceived: (citations: Host.AidaClient.Citation[]) => void;
53
+ panel: AiCodeCompletion.AiCodeCompletion.ContextFlavor;
49
54
  }
50
55
 
51
56
  export const DELAY_BEFORE_SHOWING_RESPONSE_MS = 500;
52
57
  export const AIDA_REQUEST_DEBOUNCE_TIMEOUT_MS = 200;
58
+ const MAX_PREFIX_SUFFIX_LENGTH = 20_000;
53
59
 
54
- // TODO(samiyac): Add code relevant to AiCodeCompletion and for triggering requests
55
60
  export class AiCodeCompletionProvider {
56
61
  #aidaClient?: Host.AidaClient.AidaClient;
62
+ #aiCodeCompletion?: AiCodeCompletion.AiCodeCompletion.AiCodeCompletion;
57
63
  #aiCodeCompletionSetting = Common.Settings.Settings.instance().createSetting('ai-code-completion-enabled', false);
58
64
  #aiCodeCompletionTeaserDismissedSetting =
59
65
  Common.Settings.Settings.instance().createSetting('ai-code-completion-teaser-dismissed', false);
@@ -66,7 +72,8 @@ export class AiCodeCompletionProvider {
66
72
  #boundOnUpdateAiCodeCompletionState = this.#updateAiCodeCompletionState.bind(this);
67
73
 
68
74
  constructor(aiCodeCompletionConfig: AiCodeCompletionConfig) {
69
- if (!this.#isAiCodeCompletionEnabled()) {
75
+ const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
76
+ if (!AiCodeCompletion.AiCodeCompletion.AiCodeCompletion.isAiCodeCompletionEnabled(devtoolsLocale.locale)) {
70
77
  throw new Error('AI code completion feature is not enabled.');
71
78
  }
72
79
  this.#aiCodeCompletionConfig = aiCodeCompletionConfig;
@@ -74,10 +81,12 @@ export class AiCodeCompletionProvider {
74
81
 
75
82
  extension(): CodeMirror.Extension[] {
76
83
  return [
84
+ CodeMirror.EditorView.updateListener.of(update => this.#triggerAiCodeCompletion(update)),
77
85
  this.#teaserCompartment.of([]),
78
86
  aiAutoCompleteSuggestion,
79
87
  aiCodeCompletionTeaserModeState,
80
88
  aiAutoCompleteSuggestionState,
89
+ CodeMirror.Prec.highest(CodeMirror.keymap.of(this.#editorKeymap())),
81
90
  ];
82
91
  }
83
92
 
@@ -105,6 +114,10 @@ export class AiCodeCompletionProvider {
105
114
  void this.#updateAiCodeCompletionState();
106
115
  }
107
116
 
117
+ clearCache(): void {
118
+ this.#aiCodeCompletion?.clearCachedRequest();
119
+ }
120
+
108
121
  #setupAiCodeCompletion(): void {
109
122
  if (!this.#editor || !this.#aiCodeCompletionConfig) {
110
123
  return;
@@ -112,6 +125,11 @@ export class AiCodeCompletionProvider {
112
125
  if (!this.#aidaClient) {
113
126
  this.#aidaClient = new Host.AidaClient.AidaClient();
114
127
  }
128
+ if (!this.#aiCodeCompletion) {
129
+ this.#aiCodeCompletion = new AiCodeCompletion.AiCodeCompletion.AiCodeCompletion(
130
+ {aidaClient: this.#aidaClient}, this.#aiCodeCompletionConfig.panel, undefined,
131
+ this.#aiCodeCompletionConfig.completionContext.stopSequences);
132
+ }
115
133
  this.#aiCodeCompletionConfig.onFeatureEnabled();
116
134
  }
117
135
 
@@ -123,6 +141,7 @@ export class AiCodeCompletionProvider {
123
141
  this.#editor?.dispatch({
124
142
  effects: setAiAutoCompleteSuggestion.of(null),
125
143
  });
144
+ this.#aiCodeCompletion = undefined;
126
145
  this.#aiCodeCompletionConfig?.onFeatureDisabled();
127
146
  }
128
147
 
@@ -145,6 +164,40 @@ export class AiCodeCompletionProvider {
145
164
  }
146
165
  }
147
166
 
167
+ #editorKeymap(): readonly CodeMirror.KeyBinding[] {
168
+ return [
169
+ {
170
+ key: 'Escape',
171
+ run: (): boolean => {
172
+ if (!this.#aiCodeCompletion || !this.#editor || !hasActiveAiSuggestion(this.#editor.state)) {
173
+ return false;
174
+ }
175
+ this.#editor.dispatch({
176
+ effects: setAiAutoCompleteSuggestion.of(null),
177
+ });
178
+ return true;
179
+ },
180
+ },
181
+ {
182
+ key: 'Tab',
183
+ run: (): boolean => {
184
+ if (!this.#aiCodeCompletion || !this.#editor || !hasActiveAiSuggestion(this.#editor.state)) {
185
+ return false;
186
+ }
187
+ const {accepted, suggestion} = acceptAiAutoCompleteSuggestion(this.#editor.editor);
188
+ if (!accepted) {
189
+ return false;
190
+ }
191
+ if (suggestion?.rpcGlobalId) {
192
+ this.#aiCodeCompletion?.registerUserAcceptance(suggestion.rpcGlobalId, suggestion.sampleId);
193
+ }
194
+ this.#aiCodeCompletionConfig?.onSuggestionAccepted();
195
+ return true;
196
+ },
197
+ },
198
+ ];
199
+ }
200
+
148
201
  #detachTeaser(): void {
149
202
  if (!this.#teaser) {
150
203
  return;
@@ -152,18 +205,198 @@ export class AiCodeCompletionProvider {
152
205
  this.#editor?.editor.dispatch({effects: this.#teaserCompartment.reconfigure([])});
153
206
  }
154
207
 
155
- // TODO(samiyac): Define static method in AiCodeCompletion and use that instead
156
- #isAiCodeCompletionEnabled(): boolean {
157
- const devtoolsLocale = i18n.DevToolsLocale.DevToolsLocale.instance();
158
- const aidaAvailability = Root.Runtime.hostConfig.aidaAvailability;
159
- if (!devtoolsLocale.locale.startsWith('en-')) {
160
- return false;
208
+ #triggerAiCodeCompletion(update: CodeMirror.ViewUpdate): void {
209
+ if (!update.docChanged || !this.#editor || !this.#aiCodeCompletion) {
210
+ return;
211
+ }
212
+ const {doc, selection} = update.state;
213
+ const query = doc.toString();
214
+ const cursor = selection.main.head;
215
+
216
+ let prefix = query.substring(0, cursor);
217
+ if (prefix.trim().length === 0) {
218
+ return;
219
+ }
220
+ const completionContextPrefix = this.#aiCodeCompletionConfig?.completionContext.getPrefix?.();
221
+ if (completionContextPrefix) {
222
+ prefix = completionContextPrefix + prefix;
161
223
  }
162
- if (!aidaAvailability || aidaAvailability.blockedByGeo || aidaAvailability.blockedByAge ||
163
- aidaAvailability.blockedByEnterprisePolicy) {
164
- return false;
224
+ if (prefix.length > MAX_PREFIX_SUFFIX_LENGTH) {
225
+ prefix = prefix.substring(prefix.length - MAX_PREFIX_SUFFIX_LENGTH);
226
+ }
227
+
228
+ const suffix = query.substring(cursor, cursor + MAX_PREFIX_SUFFIX_LENGTH);
229
+
230
+ this.#debouncedRequestAidaSuggestion(
231
+ prefix, suffix, cursor, this.#aiCodeCompletionConfig?.completionContext.inferenceLanguage,
232
+ this.#aiCodeCompletionConfig?.completionContext.additionalFiles);
233
+ }
234
+
235
+ #debouncedRequestAidaSuggestion = Common.Debouncer.debounce(
236
+ (prefix: string, suffix: string, cursorPositionAtRequest: number,
237
+ inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage,
238
+ additionalFiles?: Host.AidaClient.AdditionalFile[]) => {
239
+ void this.#requestAidaSuggestion(prefix, suffix, cursorPositionAtRequest, inferenceLanguage, additionalFiles);
240
+ },
241
+ AIDA_REQUEST_DEBOUNCE_TIMEOUT_MS);
242
+
243
+ async #requestAidaSuggestion(
244
+ prefix: string, suffix: string, cursorPositionAtRequest: number,
245
+ inferenceLanguage?: Host.AidaClient.AidaInferenceLanguage,
246
+ additionalFiles?: Host.AidaClient.AdditionalFile[]): Promise<void> {
247
+ if (!this.#aiCodeCompletion) {
248
+ AiCodeCompletion.debugLog('Ai Code Completion is not initialized');
249
+ this.#aiCodeCompletionConfig?.onResponseReceived([]);
250
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionError);
251
+ return;
252
+ }
253
+
254
+ const startTime = performance.now();
255
+ this.#aiCodeCompletionConfig?.onRequestTriggered();
256
+ // Registering AiCodeCompletionRequestTriggered metric even if the request is served from cache
257
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionRequestTriggered);
258
+
259
+ try {
260
+ const completionResponse = await this.#aiCodeCompletion.completeCode(
261
+ prefix, suffix, cursorPositionAtRequest, inferenceLanguage, additionalFiles);
262
+
263
+ if (!completionResponse) {
264
+ this.#aiCodeCompletionConfig?.onResponseReceived([]);
265
+ return;
266
+ }
267
+
268
+ const {response, fromCache} = completionResponse;
269
+
270
+ if (!response) {
271
+ this.#aiCodeCompletionConfig?.onResponseReceived([]);
272
+ return;
273
+ }
274
+
275
+ const sampleResponse = await this.#generateSampleForRequest(response, prefix, suffix);
276
+ if (!sampleResponse) {
277
+ this.#aiCodeCompletionConfig?.onResponseReceived([]);
278
+ return;
279
+ }
280
+
281
+ const {
282
+ suggestionText,
283
+ sampleId,
284
+ citations,
285
+ rpcGlobalId,
286
+ } = sampleResponse;
287
+ const remainingDelay = Math.max(
288
+ AiCodeCompletion.AiCodeCompletion.DELAY_BEFORE_SHOWING_RESPONSE_MS - (performance.now() - startTime), 0);
289
+ this.#suggestionRenderingTimeout = window.setTimeout(() => {
290
+ const currentCursorPosition = this.#editor?.editor.state.selection.main.head;
291
+ if (currentCursorPosition !== cursorPositionAtRequest) {
292
+ this.#aiCodeCompletionConfig?.onResponseReceived([]);
293
+ return;
294
+ }
295
+ if (this.#aiCodeCompletion) {
296
+ this.#editor?.dispatch({
297
+ effects: setAiAutoCompleteSuggestion.of({
298
+ text: suggestionText,
299
+ from: cursorPositionAtRequest,
300
+ rpcGlobalId,
301
+ sampleId,
302
+ startTime,
303
+ clearCachedRequest: this.clearCache.bind(this),
304
+ onImpression: this.#aiCodeCompletion?.registerUserImpression.bind(this.#aiCodeCompletion),
305
+ })
306
+ });
307
+ }
308
+ if (fromCache) {
309
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionResponseServedFromCache);
310
+ }
311
+
312
+ AiCodeCompletion.debugLog(
313
+ 'Suggestion dispatched to the editor', suggestionText, 'at cursor position', cursorPositionAtRequest);
314
+ this.#aiCodeCompletionConfig?.onResponseReceived(citations);
315
+ }, remainingDelay);
316
+ } catch (e) {
317
+ AiCodeCompletion.debugLog('Error while fetching code completion suggestions from AIDA', e);
318
+ this.#aiCodeCompletionConfig?.onResponseReceived([]);
319
+ Host.userMetrics.actionTaken(Host.UserMetrics.Action.AiCodeCompletionError);
320
+ }
321
+ }
322
+
323
+ async #generateSampleForRequest(response: Host.AidaClient.CompletionResponse, prefix: string, suffix?: string):
324
+ Promise<{
325
+ suggestionText: string,
326
+ citations: Host.AidaClient.Citation[],
327
+ rpcGlobalId?: Host.AidaClient.RpcGlobalId,
328
+ sampleId?: number,
329
+ }|null> {
330
+ const suggestionSample = this.#pickSampleFromResponse(response);
331
+ if (!suggestionSample) {
332
+ return null;
333
+ }
334
+
335
+ const shouldBlock =
336
+ suggestionSample.attributionMetadata?.attributionAction === Host.AidaClient.RecitationAction.BLOCK;
337
+ if (shouldBlock) {
338
+ return null;
339
+ }
340
+
341
+ const isRepetitive = this.#checkIfSuggestionRepeatsExistingText(suggestionSample.generationString, prefix, suffix);
342
+ if (isRepetitive) {
343
+ return null;
344
+ }
345
+
346
+ const suggestionText = this.#trimSuggestionOverlap(suggestionSample.generationString, suffix);
347
+ if (suggestionText.length === 0) {
348
+ return null;
349
+ }
350
+
351
+ return {
352
+ suggestionText,
353
+ sampleId: suggestionSample.sampleId,
354
+ citations: suggestionSample.attributionMetadata?.citations ?? [],
355
+ rpcGlobalId: response.metadata.rpcGlobalId,
356
+ };
357
+ }
358
+
359
+ #pickSampleFromResponse(response: Host.AidaClient.CompletionResponse): Host.AidaClient.GenerationSample|null {
360
+ if (!response.generatedSamples.length) {
361
+ return null;
362
+ }
363
+
364
+ // `currentHint` is the portion of a standard autocomplete suggestion that the user has not yet typed.
365
+ // For example, if the user types `document.queryS` and the autocomplete suggests `document.querySelector`,
366
+ // the `currentHint` is `elector`.
367
+ const currentHintInMenu = this.#editor?.editor.plugin(showCompletionHint)?.currentHint;
368
+ if (!currentHintInMenu) {
369
+ return response.generatedSamples[0];
370
+ }
371
+
372
+ // TODO(ergunsh): This does not handle looking for `selectedCompletion`. The `currentHint` is `null`
373
+ // for the Sources panel case.
374
+ // Even though there is no match, we still return the first suggestion which will be displayed
375
+ // when the traditional autocomplete menu is closed.
376
+ return response.generatedSamples.find(sample => sample.generationString.startsWith(currentHintInMenu)) ??
377
+ response.generatedSamples[0];
378
+ }
379
+
380
+ #checkIfSuggestionRepeatsExistingText(generationString: string, prefix: string, suffix?: string): boolean {
381
+ return Boolean(prefix.includes(generationString.trim()) || suffix?.includes(generationString.trim()));
382
+ }
383
+
384
+ /**
385
+ * Removes the end of a suggestion if it overlaps with the start of the suffix.
386
+ */
387
+ #trimSuggestionOverlap(generationString: string, suffix?: string): string {
388
+ if (!suffix) {
389
+ return generationString;
390
+ }
391
+
392
+ // Iterate from the longest possible overlap down to the shortest
393
+ for (let i = Math.min(generationString.length, suffix.length); i > 0; i--) {
394
+ const overlapCandidate = suffix.substring(0, i);
395
+ if (generationString.endsWith(overlapCandidate)) {
396
+ return generationString.slice(0, -i);
397
+ }
165
398
  }
166
- return Boolean(aidaAvailability.enabled && Root.Runtime.hostConfig.devToolsAiCodeCompletion?.enabled);
399
+ return generationString;
167
400
  }
168
401
  }
169
402
 
@@ -483,7 +483,7 @@ export function contentIncludingHint(view: CM.EditorView): string {
483
483
 
484
484
  export const setAiAutoCompleteSuggestion = CM.StateEffect.define<ActiveSuggestion|null>();
485
485
 
486
- interface ActiveSuggestion {
486
+ export interface ActiveSuggestion {
487
487
  text: string;
488
488
  from: number;
489
489
  sampleId?: number;
@@ -113,8 +113,10 @@ function cancelUpdate(widget: Widget): void {
113
113
 
114
114
  function runNextUpdate(): void {
115
115
  pendingAnimationFrame = null;
116
- currentUpdateQueue = nextUpdateQueue;
117
- nextUpdateQueue = new Map();
116
+ if (!currentUpdateQueue) {
117
+ currentUpdateQueue = nextUpdateQueue;
118
+ nextUpdateQueue = new Map();
119
+ }
118
120
  for (const [widget, {resolve}] of currentUpdateQueue) {
119
121
  currentlyProcessed.add(widget);
120
122
  void (async () => {
@@ -122,8 +124,15 @@ function runNextUpdate(): void {
122
124
  resolve();
123
125
  })();
124
126
  }
125
- currentUpdateQueue = null;
126
- currentlyProcessed.clear();
127
+ currentUpdateQueue.clear();
128
+ queueMicrotask(() => {
129
+ if (currentUpdateQueue && currentUpdateQueue.size > 0) {
130
+ runNextUpdate();
131
+ } else {
132
+ currentUpdateQueue = null;
133
+ currentlyProcessed.clear();
134
+ }
135
+ });
127
136
  }
128
137
 
129
138
  export class WidgetElement<WidgetT extends Widget> extends HTMLElement {
@@ -320,6 +320,7 @@ class ArrayGroupTreeNode extends ObjectTreeNodeBase {
320
320
  }
321
321
 
322
322
  export class ObjectTreeNode extends ObjectTreeNodeBase {
323
+ #path?: string;
323
324
  constructor(
324
325
  readonly property: SDK.RemoteObject.RemoteObjectProperty,
325
326
  propertiesMode: ObjectPropertiesMode = ObjectPropertiesMode.OWN_AND_INTERNAL_AND_INHERITED,
@@ -336,6 +337,31 @@ export class ObjectTreeNode extends ObjectTreeNodeBase {
336
337
  return this.property.name;
337
338
  }
338
339
 
340
+ get path(): string {
341
+ if (!this.#path) {
342
+ if (this.property.synthetic) {
343
+ this.#path = this.name;
344
+ return this.name;
345
+ }
346
+
347
+ // https://tc39.es/ecma262/#prod-IdentifierName
348
+ const useDotNotation = /^(?:[$_\p{ID_Start}])(?:[$_\u200C\u200D\p{ID_Continue}])*$/u;
349
+ const isInteger = /^(?:0|[1-9]\d*)$/;
350
+
351
+ const parentPath =
352
+ (this.parent instanceof ObjectTreeNode && !this.parent.property.synthetic) ? this.parent.path : '';
353
+
354
+ if (this.property.private || useDotNotation.test(this.name)) {
355
+ this.#path = parentPath ? `${parentPath}.${this.name}` : this.name;
356
+ } else if (isInteger.test(this.name)) {
357
+ this.#path = `${parentPath}[${this.name}]`;
358
+ } else {
359
+ this.#path = `${parentPath}[${JSON.stringify(this.name)}]`;
360
+ }
361
+ }
362
+ return this.#path;
363
+ }
364
+
339
365
  override selfOrParentIfInternal(): ObjectTreeNodeBase {
340
366
  return this.name === '[[Prototype]]' ? (this.parent ?? this) : this;
341
367
  }
@@ -830,6 +856,11 @@ export class RootElement extends UI.TreeOutline.TreeElement {
830
856
  this.listItemElement.addEventListener('contextmenu', this.onContextMenu.bind(this), false);
831
857
  }
832
858
 
859
+ override invalidateChildren(): void {
860
+ super.invalidateChildren();
861
+ this.object.removeChildren();
862
+ }
863
+
833
864
  override onexpand(): void {
834
865
  if (this.treeOutline) {
835
866
  this.treeOutline.element.classList.add('expanded');
@@ -1189,8 +1220,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1189
1220
  if (this.property.property.synthetic) {
1190
1221
  this.nameElement.classList.add('synthetic-property');
1191
1222
  }
1192
-
1193
- this.updatePropertyPath();
1223
+ this.nameElement.title = this.property.path;
1194
1224
 
1195
1225
  const isInternalEntries = this.property.property.synthetic && this.property.name === '[[Entries]]';
1196
1226
  if (isInternalEntries) {
@@ -1200,7 +1230,7 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1200
1230
  const showPreview = this.property.name !== '[[Prototype]]';
1201
1231
  this.propertyValue = ObjectPropertiesSection.createPropertyValueWithCustomSupport(
1202
1232
  this.property.object, this.property.property.wasThrown, showPreview, this.linkifier,
1203
- this.property.property.synthetic, this.path() /* variableName */);
1233
+ this.property.property.synthetic, this.property.path /* variableName */);
1204
1234
  this.valueElement = this.propertyValue;
1205
1235
  } else if (this.property.property.getter) {
1206
1236
  this.valueElement = document.createElement('span');
@@ -1254,36 +1284,6 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1254
1284
  this.listItemElement.appendChild(this.rowContainer);
1255
1285
  }
1256
1286
 
1257
- private updatePropertyPath(): void {
1258
- if (this.nameElement.title) {
1259
- return;
1260
- }
1261
-
1262
- const name = this.property.name;
1263
-
1264
- if (this.property.property.synthetic) {
1265
- UI.Tooltip.Tooltip.install(this.nameElement, name);
1266
- return;
1267
- }
1268
-
1269
- // https://tc39.es/ecma262/#prod-IdentifierName
1270
- const useDotNotation = /^(?:[$_\p{ID_Start}])(?:[$_\u200C\u200D\p{ID_Continue}])*$/u;
1271
- const isInteger = /^(?:0|[1-9]\d*)$/;
1272
-
1273
- const parentPath = (this.parent instanceof ObjectPropertyTreeElement && this.parent.nameElement &&
1274
- !this.parent.property.property.synthetic) ?
1275
- this.parent.nameElement.title :
1276
- '';
1277
-
1278
- if (this.property.property.private || useDotNotation.test(name)) {
1279
- UI.Tooltip.Tooltip.install(this.nameElement, parentPath ? `${parentPath}.${name}` : name);
1280
- } else if (isInteger.test(name)) {
1281
- UI.Tooltip.Tooltip.install(this.nameElement, `${parentPath}[${name}]`);
1282
- } else {
1283
- UI.Tooltip.Tooltip.install(this.nameElement, `${parentPath}[${JSON.stringify(name)}]`);
1284
- }
1285
- }
1286
-
1287
1287
  getContextMenu(event: Event): UI.ContextMenu.ContextMenu {
1288
1288
  const contextMenu = new UI.ContextMenu.ContextMenu(event);
1289
1289
  contextMenu.appendApplicableItems(this);
@@ -1449,6 +1449,11 @@ export class ObjectPropertyTreeElement extends UI.TreeOutline.TreeElement {
1449
1449
  }
1450
1450
  }
1451
1451
 
1452
+ override invalidateChildren(): void {
1453
+ super.invalidateChildren();
1454
+ this.property.removeChildren();
1455
+ }
1456
+
1452
1457
  private onInvokeGetterClick(result: SDK.RemoteObject.CallFunctionResult): void {
1453
1458
  if (!result.object) {
1454
1459
  return;
@@ -1642,6 +1647,11 @@ export class ArrayGroupingTreeElement extends UI.TreeOutline.TreeElement {
1642
1647
  ObjectPropertyTreeElement.populateWithProperties(treeNode, children, false, false, linkifier);
1643
1648
  }
1644
1649
 
1650
+ override invalidateChildren(): void {
1651
+ super.invalidateChildren();
1652
+ this.#child.removeChildren();
1653
+ }
1654
+
1645
1655
  override async onpopulate(): Promise<void> {
1646
1656
  this.removeChildren();
1647
1657
  this.#child.removeChildren();
@@ -809,9 +809,14 @@ export const knownContextValues = new Set([
809
809
  'column-rule',
810
810
  'column-rule-break',
811
811
  'column-rule-color',
812
+ 'column-rule-edge-end-inset',
812
813
  'column-rule-edge-end-outset',
814
+ 'column-rule-edge-start-inset',
813
815
  'column-rule-edge-start-outset',
816
+ 'column-rule-inset',
817
+ 'column-rule-interior-end-inset',
814
818
  'column-rule-interior-end-outset',
819
+ 'column-rule-interior-start-inset',
815
820
  'column-rule-interior-start-outset',
816
821
  'column-rule-outset',
817
822
  'column-rule-style',
@@ -3167,9 +3172,14 @@ export const knownContextValues = new Set([
3167
3172
  'row-rule',
3168
3173
  'row-rule-break',
3169
3174
  'row-rule-color',
3175
+ 'row-rule-edge-end-inset',
3170
3176
  'row-rule-edge-end-outset',
3177
+ 'row-rule-edge-start-inset',
3171
3178
  'row-rule-edge-start-outset',
3179
+ 'row-rule-inset',
3180
+ 'row-rule-interior-end-inset',
3172
3181
  'row-rule-interior-end-outset',
3182
+ 'row-rule-interior-start-inset',
3173
3183
  'row-rule-interior-start-outset',
3174
3184
  'row-rule-outset',
3175
3185
  'row-rule-style',
@@ -3184,6 +3194,7 @@ export const knownContextValues = new Set([
3184
3194
  'rule',
3185
3195
  'rule-break',
3186
3196
  'rule-color',
3197
+ 'rule-inset',
3187
3198
  'rule-outset',
3188
3199
  'rule-set',
3189
3200
  'rule-set-details',
@@ -3831,6 +3842,7 @@ export const knownContextValues = new Set([
3831
3842
  'timeline.annotation-sidebar.delete',
3832
3843
  'timeline.annotations-tab',
3833
3844
  'timeline.annotations.',
3845
+ 'timeline.annotations.ai-generate-label',
3834
3846
  'timeline.annotations.create-entries-link',
3835
3847
  'timeline.annotations.create-entry-label',
3836
3848
  'timeline.annotations.create-entry-link',
package/mcp/mcp.ts CHANGED
@@ -10,6 +10,7 @@
10
10
  export * as Common from '../front_end/core/common/common.js';
11
11
  export * as I18n from '../front_end/core/i18n/i18n.js';
12
12
  export {ConnectionTransport} from '../front_end/core/protocol_client/ConnectionTransport.js';
13
+ export * as Foundation from '../front_end/foundation/foundation.js';
13
14
  export {
14
15
  PerformanceInsightFormatter
15
16
  } from '../front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.js';
@@ -30,4 +31,5 @@ export {
30
31
  IssuesManager
31
32
  } from '../front_end/models/issues_manager/IssuesManager.js';
32
33
  export {findTitleFromMarkdownAst} from '../front_end/models/issues_manager/MarkdownIssueDescription.js';
34
+ export * as TraceEngine from '../front_end/models/trace/trace.js';
33
35
  export * as Marked from '../front_end/third_party/marked/marked.js';
package/package.json CHANGED
@@ -103,5 +103,5 @@
103
103
  "flat-cache": "6.1.12"
104
104
  }
105
105
  },
106
- "version": "1.0.1541169"
106
+ "version": "1.0.1542501"
107
107
  }
@@ -1,72 +0,0 @@
1
- // Copyright 2025 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 type * as SDK from '../../core/sdk/sdk.js';
6
- import type * as Protocol from '../../generated/protocol.js';
7
-
8
- import {Issue, IssueCategory, IssueKind} from './Issue.js';
9
- import {
10
- type LazyMarkdownIssueDescription,
11
- type MarkdownIssueDescription,
12
- resolveLazyDescription,
13
- } from './MarkdownIssueDescription.js';
14
-
15
- export class UserReidentificationIssue extends Issue {
16
- #issueDetails: Protocol.Audits.UserReidentificationIssueDetails;
17
-
18
- constructor(
19
- issueDetails: Protocol.Audits.UserReidentificationIssueDetails, issuesModel: SDK.IssuesModel.IssuesModel|null) {
20
- super('UserReidentificationIssue', issuesModel);
21
- this.#issueDetails = issueDetails;
22
- }
23
-
24
- primaryKey(): string {
25
- const requestId = this.#issueDetails.request ? this.#issueDetails.request.requestId : 'no-request';
26
- return `${this.code()}-(${requestId})`;
27
- }
28
-
29
- override requests(): Iterable<Protocol.Audits.AffectedRequest> {
30
- return this.#issueDetails.request ? [this.#issueDetails.request] : [];
31
- }
32
-
33
- getCategory(): IssueCategory {
34
- return IssueCategory.OTHER;
35
- }
36
-
37
- getDescription(): MarkdownIssueDescription|null {
38
- const description = issueDescriptions.get(this.code());
39
- if (!description) {
40
- return null;
41
- }
42
- return resolveLazyDescription(description);
43
- }
44
-
45
- getKind(): IssueKind {
46
- return IssueKind.IMPROVEMENT;
47
- }
48
-
49
- static fromInspectorIssue(
50
- issuesModel: SDK.IssuesModel.IssuesModel|null,
51
- inspectorIssue: Protocol.Audits.InspectorIssue): UserReidentificationIssue[] {
52
- const userReidentificationIssueDetails = inspectorIssue.details.userReidentificationIssueDetails;
53
- if (!userReidentificationIssueDetails) {
54
- console.warn('User Reidentification issue without details received.');
55
- return [];
56
- }
57
- return [new UserReidentificationIssue(userReidentificationIssueDetails, issuesModel)];
58
- }
59
- }
60
-
61
- // Add new issue types to this map (with a unique code per type).
62
- const issueDescriptions = new Map<string, LazyMarkdownIssueDescription>([
63
- [
64
- 'UserReidentificationIssue',
65
- {
66
- file: 'userReidentificationBlocked.md',
67
- // TODO(https://g-issues.chromium.org/issues/409596758): Add
68
- // internationalized learn more link text.
69
- links: [],
70
- },
71
- ],
72
- ]);
@@ -1,5 +0,0 @@
1
- # Resources suspected of tracking users are blocked
2
-
3
- This is currently an experimental feature. In case of site breakage, you can disable it for this page via the eye icon in the URL bar or completely disable it via chrome://settings/incognito. You may also report bugs [here](userReidentificationBugReports).
4
-
5
- Chrome identifies domains known to serve content which misuses web platform APIs to re-identify users across sites. Resources from these domains are blocked when users express a preference for additional privacy. Consider removing the following resources from your site:
@@ -1,19 +0,0 @@
1
- <!--
2
- Copyright 2021 The Chromium Authors
3
- Use of this source code is governed by a BSD-style license that can be
4
- found in the LICENSE file.
5
- -->
6
- <!DOCTYPE html>
7
- <html>
8
- <head>
9
- <meta charset="UTF-8" />
10
- <meta name="viewport" content="width=device-width" />
11
- <title>Basic Markdown Image example</title>
12
- </head>
13
- <body>
14
- <div id="icon"></div>
15
- <div id="image"></div>
16
-
17
- <script type="module" src="./basic.js"></script>
18
- </body>
19
- </html>