chrome-devtools-frontend 1.0.1021582 → 1.0.1022059

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.eslintignore +14 -1
  2. package/extension-api/ExtensionAPI.d.ts +54 -4
  3. package/front_end/.eslintrc.js +3 -1
  4. package/front_end/core/host/InspectorFrontendHostAPI.ts +1 -0
  5. package/front_end/core/host/UserMetrics.ts +18 -0
  6. package/front_end/core/i18n/locales/en-US.json +27 -0
  7. package/front_end/core/i18n/locales/en-XL.json +27 -0
  8. package/front_end/devtools_compatibility.js +1 -0
  9. package/front_end/legacy_test_runner/sources_test_runner/DebuggerTestRunner.js +4 -3
  10. package/front_end/models/bindings/DebuggerLanguagePlugins.ts +157 -117
  11. package/front_end/models/extensions/ExtensionAPI.ts +101 -13
  12. package/front_end/models/extensions/ExtensionServer.ts +63 -1
  13. package/front_end/models/extensions/LanguageExtensionEndpoint.ts +16 -3
  14. package/front_end/models/timeline_model/TimelineModel.ts +164 -7
  15. package/front_end/panels/application/AppManifestView.ts +13 -2
  16. package/front_end/panels/application/ApplicationPanelSidebar.ts +67 -5
  17. package/front_end/panels/elements/ElementsTreeOutline.ts +41 -7
  18. package/front_end/panels/elements/TopLayerContainer.ts +9 -1
  19. package/front_end/panels/elements/components/AdornerManager.ts +7 -0
  20. package/front_end/panels/elements/elementsTreeOutline.css +4 -0
  21. package/front_end/third_party/acorn/acorn.ts +1 -1
  22. package/front_end/third_party/chromium/client-variations/client-variations.ts +1 -1
  23. package/front_end/third_party/diff/DiffWrapper.ts +2 -0
  24. package/front_end/third_party/i18n/i18n-impl.ts +5 -1
  25. package/front_end/third_party/i18n/i18n.ts +1 -1
  26. package/front_end/third_party/i18n/locales.ts +1 -1
  27. package/front_end/third_party/marked/marked.ts +1 -1
  28. package/front_end/third_party/puppeteer/puppeteer.ts +6 -6
  29. package/front_end/ui/components/linear_memory_inspector/LinearMemoryInspectorController.ts +23 -1
  30. package/front_end/ui/legacy/ReportView.ts +8 -0
  31. package/package.json +1 -1
  32. package/scripts/eslint_rules/lib/custom_element_definitions_location.js +28 -13
  33. package/scripts/eslint_rules/lib/es_modules_import.js +5 -1
  34. package/scripts/eslint_rules/tests/custom_element_definitions_location_test.js +9 -2
  35. package/scripts/eslint_rules/tests/es_modules_import_test.js +5 -0
@@ -14,6 +14,7 @@ import {ContentProviderBasedProject} from './ContentProviderBasedProject.js';
14
14
 
15
15
  import type {DebuggerWorkspaceBinding} from './DebuggerWorkspaceBinding.js';
16
16
  import {NetworkProject} from './NetworkProject.js';
17
+ import {assertNotNullOrUndefined} from '../../core/platform/platform.js';
17
18
 
