| /* | |
| * 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. | |
| */ | |
| /** | |
| * @unrestricted | |
| */ | |
| export default class RuntimeModel extends SDK.SDKModel { | |
| /** | |
| * @param {!SDK.Target} target | |
| */ | |
| constructor(target) { | |
| super(target); | |
| this._agent = target.runtimeAgent(); | |
| this.target().registerRuntimeDispatcher(new RuntimeDispatcher(this)); | |
| this._agent.enable(); | |
| /** @type {!Map<number, !ExecutionContext>} */ | |
| this._executionContextById = new Map(); | |
| this._executionContextComparator = ExecutionContext.comparator; | |
| /** @type {?boolean} */ | |
| this._hasSideEffectSupport = null; | |
| if (Common.moduleSetting('customFormatters').get()) { | |
| this._agent.setCustomObjectFormatterEnabled(true); | |
| } | |
| Common.moduleSetting('customFormatters').addChangeListener(this._customFormattersStateChanged.bind(this)); | |
| } | |
| /** | |
| * @param {!SDK.RuntimeModel.EvaluationResult} response | |
| * @return {boolean} | |
| */ | |
| static isSideEffectFailure(response) { | |
| const exceptionDetails = !response[Protocol.Error] && response.exceptionDetails; | |
| return !!( | |
| exceptionDetails && exceptionDetails.exception && exceptionDetails.exception.description && | |
| exceptionDetails.exception.description.startsWith('EvalError: Possible side-effect in debug-evaluate')); | |
| } | |
| /** | |
| * @return {!SDK.DebuggerModel} | |
| */ | |
| debuggerModel() { | |
| return /** @type {!SDK.DebuggerModel} */ (this.target().model(SDK.DebuggerModel)); | |
| } | |
| /** | |
| * @return {!SDK.HeapProfilerModel} | |
| */ | |
| heapProfilerModel() { | |
| return /** @type {!SDK.HeapProfilerModel} */ (this.target().model(SDK.HeapProfilerModel)); | |
| } | |
| /** | |
| * @return {!Array.<!ExecutionContext>} | |
| */ | |
| executionContexts() { | |
| return this._executionContextById.valuesArray().sort(this.executionContextComparator()); | |
| } | |
| /** | |
| * @param {function(!ExecutionContext,!ExecutionContext)} comparator | |
| */ | |
| setExecutionContextComparator(comparator) { | |
| this._executionContextComparator = comparator; | |
| } | |
| /** | |
| * @return {function(!ExecutionContext,!ExecutionContext)} comparator | |
| */ | |
| executionContextComparator() { | |
| return this._executionContextComparator; | |
| } | |
| /** | |
| * @return {?ExecutionContext} | |
| */ | |
| defaultExecutionContext() { | |
| for (const context of this.executionContexts()) { | |
| if (context.isDefault) { | |
| return context; | |
| } | |
| } | |
| return null; | |
| } | |
| /** | |
| * @param {!Protocol.Runtime.ExecutionContextId} id | |
| * @return {?ExecutionContext} | |
| */ | |
| executionContext(id) { | |
| return this._executionContextById.get(id) || null; | |
| } | |
| /** | |
| * @param {!Protocol.Runtime.ExecutionContextDescription} context | |
| */ | |
| _executionContextCreated(context) { | |
| const data = context.auxData || {isDefault: true}; | |
| const executionContext = | |
| new ExecutionContext(this, context.id, context.name, context.origin, data['isDefault'], data['frameId']); | |
| this._executionContextById.set(executionContext.id, executionContext); | |
| this.dispatchEventToListeners(Events.ExecutionContextCreated, executionContext); | |
| } | |
| /** | |
| * @param {number} executionContextId | |
| */ | |
| _executionContextDestroyed(executionContextId) { | |
| const executionContext = this._executionContextById.get(executionContextId); | |
| if (!executionContext) { | |
| return; | |
| } | |
| this.debuggerModel().executionContextDestroyed(executionContext); | |
| this._executionContextById.delete(executionContextId); | |
| this.dispatchEventToListeners(Events.ExecutionContextDestroyed, executionContext); | |
| } | |
| fireExecutionContextOrderChanged() { | |
| this.dispatchEventToListeners(Events.ExecutionContextOrderChanged, this); | |
| } | |
| _executionContextsCleared() { | |
| this.debuggerModel().globalObjectCleared(); | |
| const contexts = this.executionContexts(); | |
| this._executionContextById.clear(); | |
| for (let i = 0; i < contexts.length; ++i) { | |
| this.dispatchEventToListeners(Events.ExecutionContextDestroyed, contexts[i]); | |
| } | |
| } | |
| /** | |
| * @param {!Protocol.Runtime.RemoteObject} payload | |
| * @return {!SDK.RemoteObject} | |
| */ | |
| createRemoteObject(payload) { | |
| console.assert(typeof payload === 'object', 'Remote object payload should only be an object'); | |
| return new SDK.RemoteObjectImpl( | |
| this, payload.objectId, payload.type, payload.subtype, payload.value, payload.unserializableValue, | |
| payload.description, payload.preview, payload.customPreview, payload.className); | |
| } | |
| /** | |
| * @param {!Protocol.Runtime.RemoteObject} payload | |
| * @param {!SDK.ScopeRef} scopeRef | |
| * @return {!SDK.RemoteObject} | |
| */ | |
| createScopeRemoteObject(payload, scopeRef) { | |
| return new SDK.ScopeRemoteObject( | |
| this, payload.objectId, scopeRef, payload.type, payload.subtype, payload.value, payload.unserializableValue, | |
| payload.description, payload.preview); | |
| } | |
| /** | |
| * @param {number|string|boolean|undefined|bigint} value | |
| * @return {!SDK.RemoteObject} | |
| */ | |
| createRemoteObjectFromPrimitiveValue(value) { | |
| const type = typeof value; | |
| let unserializableValue = undefined; | |
| const unserializableDescription = SDK.RemoteObject.unserializableDescription(value); | |
| if (unserializableDescription !== null) { | |
| unserializableValue = /** @type {!Protocol.Runtime.UnserializableValue} */ (unserializableDescription); | |
| } | |
| if (typeof unserializableValue !== 'undefined') { | |
| value = undefined; | |
| } | |
| return new SDK.RemoteObjectImpl(this, undefined, type, undefined, value, unserializableValue); | |
| } | |
| /** | |
| * @param {string} name | |
| * @param {number|string|boolean} value | |
| * @return {!SDK.RemoteObjectProperty} | |
| */ | |
| createRemotePropertyFromPrimitiveValue(name, value) { | |
| return new SDK.RemoteObjectProperty(name, this.createRemoteObjectFromPrimitiveValue(value)); | |
| } | |
| discardConsoleEntries() { | |
| this._agent.discardConsoleEntries(); | |
| } | |
| /** | |
| * @param {string} objectGroupName | |
| */ | |
| releaseObjectGroup(objectGroupName) { | |
| this._agent.releaseObjectGroup(objectGroupName); | |
| } | |
| /** | |
| * @param {!SDK.RuntimeModel.EvaluationResult} result | |
| */ | |
| releaseEvaluationResult(result) { | |
| if (result.object) { | |
| result.object.release(); | |
| } | |
| if (result.exceptionDetails && result.exceptionDetails.exception) { | |
| const exception = result.exceptionDetails.exception; | |
| const exceptionObject = this.createRemoteObject({type: exception.type, objectId: exception.objectId}); | |
| exceptionObject.release(); | |
| } | |
| } | |
| runIfWaitingForDebugger() { | |
| this._agent.runIfWaitingForDebugger(); | |
| } | |
| /** | |
| * @param {!Common.Event} event | |
| */ | |
| _customFormattersStateChanged(event) { | |
| const enabled = /** @type {boolean} */ (event.data); | |
| this._agent.setCustomObjectFormatterEnabled(enabled); | |
| } | |
| /** | |
| * @param {string} expression | |
| * @param {string} sourceURL | |
| * @param {boolean} persistScript | |
| * @param {number} executionContextId | |
| * @return {!Promise<?SDK.RuntimeModel.CompileScriptResult>} | |
| */ | |
| async compileScript(expression, sourceURL, persistScript, executionContextId) { | |
| const response = await this._agent.invoke_compileScript({ | |
| expression: String.escapeInvalidUnicodeCharacters(expression), | |
| sourceURL: sourceURL, | |
| persistScript: persistScript, | |
| executionContextId: executionContextId | |
| }); | |
| if (response[Protocol.Error]) { | |
| console.error(response[Protocol.Error]); | |
| return null; | |
| } | |
| return {scriptId: response.scriptId, exceptionDetails: response.exceptionDetails}; | |
| } | |
| /** | |
| * @param {!Protocol.Runtime.ScriptId} scriptId | |
| * @param {number} executionContextId | |
| * @param {string=} objectGroup | |
| * @param {boolean=} silent | |
| * @param {boolean=} includeCommandLineAPI | |
| * @param {boolean=} returnByValue | |
| * @param {boolean=} generatePreview | |
| * @param {boolean=} awaitPromise | |
| * @return {!Promise<!SDK.RuntimeModel.EvaluationResult>} | |
| */ | |
| async runScript( | |
| scriptId, executionContextId, objectGroup, silent, includeCommandLineAPI, returnByValue, generatePreview, | |
| awaitPromise) { | |
| const response = await this._agent.invoke_runScript({ | |
| scriptId, | |
| executionContextId, | |
| objectGroup, | |
| silent, | |
| includeCommandLineAPI, | |
| returnByValue, | |
| generatePreview, | |
| awaitPromise | |
| }); | |
| const error = response[Protocol.Error]; | |
| if (error) { | |
| console.error(error); | |
| return {error: error}; | |
| } | |
| return {object: this.createRemoteObject(response.result), exceptionDetails: response.exceptionDetails}; | |
| } | |
| /** | |
| * @param {!SDK.RemoteObject} prototype | |
| * @return {!Promise<!SDK.RuntimeModel.QueryObjectResult>} | |
| */ | |
| async queryObjects(prototype) { | |
| if (!prototype.objectId) { | |
| return {error: 'Prototype should be an Object.'}; | |
| } | |
| const response = await this._agent.invoke_queryObjects( | |
| {prototypeObjectId: /** @type {string} */ (prototype.objectId), objectGroup: 'console'}); | |
| const error = response[Protocol.Error]; | |
| if (error) { | |
| console.error(error); | |
| return {error: error}; | |
| } | |
| return {objects: this.createRemoteObject(response.objects)}; | |
| } | |
| /** | |
| * @return {!Promise<string>} | |
| */ | |
| async isolateId() { | |
| return (await this._agent.getIsolateId()) || this.target().id(); | |
| } | |
| /** | |
| * @return {!Promise<?{usedSize: number, totalSize: number}>} | |
| */ | |
| async heapUsage() { | |
| const result = await this._agent.invoke_getHeapUsage({}); | |
| return result[Protocol.Error] ? null : result; | |
| } | |
| /** | |
| * @param {!Protocol.Runtime.RemoteObject} payload | |
| * @param {!Object=} hints | |
| */ | |
| _inspectRequested(payload, hints) { | |
| const object = this.createRemoteObject(payload); | |
| if (hints.copyToClipboard) { | |
| this._copyRequested(object); | |
| return; | |
| } | |
| if (hints.queryObjects) { | |
| this._queryObjectsRequested(object); | |
| return; | |
| } | |
| if (object.isNode()) { | |
| Common.Revealer.reveal(object).then(object.release.bind(object)); | |
| return; | |
| } | |
| if (object.type === 'function') { | |
| SDK.RemoteFunction.objectAsFunction(object).targetFunctionDetails().then(didGetDetails); | |
| return; | |
| } | |
| /** | |
| * @param {?SDK.DebuggerModel.FunctionDetails} response | |
| */ | |
| function didGetDetails(response) { | |
| object.release(); | |
| if (!response || !response.location) { | |
| return; | |
| } | |
| Common.Revealer.reveal(response.location); | |
| } | |
| object.release(); | |
| } | |
| /** | |
| * @param {!SDK.RemoteObject} object | |
| */ | |
| _copyRequested(object) { | |
| if (!object.objectId) { | |
| Host.InspectorFrontendHost.copyText(object.unserializableValue() || /** @type {string} */ (object.value)); | |
| return; | |
| } | |
| object.callFunctionJSON(toStringForClipboard, [{value: object.subtype}]) | |
| .then(Host.InspectorFrontendHost.copyText.bind(Host.InspectorFrontendHost)); | |
| /** | |
| * @param {string} subtype | |
| * @this {Object} | |
| * @suppressReceiverCheck | |
| */ | |
| function toStringForClipboard(subtype) { | |
| if (subtype === 'node') { | |
| return this.outerHTML; | |
| } | |
| if (subtype && typeof this === 'undefined') { | |
| return subtype + ''; | |
| } | |
| try { | |
| return JSON.stringify(this, null, ' '); | |
| } catch (e) { | |
| return '' + this; | |
| } | |
| } | |
| } | |
| /** | |
| * @param {!SDK.RemoteObject} object | |
| */ | |
| async _queryObjectsRequested(object) { | |
| const result = await this.queryObjects(object); | |
| object.release(); | |
| if (result.error) { | |
| Common.console.error(result.error); | |
| return; | |
| } | |
| this.dispatchEventToListeners(Events.QueryObjectRequested, {objects: result.objects}); | |
| } | |
| /** | |
| * @param {!Protocol.Runtime.ExceptionDetails} exceptionDetails | |
| * @return {string} | |
| */ | |
| static simpleTextFromException(exceptionDetails) { | |
| let text = exceptionDetails.text; | |
| if (exceptionDetails.exception && exceptionDetails.exception.description) { | |
| let description = exceptionDetails.exception.description; | |
| if (description.indexOf('\n') !== -1) { | |
| description = description.substring(0, description.indexOf('\n')); | |
| } | |
| text += ' ' + description; | |
| } | |
| return text; | |
| } | |
| /** | |
| * @param {number} timestamp | |
| * @param {!Protocol.Runtime.ExceptionDetails} exceptionDetails | |
| */ | |
| exceptionThrown(timestamp, exceptionDetails) { | |
| const exceptionWithTimestamp = {timestamp: timestamp, details: exceptionDetails}; | |
| this.dispatchEventToListeners(Events.ExceptionThrown, exceptionWithTimestamp); | |
| } | |
| /** | |
| * @param {number} exceptionId | |
| */ | |
| _exceptionRevoked(exceptionId) { | |
| this.dispatchEventToListeners(Events.ExceptionRevoked, exceptionId); | |
| } | |
| /** | |
| * @param {string} type | |
| * @param {!Array.<!Protocol.Runtime.RemoteObject>} args | |
| * @param {number} executionContextId | |
| * @param {number} timestamp | |
| * @param {!Protocol.Runtime.StackTrace=} stackTrace | |
| * @param {string=} context | |
| */ | |
| _consoleAPICalled(type, args, executionContextId, timestamp, stackTrace, context) { | |
| const consoleAPICall = { | |
| type: type, | |
| args: args, | |
| executionContextId: executionContextId, | |
| timestamp: timestamp, | |
| stackTrace: stackTrace, | |
| context: context | |
| }; | |
| this.dispatchEventToListeners(Events.ConsoleAPICalled, consoleAPICall); | |
| } | |
| /** | |
| * @param {!Protocol.Runtime.ScriptId} scriptId | |
| * @return {number} | |
| */ | |
| executionContextIdForScriptId(scriptId) { | |
| const script = this.debuggerModel().scriptForId(scriptId); | |
| return script ? script.executionContextId : 0; | |
| } | |
| /** | |
| * @param {!Protocol.Runtime.StackTrace} stackTrace | |
| * @return {number} | |
| */ | |
| executionContextForStackTrace(stackTrace) { | |
| while (stackTrace && !stackTrace.callFrames.length) { | |
| stackTrace = stackTrace.parent; | |
| } | |
| if (!stackTrace || !stackTrace.callFrames.length) { | |
| return 0; | |
| } | |
| return this.executionContextIdForScriptId(stackTrace.callFrames[0].scriptId); | |
| } | |
| /** | |
| * @return {?boolean} | |
| */ | |
| hasSideEffectSupport() { | |
| return this._hasSideEffectSupport; | |
| } | |
| /** | |
| * @return {!Promise<boolean>} | |
| */ | |
| async checkSideEffectSupport() { | |
| const testContext = this.executionContexts().peekLast(); | |
| if (!testContext) { | |
| return false; | |
| } | |
| // Check for a positive throwOnSideEffect response without triggering side effects. | |
| const response = await this._agent.invoke_evaluate({ | |
| expression: String.escapeInvalidUnicodeCharacters(_sideEffectTestExpression), | |
| contextId: testContext.id, | |
| throwOnSideEffect: true | |
| }); | |
| this._hasSideEffectSupport = RuntimeModel.isSideEffectFailure(response); | |
| return this._hasSideEffectSupport; | |
| } | |
| /** | |
| * @return {!Promise} | |
| */ | |
| terminateExecution() { | |
| return this._agent.invoke_terminateExecution({}); | |
| } | |
| } | |
| /** | |
| * This expression: | |
| * - IMPORTANT: must not actually cause user-visible or JS-visible side-effects. | |
| * - Must throw when evaluated with `throwOnSideEffect: true`. | |
| * - Must be valid when run from any ExecutionContext that supports `throwOnSideEffect`. | |
| * @const | |
| * @type {string} | |
| */ | |
| const _sideEffectTestExpression = '(async function(){ await 1; })()'; | |
| /** @enum {symbol} */ | |
| export const Events = { | |
| ExecutionContextCreated: Symbol('ExecutionContextCreated'), | |
| ExecutionContextDestroyed: Symbol('ExecutionContextDestroyed'), | |
| ExecutionContextChanged: Symbol('ExecutionContextChanged'), | |
| ExecutionContextOrderChanged: Symbol('ExecutionContextOrderChanged'), | |
| ExceptionThrown: Symbol('ExceptionThrown'), | |
| ExceptionRevoked: Symbol('ExceptionRevoked'), | |
| ConsoleAPICalled: Symbol('ConsoleAPICalled'), | |
| QueryObjectRequested: Symbol('QueryObjectRequested'), | |
| }; | |
| /** | |
| * @extends {Protocol.RuntimeDispatcher} | |
| * @unrestricted | |
| */ | |
| class RuntimeDispatcher { | |
| /** | |
| * @param {!RuntimeModel} runtimeModel | |
| */ | |
| constructor(runtimeModel) { | |
| this._runtimeModel = runtimeModel; | |
| } | |
| /** | |
| * @override | |
| * @param {!Protocol.Runtime.ExecutionContextDescription} context | |
| */ | |
| executionContextCreated(context) { | |
| this._runtimeModel._executionContextCreated(context); | |
| } | |
| /** | |
| * @override | |
| * @param {!Protocol.Runtime.ExecutionContextId} executionContextId | |
| */ | |
| executionContextDestroyed(executionContextId) { | |
| this._runtimeModel._executionContextDestroyed(executionContextId); | |
| } | |
| /** | |
| * @override | |
| */ | |
| executionContextsCleared() { | |
| this._runtimeModel._executionContextsCleared(); | |
| } | |
| /** | |
| * @override | |
| * @param {number} timestamp | |
| * @param {!Protocol.Runtime.ExceptionDetails} exceptionDetails | |
| */ | |
| exceptionThrown(timestamp, exceptionDetails) { | |
| this._runtimeModel.exceptionThrown(timestamp, exceptionDetails); | |
| } | |
| /** | |
| * @override | |
| * @param {string} reason | |
| * @param {number} exceptionId | |
| */ | |
| exceptionRevoked(reason, exceptionId) { | |
| this._runtimeModel._exceptionRevoked(exceptionId); | |
| } | |
| /** | |
| * @override | |
| * @param {string} type | |
| * @param {!Array.<!Protocol.Runtime.RemoteObject>} args | |
| * @param {number} executionContextId | |
| * @param {number} timestamp | |
| * @param {!Protocol.Runtime.StackTrace=} stackTrace | |
| * @param {string=} context | |
| */ | |
| consoleAPICalled(type, args, executionContextId, timestamp, stackTrace, context) { | |
| this._runtimeModel._consoleAPICalled(type, args, executionContextId, timestamp, stackTrace, context); | |
| } | |
| /** | |
| * @override | |
| * @param {!Protocol.Runtime.RemoteObject} payload | |
| * @param {!Object=} hints | |
| */ | |
| inspectRequested(payload, hints) { | |
| this._runtimeModel._inspectRequested(payload, hints); | |
| } | |
| } | |
| /** | |
| * @unrestricted | |
| */ | |
| export class ExecutionContext { | |
| /** | |
| * @param {!RuntimeModel} runtimeModel | |
| * @param {number} id | |
| * @param {string} name | |
| * @param {string} origin | |
| * @param {boolean} isDefault | |
| * @param {string=} frameId | |
| */ | |
| constructor(runtimeModel, id, name, origin, isDefault, frameId) { | |
| this.id = id; | |
| this.name = name; | |
| this.origin = origin; | |
| this.isDefault = isDefault; | |
| this.runtimeModel = runtimeModel; | |
| this.debuggerModel = runtimeModel.debuggerModel(); | |
| this.frameId = frameId; | |
| this._setLabel(''); | |
| } | |
| /** | |
| * @return {!SDK.Target} | |
| */ | |
| target() { | |
| return this.runtimeModel.target(); | |
| } | |
| /** | |
| * @param {!ExecutionContext} a | |
| * @param {!ExecutionContext} b | |
| * @return {number} | |
| */ | |
| static comparator(a, b) { | |
| /** | |
| * @param {!SDK.Target} target | |
| * @return {number} | |
| */ | |
| function targetWeight(target) { | |
| if (!target.parentTarget()) { | |
| return 5; | |
| } | |
| if (target.type() === SDK.Target.Type.Frame) { | |
| return 4; | |
| } | |
| if (target.type() === SDK.Target.Type.ServiceWorker) { | |
| return 3; | |
| } | |
| if (target.type() === SDK.Target.Type.Worker) { | |
| return 2; | |
| } | |
| return 1; | |
| } | |
| /** | |
| * @param {!SDK.Target} target | |
| * @return {!Array<!SDK.Target>} | |
| */ | |
| function targetPath(target) { | |
| let currentTarget = target; | |
| const parents = []; | |
| while (currentTarget) { | |
| parents.push(currentTarget); | |
| currentTarget = currentTarget.parentTarget(); | |
| } | |
| return parents.reverse(); | |
| } | |
| const tagetsA = targetPath(a.target()); | |
| const targetsB = targetPath(b.target()); | |
| let targetA; | |
| let targetB; | |
| for (let i = 0;; i++) { | |
| if (!tagetsA[i] || !targetsB[i] || (tagetsA[i] !== targetsB[i])) { | |
| targetA = tagetsA[i]; | |
| targetB = targetsB[i]; | |
| break; | |
| } | |
| } | |
| if (!targetA && targetB) { | |
| return -1; | |
| } | |
| if (!targetB && targetA) { | |
| return 1; | |
| } | |
| if (targetA && targetB) { | |
| const weightDiff = targetWeight(targetA) - targetWeight(targetB); | |
| if (weightDiff) { | |
| return -weightDiff; | |
| } | |
| return targetA.id().localeCompare(targetB.id()); | |
| } | |
| // Main world context should always go first. | |
| if (a.isDefault) { | |
| return -1; | |
| } | |
| if (b.isDefault) { | |
| return +1; | |
| } | |
| return a.name.localeCompare(b.name); | |
| } | |
| /** | |
| * @param {!SDK.RuntimeModel.EvaluationOptions} options | |
| * @param {boolean} userGesture | |
| * @param {boolean} awaitPromise | |
| * @return {!Promise<!SDK.RuntimeModel.EvaluationResult>} | |
| */ | |
| evaluate(options, userGesture, awaitPromise) { | |
| // FIXME: It will be moved to separate ExecutionContext. | |
| if (this.debuggerModel.selectedCallFrame()) { | |
| return this.debuggerModel.evaluateOnSelectedCallFrame(options); | |
| } | |
| // Assume backends either support both throwOnSideEffect and timeout options or neither. | |
| const needsTerminationOptions = !!options.throwOnSideEffect || options.timeout !== undefined; | |
| if (!needsTerminationOptions || this.runtimeModel.hasSideEffectSupport()) { | |
| return this._evaluateGlobal(options, userGesture, awaitPromise); | |
| } | |
| /** @type {!SDK.RuntimeModel.EvaluationResult} */ | |
| const unsupportedError = {error: 'Side-effect checks not supported by backend.'}; | |
| if (this.runtimeModel.hasSideEffectSupport() === false) { | |
| return Promise.resolve(unsupportedError); | |
| } | |
| return this.runtimeModel.checkSideEffectSupport().then(() => { | |
| if (this.runtimeModel.hasSideEffectSupport()) { | |
| return this._evaluateGlobal(options, userGesture, awaitPromise); | |
| } | |
| return Promise.resolve(unsupportedError); | |
| }); | |
| } | |
| /** | |
| * @param {string} objectGroup | |
| * @param {boolean} generatePreview | |
| * @return {!Promise<!SDK.RuntimeModel.EvaluationResult>} | |
| */ | |
| globalObject(objectGroup, generatePreview) { | |
| return this._evaluateGlobal( | |
| { | |
| expression: 'this', | |
| objectGroup: objectGroup, | |
| includeCommandLineAPI: false, | |
| silent: true, | |
| returnByValue: false, | |
| generatePreview: generatePreview | |
| }, | |
| /* userGesture */ false, /* awaitPromise */ false); | |
| } | |
| /** | |
| * @param {!SDK.RuntimeModel.EvaluationOptions} options | |
| * @param {boolean} userGesture | |
| * @param {boolean} awaitPromise | |
| * @return {!Promise<!SDK.RuntimeModel.EvaluationResult>} | |
| */ | |
| async _evaluateGlobal(options, userGesture, awaitPromise) { | |
| if (!options.expression) { | |
| // There is no expression, so the completion should happen against global properties. | |
| options.expression = 'this'; | |
| } | |
| const response = await this.runtimeModel._agent.invoke_evaluate({ | |
| expression: String.escapeInvalidUnicodeCharacters(options.expression), | |
| objectGroup: options.objectGroup, | |
| includeCommandLineAPI: options.includeCommandLineAPI, | |
| silent: options.silent, | |
| contextId: this.id, | |
| returnByValue: options.returnByValue, | |
| generatePreview: options.generatePreview, | |
| userGesture: userGesture, | |
| awaitPromise: awaitPromise, | |
| throwOnSideEffect: options.throwOnSideEffect, | |
| timeout: options.timeout, | |
| disableBreaks: options.disableBreaks, | |
| replMode: options.replMode | |
| }); | |
| const error = response[Protocol.Error]; | |
| if (error) { | |
| console.error(error); | |
| return {error: error}; | |
| } | |
| return {object: this.runtimeModel.createRemoteObject(response.result), exceptionDetails: response.exceptionDetails}; | |
| } | |
| /** | |
| * @return {!Promise<?Array<string>>} | |
| */ | |
| async globalLexicalScopeNames() { | |
| const response = await this.runtimeModel._agent.invoke_globalLexicalScopeNames({executionContextId: this.id}); | |
| return response[Protocol.Error] ? [] : response.names; | |
| } | |
| /** | |
| * @return {string} | |
| */ | |
| label() { | |
| return this._label; | |
| } | |
| /** | |
| * @param {string} label | |
| */ | |
| setLabel(label) { | |
| this._setLabel(label); | |
| this.runtimeModel.dispatchEventToListeners(Events.ExecutionContextChanged, this); | |
| } | |
| /** | |
| * @param {string} label | |
| */ | |
| _setLabel(label) { | |
| if (label) { | |
| this._label = label; | |
| return; | |
| } | |
| if (this.name) { | |
| this._label = this.name; | |
| return; | |
| } | |
| const parsedUrl = Common.ParsedURL.fromString(this.origin); | |
| this._label = parsedUrl ? parsedUrl.lastPathComponentWithFragment() : ''; | |
| } | |
| } | |
| /* Legacy exported object */ | |
| self.SDK = self.SDK || {}; | |
| /* Legacy exported object */ | |
| SDK = SDK || {}; | |
| /** @constructor */ | |
| SDK.RuntimeModel = RuntimeModel; | |
| /** @enum {symbol} */ | |
| SDK.RuntimeModel.Events = Events; | |
| /** @constructor */ | |
| SDK.ExecutionContext = ExecutionContext; | |
| /** @typedef {{ | |
| * scriptId: (Protocol.Runtime.ScriptId|undefined), | |
| * exceptionDetails: (!Protocol.Runtime.ExceptionDetails|undefined) | |
| * }} | |
| */ | |
| SDK.RuntimeModel.CompileScriptResult; | |
| /** @typedef {{ | |
| * expression: string, | |
| * objectGroup: (string|undefined), | |
| * includeCommandLineAPI: (boolean|undefined), | |
| * silent: (boolean|undefined), | |
| * returnByValue: (boolean|undefined), | |
| * generatePreview: (boolean|undefined), | |
| * throwOnSideEffect: (boolean|undefined), | |
| * timeout: (number|undefined), | |
| * disableBreaks: (boolean|undefined), | |
| * replMode: (boolean|undefined) | |
| * }} | |
| */ | |
| SDK.RuntimeModel.EvaluationOptions; | |
| /** @typedef {{ | |
| * object: (!SDK.RemoteObject|undefined), | |
| * exceptionDetails: (!Protocol.Runtime.ExceptionDetails|undefined), | |
| * error: (!Protocol.Error|undefined)} | |
| * }} | |
| */ | |
| SDK.RuntimeModel.EvaluationResult; | |
| /** @typedef {{ | |
| * objects: (!SDK.RemoteObject|undefined), | |
| * error: (!Protocol.Error|undefined)} | |
| * }} | |
| */ | |
| SDK.RuntimeModel.QueryObjectResult; | |
| /** | |
| * @typedef {{ | |
| * type: string, | |
| * args: !Array<!Protocol.Runtime.RemoteObject>, | |
| * executionContextId: number, | |
| * timestamp: number, | |
| * stackTrace: (!Protocol.Runtime.StackTrace|undefined) | |
| * }} | |
| */ | |
| SDK.RuntimeModel.ConsoleAPICall; | |
| /** @typedef {{timestamp: number, details: !Protocol.Runtime.ExceptionDetails}} */ | |
| SDK.RuntimeModel.ExceptionWithTimestamp; | |
| SDK.SDKModel.register(SDK.RuntimeModel, SDK.Target.Capability.JS, true); |