chrome-devtools-mcp 0.9.0 → 0.10.0

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 (66) hide show
  1. package/README.md +14 -9
  2. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Console.js +1 -8
  3. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/ParsedURL.js +10 -20
  4. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/SegmentedRange.js +1 -2
  5. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/Settings.js +3 -0
  6. package/build/node_modules/chrome-devtools-frontend/front_end/core/common/StringOutputStream.js +1 -4
  7. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/AidaClient.js +19 -0
  8. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/DispatchHttpRequestClient.js +54 -0
  9. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/GdpClient.js +6 -51
  10. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHost.js +2 -2
  11. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/InspectorFrontendHostAPI.js +32 -29
  12. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/UserMetrics.js +14 -6
  13. package/build/node_modules/chrome-devtools-frontend/front_end/core/host/host.js +2 -1
  14. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/CDPConnection.js +17 -0
  15. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/InspectorBackend.js +68 -188
  16. package/build/node_modules/chrome-devtools-frontend/front_end/core/protocol_client/protocol_client.js +2 -1
  17. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/AnimationModel.js +1 -2
  18. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSMatchedStyles.js +3 -3
  19. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSModel.js +1 -1
  20. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSProperty.js +3 -6
  21. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSPropertyParserMatchers.js +14 -10
  22. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/CSSStyleDeclaration.js +4 -4
  23. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/ChildTargetManager.js +5 -33
  24. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/Connections.js +9 -46
  25. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DOMModel.js +1 -0
  26. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/DebuggerModel.js +1 -2
  27. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/EnhancedTracesParser.js +17 -3
  28. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkManager.js +59 -37
  29. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/NetworkRequest.js +5 -0
  30. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/RehydratingConnection.js +102 -4
  31. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/SourceMap.js +2 -3
  32. package/build/node_modules/chrome-devtools-frontend/front_end/core/sdk/sdk-meta.js +8 -1
  33. package/build/node_modules/chrome-devtools-frontend/front_end/generated/InspectorBackendCommands.js +1 -39
  34. package/build/node_modules/chrome-devtools-frontend/front_end/generated/SupportedCSSProperties.js +58 -0
  35. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/data_formatters/PerformanceTraceFormatter.js +46 -45
  36. package/build/node_modules/chrome-devtools-frontend/front_end/models/ai_assistance/performance/AIContext.js +10 -25
  37. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/CompilerScriptMapping.js +1 -1
  38. package/build/node_modules/chrome-devtools-frontend/front_end/models/bindings/DebuggerWorkspaceBinding.js +1 -1
  39. package/build/node_modules/chrome-devtools-frontend/front_end/models/cpu_profile/ProfileTreeModel.js +6 -7
  40. package/build/node_modules/chrome-devtools-frontend/front_end/models/stack_trace/StackTraceModel.js +1 -1
  41. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/handlers/NetworkRequestsHandler.js +12 -3
  42. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace/types/TraceEvents.js +3 -0
  43. package/build/node_modules/chrome-devtools-frontend/front_end/models/trace_source_maps_resolver/SourceMapsResolver.js +1 -1
  44. package/build/node_modules/chrome-devtools-frontend/mcp/mcp.js +14 -0
  45. package/build/src/DevToolsConnectionAdapter.js +1 -0
  46. package/build/src/DevtoolsUtils.js +44 -0
  47. package/build/src/McpContext.js +117 -7
  48. package/build/src/McpResponse.js +32 -21
  49. package/build/src/PageCollector.js +21 -9
  50. package/build/src/browser.js +8 -8
  51. package/build/src/cli.js +8 -3
  52. package/build/src/formatters/networkFormatter.js +2 -2
  53. package/build/src/formatters/snapshotFormatter.js +18 -6
  54. package/build/src/main.js +7 -2
  55. package/build/src/third_party/THIRD_PARTY_NOTICES +72 -52
  56. package/build/src/third_party/index.js +12684 -6052
  57. package/build/src/tools/emulation.js +37 -44
  58. package/build/src/tools/input.js +36 -6
  59. package/build/src/tools/network.js +27 -5
  60. package/build/src/tools/pages.js +59 -33
  61. package/build/src/tools/performance.js +5 -2
  62. package/build/src/tools/screenshot.js +2 -1
  63. package/build/src/tools/snapshot.js +13 -4
  64. package/build/src/trace-processing/parse.js +6 -16
  65. package/build/src/utils/keyboard.js +291 -0
  66. package/package.json +7 -6
@@ -6,7 +6,7 @@ import * as Common from '../common/common.js';
6
6
  import * as Host from '../host/host.js';
7
7
  import * as ProtocolClient from '../protocol_client/protocol_client.js';
