chrome-devtools-frontend 1.0.1604514 → 1.0.1605219

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 (54) hide show
  1. package/AUTHORS +1 -0
  2. package/front_end/Tests.js +23 -7
  3. package/front_end/core/host/AidaClient.ts +114 -63
  4. package/front_end/core/host/AidaGcaTranslation.ts +6 -0
  5. package/front_end/core/host/GcaClient.ts +112 -0
  6. package/front_end/core/host/UserMetrics.ts +0 -1
  7. package/front_end/core/host/host.ts +2 -0
  8. package/front_end/core/root/ExperimentNames.ts +0 -1
  9. package/front_end/core/root/Runtime.ts +5 -0
  10. package/front_end/core/sdk/AutofillModel.ts +3 -2
  11. package/front_end/core/sdk/CSSModel.ts +3 -4
  12. package/front_end/core/sdk/CSSProperty.ts +1 -1
  13. package/front_end/core/sdk/ConsoleModel.ts +15 -13
  14. package/front_end/core/sdk/CookieModel.ts +5 -4
  15. package/front_end/core/sdk/DOMDebuggerModel.ts +20 -14
  16. package/front_end/core/sdk/DebuggerModel.ts +21 -36
  17. package/front_end/core/sdk/EventBreakpointsModel.ts +16 -6
  18. package/front_end/core/sdk/IsolateManager.ts +12 -7
  19. package/front_end/entrypoints/main/MainImpl.ts +0 -4
  20. package/front_end/models/ai_assistance/agents/PerformanceAgent.ts +31 -17
  21. package/front_end/models/ai_assistance/data_formatters/PerformanceInsightFormatter.snapshot.txt +1 -1
  22. package/front_end/models/emulation/DeviceModeModel.ts +21 -26
  23. package/front_end/models/trace/insights/LCPDiscovery.ts +14 -6
  24. package/front_end/panels/ai_assistance/AiAssistancePanel.ts +2 -17
  25. package/front_end/panels/ai_assistance/components/ChatMessage.ts +36 -20
  26. package/front_end/panels/ai_assistance/components/chatMessage.css +5 -1
  27. package/front_end/panels/application/ApplicationPanelSidebar.ts +6 -0
  28. package/front_end/panels/application/IndexedDBViews.ts +0 -9
  29. package/front_end/panels/application/WebMCPTreeElement.ts +35 -0
  30. package/front_end/panels/application/WebMCPView.ts +118 -0
  31. package/front_end/panels/application/application.ts +4 -0
  32. package/front_end/panels/application/webMCPView.css +65 -0
  33. package/front_end/panels/common/aiCodeCompletionSummaryToolbar.css +1 -1
  34. package/front_end/panels/console/consoleView.css +4 -0
  35. package/front_end/panels/elements/StylesAiCodeCompletionProvider.ts +23 -2
  36. package/front_end/panels/elements/StylesSidebarPane.ts +85 -20
  37. package/front_end/panels/elements/elementsPanel.css +14 -0
  38. package/front_end/panels/network/NetworkLogViewColumns.ts +3 -3
  39. package/front_end/panels/recorder/components/recordingView.css +0 -6
  40. package/front_end/panels/sensors/SensorsView.ts +28 -59
  41. package/front_end/panels/timeline/EventsTimelineTreeView.ts +2 -2
  42. package/front_end/panels/timeline/ThirdPartyTreeView.ts +35 -36
  43. package/front_end/panels/timeline/TimelineDetailsView.ts +47 -41
  44. package/front_end/panels/timeline/TimelineTreeView.ts +13 -13
  45. package/front_end/panels/timeline/components/LiveMetricsView.ts +839 -783
  46. package/front_end/panels/timeline/thirdPartyTreeView.css +2 -2
  47. package/front_end/ui/components/buttons/button.css +2 -2
  48. package/front_end/ui/components/text_editor/AiCodeCompletionProvider.ts +0 -11
  49. package/front_end/ui/legacy/InspectorDrawerView.ts +206 -0
  50. package/front_end/ui/legacy/InspectorView.ts +41 -91
  51. package/front_end/ui/legacy/ViewManager.ts +14 -5
  52. package/front_end/ui/legacy/inspectorDrawerTabbedPane.css +5 -0
  53. package/front_end/ui/visual_logging/KnownContextValues.ts +2 -1
  54. package/package.json +1 -1
package/AUTHORS CHANGED
@@ -58,6 +58,7 @@ Jithil p Ponnan <jithil.p@gmail.com>
58
58
  Juba Borgohain <chromiumjuba@gmail.com>
