chrome-devtools-frontend 1.0.1006768 → 1.0.1007846

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 (47) hide show
  1. package/config/gni/devtools_grd_files.gni +5 -0
  2. package/config/gni/devtools_image_files.gni +2 -0
  3. package/extension-api/ExtensionAPI.d.ts +10 -0
  4. package/front_end/Images/src/ic_sources_authored.svg +5 -0
  5. package/front_end/Images/src/ic_sources_deployed.svg +5 -0
  6. package/front_end/core/i18n/locales/en-US.json +39 -3
  7. package/front_end/core/i18n/locales/en-XL.json +39 -3
  8. package/front_end/core/sdk/CSSFontFace.ts +8 -0
  9. package/front_end/core/sdk/DebuggerModel.ts +12 -3
  10. package/front_end/core/sdk/NetworkManager.ts +6 -2
  11. package/front_end/devtools_compatibility.js +1 -0
  12. package/front_end/entrypoints/formatter_worker/FormatterActions.ts +1 -0
  13. package/front_end/entrypoints/formatter_worker/ScopeParser.ts +12 -10
  14. package/front_end/entrypoints/formatter_worker/formatter_worker-entrypoint.ts +4 -0
  15. package/front_end/entrypoints/lighthouse_worker/LighthouseWorkerService.ts +1 -4
  16. package/front_end/legacy_test_runner/lighthouse_test_runner/lighthouse_test_runner.js +16 -0
  17. package/front_end/models/extensions/ExtensionAPI.ts +95 -12
  18. package/front_end/models/extensions/ExtensionEndpoint.ts +69 -0
  19. package/front_end/models/extensions/ExtensionServer.ts +21 -0
  20. package/front_end/models/extensions/LanguageExtensionEndpoint.ts +46 -78
  21. package/front_end/models/extensions/RecorderExtensionEndpoint.ts +43 -0
  22. package/front_end/models/extensions/RecorderPluginManager.ts +30 -0
  23. package/front_end/models/extensions/extensions.ts +2 -0
  24. package/front_end/models/formatter/FormatterWorkerPool.ts +6 -0
  25. package/front_end/models/javascript_metadata/JavaScriptMetadata.ts +13 -20
  26. package/front_end/models/javascript_metadata/NativeFunctions.js +1237 -3962
  27. package/front_end/models/source_map_scopes/NamesResolver.ts +206 -73
  28. package/front_end/models/workspace/UISourceCode.ts +7 -0
  29. package/front_end/panels/application/AppManifestView.ts +2 -1
  30. package/front_end/panels/application/components/BackForwardCacheView.ts +16 -0
  31. package/front_end/panels/browser_debugger/DOMBreakpointsSidebarPane.ts +15 -1
  32. package/front_end/panels/lighthouse/LighthouseController.ts +25 -10
  33. package/front_end/panels/lighthouse/LighthouseStartView.ts +32 -6
  34. package/front_end/panels/lighthouse/LighthouseStartViewFR.ts +70 -49
  35. package/front_end/panels/network/components/RequestHeadersView.css +36 -3
  36. package/front_end/panels/network/components/RequestHeadersView.ts +176 -3
  37. package/front_end/panels/sources/NavigatorView.ts +141 -40
  38. package/front_end/panels/sources/SourcesPanel.ts +8 -0
  39. package/front_end/panels/sources/TabbedEditorContainer.ts +2 -2
  40. package/front_end/panels/sources/sources-meta.ts +6 -0
  41. package/front_end/ui/components/text_editor/javascript.ts +12 -14
  42. package/front_end/ui/legacy/Treeoutline.ts +5 -2
  43. package/package.json +1 -1
  44. package/scripts/hosted_mode/server.js +14 -1
  45. package/scripts/javascript_natives/helpers.js +26 -7
  46. package/scripts/javascript_natives/index.js +4 -3
  47. package/scripts/javascript_natives/tests.js +2 -2
@@ -86,6 +86,7 @@ export namespace PrivateAPI {
86
86
  Unsubscribe = 'unsubscribe',
87
87
  UpdateButton = 'updateButton',
88
88
  RegisterLanguageExtensionPlugin = 'registerLanguageExtensionPlugin',
89
+ RegisterRecorderExtensionPlugin = 'registerRecorderExtensionPlugin',
89
90
  }
90
91
 