8
8
  import * as Root from '../root/root.js';
9
- import { RehydratingConnection } from './RehydratingConnection.js';
9
+ import { RehydratingConnectionTransport } from './RehydratingConnection.js';
10
10
  const UIStrings = {
11
11
  /**
12
12
  * @description Text on the remote debugging window to indicate the connection is lost
@@ -66,7 +66,7 @@ export class MainConnection {
66
66
  }
67
67
  }
68
68
  }
69
- export class WebSocketConnection {
69
+ export class WebSocketTransport {
70
70
  #socket;
71
71
  onMessage = null;
72
72
  #onDisconnect = null;
@@ -150,7 +150,7 @@ export class WebSocketConnection {
150
150
  });
151
151
  }
152
152
  }
153
- export class StubConnection {
153
+ export class StubTransport {
154
154
  onMessage = null;
155
155
  #onDisconnect = null;
156
156
  setOnMessage(onMessage) {
@@ -166,7 +166,7 @@ export class StubConnection {
166
166
  const messageObject = JSON.parse(message);
167
167
  const error = {
168
168
  message: 'This is a stub connection, can\'t dispatch message.',
169
- code: ProtocolClient.InspectorBackend.DevToolsStubErrorCode,
169
+ code: ProtocolClient.CDPConnection.CDPErrorStatus.DEVTOOLS_STUB_ERROR,
170
170
  data: messageObject,
171
171
  };
172
172
  if (this.onMessage) {
@@ -181,62 +181,25 @@ export class StubConnection {
181
181
  this.onMessage = null;
182
182
  }
183
183
  }
184
- export class ParallelConnection {
185
- #connection;
186
- #sessionId;
187
- onMessage = null;
188
- #onDisconnect = null;
189
- constructor(connection, sessionId) {
190
- this.#connection = connection;
191
- this.#sessionId = sessionId;
192
- }
193
- setOnMessage(onMessage) {
194
- this.onMessage = onMessage;
195
- }
196
- setOnDisconnect(onDisconnect) {
197
- this.#onDisconnect = onDisconnect;
198
- }
199
- getOnDisconnect() {
200
- return this.#onDisconnect;
201
- }
202
- sendRawMessage(message) {
203
- const messageObject = JSON.parse(message);
204
- // If the message isn't for a specific session, it must be for the root session.
205
- if (!messageObject.sessionId) {
206
- messageObject.sessionId = this.#sessionId;
207
- }
208
- this.#connection.sendRawMessage(JSON.stringify(messageObject));
209
- }
210
- getSessionId() {
211
- return this.#sessionId;
212
- }
213
- async disconnect() {
214
- if (this.#onDisconnect) {
215
- this.#onDisconnect.call(null, 'force disconnect');
216
- }
217
- this.#onDisconnect = null;
218
- this.onMessage = null;
219
- }
220
- }
221
184
  export async function initMainConnection(createRootTarget, onConnectionLost) {
222
- ProtocolClient.ConnectionTransport.ConnectionTransport.setFactory(createMainConnection.bind(null, onConnectionLost));
185
+ ProtocolClient.ConnectionTransport.ConnectionTransport.setFactory(createMainTransport.bind(null, onConnectionLost));
223
186
  await createRootTarget();
224
187
  Host.InspectorFrontendHost.InspectorFrontendHostInstance.connectionReady();
225
188
  }
226
- function createMainConnection(onConnectionLost) {
189
+ function createMainTransport(onConnectionLost) {
227
190
  if (Root.Runtime.Runtime.isTraceApp()) {
228
- return new RehydratingConnection(onConnectionLost);
191
+ return new RehydratingConnectionTransport(onConnectionLost);
229
192
  }
230
193
  const wsParam = Root.Runtime.Runtime.queryParam('ws');
231
194
  const wssParam = Root.Runtime.Runtime.queryParam('wss');
232
195
  if (wsParam || wssParam) {
233
196
  const ws = (wsParam ? `ws://${wsParam}` : `wss://${wssParam}`);
234
- return new WebSocketConnection(ws, onConnectionLost);
197
+ return new WebSocketTransport(ws, onConnectionLost);
235
198
  }
236
199
  const notEmbeddedOrWs = Host.InspectorFrontendHost.InspectorFrontendHostInstance.isHostedMode();
237
200
  if (notEmbeddedOrWs) {
238
201
  // eg., hosted mode (e.g. `http://localhost:9222/devtools/inspector.html`) without a WebSocket URL,
239
- return new StubConnection();
202
+ return new StubTransport();
240
203
  }
241
204
  return new MainConnection();
242
205
  }
@@ -509,6 +509,7 @@ export class DOMNode {
509
509
  });
510
510
  }
511
511
  async getSubtree(depth, pierce) {
512
+ console.assert(depth > 0, 'Do not fetch an infinite subtree to avoid crashing the renderer for large documents');
512
513
  const response = await this.#agent.invoke_requestChildNodes({ nodeId: this.id, depth, pierce });
513
514
  return response.getError() ? null : this.childrenInternal;
514
515
  }
@@ -1089,14 +1089,13 @@ export class Scope {
1089
1089
  #name;
1090
1090
  #ordinal;
1091
1091
  #locationRange;
1092
- #object;
1092
+ #object = null;
1093
1093
  constructor(callFrame, ordinal) {
1094
1094
  this.#callFrame = callFrame;
1095
1095
  this.#payload = callFrame.getPayload().scopeChain[ordinal];
1096
1096
  this.#type = this.#payload.type;
1097
1097
  this.#name = this.#payload.name;
1098
1098
  this.#ordinal = ordinal;
1099
- this.#object = null;
1100
1099
  const start = this.#payload.startLocation ? Location.fromPayload(callFrame.debuggerModel, this.#payload.startLocation) : null;
1101
1100
  const end = this.#payload.endLocation ? Location.fromPayload(callFrame.debuggerModel, this.#payload.endLocation) : null;
1102
1101
  if (start && end && start.scriptId === end.scriptId) {
@@ -14,6 +14,7 @@ export class EnhancedTracesParser {
14
14
  #targets = [];
15
15
  #executionContexts = [];
16
16
  #scripts = [];
17
+ #resources = [];
17
18
  static enhancedTraceVersion = 1;
18
19
  constructor(trace) {
19
20
  this.#trace = trace;
@@ -34,6 +35,9 @@ export class EnhancedTracesParser {
34
35
  if (frame.url === 'about:blank') {
35
36
  continue;
36
37
  }
38
+ if (!frame.isInPrimaryMainFrame) {
39
+ continue;
40
+ }
37
41
  const frameId = frame.frame;
38
42
  if (!this.#targets.find(target => target.targetId === frameId)) {
39
43
  const frameType = frame.isOutermostMainFrame ? 'page' : 'iframe';
@@ -184,7 +188,8 @@ export class EnhancedTracesParser {
184
188
  // this fake event. Would avoid a lot of wasteful (de)serialization. Maybe add SDK.Script.hydratedSourceMap.
185
189
  this.resolveSourceMap(script);
186
190
  }
187
- return this.groupContextsAndScriptsUnderTarget(this.#targets, this.#executionContexts, this.#scripts);
191
+ this.#resources = this.#trace.metadata.resources ?? [];
192
+ return this.groupContextsAndScriptsUnderTarget(this.#targets, this.#executionContexts, this.#scripts, this.#resources);
188
193
  }
189
194
  resolveSourceMap(script) {
190
195
  if (script.sourceMapURL?.startsWith('data:')) {
@@ -253,7 +258,7 @@ export class EnhancedTracesParser {
253
258
  isFunctionCallEvent(event) {
254
259
  return this.isTraceEvent(event) && event.cat === 'devtools.timeline' && event.name === 'FunctionCall';
255
260
  }
256
- groupContextsAndScriptsUnderTarget(targets, executionContexts, scripts) {
261
+ groupContextsAndScriptsUnderTarget(targets, executionContexts, scripts, resources) {
257
262
  const data = [];
258
263
  const targetIds = new Set();
259
264
  const targetToExecutionContexts = new Map();
@@ -262,11 +267,13 @@ export class EnhancedTracesParser {
262
267
  const executionContextIsolateToTarget = new Map();
263
268
  const targetToScripts = new Map();
264
269
  const orphanScripts = [];
270
+ const targetToResources = new Map();
265
271
  // Initialize all the mapping needed
266
272
  for (const target of targets) {
267
273
  targetIds.add(target.targetId);
268
274
  targetToExecutionContexts.set(target.targetId, []);
269
275
  targetToScripts.set(target.targetId, []);
276
+ targetToResources.set(target.targetId, []);
270
277
  }
271
278
  // Put all of the known execution contexts under respective targets
272
279
  for (const executionContext of executionContexts) {
@@ -318,12 +325,19 @@ export class EnhancedTracesParser {
318
325
  console.error('Script can\'t be linked to any target', orphanScript);
319
326
  }
320
327
  }
328
+ for (const resource of resources) {
329
+ const frameId = resource.frame;
330
+ if (targetIds.has(frameId)) {
331
+ targetToResources.get(frameId)?.push(resource);
332
+ }
333
+ }
321
334
  // Now all the scripts are linked to a target, we want to make sure all the scripts are pointing to a valid
322
335
  // execution context. If not, we will create an artificial execution context for the script
323
336
  for (const target of targets) {
324
337
  const targetId = target.targetId;
325
338
  const executionContexts = targetToExecutionContexts.get(targetId) || [];
326
339
  const scripts = targetToScripts.get(targetId) || [];
340
+ const resources = targetToResources.get(targetId) || [];
327
341
  for (const script of scripts) {
328
342
  if (!executionContexts.find(context => context.id === script.executionContextId)) {
329
343
  const artificialContext = {
@@ -343,7 +357,7 @@ export class EnhancedTracesParser {
343
357
  }
344
358
  }
345
359
  // Finally, we put all the information into the data structure we want to return as.
346
- data.push({ target, executionContexts, scripts });
360
+ data.push({ target, executionContexts, scripts, resources });
347
361
  }
348
362
  return data;
349
363
  }
@@ -824,7 +824,7 @@ export class NetworkDispatcher {
824
824
  }
825
825
  requestIntercepted({}) {
826
826
  }
827
- requestWillBeSentExtraInfo({ requestId, associatedCookies, headers, clientSecurityState, connectTiming, siteHasCookieInOtherPartition }) {
827
+ requestWillBeSentExtraInfo({ requestId, associatedCookies, headers, clientSecurityState, connectTiming, siteHasCookieInOtherPartition, appliedNetworkConditionsId }) {
828
828
  const blockedRequestCookies = [];
829
829
  const includedRequestCookies = [];
830
830
  for (const { blockedReasons, exemptionReason, cookie } of associatedCookies) {
@@ -842,6 +842,7 @@ export class NetworkDispatcher {
842
842
  clientSecurityState,
843
843
  connectTiming,
844
844
  siteHasCookieInOtherPartition,
845
+ appliedNetworkConditionsId,
845
846
  };
846
847
  this.getExtraInfoBuilder(requestId).addRequestExtraInfo(extraRequestInfo);
847
848
  }
@@ -1241,18 +1242,6 @@ export class NetworkDispatcher {
1241
1242
  }
1242
1243
  request.setTrustTokenOperationDoneEvent(event);
1243
1244
  }
1244
- subresourceWebBundleMetadataReceived() {
1245
- // TODO: remove implementation after deleting this methods from definition in Network.pdl
1246
- }
1247
- subresourceWebBundleMetadataError() {
1248
- // TODO: remove implementation after deleting this methods from definition in Network.pdl
1249
- }
1250
- subresourceWebBundleInnerResponseParsed() {
1251
- // TODO: remove implementation after deleting this methods from definition in Network.pdl
1252
- }
1253
- subresourceWebBundleInnerResponseError() {
1254
- // TODO: remove implementation after deleting this methods from definition in Network.pdl
1255
- }
1256
1245
  reportingApiReportAdded(data) {
1257
1246
  this.#manager.dispatchEventToListeners(Events.ReportingApiReportAdded, data.report);
1258
1247
  }
@@ -1330,6 +1319,7 @@ export class RequestCondition extends Common.ObjectWrapper.ObjectWrapper {
1330
1319
  #pattern;
1331
1320
  #enabled;
1332
1321
  #conditions;
1322
+ #ruleIds = new Set();
1333
1323
  static createFromSetting(setting) {
1334
1324
  if ('urlPattern' in setting) {
1335
1325
  const pattern = RequestURLPattern.create(setting.urlPattern) ?? {
@@ -1356,6 +1346,12 @@ export class RequestCondition extends Common.ObjectWrapper.ObjectWrapper {
1356
1346
  this.#enabled = enabled;
1357
1347
  this.#conditions = conditions;
1358
1348
  }
1349
+ get isBlocking() {
1350
+ return this.conditions === BlockingConditions;
1351
+ }
1352
+ get ruleIds() {
1353
+ return this.#ruleIds;
1354
+ }
1359
1355
  get constructorString() {
1360
1356
  return this.#pattern instanceof RequestURLPattern ? this.#pattern.constructorString :
1361
1357
  this.#pattern.upgradedPattern?.constructorString;
@@ -1396,6 +1392,7 @@ export class RequestCondition extends Common.ObjectWrapper.ObjectWrapper {
1396
1392
  }
1397
1393
  set conditions(conditions) {
1398
1394
  this.#conditions = conditions;
1395
+ this.#ruleIds = new Set();
1399
1396
  this.dispatchEventToListeners("request-condition-changed" /* RequestCondition.Events.REQUEST_CONDITION_CHANGED */);
