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.
- package/.vscode/devtools-workspace-launch.json +1 -1
- package/extension-api/ExtensionAPI.d.ts +4 -2
- package/front_end/core/i18n/locales/en-US.json +3 -0
- package/front_end/core/i18n/locales/en-XL.json +3 -0
- package/front_end/core/sdk/CSSProperty.ts +3 -3
- package/front_end/core/sdk/Script.ts +3 -2
- package/front_end/core/sdk/Target.ts +2 -2
- package/front_end/models/bindings/BreakpointManager.ts +57 -21
- package/front_end/models/bindings/ResourceMapping.ts +43 -10
- package/front_end/models/bindings/ResourceScriptMapping.ts +1 -1
- package/front_end/models/extensions/ExtensionAPI.ts +15 -4
- package/front_end/models/extensions/ExtensionServer.ts +3 -3
- package/front_end/models/extensions/RecorderExtensionEndpoint.ts +18 -1
- package/front_end/models/logs/NetworkLog.ts +18 -8
- package/front_end/models/persistence/Automapping.ts +18 -12
- package/front_end/models/persistence/PersistenceImpl.ts +10 -2
- package/front_end/models/source_map_scopes/NamesResolver.ts +7 -2
- package/front_end/models/workspace/WorkspaceImpl.ts +16 -9
- package/front_end/panels/application/DOMStorageItemsView.ts +6 -0
- package/front_end/panels/elements/ElementsTreeElement.ts +101 -90
- package/front_end/panels/network/NetworkSearchScope.ts +1 -1
- package/front_end/ui/components/data_grid/DataGrid.ts +5 -0
- package/front_end/ui/components/data_grid/DataGridController.ts +5 -0
- package/front_end/ui/legacy/components/data_grid/DataGrid.ts +4 -6
- package/package.json +1 -1
@@ -172,7 +172,8 @@ export namespace Chrome {
|
|
172
172
|
}
|
173
173
|
|
174
174
|
export interface RecorderExtensionPlugin {
|
175
|
-
stringify(
|
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):
|
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).
|
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.
|
234
|
-
propertyText = propertyText.
|
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.
|
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 (
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
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
|
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
|
-
|
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
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
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.
|
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.
|
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
|
-
|
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
|
-
|
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.
|
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):
|
200
|
-
|
201
|
-
|
202
|
-
|
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
|
-
|
177
|
-
|
178
|
-
|
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.
|
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
|
-
|
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
|
151
|
+
private onStatusAdded(status: AutomappingStatus): Promise<void> {
|
144
152
|
const binding = new PersistenceBinding(status.network, status.fileSystem);
|
145
153
|
statusBindings.set(status, binding);
|
146
|
-
|
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
|
-
|
55
|
-
|
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
|
}
|