18
19
  const UIStrings = {
19
20
  /**
@@ -218,7 +219,7 @@ async function getValueTreeForExpression(
218
219
  try {
219
220
  typeInfo = await plugin.getTypeInfo(expression, location);
220
221
  } catch (e) {
221
- FormattingError.throwLocal(callFrame, e.message);
222
+ throw FormattingError.makeLocal(callFrame, e.message);
222
223
  }
223
224
  // If there's no type information we cannot represent this expression.
224
225
  if (!typeInfo) {
@@ -454,7 +455,7 @@ class FormattingError extends Error {
454
455
  this.exceptionDetails = exceptionDetails;
455
456
  }
456
457
 
457
- static throwLocal(callFrame: SDK.DebuggerModel.CallFrame, message: string): void {
458
+ static makeLocal(callFrame: SDK.DebuggerModel.CallFrame, message: string): FormattingError {
458
459
  const exception: Protocol.Runtime.RemoteObject = {
459
460
  type: Protocol.Runtime.RemoteObjectType.Object,
460
461
  subtype: Protocol.Runtime.RemoteObjectSubtype.Error,
@@ -463,7 +464,7 @@ class FormattingError extends Error {
463
464
  const exceptionDetails: Protocol.Runtime
464
465
  .ExceptionDetails = {text: 'Uncaught', exceptionId: -1, columnNumber: 0, lineNumber: 0, exception};
465
466
  const errorObject = callFrame.debuggerModel.runtimeModel().createRemoteObject(exception);
466
- throw new FormattingError(errorObject, exceptionDetails);
467
+ return new FormattingError(errorObject, exceptionDetails);
467
468
  }
468
469
  }
469
470
 
@@ -623,12 +624,14 @@ class SourceScopeRemoteObject extends SDK.RemoteObject.RemoteObjectImpl {
623
624
  variables: Chrome.DevTools.Variable[];
624
625
  #callFrame: SDK.DebuggerModel.CallFrame;
625
626
  #plugin: DebuggerLanguagePlugin;
627
+ stopId: StopId;
626
628
 
627
- constructor(callFrame: SDK.DebuggerModel.CallFrame, plugin: DebuggerLanguagePlugin) {
629
+ constructor(callFrame: SDK.DebuggerModel.CallFrame, stopId: StopId, plugin: DebuggerLanguagePlugin) {
628
630
  super(callFrame.debuggerModel.runtimeModel(), undefined, 'object', undefined, null);
629
631
  this.variables = [];
630
632
  this.#callFrame = callFrame;
631
633
  this.#plugin = plugin;
634
+ this.stopId = stopId;
632
635
  }
633
636
 
634
637
  async doGetProperties(ownProperties: boolean, accessorPropertiesOnly: boolean, _generatePreview: boolean):
@@ -699,13 +702,13 @@ export class SourceScope implements SDK.DebuggerModel.ScopeChainEntry {
699
702
  readonly #startLocationInternal: SDK.DebuggerModel.Location|null;
700
703
  readonly #endLocationInternal: SDK.DebuggerModel.Location|null;
701
704
  constructor(
702
- callFrame: SDK.DebuggerModel.CallFrame, type: string, typeName: string, icon: string|undefined,
705
+ callFrame: SDK.DebuggerModel.CallFrame, stopId: StopId, type: string, typeName: string, icon: string|undefined,
703
706
  plugin: DebuggerLanguagePlugin) {
704
707
  this.#callFrameInternal = callFrame;
705
708
  this.#typeInternal = type;
706
709
  this.#typeNameInternal = typeName;
707
710
  this.#iconInternal = icon;
708
- this.#objectInternal = new SourceScopeRemoteObject(callFrame, plugin);
711
+ this.#objectInternal = new SourceScopeRemoteObject(callFrame, stopId, plugin);
709
712
  this.#startLocationInternal = null;
710
713
  this.#endLocationInternal = null;
711
714
  }
@@ -764,6 +767,115 @@ export class SourceScope implements SDK.DebuggerModel.ScopeChainEntry {
764
767
  }
765
768
  }
766
769
 
770
+ export class ExtensionRemoteObject extends SDK.RemoteObject.RemoteObject {
771
+ private readonly extensionObject: Chrome.DevTools.RemoteObject;
772
+ private readonly plugin: DebuggerLanguagePlugin;
773
+ readonly callFrame: SDK.DebuggerModel.CallFrame;
774
+ constructor(
775
+ callFrame: SDK.DebuggerModel.CallFrame, extensionObject: Chrome.DevTools.RemoteObject,
776
+ plugin: DebuggerLanguagePlugin) {
777
+ super();
778
+ this.extensionObject = extensionObject;
779
+ this.plugin = plugin;
780
+ this.callFrame = callFrame;
781
+ }
782
+
783
+ get linearMemoryAddress(): number|undefined {
784
+ return this.extensionObject.linearMemoryAddress;
785
+ }
786
+
787
+ get objectId(): Protocol.Runtime.RemoteObjectId|undefined {
788
+ return this.extensionObject.objectId as Protocol.Runtime.RemoteObjectId;
789
+ }
790
+
791
+ get type(): string {
792
+ if (this.extensionObject.type === 'array' || this.extensionObject.type === 'null') {
793
+ return 'object';
794
+ }
795
+ return this.extensionObject.type;
796
+ }
797
+
798
+ get subtype(): string|undefined {
799
+ if (this.extensionObject.type === 'array' || this.extensionObject.type === 'null') {
800
+ return this.extensionObject.type;
801
+ }
802
+ return undefined;
803
+ }
804
+
805
+ get value(): unknown {
806
+ return this.extensionObject.value;
807
+ }
808
+
809
+ unserializableValue(): string|undefined {
810
+ return undefined;
811
+ }
812
+
813
+ get description(): string|undefined {
814
+ return this.extensionObject.description;
815
+ }
816
+
817
+ set description(description: string|undefined) {
818
+ }
819
+
820
+ get hasChildren(): boolean {
821
+ return this.extensionObject.hasChildren;
822
+ }
823
+
824
+ get preview(): Protocol.Runtime.ObjectPreview|undefined {
825
+ return undefined;
826
+ }
827
+
828
+ get className(): string|null {
829
+ return this.extensionObject.className ?? null;
830
+ }
831
+
832
+ arrayLength(): number {
833
+ return 0;
834
+ }
835
+
836
+ arrayBufferByteLength(): number {
837
+ return 0;
838
+ }
839
+
840
+ getOwnProperties(_generatePreview: boolean, _nonIndexedPropertiesOnly?: boolean):
841
+ Promise<SDK.RemoteObject.GetPropertiesResult> {
842
+ return this.getAllProperties(false, _generatePreview, _nonIndexedPropertiesOnly);
843
+ }
844
+
845
+ async getAllProperties(
846
+ _accessorPropertiesOnly: boolean, _generatePreview: boolean,
847
+ _nonIndexedPropertiesOnly?: boolean): Promise<SDK.RemoteObject.GetPropertiesResult> {
848
+ const {objectId} = this.extensionObject;
849
+ if (objectId) {
850
+ assertNotNullOrUndefined(this.plugin.getProperties);
851
+ const extensionObjectProperties = await this.plugin.getProperties(objectId);
852
+ const properties = extensionObjectProperties.map(
853
+ p => new SDK.RemoteObject.RemoteObjectProperty(
854
+ p.name, new ExtensionRemoteObject(this.callFrame, p.value, this.plugin)));
855
+ return {properties, internalProperties: null};
856
+ }
857
+
858
+ return {properties: null, internalProperties: null};
859
+ }
860
+
861
+ release(): void {
862
+ const {objectId} = this.extensionObject;
863
+ if (objectId) {
864
+ assertNotNullOrUndefined(this.plugin.releaseObject);
865
+ void this.plugin.releaseObject(objectId);
866
+ }
867
+ }
868
+
869
+ debuggerModel(): SDK.DebuggerModel.DebuggerModel {
870
+ return this.callFrame.debuggerModel;
871
+ }
872
+
873
+ runtimeModel(): SDK.RuntimeModel.RuntimeModel {
874
+ return this.callFrame.debuggerModel.runtimeModel();
875
+ }
876
+ }
877
+
878
+ export type StopId = bigint;
767
879
  export class DebuggerLanguagePluginManager implements
768
880
  SDK.TargetManager.SDKModelObserver<SDK.DebuggerModel.DebuggerModel> {
769
881
  readonly #workspace: Workspace.Workspace.WorkspaceImpl;
@@ -776,6 +888,9 @@ export class DebuggerLanguagePluginManager implements
776
888
  scripts: Array<SDK.Script.Script>,
777
889
  addRawModulePromise: Promise<Array<Platform.DevToolsPath.UrlString>|{missingSymbolFiles: string[]}>,
778
890
  }>;
891
+ private readonly callFrameByStopId: Map<StopId, SDK.DebuggerModel.CallFrame> = new Map();
892
+ private readonly stopIdByCallFrame: Map<SDK.DebuggerModel.CallFrame, StopId> = new Map();
893
+ private nextStopId: StopId = 0n;
779
894
 
780
895
  constructor(
781
896
  targetManager: SDK.TargetManager.TargetManager, workspace: Workspace.Workspace.WorkspaceImpl,
@@ -818,10 +933,27 @@ export class DebuggerLanguagePluginManager implements
818
933
  const {exception: object, exceptionDetails} = error;
819
934
  return {object, exceptionDetails};
820
935
  }
821
- return {error: error.message};
936
+ const {exception: object, exceptionDetails} = FormattingError.makeLocal(callFrame, error.message);
937
+ return {object, exceptionDetails};
822
938
  }
823
939
  }
824
940
 
941
+ stopIdForCallFrame(callFrame: SDK.DebuggerModel.CallFrame): StopId {
942
+ let stopId = this.stopIdByCallFrame.get(callFrame);
943
+ if (stopId !== undefined) {
944
+ return stopId;
945
+ }
946
+
947
+ stopId = this.nextStopId++;
948
+ this.stopIdByCallFrame.set(callFrame, stopId);
949
+ this.callFrameByStopId.set(stopId, callFrame);
950
+ return stopId;
951
+ }
952
+
953
+ callFrameForStopId(stopId: StopId): SDK.DebuggerModel.CallFrame|undefined {
954
+ return this.callFrameByStopId.get(stopId);
955
+ }
956
+
825
957
  private expandCallFrames(callFrames: SDK.DebuggerModel.CallFrame[]): Promise<SDK.DebuggerModel.CallFrame[]> {
826
958
  return Promise
827
959
  .all(callFrames.map(async callFrame => {
@@ -850,6 +982,7 @@ export class DebuggerLanguagePluginManager implements
850
982
  this.#debuggerModelToData.set(debuggerModel, new ModelData(debuggerModel, this.#workspace));
851
983
  debuggerModel.addEventListener(SDK.DebuggerModel.Events.GlobalObjectCleared, this.globalObjectCleared, this);
852
984
  debuggerModel.addEventListener(SDK.DebuggerModel.Events.ParsedScriptSource, this.parsedScriptSource, this);
985
+ debuggerModel.addEventListener(SDK.DebuggerModel.Events.DebuggerResumed, this.debuggerResumed, this);
853
986
  debuggerModel.setEvaluateOnCallFrameCallback(this.evaluateOnCallFrame.bind(this));
854
987
  debuggerModel.setExpandCallFramesCallback(this.expandCallFrames.bind(this));
855
988
  }
@@ -857,6 +990,7 @@ export class DebuggerLanguagePluginManager implements
857
990
  modelRemoved(debuggerModel: SDK.DebuggerModel.DebuggerModel): void {
858
991
  debuggerModel.removeEventListener(SDK.DebuggerModel.Events.GlobalObjectCleared, this.globalObjectCleared, this);
859
992
  debuggerModel.removeEventListener(SDK.DebuggerModel.Events.ParsedScriptSource, this.parsedScriptSource, this);
993
+ debuggerModel.removeEventListener(SDK.DebuggerModel.Events.DebuggerResumed, this.debuggerResumed, this);
860
994
  debuggerModel.setEvaluateOnCallFrameCallback(null);
861
995
  debuggerModel.setExpandCallFramesCallback(null);
862
996
  const modelData = this.#debuggerModelToData.get(debuggerModel);
@@ -1144,6 +1278,17 @@ export class DebuggerLanguagePluginManager implements
1144
1278
  }
1145
1279
  }
1146
1280
 
1281
+ private debuggerResumed(event: Common.EventTarget.EventTargetEvent<SDK.DebuggerModel.DebuggerModel>): void {
1282
+ const resumedFrames =
1283
+ Array.from(this.callFrameByStopId.values()).filter(callFrame => callFrame.debuggerModel === event.data);
1284
+ for (const callFrame of resumedFrames) {
1285
+ const stopId = this.stopIdByCallFrame.get(callFrame);
1286
+ assertNotNullOrUndefined(stopId);
1287
+ this.stopIdByCallFrame.delete(callFrame);
1288
+ this.callFrameByStopId.delete(stopId);
1289
+ }
1290
+ }
1291
+
1147
1292
  getSourcesForScript(script: SDK.Script.Script):
1148
1293
  Promise<Array<Platform.DevToolsPath.UrlString>|{missingSymbolFiles: string[]}|undefined> {
1149
1294
  const rawModuleId = rawModuleIdForScript(script);
@@ -1167,6 +1312,8 @@ export class DebuggerLanguagePluginManager implements
1167
1312
  inlineFrameIndex: callFrame.inlineFrameIndex,
1168
1313
  };
1169
1314
 
1315
+ const stopId = this.stopIdForCallFrame(callFrame);
1316
+
1170
1317
  try {
1171
1318
  const sourceMapping = await plugin.rawLocationToSourceLocation(location);
1172
1319
  if (sourceMapping.length === 0) {
@@ -1178,7 +1325,7 @@ export class DebuggerLanguagePluginManager implements
1178
1325
  let scope = scopes.get(variable.scope);
1179
1326
  if (!scope) {
1180
1327
  const {type, typeName, icon} = await plugin.getScopeInfo(variable.scope);
1181
- scope = new SourceScope(callFrame, type, typeName, icon, plugin);
1328
+ scope = new SourceScope(callFrame, stopId, type, typeName, icon, plugin);
1182
1329
  scopes.set(variable.scope, scope);
1183
1330
  }
1184
1331
  scope.object().variables.push(variable);
@@ -1379,114 +1526,7 @@ class ModelData {
1379
1526
  }
1380
1527
  }
1381
1528
 
1382
- export class DebuggerLanguagePlugin implements Chrome.DevTools.LanguageExtensionPlugin {
1529
+ export interface DebuggerLanguagePlugin extends Chrome.DevTools.LanguageExtensionPlugin {
1383
1530
  name: string;
1384
- constructor(name: string) {
1385
- this.name = name;
1386
- }
1387
-
1388
- handleScript(_script: SDK.Script.Script): boolean {
1389
- throw new Error('Not implemented yet');
1390
- }
1391
-
1392
- dispose(): void {
1393
- }
1394
-
1395
- /** Notify the #plugin about a new script
1396
- */
1397
- async addRawModule(_rawModuleId: string, _symbolsURL: string, _rawModule: Chrome.DevTools.RawModule):
1398
- Promise<string[]|{missingSymbolFiles: string[]}> {
1399
- throw new Error('Not implemented yet');
1400
- }
1401
-
1402
- /** Find #locations in raw modules from a #location in a source file
1403
- */
1404
- async sourceLocationToRawLocation(_sourceLocation: Chrome.DevTools.SourceLocation):
1405
- Promise<Chrome.DevTools.RawLocationRange[]> {
1406
- throw new Error('Not implemented yet');
1407
- }
1408
-
1409
- /** Find #locations in source files from a #location in a raw module
1410
- */
1411
- async rawLocationToSourceLocation(_rawLocation: Chrome.DevTools.RawLocation):
1412
- Promise<Chrome.DevTools.SourceLocation[]> {
1413
- throw new Error('Not implemented yet');
1414
- }
1415
-
1416
- /** Return detailed information about a scope
1417
- */
1418
- async getScopeInfo(_type: string): Promise<Chrome.DevTools.ScopeInfo> {
1419
- throw new Error('Not implemented yet');
1420
- }
1421
-
1422
- /** List all variables in lexical scope at a given #location in a raw module
1423
- */
1424
- async listVariablesInScope(_rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.Variable[]> {
1425
- throw new Error('Not implemented yet');
1426
- }
1427
-
1428
- /**
1429
- * Notifies the #plugin that a script is removed.
1430
- */
1431
- removeRawModule(_rawModuleId: string): Promise<void> {
1432
- throw new Error('Not implemented yet');
1433
- }
1434
-
1435
- getTypeInfo(_expression: string, _context: Chrome.DevTools.RawLocation): Promise<{
1436
- typeInfos: Array<Chrome.DevTools.TypeInfo>,
1437
- base: Chrome.DevTools.EvalBase,
1438
- }|null> {
1439
- throw new Error('Not implemented yet');
1440
- }
1441
-
1442
- getFormatter(
1443
- _expressionOrField: string|{
1444
- base: Chrome.DevTools.EvalBase,
1445
- field: Array<Chrome.DevTools.FieldInfo>,
1446
- },
1447
- _context: Chrome.DevTools.RawLocation): Promise<{
1448
- js: string,
1449
- }|null> {
1450
- throw new Error('Not implemented yet');
1451
- }
1452
-
1453
- getInspectableAddress(_field: {
1454
- base: Chrome.DevTools.EvalBase,
1455
- field: Array<Chrome.DevTools.FieldInfo>,
1456
- }): Promise<{
1457
- js: string,
1458
- }> {
1459
- throw new Error('Not implemented yet');
1460
- }
1461
-
1462
- /**
1463
- * Find #locations in source files from a #location in a raw module
1464
- */
1465
- async getFunctionInfo(_rawLocation: Chrome.DevTools.RawLocation):
1466
- Promise<{frames: Array<Chrome.DevTools.FunctionInfo>}|{missingSymbolFiles: string[]}> {
1467
- throw new Error('Not implemented yet');
1468
- }
1469
-
1470
- /**
1471
- * Find #locations in raw modules corresponding to the inline function
1472
- * that rawLocation is in. Used for stepping out of an inline function.
1473
- */
1474
- async getInlinedFunctionRanges(_rawLocation: Chrome.DevTools.RawLocation):
1475
- Promise<Chrome.DevTools.RawLocationRange[]> {
1476
- throw new Error('Not implemented yet');
1477
- }
1478
-
1479
- /**
1480
- * Find #locations in raw modules corresponding to inline functions
1481
- * called by the function or inline frame that rawLocation is in.
1482
- * Used for stepping over inline functions.
1483
- */
1484
- async getInlinedCalleesRanges(_rawLocation: Chrome.DevTools.RawLocation):
1485
- Promise<Chrome.DevTools.RawLocationRange[]> {
1486
- throw new Error('Not implemented yet');
1487
- }
1488
-
1489
- async getMappedLines(_rawModuleId: string, _sourceFileURL: string): Promise<number[]|undefined> {
1490
- throw new Error('Not implemented yet');
1491
- }
1531
+ handleScript(script: SDK.Script.Script): boolean;
1492
1532
  }
@@ -86,6 +86,10 @@ export namespace PrivateAPI {
86
86
  Unsubscribe = 'unsubscribe',
87
87
  UpdateButton = 'updateButton',
88
88
  RegisterLanguageExtensionPlugin = 'registerLanguageExtensionPlugin',
89
+ GetWasmLinearMemory = 'getWasmLinearMemory',
90
+ GetWasmLocal = 'getWasmLocal',
91
+ GetWasmGlobal = 'getWasmGlobal',
92
+ GetWasmOp = 'getWasmOp',
89
93
  RegisterRecorderExtensionPlugin = 'registerRecorderExtensionPlugin',
90
94
  }
91
95
 
@@ -103,6 +107,9 @@ export namespace PrivateAPI {
103
107
  GetInlinedFunctionRanges = 'getInlinedFunctionRanges',
104
108
  GetInlinedCalleesRanges = 'getInlinedCalleesRanges',
105
109
  GetMappedLines = 'getMappedLines',
110
+ FormatValue = 'formatValue',
111
+ GetProperties = 'getProperties',
112
+ ReleaseObject = 'releaseObject',
106
113
  }
107
114
 
108
115
  export const enum LanguageExtensionPluginEvents {
@@ -197,6 +204,23 @@ export namespace PrivateAPI {
197
204
  };
198
205
  type GetHARRequest = {command: Commands.GetHAR};
199
206
  type GetPageResourcesRequest = {command: Commands.GetPageResources};
207
+ type GetWasmLinearMemoryRequest = {
208
+ command: Commands.GetWasmLinearMemory,
209
+ offset: number,
210
+ length: number,
211
+ stopId: unknown,
212
+ };
213
+ type GetWasmLocalRequest = {
214
+ command: Commands.GetWasmLocal,
215
+ local: number,
216
+ stopId: unknown,
217
+ };
218
+ type GetWasmGlobalRequest = {
219
+ command: Commands.GetWasmGlobal,
220
+ global: number,
221
+ stopId: unknown,
222
+ };
223
+ type GetWasmOpRequest = {command: Commands.GetWasmOp, op: number, stopId: unknown};
200
224
 
201
225
  export type ServerRequests = RegisterRecorderExtensionPluginRequest|RegisterLanguageExtensionPluginRequest|
202
226
  SubscribeRequest|UnsubscribeRequest|AddRequestHeadersRequest|ApplyStyleSheetRequest|CreatePanelRequest|
@@ -204,7 +228,8 @@ export namespace PrivateAPI {
204
228
  CreateSidebarPaneRequest|SetSidebarHeightRequest|SetSidebarContentRequest|SetSidebarPageRequest|
205
229
  OpenResourceRequest|SetOpenResourceHandlerRequest|SetThemeChangeHandlerRequest|ReloadRequest|
206
230
  EvaluateOnInspectedPageRequest|GetRequestContentRequest|GetResourceContentRequest|SetResourceContentRequest|
207
- AddTraceProviderRequest|ForwardKeyboardEventRequest|GetHARRequest|GetPageResourcesRequest;
231
+ AddTraceProviderRequest|ForwardKeyboardEventRequest|GetHARRequest|GetPageResourcesRequest|
232
+ GetWasmLinearMemoryRequest|GetWasmLocalRequest|GetWasmGlobalRequest|GetWasmOpRequest;
208
233
  export type ExtensionServerRequestMessage = PrivateAPI.ServerRequests&{requestId?: number};
209
234
 
210
235
  type AddRawModuleRequest = {
@@ -267,11 +292,24 @@ export namespace PrivateAPI {
267
292
  method: LanguageExtensionPluginCommands.GetMappedLines,
268
293
  parameters: {rawModuleId: string, sourceFileURL: string},
269
294
  };
295
+ type FormatValueRequest = {
296
+ method: LanguageExtensionPluginCommands.FormatValue,
297
+ parameters: {expression: string, context: PublicAPI.Chrome.DevTools.RawLocation, stopId: number},
298
+ };
299
+ type GetPropertiesRequest = {
300
+ method: LanguageExtensionPluginCommands.GetProperties,
301
+ parameters: {objectId: PublicAPI.Chrome.DevTools.RemoteObjectId},
302
+ };
303
+ type ReleaseObjectRequest = {
304
+ method: LanguageExtensionPluginCommands.ReleaseObject,
305
+ parameters: {objectId: PublicAPI.Chrome.DevTools.RemoteObjectId},
306
+ };
270
307
 
271
- export type LanguageExtensionRequests = AddRawModuleRequest|SourceLocationToRawLocationRequest|
272
- RawLocationToSourceLocationRequest|GetScopeInfoRequest|ListVariablesInScopeRequest|RemoveRawModuleRequest|
273
- GetTypeInfoRequest|GetFormatterRequest|GetInspectableAddressRequest|GetFunctionInfoRequest|
274
- GetInlinedFunctionRangesRequest|GetInlinedCalleesRangesRequest|GetMappedLinesRequest;
308
+ export type LanguageExtensionRequests =
309
+ AddRawModuleRequest|SourceLocationToRawLocationRequest|RawLocationToSourceLocationRequest|GetScopeInfoRequest|
310
+ ListVariablesInScopeRequest|RemoveRawModuleRequest|GetTypeInfoRequest|GetFormatterRequest|
311
+ GetInspectableAddressRequest|GetFunctionInfoRequest|GetInlinedFunctionRangesRequest|
312
+ GetInlinedCalleesRangesRequest|GetMappedLinesRequest|FormatValueRequest|GetPropertiesRequest|ReleaseObjectRequest;
275
313
 
276
314
  type StringifyRequest = {
277
315
  method: RecorderExtensionPluginCommands.Stringify,
@@ -331,8 +369,8 @@ namespace APIImpl {
331
369
  registerHandler(command: string, handler: (request: {arguments: unknown[]}) => unknown): void;
332
370
  unregisterHandler(command: string): void;
333
371
  hasHandler(command: string): boolean;
334
- sendRequest(request: PrivateAPI.ServerRequests, callback?: ((response: unknown) => unknown), transfers?: unknown[]):
335
- void;
372
+ sendRequest<ResponseT>(
373
+ request: PrivateAPI.ServerRequests, callback?: ((response: ResponseT) => unknown), transfers?: unknown[]): void;
336
374
  nextObjectId(): string;
337
375
  }
338
376
 
@@ -767,8 +805,7 @@ self.injectedExtensionAPI = function(
767
805
  this._plugins = new Map();
768
806
  }
769
807
 
770
- (LanguageServicesAPIImpl.prototype as
771
- Pick<APIImpl.LanguageExtensions, 'registerLanguageExtensionPlugin'|'unregisterLanguageExtensionPlugin'>) = {
808
+ (LanguageServicesAPIImpl.prototype as PublicAPI.Chrome.DevTools.LanguageExtensions) = {
772
809
  registerLanguageExtensionPlugin: async function(
773
810
  this: APIImpl.LanguageExtensions, plugin: PublicAPI.Chrome.DevTools.LanguageExtensionPlugin, pluginName: string,
774
811
  supportedScriptTypes: PublicAPI.Chrome.DevTools.SupportedScriptTypes): Promise<void> {
@@ -822,8 +859,31 @@ self.injectedExtensionAPI = function(
822
859
  return plugin.getMappedLines(request.parameters.rawModuleId, request.parameters.sourceFileURL);
823
860
  }
824
861
  return Promise.resolve(undefined);
862
+ case PrivateAPI.LanguageExtensionPluginCommands.FormatValue:
863
+ if ('evaluate' in plugin && plugin.evaluate) {
864
+ return plugin.evaluate(
865
+ request.parameters.expression, request.parameters.context, request.parameters.stopId);
866
+ }
867
+ return Promise.resolve(undefined);
868
+ case PrivateAPI.LanguageExtensionPluginCommands.GetProperties:
869
+ if ('getProperties' in plugin && plugin.getProperties) {
870
+ return plugin.getProperties(request.parameters.objectId);
871
+ }
872
+ if (!('evaluate' in plugin &&
873
+ plugin.evaluate)) { // If evalute is defined but the remote objects methods aren't, that's a bug
874
+ return Promise.resolve(undefined);
875
+ }
876
+ break;
877
+ case PrivateAPI.LanguageExtensionPluginCommands.ReleaseObject:
878
+ if ('releaseObject' in plugin && plugin.releaseObject) {
879
+ return plugin.releaseObject(request.parameters.objectId);
880
+ }
881
+ if (!('evaluate' in plugin) &&
882
+ plugin.evaluate) { // If evalute is defined but the remote objects methods aren't, that's a bug
883
+ return Promise.resolve(undefined);
884
+ }
885
+ break;
825
886
  }
826
- // @ts-expect-error
827
887
  throw new Error(`Unknown language plugin method ${request.method}`);
828
888
  }
829
889
 
@@ -849,6 +909,33 @@ self.injectedExtensionAPI = function(
849
909
  port.postMessage({event: PrivateAPI.LanguageExtensionPluginEvents.UnregisteredLanguageExtensionPlugin});
850
910
  port.close();
851
911
  },
912
+
913
+ getWasmLinearMemory: async function(
914
+ this: APIImpl.LanguageExtensions, offset: number, length: number, stopId: number): Promise<ArrayBuffer> {
915
+ const result = await new Promise(
916
+ resolve => extensionServer.sendRequest(
917
+ {command: PrivateAPI.Commands.GetWasmLinearMemory, offset, length, stopId}, resolve));
918
+ if (Array.isArray(result)) {
919
+ return new Uint8Array(result).buffer;
920
+ }
921
+ return new ArrayBuffer(0);
922
+ },
923
+ getWasmLocal: async function(
924
+ this: APIImpl.LanguageExtensions, local: number, stopId: number): Promise<PublicAPI.Chrome.DevTools.WasmValue> {
925
+ return new Promise(
926
+ resolve => extensionServer.sendRequest({command: PrivateAPI.Commands.GetWasmLocal, local, stopId}, resolve));
927
+ },
928
+ getWasmGlobal: async function(this: APIImpl.LanguageExtensions, global: number, stopId: number):
929
+ Promise<PublicAPI.Chrome.DevTools.WasmValue> {
930
+ return new Promise(
931
+ resolve =>
932
+ extensionServer.sendRequest({command: PrivateAPI.Commands.GetWasmGlobal, global, stopId}, resolve));
933
+ },
934
+ getWasmOp: async function(this: APIImpl.LanguageExtensions, op: number, stopId: number):
935
+ Promise<PublicAPI.Chrome.DevTools.WasmValue> {
936
+ return new Promise(
937
+ resolve => extensionServer.sendRequest({command: PrivateAPI.Commands.GetWasmOp, op, stopId}, resolve));
938
+ },
852
939
  };
853
940
 
854
941
  function declareInterfaceClass<ImplT extends APIImpl.Callable>(implConstructor: ImplT): (
@@ -1241,11 +1328,12 @@ self.injectedExtensionAPI = function(
1241
1328
  APIImpl.ExtensionServerClient,
1242
1329
  'sendRequest'|'hasHandler'|'registerHandler'|'unregisterHandler'|'nextObjectId'|'_registerCallback'|
1243
1330
  '_onCallback'|'_onMessage'>) = {
1244
- sendRequest: function(
1331
+ sendRequest: function<ResponseT>(
1245
1332
  this: APIImpl.ExtensionServerClient, message: PrivateAPI.ServerRequests,
1246
- callback?: (response: unknown) => unknown, transfers?: Transferable[]): void {
1333
+ callback?: (response: ResponseT) => unknown, transfers?: Transferable[]): void {
1247
1334
  if (typeof callback === 'function') {
1248
- (message as PrivateAPI.ExtensionServerRequestMessage).requestId = this._registerCallback(callback);
1335
+ (message as PrivateAPI.ExtensionServerRequestMessage).requestId =
1336
+ this._registerCallback(callback as (response: unknown) => unknown);
1249
1337
  }
1250
1338
  // @ts-expect-error
1251
1339
  this._port.postMessage(message, transfers);
@@ -55,6 +55,7 @@ import {LanguageExtensionEndpoint} from './LanguageExtensionEndpoint.js';
55
55
  import {RecorderExtensionEndpoint} from './RecorderExtensionEndpoint.js';
56
56
  import {PrivateAPI} from './ExtensionAPI.js';
57
57
  import {RecorderPluginManager} from './RecorderPluginManager.js';
58
+ import type {Chrome} from '../../../extension-api/ExtensionAPI.js'; // eslint-disable-line rulesdir/es_modules_import
58
59
 
59
60
  const extensionOrigins: WeakMap<MessagePort, Platform.DevToolsPath.UrlString> = new WeakMap();
60
61
 
@@ -140,6 +141,10 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
140
141
  this.registerHandler(PrivateAPI.Commands.UpdateButton, this.onUpdateButton.bind(this));
141
142
  this.registerHandler(
142
143
  PrivateAPI.Commands.RegisterLanguageExtensionPlugin, this.registerLanguageExtensionEndpoint.bind(this));
144
+ this.registerHandler(PrivateAPI.Commands.GetWasmLinearMemory, this.onGetWasmLinearMemory.bind(this));
145
+ this.registerHandler(PrivateAPI.Commands.GetWasmGlobal, this.onGetWasmGlobal.bind(this));
146
+ this.registerHandler(PrivateAPI.Commands.GetWasmLocal, this.onGetWasmLocal.bind(this));
147
+ this.registerHandler(PrivateAPI.Commands.GetWasmOp, this.onGetWasmOp.bind(this));
143
148
  this.registerHandler(
144
149
  PrivateAPI.Commands.RegisterRecorderExtensionPlugin, this.registerRecorderExtensionEndpoint.bind(this));
145
150
  window.addEventListener('message', this.onWindowMessage.bind(this), false); // Only for main window.
@@ -204,7 +209,7 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
204
209
  private registerLanguageExtensionEndpoint(
205
210
  message: PrivateAPI.ExtensionServerRequestMessage, _shared_port: MessagePort): Record {
206
211
  if (message.command !== PrivateAPI.Commands.RegisterLanguageExtensionPlugin) {
207
- return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.Subscribe}`);
212
+ return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.RegisterLanguageExtensionPlugin}`);
208
213
  }
209
214
  const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
210
215
  if (!pluginManager) {
@@ -219,6 +224,63 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
219
224
  return this.status.OK();
220
225
  }
221
226
 
227
+ private async loadWasmValue<T>(expression: string, stopId: unknown): Promise<Record|T> {
228
+ const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
229
+ if (!pluginManager) {
230
+ return this.status.E_FAILED('WebAssembly DWARF support needs to be enabled to use this extension');
231
+ }
232
+
233
+ const callFrame = pluginManager.callFrameForStopId(stopId as Bindings.DebuggerLanguagePlugins.StopId);
234
+ if (!callFrame) {
235
+ return this.status.E_BADARG('stopId', 'Unknown stop id');
236
+ }
237
+ const result = await callFrame.debuggerModel.agent.invoke_evaluateOnCallFrame({
238
+ callFrameId: callFrame.id,
239
+ expression,
240
+ silent: true,
241
+ returnByValue: true,
242
+ throwOnSideEffect: true,
243
+ });
244
+
245
+ if (!result.exceptionDetails && !result.getError()) {
246
+ return result.result.value;
247
+ }
248
+
249
+ return this.status.E_FAILED('Failed');
250
+ }
251
+
252
+ private async onGetWasmLinearMemory(message: PrivateAPI.ExtensionServerRequestMessage): Promise<Record|number[]> {
253
+ if (message.command !== PrivateAPI.Commands.GetWasmLinearMemory) {
254
+ return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.GetWasmLinearMemory}`);
255
+ }
256
+ return await this.loadWasmValue<number[]>(
257
+ `[].slice.call(new Uint8Array(memories[0].buffer, ${Number(message.offset)}, ${Number(message.length)}))`,
258
+ message.stopId);
259
+ }
260
+
261
+ private async onGetWasmGlobal(message: PrivateAPI.ExtensionServerRequestMessage):
262
+ Promise<Record|Chrome.DevTools.WasmValue> {
263
+ if (message.command !== PrivateAPI.Commands.GetWasmGlobal) {
264
+ return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.GetWasmGlobal}`);
265
+ }
266
+ return this.loadWasmValue(`globals[${Number(message.global)}]`, message.stopId);
267
+ }
268
+
269
+ private async onGetWasmLocal(message: PrivateAPI.ExtensionServerRequestMessage):
270
+ Promise<Record|Chrome.DevTools.WasmValue> {
271
+ if (message.command !== PrivateAPI.Commands.GetWasmLocal) {
272
+ return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.GetWasmLocal}`);
273
+ }
274
+ return this.loadWasmValue(`locals[${Number(message.local)}]`, message.stopId);
275
+ }
276
+ private async onGetWasmOp(message: PrivateAPI.ExtensionServerRequestMessage):
277
+ Promise<Record|Chrome.DevTools.WasmValue> {
278
+ if (message.command !== PrivateAPI.Commands.GetWasmOp) {
279
+ return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.GetWasmOp}`);
280
+ }
281
+ return this.loadWasmValue(`stack[${Number(message.op)}]`, message.stopId);
282
+ }
283
+
222
284
  private registerRecorderExtensionEndpoint(
223
285
  message: PrivateAPI.ExtensionServerRequestMessage, _shared_port: MessagePort): Record {
224
286
  if (message.command !== PrivateAPI.Commands.RegisterRecorderExtensionPlugin) {