chrome-devtools-frontend 1.0.1009019 → 1.0.1010492
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/extension-api/ExtensionAPI.d.ts +8 -6
- package/front_end/core/host/UserMetrics.ts +3 -1
- package/front_end/core/i18n/locales/en-US.json +16 -4
- package/front_end/core/i18n/locales/en-XL.json +16 -4
- package/front_end/core/sdk/CSSProperty.ts +3 -3
- package/front_end/core/sdk/DebuggerModel.ts +12 -3
- package/front_end/core/sdk/EmulationModel.ts +9 -3
- package/front_end/core/sdk/Script.ts +3 -2
- package/front_end/models/bindings/BreakpointManager.ts +58 -22
- package/front_end/models/bindings/DebuggerLanguagePlugins.ts +72 -65
- package/front_end/models/bindings/ResourceMapping.ts +43 -10
- package/front_end/models/bindings/ResourceScriptMapping.ts +13 -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/persistence/Automapping.ts +18 -12
- package/front_end/models/persistence/PersistenceImpl.ts +10 -2
- package/front_end/models/workspace/WorkspaceImpl.ts +16 -9
- package/front_end/panels/application/DOMStorageItemsView.ts +6 -0
- package/front_end/panels/console/ConsoleViewMessage.ts +2 -1
- package/front_end/panels/sources/CallStackSidebarPane.ts +11 -4
- package/front_end/panels/sources/DebuggerPlugin.ts +51 -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/Infobar.ts +1 -0
- package/front_end/ui/legacy/components/data_grid/DataGrid.ts +1 -1
- package/front_end/ui/legacy/infobar.css +17 -0
- package/package.json +1 -1
@@ -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);
|
@@ -423,6 +423,18 @@ export class ResourceScriptFile extends Common.ObjectWrapper.ObjectWrapper<Resou
|
|
423
423
|
return this.scriptInternal !== undefined && Boolean(this.scriptInternal.sourceMapURL);
|
424
424
|
}
|
425
425
|
|
426
|
+
async missingSymbolFiles(): Promise<string[]|null> {
|
427
|
+
if (!this.scriptInternal) {
|
428
|
+
return null;
|
429
|
+
}
|
430
|
+
const {pluginManager} = this.#resourceScriptMapping.debuggerWorkspaceBinding;
|
431
|
+
if (!pluginManager) {
|
432
|
+
return null;
|
433
|
+
}
|
434
|
+
const sources = await pluginManager.getSourcesForScript(this.scriptInternal);
|
435
|
+
return sources && 'missingSymbolFiles' in sources ? sources.missingSymbolFiles : null;
|
436
|
+
}
|
437
|
+
|
426
438
|
get script(): SDK.Script.Script|null {
|
427
439
|
return this.scriptInternal || null;
|
428
440
|
}
|
@@ -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
|
+
mediaType: 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
|
+
mediaType: 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
|
+
mediaType,
|
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, mediaType, port} = message;
|
228
|
+
RecorderPluginManager.instance().addPlugin(new RecorderExtensionEndpoint(pluginName, mediaType, 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 mediaType: string;
|
11
12
|
|
12
|
-
constructor(name: string, port: MessagePort) {
|
13
|
+
constructor(name: string, mediaType: string, port: MessagePort) {
|
13
14
|
super(port);
|
14
15
|
this.name = name;
|
16
|
+
this.mediaType = mediaType;
|
15
17
|
}
|
16
18
|
|
17
19
|
getName(): string {
|
18
20
|
return this.name;
|
19
21
|
}
|
20
22
|
|
23
|
+
getMediaType(): string {
|
24
|
+
return this.mediaType;
|
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
|
}
|
@@ -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> {
|
@@ -261,8 +261,8 @@ export class WorkspaceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTypes
|
|
261
261
|
|
262
262
|
// This method explicitly awaits the UISourceCode if not yet
|
263
263
|
// available.
|
264
|
-
uiSourceCodeForURLPromise(url: Platform.DevToolsPath.UrlString): Promise<UISourceCode> {
|
265
|
-
const uiSourceCode = this.uiSourceCodeForURL(url);
|
264
|
+
uiSourceCodeForURLPromise(url: Platform.DevToolsPath.UrlString, type?: projectTypes): Promise<UISourceCode> {
|
265
|
+
const uiSourceCode = this.uiSourceCodeForURL(url, type);
|
266
266
|
if (uiSourceCode) {
|
267
267
|
return Promise.resolve(uiSourceCode);
|
268
268
|
}
|
@@ -270,24 +270,31 @@ export class WorkspaceImpl extends Common.ObjectWrapper.ObjectWrapper<EventTypes
|
|
270
270
|
const descriptor = this.addEventListener(Events.UISourceCodeAdded, event => {
|
271
271
|
const uiSourceCode = event.data;
|
272
272
|
if (uiSourceCode.url() === url) {
|
273
|
-
|
274
|
-
|
273
|
+
if (!type || type === uiSourceCode.project().type()) {
|
274
|
+
this.removeEventListener(Events.UISourceCodeAdded, descriptor.listener);
|
275
|
+
resolve(uiSourceCode);
|
276
|
+
}
|
275
277
|
}
|
276
278
|
});
|
277
279
|
});
|
278
280
|
}
|
279
281
|
|
280
|
-
uiSourceCodeForURL(url: Platform.DevToolsPath.UrlString): UISourceCode|null {
|
282
|
+
uiSourceCodeForURL(url: Platform.DevToolsPath.UrlString, type?: projectTypes): UISourceCode|null {
|
281
283
|
for (const project of this.projectsInternal.values()) {
|
282
|
-
|
283
|
-
|
284
|
-
|
284
|
+
// For snippets, we may get two different UISourceCodes for the same url (one belonging to
|
285
|
+
// the file system project, one belonging to the network project). Allow selecting the UISourceCode
|
286
|
+
// for a specific project type.
|
287
|
+
if (!type || project.type() === type) {
|
288
|
+
const uiSourceCode = project.uiSourceCodeForURL(url);
|
289
|
+
if (uiSourceCode) {
|
290
|
+
return uiSourceCode;
|
291
|
+
}
|
285
292
|
}
|
286
293
|
}
|
287
294
|
return null;
|
288
295
|
}
|
289
296
|
|
290
|
-
uiSourceCodesForProjectType(type:
|
297
|
+
uiSourceCodesForProjectType(type: projectTypes): UISourceCode[] {
|
291
298
|
const result: UISourceCode[] = [];
|
292
299
|
for (const project of this.projectsInternal.values()) {
|
293
300
|
if (project.type() === type) {
|
@@ -56,6 +56,11 @@ const UIStrings = {
|
|
56
56
|
*@description Data grid name for DOM Storage Items data grids
|
57
57
|
*/
|
58
58
|
domStorageItems: 'DOM Storage Items',
|
59
|
+
/**
|
60
|
+
*@description Text for announcing number of entries after filtering
|
61
|
+
*@example {'DOM Storage Items'} PH1
|
62
|
+
*/
|
63
|
+
domStorageItemsCleared: '{PH1} cleared',
|
59
64
|
/**
|
60
65
|
*@description Text in DOMStorage Items View of the Application panel
|
61
66
|
*/
|
@@ -149,6 +154,7 @@ export class DOMStorageItemsView extends StorageItemsView {
|
|
149
154
|
|
150
155
|
this.dataGrid.rootNode().removeChildren();
|
151
156
|
this.dataGrid.addCreationNode(false);
|
157
|
+
UI.ARIAUtils.alert(i18nString(UIStrings.domStorageItemsCleared, {PH1: this.dataGrid.displayName}));
|
152
158
|
this.setCanDeleteSelected(false);
|
153
159
|
}
|
154
160
|
|
@@ -1397,7 +1397,8 @@ export class ConsoleViewMessage implements ConsoleViewportElement {
|
|
1397
1397
|
if (scripts.length) {
|
1398
1398
|
const location =
|
1399
1399
|
new SDK.DebuggerModel.Location(debuggerModel, scripts[0].scriptId, lineNumber || 0, columnNumber);
|
1400
|
-
|
1400
|
+
const functionInfo = await debuggerWorkspaceBinding.pluginManager.getFunctionInfo(scripts[0], location);
|
1401
|
+
return functionInfo && 'frames' in functionInfo ? functionInfo : {frames: []};
|
1401
1402
|
}
|
1402
1403
|
}
|
1403
1404
|
|
@@ -86,6 +86,11 @@ const UIStrings = {
|
|
86
86
|
*@description Text in Call Stack Sidebar Pane of the Sources panel when some call frames have warnings
|
87
87
|
*/
|
88
88
|
callFrameWarnings: 'Some call frames have warnings',
|
89
|
+
/**
|
90
|
+
*@description Error message that is displayed in UI when a file needed for debugging information for a call frame is missing
|
91
|
+
*@example {src/myapp.debug.wasm.dwp} PH1
|
92
|
+
*/
|
93
|
+
debugFileNotFound: 'Failed to load debug file "{PH1}".',
|
89
94
|
/**
|
90
95
|
* @description A contex menu item in the Call Stack Sidebar Pane. "Restart" is a verb and
|
91
96
|
* "frame" is a noun. "Frame" refers to an individual item in the call stack, i.e. a call frame.
|
@@ -204,8 +209,8 @@ export class CallStackSidebarPane extends UI.View.SimpleView implements UI.Conte
|
|
204
209
|
return item;
|
205
210
|
});
|
206
211
|
itemPromises.push(itemPromise);
|
207
|
-
|
208
|
-
uniqueWarnings.add(
|
212
|
+
if (frame.missingDebugInfoDetails) {
|
213
|
+
uniqueWarnings.add(frame.missingDebugInfoDetails.details);
|
209
214
|
}
|
210
215
|
}
|
211
216
|
const items = await Promise.all(itemPromises);
|
@@ -316,9 +321,11 @@ export class CallStackSidebarPane extends UI.View.SimpleView implements UI.Conte
|
|
316
321
|
element.appendChild(UI.Icon.Icon.create('smallicon-thick-right-arrow', 'selected-call-frame-icon'));
|
317
322
|
element.tabIndex = item === this.list.selectedItem() ? 0 : -1;
|
318
323
|
|
319
|
-
if (callframe && callframe.
|
324
|
+
if (callframe && callframe.missingDebugInfoDetails) {
|
320
325
|
const icon = UI.Icon.Icon.create('smallicon-warning', 'call-frame-warning-icon');
|
321
|
-
|
326
|
+
const messages =
|
327
|
+
callframe.missingDebugInfoDetails.resources.map(r => i18nString(UIStrings.debugFileNotFound, {PH1: r}));
|
328
|
+
UI.Tooltip.Tooltip.install(icon, [callframe.missingDebugInfoDetails.details, ...messages].join('\n'));
|
322
329
|
element.appendChild(icon);
|
323
330
|
}
|
324
331
|
return element;
|
@@ -141,6 +141,16 @@ const UIStrings = {
|
|
141
141
|
*@description Text in Debugger Plugin of the Sources panel
|
142
142
|
*/
|
143
143
|
theDebuggerWillSkipStepping: 'The debugger will skip stepping through this script, and will not stop on exceptions.',
|
144
|
+
/**
|
145
|
+
*@description Error message that is displayed in UI when a file needed for debugging information for a call frame is missing
|
146
|
+
*@example {src/myapp.debug.wasm.dwp} PH1
|
147
|
+
*/
|
148
|
+
debugFileNotFound: 'Failed to load debug file "{PH1}".',
|
149
|
+
/**
|
150
|
+
*@description Error message that is displayed when no debug info could be loaded
|
151
|
+
*@example {app.wasm} PH1
|
152
|
+
*/
|
153
|
+
debugInfoNotFound: 'Failed to load any debug info for {PH1}.',
|
144
154
|
};
|
145
155
|
const str_ = i18n.i18n.registerUIStrings('panels/sources/DebuggerPlugin.ts', UIStrings);
|
146
156
|
const i18nString = i18n.i18n.getLocalizedString.bind(undefined, str_);
|
@@ -194,6 +204,7 @@ export class DebuggerPlugin extends Plugin {
|
|
194
204
|
private prettyPrintInfobar!: UI.Infobar.Infobar|null;
|
195
205
|
private refreshBreakpointsTimeout: undefined|number = undefined;
|
196
206
|
private activeBreakpointDialog: BreakpointEditDialog|null = null;
|
207
|
+
private missingDebugInfoBar: UI.Infobar.Infobar|null = null;
|
197
208
|
|
198
209
|
constructor(
|
199
210
|
uiSourceCode: Workspace.UISourceCode.UISourceCode,
|
@@ -237,7 +248,6 @@ export class DebuggerPlugin extends Plugin {
|
|
237
248
|
|
238
249
|
this.ignoreListInfobar = null;
|
239
250
|
this.showIgnoreListInfobarIfNeeded();
|
240
|
-
|
241
251
|
for (const scriptFile of this.scriptFileForDebuggerModel.values()) {
|
242
252
|
scriptFile.checkMapping();
|
243
253
|
}
|
@@ -332,6 +342,9 @@ export class DebuggerPlugin extends Plugin {
|
|
332
342
|
editor.dispatch({effects: SourceFrame.SourceFrame.addNonBreakableLines.of(linePositions)});
|
333
343
|
}
|
334
344
|
}, console.error);
|
345
|
+
if (this.missingDebugInfoBar) {
|
346
|
+
this.attachInfobar(this.missingDebugInfoBar);
|
347
|
+
}
|
335
348
|
if (!this.muted) {
|
336
349
|
void this.refreshBreakpoints();
|
337
350
|
}
|
@@ -1350,6 +1363,42 @@ export class DebuggerPlugin extends Plugin {
|
|
1350
1363
|
if (newScriptFile.hasSourceMapURL()) {
|
1351
1364
|
this.showSourceMapInfobar();
|
1352
1365
|
}
|
1366
|
+
|
1367
|
+
void newScriptFile.missingSymbolFiles().then(resources => {
|
1368
|
+
if (resources) {
|
1369
|
+
const details = i18nString(UIStrings.debugInfoNotFound, {PH1: newScriptFile.uiSourceCode.url()});
|
1370
|
+
this.updateMissingDebugInfoInfobar({resources, details});
|
1371
|
+
} else {
|
1372
|
+
this.updateMissingDebugInfoInfobar(null);
|
1373
|
+
}
|
1374
|
+
});
|
1375
|
+
}
|
1376
|
+
|
1377
|
+
private updateMissingDebugInfoInfobar(warning: SDK.DebuggerModel.MissingDebugInfoDetails|null): void {
|
1378
|
+
if (this.missingDebugInfoBar) {
|
1379
|
+
return;
|
1380
|
+
}
|
1381
|
+
if (warning === null) {
|
1382
|
+
this.removeInfobar(this.missingDebugInfoBar);
|
1383
|
+
this.missingDebugInfoBar = null;
|
1384
|
+
return;
|
1385
|
+
}
|
1386
|
+
this.missingDebugInfoBar = UI.Infobar.Infobar.create(UI.Infobar.Type.Error, warning.details, []);
|
1387
|
+
if (!this.missingDebugInfoBar) {
|
1388
|
+
return;
|
1389
|
+
}
|
1390
|
+
for (const resource of warning.resources) {
|
1391
|
+
const detailsRow =
|
1392
|
+
this.missingDebugInfoBar?.createDetailsRowMessage(i18nString(UIStrings.debugFileNotFound, {PH1: resource}));
|
1393
|
+
if (detailsRow) {
|
1394
|
+
detailsRow.classList.add('infobar-selectable');
|
1395
|
+
}
|
1396
|
+
}
|
1397
|
+
this.missingDebugInfoBar.setCloseCallback(() => {
|
1398
|
+
this.removeInfobar(this.missingDebugInfoBar);
|
1399
|
+
this.missingDebugInfoBar = null;
|
1400
|
+
});
|
1401
|
+
this.attachInfobar(this.missingDebugInfoBar);
|
1353
1402
|
}
|
1354
1403
|
|
1355
1404
|
private showSourceMapInfobar(): void {
|
@@ -1481,6 +1530,7 @@ export class DebuggerPlugin extends Plugin {
|
|
1481
1530
|
const uiLocation = await liveLocation.uiLocation();
|
1482
1531
|
if (uiLocation && uiLocation.uiSourceCode.url() === this.uiSourceCode.url()) {
|
1483
1532
|
this.setExecutionLocation(uiLocation);
|
1533
|
+
this.updateMissingDebugInfoInfobar(callFrame.missingDebugInfoDetails);
|
1484
1534
|
} else {
|
1485
1535
|
this.setExecutionLocation(null);
|
1486
1536
|
}
|
@@ -53,6 +53,7 @@ export interface DataGridData {
|
|
53
53
|
rows: Row[];
|
54
54
|
activeSort: SortState|null;
|
55
55
|
contextMenus?: DataGridContextMenusConfiguration;
|
56
|
+
label?: string;
|
56
57
|
}
|
57
58
|
|
58
59
|
const enum UserScrollState {
|
@@ -76,6 +77,7 @@ export class DataGrid extends HTMLElement {
|
|
76
77
|
#isRendering = false;
|
77
78
|
#userScrollState: UserScrollState = UserScrollState.NOT_SCROLLED;
|
78
79
|
#contextMenus?: DataGridContextMenusConfiguration = undefined;
|
80
|
+
#label?: string = undefined;
|
79
81
|
#currentResize: {
|
80
82
|
rightCellCol: HTMLTableColElement,
|
81
83
|
leftCellCol: HTMLTableColElement,
|
@@ -132,6 +134,7 @@ export class DataGrid extends HTMLElement {
|
|
132
134
|
rows: this.#rows as Row[],
|
133
135
|
activeSort: this.#sortState,
|
134
136
|
contextMenus: this.#contextMenus,
|
137
|
+
label: this.#label,
|
135
138
|
};
|
136
139
|
}
|
137
140
|
|
@@ -143,6 +146,7 @@ export class DataGrid extends HTMLElement {
|
|
143
146
|
});
|
144
147
|
this.#sortState = data.activeSort;
|
145
148
|
this.#contextMenus = data.contextMenus;
|
149
|
+
this.#label = data.label;
|
146
150
|
|
147
151
|
/**
|
148
152
|
* On first render, now we have data, we can figure out which cell is the
|
@@ -711,6 +715,7 @@ export class DataGrid extends HTMLElement {
|
|
711
715
|
})}
|
712
716
|
<div class="wrapping-container" @scroll=${this.#onScroll} @focusout=${this.#onFocusOut}>
|
713
717
|
<table
|
718
|
+
aria-label=${LitHtml.Directives.ifDefined(this.#label)}
|
714
719
|
aria-rowcount=${this.#rows.length}
|
715
720
|
aria-colcount=${this.#columns.length}
|
716
721
|
@keydown=${this.#onTableKeyDown}
|
@@ -23,6 +23,7 @@ export interface DataGridControllerData {
|
|
23
23
|
*/
|
24
24
|
initialSort?: SortState;
|
25
25
|
contextMenus?: DataGridContextMenusConfiguration;
|
26
|
+
label?: string;
|
26
27
|
}
|
27
28
|
|
28
29
|
export class DataGridController extends HTMLElement {
|
@@ -33,6 +34,7 @@ export class DataGridController extends HTMLElement {
|
|
33
34
|
#columns: readonly Column[] = [];
|
34
35
|
#rows: Row[] = [];
|
35
36
|
#contextMenus?: DataGridContextMenusConfiguration = undefined;
|
37
|
+
#label?: string = undefined;
|
36
38
|
|
37
39
|
/**
|
38
40
|
* Because the controller will sort data in place (e.g. mutate it) when we get
|
@@ -56,6 +58,7 @@ export class DataGridController extends HTMLElement {
|
|
56
58
|
rows: this.#originalRows as Row[],
|
57
59
|
filters: this.#filters,
|
58
60
|
contextMenus: this.#contextMenus,
|
61
|
+
label: this.#label,
|
59
62
|
};
|
60
63
|
}
|
61
64
|
|
@@ -65,6 +68,7 @@ export class DataGridController extends HTMLElement {
|
|
65
68
|
this.#contextMenus = data.contextMenus;
|
66
69
|
this.#filters = data.filters || [];
|
67
70
|
this.#contextMenus = data.contextMenus;
|
71
|
+
this.#label = data.label;
|
68
72
|
|
69
73
|
this.#columns = [...this.#originalColumns];
|
70
74
|
this.#rows = this.#cloneAndFilterRows(data.rows, this.#filters);
|
@@ -210,6 +214,7 @@ export class DataGridController extends HTMLElement {
|
|
210
214
|
rows: this.#rows,
|
211
215
|
activeSort: this.#sortState,
|
212
216
|
contextMenus: this.#contextMenus,
|
217
|
+
label: this.#label,
|
213
218
|
} as DataGridData}
|
214
219
|
@columnheaderclick=${this.#onColumnHeaderClick}
|
215
220
|
@contextmenucolumnsortclick=${this.#onContextMenuColumnSortClick}
|