1400
1397
  }
1401
1398
  toSetting() {
@@ -1416,6 +1413,8 @@ export class RequestConditions extends Common.ObjectWrapper.ObjectWrapper {
1416
1413
  #setting = Common.Settings.Settings.instance().createSetting('network-blocked-patterns', []);
1417
1414
  #conditionsEnabledSetting = Common.Settings.Settings.instance().moduleSetting('request-blocking-enabled');
1418
1415
  #conditions = [];
1416
+ #requestConditionsById = new Map();
1417
+ #conditionsAppliedForTestPromise = Promise.resolve();
1419
1418
  constructor() {
1420
1419
  super();
1421
1420
  for (const condition of this.#setting.get()) {
@@ -1504,6 +1503,7 @@ export class RequestConditions extends Common.ObjectWrapper.ObjectWrapper {
1504
1503
  }
1505
1504
  if (Root.Runtime.hostConfig.devToolsIndividualRequestThrottling?.enabled) {
1506
1505
  const urlPatterns = [];
1506
+ // We store all this info out-of-band to prevent races with changing conditions while the promise is still pending
1507
1507
  const matchedNetworkConditions = [];
1508
1508
  if (this.conditionsEnabled) {
1509
1509
  for (const condition of this.#conditions) {
@@ -1515,41 +1515,51 @@ export class RequestConditions extends Common.ObjectWrapper.ObjectWrapper {
1515
1515
  const block = !isNonBlockingCondition(conditions);
1516
1516
  urlPatterns.push({ urlPattern, block });
1517
1517
  if (!block) {
1518
- matchedNetworkConditions.push({
1519
- urlPattern,
1520
- latency: conditions.latency,
1521
- downloadThroughput: conditions.download < 0 ? 0 : conditions.download,
1522
- uploadThroughput: conditions.upload < 0 ? 0 : conditions.upload,
1523
- packetLoss: (conditions.packetLoss ?? 0) < 0 ? 0 : conditions.packetLoss,
1524
- packetQueueLength: conditions.packetQueueLength,
1525
- packetReordering: conditions.packetReordering,
1526
- connectionType: NetworkManager.connectionType(conditions),
1527
- });
1518
+ const { ruleIds } = condition;
1519
+ matchedNetworkConditions.push({ ruleIds, urlPattern, conditions });
1528
1520
  }
1529
1521
  }
1530
1522
  if (globalConditions) {
1531
- matchedNetworkConditions.push({
1532
- urlPattern: '',
1533
- latency: globalConditions.latency,
1534
- downloadThroughput: globalConditions.download < 0 ? 0 : globalConditions.download,
1535
- uploadThroughput: globalConditions.upload < 0 ? 0 : globalConditions.upload,
1536
- packetLoss: (globalConditions.packetLoss ?? 0) < 0 ? 0 : globalConditions.packetLoss,
1537
- packetQueueLength: globalConditions.packetQueueLength,
1538
- packetReordering: globalConditions.packetReordering,
1539
- connectionType: NetworkManager.connectionType(globalConditions),
1540
- });
1523
+ matchedNetworkConditions.push({ conditions: globalConditions });
1541
1524
  }
1542
1525
  }
1526
+ const promises = [];
1543
1527
  for (const agent of agents) {
1544
- void agent.invoke_setBlockedURLs({ urlPatterns });
1545
- void agent.invoke_emulateNetworkConditionsByRule({ offline, matchedNetworkConditions });
1546
- void agent.invoke_overrideNetworkState({
1528
+ promises.push(agent.invoke_setBlockedURLs({ urlPatterns }));
1529
+ promises.push(agent
1530
+ .invoke_emulateNetworkConditionsByRule({
1531
+ offline,
1532
+ matchedNetworkConditions: matchedNetworkConditions.map(({ urlPattern, conditions }) => ({
1533
+ urlPattern: urlPattern ?? '',
1534
+ latency: conditions.latency,
1535
+ downloadThroughput: conditions.download < 0 ? 0 : conditions.download,
1536
+ uploadThroughput: conditions.upload < 0 ? 0 : conditions.upload,
1537
+ packetLoss: (conditions.packetLoss ?? 0) < 0 ? 0 : conditions.packetLoss,
1538
+ packetQueueLength: conditions.packetQueueLength,
1539
+ packetReordering: conditions.packetReordering,
1540
+ connectionType: NetworkManager.connectionType(conditions),
1541
+ }))
1542
+ })
1543
+ .then(response => {
1544
+ if (!response.getError()) {
1545
+ for (let i = 0; i < response.ruleIds.length; ++i) {
1546
+ const ruleId = response.ruleIds[i];
1547
+ const { ruleIds, conditions, urlPattern } = matchedNetworkConditions[i];
1548
+ if (ruleIds) {
1549
+ this.#requestConditionsById.set(ruleId, { urlPattern, conditions });
1550
+ matchedNetworkConditions[i].ruleIds?.add(ruleId);
1551
+ }
1552
+ }
1553
+ }
1554
+ }));
1555
+ promises.push(agent.invoke_overrideNetworkState({
1547
1556
  offline,
1548
1557
  latency: globalConditions?.latency ?? 0,
1549
1558
  downloadThroughput: !globalConditions || globalConditions.download < 0 ? 0 : globalConditions.download,
1550
1559
  uploadThroughput: !globalConditions || globalConditions.upload < 0 ? 0 : globalConditions.upload,
1551
- });
1560
+ }));
1552
1561
  }
1562
+ this.#conditionsAppliedForTestPromise = this.#conditionsAppliedForTestPromise.then(() => Promise.all(promises));
1553
1563
  return urlPatterns.length > 0;
1554
1564
  }
1555
1565
  const urls = this.conditionsEnabled ?
@@ -1561,6 +1571,12 @@ export class RequestConditions extends Common.ObjectWrapper.ObjectWrapper {
1561
1571
  }
1562
1572
  return urls.length > 0;
1563
1573
  }
1574
+ conditionsAppliedForTest() {
1575
+ return this.#conditionsAppliedForTestPromise;
1576
+ }
1577
+ conditionsForId(appliedNetworkConditionsId) {
1578
+ return this.#requestConditionsById.get(appliedNetworkConditionsId);
1579
+ }
1564
1580
  }
1565
1581
  _a = RequestConditions;
1566
1582
  let multiTargetNetworkManagerInstance;
@@ -1872,6 +1888,12 @@ export class MultitargetNetworkManager extends Common.ObjectWrapper.ObjectWrappe
1872
1888
  resolve({ success, content, errorDescription });
1873
1889
  }, allowRemoteFilePaths));
