| /* |
| * 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); |