mirror of
				https://github.com/microsoft/playwright.git
				synced 2025-06-26 21:40:17 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			581 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			581 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 | 
						|
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
"use strict";
 | 
						|
// Note: this file should be loadabale with eval() into worker environment.
 | 
						|
// Avoid Components.*, ChromeUtils and global const variables.
 | 
						|
 | 
						|
if (!this.Debugger) {
 | 
						|
  // Worker has a Debugger defined already.
 | 
						|
  const {addDebuggerToGlobal} = ChromeUtils.import("resource://gre/modules/jsdebugger.jsm", {});
 | 
						|
  addDebuggerToGlobal(Components.utils.getGlobalForObject(this));
 | 
						|
}
 | 
						|
 | 
						|
let lastId = 0;
 | 
						|
function generateId() {
 | 
						|
  return 'id-' + (++lastId);
 | 
						|
}
 | 
						|
 | 
						|
const consoleLevelToProtocolType = {
 | 
						|
  'dir': 'dir',
 | 
						|
  'log': 'log',
 | 
						|
  'debug': 'debug',
 | 
						|
  'info': 'info',
 | 
						|
  'error': 'error',
 | 
						|
  'warn': 'warning',
 | 
						|
  'dirxml': 'dirxml',
 | 
						|
  'table': 'table',
 | 
						|
  'trace': 'trace',
 | 
						|
  'clear': 'clear',
 | 
						|
  'group': 'startGroup',
 | 
						|
  'groupCollapsed': 'startGroupCollapsed',
 | 
						|
  'groupEnd': 'endGroup',
 | 
						|
  'assert': 'assert',
 | 
						|
  'profile': 'profile',
 | 
						|
  'profileEnd': 'profileEnd',
 | 
						|
  'count': 'count',
 | 
						|
  'countReset': 'countReset',
 | 
						|
  'time': null,
 | 
						|
  'timeLog': 'timeLog',
 | 
						|
  'timeEnd': 'timeEnd',
 | 
						|
  'timeStamp': 'timeStamp',
 | 
						|
};
 | 
						|
 | 
						|
const disallowedMessageCategories = new Set([
 | 
						|
  'XPConnect JavaScript',
 | 
						|
  'component javascript',
 | 
						|
  'chrome javascript',
 | 
						|
  'chrome registration',
 | 
						|
  'XBL',
 | 
						|
  'XBL Prototype Handler',
 | 
						|
  'XBL Content Sink',
 | 
						|
  'xbl javascript',
 | 
						|
]);
 | 
						|
 | 
						|
