UNPKG

@quick-game/cli

Version:

Command line interface for rapid qg development

859 lines 41.5 kB
/* * Copyright (C) 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * * Neither the name of Google Inc. nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ import * as Platform from '../../core/platform/platform.js'; self.injectedExtensionAPI = function (extensionInfo, inspectedTabId, themeName, keysToForward, testHook, injectedScriptId, targetWindowForTest) { const keysToForwardSet = new Set(keysToForward); const chrome = window.chrome || {}; const devtools_descriptor = Object.getOwnPropertyDescriptor(chrome, 'devtools'); if (devtools_descriptor) { return; } let userAction = false; let userRecorderAction = false; // Here and below, all constructors are private to API implementation. // For a public type Foo, if internal fields are present, these are on // a private FooImpl type, an instance of FooImpl is used in a closure // by Foo consutrctor to re-bind publicly exported members to an instance // of Foo. function EventSinkImpl(type, customDispatch) { this._type = type; this._listeners = []; this._customDispatch = customDispatch; } EventSinkImpl.prototype = { addListener: function (callback) { if (typeof callback !== 'function') { throw 'addListener: callback is not a function'; } if (this._listeners.length === 0) { extensionServer.sendRequest({ command: "subscribe" /* PrivateAPI.Commands.Subscribe */, type: this._type }); } this._listeners.push(callback); extensionServer.registerHandler('notify-' + this._type, this._dispatch.bind(this)); }, removeListener: function (callback) { const listeners = this._listeners; for (let i = 0; i < listeners.length; ++i) { if (listeners[i] === callback) { listeners.splice(i, 1); break; } } if (this._listeners.length === 0) { extensionServer.sendRequest({ command: "unsubscribe" /* PrivateAPI.Commands.Unsubscribe */, type: this._type }); } }, _fire: function (..._vararg) { const listeners = this._listeners.slice(); for (let i = 0; i < listeners.length; ++i) { listeners[i].apply(null, Array.from(arguments)); } }, _dispatch: function (request) { if (this._customDispatch) { this._customDispatch.call(this, request); } else { this._fire.apply(this, request.arguments); } }, }; function Constructor(ctor) { return ctor; } function InspectorExtensionAPI() { this.inspectedWindow = new (Constructor(InspectedWindow))(); this.panels = new (Constructor(Panels))(); this.network = new (Constructor(Network))(); this.timeline = new (Constructor(Timeline))(); this.languageServices = new (Constructor(LanguageServicesAPI))(); this.recorder = new (Constructor(RecorderServicesAPI))(); defineDeprecatedProperty(this, 'webInspector', 'resources', 'network'); } function Network() { function dispatchRequestEvent(message) { const request = message.arguments[1]; request.__proto__ = new (Constructor(Request))(message.arguments[0]); this._fire(request); } this.onRequestFinished = new (Constructor(EventSink))("network-request-finished" /* PrivateAPI.Events.NetworkRequestFinished */, dispatchRequestEvent); defineDeprecatedProperty(this, 'network', 'onFinished', 'onRequestFinished'); this.onNavigated = new (Constructor(EventSink))("inspected-url-changed" /* PrivateAPI.Events.InspectedURLChanged */); } Network.prototype = { getHAR: function (callback) { function callbackWrapper(response) { const result = response; const entries = (result && result.entries) || []; for (let i = 0; i < entries.length; ++i) { entries[i].__proto__ = new (Constructor(Request))(entries[i]._requestId); delete entries[i]._requestId; } callback && callback(result); } extensionServer.sendRequest({ command: "getHAR" /* PrivateAPI.Commands.GetHAR */ }, callback && callbackWrapper); }, addRequestHeaders: function (headers) { extensionServer.sendRequest({ command: "addRequestHeaders" /* PrivateAPI.Commands.AddRequestHeaders */, headers: headers, extensionId: window.location.hostname }); }, }; function RequestImpl(id) { this._id = id; } RequestImpl.prototype = { getContent: function (callback) { function callbackWrapper(response) { const { content, encoding } = response; callback && callback(content, encoding); } extensionServer.sendRequest({ command: "getRequestContent" /* PrivateAPI.Commands.GetRequestContent */, id: this._id }, callback && callbackWrapper); }, }; function Panels() { const panels = { elements: new ElementsPanel(), sources: new SourcesPanel(), }; function panelGetter(name) { return panels[name]; } for (const panel in panels) { Object.defineProperty(this, panel, { get: panelGetter.bind(null, panel), enumerable: true }); } this.applyStyleSheet = function (styleSheet) { extensionServer.sendRequest({ command: "applyStyleSheet" /* PrivateAPI.Commands.ApplyStyleSheet */, styleSheet: styleSheet }); }; } Panels.prototype = { create: function (title, icon, page, callback) { const id = 'extension-panel-' + extensionServer.nextObjectId(); extensionServer.sendRequest({ command: "createPanel" /* PrivateAPI.Commands.CreatePanel */, id, title, page }, callback && (() => callback.call(this, new (Constructor(ExtensionPanel))(id)))); }, setOpenResourceHandler: function (callback) { const hadHandler = extensionServer.hasHandler("open-resource" /* PrivateAPI.Events.OpenResource */); function callbackWrapper(message) { // Allow the panel to show itself when handling the event. userAction = true; try { const { resource, lineNumber } = message; if (canAccessResource(resource)) { callback.call(null, new (Constructor(Resource))(resource), lineNumber); } } finally { userAction = false; } } if (!callback) { extensionServer.unregisterHandler("open-resource" /* PrivateAPI.Events.OpenResource */); } else { extensionServer.registerHandler("open-resource" /* PrivateAPI.Events.OpenResource */, callbackWrapper); } // Only send command if we either removed an existing handler or added handler and had none before. if (hadHandler === !callback) { extensionServer.sendRequest({ command: "setOpenResourceHandler" /* PrivateAPI.Commands.SetOpenResourceHandler */, 'handlerPresent': Boolean(callback) }); } }, setThemeChangeHandler: function (callback) { const hadHandler = extensionServer.hasHandler("host-theme-change" /* PrivateAPI.Events.ThemeChange */); function callbackWrapper(message) { const { themeName } = message; chrome.devtools.panels.themeName = themeName; callback.call(null, themeName); } if (!callback) { extensionServer.unregisterHandler("host-theme-change" /* PrivateAPI.Events.ThemeChange */); } else { extensionServer.registerHandler("host-theme-change" /* PrivateAPI.Events.ThemeChange */, callbackWrapper); } // Only send command if we either removed an existing handler or added handler and had none before. if (hadHandler === !callback) { extensionServer.sendRequest({ command: "setThemeChangeHandler" /* PrivateAPI.Commands.SetThemeChangeHandler */, 'handlerPresent': Boolean(callback) }); } }, openResource: function (url, lineNumber, columnNumber, _callback) { const callbackArg = extractCallbackArgument(arguments); // Handle older API: const columnNumberArg = typeof columnNumber === 'number' ? columnNumber : 0; extensionServer.sendRequest({ command: "openResource" /* PrivateAPI.Commands.OpenResource */, url, lineNumber, columnNumber: columnNumberArg }, callbackArg); }, get SearchAction() { return { CancelSearch: "cancelSearch" /* PrivateAPI.Panels.SearchAction.CancelSearch */, PerformSearch: "performSearch" /* PrivateAPI.Panels.SearchAction.PerformSearch */, NextSearchResult: "nextSearchResult" /* PrivateAPI.Panels.SearchAction.NextSearchResult */, PreviousSearchResult: "previousSearchResult" /* PrivateAPI.Panels.SearchAction.PreviousSearchResult */, }; }, }; function ExtensionViewImpl(id) { this._id = id; function dispatchShowEvent(message) { const frameIndex = message.arguments[0]; if (typeof frameIndex === 'number') { this._fire(window.parent.frames[frameIndex]); } else { this._fire(); } } if (id) { this.onShown = new (Constructor(EventSink))("view-shown-" /* PrivateAPI.Events.ViewShown */ + id, dispatchShowEvent); this.onHidden = new (Constructor(EventSink))("view-hidden," /* PrivateAPI.Events.ViewHidden */ + id); } } function PanelWithSidebarImpl(hostPanelName) { ExtensionViewImpl.call(this, null); this._hostPanelName = hostPanelName; this.onSelectionChanged = new (Constructor(EventSink))("panel-objectSelected-" /* PrivateAPI.Events.PanelObjectSelected */ + hostPanelName); } PanelWithSidebarImpl.prototype = { createSidebarPane: function (title, callback) { const id = 'extension-sidebar-' + extensionServer.nextObjectId(); function callbackWrapper() { callback && callback(new (Constructor(ExtensionSidebarPane))(id)); } extensionServer.sendRequest({ command: "createSidebarPane" /* PrivateAPI.Commands.CreateSidebarPane */, panel: this._hostPanelName, id, title }, callback && callbackWrapper); }, __proto__: ExtensionViewImpl.prototype, }; function RecorderServicesAPIImpl() { this._plugins = new Map(); } async function registerRecorderExtensionPluginImpl(plugin, pluginName, mediaType) { if (this._plugins.has(plugin)) { throw new Error(`Tried to register plugin '${pluginName}' twice`); } const channel = new MessageChannel(); const port = channel.port1; this._plugins.set(plugin, port); port.onmessage = ({ data }) => { const { requestId } = data; dispatchMethodCall(data) .then(result => port.postMessage({ requestId, result })) .catch(error => port.postMessage({ requestId, error: { message: error.message } })); }; async function dispatchMethodCall(request) { switch (request.method) { case "stringify" /* PrivateAPI.RecorderExtensionPluginCommands.Stringify */: return plugin .stringify(request.parameters.recording); case "stringifyStep" /* PrivateAPI.RecorderExtensionPluginCommands.StringifyStep */: return plugin .stringifyStep(request.parameters.step); case "replay" /* PrivateAPI.RecorderExtensionPluginCommands.Replay */: try { userAction = true; userRecorderAction = true; return plugin .replay(request.parameters.recording); } finally { userAction = false; userRecorderAction = false; } default: // @ts-expect-error throw new Error(`'${request.method}' is not recognized`); } } const capabilities = []; if ('stringify' in plugin && 'stringifyStep' in plugin) { capabilities.push('export'); } if ('replay' in plugin) { capabilities.push('replay'); } await new Promise(resolve => { extensionServer.sendRequest({ command: "registerRecorderExtensionPlugin" /* PrivateAPI.Commands.RegisterRecorderExtensionPlugin */, pluginName, mediaType, capabilities, port: channel.port2, }, () => resolve(), [channel.port2]); }); } RecorderServicesAPIImpl.prototype = { registerRecorderExtensionPlugin: registerRecorderExtensionPluginImpl, unregisterRecorderExtensionPlugin: async function (plugin) { const port = this._plugins.get(plugin); if (!port) { throw new Error('Tried to unregister a plugin that was not previously registered'); } this._plugins.delete(plugin); port.postMessage({ event: "unregisteredRecorderExtensionPlugin" /* PrivateAPI.RecorderExtensionPluginEvents.UnregisteredRecorderExtensionPlugin */ }); port.close(); }, createView: async function (title, pagePath) { const id = 'recorder-extension-view-' + extensionServer.nextObjectId(); await new Promise(resolve => { extensionServer.sendRequest({ command: "createRecorderView" /* PrivateAPI.Commands.CreateRecorderView */, id, title, pagePath }, resolve); }); return new (Constructor(RecorderView))(id); }, }; function LanguageServicesAPIImpl() { this._plugins = new Map(); } LanguageServicesAPIImpl.prototype = { registerLanguageExtensionPlugin: async function (plugin, pluginName, supportedScriptTypes) { if (this._plugins.has(plugin)) { throw new Error(`Tried to register plugin '${pluginName}' twice`); } const channel = new MessageChannel(); const port = channel.port1; this._plugins.set(plugin, port); port.onmessage = ({ data }) => { const { requestId } = data; console.time(`${requestId}: ${data.method}`); dispatchMethodCall(data) .then(result => port.postMessage({ requestId, result })) .catch(error => port.postMessage({ requestId, error: { message: error.message } })) .finally(() => console.timeEnd(`${requestId}: ${data.method}`)); }; function dispatchMethodCall(request) { switch (request.method) { case "addRawModule" /* PrivateAPI.LanguageExtensionPluginCommands.AddRawModule */: return plugin.addRawModule(request.parameters.rawModuleId, request.parameters.symbolsURL, request.parameters.rawModule); case "removeRawModule" /* PrivateAPI.LanguageExtensionPluginCommands.RemoveRawModule */: return plugin.removeRawModule(request.parameters.rawModuleId); case "sourceLocationToRawLocation" /* PrivateAPI.LanguageExtensionPluginCommands.SourceLocationToRawLocation */: return plugin.sourceLocationToRawLocation(request.parameters.sourceLocation); case "rawLocationToSourceLocation" /* PrivateAPI.LanguageExtensionPluginCommands.RawLocationToSourceLocation */: return plugin.rawLocationToSourceLocation(request.parameters.rawLocation); case "getScopeInfo" /* PrivateAPI.LanguageExtensionPluginCommands.GetScopeInfo */: return plugin.getScopeInfo(request.parameters.type); case "listVariablesInScope" /* PrivateAPI.LanguageExtensionPluginCommands.ListVariablesInScope */: return plugin.listVariablesInScope(request.parameters.rawLocation); case "getFunctionInfo" /* PrivateAPI.LanguageExtensionPluginCommands.GetFunctionInfo */: return plugin.getFunctionInfo(request.parameters.rawLocation); case "getInlinedFunctionRanges" /* PrivateAPI.LanguageExtensionPluginCommands.GetInlinedFunctionRanges */: return plugin.getInlinedFunctionRanges(request.parameters.rawLocation); case "getInlinedCalleesRanges" /* PrivateAPI.LanguageExtensionPluginCommands.GetInlinedCalleesRanges */: return plugin.getInlinedCalleesRanges(request.parameters.rawLocation); case "getMappedLines" /* PrivateAPI.LanguageExtensionPluginCommands.GetMappedLines */: if ('getMappedLines' in plugin) { return plugin.getMappedLines(request.parameters.rawModuleId, request.parameters.sourceFileURL); } return Promise.resolve(undefined); case "formatValue" /* PrivateAPI.LanguageExtensionPluginCommands.FormatValue */: if ('evaluate' in plugin && plugin.evaluate) { return plugin.evaluate(request.parameters.expression, request.parameters.context, request.parameters.stopId); } return Promise.resolve(undefined); case "getProperties" /* PrivateAPI.LanguageExtensionPluginCommands.GetProperties */: if ('getProperties' in plugin && plugin.getProperties) { return plugin.getProperties(request.parameters.objectId); } if (!('evaluate' in plugin && plugin.evaluate)) { // If evalute is defined but the remote objects methods aren't, that's a bug return Promise.resolve(undefined); } break; case "releaseObject" /* PrivateAPI.LanguageExtensionPluginCommands.ReleaseObject */: if ('releaseObject' in plugin && plugin.releaseObject) { return plugin.releaseObject(request.parameters.objectId); } break; } throw new Error(`Unknown language plugin method ${request.method}`); } await new Promise(resolve => { extensionServer.sendRequest({ command: "registerLanguageExtensionPlugin" /* PrivateAPI.Commands.RegisterLanguageExtensionPlugin */, pluginName, port: channel.port2, supportedScriptTypes, }, () => resolve(), [channel.port2]); }); }, unregisterLanguageExtensionPlugin: async function (plugin) { const port = this._plugins.get(plugin); if (!port) { throw new Error('Tried to unregister a plugin that was not previously registered'); } this._plugins.delete(plugin); port.postMessage({ event: "unregisteredLanguageExtensionPlugin" /* PrivateAPI.LanguageExtensionPluginEvents.UnregisteredLanguageExtensionPlugin */ }); port.close(); }, getWasmLinearMemory: async function (offset, length, stopId) { const result = await new Promise(resolve => extensionServer.sendRequest({ command: "getWasmLinearMemory" /* PrivateAPI.Commands.GetWasmLinearMemory */, offset, length, stopId }, resolve)); if (Array.isArray(result)) { return new Uint8Array(result).buffer; } return new ArrayBuffer(0); }, getWasmLocal: async function (local, stopId) { return new Promise(resolve => extensionServer.sendRequest({ command: "getWasmLocal" /* PrivateAPI.Commands.GetWasmLocal */, local, stopId }, resolve)); }, getWasmGlobal: async function (global, stopId) { return new Promise(resolve => extensionServer.sendRequest({ command: "getWasmGlobal" /* PrivateAPI.Commands.GetWasmGlobal */, global, stopId }, resolve)); }, getWasmOp: async function (op, stopId) { return new Promise(resolve => extensionServer.sendRequest({ command: "getWasmOp" /* PrivateAPI.Commands.GetWasmOp */, op, stopId }, resolve)); }, }; function declareInterfaceClass(implConstructor) { return function (...args) { const impl = { __proto__: implConstructor.prototype }; implConstructor.apply(impl, args); populateInterfaceClass(this, impl); }; } // eslint-disable-next-line @typescript-eslint/no-explicit-any function defineDeprecatedProperty(object, className, oldName, newName) { let warningGiven = false; function getter() { if (!warningGiven) { console.warn(className + '.' + oldName + ' is deprecated. Use ' + className + '.' + newName + ' instead'); warningGiven = true; } return object[newName]; } object.__defineGetter__(oldName, getter); } function extractCallbackArgument(args) { const lastArgument = args[args.length - 1]; return typeof lastArgument === 'function' ? lastArgument : undefined; } const LanguageServicesAPI = declareInterfaceClass(LanguageServicesAPIImpl); const RecorderServicesAPI = declareInterfaceClass(RecorderServicesAPIImpl); const Button = declareInterfaceClass(ButtonImpl); const EventSink = declareInterfaceClass(EventSinkImpl); const ExtensionPanel = declareInterfaceClass(ExtensionPanelImpl); const RecorderView = declareInterfaceClass(RecorderViewImpl); const ExtensionSidebarPane = declareInterfaceClass(ExtensionSidebarPaneImpl); const PanelWithSidebarClass = declareInterfaceClass(PanelWithSidebarImpl); const Request = declareInterfaceClass(RequestImpl); const Resource = declareInterfaceClass(ResourceImpl); const TraceSession = declareInterfaceClass(TraceSessionImpl); class ElementsPanel extends (Constructor(PanelWithSidebarClass)) { constructor() { super('elements'); } } class SourcesPanel extends (Constructor(PanelWithSidebarClass)) { constructor() { super('sources'); } } function ExtensionPanelImpl(id) { ExtensionViewImpl.call(this, id); this.onSearch = new (Constructor(EventSink))("panel-search-" /* PrivateAPI.Events.PanelSearch */ + id); } ExtensionPanelImpl.prototype = { createStatusBarButton: function (iconPath, tooltipText, disabled) { const id = 'button-' + extensionServer.nextObjectId(); extensionServer.sendRequest({ command: "createToolbarButton" /* PrivateAPI.Commands.CreateToolbarButton */, panel: this._id, id: id, icon: iconPath, tooltip: tooltipText, disabled: Boolean(disabled), }); return new (Constructor(Button))(id); }, show: function () { if (!userAction) { return; } extensionServer.sendRequest({ command: "showPanel" /* PrivateAPI.Commands.ShowPanel */, id: this._id }); }, __proto__: ExtensionViewImpl.prototype, }; function RecorderViewImpl(id) { ExtensionViewImpl.call(this, id); } RecorderViewImpl.prototype = { show: function () { if (!userAction || !userRecorderAction) { return; } extensionServer.sendRequest({ command: "showRecorderView" /* PrivateAPI.Commands.ShowRecorderView */, id: this._id }); }, __proto__: ExtensionViewImpl.prototype, }; function ExtensionSidebarPaneImpl(id) { ExtensionViewImpl.call(this, id); } ExtensionSidebarPaneImpl.prototype = { setHeight: function (height) { extensionServer.sendRequest({ command: "setSidebarHeight" /* PrivateAPI.Commands.SetSidebarHeight */, id: this._id, height: height }); }, setExpression: function (expression, rootTitle, evaluateOptions, _callback) { extensionServer.sendRequest({ command: "setSidebarContent" /* PrivateAPI.Commands.SetSidebarContent */, id: this._id, expression: expression, rootTitle: rootTitle, evaluateOnPage: true, evaluateOptions: (typeof evaluateOptions === 'object' ? evaluateOptions : {}), }, extractCallbackArgument(arguments)); }, setObject: function (jsonObject, rootTitle, callback) { extensionServer.sendRequest({ command: "setSidebarContent" /* PrivateAPI.Commands.SetSidebarContent */, id: this._id, expression: jsonObject, rootTitle: rootTitle, }, callback); }, setPage: function (page) { extensionServer.sendRequest({ command: "setSidebarPage" /* PrivateAPI.Commands.SetSidebarPage */, id: this._id, page: page }); }, __proto__: ExtensionViewImpl.prototype, }; function ButtonImpl(id) { this._id = id; this.onClicked = new (Constructor(EventSink))("button-clicked-" /* PrivateAPI.Events.ButtonClicked */ + id); } ButtonImpl.prototype = { update: function (iconPath, tooltipText, disabled) { extensionServer.sendRequest({ command: "updateButton" /* PrivateAPI.Commands.UpdateButton */, id: this._id, icon: iconPath, tooltip: tooltipText, disabled: Boolean(disabled), }); }, }; function Timeline() { } Timeline.prototype = { addTraceProvider: function (categoryName, categoryTooltip) { const id = 'extension-trace-provider-' + extensionServer.nextObjectId(); extensionServer.sendRequest({ command: "addTraceProvider" /* PrivateAPI.Commands.AddTraceProvider */, id: id, categoryName: categoryName, categoryTooltip: categoryTooltip, }); return new (Constructor(TraceProvider))(id); }, }; function TraceSessionImpl(id) { this._id = id; } TraceSessionImpl.prototype = { complete: function (url, timeOffset) { extensionServer.sendRequest({ command: "completeTra.eSession" /* PrivateAPI.Commands.CompleteTraceSession */, id: this._id, url: url || Platform.DevToolsPath.EmptyUrlString, timeOffset: timeOffset || 0, }); }, }; function TraceProvider(id) { function dispatchRecordingStarted(message) { const sessionId = message.arguments[0]; this._fire(new (Constructor(TraceSession))(sessionId)); } this.onRecordingStarted = new (Constructor(EventSink))("trace-recording-started-" /* PrivateAPI.Events.RecordingStarted */ + id, dispatchRecordingStarted); this.onRecordingStopped = new (Constructor(EventSink))("trace-recording-stopped-" /* PrivateAPI.Events.RecordingStopped */ + id); } function canAccessResource(resource) { try { return extensionInfo.allowFileAccess || (new URL(resource.url)).protocol !== 'file:'; } catch (e) { return false; } } function InspectedWindow() { function dispatchResourceEvent(message) { const resourceData = message.arguments[0]; if (!canAccessResource(resourceData)) { return; } this._fire(new (Constructor(Resource))(resourceData)); } function dispatchResourceContentEvent(message) { const resourceData = message.arguments[0]; if (!canAccessResource(resourceData)) { return; } this._fire(new (Constructor(Resource))(resourceData), message.arguments[1]); } this.onResourceAdded = new (Constructor(EventSink))("resource-added" /* PrivateAPI.Events.ResourceAdded */, dispatchResourceEvent); this.onResourceContentCommitted = new (Constructor(EventSink))("resource-content-committed" /* PrivateAPI.Events.ResourceContentCommitted */, dispatchResourceContentEvent); } InspectedWindow.prototype = { reload: function (optionsOrUserAgent) { let options = null; if (typeof optionsOrUserAgent === 'object') { options = optionsOrUserAgent; } else if (typeof optionsOrUserAgent === 'string') { options = { userAgent: optionsOrUserAgent }; console.warn('Passing userAgent as string parameter to inspectedWindow.reload() is deprecated. ' + 'Use inspectedWindow.reload({ userAgent: value}) instead.'); } extensionServer.sendRequest({ command: "Reload" /* PrivateAPI.Commands.Reload */, options: options }); }, eval: function (expression, evaluateOptions) { const callback = extractCallbackArgument(arguments); function callbackWrapper(result) { const { isError, isException, value } = result; if (isError || isException) { callback && callback(undefined, result); } else { callback && callback(value); } } extensionServer.sendRequest({ command: "evaluateOnInspectedPage" /* PrivateAPI.Commands.EvaluateOnInspectedPage */, expression: expression, evaluateOptions: (typeof evaluateOptions === 'object' ? evaluateOptions : undefined), }, callback && callbackWrapper); return null; }, getResources: function (callback) { function wrapResource(resourceData) { return new (Constructor(Resource))(resourceData); } function callbackWrapper(resources) { callback && callback(resources.filter(canAccessResource).map(wrapResource)); } extensionServer.sendRequest({ command: "getPageResources" /* PrivateAPI.Commands.GetPageResources */ }, callback && callbackWrapper); }, }; function ResourceImpl(resourceData) { if (!canAccessResource(resourceData)) { throw new Error('Resource access not allowed'); } this._url = resourceData.url; this._type = resourceData.type; } ResourceImpl.prototype = { get url() { return this._url; }, get type() { return this._type; }, getContent: function (callback) { function callbackWrapper(response) { const { content, encoding } = response; callback && callback(content, encoding); } extensionServer.sendRequest({ command: "getResourceContent" /* PrivateAPI.Commands.GetResourceContent */, url: this._url }, callback && callbackWrapper); }, setContent: function (content, commit, callback) { extensionServer.sendRequest({ command: "setResourceContent" /* PrivateAPI.Commands.SetResourceContent */, url: this._url, content: content, commit: commit }, callback); }, }; function getTabId() { return inspectedTabId; } let keyboardEventRequestQueue = []; let forwardTimer = null; function forwardKeyboardEvent(event) { // Check if the event should be forwarded. // This is a workaround for crbug.com/923338. const focused = document.activeElement; if (focused) { const isInput = focused.nodeName === 'INPUT' || focused.nodeName === 'TEXTAREA' || focused.isContentEditable; if (isInput && !(event.ctrlKey || event.altKey || event.metaKey)) { return; } } let modifiers = 0; if (event.shiftKey) { modifiers |= 1; } if (event.ctrlKey) { modifiers |= 2; } if (event.altKey) { modifiers |= 4; } if (event.metaKey) { modifiers |= 8; } const num = (event.keyCode & 255) | (modifiers << 8); // We only care about global hotkeys, not about random text if (!keysToForwardSet.has(num)) { return; } event.preventDefault(); const requestPayload = { eventType: event.type, ctrlKey: event.ctrlKey, altKey: event.altKey, metaKey: event.metaKey, shiftKey: event.shiftKey, // @ts-expect-error keyIdentifier is a deprecated non-standard property that typescript doesn't know about. keyIdentifier: event.keyIdentifier, key: event.key, code: event.code, location: event.location, keyCode: event.keyCode, }; keyboardEventRequestQueue.push(requestPayload); if (!forwardTimer) { forwardTimer = window.setTimeout(forwardEventQueue, 0); } } function forwardEventQueue() { forwardTimer = null; extensionServer.sendRequest({ command: "_forwardKeyboardEvent" /* PrivateAPI.Commands.ForwardKeyboardEvent */, entries: keyboardEventRequestQueue }); keyboardEventRequestQueue = []; } document.addEventListener('keydown', forwardKeyboardEvent, false); function ExtensionServerClient(targetWindow) { this._callbacks = {}; this._handlers = {}; this._lastRequestId = 0; this._lastObjectId = 0; this.registerHandler('callback', this._onCallback.bind(this)); const channel = new MessageChannel(); this._port = channel.port1; this._port.addEventListener('message', this._onMessage.bind(this), false); this._port.start(); targetWindow.postMessage('registerExtension', '*', [channel.port2]); } ExtensionServerClient.prototype = { sendRequest: function (message, callback, transfers) { if (typeof callback === 'function') { message.requestId = this._registerCallback(callback); } // @ts-expect-error this._port.postMessage(message, transfers); }, hasHandler: function (command) { return Boolean(this._handlers[command]); }, registerHandler: function (command, handler) { this._handlers[command] = handler; }, unregisterHandler: function (command) { delete this._handlers[command]; }, nextObjectId: function () { return injectedScriptId.toString() + '_' + ++this._lastObjectId; }, _registerCallback: function (callback) { const id = ++this._lastRequestId; this._callbacks[id] = callback; return id; }, _onCallback: function (request) { if (request.requestId in this._callbacks) { const callback = this._callbacks[request.requestId]; delete this._callbacks[request.requestId]; callback(request.result); } }, _onMessage: function (event) { const request = event.data; const handler = this._handlers[request.command]; if (handler) { handler.call(this, request); } }, }; function populateInterfaceClass(interfaze, implementation) { for (const member in implementation) { if (member.charAt(0) === '_') { continue; } let descriptor = null; // Traverse prototype chain until we find the owner. for (let owner = implementation; owner && !descriptor; owner = owner.__proto__) { descriptor = Object.getOwnPropertyDescriptor(owner, member); } if (!descriptor) { continue; } if (typeof descriptor.value === 'function') { interfaze[member] = descriptor.value.bind(implementation); } else if (typeof descriptor.get === 'function') { // @ts-expect-error interfaze.__defineGetter__(member, descriptor.get.bind(implementation)); } else { Object.defineProperty(interfaze, member, descriptor); } } } const extensionServer = new (Constructor(ExtensionServerClient))(targetWindowForTest || window.parent); const coreAPI = new (Constructor(InspectorExtensionAPI))(); Object.defineProperty(chrome, 'devtools', { value: {}, enumerable: true }); // Only expose tabId on chrome.devtools.inspectedWindow, not webInspector.inspectedWindow. // @ts-expect-error chrome.devtools.inspectedWindow = {}; Object.defineProperty(chrome.devtools.inspectedWindow, 'tabId', { get: getTabId }); // @ts-expect-error chrome.devtools.inspectedWindow.__proto__ = coreAPI.inspectedWindow; chrome.devtools.network = coreAPI.network; chrome.devtools.panels = coreAPI.panels; chrome.devtools.panels.themeName = themeName; chrome.devtools.languageServices = coreAPI.languageServices; chrome.devtools.recorder = coreAPI.recorder; // default to expose experimental APIs for now. if (extensionInfo.exposeExperimentalAPIs !== false) { chrome.experimental = chrome.experimental || {}; chrome.experimental.devtools = chrome.experimental.devtools || {}; const properties = Object.getOwnPropertyNames(coreAPI); for (let i = 0; i < properties.length; ++i) { const descriptor = Object.getOwnPropertyDescriptor(coreAPI, properties[i]); if (descriptor) { Object.defineProperty(chrome.experimental.devtools, properties[i], descriptor); } } chrome.experimental.devtools.inspectedWindow = chrome.devtools.inspectedWindow; } if (extensionInfo.exposeWebInspectorNamespace) { window.webInspector = coreAPI; } testHook(extensionServer, coreAPI); }; self.buildExtensionAPIInjectedScript = function (extensionInfo, inspectedTabId, themeName, keysToForward, testHook) { const argumentsJSON = [extensionInfo, inspectedTabId || null, themeName, keysToForward].map(_ => JSON.stringify(_)).join(','); if (!testHook) { testHook = () => { }; } return '(function(injectedScriptId){ ' + '(' + self.injectedExtensionAPI.toString() + ')(' + argumentsJSON + ',' + testHook + ', injectedScriptId);' + '})'; }; //# sourceMappingURL=ExtensionAPI.js.map