mirror of
https://github.com/react/react-native-devtools-frontend.git
synced 2026-10-10 21:54:47 +08:00
To make type checks using closure we generate protocol_externs, for
each domain we generate Protocol.${domainName}Dispatcher @interface.
SDK.${domainName}Dispatcher uses @implements with generated interface,
closure compiler in this case forces us to have all interface method
implemented.
Problem: when we try to add new event for one of V8 domains, we can not
add this notification and its implementation in the same CL, so closure
blocks landing new events in V8 domains.
We can resolve this problem by using @extends instead of @implements
for V8 domains. In this case closure will still check that if we
override something - we override it properly but allow us to not
override all events.
I tried this approach for Page domain as well since it contains a lot
of unused by DevTools events but my attempt failed because we have
SDK.ScreenCaptureModel. It implements dispatcher and extends model,
unfortunately closure does not support multiple inheritance and to
use @extends here we should extract dispatcher from ScreenCaptureModel.
R=dgozman@chromium.org
Bug: chromium:848849
Change-Id: I14fa87b53aeaff5bd7daea7dd36a6917688841cf
Reviewed-on: https://chromium-review.googlesource.com/1082594
Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org>
Reviewed-by: Andrey Lushnikov <lushnikov@chromium.org>
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
Cr-Original-Commit-Position: refs/heads/master@{#563830}
Cr-Mirrored-From: https://chromium.googlesource.com/chromium/src
Cr-Mirrored-Commit: 14157eee77965e191ec21af278bdcb2aecf9f08e
841 lines
25 KiB
JavaScript
841 lines
25 KiB
JavaScript
/*
|
|
* 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
|
|
*/
|
|
SDK.RuntimeModel = class extends SDK.SDKModel {
|
|
/**
|
|
* @param {!SDK.Target} target
|
|
*/
|
|
constructor(target) {
|
|
super(target);
|
|
|
|
this._agent = target.runtimeAgent();
|
|
this.target().registerRuntimeDispatcher(new SDK.RuntimeDispatcher(this));
|
|
this._agent.enable();
|
|
/** @type {!Map<number, !SDK.ExecutionContext>} */
|
|
this._executionContextById = new Map();
|
|
this._executionContextComparator = SDK.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 {string} code
|
|
* @return {string}
|
|
*/
|
|
static wrapObjectLiteralExpressionIfNeeded(code) {
|
|
// Only parenthesize what appears to be an object literal.
|
|
if (!(/^\s*\{/.test(code) && /\}\s*$/.test(code)))
|
|
return code;
|
|
|
|
const parse = (async () => 0).constructor;
|
|
try {
|
|
// Check if the code can be interpreted as an expression.
|
|
parse('return ' + code + ';');
|
|
|
|
// No syntax error! Does it work parenthesized?
|
|
const wrappedCode = '(' + code + ')';
|
|
parse(wrappedCode);
|
|
|
|
return wrappedCode;
|
|
} catch (e) {
|
|
return code;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @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.<!SDK.ExecutionContext>}
|
|
*/
|
|
executionContexts() {
|
|
return this._executionContextById.valuesArray().sort(this.executionContextComparator());
|
|
}
|
|
|
|
/**
|
|
* @param {function(!SDK.ExecutionContext,!SDK.ExecutionContext)} comparator
|
|
*/
|
|
setExecutionContextComparator(comparator) {
|
|
this._executionContextComparator = comparator;
|
|
}
|
|
|
|
/**
|
|
* @return {function(!SDK.ExecutionContext,!SDK.ExecutionContext)} comparator
|
|
*/
|
|
executionContextComparator() {
|
|
return this._executionContextComparator;
|
|
}
|
|
|
|
/**
|
|
* @return {?SDK.ExecutionContext}
|
|
*/
|
|
defaultExecutionContext() {
|
|
for (const context of this.executionContexts()) {
|
|
if (context.isDefault)
|
|
return context;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* @param {!Protocol.Runtime.ExecutionContextId} id
|
|
* @return {?SDK.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 SDK.ExecutionContext(this, context.id, context.name, context.origin, data['isDefault'], data['frameId']);
|
|
this._executionContextById.set(executionContext.id, executionContext);
|
|
this.dispatchEventToListeners(SDK.RuntimeModel.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(SDK.RuntimeModel.Events.ExecutionContextDestroyed, executionContext);
|
|
}
|
|
|
|
fireExecutionContextOrderChanged() {
|
|
this.dispatchEventToListeners(SDK.RuntimeModel.Events.ExecutionContextOrderChanged, this);
|
|
}
|
|
|
|
_executionContextsCleared() {
|
|
this.debuggerModel().globalObjectCleared();
|
|
const contexts = this.executionContexts();
|
|
this._executionContextById.clear();
|
|
for (let i = 0; i < contexts.length; ++i)
|
|
this.dispatchEventToListeners(SDK.RuntimeModel.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);
|
|
}
|
|
|
|
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: 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) {
|
|
InspectorFrontendHost.copyText(object.unserializableValue() || object.value);
|
|
return;
|
|
}
|
|
object.callFunctionJSON(
|
|
toStringForClipboard, [{value: object.subtype}], InspectorFrontendHost.copyText.bind(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(SDK.RuntimeModel.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(SDK.RuntimeModel.Events.ExceptionThrown, exceptionWithTimestamp);
|
|
}
|
|
|
|
/**
|
|
* @param {number} exceptionId
|
|
*/
|
|
_exceptionRevoked(exceptionId) {
|
|
this.dispatchEventToListeners(SDK.RuntimeModel.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(SDK.RuntimeModel.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: SDK.RuntimeModel._sideEffectTestExpression, contextId: testContext.id, throwOnSideEffect: true});
|
|
|
|
const exceptionDetails = !response[Protocol.Error] && response.exceptionDetails;
|
|
const supports =
|
|
!!(exceptionDetails && exceptionDetails.exception &&
|
|
exceptionDetails.exception.description.startsWith('EvalError: Possible side-effect in debug-evaluate'));
|
|
this._hasSideEffectSupport = supports;
|
|
return supports;
|
|
}
|
|
|
|
/**
|
|
* @return {!Promise}
|
|
*/
|
|
terminateExecution() {
|
|
return this._agent.invoke_terminateExecution({});
|
|
}
|
|
};
|
|
|
|
SDK.SDKModel.register(SDK.RuntimeModel, SDK.Target.Capability.JS, true);
|
|
|
|
/**
|
|
* 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}
|
|
*/
|
|
SDK.RuntimeModel._sideEffectTestExpression = '(async function(){ await 1; })()';
|
|
|
|
/** @enum {symbol} */
|
|
SDK.RuntimeModel.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'),
|
|
};
|
|
|
|
/** @typedef {{timestamp: number, details: !Protocol.Runtime.ExceptionDetails}} */
|
|
SDK.RuntimeModel.ExceptionWithTimestamp;
|
|
|
|
/** @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)
|
|
* }}
|
|
*/
|
|
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;
|
|
|
|
/**
|
|
* @extends {Protocol.RuntimeDispatcher}
|
|
* @unrestricted
|
|
*/
|
|
SDK.RuntimeDispatcher = class {
|
|
/**
|
|
* @param {!SDK.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
|
|
*/
|
|
SDK.ExecutionContext = class {
|
|
/**
|
|
* @param {!SDK.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 {!SDK.ExecutionContext} a
|
|
* @param {!SDK.ExecutionContext} b
|
|
* @return {number}
|
|
*/
|
|
static comparator(a, b) {
|
|
/**
|
|
* @param {!SDK.Target} target
|
|
* @return {number}
|
|
*/
|
|
function targetWeight(target) {
|
|
if (!target.parentTarget())
|
|
return 4;
|
|
if (target.hasBrowserCapability())
|
|
return 3;
|
|
if (target.hasJSCapability())
|
|
return 2;
|
|
return 1;
|
|
}
|
|
|
|
const weightDiff = targetWeight(a.target()) - targetWeight(b.target());
|
|
if (weightDiff)
|
|
return -weightDiff;
|
|
|
|
// 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: 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
|
|
});
|
|
|
|
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(SDK.RuntimeModel.Events.ExecutionContextChanged, this);
|
|
}
|
|
|
|
/**
|
|
* @param {string} label
|
|
*/
|
|
_setLabel(label) {
|
|
if (label) {
|
|
this._label = label;
|
|
return;
|
|
}
|
|
if (this.name) {
|
|
this._label = this.name;
|
|
return;
|
|
}
|
|
const parsedUrl = this.origin.asParsedURL();
|
|
this._label = parsedUrl ? parsedUrl.lastPathComponentWithFragment() : '';
|
|
}
|
|
};
|