[wasm debug] Add an API for Language Plugins Extensions
This CL extends the existing extension API to support debugger language
plugins hosted inside chrome extensions. The patch adds a new
experimental interface to allow extensions to egister a language plugin,
which then gets called by the debugger to provide debug information.
Drive-By: Drop the experimental out-of-process language plugin made
obsolete by the language extensions.
Bug: chromium:1083146
Change-Id: Icbf755e5761f6b7a5df6406a725e38eba965683e
Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/2245146
Commit-Queue: Philip Pfaffe <[email protected]>
Reviewed-by: Benedikt Meurer <[email protected]>
Reviewed-by: Paul Lewis <[email protected]>
Reviewed-by: Andrey Kosyakov <[email protected]>
diff --git a/front_end/extensions/ExtensionAPI.js b/front_end/extensions/ExtensionAPI.js
index 0164362..d6dd920 100644
--- a/front_end/extensions/ExtensionAPI.js
+++ b/front_end/extensions/ExtensionAPI.js
@@ -80,7 +80,18 @@
SetSidebarPage: 'setSidebarPage',
ShowPanel: 'showPanel',
Unsubscribe: 'unsubscribe',
- UpdateButton: 'updateButton'
+ UpdateButton: 'updateButton',
+ RegisterLanguageExtensionPlugin: 'registerLanguageExtensionPlugin'
+ };
+
+ /** @enum {string} */
+ apiPrivate.LanguageExtensionPluginCommands = {
+ AddRawModule: 'addRawModule',
+ RemoveRawModule: 'removeRawModule',
+ SourceLocationToRawLocation: 'sourceLocationToRawLocation',
+ RawLocationToSourceLocation: 'rawLocationToSourceLocation',
+ ListVariablesInScope: 'listVariablesInScope',
+ EvaluateVariable: 'evaluateVariable'
};
}
@@ -107,6 +118,7 @@
defineCommonExtensionSymbols(apiPrivate);
const commands = apiPrivate.Commands;
+ const languageExtensionPluginCommands = apiPrivate.LanguageExtensionPluginCommands;
const events = apiPrivate.Events;
let userAction = false;
@@ -178,6 +190,7 @@
this.panels = new Panels();
this.network = new Network();
this.timeline = new Timeline();
+ this.languageServices = new LanguageServicesAPI();
defineDeprecatedProperty(this, 'webInspector', 'resources', 'network');
}
@@ -342,6 +355,61 @@
__proto__: ExtensionViewImpl.prototype
};
+ /**
+ * @constructor
+ */
+ function LanguageServicesAPIImpl() {
+ this._plugins = new Map();
+ }
+
+ LanguageServicesAPIImpl.prototype = {
+ /**
+ * @param {*} plugin The language plugin instance to register.
+ * @param {string} pluginName The plugin name
+ * @param {{language: string, symbol_types: !Array<string>}} supportedScriptTypes Script language and debug symbol types supported by this extension.
+ */
+ registerLanguageExtensionPlugin: function(plugin, pluginName, supportedScriptTypes) {
+ if (this._plugins.has(plugin)) {
+ throw new Error('Tried to register a plugin twice');
+ }
+ const channel = new MessageChannel();
+ const port = channel.port1;
+ this._plugins.set(plugin, port);
+ port.onmessage = ({data: {requestId, method, parameters}}) => {
+ dispatchMethodCall(method, parameters)
+ .then(result => port.postMessage({requestId, result}))
+ .catch(error => port.postMessage({requestId, error: {message: error.message}}));
+ };
+
+ /**
+ * @param {string} method
+ * @param {*} parameters
+ * @return {!Promise<*>}
+ */
+ function dispatchMethodCall(method, parameters) {
+ switch (method) {
+ case languageExtensionPluginCommands.AddRawModule:
+ return plugin.addRawModule(parameters.rawModuleId, parameters.symbolsURL, parameters.rawModule);
+ case languageExtensionPluginCommands.RemoveRawModule:
+ return plugin.removeRawModule(parameters.rawModuleId);
+ case languageExtensionPluginCommands.SourceLocationToRawLocation:
+ return plugin.sourceLocationToRawLocation(parameters.sourceLocation);
+ case languageExtensionPluginCommands.RawLocationToSourceLocation:
+ return plugin.rawLocationToSourceLocation(parameters.rawLocation);
+ case languageExtensionPluginCommands.ListVariablesInScope:
+ return plugin.listVariablesInScope(parameters.rawLocation);
+ case languageExtensionPluginCommands.EvaluateVariable:
+ return plugin.evaluateVariable(parameters.name, parameters.location);
+ }
+ throw new Error(`Unknown language plugin method ${method}`);
+ }
+
+ extensionServer.sendRequest(
+ {command: commands.RegisterLanguageExtensionPlugin, pluginName, port: channel.port2, supportedScriptTypes},
+ undefined, [channel.port2]);
+ }
+ };
+
function declareInterfaceClass(implConstructor) {
return function() {
const impl = {__proto__: implConstructor.prototype};
@@ -367,6 +435,7 @@
return typeof lastArgument === 'function' ? lastArgument : undefined;
}
+ const LanguageServicesAPI = declareInterfaceClass(LanguageServicesAPIImpl);
const Button = declareInterfaceClass(ButtonImpl);
const EventSink = declareInterfaceClass(EventSinkImpl);
const ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl);
@@ -731,12 +800,13 @@
/**
* @param {!Object} message
* @param {function()=} callback
+ * @param {!Array<*>=} transfers
*/
- sendRequest: function(message, callback) {
+ sendRequest: function(message, callback, transfers) {
if (typeof callback === 'function') {
message.requestId = this._registerCallback(callback);
}
- this._port.postMessage(message);
+ this._port.postMessage(message, transfers);
},
/**
@@ -824,6 +894,7 @@
if (extensionInfo.exposeExperimentalAPIs !== false) {
chrome.experimental = chrome.experimental || {};
chrome.experimental.devtools = chrome.experimental.devtools || {};
+ chrome.experimental.devtools.languageServices = new LanguageServicesAPI();
const properties = Object.getOwnPropertyNames(coreAPI);
for (let i = 0; i < properties.length; ++i) {
@@ -850,7 +921,8 @@
* @return {string}
*/
self.buildExtensionAPIInjectedScript = function(extensionInfo, inspectedTabId, themeName, keysToForward, testHook) {
- const argumentsJSON = [extensionInfo, inspectedTabId || null, themeName, keysToForward].map(_ => JSON.stringify(_)).join(',');
+ const argumentsJSON =
+ [extensionInfo, inspectedTabId || null, themeName, keysToForward].map(_ => JSON.stringify(_)).join(',');
if (!testHook) {
testHook = () => {};
}