chrome-devtools-frontend 1.0.1008735 → 1.0.1009983

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.
@@ -8,7 +8,7 @@
8
8
  "preLaunchTask": "1-build_Debug",
9
9
  "runtimeExecutable": "/usr/bin/npm",
10
10
  "runtimeArgs": ["run", "unittest"],
11
- "port": 9222,
11
+ "port": 7722,
12
12
  "timeout": 30000,
13
13
  "browserLaunchLocation": "workspace",
14
14
  "outputCapture": "std",
@@ -172,7 +172,8 @@ export namespace Chrome {
172
172
  }
173
173
 
174
174
  export interface RecorderExtensionPlugin {
175
- stringify(obj: Record<string, any>): Promise<string>;
175
+ stringify(recording: Record<string, any>): Promise<string>;
176
+ stringifyStep(step: Record<string, any>): Promise<string>;
176
177
  }
177
178
 
178
179
  export interface LanguageExtensionPlugin {
@@ -278,7 +279,8 @@ export namespace Chrome {
278
279
  }
279
280
 
280
281
  export interface RecorderExtensions {
281
- registerRecorderExtensionPlugin(plugin: RecorderExtensionPlugin, pluginName: string): Promise<void>;
282
+ registerRecorderExtensionPlugin(plugin: RecorderExtensionPlugin, pluginName: string, mimeType: string):
283
+ Promise<void>;
282
284
  unregisterRecorderExtensionPlugin(plugin: RecorderExtensionPlugin): Promise<void>;
283
285
  }
284
286
 
@@ -3293,6 +3293,9 @@
3293
3293
  "panels/application/DOMStorageItemsView.ts | domStorageItems": {
3294
3294
  "message": "DOM Storage Items"
3295
3295
  },
3296
+ "panels/application/DOMStorageItemsView.ts | domStorageItemsCleared": {
3297
+ "message": "{PH1} cleared"
3298
+ },
3296
3299
  "panels/application/DOMStorageItemsView.ts | domStorageNumberEntries": {
3297
3300
  "message": "Number of entries shown in table: {PH1}"
3298
3301
  },
@@ -3293,6 +3293,9 @@
3293
3293
  "panels/application/DOMStorageItemsView.ts | domStorageItems": {
3294
3294
  "message": "D̂ÓM̂ Śt̂ór̂áĝé Ît́êḿŝ"
3295
3295
  },
3296
+ "panels/application/DOMStorageItemsView.ts | domStorageItemsCleared": {
3297
+ "message": "{PH1} ĉĺêár̂éd̂"
3298
+ },
3296
3299
  "panels/application/DOMStorageItemsView.ts | domStorageNumberEntries": {
3297
3300
  "message": "N̂úm̂b́êŕ ôf́ êńt̂ŕîéŝ śĥóŵń îń t̂áb̂ĺê: {PH1}"
3298
3301
  },
@@ -188,7 +188,7 @@ export class CSSProperty {
188
188
  if (insideProperty) {
189
189
  result += propertyText;
190
190
  }
191
- result = result.substring(2, result.length - 1).trimRight();
191
+ result = result.substring(2, result.length - 1).trimEnd();
192
192
  return result + (indentation ? '\n' + endIndentation : '');
193
193
 
194
194
  function processToken(token: string, tokenType: string|null): void {
@@ -230,8 +230,8 @@ export class CSSProperty {
230
230
  }
231
231
  if (cssMetadata().isGridAreaDefiningProperty(propertyName)) {
232
232
  const rowResult = GridAreaRowRegex.exec(token);
233
- if (rowResult && rowResult.index === 0 && !propertyText.trimRight().endsWith(']')) {
234
- propertyText = propertyText.trimRight() + '\n' + doubleIndent;
233
+ if (rowResult && rowResult.index === 0 && !propertyText.trimEnd().endsWith(']')) {
234
+ propertyText = propertyText.trimEnd() + '\n' + doubleIndent;
235
235
  }
236
236
  }
237
237
  if (!propertyName && token === ':') {
@@ -192,7 +192,6 @@ export class Script implements TextUtils.ContentProvider.ContentProvider, FrameA
192
192
 
193
193
  originalContentProvider(): TextUtils.ContentProvider.ContentProvider {
194
194
  if (!this.#originalContentProviderInternal) {
195
- /* } */
196
195
  let lazyContentPromise: Promise<TextUtils.ContentProvider.DeferredContent>|null;
197
196
  this.#originalContentProviderInternal =
198
197
  new TextUtils.StaticContentProvider.StaticContentProvider(this.contentURL(), this.contentType(), () => {
@@ -220,7 +219,9 @@ export class Script implements TextUtils.ContentProvider.ContentProvider, FrameA
220
219
  return {content: bytecode, isEncoded: true};
221
220
  }
222
221
  let content: string = scriptSource || '';
223
- if (this.hasSourceURL) {
222
+ if (this.hasSourceURL && this.sourceURL.startsWith('snippet://')) {
223
+ // TODO(crbug.com/1330846): Find a better way to establish the snippet automapping binding then adding
224
+ // a sourceURL comment before evaluation and removing it here.
224
225
  content = Script.trimSourceURLComment(content);
225
226
  }
226
227
  return {content, isEncoded: false};
@@ -61,8 +61,8 @@ export class Target extends ProtocolClient.InspectorBackend.TargetBase {
61
61
  Capability.IO | Capability.Media | Capability.Inspector;
62
62
  break;
63
63
  case Type.Worker:
64
- this.#capabilitiesMask =
65
- Capability.JS | Capability.Log | Capability.Network | Capability.Target | Capability.IO | Capability.Media;
64
+ this.#capabilitiesMask = Capability.JS | Capability.Log | Capability.Network | Capability.Target |
65
+ Capability.IO | Capability.Media | Capability.Emulation;
66
66
  break;
67
67
  case Type.Node:
68
68
  this.#capabilitiesMask = Capability.JS;
@@ -51,6 +51,7 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
51
51
  readonly debuggerWorkspaceBinding: DebuggerWorkspaceBinding;
52
52
  readonly #breakpointsForUISourceCode: Map<Workspace.UISourceCode.UISourceCode, Map<string, BreakpointLocation>>;
53
53
  readonly #breakpointByStorageId: Map<string, Breakpoint>;
54
+ #updateBindingsCallbacks: ((uiSourceCode: Workspace.UISourceCode.UISourceCode) => Promise<void>)[];
54
55
 
55
56
  private constructor(
56
57
  targetManager: SDK.TargetManager.TargetManager, workspace: Workspace.Workspace.WorkspaceImpl,
@@ -69,6 +70,7 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
69
70
  this.#workspace.addEventListener(Workspace.Workspace.Events.ProjectRemoved, this.projectRemoved, this);
70
71
 
71
72
  this.targetManager.observeModels(SDK.DebuggerModel.DebuggerModel, this);
73
+ this.#updateBindingsCallbacks = [];
72
74
  }
73
75
 
74
76
  static instance(opts: {
@@ -108,6 +110,10 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
108
110
  debuggerModel.setSynchronizeBreakpointsCallback(null);
109
111
  }
110
112
 
113
+ addUpdateBindingsCallback(callback: ((uiSourceCode: Workspace.UISourceCode.UISourceCode) => Promise<void>)): void {
114
+ this.#updateBindingsCallbacks.push(callback);
115
+ }
116
+
111
117
  async copyBreakpoints(fromURL: Platform.DevToolsPath.UrlString, toSourceCode: Workspace.UISourceCode.UISourceCode):
112
118
  Promise<void> {
113
119
  const breakpointItems = this.storage.breakpointItems(fromURL);
@@ -122,15 +128,13 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
122
128
  if (!Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.INSTRUMENTATION_BREAKPOINTS)) {
123
129
  return;
124
130
  }
131
+ if (!script.sourceURL) {
132
+ return;
133
+ }
134
+
125
135
  const debuggerModel = script.debuggerModel;
136
+ const uiSourceCode = await this.getUpdatedUISourceCode(script);
126
137
  if (this.#hasBreakpointsForUrl(script.sourceURL)) {
127
- // Handle inline scripts without sourceURL comment separately:
128
- // The UISourceCode of inline scripts without sourceURLs will not be availabe
129
- // until a later point. Use the v8 script for setting the breakpoint.
130
- const isInlineScriptWithoutSourceURL = script.isInlineScript() && !script.hasSourceURL;
131
- const sourceURL =
132
- isInlineScriptWithoutSourceURL ? DefaultScriptMapping.createV8ScriptURL(script) : script.sourceURL;
133
- const uiSourceCode = await Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURLPromise(sourceURL);
134
138
  await this.#restoreBreakpointsForUrl(uiSourceCode);
135
139
  }
136
140
 
@@ -161,6 +165,34 @@ export class BreakpointManager extends Common.ObjectWrapper.ObjectWrapper<EventT
161
165
  }
162
166
  }
163
167
 
168
+ async getUpdatedUISourceCode(script: SDK.Script.Script): Promise<Workspace.UISourceCode.UISourceCode> {
169
+ const isSnippet = script.sourceURL.startsWith('snippet://');
170
+ const projectType = isSnippet ? Workspace.Workspace.projectTypes.Network : undefined;
171
+
172
+ // Handle inline scripts without sourceURL comment separately:
173
+ // The UISourceCode of inline scripts without sourceURLs will not be availabe
174
+ // until a later point. Use the v8 script for setting the breakpoint.
175
+ const isInlineScriptWithoutSourceURL = script.isInlineScript() && !script.hasSourceURL;
176
+ const sourceURL =
177
+ isInlineScriptWithoutSourceURL ? DefaultScriptMapping.createV8ScriptURL(script) : script.sourceURL;
178
+ const uiSourceCode =
179
+ await Workspace.Workspace.WorkspaceImpl.instance().uiSourceCodeForURLPromise(sourceURL, projectType);
180
+
181
+ if (this.#updateBindingsCallbacks.length > 0) {
182
+ // It's possible to set breakpoints on files on the file system, and to have them
183
+ // hit whenever we navigate to a page that serves that file.
184
+ // To make sure that we have all breakpoint information moved from the file system
185
+ // to the served file, we need to update the bindings and await it. This will
186
+ // move the breakpoints from the FileSystem UISourceCode to the Network UiSourceCode.
187
+ const promises = [];
188
+ for (const callback of this.#updateBindingsCallbacks) {
189
+ promises.push(callback(uiSourceCode));
190
+ }
191
+ await Promise.all(promises);
192
+ }
193
+ return uiSourceCode;
194
+ }
195
+
164
196
  async #restoreBreakpointsForUrl(uiSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
165
197
  this.restoreBreakpoints(uiSourceCode);
166
198
  const breakpoints = this.#breakpointByStorageId.values();
@@ -713,20 +745,24 @@ export class ModelBreakpoint {
713
745
  };
714
746
  });
715
747
  newState = new Breakpoint.State(positions, condition);
716
- } else if (this.#breakpoint.currentState) {
717
- newState = new Breakpoint.State(this.#breakpoint.currentState.positions, condition);
718
- } else {
719
- // TODO(bmeurer): This fallback doesn't make a whole lot of sense, we should
720
- // at least signal a warning to the developer that this #breakpoint wasn't
721
- // really resolved.
722
- const position = {
723
- url: this.#breakpoint.url(),
724
- scriptId: '' as Protocol.Runtime.ScriptId,
725
- scriptHash: '',
726
- lineNumber,
727
- columnNumber,
728
- };
729
- newState = new Breakpoint.State([position], condition);
748
+ } else if (!Root.Runtime.experiments.isEnabled(Root.Runtime.ExperimentName.INSTRUMENTATION_BREAKPOINTS)) {
749
+ // Use this fallback if we do not have instrumentation breakpoints enabled yet. This currently makes
750
+ // sure that v8 knows about the breakpoint and is able to restore it whenever the script is parsed.
751
+ if (this.#breakpoint.currentState) {
752
+ newState = new Breakpoint.State(this.#breakpoint.currentState.positions, condition);
753
+ } else {
754
+ // TODO(bmeurer): This fallback doesn't make a whole lot of sense, we should
755
+ // at least signal a warning to the developer that this #breakpoint wasn't
756
+ // really resolved.
757
+ const position = {
758
+ url: this.#breakpoint.url(),
759
+ scriptId: '' as Protocol.Runtime.ScriptId,
760
+ scriptHash: '',
761
+ lineNumber,
762
+ columnNumber,
763
+ };
764
+ newState = new Breakpoint.State([position], condition);
765
+ }
730
766
  }
731
767
  }
732
768
 
@@ -14,7 +14,7 @@ import {DebuggerWorkspaceBinding} from './DebuggerWorkspaceBinding.js';
14
14
  import {NetworkProject} from './NetworkProject.js';
15
15
  import {resourceMetadata} from './ResourceUtils.js';
16
16
 
17
- let resourceMappingInstance: ResourceMapping;
17
+ let resourceMappingInstance: ResourceMapping|undefined;
18
18
 
19
19
  const styleSheetOffsetMap = new WeakMap<SDK.CSSStyleSheetHeader.CSSStyleSheetHeader, TextUtils.TextRange.TextRange>();
20
20
  const scriptOffsetMap = new WeakMap<SDK.Script.Script, TextUtils.TextRange.TextRange>();
@@ -47,6 +47,10 @@ export class ResourceMapping implements SDK.TargetManager.SDKModelObserver<SDK.R
47
47
  return resourceMappingInstance;
48
48
  }
49
49
 
50
+ static removeInstance(): void {
51
+ resourceMappingInstance = undefined;
52
+ }
53
+
50
54
  modelAdded(resourceTreeModel: SDK.ResourceTreeModel.ResourceTreeModel): void {
51
55
  const info = new ModelInfo(this.#workspace, resourceTreeModel);
52
56
  this.#modelToInfo.set(resourceTreeModel, info);
@@ -97,17 +101,27 @@ export class ResourceMapping implements SDK.TargetManager.SDKModelObserver<SDK.R
97
101
  if (!info) {
98
102
  return null;
99
103
  }
100
- const uiSourceCode = info.getProject().uiSourceCodeForURL(script.sourceURL);
104
+ const embedderName = script.embedderName();
105
+ if (!embedderName) {
106
+ return null;
107
+ }
108
+ const uiSourceCode = info.getProject().uiSourceCodeForURL(embedderName);
101
109
  if (!uiSourceCode) {
102
110
  return null;
103
111
  }
104
112
  const offset = scriptOffsetMap.get(script) ||
105
113
  TextUtils.TextRange.TextRange.createFromLocation(script.lineOffset, script.columnOffset);
106
- const lineNumber = jsLocation.lineNumber + offset.startLine - script.lineOffset;
114
+ let lineNumber = jsLocation.lineNumber + offset.startLine - script.lineOffset;
107
115
  let columnNumber = jsLocation.columnNumber;
108
116
  if (jsLocation.lineNumber === script.lineOffset) {
109
117
  columnNumber += offset.startColumn - script.columnOffset;
110
118
  }
119
+ if (script.hasSourceURL) {
120
+ if (lineNumber === 0) {
121
+ columnNumber += script.columnOffset;
122
+ }
123
+ lineNumber += script.lineOffset;
124
+ }
111
125
  return uiSourceCode.uiLocation(lineNumber, columnNumber);
112
126
  }
113
127
 
@@ -124,14 +138,33 @@ export class ResourceMapping implements SDK.TargetManager.SDKModelObserver<SDK.R
124
138
  if (!debuggerModel) {
125
139
  return [];
126
140
  }
127
- const location = debuggerModel.createRawLocationByURL(uiSourceCode.url(), lineNumber, columnNumber);
128
- if (location) {
129
- const script = location.script();
130
- if (script && script.containsLocation(lineNumber, columnNumber)) {
131
- return [location];
141
+ const locations = [];
142
+ for (const script of debuggerModel.scripts()) {
143
+ if (script.embedderName() !== uiSourceCode.url()) {
144
+ continue;
145
+ }
146
+ const {startLine, startColumn} = scriptOffsetMap.get(script) ||
147
+ TextUtils.TextRange.TextRange.createFromLocation(script.lineOffset, script.columnOffset);
148
+ if (lineNumber < startLine || (lineNumber === startLine && columnNumber < startColumn)) {
149
+ continue;
150
+ }
151
+ const endLine = startLine + (script.endLine - script.lineOffset);
152
+ const endColumn =
153
+ startLine === endLine ? startColumn + (script.endColumn - script.columnOffset) : script.endColumn;
154
+ if (lineNumber > endLine || (lineNumber === endLine && columnNumber > endColumn)) {
155
+ continue;
156
+ }
157
+ let scriptLineNumber = lineNumber;
158
+ let scriptColumnNumber = columnNumber;
159
+ if (script.hasSourceURL) {
160
+ scriptLineNumber -= startLine;
161
+ if (scriptLineNumber === 0) {
162
+ scriptColumnNumber -= startColumn;
163
+ }
132
164
  }
165
+ locations.push(debuggerModel.createRawLocation(script, scriptLineNumber, scriptColumnNumber));
133
166
  }
134
- return [];
167
+ return locations;
135
168
  }
136
169
 
137
170
  uiLocationToCSSLocations(uiLocation: Workspace.UISourceCode.UILocation): SDK.CSSModel.CSSLocation[] {
@@ -341,7 +374,7 @@ class Binding implements TextUtils.ContentProvider.ContentProvider {
341
374
  if (!debuggerModel) {
342
375
  return [];
343
376
  }
344
- return debuggerModel.scriptsForSourceURL(this.#uiSourceCode.url());
377
+ return debuggerModel.scripts().filter(script => script.embedderName() === this.#uiSourceCode.url());
345
378
  }
346
379
 
347
380
  async styleSheetChanged(stylesheet: SDK.CSSStyleSheetHeader.CSSStyleSheetHeader, edit: SDK.CSSModel.Edit|null):
@@ -298,7 +298,7 @@ export class ResourceScriptFile extends Common.ObjectWrapper.ObjectWrapper<Resou
298
298
  }
299
299
 
300
300
  // Match ignoring sourceURL.
301
- if (!workingCopy.startsWith(this.#scriptSource.trimRight())) {
301
+ if (!workingCopy.startsWith(this.#scriptSource.trimEnd())) {
302
302
  return true;
303
303
  }
304
304
  const suffix = this.#uiSourceCodeInternal.workingCopy().substr(this.#scriptSource.length);
@@ -111,6 +111,7 @@ export namespace PrivateAPI {
111
111
 
112
112
  export const enum RecorderExtensionPluginCommands {
113
113
  Stringify = 'stringify',
114
+ StringifyStep = 'stringifyStep',
114
115
  }
115
116
 
116
117
  export const enum RecorderExtensionPluginEvents {
@@ -132,6 +133,7 @@ export namespace PrivateAPI {
132
133
  type RegisterRecorderExtensionPluginRequest = {
133
134
  command: Commands.RegisterRecorderExtensionPlugin,
134
135
  pluginName: string,
136
+ mimeType: string,
135
137
  port: MessagePort,
136
138
  };
137
139
  type SubscribeRequest = {command: Commands.Subscribe, type: string};
@@ -276,7 +278,12 @@ export namespace PrivateAPI {
276
278
  parameters: {recording: Record<string, unknown>},
277
279
  };
278
280
 
279
- export type RecorderExtensionRequests = StringifyRequest;
281
+ type StringifyStepRequest = {
282
+ method: RecorderExtensionPluginCommands.StringifyStep,
283
+ parameters: {step: Record<string, unknown>},
284
+ };
285
+
286
+ export type RecorderExtensionRequests = StringifyRequest|StringifyStepRequest;
280
287
  }
281
288
 
282
289
  declare global {
@@ -705,8 +712,8 @@ self.injectedExtensionAPI = function(
705
712
  (RecorderServicesAPIImpl.prototype as
706
713
  Pick<APIImpl.RecorderExtensions, 'registerRecorderExtensionPlugin'|'unregisterRecorderExtensionPlugin'>) = {
707
714
  registerRecorderExtensionPlugin: async function(
708
- this: APIImpl.RecorderExtensions, plugin: PublicAPI.Chrome.DevTools.RecorderExtensionPlugin,
709
- pluginName: string): Promise<void> {
715
+ this: APIImpl.RecorderExtensions, plugin: PublicAPI.Chrome.DevTools.RecorderExtensionPlugin, pluginName: string,
716
+ mimeType: string): Promise<void> {
710
717
  if (this._plugins.has(plugin)) {
711
718
  throw new Error(`Tried to register plugin '${pluginName}' twice`);
712
719
  }
@@ -720,11 +727,14 @@ self.injectedExtensionAPI = function(
720
727
  .catch(error => port.postMessage({requestId, error: {message: error.message}}));
721
728
  };
722
729
 
723
- function dispatchMethodCall(request: PrivateAPI.RecorderExtensionRequests): Promise<unknown> {
730
+ async function dispatchMethodCall(request: PrivateAPI.RecorderExtensionRequests): Promise<unknown> {
724
731
  switch (request.method) {
725
732
  case PrivateAPI.RecorderExtensionPluginCommands.Stringify:
726
733
  return plugin.stringify(request.parameters.recording);
734
+ case PrivateAPI.RecorderExtensionPluginCommands.StringifyStep:
735
+ return plugin.stringifyStep(request.parameters.step);
727
736
  default:
737
+ // @ts-expect-error
728
738
  throw new Error(`'${request.method}' is not recognized`);
729
739
  }
730
740
  }
@@ -734,6 +744,7 @@ self.injectedExtensionAPI = function(
734
744
  {
735
745
  command: PrivateAPI.Commands.RegisterRecorderExtensionPlugin,
736
746
  pluginName,
747
+ mimeType,
737
748
  port: channel.port2,
738
749
  },
739
750
  () => resolve(), [channel.port2]);
@@ -222,10 +222,10 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
222
222
  private registerRecorderExtensionEndpoint(
223
223
  message: PrivateAPI.ExtensionServerRequestMessage, _shared_port: MessagePort): Record {
224
224
  if (message.command !== PrivateAPI.Commands.RegisterRecorderExtensionPlugin) {
225
- return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.Subscribe}`);
225
+ return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.RegisterRecorderExtensionPlugin}`);
226
226
  }
227
- const {pluginName, port} = message;
228
- RecorderPluginManager.instance().addPlugin(new RecorderExtensionEndpoint(pluginName, port));
227
+ const {pluginName, mimeType, port} = message;
228
+ RecorderPluginManager.instance().addPlugin(new RecorderExtensionEndpoint(pluginName, mimeType, port));
229
229
  return this.status.OK();
230
230
  }
231
231
 
@@ -8,16 +8,22 @@ import {RecorderPluginManager} from './RecorderPluginManager.js';
8
8
 
9
9
  export class RecorderExtensionEndpoint extends ExtensionEndpoint {
10
10
  private readonly name: string;
11
+ private readonly mimeType: string;
11
12
 
12
- constructor(name: string, port: MessagePort) {
13
+ constructor(name: string, mimeType: string, port: MessagePort) {
13
14
  super(port);
14
15
  this.name = name;
16
+ this.mimeType = mimeType;
15
17
  }
16
18
 
17
19
  getName(): string {
18
20
  return this.name;
19
21
  }
20
22
 
23
+ getMimeType(): string {
24
+ return this.mimeType;
25
+ }
26
+
21
27
  protected handleEvent({event}: {event: string}): void {
22
28
  switch (event) {
23
29
  case PrivateAPI.RecorderExtensionPluginEvents.UnregisteredRecorderExtensionPlugin: {
@@ -40,4 +46,15 @@ export class RecorderExtensionEndpoint extends ExtensionEndpoint {
40
46
  stringify(recording: Object): Promise<string> {
41
47
  return this.sendRequest(PrivateAPI.RecorderExtensionPluginCommands.Stringify, {recording});
42
48
  }
49
+
50
+ /**
51
+ * In practice, `step` is a Step[1], but we avoid defining this type on the
52
+ * API in order to prevent dependencies between Chrome and puppeteer. Extensions
53
+ * are responsible for working out compatibility issues.
54
+ *
55
+ * [1]: https://github.com/puppeteer/replay/blob/main/src/Schema.ts#L243
56
+ */
57
+ stringifyStep(step: Object): Promise<string> {
58
+ return this.sendRequest(PrivateAPI.RecorderExtensionPluginCommands.StringifyStep, {step});
59
+ }
43
60
  }
@@ -196,11 +196,13 @@ export class NetworkLog extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
196
196
  return initiatorInfo;
197
197
  }
198
198
 
199
- initiatorInfoForRequest(request: SDK.NetworkRequest.NetworkRequest): InitiatorInfo {
200
- const initiatorInfo = this.initializeInitiatorSymbolIfNeeded(request);
201
- if (initiatorInfo.info) {
202
- return initiatorInfo.info;
203
- }
199
+ static initiatorInfoForRequest(request: SDK.NetworkRequest.NetworkRequest, existingInitiatorData?: InitiatorData):
200
+ InitiatorInfo {
201
+ const initiatorInfo: InitiatorData = existingInitiatorData || {
202
+ info: null,
203
+ chain: null,
204
+ request: undefined,
205
+ };
204
206
 
205
207
  let type = SDK.NetworkRequest.InitiatorType.Other;
206
208
  let url = Platform.DevToolsPath.EmptyUrlString;
@@ -253,11 +255,19 @@ export class NetworkLog extends Common.ObjectWrapper.ObjectWrapper<EventTypes> i
253
255
  url = initiator.url as Platform.DevToolsPath.UrlString || Platform.DevToolsPath.EmptyUrlString;
254
256
  }
255
257
  }
256
-
257
258
  initiatorInfo.info = {type, url, lineNumber, columnNumber, scriptId, stack: initiatorStack, initiatorRequest};
258
259
  return initiatorInfo.info;
259
260
  }
260
261
 
262
+ initiatorInfoForRequest(request: SDK.NetworkRequest.NetworkRequest): InitiatorInfo {
263
+ const initiatorInfo = this.initializeInitiatorSymbolIfNeeded(request);
264
+ if (initiatorInfo.info) {
265
+ return initiatorInfo.info;
266
+ }
267
+
268
+ return NetworkLog.initiatorInfoForRequest(request, initiatorInfo);
269
+ }
270
+
261
271
  initiatorGraphForRequest(request: SDK.NetworkRequest.NetworkRequest): InitiatorGraph {
262
272
  const initiated = new Map<SDK.NetworkRequest.NetworkRequest, SDK.NetworkRequest.NetworkRequest>();
263
273
  const networkManager = SDK.NetworkManager.NetworkManager.forRequest(request);
@@ -584,7 +594,7 @@ export type EventTypes = {
584
594
  [Events.RequestUpdated]: SDK.NetworkRequest.NetworkRequest,
585
595
  };
586
596
 
587
- interface InitiatorData {
597
+ export interface InitiatorData {
588
598
  info: InitiatorInfo|null;
589
599
  chain: Set<SDK.NetworkRequest.NetworkRequest>|null;
590
600
  request?: SDK.NetworkRequest.NetworkRequest|null;
@@ -595,7 +605,7 @@ export interface InitiatorGraph {
595
605
  initiated: Map<SDK.NetworkRequest.NetworkRequest, SDK.NetworkRequest.NetworkRequest>;
596
606
  }
597
607
 
598
- interface InitiatorInfo {
608
+ export interface InitiatorInfo {
599
609
  type: SDK.NetworkRequest.InitiatorType;
600
610
  // generally this is a url but can also contain "<anonymous>"
601
611
  url: Platform.DevToolsPath.UrlString;
@@ -88,7 +88,7 @@ export class Automapping {
88
88
  const networkProjects = this.workspace.projectsForType(Workspace.Workspace.projectTypes.Network);
89
89
  for (const networkProject of networkProjects) {
90
90
  for (const uiSourceCode of networkProject.uiSourceCodes()) {
91
- this.computeNetworkStatus(uiSourceCode);
91
+ void this.computeNetworkStatus(uiSourceCode);
92
92
  }
93
93
  }
94
94
  this.onSweepHappenedForTest();
@@ -137,7 +137,7 @@ export class Automapping {
137
137
  this.fileSystemUISourceCodes.add(uiSourceCode);
138
138
  this.scheduleSweep();
139
139
  } else if (project.type() === Workspace.Workspace.projectTypes.Network) {
140
- this.computeNetworkStatus(uiSourceCode);
140
+ void this.computeNetworkStatus(uiSourceCode);
141
141
  }
142
142
  }
143
143
 
@@ -173,20 +173,24 @@ export class Automapping {
173
173
  this.scheduleSweep();
174
174
  }
175
175
 
176
- private computeNetworkStatus(networkSourceCode: Workspace.UISourceCode.UISourceCode): void {
177
- if (this.sourceCodeToProcessingPromiseMap.has(networkSourceCode) ||
178
- this.sourceCodeToAutoMappingStatusMap.has(networkSourceCode)) {
179
- return;
176
+ computeNetworkStatus(networkSourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
177
+ const processingPromise = this.sourceCodeToProcessingPromiseMap.get(networkSourceCode);
178
+ if (processingPromise) {
179
+ return processingPromise;
180
+ }
181
+ if (this.sourceCodeToAutoMappingStatusMap.has(networkSourceCode)) {
182
+ return Promise.resolve();
180
183
  }
181
184
  if (this.interceptors.some(interceptor => interceptor(networkSourceCode))) {
182
- return;
185
+ return Promise.resolve();
183
186
  }
184
187
  if (networkSourceCode.url().startsWith('wasm://')) {
185
- return;
188
+ return Promise.resolve();
186
189
  }
187
190
  const createBindingPromise =
188
191
  this.createBinding(networkSourceCode).then(validateStatus.bind(this)).then(onStatus.bind(this));
189
192
  this.sourceCodeToProcessingPromiseMap.set(networkSourceCode, createBindingPromise);
193
+ return createBindingPromise;
190
194
 
191
195
  async function validateStatus(this: Automapping, status: AutomappingStatus|null): Promise<AutomappingStatus|null> {
192
196
  if (!status) {
@@ -242,7 +246,7 @@ export class Automapping {
242
246
  } else {
243
247
  if (networkContent.content) {
244
248
  // Trim trailing whitespaces because V8 adds trailing newline.
245
- isValid = fileContent.trimRight() === networkContent.content.trimRight();
249
+ isValid = fileContent.trimEnd() === networkContent.content.trimEnd();
246
250
  }
247
251
  }
248
252
  if (!isValid) {
@@ -252,18 +256,19 @@ export class Automapping {
252
256
  return status;
253
257
  }
254
258
 
255
- function onStatus(this: Automapping, status: AutomappingStatus|null): void {
259
+ async function onStatus(this: Automapping, status: AutomappingStatus|null): Promise<void> {
256
260
  if (this.sourceCodeToProcessingPromiseMap.get(networkSourceCode) !== createBindingPromise) {
257
261
  return;
258
262
  }
259
- this.sourceCodeToProcessingPromiseMap.delete(networkSourceCode);
260
263
  if (!status) {
261
264
  this.onBindingFailedForTest();
265
+ this.sourceCodeToProcessingPromiseMap.delete(networkSourceCode);
262
266
  return;
263
267
  }
264
268
  // TODO(lushnikov): remove this check once there's a single uiSourceCode per url. @see crbug.com/670180
265
269
  if (this.sourceCodeToAutoMappingStatusMap.has(status.network) ||
266
270
  this.sourceCodeToAutoMappingStatusMap.has(status.fileSystem)) {
271
+ this.sourceCodeToProcessingPromiseMap.delete(networkSourceCode);
267
272
  return;
268
273
  }
269
274
 
@@ -277,7 +282,8 @@ export class Automapping {
277
282
  this.scheduleSweep();
278
283
  }
279
284
  }
280
- void this.onStatusAdded.call(null, status);
285
+ await this.onStatusAdded.call(null, status);
286
+ this.sourceCodeToProcessingPromiseMap.delete(networkSourceCode);
281
287
  }
282
288
  }
283
289
 
@@ -29,6 +29,7 @@ export class PersistenceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTyp
29
29
  super();
30
30
  this.workspace = workspace;
31
31
  this.breakpointManager = breakpointManager;
32
+ this.breakpointManager.addUpdateBindingsCallback(this.#setupBindings.bind(this));
32
33
  this.filePathPrefixesToBindingCount = new FilePathPrefixesBindingCounts();
33
34
 
34
35
  this.subscribedBindingEventListeners = new Platform.MapUtilities.Multimap();
@@ -79,6 +80,13 @@ export class PersistenceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTyp
79
80
  await this.innerRemoveBinding(binding);
80
81
  }
81
82
 
83
+ #setupBindings(networkUISourceCode: Workspace.UISourceCode.UISourceCode): Promise<void> {
84
+ if (networkUISourceCode.project().type() !== Workspace.Workspace.projectTypes.Network) {
85
+ return Promise.resolve();
86
+ }
87
+ return this.mapping.computeNetworkStatus(networkUISourceCode);
88
+ }
89
+
82
90
  private async innerAddBinding(binding: PersistenceBinding): Promise<void> {
83
91
  bindings.set(binding.network, binding);
84
92
  bindings.set(binding.fileSystem, binding);
@@ -140,10 +148,10 @@ export class PersistenceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTyp
140
148
  this.dispatchEventToListeners(Events.BindingRemoved, binding);
141
149
  }
142
150
 
143
- private async onStatusAdded(status: AutomappingStatus): Promise<void> {
151
+ private onStatusAdded(status: AutomappingStatus): Promise<void> {
144
152
  const binding = new PersistenceBinding(status.network, status.fileSystem);
145
153
  statusBindings.set(status, binding);
146
- await this.innerAddBinding(binding);
154
+ return this.innerAddBinding(binding);
147
155
  }
148
156
 
149
157
  private async onStatusRemoved(status: AutomappingStatus): Promise<void> {
@@ -51,8 +51,13 @@ const computeScopeTree = async function(functionScope: SDK.DebuggerModel.ScopeCh
51
51
  functionEndLocation.columnNumber);
52
52
  const scopeText = text.extract(scopeRange);
53
53
  const scopeStart = text.toSourceRange(scopeRange).offset;
54
- const prefix = 'function fui';
55
- const scopeTree = await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptScopeTree(prefix + scopeText);
54
+ let prefix = 'function fui';
55
+ let scopeTree = await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptScopeTree(prefix + scopeText);
56
+ if (!scopeTree) {
57
+ // Try to parse the function as an arrow function.
58
+ prefix = 'let fui = ';
59
+ scopeTree = await Formatter.FormatterWorkerPool.formatterWorkerPool().javaScriptScopeTree(prefix + scopeText);
60
+ }
56
61
  if (!scopeTree) {
57
62
  return null;
58
63
  }