| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import type * as ProtocolProxyApi from '../../generated/protocol-proxy-api.js'; |
| import type * as Protocol from '../../generated/protocol.js'; |
| import * as Common from '../common/common.js'; |
| import * as Host from '../host/host.js'; |
| import type * as Platform from '../platform/platform.js'; |
| |
| import {DebuggerModel, type FunctionDetails} from './DebuggerModel.js'; |
| import {HeapProfilerModel} from './HeapProfilerModel.js'; |
| import { |
| RemoteFunction, |
| RemoteObject, |
| RemoteObjectImpl, |
| RemoteObjectProperty, |
| type ScopeRef, |
| ScopeRemoteObject, |
| } from './RemoteObject.js'; |
| import {SDKModel} from './SDKModel.js'; |
| import {Capability, type Target, Type} from './Target.js'; |
| |
| export class RuntimeModel extends SDKModel<EventTypes> { |
| readonly agent: ProtocolProxyApi.RuntimeApi; |
| readonly #executionContextById = new Map<number, ExecutionContext>(); |
| #executionContextComparator: (arg0: ExecutionContext, arg1: ExecutionContext) => number = ExecutionContext.comparator; |
| constructor(target: Target) { |
| super(target); |
| |
| this.agent = target.runtimeAgent(); |
| this.target().registerRuntimeDispatcher(new RuntimeDispatcher(this)); |
| void this.agent.invoke_enable(); |
| |
| if (Common.Settings.Settings.instance().moduleSetting('custom-formatters').get()) { |
| void this.agent.invoke_setCustomObjectFormatterEnabled({enabled: true}); |
| } |
| |
| Common.Settings.Settings.instance() |
| .moduleSetting('custom-formatters') |
| .addChangeListener(this.customFormattersStateChanged.bind(this)); |
| } |
| |
| static isSideEffectFailure(response: Protocol.Runtime.EvaluateResponse|EvaluationResult): boolean { |
| const exceptionDetails = 'exceptionDetails' in response && response.exceptionDetails; |
| return Boolean( |
| exceptionDetails && |
| exceptionDetails.exception?.description?.startsWith('EvalError: Possible side-effect in debug-evaluate')); |
| } |
| |
| debuggerModel(): DebuggerModel { |
| return this.target().model(DebuggerModel) as DebuggerModel; |
| } |
| |
| heapProfilerModel(): HeapProfilerModel { |
| return this.target().model(HeapProfilerModel) as HeapProfilerModel; |
| } |
| |
| executionContexts(): ExecutionContext[] { |
| return [...this.#executionContextById.values()].sort(this.executionContextComparator()); |
| } |
| |
| setExecutionContextComparator(comparator: (arg0: ExecutionContext, arg1: ExecutionContext) => number): void { |
| this.#executionContextComparator = comparator; |
| } |
| |
| /** |
| * comparator |
| */ |
| executionContextComparator(): (arg0: ExecutionContext, arg1: ExecutionContext) => number { |
| return this.#executionContextComparator; |
| } |
| |
| defaultExecutionContext(): ExecutionContext|null { |
| for (const context of this.executionContexts()) { |
| if (context.isDefault) { |
| return context; |
| } |
| } |
| return null; |
| } |
| |
| executionContext(id: number): ExecutionContext|null { |
| return this.#executionContextById.get(id) || null; |
| } |
| |
| executionContextCreated(context: Protocol.Runtime.ExecutionContextDescription): void { |
| const data = context.auxData || {isDefault: true}; |
| const executionContext = new ExecutionContext( |
| this, context.id, context.uniqueId, context.name, context.origin as Platform.DevToolsPath.UrlString, |
| data['isDefault'], data['frameId']); |
| this.#executionContextById.set(executionContext.id, executionContext); |
| this.dispatchEventToListeners(Events.ExecutionContextCreated, executionContext); |
| } |
| |
| executionContextDestroyed(executionContextId: number): void { |
| const executionContext = this.#executionContextById.get(executionContextId); |
| if (!executionContext) { |
| return; |
| } |
| this.debuggerModel().executionContextDestroyed(executionContext); |
| this.#executionContextById.delete(executionContextId); |
| this.dispatchEventToListeners(Events.ExecutionContextDestroyed, executionContext); |
| } |
| |
| fireExecutionContextOrderChanged(): void { |
| this.dispatchEventToListeners(Events.ExecutionContextOrderChanged, this); |
| } |
| |
| executionContextsCleared(): void { |
| this.debuggerModel().globalObjectCleared(); |
| const contexts = this.executionContexts(); |
| this.#executionContextById.clear(); |
| for (let i = 0; i < contexts.length; ++i) { |
| this.dispatchEventToListeners(Events.ExecutionContextDestroyed, contexts[i]); |
| } |
| } |
| |
| createRemoteObject(payload: Protocol.Runtime.RemoteObject): RemoteObject { |
| console.assert(typeof payload === 'object', 'Remote object payload should only be an object'); |
| return new RemoteObjectImpl( |
| this, payload.objectId, payload.type, payload.subtype, payload.value, payload.unserializableValue, |
| payload.description, payload.preview, payload.customPreview, payload.className); |
| } |
| |
| createScopeRemoteObject(payload: Protocol.Runtime.RemoteObject, scopeRef: ScopeRef): RemoteObject { |
| return new ScopeRemoteObject( |
| this, payload.objectId, scopeRef, payload.type, payload.subtype, payload.value, payload.unserializableValue, |
| payload.description, payload.preview); |
| } |
| |
| createRemoteObjectFromPrimitiveValue(value: string|number|bigint|boolean|undefined): RemoteObject { |
| const type = typeof value; |
| let unserializableValue: string|undefined = undefined; |
| const unserializableDescription = RemoteObject.unserializableDescription(value); |
| if (unserializableDescription !== null) { |
| unserializableValue = (unserializableDescription); |
| } |
| if (typeof unserializableValue !== 'undefined') { |
| value = undefined; |
| } |
| return new RemoteObjectImpl(this, undefined, type, undefined, value, unserializableValue); |
| } |
| |
| createRemotePropertyFromPrimitiveValue(name: string, value: string|number|boolean): RemoteObjectProperty { |
| return new RemoteObjectProperty(name, this.createRemoteObjectFromPrimitiveValue(value)); |
| } |
| |
| discardConsoleEntries(): void { |
| void this.agent.invoke_discardConsoleEntries(); |
| } |
| |
| releaseObjectGroup(objectGroup: string): void { |
| void this.agent.invoke_releaseObjectGroup({objectGroup}); |
| } |
| |
| releaseEvaluationResult(result: EvaluationResult): void { |
| if ('object' in result && result.object) { |
| result.object.release(); |
| } |
| if ('exceptionDetails' in result && result.exceptionDetails?.exception) { |
| const exception = result.exceptionDetails.exception; |
| const exceptionObject = this.createRemoteObject({type: exception.type, objectId: exception.objectId}); |
| exceptionObject.release(); |
| } |
| } |
| |
| runIfWaitingForDebugger(): void { |
| void this.agent.invoke_runIfWaitingForDebugger(); |
| } |
| |
| private customFormattersStateChanged({data: enabled}: Common.EventTarget.EventTargetEvent<boolean>): void { |
| void this.agent.invoke_setCustomObjectFormatterEnabled({enabled}); |
| } |
| |
| async compileScript( |
| expression: string, sourceURL: string, persistScript: boolean, |
| executionContextId: Protocol.Runtime.ExecutionContextId): Promise<CompileScriptResult|null> { |
| const response = await this.agent.invoke_compileScript({ |
| expression, |
| sourceURL, |
| persistScript, |
| executionContextId, |
| }); |
| |
| if (response.getError()) { |
| console.error(response.getError()); |
| return null; |
| } |
| return {scriptId: response.scriptId, exceptionDetails: response.exceptionDetails}; |
| } |
| |
| async runScript( |
| scriptId: Protocol.Runtime.ScriptId, executionContextId: Protocol.Runtime.ExecutionContextId, |
| objectGroup?: string, silent?: boolean, includeCommandLineAPI?: boolean, returnByValue?: boolean, |
| generatePreview?: boolean, awaitPromise?: boolean): Promise<EvaluationResult> { |
| const response = await this.agent.invoke_runScript({ |
| scriptId, |
| executionContextId, |
| objectGroup, |
| silent, |
| includeCommandLineAPI, |
| returnByValue, |
| generatePreview, |
| awaitPromise, |
| }); |
| |
| const error = response.getError(); |
| if (error) { |
| console.error(error); |
| return {error}; |
| } |
| return {object: this.createRemoteObject(response.result), exceptionDetails: response.exceptionDetails}; |
| } |
| |
| async queryObjects(prototype: RemoteObject): Promise<QueryObjectResult> { |
| if (!prototype.objectId) { |
| return {error: 'Prototype should be an Object.'}; |
| } |
| const response = |
| await this.agent.invoke_queryObjects({prototypeObjectId: prototype.objectId, objectGroup: 'console'}); |
| const error = response.getError(); |
| if (error) { |
| console.error(error); |
| return {error}; |
| } |
| return {objects: this.createRemoteObject(response.objects)}; |
| } |
| |
| async isolateId(): Promise<string> { |
| const response = await this.agent.invoke_getIsolateId(); |
| if (response.getError() || !response.id) { |
| return this.target().id(); |
| } |
| return response.id; |
| } |
| |
| async heapUsage(): Promise<{ |
| usedSize: number, |
| totalSize: number, |
| // Available after V8 13.4. Node.js has not yet been released with this version of V8 yet. |
| embedderHeapUsedSize?: number, |
| backingStorageSize?: number, |
| }|null> { |
| const result = await this.agent.invoke_getHeapUsage(); |
| return result.getError() ? null : result; |
| } |
| |
| inspectRequested(payload: Protocol.Runtime.RemoteObject, hints: unknown, executionContextId?: number): void { |
| const object = this.createRemoteObject(payload); |
| |
| if (hints !== null && typeof hints === 'object') { |
| if ('copyToClipboard' in hints && Boolean(hints.copyToClipboard)) { |
| this.copyRequested(object); |
| return; |
| } |
| |
| if ('queryObjects' in hints && hints.queryObjects) { |
| void this.queryObjectsRequested(object, executionContextId); |
| return; |
| } |
| } |
| |
| if (object.isNode()) { |
| void Common.Revealer.reveal(object).then(object.release.bind(object)); |
| return; |
| } |
| |
| if (object.type === 'function') { |
| void RemoteFunction.objectAsFunction(object).targetFunctionDetails().then(didGetDetails); |
| return; |
| } |
| |
| function didGetDetails(response: FunctionDetails|null): void { |
| object.release(); |
| if (!response?.location) { |
| return; |
| } |
| void Common.Revealer.reveal(response.location); |
| } |
| object.release(); |
| } |
| |
| async addBinding(event: Protocol.Runtime.AddBindingRequest): Promise<Protocol.ProtocolResponseWithError> { |
| return await this.agent.invoke_addBinding(event); |
| } |
| |
| async removeBinding(request: Protocol.Runtime.RemoveBindingRequest): Promise<Protocol.ProtocolResponseWithError> { |
| return await this.agent.invoke_removeBinding(request); |
| } |
| |
| bindingCalled(event: Protocol.Runtime.BindingCalledEvent): void { |
| this.dispatchEventToListeners(Events.BindingCalled, event); |
| } |
| |
| private copyRequested(object: RemoteObject): void { |
| if (!object.objectId) { |
| Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText( |
| object.unserializableValue() || (object.value as string)); |
| return; |
| } |
| |
| const indent = Common.Settings.Settings.instance().moduleSetting('text-editor-indent').get(); |
| void object |
| .callFunctionJSON(toStringForClipboard, [{ |
| value: { |
| subtype: object.subtype, |
| indent, |
| }, |
| }]) |
| .then(Host.InspectorFrontendHost.InspectorFrontendHostInstance.copyText.bind( |
| Host.InspectorFrontendHost.InspectorFrontendHostInstance)); |
| |
| function toStringForClipboard(this: Object, data: { |
| subtype: string, |
| indent: string, |
| }): string|undefined { |
| const subtype = data.subtype; |
| const indent = data.indent; |
| |
| if (subtype === 'node') { |
| return this instanceof Element ? this.outerHTML : undefined; |
| } |
| if (subtype && typeof this === 'undefined') { |
| return String(subtype); |
| } |
| try { |
| return JSON.stringify(this, null, indent); |
| } catch { |
| return String(this); |
| } |
| } |
| } |
| |
| private async queryObjectsRequested(object: RemoteObject, executionContextId?: number): Promise<void> { |
| const result = await this.queryObjects(object); |
| object.release(); |
| if ('error' in result) { |
| Common.Console.Console.instance().error(result.error); |
| return; |
| } |
| this.dispatchEventToListeners(Events.QueryObjectRequested, {objects: result.objects, executionContextId}); |
| } |
| |
| static simpleTextFromException(exceptionDetails: Protocol.Runtime.ExceptionDetails): string { |
| let text = exceptionDetails.text; |
| if (exceptionDetails.exception?.description) { |
| let description: string = exceptionDetails.exception.description; |
| if (description.indexOf('\n') !== -1) { |
| description = description.substring(0, description.indexOf('\n')); |
| } |
| text += ' ' + description; |
| } |
| return text; |
| } |
| |
| exceptionThrown(timestamp: number, exceptionDetails: Protocol.Runtime.ExceptionDetails): void { |
| const exceptionWithTimestamp = {timestamp, details: exceptionDetails}; |
| this.dispatchEventToListeners(Events.ExceptionThrown, exceptionWithTimestamp); |
| } |
| |
| exceptionRevoked(exceptionId: number): void { |
| this.dispatchEventToListeners(Events.ExceptionRevoked, exceptionId); |
| } |
| |
| consoleAPICalled( |
| type: Protocol.Runtime.ConsoleAPICalledEventType, args: Protocol.Runtime.RemoteObject[], |
| executionContextId: number, timestamp: number, stackTrace?: Protocol.Runtime.StackTrace, context?: string): void { |
| const consoleAPICall = { |
| type, |
| args, |
| executionContextId, |
| timestamp, |
| stackTrace, |
| context, |
| }; |
| this.dispatchEventToListeners(Events.ConsoleAPICalled, consoleAPICall); |
| } |
| |
| executionContextIdForScriptId(scriptId: string): number { |
| const script = this.debuggerModel().scriptForId(scriptId); |
| return script ? script.executionContextId : 0; |
| } |
| |
| executionContextForStackTrace(stackTrace: Protocol.Runtime.StackTrace): number { |
| let currentStackTrace: (Protocol.Runtime.StackTrace|null)|Protocol.Runtime.StackTrace = stackTrace; |
| while (currentStackTrace && !currentStackTrace.callFrames.length) { |
| currentStackTrace = currentStackTrace.parent || null; |
| } |
| if (!currentStackTrace?.callFrames.length) { |
| return 0; |
| } |
| return this.executionContextIdForScriptId(currentStackTrace.callFrames[0].scriptId); |
| } |
| |
| terminateExecution(): Promise<Protocol.ProtocolResponseWithError> { |
| return this.agent.invoke_terminateExecution(); |
| } |
| |
| async getExceptionDetails(errorObjectId: Protocol.Runtime.RemoteObjectId): |
| Promise<Protocol.Runtime.ExceptionDetails|undefined> { |
| const response = await this.agent.invoke_getExceptionDetails({errorObjectId}); |
| if (response.getError()) { |
| // This CDP method errors if called with non-Error object ids. Swallow that. |
| return undefined; |
| } |
| return response.exceptionDetails; |
| } |
| } |
| |
| export enum Events { |
| /* eslint-disable @typescript-eslint/naming-convention -- Used by web_tests. */ |
| BindingCalled = 'BindingCalled', |
| ExecutionContextCreated = 'ExecutionContextCreated', |
| ExecutionContextDestroyed = 'ExecutionContextDestroyed', |
| ExecutionContextChanged = 'ExecutionContextChanged', |
| ExecutionContextOrderChanged = 'ExecutionContextOrderChanged', |
| ExceptionThrown = 'ExceptionThrown', |
| ExceptionRevoked = 'ExceptionRevoked', |
| ConsoleAPICalled = 'ConsoleAPICalled', |
| QueryObjectRequested = 'QueryObjectRequested', |
| /* eslint-enable @typescript-eslint/naming-convention */ |
| } |
| |
| export interface ConsoleAPICall { |
| type: Protocol.Runtime.ConsoleAPICalledEventType; |
| args: Protocol.Runtime.RemoteObject[]; |
| executionContextId: number; |
| timestamp: number; |
| stackTrace?: Protocol.Runtime.StackTrace; |
| context?: string; |
| } |
| |
| export interface ExceptionWithTimestamp { |
| timestamp: number; |
| details: Protocol.Runtime.ExceptionDetails; |
| } |
| |
| export interface QueryObjectRequestedEvent { |
| objects: RemoteObject; |
| executionContextId?: number; |
| } |
| |
| export interface EventTypes { |
| [Events.BindingCalled]: Protocol.Runtime.BindingCalledEvent; |
| [Events.ExecutionContextCreated]: ExecutionContext; |
| [Events.ExecutionContextDestroyed]: ExecutionContext; |
| [Events.ExecutionContextChanged]: ExecutionContext; |
| [Events.ExecutionContextOrderChanged]: RuntimeModel; |
| [Events.ExceptionThrown]: ExceptionWithTimestamp; |
| [Events.ExceptionRevoked]: number; |
| [Events.ConsoleAPICalled]: ConsoleAPICall; |
| [Events.QueryObjectRequested]: QueryObjectRequestedEvent; |
| } |
| |
| class RuntimeDispatcher implements ProtocolProxyApi.RuntimeDispatcher { |
| readonly #runtimeModel: RuntimeModel; |
| constructor(runtimeModel: RuntimeModel) { |
| this.#runtimeModel = runtimeModel; |
| } |
| |
| executionContextCreated({context}: Protocol.Runtime.ExecutionContextCreatedEvent): void { |
| this.#runtimeModel.executionContextCreated(context); |
| } |
| |
| executionContextDestroyed({executionContextId}: Protocol.Runtime.ExecutionContextDestroyedEvent): void { |
| this.#runtimeModel.executionContextDestroyed(executionContextId); |
| } |
| |
| executionContextsCleared(): void { |
| this.#runtimeModel.executionContextsCleared(); |
| } |
| |
| exceptionThrown({timestamp, exceptionDetails}: Protocol.Runtime.ExceptionThrownEvent): void { |
| this.#runtimeModel.exceptionThrown(timestamp, exceptionDetails); |
| } |
| |
| exceptionRevoked({exceptionId}: Protocol.Runtime.ExceptionRevokedEvent): void { |
| this.#runtimeModel.exceptionRevoked(exceptionId); |
| } |
| |
| consoleAPICalled({type, args, executionContextId, timestamp, stackTrace, context}: |
| Protocol.Runtime.ConsoleAPICalledEvent): void { |
| this.#runtimeModel.consoleAPICalled(type, args, executionContextId, timestamp, stackTrace, context); |
| } |
| |
| inspectRequested({object, hints, executionContextId}: Protocol.Runtime.InspectRequestedEvent): void { |
| this.#runtimeModel.inspectRequested(object, hints, executionContextId); |
| } |
| |
| bindingCalled(event: Protocol.Runtime.BindingCalledEvent): void { |
| this.#runtimeModel.bindingCalled(event); |
| } |
| } |
| |
| export class ExecutionContext { |
| id: Protocol.Runtime.ExecutionContextId; |
| uniqueId: string; |
| name: string; |
| #label: string|null; |
| origin: Platform.DevToolsPath.UrlString; |
| isDefault: boolean; |
| runtimeModel: RuntimeModel; |
| debuggerModel: DebuggerModel; |
| frameId: Protocol.Page.FrameId|undefined; |
| constructor( |
| runtimeModel: RuntimeModel, id: Protocol.Runtime.ExecutionContextId, uniqueId: string, name: string, |
| origin: Platform.DevToolsPath.UrlString, isDefault: boolean, frameId?: Protocol.Page.FrameId) { |
| this.id = id; |
| this.uniqueId = uniqueId; |
| this.name = name; |
| this.#label = null; |
| this.origin = origin; |
| this.isDefault = isDefault; |
| this.runtimeModel = runtimeModel; |
| this.debuggerModel = runtimeModel.debuggerModel(); |
| this.frameId = frameId; |
| this.#setLabel(''); |
| } |
| |
| target(): Target { |
| return this.runtimeModel.target(); |
| } |
| |
| static comparator(a: ExecutionContext, b: ExecutionContext): number { |
| function targetWeight(target: Target): number { |
| if (target.parentTarget()?.type() !== Type.FRAME) { |
| return 5; |
| } |
| if (target.type() === Type.FRAME) { |
| return 4; |
| } |
| if (target.type() === Type.ServiceWorker) { |
| return 3; |
| } |
| if (target.type() === Type.Worker || target.type() === Type.SHARED_WORKER) { |
| return 2; |
| } |
| return 1; |
| } |
| |
| function targetPath(target: Target): Target[] { |
| let currentTarget: (Target|null)|Target = 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); |
| } |
| |
| async evaluate(options: EvaluationOptions, userGesture: boolean, awaitPromise: boolean): Promise<EvaluationResult> { |
| // FIXME: It will be moved to separate ExecutionContext. |
| if (this.debuggerModel.selectedCallFrame()) { |
| return await this.debuggerModel.evaluateOnSelectedCallFrame(options); |
| } |
| return await this.evaluateGlobal(options, userGesture, awaitPromise); |
| } |
| |
| globalObject(objectGroup: string, generatePreview: boolean): Promise<EvaluationResult> { |
| const evaluationOptions = { |
| expression: 'this', |
| objectGroup, |
| includeCommandLineAPI: false, |
| silent: true, |
| returnByValue: false, |
| generatePreview, |
| }; |
| return this.evaluateGlobal((evaluationOptions as EvaluationOptions), false, false); |
| } |
| |
| async callFunctionOn(options: CallFunctionOptions): Promise<EvaluationResult> { |
| const response = await this.runtimeModel.agent.invoke_callFunctionOn({ |
| functionDeclaration: options.functionDeclaration, |
| returnByValue: options.returnByValue, |
| userGesture: options.userGesture, |
| awaitPromise: options.awaitPromise, |
| throwOnSideEffect: options.throwOnSideEffect, |
| arguments: options.arguments, |
| // Old back-ends don't know about uniqueContextId (and also don't generate |
| // one), so fall back to contextId in that case (https://crbug.com/1192621). |
| ...(this.uniqueId ? {uniqueContextId: this.uniqueId} : {contextId: this.id}), |
| }); |
| |
| const error = response.getError(); |
| if (error) { |
| return {error}; |
| } |
| return {object: this.runtimeModel.createRemoteObject(response.result), exceptionDetails: response.exceptionDetails}; |
| } |
| |
| private async evaluateGlobal(options: EvaluationOptions, userGesture: boolean, awaitPromise: boolean): |
| Promise<EvaluationResult> { |
| 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, |
| returnByValue: options.returnByValue, |
| generatePreview: options.generatePreview, |
| userGesture, |
| awaitPromise, |
| throwOnSideEffect: options.throwOnSideEffect, |
| timeout: options.timeout, |
| disableBreaks: options.disableBreaks, |
| replMode: options.replMode, |
| allowUnsafeEvalBlockedByCSP: options.allowUnsafeEvalBlockedByCSP, |
| // Old back-ends don't know about uniqueContextId (and also don't generate |
| // one), so fall back to contextId in that case (https://crbug.com/1192621). |
| ...(this.uniqueId ? {uniqueContextId: this.uniqueId} : {contextId: this.id}), |
| }); |
| |
| const error = response.getError(); |
| if (error) { |
| console.error(error); |
| return {error}; |
| } |
| return {object: this.runtimeModel.createRemoteObject(response.result), exceptionDetails: response.exceptionDetails}; |
| } |
| |
| async globalLexicalScopeNames(): Promise<string[]|null> { |
| const response = await this.runtimeModel.agent.invoke_globalLexicalScopeNames({executionContextId: this.id}); |
| return response.getError() ? [] : response.names; |
| } |
| |
| label(): string|null { |
| return this.#label; |
| } |
| |
| setLabel(label: string): void { |
| this.#setLabel(label); |
| this.runtimeModel.dispatchEventToListeners(Events.ExecutionContextChanged, this); |
| } |
| |
| #setLabel(label: string): void { |
| if (label) { |
| this.#label = label; |
| return; |
| } |
| if (this.name) { |
| this.#label = this.name; |
| return; |
| } |
| const parsedUrl = Common.ParsedURL.ParsedURL.fromString(this.origin); |
| this.#label = parsedUrl ? parsedUrl.lastPathComponentWithFragment() : ''; |
| } |
| } |
| |
| SDKModel.register(RuntimeModel, {capabilities: Capability.JS, autostart: true}); |
| |
| export type EvaluationResult = { |
| object: RemoteObject, |
| exceptionDetails?: Protocol.Runtime.ExceptionDetails, |
| }|{ |
| error: string, |
| }; |
| |
| export interface CompileScriptResult { |
| scriptId?: string; |
| exceptionDetails?: Protocol.Runtime.ExceptionDetails; |
| } |
| |
| export interface EvaluationOptions { |
| expression: string; |
| objectGroup?: string; |
| includeCommandLineAPI?: boolean; |
| silent?: boolean; |
| returnByValue?: boolean; |
| generatePreview?: boolean; |
| throwOnSideEffect?: boolean; |
| timeout?: number; |
| disableBreaks?: boolean; |
| replMode?: boolean; |
| allowUnsafeEvalBlockedByCSP?: boolean; |
| contextId?: number; |
| } |
| |
| export interface CallFunctionOptions { |
| functionDeclaration: string; |
| returnByValue?: boolean; |
| throwOnSideEffect?: boolean; |
| allowUnsafeEvalBlockedByCSP?: boolean; |
| arguments: Protocol.Runtime.CallArgument[]; |
| userGesture: boolean; |
| awaitPromise: boolean; |
| } |
| |
| export type QueryObjectResult = { |
| objects: RemoteObject, |
| }|{error: string}; |