59
59
  Julian Geppert <spctstr@gmail.com>
60
60
  Junseo Yoo <joon.yoo181@berkeley.edu>
61
+ Kanishk Ranjan <kanishkranjan17@gmail.com>
61
62
  Karntino Areros <karntino.c.areros@gmail.com>
62
63
  Kohei Ueno <kohei.ueno119@gmail.com>
63
64
  Krishnal Ciccolella <ciccolella.krishnal@gmail.com>
@@ -1086,20 +1086,36 @@
1086
1086
  this.takeControl({slownessFactor: 10});
1087
1087
  };
1088
1088
 
1089
- TestSuite.prototype.waitForTestResultsAsMessage = function() {
1090
- const onMessage = event => {
1091
- if (!event.data.testOutput) {
1092
- return;
1093
- }
1094
- top.removeEventListener('message', onMessage);
1089
+ const earlyTestResults = [];
1090
+ let testResultsWaiter = null;
1091
+
1092
+ top.addEventListener('message', event => {
1093
+ if (event.data && event.data.testOutput) {
1095
1094
  const text = event.data.testOutput;
1095
+ if (testResultsWaiter) {
1096
+ testResultsWaiter(text);
1097
+ } else {
1098
+ earlyTestResults.push(text);
1099
+ }
1100
+ }
1101
+ });
1102
+
1103
+ TestSuite.prototype.waitForTestResultsAsMessage = function() {
1104
+ const handleMessage = text => {
1105
+ testResultsWaiter = null;
1096
1106
  if (text === 'PASS') {
1097
1107
  this.releaseControl();
1098
1108
  } else {
1099
1109
  this.fail(text);
1100
1110
  }
1101
1111
  };
1102
- top.addEventListener('message', onMessage);
1112
+
1113
+ if (earlyTestResults.length) {
1114
+ handleMessage(earlyTestResults.shift());
1115
+ return;
1116
+ }
1117
+
1118
+ testResultsWaiter = handleMessage;
1103
1119
  this.takeControl();
1104
1120
  };
1105
1121
 
@@ -7,12 +7,14 @@ import * as Root from '../root/root.js';
7
7
 
8
8
  import {
9
9
  AidaAccessPreconditions,
10
+ type AidaChunkResponse,
10
11
  type AidaFunctionCallResponse,
11
12
  AidaInferenceLanguage,
12
13
  type AidaRegisterClientEvent,
13
14
  ClientFeature,
14
15
  type CompletionRequest,
15
16
  type CompletionResponse,
17
+ debugLog,
16
18
  type DoConversationRequest,
17
19
  type DoConversationResponse,
18
20
  FunctionalityType,
@@ -24,7 +26,10 @@ import {
24
26
  Role,
25
27
  UserTier,
26
28
  } from './AidaClientTypes.js';
29
+ import {gcaChunkResponseToAidaChunkResponse} from './AidaGcaTranslation.js';
27
30
  import * as DispatchHttpRequestClient from './DispatchHttpRequestClient.js';
31
+ import * as GcaClient from './GcaClient.js';
32
+ import type {GenerateContentResponse} from './GcaTypes.js';
28
33
  import {InspectorFrontendHostInstance} from './InspectorFrontendHost.js';
29
34
  import type {AidaClientResult, AidaCodeCompleteResult, SyncInformation} from './InspectorFrontendHostAPI.js';
30
35
  import {bindOutputStream} from './ResourceLoader.js';
@@ -58,7 +63,17 @@ const AidaLanguageToMarkdown: Record<AidaInferenceLanguage, string> = {
58
63
  export class AidaAbortError extends Error {}
59
64
  export class AidaBlockError extends Error {}
60
65
 
66
+ interface AiStream {
67
+ write: (data: string) => Promise<void>;
68
+ close: () => Promise<void>;
69
+ read: () => Promise<string|null>;
70
+ fail: (e: Error) => void;
71
+ }
72
+
61
73
  export class AidaClient {
74
+ // Delegate client
75
+ #gcaClient = new GcaClient.GcaClient();
76
+
62
77
  static buildConsoleInsightsRequest(input: string): DoConversationRequest {
63
78
  const disallowLogging = Root.Runtime.hostConfig.aidaAvailability?.disallowLogging ?? true;
64
79
  const chromeVersion = Root.Runtime.getChromeVersion();
@@ -145,81 +160,71 @@ export class AidaClient {
145
160
  };
146
161
  })();
147
162
  const streamId = bindOutputStream(stream);
148
- DispatchHttpRequestClient
149
- .makeHttpRequest(
150
- {
151
- service: SERVICE_NAME,
152
- path: '/v1/aida:doConversation',
153
- method: 'POST',
154
- body: JSON.stringify(request),
155
- streamId,
156
- },
157
- options)
158
- .then(
159
- () => {
160
- void stream.close();
161
- },
162
- err => {
163
- if (err instanceof DispatchHttpRequestClient.DispatchHttpRequestError && err.response) {
164
- const result = err.response;
165
- if (result.statusCode === 403) {
166
- stream.fail(new Error('Server responded: permission denied'));
167
- return;
168
- }
169
- if ('error' in result && result.error) {
170
- stream.fail(new Error(`Cannot send request: ${result.error} ${result.detail || ''}`));
171
- return;
172
- }
173
- if ('netErrorName' in result && result.netErrorName === 'net::ERR_TIMED_OUT') {
174
- stream.fail(new Error('doAidaConversation timed out'));
175
- return;
176
- }
177
- if (result.statusCode !== 200) {
178
- stream.fail(new Error(`Request failed: ${JSON.stringify(result)}`));
179
- return;
180
- }
181
- }
182
- stream.fail(err);
183
- });
163
+
164
+ let response;
165
+ if (this.#gcaClient.enabled()) {
166
+ // Inline and remove the else clause after migration
167
+ response = this.#gcaClient.conversationRequest(request, streamId, options);
168
+ } else {
169
+ response = DispatchHttpRequestClient.makeHttpRequest(
170
+ {
171
+ service: SERVICE_NAME,
172
+ path: '/v1/aida:doConversation',
173
+ method: 'POST',
174
+ body: JSON.stringify(request),
175
+ streamId,
176
+ },
177
+ options);
178
+ }
179
+ response.then(
180
+ () => {
181
+ void stream.close();
182
+ },
183
+ err => {
184
+ debugLog('doConversation failed with error:', JSON.stringify(err));
185
+ if (err instanceof DispatchHttpRequestClient.DispatchHttpRequestError && err.response) {
186
+ const result = err.response;
187
+ if (result.statusCode === 403) {
188
+ stream.fail(new Error('Server responded: permission denied'));
189
+ return;
190
+ }
191
+ if ('error' in result && result.error) {
192
+ stream.fail(new Error(`Cannot send request: ${result.error} ${result.detail || ''}`));
193
+ return;
194
+ }
195
+ if ('netErrorName' in result && result.netErrorName === 'net::ERR_TIMED_OUT') {
196
+ stream.fail(new Error('doAidaConversation timed out'));
197
+ return;
198
+ }
199
+ if (result.statusCode !== 200) {
200
+ stream.fail(new Error(`Request failed: ${JSON.stringify(result)}`));
201
+ return;
202
+ }
203
+ }
204
+ stream.fail(err);
205
+ });
206
+ await (yield* this.#handleResponseStream(stream));
207
+ }
208
+
209
+ async * #handleResponseStream(stream: AiStream): AsyncGenerator<DoConversationResponse, void, void> {
184
210
  let chunk;
185
211
  const text = [];
186
212
  let inCodeChunk = false;
187
213
  const functionCalls: AidaFunctionCallResponse[] = [];
188
214
  let metadata: ResponseMetadata = {rpcGlobalId: 0};
189
215
  while ((chunk = await stream.read())) {
216
+ debugLog('doConversation stream chunk:', chunk);
190
217
  let textUpdated = false;
191
- // The AIDA response is a JSON array of objects, split at the object
192
- // boundary. Therefore each chunk may start with `[` or `,` and possibly
193
- // followed by `]`. Each chunk may include one or more objects, so we
194
- // make sure that each chunk becomes a well-formed JSON array when we
195
- // parse it by adding `[` and `]` and removing `,` where appropriate.
196
- if (!chunk.length) {
197
- continue;
198
- }
199
- if (chunk.startsWith(',')) {
200
- chunk = chunk.slice(1);
201
- }
202
- if (!chunk.startsWith('[')) {
203
- chunk = '[' + chunk;
204
- }
205
- if (!chunk.endsWith(']')) {
206
- chunk = chunk + ']';
207
- }
208
- let results;
209
- try {
210
- results = JSON.parse(chunk);
211
- } catch (error) {
212
- throw new Error('Cannot parse chunk: ' + chunk, {cause: error});
213
- }
218
+ const results = this.#parseAndTranslate(chunk);
214
219
 
215
220
  for (const result of results) {
216
- if ('metadata' in result) {
221
+ if (result.metadata) {
217
222
  metadata = result.metadata;
218
223
  if (metadata?.attributionMetadata?.attributionAction === RecitationAction.BLOCK) {
219
224
  throw new AidaBlockError();
220
225
  }
221
226
  }
222
- if ('textChunk' in result) {
227
+ if (result.textChunk) {
223
228
  if (inCodeChunk) {
224
229
  text.push(CODE_CHUNK_SEPARATOR());
225
230
  inCodeChunk = false;
@@ -227,7 +232,7 @@ export class AidaClient {
227
232
 
228
233
  text.push(result.textChunk.text);
229
234
  textUpdated = true;
230
- } else if ('codeChunk' in result) {
235
+ } else if (result.codeChunk) {
231
236
  if (!inCodeChunk) {
232
237
  const language = AidaLanguageToMarkdown[result.codeChunk.inferenceLanguage as AidaInferenceLanguage] ?? '';
233
238
  text.push(CODE_CHUNK_SEPARATOR(language));
@@ -236,7 +241,7 @@ export class AidaClient {
236
241
 
237
242
  text.push(result.codeChunk.code);
238
243
  textUpdated = true;
239
- } else if ('functionCallChunk' in result) {
244
+ } else if (result.functionCallChunk) {
240
245
  functionCalls.push({
241
246
  name: result.functionCallChunk.functionCall.name,
242
247
  args: result.functionCallChunk.functionCall.args,
@@ -264,6 +269,40 @@ export class AidaClient {
264
269
  };
265
270
  }
266
271
 
272
+ #parseAndTranslate(chunk: string): AidaChunkResponse[] {
273
+ const results: AidaChunkResponse[] = this.#parseStreamChunk(chunk);
274
+ if (this.#gcaClient.enabled()) {
275
+ return (results as GenerateContentResponse[]).flatMap(gcaChunkResponseToAidaChunkResponse);
276
+ }
277
+ return results as AidaChunkResponse[];
278
+ }
279
+
280
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
281
+ #parseStreamChunk(chunk: string): any {
282
+ // The streamed response is a JSON array of objects, split at the object
283
+ // boundary. Therefore each chunk may start with `[` or `,` and possibly
284
+ // followed by `]`. Each chunk may include one or more objects, so we
285
+ // make sure that each chunk becomes a well-formed JSON array when we
286
+ // parse it by adding `[` and `]` and removing `,` where appropriate.
287
+ if (!chunk.length) {
288
+ return [];
289
+ }
290
+ if (chunk.startsWith(',')) {
291
+ chunk = chunk.slice(1);
292
+ }
293
+ if (!chunk.startsWith('[')) {
294
+ chunk = '[' + chunk;
295
+ }
296
+ if (!chunk.endsWith(']')) {
297
+ chunk = chunk + ']';
298
+ }
299
+ try {
300
+ return JSON.parse(chunk);
301
+ } catch (error) {
302
+ throw new Error('Cannot parse chunk: ' + chunk, {cause: error});
303
+ }
304
+ }
305
+
267
306
  registerClientEvent(clientEvent: AidaRegisterClientEvent): Promise<AidaClientResult> {
268
307
  // Disable logging for now.
269
308
  // For context, see b/454563259#comment35.
@@ -272,7 +311,11 @@ export class AidaClient {
272
311
  clientEvent.disable_user_content_logging = true;
273
312
  }
274
313
 
314
+ if (this.#gcaClient.enabled()) {
315
+ return this.#gcaClient.registerClientEvent(clientEvent);
316
+ }
275
317
  const {promise, resolve} = Promise.withResolvers<AidaClientResult>();
318
+
276
319
  InspectorFrontendHostInstance.registerAidaClientEvent(
277
320
  JSON.stringify({
278
321
  client: CLIENT_NAME,
@@ -297,6 +340,9 @@ export class AidaClient {
297
340
  request.metadata.disable_user_content_logging = true;
298
341
  }
299
342
 
343
+ if (this.#gcaClient.enabled()) {
344
+ return await this.#gcaClient.completeCode(request);
345
+ }
300
346
  const {promise, resolve} = Promise.withResolvers<AidaCodeCompleteResult>();
301
347
  InspectorFrontendHostInstance.aidaCodeComplete(JSON.stringify(request), resolve);
302
348
  const completeCodeResult = await promise;
@@ -348,6 +394,11 @@ export class AidaClient {
348
394
  if (Root.Runtime.hostConfig.devToolsGeminiRebranding?.enabled) {
349
395
  request.metadata.disable_user_content_logging = true;
350
396
  }
397
+
398
+ if (this.#gcaClient.enabled()) {
399
+ // Inline and remove the else clause after migration
400
+ return await this.#gcaClient.generateCode(request, options);
401
+ }
351
402
  const response = await DispatchHttpRequestClient.makeHttpRequest<GenerateCodeResponse>(
352
403
  {
353
404
  service: SERVICE_NAME,
@@ -254,6 +254,12 @@ function buildLabels(request: AidaRequest, gcaRequest: GCA.GenerateContentReques
254
254
  if (options?.expect_code_output !== undefined) {
255
255
  labels['expect_code_output'] = String(options.expect_code_output);
256
256
  }
257
+ if (request.metadata.disable_user_content_logging !== undefined) {
258
+ labels['disable_user_content_logging'] = String(request.metadata.disable_user_content_logging);
259
+ }
260
+ if (request.metadata.client_version) {
261
+ labels['client_version'] = request.metadata.client_version;
262
+ }
257
263
 
258
264
  if (Object.keys(labels).length > 0) {
259
265
  gcaRequest.labels = labels;
@@ -0,0 +1,112 @@
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 Root from '../root/root.js';
6
+
7
+ import {
8
+ type AidaRegisterClientEvent,
9
+ type CompletionRequest,
10
+ type CompletionResponse,
11
+ debugLog,
12
+ type DoConversationRequest,
13
+ type GenerateCodeRequest,
14
+ type GenerateCodeResponse
15
+ } from './AidaClientTypes.js';
16
+ import {
17
+ aidaCompletionRequestToGcaRequest,
18
+ aidaDoConversationRequestToGcaRequest,
19
+ aidaEventToGcaTelemetryRequest,
20
+ aidaGenerateCodeRequestToGcaRequest,
21
+ gcaResponseToAidaCompletionResponse,
22
+ gcaResponseToAidaGenerateCodeResponse
23
+ } from './AidaGcaTranslation.js';
24
+ import * as DispatchHttpRequestClient from './DispatchHttpRequestClient.js';
25
+ import type {GenerateContentRequest, GenerateContentResponse} from './GcaTypes.js';
26
+ import type {AidaClientResult} from './InspectorFrontendHostAPI.js';
27
+
28
+ const SERVICE_NAME = 'gcaService';
29
+
30
+ const ENDPOINTS = {
31
+ CONTENT: '/v1beta:generateContent',
32
+ SEND_TELEMETRY: '/v1beta:sendTelemetry',
33
+ STREAM_CONTENT: '/v1beta:streamGenerateContent',
34
+ };
35
+
36
+ export class GcaClient {
37
+ enabled(): boolean|undefined {
38
+ return Root.Runtime.hostConfig.devToolsUseGcaApi?.enabled;
39
+ }
40
+
41
+ async conversationRequest(request: DoConversationRequest, streamId: number, options?: {signal?: AbortSignal}):
42
+ Promise<void> {
43
+ try {
44
+ const gcaRequest = aidaDoConversationRequestToGcaRequest(request);
45
+ const response = await DispatchHttpRequestClient.makeHttpRequest<GenerateContentResponse>(
46
+ {
47
+ service: SERVICE_NAME,
48
+ path: ENDPOINTS.STREAM_CONTENT,
49
+ method: 'POST',
50
+ body: JSON.stringify(gcaRequest),
51
+ streamId,
52
+ },
53
+ options);
54
+ debugLog('GCA conversation request succeeded:', JSON.stringify(request), JSON.stringify(response));
55
+ } catch (err) {
56
+ debugLog('GCA request failed:', JSON.stringify(request), err);
57
+ throw err;
58
+ }
59
+ }
60
+
61
+ registerClientEvent(clientEvent: AidaRegisterClientEvent): Promise<AidaClientResult> {
62
+ const gcaEvent = aidaEventToGcaTelemetryRequest(clientEvent);
63
+ const response = DispatchHttpRequestClient.makeHttpRequest({
64
+ service: SERVICE_NAME,
65
+ path: ENDPOINTS.SEND_TELEMETRY,
66
+ method: 'POST',
67
+ body: JSON.stringify(gcaEvent),
68
+ });
69
+ return response.then(
70
+ result => {
71
+ debugLog('GCA register event succeeded:', JSON.stringify(gcaEvent), JSON.stringify(result));
72
+ return {} as AidaClientResult;
73
+ },
74
+ err => {
75
+ debugLog('GCA register event failed:', JSON.stringify(gcaEvent), err);
76
+ return {error: JSON.stringify(err)} as AidaClientResult;
77
+ });
78
+ }
79
+
80
+ async completeCode(request: CompletionRequest): Promise<CompletionResponse|null> {
81
+ const gcaRequest = aidaCompletionRequestToGcaRequest(request);
82
+ const result = await this.#requestContent(gcaRequest);
83
+ const aidaResult = result ? gcaResponseToAidaCompletionResponse(result) : null;
84
+ return aidaResult;
85
+ }
86
+
87
+ async generateCode(request: GenerateCodeRequest, options?: {signal?: AbortSignal}):
88
+ Promise<GenerateCodeResponse|null> {
89
+ const gcaRequest = aidaGenerateCodeRequestToGcaRequest(request);
90
+ const result = await this.#requestContent(gcaRequest, options);
91
+ return result ? gcaResponseToAidaGenerateCodeResponse(result) : null;
92
+ }
93
+
94
+ async #requestContent(request: GenerateContentRequest, options?: {signal?: AbortSignal}):
95
+ Promise<GenerateContentResponse|null> {
96
+ try {
97
+ const response = await DispatchHttpRequestClient.makeHttpRequest<GenerateContentResponse>(
98
+ {
99
+ service: SERVICE_NAME,
100
+ path: ENDPOINTS.CONTENT,
101
+ method: 'POST',
102
+ body: JSON.stringify(request),
103
+ },
104
+ options);
105
+ debugLog('GCA request succeeded:', JSON.stringify(request), JSON.stringify(response));
106
+ return response;
107
+ } catch (err) {
108
+ debugLog('GCA request failed:', JSON.stringify(request), err);
109
+ return null;
110
+ }
111
+ }
112
+ }
@@ -821,7 +821,6 @@ export enum DevtoolsExperiments {
821
821
  apca = 39,
822
822
  'font-editor' = 41,
823
823
  'full-accessibility-tree' = 42,
824
- 'experimental-cookie-features' = 45,
825
824
  'instrumentation-breakpoints' = 61,
826
825
  'use-source-map-scopes' = 76,
827
826
  'timeline-debug-mode' = 93,
@@ -5,6 +5,7 @@
5
5
  import * as AidaClient from './AidaClient.js';
6
6
  import * as AidaGcaTranslation from './AidaGcaTranslation.js';
7
7
  import * as DispatchHttpRequestClient from './DispatchHttpRequestClient.js';
8
+ import * as GcaClient from './GcaClient.js';
8
9
  import * as GcaTypes from './GcaTypes.js';
9
10
  import * as GdpClient from './GdpClient.js';
10
11
  import * as InspectorFrontendHost from './InspectorFrontendHost.js';
@@ -17,6 +18,7 @@ export {
17
18
  AidaClient,
18
19
  AidaGcaTranslation,
19
20
  DispatchHttpRequestClient,
21
+ GcaClient,
20
22
  GcaTypes,
21
23
  GdpClient,
22
24
  InspectorFrontendHost,
@@ -14,7 +14,6 @@ export enum ExperimentName {
14
14
  APCA = 'apca',
15
15
  FONT_EDITOR = 'font-editor',
16
16
  FULL_ACCESSIBILITY_TREE = 'full-accessibility-tree',
17
- EXPERIMENTAL_COOKIE_FEATURES = 'experimental-cookie-features',
18
17
  INSTRUMENTATION_BREAKPOINTS = 'instrumentation-breakpoints',
19
18
  USE_SOURCE_MAP_SCOPES = 'use-source-map-scopes',
20
19
  TIMELINE_DEBUG_MODE = 'timeline-debug-mode',
@@ -602,6 +602,10 @@ interface ConsoleInsightsTeasers {
602
602
  allowWithoutGpu: boolean;
603
603
  }
604
604
 
605
+ interface UseGcaApi {
606
+ enabled: boolean;
607
+ }
608
+
605
609
  interface DevToolsProtocolMonitor {
606
610
  enabled: boolean;
607
611
  }
@@ -662,6 +666,7 @@ export type HostConfig = Platform.TypeScriptUtilities.RecursivePartial<{
662
666
  devToolsGeminiRebranding: HostConfigGeminiRebranding,
663
667
  devToolsProtocolMonitor: DevToolsProtocolMonitor,
664
668
  devToolsWebMCPSupport: DevToolsWebMCPSupport,
669
+ devToolsUseGcaApi: UseGcaApi,
665
670
  }>;
666
671
 
667
672
  /**
@@ -2,7 +2,7 @@
2
2
  // Use of this source code is governed by a BSD-style license that can be
3
3
  // found in the LICENSE file.
4
4
 
5
- import * as Common from '../../core/common/common.js';
5
+ import type * as Common from '../../core/common/common.js';
6
6
  import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js';
7
7
  import type * as Protocol from '../../generated/protocol.js';
8
8
  import * as Host from '../host/host.js';
@@ -19,8 +19,9 @@ export class AutofillModel extends SDKModel<EventTypes> implements ProtocolProxy
19
19
  super(target);
20
20
 
21
21
  this.agent = target.autofillAgent();
22
+ const settings = this.target().targetManager().settings;
22
23
  this.#showTestAddressesInAutofillMenu =
23
- Common.Settings.Settings.instance().createSetting('show-test-addresses-in-autofill-menu-on-event', false);
24
+ settings.createSetting('show-test-addresses-in-autofill-menu-on-event', false);
24
25
  this.#showTestAddressesInAutofillMenu.addChangeListener(this.#setTestAddresses, this);
25
26
  target.registerAutofillDispatcher(this);
26
27
  this.enable();
@@ -79,10 +79,9 @@ export class CSSModel extends SDKModel<EventTypes> {
79
79
  void this.enable();
80
80
  }
81
81
 
82
- this.#sourceMapManager.setEnabled(
83
- Common.Settings.Settings.instance().moduleSetting<boolean>('css-source-maps-enabled').get());
84
- Common.Settings.Settings.instance()
85
- .moduleSetting<boolean>('css-source-maps-enabled')
82
+ const settings = this.target().targetManager().settings;
83
+ this.#sourceMapManager.setEnabled(settings.moduleSetting<boolean>('css-source-maps-enabled').get());
84
+ settings.moduleSetting<boolean>('css-source-maps-enabled')
86
85
  .addChangeListener(event => this.#sourceMapManager.setEnabled(event.data));
87
86
  }
88
87
 
@@ -225,7 +225,7 @@ export class CSSProperty extends Common.ObjectWrapper.ObjectWrapper<EventTypes>
225
225
  const range = this.range.relativeTo(this.ownerStyle.range.startLine, this.ownerStyle.range.startColumn);
226
226
  const indentation = this.ownerStyle.cssText ?
227
227
  this.detectIndentation(this.ownerStyle.cssText) :
228
- Common.Settings.Settings.instance().moduleSetting('text-editor-indent').get();
228
+ this.ownerStyle.cssModel().target().targetManager().settings.moduleSetting('text-editor-indent').get();
229
229
  const endIndentation = this.ownerStyle.cssText ? indentation.substring(0, this.ownerStyle.range.endColumn) : '';
230
230
  const text = new TextUtils.Text.Text(this.ownerStyle.cssText || '');
231
231
  const newStyleText = text.replaceRange(range, Platform.StringUtilities.sprintf(';%s;', propertyText));
@@ -153,7 +153,7 @@ export class ConsoleModel extends SDKModel<EventTypes> {
153
153
  replMode: true,
154
154
  allowUnsafeEvalBlockedByCSP: false,
155
155
  },
156
- Common.Settings.Settings.instance().moduleSetting('console-user-activation-eval').get(),
156
+ this.target().targetManager().settings.moduleSetting('console-user-activation-eval').get(),
157
157
  /* awaitPromise */ false);
158
158
  Host.userMetrics.actionTaken(Host.UserMetrics.Action.ConsoleEvaluated);
159
159
  if ('error' in result) {
@@ -284,7 +284,8 @@ export class ConsoleModel extends SDKModel<EventTypes> {
284
284
  }
285
285
 
286
286
  private clearIfNecessary(): void {
287
- if (!Common.Settings.Settings.instance().moduleSetting('preserve-console-log').get()) {
287
+ const settings = this.target().targetManager().settings;
288
+ if (!settings.moduleSetting('preserve-console-log').get()) {
288
289
  this.clear();
289
290
  }
290
291
  ++this.#pageLoadSequenceNumber;
@@ -292,7 +293,8 @@ export class ConsoleModel extends SDKModel<EventTypes> {
292
293
 
293
294
  private primaryPageChanged(
294
295
  event: Common.EventTarget.EventTargetEvent<{frame: ResourceTreeFrame, type: PrimaryPageChangeType}>): void {
295
- if (Common.Settings.Settings.instance().moduleSetting('preserve-console-log').get()) {
296
+ const settings = this.target().targetManager().settings;
297
+ if (settings.moduleSetting('preserve-console-log').get()) {
296
298
  const {frame} = event.data;
297
299
  if (frame.backForwardCacheDetails.restoredFromCache) {
298
300
  Common.Console.Console.instance().log(i18nString(UIStrings.bfcacheNavigation, {PH1: frame.url}));
@@ -353,25 +355,25 @@ export class ConsoleModel extends SDKModel<EventTypes> {
353
355
  }
354
356
 
355
357
  // messages[] are not ordered by timestamp.
356
- static allMessagesUnordered(): ConsoleMessage[] {
358
+ static allMessagesUnordered(targetManager: TargetManager = TargetManager.instance()): ConsoleMessage[] {
357
359
  const messages = [];
358
- for (const target of TargetManager.instance().targets()) {
360
+ for (const target of targetManager.targets()) {
359
361
  const targetMessages = target.model(ConsoleModel)?.messages() || [];
360
362
  messages.push(...targetMessages);
361
363
  }
362
364
  return messages;
363
365
  }
364
366
 
365
- static requestClearMessages(): void {
366
- for (const logModel of TargetManager.instance().models(LogModel)) {
367
+ static requestClearMessages(targetManager: TargetManager = TargetManager.instance()): void {
368
+ for (const logModel of targetManager.models(LogModel)) {
367
369
  logModel.requestClear();
368
370
  }
369
- for (const runtimeModel of TargetManager.instance().models(RuntimeModel)) {
371
+ for (const runtimeModel of targetManager.models(RuntimeModel)) {
370
372
  runtimeModel.discardConsoleEntries();
371
373
  // Runtime.discardConsoleEntries implies Runtime.releaseObjectGroup('console').
372
374
  runtimeModel.releaseObjectGroup('live-expression');
373
375
  }
374
- for (const target of TargetManager.instance().targets()) {
376
+ for (const target of targetManager.targets()) {
375
377
  target.model(ConsoleModel)?.clear();
376
378
  }
377
379
  }
@@ -390,9 +392,9 @@ export class ConsoleModel extends SDKModel<EventTypes> {
390
392
  return this.#errors;
391
393
  }
392
394
 
393
- static allErrors(): number {
395
+ static allErrors(targetManager: TargetManager = TargetManager.instance()): number {
394
396
  let errors = 0;
395
- for (const target of TargetManager.instance().targets()) {
397
+ for (const target of targetManager.targets()) {
396
398
  errors += target.model(ConsoleModel)?.errors() || 0;
397
399
  }
398
400
  return errors;
@@ -402,9 +404,9 @@ export class ConsoleModel extends SDKModel<EventTypes> {
402
404
  return this.#warnings;
403
405
  }
404
406
 
405
- static allWarnings(): number {
407
+ static allWarnings(targetManager: TargetManager = TargetManager.instance()): number {
406
408
  let warnings = 0;
407
- for (const target of TargetManager.instance().targets()) {
409
+ for (const target of targetManager.targets()) {
408
410
  warnings += target.model(ConsoleModel)?.warnings() || 0;
409
411
  }
410
412
  return warnings;
@@ -99,8 +99,9 @@ export class CookieModel extends SDKModel<EventTypes> {
99
99
  if (cookie.expires()) {
100
100
  expires = Math.floor(Date.parse(`${cookie.expires()}`) / 1000);
101
101
  }
102
- const enabled =
103
- Root.Runtime.experiments.isEnabled(Root.ExperimentNames.ExperimentName.EXPERIMENTAL_COOKIE_FEATURES);
102
+ const schemeBindingEnabled =
103
+ Boolean(Root.Runtime.hostConfig.devToolsEnableOriginBoundCookies?.schemeBindingEnabled);
104
+ const portBindingEnabled = Boolean(Root.Runtime.hostConfig.devToolsEnableOriginBoundCookies?.portBindingEnabled);
104
105
  const preserveUnset = (scheme: Protocol.Network.CookieSourceScheme): Protocol.Network.CookieSourceScheme.Unset|
105
106
  undefined => scheme === Protocol.Network.CookieSourceScheme.Unset ? scheme : undefined;
106
107
  const protocolCookie = {
@@ -115,8 +116,8 @@ export class CookieModel extends SDKModel<EventTypes> {
115
116
  expires,
116
117
  priority: cookie.priority(),
117
118
  partitionKey: cookie.partitionKey(),
118
- sourceScheme: enabled ? cookie.sourceScheme() : preserveUnset(cookie.sourceScheme()),
119
- sourcePort: enabled ? cookie.sourcePort() : undefined,
119
+ sourceScheme: schemeBindingEnabled ? cookie.sourceScheme() : preserveUnset(cookie.sourceScheme()),
120
+ sourcePort: portBindingEnabled ? cookie.sourcePort() : undefined,
120
121
  };
121
122
  const response = await this.target().networkAgent().invoke_setCookie(protocolCookie);
122
123
  const error = response.getError();