1874
1890
  }
1891
+ appliedRequestConditions(requestInternal) {
1892
+ if (!requestInternal.appliedNetworkConditionsId) {
1893
+ return undefined;
1894
+ }
1895
+ return this.requestConditions.conditionsForId(requestInternal.appliedNetworkConditionsId);
1896
+ }
1875
1897
  }
1876
1898
  export class InterceptedRequest {
1877
1899
  #fetchAgent;
@@ -306,6 +306,7 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper {
306
306
  #directSocketChunks = [];
307
307
  #isIpProtectionUsed;
308
308
  #isAdRelated;
309
+ #appliedNetworkConditionsId;
309
310
  constructor(requestId, backendRequestId, url, documentURL, frameId, loaderId, initiator, hasUserGesture) {
310
311
  super();
311
312
  this.#requestId = requestId;
@@ -374,6 +375,9 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper {
374
375
  get loaderId() {
375
376
  return this.#loaderId;
376
377
  }
378
+ get appliedNetworkConditionsId() {
379
+ return this.#appliedNetworkConditionsId;
380
+ }
377
381
  setRemoteAddress(ip, port) {
378
382
  this.#remoteAddress = ip + ':' + port;
379
383
  this.dispatchEventToListeners(Events.REMOTE_ADDRESS_CHANGED, this);
@@ -1251,6 +1255,7 @@ export class NetworkRequest extends Common.ObjectWrapper.ObjectWrapper {
1251
1255
  this.#hasExtraRequestInfo = true;
1252
1256
  this.setRequestHeadersText(''); // Mark request headers as non-provisional
1253
1257
  this.#clientSecurityState = extraRequestInfo.clientSecurityState;
1258
+ this.#appliedNetworkConditionsId = extraRequestInfo.appliedNetworkConditionsId;
1254
1259
  if (extraRequestInfo.connectTiming) {
1255
1260
  this.setConnectTimingFromExtraInfo(extraRequestInfo.connectTiming);
1256
1261
  }
@@ -22,7 +22,7 @@ const UIStrings = {
22
22
  };
23
23
  const str_ = i18n.i18n.registerUIStrings('core/sdk/RehydratingConnection.ts', UIStrings);
24
24
  const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
25
- export class RehydratingConnection {
25
+ export class RehydratingConnectionTransport {
26
26
  rehydratingConnectionState = 1 /* RehydratingConnectionState.UNINITIALIZED */;
27
27
  onDisconnect = null;
28
28
  onMessage = null;
@@ -63,7 +63,7 @@ export class RehydratingConnection {
63
63
  this.#rehydratingWindow.opener.postMessage({ type: 'REHYDRATING_WINDOW_READY' });
64
64
  }
65
65
  else if (this.#rehydratingWindow !== window.top) {
66
- this.#rehydratingWindow.parent.postMessage({ type: 'REHYDRATING_IFRAME_READY' });
66
+ this.#rehydratingWindow.parent.postMessage({ type: 'REHYDRATING_IFRAME_READY' }, '*');
67
67
  }
68
68
  else {
69
69
  this.#onConnectionLost(i18nString(UIStrings.noHostWindow));
@@ -108,6 +108,7 @@ export class RehydratingConnection {
108
108
  const target = hydratingDataPerTarget.target;
109
109
  const executionContexts = hydratingDataPerTarget.executionContexts;
110
110
  const scripts = hydratingDataPerTarget.scripts;
111
+ const resources = hydratingDataPerTarget.resources;
111
112
  this.postToFrontend({
112
113
  method: 'Target.targetCreated',
113
114
  params: {
@@ -122,7 +123,7 @@ export class RehydratingConnection {
122
123
  },
123
124
  });
124
125
  sessionId += 1;
125
- const session = new RehydratingSession(sessionId, target, executionContexts, scripts, this);
126
+ const session = new RehydratingSession(sessionId, target, executionContexts, scripts, resources, this);
126
127
  this.sessions.set(sessionId, session);
127
128
  session.declareSessionAttachedToTarget();
128
129
  }
@@ -208,12 +209,14 @@ export class RehydratingSession extends RehydratingSessionBase {
208
209
  target;
209
210
  executionContexts = [];
210
211
  scripts = [];
211
- constructor(sessionId, target, executionContexts, scripts, connection) {
212
+ resources = [];
213
+ constructor(sessionId, target, executionContexts, scripts, resources, connection) {
212
214
  super(connection);
213
215
  this.sessionId = sessionId;
214
216
  this.target = target;
215
217
  this.executionContexts = executionContexts;
216
218
  this.scripts = scripts;
219
+ this.resources = resources;
217
220
  }
218
221
  sendMessageToFrontend(payload, attachSessionId = true) {
219
222
  // Attach the session's Id to the message.
@@ -230,12 +233,31 @@ export class RehydratingSession extends RehydratingSessionBase {
230
233
  case 'Debugger.enable':
231
234
  this.handleDebuggerEnable(data.id);
232
235
  break;
236
+ case 'CSS.enable':
237
+ this.sendMessageToFrontend({
238
+ id: data.id,
239
+ result: {},
240
+ });
241
+ break;
233
242
  case 'Debugger.getScriptSource':
234
243
  if (data.params) {
235
244
  const params = data.params;
236
245
  this.handleDebuggerGetScriptSource(data.id, params.scriptId);
237
246
  }
238
247
  break;
248
+ case 'Page.getResourceTree':
249
+ this.handleGetResourceTree(data.id);
250
+ break;
251
+ case 'Page.getResourceContent': {
252
+ const request = data.params;
253
+ this.handleGetResourceContent(request.frameId, request.url, data.id);
254
+ break;
255
+ }
256
+ case 'CSS.getStyleSheetText': {
257
+ const request = data.params;
258
+ this.handleGetStyleSheetText(request.styleSheetId, data.id);
259
+ break;
260
+ }
239
261
  default:
240
262
  this.sendMessageToFrontend({
241
263
  id: data.id,
@@ -298,7 +320,20 @@ export class RehydratingSession extends RehydratingSessionBase {
298
320
  // script parsed event to communicate the current script state and respond with a mock
299
321
  // debugger id.
300
322
  handleDebuggerEnable(id) {
323
+ const htmlResourceUrls = new Set(this.resources.filter(r => r.mimeType === 'text/html').map(r => r.url));
301
324
  for (const script of this.scripts) {
325
+ // Handle inline scripts.
326
+ if (htmlResourceUrls.has(script.url)) {
327
+ script.embedderName = script.url;
328
+ // We don't have the actual embedded offset from this trace event. Non-zero
329
+ // values are important though: that is what `Script.isInlineScript()`
330
+ // checks. Otherwise these scripts would try to show individually within the
331
+ // Sources panel.
332
+ script.startColumn = 1;
333
+ script.startLine = 1;
334
+ script.endColumn = 1;
335
+ script.endLine = 1;
336
+ }
302
337
  this.sendMessageToFrontend({
303
338
  method: 'Debugger.scriptParsed',
304
339
  params: script,
@@ -312,4 +347,67 @@ export class RehydratingSession extends RehydratingSessionBase {
312
347
  },
313
348
  });
314
349
  }
350
+ handleGetResourceTree(id) {
351
+ const resources = this.resources.filter(r => r.mimeType === 'text/html' || r.mimeType === 'text/css');
352
+ if (!resources.length) {
353
+ return;
354
+ }
355
+ const frameTree = {
356
+ frame: {
357
+ id: this.target.targetId,
358
+ url: this.target.url,
359
+ },
360
+ childFrames: [],
361
+ resources: resources.map(r => ({
362
+ url: r.url,
363
+ type: r.mimeType === 'text/html' ? 'Document' : 'Stylesheet',
364
+ mimeType: r.mimeType,
365
+ contentSize: r.content.length,
366
+ })),
367
+ };
368
+ this.sendMessageToFrontend({
369
+ id,
370
+ result: {
371
+ frameTree,
372
+ },
373
+ });
374
+ const stylesheets = this.resources.filter(r => r.mimeType === 'text/css');
375
+ for (const stylesheet of stylesheets) {
376
+ this.sendMessageToFrontend({
377
+ method: 'CSS.styleSheetAdded',
378
+ params: {
379
+ header: {
380
+ styleSheetId: `sheet.${stylesheet.frame}.${stylesheet.url}`,
381
+ frameId: stylesheet.frame,
382
+ sourceURL: stylesheet.url,
383
+ },
384
+ },
385
+ });
386
+ }
387
+ }
388
+ handleGetResourceContent(frame, url, id) {
389
+ const resource = this.resources.find(r => r.frame === frame && r.url === url);
390
+ if (!resource) {
391
+ return;
392
+ }
393
+ this.sendMessageToFrontend({
394
+ id,
395
+ result: {
396
+ content: resource.content,
397
+ base64Encoded: false,
398
+ },
399
+ });
400
+ }
401
+ handleGetStyleSheetText(stylesheetId, id) {
402
+ const resource = this.resources.find(r => `sheet.${r.frame}.${r.url}` === stylesheetId);
403
+ if (!resource) {
404
+ return;
405
+ }
406
+ this.sendMessageToFrontend({
407
+ id,
408
+ result: {
409
+ text: resource.content,
410
+ },
411
+ });
412
+ }
315
413
  }
@@ -57,7 +57,7 @@ export class SourceMap {
57
57
  #compiledURL;
58
58
  #sourceMappingURL;
59
59
  #baseURL;
60
- #mappings;
60
+ #mappings = null;
61
61
  #sourceInfos = [];
62
62
  #sourceInfoByURL = new Map();
63
63
  #script;
@@ -75,7 +75,6 @@ export class SourceMap {
75
75
  this.#sourceMappingURL = sourceMappingURL;
76
76
  this.#baseURL = (Common.ParsedURL.schemeIs(sourceMappingURL, 'data:')) ? compiledURL : sourceMappingURL;
77
77
  this.#debugId = 'debugId' in payload ? payload.debugId : undefined;
78
- this.#mappings = null;
79
78
  if ('sections' in this.#json) {
80
79
  if (this.#json.sections.find(section => 'url' in section)) {
81
80
  Common.Console.Console.instance().warn(`SourceMap "${sourceMappingURL}" contains unsupported "URL" field in one of its sections.`);
@@ -306,7 +305,7 @@ export class SourceMap {
306
305
  #computeReverseMappings(mappings) {
307
306
  const reverseMappingsPerUrl = new Map();
308
307
  for (let i = 0; i < mappings.length; i++) {
309
- const entryUrl = mappings[i].sourceURL;
308
+ const entryUrl = mappings[i]?.sourceURL;
310
309
  if (!entryUrl) {
311
310
  continue;
312
311
  }
@@ -351,7 +351,11 @@ const UIStrings = {
351
351
  /**
352
352
  * @description Label of a checkbox in the DevTools settings UI.
353
353
  */
354
- enableRemoteFileLoading: 'Allow `DevTools` to load resources, such as source maps, from remote file paths. Disabled by default for security reasons.',
354
+ enableRemoteFileLoading: 'Allow loading remote file path resources in DevTools',
355
+ /**
356
+ * @description Tooltip text for a setting that controls whether external resource can be loaded in DevTools.
357
+ */
358
+ remoteFileLoadingInfo: 'Example resource are source maps. Disabled by default for security reasons.',
355
359
  /**
356
360
  * @description Tooltip text for a setting that controls the network cache. Disabling the network cache can simulate the network connections of users that are visiting a page for the first time.
357
361
  */
@@ -1114,4 +1118,7 @@ Common.Settings.registerSettingExtension({
1114
1118
  settingName: 'network.enable-remote-file-loading',
1115
1119
  settingType: "boolean" /* Common.Settings.SettingType.BOOLEAN */,
1116
1120
  defaultValue: false,
1121
+ learnMore: {
1122
+ tooltip: i18nLazyString(UIStrings.remoteFileLoadingInfo),
1123
+ }
1117
1124
  });