class Runtime {
 | 
						|
  constructor(isWorker = false) {
 | 
						|
    this._debugger = new Debugger();
 | 
						|
    this._pendingPromises = new Map();
 | 
						|
    this._executionContexts = new Map();
 | 
						|
    this._windowToExecutionContext = new Map();
 | 
						|
    this._eventListeners = [];
 | 
						|
    if (isWorker) {
 | 
						|
      this._registerWorkerConsoleHandler();
 | 
						|
    } else {
 | 
						|
      const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 | 
						|
      this._registerConsoleServiceListener(Services);
 | 
						|
      this._registerConsoleObserver(Services);
 | 
						|
    }
 | 
						|
    // We can't use event listener here to be compatible with Worker Global Context.
 | 
						|
    // Use plain callbacks instead.
 | 
						|
    this.events = {
 | 
						|
      onConsoleMessage: createEvent(),
 | 
						|
      onErrorFromWorker: createEvent(),
 | 
						|
      onExecutionContextCreated: createEvent(),
 | 
						|
      onExecutionContextDestroyed: createEvent(),
 | 
						|
      onBindingCalled: createEvent(),
 | 
						|
    };
 | 
						|
  }
 | 
						|
 | 
						|
  executionContexts() {
 | 
						|
    return [...this._executionContexts.values()];
 | 
						|
  }
 | 
						|
 | 
						|
  async evaluate({executionContextId, expression, returnByValue}) {
 | 
						|
    const executionContext = this.findExecutionContext(executionContextId);
 | 
						|
    if (!executionContext)
 | 
						|
      throw new Error('Failed to find execution context with id = ' + executionContextId);
 | 
						|
    const exceptionDetails = {};
 | 
						|
    let result = await executionContext.evaluateScript(expression, exceptionDetails);
 | 
						|
    if (!result)
 | 
						|
      return {exceptionDetails};
 | 
						|
    if (returnByValue)
 | 
						|
      result = executionContext.ensureSerializedToValue(result);
 | 
						|
    return {result};
 | 
						|
  }
 | 
						|
 | 
						|
  async callFunction({executionContextId, functionDeclaration, args, returnByValue}) {
 | 
						|
    const executionContext = this.findExecutionContext(executionContextId);
 | 
						|
    if (!executionContext)
 | 
						|
      throw new Error('Failed to find execution context with id = ' + executionContextId);
 | 
						|
    const exceptionDetails = {};
 | 
						|
    let result = await executionContext.evaluateFunction(functionDeclaration, args, exceptionDetails);
 | 
						|
    if (!result)
 | 
						|
      return {exceptionDetails};
 | 
						|
    if (returnByValue)
 | 
						|
      result = executionContext.ensureSerializedToValue(result);
 | 
						|
    return {result};
 | 
						|
  }
 | 
						|
 | 
						|
  async getObjectProperties({executionContextId, objectId}) {
 | 
						|
    const executionContext = this.findExecutionContext(executionContextId);
 | 
						|
    if (!executionContext)
 | 
						|
      throw new Error('Failed to find execution context with id = ' + executionContextId);
 | 
						|
    return {properties: executionContext.getObjectProperties(objectId)};
 | 
						|
  }
 | 
						|
 | 
						|
  async disposeObject({executionContextId, objectId}) {
 | 
						|
    const executionContext = this.findExecutionContext(executionContextId);
 | 
						|
    if (!executionContext)
 | 
						|
      throw new Error('Failed to find execution context with id = ' + executionContextId);
 | 
						|
    return executionContext.disposeObject(objectId);
 | 
						|
  }
 | 
						|
 | 
						|
  _registerConsoleServiceListener(Services) {
 | 
						|
    const Ci = Components.interfaces;
 | 
						|
    const consoleServiceListener = {
 | 
						|
      QueryInterface: ChromeUtils.generateQI([Ci.nsIConsoleListener]),
 | 
						|
 | 
						|
      observe: message => {
 | 
						|
        if (!(message instanceof Ci.nsIScriptError) || !message.outerWindowID ||
 | 
						|
            !message.category || disallowedMessageCategories.has(message.category) || message.hasException) {
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        const errorWindow = Services.wm.getOuterWindowWithId(message.outerWindowID);
 | 
						|
        if (message.category === 'Web Worker' && message.logLevel === Ci.nsIConsoleMessage.error) {
 | 
						|
          emitEvent(this.events.onErrorFromWorker, errorWindow, message.message, '' + message.stack);
 | 
						|
          return;
 | 
						|
        }
 | 
						|
        const executionContext = this._windowToExecutionContext.get(errorWindow);
 | 
						|
        if (!executionContext)
 | 
						|
          return;
 | 
						|
        const typeNames = {
 | 
						|
          [Ci.nsIConsoleMessage.debug]: 'debug',
 | 
						|
          [Ci.nsIConsoleMessage.info]: 'info',
 | 
						|
          [Ci.nsIConsoleMessage.warn]: 'warn',
 | 
						|
          [Ci.nsIConsoleMessage.error]: 'error',
 | 
						|
        };
 | 
						|
        emitEvent(this.events.onConsoleMessage, {
 | 
						|
          args: [{
 | 
						|
            value: message.message,
 | 
						|
          }],
 | 
						|
          type: typeNames[message.logLevel],
 | 
						|
          executionContextId: executionContext.id(),
 | 
						|
          location: {
 | 
						|
            lineNumber: message.lineNumber,
 | 
						|
            columnNumber: message.columnNumber,
 | 
						|
            url: message.sourceName,
 | 
						|
          },
 | 
						|
        });
 | 
						|
      },
 | 
						|
    };
 | 
						|
    Services.console.registerListener(consoleServiceListener);
 | 
						|
    this._eventListeners.push(() => Services.console.unregisterListener(consoleServiceListener));
 | 
						|
  }
 | 
						|
 | 
						|
  _registerConsoleObserver(Services) {
 | 
						|
    const consoleObserver = ({wrappedJSObject}, topic, data) => {
 | 
						|
      const executionContext = Array.from(this._executionContexts.values()).find(context => {
 | 
						|
        // There is no easy way to determine isolated world context and we normally don't write
 | 
						|
        // objects to console from utility worlds so we always return main world context here.
 | 
						|
        if (context._isIsolatedWorldContext())
 | 
						|
          return false;
 | 
						|
        const domWindow = context._domWindow;
 | 
						|
        return domWindow && domWindow.windowGlobalChild.innerWindowId === wrappedJSObject.innerID;
 | 
						|
      });
 | 
						|
      if (!executionContext)
 | 
						|
        return;
 | 
						|
      this._onConsoleMessage(executionContext, wrappedJSObject);
 | 
						|
    };
 | 
						|
    Services.obs.addObserver(consoleObserver, "console-api-log-event");
 | 
						|
    this._eventListeners.push(() => Services.obs.removeObserver(consoleObserver, "console-api-log-event"));
 | 
						|
  }
 | 
						|
 | 
						|
  _registerWorkerConsoleHandler() {
 | 
						|
    setConsoleEventHandler(message => {
 | 
						|
      const executionContext = Array.from(this._executionContexts.values())[0];
 | 
						|
      this._onConsoleMessage(executionContext, message);
 | 
						|
    });
 | 
						|
    this._eventListeners.push(() => setConsoleEventHandler(null));
 | 
						|
  }
 | 
						|
 | 
						|
  _onConsoleMessage(executionContext, message) {
 | 
						|
    const type = consoleLevelToProtocolType[message.level];
 | 
						|
    if (!type)
 | 
						|
      return;
 | 
						|
    const args = message.arguments.map(arg => executionContext.rawValueToRemoteObject(arg));
 | 
						|
    emitEvent(this.events.onConsoleMessage, {
 | 
						|
      args,
 | 
						|
      type,
 | 
						|
      executionContextId: executionContext.id(),
 | 
						|
      location: {
 | 
						|
        lineNumber: message.lineNumber - 1,
 | 
						|
        columnNumber: message.columnNumber - 1,
 | 
						|
        url: message.filename,
 | 
						|
      },
 | 
						|
    });
 | 
						|
  }
 | 
						|
 | 
						|
  dispose() {
 | 
						|
    for (const tearDown of this._eventListeners)
 | 
						|
      tearDown.call(null);
 | 
						|
    this._eventListeners = [];
 | 
						|
  }
 | 
						|
 | 
						|
  async _awaitPromise(executionContext, obj, exceptionDetails = {}) {
 | 
						|
    if (obj.promiseState === 'fulfilled')
 | 
						|
      return {success: true, obj: obj.promiseValue};
 | 
						|
    if (obj.promiseState === 'rejected') {
 | 
						|
      const debuggee = executionContext._debuggee;
 | 
						|
      exceptionDetails.text = debuggee.executeInGlobalWithBindings('e.message', {e: obj.promiseReason}).return;
 | 
						|
      exceptionDetails.stack = debuggee.executeInGlobalWithBindings('e.stack', {e: obj.promiseReason}).return;
 | 
						|
      return {success: false, obj: null};
 | 
						|
    }
 | 
						|
    let resolve, reject;
 | 
						|
    const promise = new Promise((a, b) => {
 | 
						|
      resolve = a;
 | 
						|
      reject = b;
 | 
						|
    });
 | 
						|
    this._pendingPromises.set(obj.promiseID, {resolve, reject, executionContext, exceptionDetails});
 | 
						|
    if (this._pendingPromises.size === 1)
 | 
						|
      this._debugger.onPromiseSettled = this._onPromiseSettled.bind(this);
 | 
						|
    return await promise;
 | 
						|
  }
 | 
						|
 | 
						|
  _onPromiseSettled(obj) {
 | 
						|
    const pendingPromise = this._pendingPromises.get(obj.promiseID);
 | 
						|
    if (!pendingPromise)
 | 
						|
      return;
 | 
						|
    this._pendingPromises.delete(obj.promiseID);
 | 
						|
    if (!this._pendingPromises.size)
 | 
						|
      this._debugger.onPromiseSettled = undefined;
 | 
						|
 | 
						|
    if (obj.promiseState === 'fulfilled') {
 | 
						|
      pendingPromise.resolve({success: true, obj: obj.promiseValue});
 | 
						|
      return;
 | 
						|
    };
 | 
						|
    const debuggee = pendingPromise.executionContext._debuggee;
 | 
						|
    pendingPromise.exceptionDetails.text = debuggee.executeInGlobalWithBindings('e.message', {e: obj.promiseReason}).return;
 | 
						|
    pendingPromise.exceptionDetails.stack = debuggee.executeInGlobalWithBindings('e.stack', {e: obj.promiseReason}).return;
 | 
						|
    pendingPromise.resolve({success: false, obj: null});
 | 
						|
  }
 | 
						|
 | 
						|
  createExecutionContext(domWindow, contextGlobal, auxData) {
 | 
						|
    // Note: domWindow is null for workers.
 | 
						|
    const context = new ExecutionContext(this, domWindow, contextGlobal, auxData);
 | 
						|
    this._executionContexts.set(context._id, context);
 | 
						|
    if (domWindow)
 | 
						|
      this._windowToExecutionContext.set(domWindow, context);
 | 
						|
    emitEvent(this.events.onExecutionContextCreated, context);
 | 
						|
    return context;
 | 
						|
  }
 | 
						|
 | 
						|
  findExecutionContext(executionContextId) {
 | 
						|
    const executionContext = this._executionContexts.get(executionContextId);
 | 
						|
    if (!executionContext)
 | 
						|
      throw new Error('Failed to find execution context with id = ' + executionContextId);
 | 
						|
    return executionContext;
 | 
						|
  }
 | 
						|
 | 
						|
  destroyExecutionContext(destroyedContext) {
 | 
						|
    for (const [promiseID, {reject, executionContext}] of this._pendingPromises) {
 | 
						|
      if (executionContext === destroyedContext) {
 | 
						|
        reject(new Error('Execution context was destroyed!'));
 | 
						|
        this._pendingPromises.delete(promiseID);
 | 
						|
      }
 | 
						|
    }
 | 
						|
    if (!this._pendingPromises.size)
 | 
						|
      this._debugger.onPromiseSettled = undefined;
 | 
						|
    this._debugger.removeDebuggee(destroyedContext._contextGlobal);
 | 
						|
    this._executionContexts.delete(destroyedContext._id);
 | 
						|
    if (destroyedContext._domWindow)
 | 
						|
      this._windowToExecutionContext.delete(destroyedContext._domWindow);
 | 
						|
    emitEvent(this.events.onExecutionContextDestroyed, destroyedContext);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
class ExecutionContext {
 | 
						|
  constructor(runtime, domWindow, contextGlobal, auxData) {
 | 
						|
    this._runtime = runtime;
 | 
						|
    this._domWindow = domWindow;
 | 
						|
    this._contextGlobal = contextGlobal;
 | 
						|
    this._debuggee = runtime._debugger.addDebuggee(contextGlobal);
 | 
						|
    this._remoteObjects = new Map();
 | 
						|
    this._id = generateId();
 | 
						|
    this._auxData = auxData;
 | 
						|
    this._jsonStringifyObject = this._debuggee.executeInGlobal(`((stringify, object) => {
 | 
						|
      const oldToJSON = Date.prototype.toJSON;
 | 
						|
      Date.prototype.toJSON = undefined;
 | 
						|
      const oldArrayToJSON = Array.prototype.toJSON;
 | 
						|
      const oldArrayHadToJSON = Array.prototype.hasOwnProperty('toJSON');
 | 
						|
      if (oldArrayHadToJSON)
 | 
						|
        Array.prototype.toJSON = undefined;
 | 
						|
 | 
						|
      let hasSymbol = false;
 | 
						|
      const result = stringify(object, (key, value) => {
 | 
						|
        if (typeof value === 'symbol')
 | 
						|
          hasSymbol = true;
 | 
						|
        return value;
 | 
						|
      });
 | 
						|
 | 
						|
      Date.prototype.toJSON = oldToJSON;
 | 
						|
      if (oldArrayHadToJSON)
 | 
						|
        Array.prototype.toJSON = oldArrayToJSON;
 | 
						|
 | 
						|
      return hasSymbol ? undefined : result;
 | 
						|
    }).bind(null, JSON.stringify.bind(JSON))`).return;
 | 
						|
  }
 | 
						|
 | 
						|
  id() {
 | 
						|
    return this._id;
 | 
						|
  }
 | 
						|
 | 
						|
  auxData() {
 | 
						|
    return this._auxData;
 | 
						|
  }
 | 
						|
 | 
						|
  _isIsolatedWorldContext() {
 | 
						|
    return !!this._auxData.name;
 | 
						|
  }
 | 
						|
 | 
						|
  async evaluateScript(script, exceptionDetails = {}) {
 | 
						|
    const userInputHelper = this._domWindow ? this._domWindow.windowUtils.setHandlingUserInput(true) : null;
 | 
						|
    if (this._domWindow && this._domWindow.document)
 | 
						|
      this._domWindow.document.notifyUserGestureActivation();
 | 
						|
 | 
						|
    let {success, obj} = this._getResult(this._debuggee.executeInGlobal(script), exceptionDetails);
 | 
						|
    userInputHelper && userInputHelper.destruct();
 | 
						|
    if (!success)
 | 
						|
      return null;
 | 
						|
    if (obj && obj.isPromise) {
 | 
						|
      const awaitResult = await this._runtime._awaitPromise(this, obj, exceptionDetails);
 | 
						|
      if (!awaitResult.success)
 | 
						|
        return null;
 | 
						|
      obj = awaitResult.obj;
 | 
						|
    }
 | 
						|
    return this._createRemoteObject(obj);
 | 
						|
  }
 | 
						|
 | 
						|
  evaluateScriptSafely(script) {
 | 
						|
    try {
 | 
						|
      this._debuggee.executeInGlobal(script);
 | 
						|
    } catch (e) {
 | 
						|
      dump(`ERROR: ${e.message}\n${e.stack}\n`);
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  async evaluateFunction(functionText, args, exceptionDetails = {}) {
 | 
						|
    const funEvaluation = this._getResult(this._debuggee.executeInGlobal('(' + functionText + ')'), exceptionDetails);
 | 
						|
    if (!funEvaluation.success)
 | 
						|
      return null;
 | 
						|
    if (!funEvaluation.obj.callable)
 | 
						|
      throw new Error('functionText does not evaluate to a function!');
 | 
						|
    args = args.map(arg => {
 | 
						|
      if (arg.objectId) {
 | 
						|
        if (!this._remoteObjects.has(arg.objectId))
 | 
						|
          throw new Error('Cannot find object with id = ' + arg.objectId);
 | 
						|
        return this._remoteObjects.get(arg.objectId);
 | 
						|
      }
 | 
						|
      switch (arg.unserializableValue) {
 | 
						|
        case 'Infinity': return Infinity;
 | 
						|
        case '-Infinity': return -Infinity;
 | 
						|
        case '-0': return -0;
 | 
						|
        case 'NaN': return NaN;
 | 
						|
        default: return this._toDebugger(arg.value);
 | 
						|
      }
 | 
						|
    });
 | 
						|
    const userInputHelper = this._domWindow ? this._domWindow.windowUtils.setHandlingUserInput(true) : null;
 | 
						|
    if (this._domWindow && this._domWindow.document)
 | 
						|
      this._domWindow.document.notifyUserGestureActivation();
 | 
						|
    let {success, obj} = this._getResult(funEvaluation.obj.apply(null, args), exceptionDetails);
 | 
						|
    userInputHelper && userInputHelper.destruct();
 | 
						|
    if (!success)
 | 
						|
      return null;
 | 
						|
    if (obj && obj.isPromise) {
 | 
						|
      const awaitResult = await this._runtime._awaitPromise(this, obj, exceptionDetails);
 | 
						|
      if (!awaitResult.success)
 | 
						|
        return null;
 | 
						|
      obj = awaitResult.obj;
 | 
						|
    }
 | 
						|
    return this._createRemoteObject(obj);
 | 
						|
  }
 | 
						|
 | 
						|
  addBinding(name, script) {
 | 
						|
    Cu.exportFunction((...args) => {
 | 
						|
      emitEvent(this._runtime.events.onBindingCalled, {
 | 
						|
        executionContextId: this._id,
 | 
						|
        name,
 | 
						|
        payload: args[0],
 | 
						|
      });
 | 
						|
    }, this._contextGlobal, {
 | 
						|
      defineAs: name,
 | 
						|
    });
 | 
						|
    this.evaluateScriptSafely(script);
 | 
						|
  }
 | 
						|
 | 
						|
  unsafeObject(objectId) {
 | 
						|
    if (!this._remoteObjects.has(objectId))
 | 
						|
      return;
 | 
						|
    return { object: this._remoteObjects.get(objectId).unsafeDereference() };
 | 
						|
  }
 | 
						|
 | 
						|
  rawValueToRemoteObject(rawValue) {
 | 
						|
    const debuggerObj = this._debuggee.makeDebuggeeValue(rawValue);
 | 
						|
    return this._createRemoteObject(debuggerObj);
 | 
						|
  }
 | 
						|
 | 
						|
  _instanceOf(debuggerObj, rawObj, className) {
 | 
						|
    if (this._domWindow)
 | 
						|
      return rawObj instanceof this._domWindow[className];
 | 
						|
    return this._debuggee.executeInGlobalWithBindings('o instanceof this[className]', {o: debuggerObj, className: this._debuggee.makeDebuggeeValue(className)}).return;
 | 
						|
  }
 | 
						|
 | 
						|
  _createRemoteObject(debuggerObj) {
 | 
						|
    if (debuggerObj instanceof Debugger.Object) {
 | 
						|
      const objectId = generateId();
 | 
						|
      this._remoteObjects.set(objectId, debuggerObj);
 | 
						|
      const rawObj = debuggerObj.unsafeDereference();
 | 
						|
      const type = typeof rawObj;
 | 
						|
      let subtype = undefined;
 | 
						|
      if (debuggerObj.isProxy)
 | 
						|
        subtype = 'proxy';
 | 
						|
      else if (Array.isArray(rawObj))
 | 
						|
        subtype = 'array';
 | 
						|
      else if (Object.is(rawObj, null))
 | 
						|
        subtype = 'null';
 | 
						|
      else if (this._instanceOf(debuggerObj, rawObj, 'Node'))
 | 
						|
        subtype = 'node';
 | 
						|
      else if (this._instanceOf(debuggerObj, rawObj, 'RegExp'))
 | 
						|
        subtype = 'regexp';
 | 
						|
      else if (this._instanceOf(debuggerObj, rawObj, 'Date'))
 | 
						|
        subtype = 'date';
 | 
						|
      else if (this._instanceOf(debuggerObj, rawObj, 'Map'))
 | 
						|
        subtype = 'map';
 | 
						|
      else if (this._instanceOf(debuggerObj, rawObj, 'Set'))
 | 
						|
        subtype = 'set';
 | 
						|
      else if (this._instanceOf(debuggerObj, rawObj, 'WeakMap'))
 | 
						|
        subtype = 'weakmap';
 | 
						|
      else if (this._instanceOf(debuggerObj, rawObj, 'WeakSet'))
 | 
						|
        subtype = 'weakset';
 | 
						|
      else if (this._instanceOf(debuggerObj, rawObj, 'Error'))
 | 
						|
        subtype = 'error';
 | 
						|
      else if (this._instanceOf(debuggerObj, rawObj, 'Promise'))
 | 
						|
        subtype = 'promise';
 | 
						|
      else if ((this._instanceOf(debuggerObj, rawObj, 'Int8Array')) || (this._instanceOf(debuggerObj, rawObj, 'Uint8Array')) ||
 | 
						|
               (this._instanceOf(debuggerObj, rawObj, 'Uint8ClampedArray')) || (this._instanceOf(debuggerObj, rawObj, 'Int16Array')) ||
 | 
						|
               (this._instanceOf(debuggerObj, rawObj, 'Uint16Array')) || (this._instanceOf(debuggerObj, rawObj, 'Int32Array')) ||
 | 
						|
               (this._instanceOf(debuggerObj, rawObj, 'Uint32Array')) || (this._instanceOf(debuggerObj, rawObj, 'Float32Array')) ||
 | 
						|
               (this._instanceOf(debuggerObj, rawObj, 'Float64Array'))) {
 | 
						|
        subtype = 'typedarray';
 | 
						|
      }
 | 
						|
      return {objectId, type, subtype};
 | 
						|
    }
 | 
						|
    if (typeof debuggerObj === 'symbol') {
 | 
						|
      const objectId = generateId();
 | 
						|
      this._remoteObjects.set(objectId, debuggerObj);
 | 
						|
      return {objectId, type: 'symbol'};
 | 
						|
    }
 | 
						|
 | 
						|
    let unserializableValue = undefined;
 | 
						|
    if (Object.is(debuggerObj, NaN))
 | 
						|
      unserializableValue = 'NaN';
 | 
						|
    else if (Object.is(debuggerObj, -0))
 | 
						|
      unserializableValue = '-0';
 | 
						|
    else if (Object.is(debuggerObj, Infinity))
 | 
						|
      unserializableValue = 'Infinity';
 | 
						|
    else if (Object.is(debuggerObj, -Infinity))
 | 
						|
      unserializableValue = '-Infinity';
 | 
						|
    return unserializableValue ? {unserializableValue} : {value: debuggerObj};
 | 
						|
  }
 | 
						|
 | 
						|
  ensureSerializedToValue(protocolObject) {
 | 
						|
    if (!protocolObject.objectId)
 | 
						|
      return protocolObject;
 | 
						|
    const obj = this._remoteObjects.get(protocolObject.objectId);
 | 
						|
    this._remoteObjects.delete(protocolObject.objectId);
 | 
						|
    return {value: this._serialize(obj)};
 | 
						|
  }
 | 
						|
 | 
						|
  _toDebugger(obj) {
 | 
						|
    if (typeof obj !== 'object')
 | 
						|
      return obj;
 | 
						|
    if (obj === null)
 | 
						|
      return obj;
 | 
						|
    const properties = {};
 | 
						|
    for (let [key, value] of Object.entries(obj)) {
 | 
						|
      properties[key] = {
 | 
						|
        configurable: true,
 | 
						|
        writable: true,
 | 
						|
        enumerable: true,
 | 
						|
        value: this._toDebugger(value),
 | 
						|
      };
 | 
						|
    }
 | 
						|
    const baseObject = Array.isArray(obj) ? '([])' : '({})';
 | 
						|
    const debuggerObj = this._debuggee.executeInGlobal(baseObject).return;
 | 
						|
    debuggerObj.defineProperties(properties);
 | 
						|
    return debuggerObj;
 | 
						|
  }
 | 
						|
 | 
						|
  _serialize(obj) {
 | 
						|
    const result = this._debuggee.executeInGlobalWithBindings('stringify(e)', {e: obj, stringify: this._jsonStringifyObject});
 | 
						|
    if (result.throw)
 | 
						|
      throw new Error('Object is not serializable');
 | 
						|
    return result.return === undefined ? undefined : JSON.parse(result.return);
 | 
						|
  }
 | 
						|
 | 
						|
  disposeObject(objectId) {
 | 
						|
    this._remoteObjects.delete(objectId);
 | 
						|
  }
 | 
						|
 | 
						|
  getObjectProperties(objectId) {
 | 
						|
    if (!this._remoteObjects.has(objectId))
 | 
						|
      throw new Error('Cannot find object with id = ' + arg.objectId);
 | 
						|
    const result = [];
 | 
						|
    for (let obj = this._remoteObjects.get(objectId); obj; obj = obj.proto) {
 | 
						|
      for (const propertyName of obj.getOwnPropertyNames()) {
 | 
						|
        const descriptor = obj.getOwnPropertyDescriptor(propertyName);
 | 
						|
        if (!descriptor.enumerable)
 | 
						|
          continue;
 | 
						|
        result.push({
 | 
						|
          name: propertyName,
 | 
						|
          value: this._createRemoteObject(descriptor.value),
 | 
						|
        });
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  _getResult(completionValue, exceptionDetails = {}) {
 | 
						|
    if (!completionValue) {
 | 
						|
      exceptionDetails.text = 'Evaluation terminated!';
 | 
						|
      exceptionDetails.stack = '';
 | 
						|
      return {success: false, obj: null};
 | 
						|
    }
 | 
						|
    if (completionValue.throw) {
 | 
						|
      if (this._debuggee.executeInGlobalWithBindings('e instanceof Error', {e: completionValue.throw}).return) {
 | 
						|
        exceptionDetails.text = this._debuggee.executeInGlobalWithBindings('e.message', {e: completionValue.throw}).return;
 | 
						|
        exceptionDetails.stack = this._debuggee.executeInGlobalWithBindings('e.stack', {e: completionValue.throw}).return;
 | 
						|
      } else {
 | 
						|
        exceptionDetails.value = this._serialize(completionValue.throw);
 | 
						|
      }
 | 
						|
      return {success: false, obj: null};
 | 
						|
    }
 | 
						|
    return {success: true, obj: completionValue.return};
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
const listenersSymbol = Symbol('listeners');
 | 
						|
 | 
						|
function createEvent() {
 | 
						|
  const listeners = new Set();
 | 
						|
  const subscribeFunction = listener => {
 | 
						|
    listeners.add(listener);
 | 
						|
    return () => listeners.delete(listener);
 | 
						|
  }
 | 
						|
  subscribeFunction[listenersSymbol] = listeners;
 | 
						|
  return subscribeFunction;
 | 
						|
}
 | 
						|
 | 
						|
function emitEvent(event, ...args) {
 | 
						|
  let listeners = event[listenersSymbol];
 | 
						|
  if (!listeners || !listeners.size)
 | 
						|
    return;
 | 
						|
  listeners = new Set(listeners);
 | 
						|
  for (const listener of listeners)
 | 
						|
    listener.call(null, ...args);
 | 
						|
}
 | 
						|
 | 
						|
var EXPORTED_SYMBOLS = ['Runtime'];
 | 
						|
this.Runtime = Runtime;
 |