chrome-devtools-frontend
Version:
Chrome DevTools UI
172 lines (150 loc) • 7.08 kB
text/typescript
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import type {Chrome} from '../../../extension-api/ExtensionAPI.js';
import type * as Platform from '../../core/platform/platform.js';
import type * as SDK from '../../core/sdk/sdk.js';
import * as Bindings from '../bindings/bindings.js';
import {PrivateAPI} from './ExtensionAPI.js';
import {ExtensionEndpoint} from './ExtensionEndpoint.js';
class LanguageExtensionEndpointImpl extends ExtensionEndpoint {
private plugin: LanguageExtensionEndpoint;
constructor(plugin: LanguageExtensionEndpoint, port: MessagePort) {
super(port);
this.plugin = plugin;
}
protected override handleEvent({event}: {event: string}): void {
switch (event) {
case PrivateAPI.LanguageExtensionPluginEvents.UnregisteredLanguageExtensionPlugin: {
this.disconnect();
const {pluginManager} = Bindings.DebuggerWorkspaceBinding.DebuggerWorkspaceBinding.instance();
pluginManager.removePlugin(this.plugin);
break;
}
}
}
}
export class LanguageExtensionEndpoint implements Bindings.DebuggerLanguagePlugins.DebuggerLanguagePlugin {
private readonly supportedScriptTypes: {
language: string,
// eslint-disable-next-line @typescript-eslint/naming-convention
symbol_types: string[],
};
private readonly endpoint: LanguageExtensionEndpointImpl;
private readonly extensionOrigin: string;
readonly allowFileAccess: boolean;
readonly name: string;
constructor(
allowFileAccess: boolean, extensionOrigin: string, name: string, supportedScriptTypes: {
language: string,
// eslint-disable-next-line @typescript-eslint/naming-convention
symbol_types: string[],
},
port: MessagePort) {
this.name = name;
this.extensionOrigin = extensionOrigin;
this.supportedScriptTypes = supportedScriptTypes;
this.endpoint = new LanguageExtensionEndpointImpl(this, port);
this.allowFileAccess = allowFileAccess;
}
canAccessURL(url: string): boolean {
try {
return !url || this.allowFileAccess || new URL(url).protocol !== 'file:';
} catch {
// If the URL isn't valid, it also isn't a valid file url and it's safe to tell the extensions about it.
return true;
}
}
handleScript(script: SDK.Script.Script): boolean {
try {
if (!this.canAccessURL(script.contentURL()) || (script.hasSourceURL && !this.canAccessURL(script.sourceURL)) ||
(script.debugSymbols?.externalURL && !this.canAccessURL(script.debugSymbols.externalURL))) {
return false;
}
} catch {
return false;
}
const language = script.scriptLanguage();
return language !== null && script.debugSymbols !== null && language === this.supportedScriptTypes.language &&
this.supportedScriptTypes.symbol_types.includes(script.debugSymbols.type);
}
createPageResourceLoadInitiator(): SDK.PageResourceLoader.PageResourceLoadInitiator {
return {
target: null,
frameId: null,
extensionId: this.extensionOrigin,
initiatorUrl: this.extensionOrigin as Platform.DevToolsPath.UrlString,
};
}
/** Notify the plugin about a new script
*/
addRawModule(rawModuleId: string, symbolsURL: string, rawModule: Chrome.DevTools.RawModule): Promise<string[]> {
if (!this.canAccessURL(symbolsURL) || !this.canAccessURL(rawModule.url)) {
return Promise.resolve([]);
}
return this.endpoint.sendRequest(
PrivateAPI.LanguageExtensionPluginCommands.AddRawModule, {rawModuleId, symbolsURL, rawModule});
}
/**
* Notifies the plugin that a script is removed.
*/
removeRawModule(rawModuleId: string): Promise<void> {
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.RemoveRawModule, {rawModuleId});
}
/** Find locations in raw modules from a location in a source file
*/
sourceLocationToRawLocation(sourceLocation: Chrome.DevTools.SourceLocation):
Promise<Chrome.DevTools.RawLocationRange[]> {
return this.endpoint.sendRequest(
PrivateAPI.LanguageExtensionPluginCommands.SourceLocationToRawLocation, {sourceLocation});
}
/** Find locations in source files from a location in a raw module
*/
rawLocationToSourceLocation(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.SourceLocation[]> {
return this.endpoint.sendRequest(
PrivateAPI.LanguageExtensionPluginCommands.RawLocationToSourceLocation, {rawLocation});
}
getScopeInfo(type: string): Promise<Chrome.DevTools.ScopeInfo> {
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetScopeInfo, {type});
}
/** List all variables in lexical scope at a given location in a raw module
*/
listVariablesInScope(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.Variable[]> {
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.ListVariablesInScope, {rawLocation});
}
/** List all function names (including inlined frames) at location
*/
getFunctionInfo(rawLocation: Chrome.DevTools.RawLocation): Promise<{
frames: Chrome.DevTools.FunctionInfo[],
}> {
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetFunctionInfo, {rawLocation});
}
/** Find locations in raw modules corresponding to the inline function
* that rawLocation is in.
*/
getInlinedFunctionRanges(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.RawLocationRange[]> {
return this.endpoint.sendRequest(
PrivateAPI.LanguageExtensionPluginCommands.GetInlinedFunctionRanges, {rawLocation});
}
/** Find locations in raw modules corresponding to inline functions
* called by the function or inline frame that rawLocation is in.
*/
getInlinedCalleesRanges(rawLocation: Chrome.DevTools.RawLocation): Promise<Chrome.DevTools.RawLocationRange[]> {
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetInlinedCalleesRanges, {rawLocation});
}
async getMappedLines(rawModuleId: string, sourceFileURL: string): Promise<number[]|undefined> {
return await this.endpoint.sendRequest(
PrivateAPI.LanguageExtensionPluginCommands.GetMappedLines, {rawModuleId, sourceFileURL});
}
async evaluate(expression: string, context: Chrome.DevTools.RawLocation, stopId: number):
Promise<Chrome.DevTools.RemoteObject> {
return await this.endpoint.sendRequest(
PrivateAPI.LanguageExtensionPluginCommands.FormatValue, {expression, context, stopId});
}
getProperties(objectId: Chrome.DevTools.RemoteObjectId): Promise<Chrome.DevTools.PropertyDescriptor[]> {
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.GetProperties, {objectId});
}
releaseObject(objectId: Chrome.DevTools.RemoteObjectId): Promise<void> {
return this.endpoint.sendRequest(PrivateAPI.LanguageExtensionPluginCommands.ReleaseObject, {objectId});
}
}