91
92
  export const enum LanguageExtensionPluginCommands {
@@ -108,6 +109,14 @@ export namespace PrivateAPI {
108
109
  UnregisteredLanguageExtensionPlugin = 'unregisteredLanguageExtensionPlugin',
109
110
  }
110
111
 
112
+ export const enum RecorderExtensionPluginCommands {
113
+ Stringify = 'stringify',
114
+ }
115
+
116
+ export const enum RecorderExtensionPluginEvents {
117
+ UnregisteredRecorderExtensionPlugin = 'unregisteredRecorderExtensionPlugin',
118
+ }
119
+
111
120
  export interface EvaluateOptions {
112
121
  frameURL?: string;
113
122
  useContentScriptContext?: boolean;
@@ -120,6 +129,11 @@ export namespace PrivateAPI {
120
129
  port: MessagePort,
121
130
  supportedScriptTypes: PublicAPI.Chrome.DevTools.SupportedScriptTypes,
122
131
  };
132
+ type RegisterRecorderExtensionPluginRequest = {
133
+ command: Commands.RegisterRecorderExtensionPlugin,
134
+ pluginName: string,
135
+ port: MessagePort,
136
+ };
123
137
  type SubscribeRequest = {command: Commands.Subscribe, type: string};
124
138
  type UnsubscribeRequest = {command: Commands.Unsubscribe, type: string};
125
139
  type AddRequestHeadersRequest = {
@@ -182,13 +196,13 @@ export namespace PrivateAPI {
182
196
  type GetHARRequest = {command: Commands.GetHAR};
183
197
  type GetPageResourcesRequest = {command: Commands.GetPageResources};
184
198
 
185
- export type ServerRequests = RegisterLanguageExtensionPluginRequest|SubscribeRequest|UnsubscribeRequest|
186
- AddRequestHeadersRequest|ApplyStyleSheetRequest|CreatePanelRequest|ShowPanelRequest|CreateToolbarButtonRequest|
187
- UpdateButtonRequest|CompleteTraceSessionRequest|CreateSidebarPaneRequest|SetSidebarHeightRequest|
188
- SetSidebarContentRequest|SetSidebarPageRequest|OpenResourceRequest|SetOpenResourceHandlerRequest|
189
- SetThemeChangeHandlerRequest|ReloadRequest|EvaluateOnInspectedPageRequest|GetRequestContentRequest|
190
- GetResourceContentRequest|SetResourceContentRequest|AddTraceProviderRequest|ForwardKeyboardEventRequest|
191
- GetHARRequest|GetPageResourcesRequest;
199
+ export type ServerRequests = RegisterRecorderExtensionPluginRequest|RegisterLanguageExtensionPluginRequest|
200
+ SubscribeRequest|UnsubscribeRequest|AddRequestHeadersRequest|ApplyStyleSheetRequest|CreatePanelRequest|
201
+ ShowPanelRequest|CreateToolbarButtonRequest|UpdateButtonRequest|CompleteTraceSessionRequest|
202
+ CreateSidebarPaneRequest|SetSidebarHeightRequest|SetSidebarContentRequest|SetSidebarPageRequest|
203
+ OpenResourceRequest|SetOpenResourceHandlerRequest|SetThemeChangeHandlerRequest|ReloadRequest|
204
+ EvaluateOnInspectedPageRequest|GetRequestContentRequest|GetResourceContentRequest|SetResourceContentRequest|
205
+ AddTraceProviderRequest|ForwardKeyboardEventRequest|GetHARRequest|GetPageResourcesRequest;
192
206
  export type ExtensionServerRequestMessage = PrivateAPI.ServerRequests&{requestId?: number};
193
207
 
194
208
  type AddRawModuleRequest = {
@@ -256,6 +270,13 @@ export namespace PrivateAPI {
256
270
  RawLocationToSourceLocationRequest|GetScopeInfoRequest|ListVariablesInScopeRequest|RemoveRawModuleRequest|
257
271
  GetTypeInfoRequest|GetFormatterRequest|GetInspectableAddressRequest|GetFunctionInfoRequest|
258
272
  GetInlinedFunctionRangesRequest|GetInlinedCalleesRangesRequest|GetMappedLinesRequest;
273
+
274
+ type StringifyRequest = {
275
+ method: RecorderExtensionPluginCommands.Stringify,
276
+ parameters: {recording: Record<string, unknown>},
277
+ };
278
+
279
+ export type RecorderExtensionRequests = StringifyRequest;
259
280
  }
260
281
 
261
282
  declare global {
@@ -264,7 +285,7 @@ declare global {
264
285
  (extensionInfo: ExtensionDescriptor, inspectedTabId: string, themeName: string, keysToForward: number[],
265
286
  testHook:
266
287
  (extensionServer: APIImpl.ExtensionServerClient, extensionAPI: APIImpl.InspectorExtensionAPI) => unknown,
267
- injectedScriptId: number) => void;
288
+ injectedScriptId: number, targetWindow?: Window) => void;
268
289
  buildExtensionAPIInjectedScript(
269
290
  extensionInfo: ExtensionDescriptor, inspectedTabId: string, themeName: string, keysToForward: number[],
270
291
  testHook: undefined|((extensionServer: unknown, extensionAPI: unknown) => unknown)): string;
@@ -283,6 +304,7 @@ export type ExtensionDescriptor = {
283
304
  namespace APIImpl {
284
305
  export interface InspectorExtensionAPI {
285
306
  languageServices: PublicAPI.Chrome.DevTools.LanguageExtensions;
307
+ recorder: PublicAPI.Chrome.DevTools.RecorderExtensions;
286
308
  timeline: Timeline;
287
309
  network: PublicAPI.Chrome.DevTools.Network;
288
310
  panels: PublicAPI.Chrome.DevTools.Panels;
@@ -357,6 +379,10 @@ namespace APIImpl {
357
379
  _plugins: Map<PublicAPI.Chrome.DevTools.LanguageExtensionPlugin, MessagePort>;
358
380
  }
359
381
 
382
+ export interface RecorderExtensions extends PublicAPI.Chrome.DevTools.RecorderExtensions {
383
+ _plugins: Map<PublicAPI.Chrome.DevTools.RecorderExtensionPlugin, MessagePort>;
384
+ }
385
+
360
386
  export interface ExtensionPanel extends ExtensionView, PublicAPI.Chrome.DevTools.ExtensionPanel {
361
387
  show(): void;
362
388
  }
@@ -392,7 +418,7 @@ namespace APIImpl {
392
418
  self.injectedExtensionAPI = function(
393
419
  extensionInfo: ExtensionDescriptor, inspectedTabId: string, themeName: string, keysToForward: number[],
394
420
  testHook: (extensionServer: APIImpl.ExtensionServerClient, extensionAPI: APIImpl.InspectorExtensionAPI) => unknown,
395
- injectedScriptId: number): void {
421
+ injectedScriptId: number, targetWindowForTest?: Window): void {
396
422
  const keysToForwardSet = new Set<number>(keysToForward);
397
423
  const chrome = window.chrome || {};
398
424
 
@@ -473,6 +499,7 @@ self.injectedExtensionAPI = function(
473
499
  this.network = new (Constructor(Network))();
474
500
  this.timeline = new (Constructor(Timeline))();
475
501
  this.languageServices = new (Constructor(LanguageServicesAPI))();
502
+ this.recorder = new (Constructor(RecorderServicesAPI))();
476
503
  defineDeprecatedProperty(this, 'webInspector', 'resources', 'network');
477
504
  }
478
505
 
@@ -671,6 +698,60 @@ self.injectedExtensionAPI = function(
671
698
  __proto__: ExtensionViewImpl.prototype,
672
699
  };
673
700
 
701
+ function RecorderServicesAPIImpl(this: APIImpl.RecorderExtensions): void {
702
+ this._plugins = new Map();
703
+ }
704
+
705
+ (RecorderServicesAPIImpl.prototype as
706
+ Pick<APIImpl.RecorderExtensions, 'registerRecorderExtensionPlugin'|'unregisterRecorderExtensionPlugin'>) = {
707
+ registerRecorderExtensionPlugin: async function(
708
+ this: APIImpl.RecorderExtensions, plugin: PublicAPI.Chrome.DevTools.RecorderExtensionPlugin,
709
+ pluginName: string): Promise<void> {
710
+ if (this._plugins.has(plugin)) {
711
+ throw new Error(`Tried to register plugin '${pluginName}' twice`);
712
+ }
713
+ const channel = new MessageChannel();
714
+ const port = channel.port1;
715
+ this._plugins.set(plugin, port);
716
+ port.onmessage = ({data}: MessageEvent<{requestId: number}&PrivateAPI.RecorderExtensionRequests>): void => {
717
+ const {requestId} = data;
718
+ dispatchMethodCall(data)
719
+ .then(result => port.postMessage({requestId, result}))
720
+ .catch(error => port.postMessage({requestId, error: {message: error.message}}));
721
+ };
722
+
723
+ function dispatchMethodCall(request: PrivateAPI.RecorderExtensionRequests): Promise<unknown> {
724
+ switch (request.method) {
725
+ case PrivateAPI.RecorderExtensionPluginCommands.Stringify:
726
+ return plugin.stringify(request.parameters.recording);
727
+ default:
728
+ throw new Error(`'${request.method}' is not recognized`);
729
+ }
730
+ }
731
+
732
+ await new Promise<void>(resolve => {
733
+ extensionServer.sendRequest(
734
+ {
735
+ command: PrivateAPI.Commands.RegisterRecorderExtensionPlugin,
736
+ pluginName,
737
+ port: channel.port2,
738
+ },
739
+ () => resolve(), [channel.port2]);
740
+ });
741
+ },
742
+
743
+ unregisterRecorderExtensionPlugin: async function(
744
+ this: APIImpl.RecorderExtensions, plugin: PublicAPI.Chrome.DevTools.RecorderExtensionPlugin): Promise<void> {
745
+ const port = this._plugins.get(plugin);
746
+ if (!port) {
747
+ throw new Error('Tried to unregister a plugin that was not previously registered');
748
+ }
749
+ this._plugins.delete(plugin);
750
+ port.postMessage({event: PrivateAPI.RecorderExtensionPluginEvents.UnregisteredRecorderExtensionPlugin});
751
+ port.close();
752
+ },
753
+ };
754
+
674
755
  function LanguageServicesAPIImpl(this: APIImpl.LanguageExtensions): void {
675
756
  this._plugins = new Map();
676
757
  }
@@ -787,6 +868,7 @@ self.injectedExtensionAPI = function(
787
868
  }
788
869
 
789
870
  const LanguageServicesAPI = declareInterfaceClass(LanguageServicesAPIImpl);
871
+ const RecorderServicesAPI = declareInterfaceClass(RecorderServicesAPIImpl);
790
872
  const Button = declareInterfaceClass(ButtonImpl);
791
873
  const EventSink = declareInterfaceClass(EventSinkImpl);
792
874
  const ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl);
@@ -1128,7 +1210,7 @@ self.injectedExtensionAPI = function(
1128
1210
 
1129
1211
  document.addEventListener('keydown', forwardKeyboardEvent, false);
1130
1212
 
1131
- function ExtensionServerClient(this: APIImpl.ExtensionServerClient): void {
1213
+ function ExtensionServerClient(this: APIImpl.ExtensionServerClient, targetWindow: Window): void {
1132
1214
  this._callbacks = {};
1133
1215
  this._handlers = {};
1134
1216
  this._lastRequestId = 0;
@@ -1141,7 +1223,7 @@ self.injectedExtensionAPI = function(
1141
1223
  this._port.addEventListener('message', this._onMessage.bind(this), false);
1142
1224
  this._port.start();
1143
1225
 
1144
- window.parent.postMessage('registerExtension', '*', [channel.port2]);
1226
+ targetWindow.postMessage('registerExtension', '*', [channel.port2]);
1145
1227
  }
1146
1228
 
1147
1229
  (ExtensionServerClient.prototype as Pick<
@@ -1225,7 +1307,7 @@ self.injectedExtensionAPI = function(
1225
1307
  }
1226
1308
  }
1227
1309
 
1228
- const extensionServer = new (Constructor(ExtensionServerClient))();
1310
+ const extensionServer = new (Constructor(ExtensionServerClient))(targetWindowForTest || window.parent);
1229
1311
 
1230
1312
  const coreAPI = new (Constructor(InspectorExtensionAPI))();
1231
1313
 
@@ -1241,6 +1323,7 @@ self.injectedExtensionAPI = function(
1241
1323
  chrome.devtools!.panels = coreAPI.panels;
1242
1324
  chrome.devtools!.panels.themeName = themeName;
1243
1325
  chrome.devtools!.languageServices = coreAPI.languageServices;
1326
+ chrome.devtools!.recorder = coreAPI.recorder;
1244
1327
 
1245
1328
  // default to expose experimental APIs for now.
1246
1329
  if (extensionInfo.exposeExperimentalAPIs !== false) {
@@ -0,0 +1,69 @@
1
+ // Copyright 2022 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ type Response = {
6
+ requestId: number,
7
+ result: unknown,
8
+ error: Error|null,
9
+ };
10
+
11
+ type Event = {
12
+ event: string,
13
+ };
14
+
15
+ type Message = MessageEvent<Response|Event>;
16
+
17
+ export class ExtensionEndpoint {
18
+ private readonly port: MessagePort;
19
+ private nextRequestId: number = 0;
20
+ private pendingRequests: Map<number, {
21
+ resolve: (arg: unknown) => void,
22
+ reject: (error: Error) => void,
23
+ }>;
24
+
25
+ constructor(port: MessagePort) {
26
+ this.port = port;
27
+ this.port.onmessage = this.onResponse.bind(this);
28
+ this.pendingRequests = new Map();
29
+ }
30
+
31
+ sendRequest<ReturnType>(method: string, parameters: unknown): Promise<ReturnType> {
32
+ return new Promise((resolve, reject) => {
33
+ const requestId = this.nextRequestId++;
34
+ this.pendingRequests.set(requestId, {resolve: resolve as (arg: unknown) => void, reject});
35
+ this.port.postMessage({requestId, method, parameters});
36
+ });
37
+ }
38
+
39
+ protected disconnect(): void {
40
+ for (const {reject} of this.pendingRequests.values()) {
41
+ reject(new Error('Extension endpoint disconnected'));
42
+ }
43
+ this.pendingRequests.clear();
44
+ this.port.close();
45
+ }
46
+
47
+ private onResponse({data}: Message): void {
48
+ if ('event' in data) {
49
+ this.handleEvent(data);
50
+ return;
51
+ }
52
+ const {requestId, result, error} = data;
53
+ const pendingRequest = this.pendingRequests.get(requestId);
54
+ if (!pendingRequest) {
55
+ console.error(`No pending request ${requestId}`);
56
+ return;
57
+ }
58
+ this.pendingRequests.delete(requestId);
59
+ if (error) {
60
+ pendingRequest.reject(new Error(error.message));
61
+ } else {
62
+ pendingRequest.resolve(result);
63
+ }
64
+ }
65
+
66
+ protected handleEvent(_event: Event): void {
67
+ throw new Error('handleEvent is not implemented');
68
+ }
69
+ }
@@ -52,7 +52,9 @@ import {ExtensionButton, ExtensionPanel, ExtensionSidebarPane} from './Extension
52
52
  import type {TracingSession} from './ExtensionTraceProvider.js';
53
53
  import {ExtensionTraceProvider} from './ExtensionTraceProvider.js';
54
54
  import {LanguageExtensionEndpoint} from './LanguageExtensionEndpoint.js';
55
+ import {RecorderExtensionEndpoint} from './RecorderExtensionEndpoint.js';
55
56
  import {PrivateAPI} from './ExtensionAPI.js';
57
+ import {RecorderPluginManager} from './RecorderPluginManager.js';
56
58
 
57
59
  const extensionOrigins: WeakMap<MessagePort, Platform.DevToolsPath.UrlString> = new WeakMap();
58
60
 
@@ -138,6 +140,8 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
138
140
  this.registerHandler(PrivateAPI.Commands.UpdateButton, this.onUpdateButton.bind(this));
139
141
  this.registerHandler(
140
142
  PrivateAPI.Commands.RegisterLanguageExtensionPlugin, this.registerLanguageExtensionEndpoint.bind(this));
143
+ this.registerHandler(
144
+ PrivateAPI.Commands.RegisterRecorderExtensionPlugin, this.registerRecorderExtensionEndpoint.bind(this));
141
145
  window.addEventListener('message', this.onWindowMessage.bind(this), false); // Only for main window.
142
146
 
143
147
  const existingTabId =
@@ -215,6 +219,16 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
215
219
  return this.status.OK();
216
220
  }
217
221
 
222
+ private registerRecorderExtensionEndpoint(
223
+ message: PrivateAPI.ExtensionServerRequestMessage, _shared_port: MessagePort): Record {
224
+ if (message.command !== PrivateAPI.Commands.RegisterRecorderExtensionPlugin) {
225
+ return this.status.E_BADARG('command', `expected ${PrivateAPI.Commands.Subscribe}`);
226
+ }
227
+ const {pluginName, port} = message;
228
+ RecorderPluginManager.instance().addPlugin(new RecorderExtensionEndpoint(pluginName, port));
229
+ return this.status.OK();
230
+ }
231
+
218
232
  private inspectedURLChanged(event: Common.EventTarget.EventTargetEvent<SDK.Target.Target>): void {
219
233
  if (!this.canInspectURL(event.data.inspectedURL())) {
220
234
  this.disableExtensions();
@@ -856,6 +870,13 @@ export class ExtensionServer extends Common.ObjectWrapper.ObjectWrapper<EventTyp
856
870
  }
857
871
  }
858
872
 
873
+ addExtensionForTest(extensionInfo: Host.InspectorFrontendHostAPI.ExtensionDescriptor, origin: string): boolean
874
+ |undefined {
875
+ const name = extensionInfo.name || `Extension ${origin}`;
876
+ this.registeredExtensions.set(origin, {name});
877
+ return true;
878
+ }
879
+
859
880
  private addExtension(extensionInfo: Host.InspectorFrontendHostAPI.ExtensionDescriptor): boolean|undefined {
860
881
  const startPage = extensionInfo.startPage;
861
882
 
@@ -5,9 +5,30 @@
5
5
  import type * as SDK from '../../core/sdk/sdk.js';
6
6
  import * as Bindings from '../bindings/bindings.js';
7
7
  import type {Chrome} from '../../../extension-api/ExtensionAPI.js'; // eslint-disable-line rulesdir/es_modules_import
8
+ import {ExtensionEndpoint} from './ExtensionEndpoint.js';
8
9
 
9
10
  import {PrivateAPI} from './ExtensionAPI.js';
10
11
 
12
+ class LanguageExtensionEndpointImpl extends ExtensionEndpoint {
13
+ private plugin: LanguageExtensionEndpoint;
14
+ constructor(plugin: LanguageExtensionEndpoint, port: MessagePort) {
15
+ super(port);
16
+ this.plugin = plugin;
17
+ }
18
+ protected handleEvent({event}: {event: string}): void {
19
+ switch (event) {
20
+ case PrivateAPI.LanguageExtensionPluginEvents.UnregisteredLanguageExtensionPlugin: {
21
+ this.disconnect();
22
+ const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
23
+ if (pluginManager) {
24
+ pluginManager.removePlugin(this.plugin);
25
+ }
26
+ break;
27
+ }
28
+ }
29
+ }
30
+ }
31
+
11
32
  export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.DebuggerLanguagePlugin {
12
33
  private readonly supportedScriptTypes: {
13
34
  language: string,
@@ -15,13 +36,7 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
15
36
  // eslint-disable-next-line @typescript-eslint/naming-convention
16
37
  symbol_types: Array<string>,
17
38
  };
18
- private readonly port: MessagePort;
19
- private nextRequestId: number;
20
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
21
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
22
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
23
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
24
- private pendingRequests: Map<any, any>;
39
+ private endpoint: LanguageExtensionEndpointImpl;
25
40
  constructor(
26
41
  name: string, supportedScriptTypes: {
27
42
  language: string,
@@ -32,63 +47,7 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
32
47
  port: MessagePort) {
33
48
  super(name);
34
49
  this.supportedScriptTypes = supportedScriptTypes;
35
- this.port = port;
36
- this.port.onmessage = this.onResponse.bind(this);
37
- this.nextRequestId = 0;
38
- this.pendingRequests = new Map();
39
- }
40
-
41
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
42
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
43
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
44
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
45
- private sendRequest(method: string, parameters: any): Promise<any> {
46
- return new Promise((resolve, reject) => {
47
- const requestId = this.nextRequestId++;
48
- this.pendingRequests.set(requestId, {resolve, reject});
49
- this.port.postMessage({requestId, method, parameters});
50
- });
51
- }
52
-
53
- private onResponse({data}: MessageEvent<{
54
- requestId: number,
55
- // TODO(crbug.com/1172300) Ignored during the jsdoc to ts migration
56
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
57
- result: any,
58
- error: Error|null,
59
- }|{
60
- event: string,
61
- }>): void {
62
- if ('event' in data) {
63
- const {event} = data;
64
- switch (event) {
65
- case PrivateAPI.LanguageExtensionPluginEvents.UnregisteredLanguageExtensionPlugin: {
66
- for (const {reject} of this.pendingRequests.values()) {
67
- reject(new Error('Language extension endpoint disconnected'));
68
- }
69
- this.pendingRequests.clear();
70
- this.port.close();
71
- const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
72
- if (pluginManager) {
73
- pluginManager.removePlugin(this);
74
- }
75
- break;
76
- }
77
- }
78
- return;
79
- }
80
- const {requestId, result, error} = data;
81
- if (!this.pendingRequests.has(requestId)) {
82
- console.error(`No pending request ${requestId}`);
83
- return;
84
- }
85
- const {resolve, reject} = this.pendingRequests.get(requestId);
86
- this.pendingRequests.delete(requestId);
87
- if (error) {
88
- reject(new Error(error.message));
89
- } else {
90
- resolve(result);
91
- }
50
+ this.endpoint = new LanguageExtensionEndpointImpl(this, port);
92
51
  }
93
52
 
94
53
  handleScript(script: SDK.Script.Script): boolean {
@@ -100,7 +59,7 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
100
59
  /** Notify the plugin about a new script
101
60
  */
102
61
  addRawModule(rawModuleId: string, symbolsURL: string, rawModule: Chrome.DevTools.RawModule): Promise<string[]> {
103
- return this.sendRequest(
62
+ return this.endpoint.sendRequest(
104
63
  PrivateAPI.LanguageExtensionPluginCommands.AddRawModule, {rawModuleId, symbolsURL, rawModule}) as
105
64
  Promise<string[]>;
106
65
  }
@@ -109,33 +68,36 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
109
68
  * Notifies the plugin that a script is removed.
110
69
  */
111
70
  removeRawModule(rawModuleId: string): Promise<void> {
112
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.RemoveRawModule, {rawModuleId}) as Promise<void>;
71
+ return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.RemoveRawModule, {rawModuleId}) as
72
+ Promise<void>;
113
73
  }
114
74
 
115
75
  /** Find locations in raw modules from a location in a source file
116
76
  */
117
77
  sourceLocationToRawLocation(sourceLocation: Chrome.DevTools.SourceLocation):
118
78
  Promise<Chrome.DevTools.RawLocationRange[]> {
119
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.SourceLocationToRawLocation, {sourceLocation}) as
79
+ return this.endpoint.sendRequest(
80
+ PrivateAPI.LanguageExtensionPluginCommands.SourceLocationToRawLocation, {sourceLocation}) as
120
81
  Promise<Chrome.DevTools.RawLocationRange[]>;
121
82
  }
122
83
 
123
84
  /** Find locations in source files from a location in a raw module
124
85
  */
125
86
  rawLocationToSourceLocation(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.SourceLocation[]> {
126
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.RawLocationToSourceLocation, {rawLocation}) as
87
+ return this.endpoint.sendRequest(
88
+ PrivateAPI.LanguageExtensionPluginCommands.RawLocationToSourceLocation, {rawLocation}) as
127
89
  Promise<Chrome.DevTools.SourceLocation[]>;
128
90
  }
129
91
 
130
92
  getScopeInfo(type: string): Promise<Chrome.DevTools.ScopeInfo> {
131
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetScopeInfo, {type}) as
93
+ return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetScopeInfo, {type}) as
132
94
  Promise<Chrome.DevTools.ScopeInfo>;
133
95
  }
134
96
 
135
97
  /** List all variables in lexical scope at a given location in a raw module
136
98
  */
137
99
  listVariablesInScope(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.Variable[]> {
138
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.ListVariablesInScope, {rawLocation}) as
100
+ return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.ListVariablesInScope, {rawLocation}) as
139
101
  Promise<Chrome.DevTools.Variable[]>;
140
102
  }
141
103
 
@@ -144,7 +106,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
144
106
  getFunctionInfo(rawLocation: Chrome.DevTools.RawLocation): Promise<{
145
107
  frames: Array<Chrome.DevTools.FunctionInfo>,
146
108
  }> {
147
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetFunctionInfo, {rawLocation}) as Promise<{
109
+ return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetFunctionInfo, {rawLocation}) as
110
+ Promise<{
148
111
  frames: Array<Chrome.DevTools.FunctionInfo>,
149
112
  }>;
150
113
  }
@@ -153,7 +116,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
153
116
  * that rawLocation is in.
154
117
  */
155
118
  getInlinedFunctionRanges(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.RawLocationRange[]> {
156
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetInlinedFunctionRanges, {rawLocation}) as
119
+ return this.endpoint.sendRequest(
120
+ PrivateAPI.LanguageExtensionPluginCommands.GetInlinedFunctionRanges, {rawLocation}) as
157
121
  Promise<Chrome.DevTools.RawLocationRange[]>;
158
122
  }
159
123
 
@@ -161,7 +125,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
161
125
  * called by the function or inline frame that rawLocation is in.
162
126
  */
163
127
  getInlinedCalleesRanges(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.RawLocationRange[]> {
164
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetInlinedCalleesRanges, {rawLocation}) as
128
+ return this.endpoint.sendRequest(
129
+ PrivateAPI.LanguageExtensionPluginCommands.GetInlinedCalleesRanges, {rawLocation}) as
165
130
  Promise<Chrome.DevTools.RawLocationRange[]>;
166
131
  }
167
132
 
@@ -169,7 +134,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
169
134
  typeInfos: Array<Chrome.DevTools.TypeInfo>,
170
135
  base: Chrome.DevTools.EvalBase,
171
136
  }|null> {
172
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetTypeInfo, {expression, context}) as Promise<{
137
+ return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetTypeInfo, {expression, context}) as
138
+ Promise<{
173
139
  typeInfos: Array<Chrome.DevTools.TypeInfo>,
174
140
  base: Chrome.DevTools.EvalBase,
175
141
  }|null>;
@@ -183,8 +149,8 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
183
149
  context: Chrome.DevTools.RawLocation): Promise<{
184
150
  js: string,
185
151
  }> {
186
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetFormatter, {expressionOrField, context}) as
187
- Promise<{
152
+ return this.endpoint.sendRequest(
153
+ PrivateAPI.LanguageExtensionPluginCommands.GetFormatter, {expressionOrField, context}) as Promise<{
188
154
  js: string,
189
155
  }>;
190
156
  }
@@ -195,13 +161,15 @@ export class LanguageExtensionEndpoint extends Bindings.DebuggerLanguagePlugins.
195
161
  }): Promise<{
196
162
  js: string,
197
163
  }> {
198
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetInspectableAddress, {field}) as Promise<{
164
+ return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetInspectableAddress, {field}) as
165
+ Promise<{
199
166
  js: string,
200
167
  }>;
201
168
  }
202
169
 
203
170
  async getMappedLines(rawModuleId: string, sourceFileURL: string): Promise<number[]|undefined> {
204
- return this.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetMappedLines, {rawModuleId, sourceFileURL});
171
+ return this.endpoint.sendRequest(
172
+ PrivateAPI.LanguageExtensionPluginCommands.GetMappedLines, {rawModuleId, sourceFileURL});
205
173
  }
206
174
 
207
175
  dispose(): void {
@@ -0,0 +1,43 @@
1
+ // Copyright 2022 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import {PrivateAPI} from './ExtensionAPI.js';
6
+ import {ExtensionEndpoint} from './ExtensionEndpoint.js';
7
+ import {RecorderPluginManager} from './RecorderPluginManager.js';
8
+
9
+ export class RecorderExtensionEndpoint extends ExtensionEndpoint {
10
+ private readonly name: string;
11
+
12
+ constructor(name: string, port: MessagePort) {
13
+ super(port);
14
+ this.name = name;
15
+ }
16
+
17
+ getName(): string {
18
+ return this.name;
19
+ }
20
+
21
+ protected handleEvent({event}: {event: string}): void {
22
+ switch (event) {
23
+ case PrivateAPI.RecorderExtensionPluginEvents.UnregisteredRecorderExtensionPlugin: {
24
+ this.disconnect();
25
+ RecorderPluginManager.instance().removePlugin(this);
26
+ break;
27
+ }
28
+ default:
29
+ throw new Error(`Unrecognized Recorder extension endpoint event: ${event}`);
30
+ }
31
+ }
32
+
33
+ /**
34
+ * In practice, `recording` is a UserFlow[1], but we avoid defining this type on the
35
+ * API in order to prevent dependencies between Chrome and puppeteer. Extensions
36
+ * are responsible for working out potential compatibility issues.
37
+ *
38
+ * [1]: https://github.com/puppeteer/replay/blob/main/src/Schema.ts#L245
39
+ */
40
+ stringify(recording: Object): Promise<string> {
41
+ return this.sendRequest(PrivateAPI.RecorderExtensionPluginCommands.Stringify, {recording});
42
+ }
43
+ }
@@ -0,0 +1,30 @@
1
+ // Copyright 2022 The Chromium Authors. All rights reserved.
2
+ // Use of this source code is governed by a BSD-style license that can be
3
+ // found in the LICENSE file.
4
+
5
+ import type {RecorderExtensionEndpoint} from './RecorderExtensionEndpoint.js';
6
+
7
+ let instance: RecorderPluginManager|null = null;
8
+
9
+ export class RecorderPluginManager {
10
+ #plugins: Set<RecorderExtensionEndpoint> = new Set();
11
+
12
+ static instance(): RecorderPluginManager {
13
+ if (!instance) {
14
+ instance = new RecorderPluginManager();
15
+ }
16
+ return instance;
17
+ }
18
+
19
+ addPlugin(plugin: RecorderExtensionEndpoint): void {
20
+ this.#plugins.add(plugin);
21
+ }
22
+
23
+ removePlugin(plugin: RecorderExtensionEndpoint): void {
24
+ this.#plugins.delete(plugin);
25
+ }
26
+
27
+ plugins(): RecorderExtensionEndpoint[] {
28
+ return Array.from(this.#plugins.values());
29
+ }
30
+ }
@@ -7,6 +7,7 @@ import * as ExtensionPanel from './ExtensionPanel.js';
7
7
  import * as ExtensionServer from './ExtensionServer.js';
8
8
  import * as ExtensionTraceProvider from './ExtensionTraceProvider.js';
9
9
  import * as ExtensionView from './ExtensionView.js';
10
+ import * as RecorderPluginManager from './RecorderPluginManager.js';
10
11
 
11
12
  export {
12
13
  ExtensionAPI,
@@ -14,4 +15,5 @@ export {
14
15
  ExtensionServer,
15
16
  ExtensionTraceProvider,
16
17
  ExtensionView,
18
+ RecorderPluginManager,
17
19
  };
@@ -4,6 +4,7 @@
4
4
 
5
5
  import * as Common from '../../core/common/common.js';
6
6
  import * as FormatterActions from '../../entrypoints/formatter_worker/FormatterActions.js'; // eslint-disable-line rulesdir/es_modules_import
7
+ export {DefinitionKind, type ScopeTreeNode} from '../../entrypoints/formatter_worker/FormatterActions.js';
7
8
 
8
9
  const MAX_WORKERS = Math.min(2, navigator.hardwareConcurrency - 1);
9
10
 
@@ -140,6 +141,11 @@ export class FormatterWorkerPool {
140
141
  .then(result => result || '');
141
142
  }
142
143
 
144
+ javaScriptScopeTree(expression: string): Promise<FormatterActions.ScopeTreeNode|null> {
145
+ return this.runTask(FormatterActions.FormatterActions.JAVASCRIPT_SCOPE_TREE, {content: expression})
146
+ .then(result => result || null);
147
+ }
148
+
143
149
  evaluatableJavaScriptSubstring(content: string): Promise<string> {
144
150
  return this.runTask(FormatterActions.FormatterActions.EVALUATE_JAVASCRIPT_SUBSTRING, {content: content})
145
151
  .then(text => text || '');