Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/remote/cdp/test/browser/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 17 kB image not shown  

Quelle  head.js   Sprache: JAVA

 
/* Any copyright is dedicated to the Public Domain.
 * http://creativecommons.org/publicdomain/zero/1.0/ */


"use strict";

// window.RemoteAgent is a simple object set in browser.js, and importing
// RemoteAgent conflicts with that.
// eslint-disable-next-line mozilla/no-redeclare-with-import-autofix
const { RemoteAgent } = ChromeUtils.importESModule(
  "chrome://remote/content/components/RemoteAgent.sys.mjs"
);
const { RemoteAgentError } = ChromeUtils.importESModule(
  "chrome://remote/content/cdp/Error.sys.mjs"
);
const { TabManager } = ChromeUtils.importESModule(
  "chrome://remote/content/shared/TabManager.sys.mjs"
);
const { Stream } = ChromeUtils.importESModule(
  "chrome://remote/content/cdp/StreamRegistry.sys.mjs"
);

const { getTimeoutMultiplier } = ChromeUtils.importESModule(
  "chrome://remote/content/shared/AppInfo.sys.mjs"
);

const TIMEOUT_MULTIPLIER = getTimeoutMultiplier();
const TIMEOUT_EVENTS = 1000 * TIMEOUT_MULTIPLIER;

/*
add_task() is overriden to setup and teardown a test environment
making it easier to  write browser-chrome tests for the remote
debugger.

Before the task is run, the nsIRemoteAgent listener is started and
a CDP client is connected to it.  A new tab is also added.  These
three things are exposed to the provided task like this:

add_task(async function testName(client, CDP, tab) {
  // client is an instance of the CDP class
  // CDP is ./chrome-remote-interface.js
  // tab is a fresh tab, destroyed after the test
});

Also target discovery is getting enabled, which means that targetCreated,
targetDestroyed, and targetInfoChanged events will be received by the client.

add_plain_task() may be used to write test tasks without the implicit
setup and teardown described above.
*/


const add_plain_task = add_task.bind(this);

this.add_task = function (taskFn, opts = {}) {
  const {
    createTab = true// By default run each test in its own tab
  } = opts;

  const fn = async function () {
    let client, tab, target;

    try {
      const CDP = await getCDP();

      if (createTab) {
        tab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
        const tabId = TabManager.getIdForBrowser(tab.linkedBrowser);

        const targets = await CDP.List();
        target = targets.find(target => target.id === tabId);
      }

      client = await CDP({ target });
      info("CDP client instantiated");

      // Bug 1605722 - Workaround to not hang when waiting for Target events
      await getDiscoveredTargets(client.Target);

      await taskFn({ client, CDP, tab });

      if (createTab) {
        // taskFn may resolve within a tick after opening a new tab.
        // We shouldn't remove the newly opened tab in the same tick.
        // Wait for the next tick here.
        await TestUtils.waitForTick();
        BrowserTestUtils.removeTab(tab);
      }
    } catch (e) {
      // Display better error message with the server side stacktrace
      // if an error happened on the server side:
      if (e.response) {
        throw RemoteAgentError.fromJSON(e.response);
      } else {
        throw e;
      }
    } finally {
      if (client) {
        await client.close();
        info("CDP client closed");
      }

      // Close any additional tabs, so that only a single tab remains open
      while (gBrowser.tabs.length > 1) {
        gBrowser.removeCurrentTab();
      }
    }
  };

  Object.defineProperty(fn, "name", { value: taskFn.name, writable: false });
  add_plain_task(fn);
};

/**
 * Create a test document in an invisible window.
 * This window will be automatically closed on test teardown.
 */

function createTestDocument() {
  const browser = Services.appShell.createWindowlessBrowser(true);
  registerCleanupFunction(() => browser.close());

  // Create a system principal content viewer to ensure there is a valid
  // empty document using system principal and avoid any wrapper issues
  // when using document's JS Objects.
  const webNavigation = browser.docShell.QueryInterface(Ci.nsIWebNavigation);
  const system = Services.scriptSecurityManager.getSystemPrincipal();
  webNavigation.createAboutBlankDocumentViewer(system, system);

  return webNavigation.document;
}

/**
 * Retrieve an intance of CDP object from chrome-remote-interface library
 */

async function getCDP() {
  // Instantiate a background test document in order to load the library
  // as in a web page
  const document = createTestDocument();

  const window = document.defaultView.wrappedJSObject;
  Services.scriptloader.loadSubScript(
    "chrome://mochitests/content/browser/remote/cdp/test/browser/chrome-remote-interface.js",
    window
  );

  // Implements `criRequest` to be called by chrome-remote-interface
  // library in order to do the cross-domain http request, which,
  // in a regular Web page, is impossible.
  window.criRequest = (options, callback) => {
    const { path } = options;
    const url = `http://${RemoteAgent.host}:${RemoteAgent.port}${path}`;
    const xhr = new XMLHttpRequest();
    xhr.open("GET", url, true);

    // Prevent "XML Parsing Error: syntax error" error messages
    xhr.overrideMimeType("text/plain");

    xhr.send(null);
    xhr.onload = () => callback(null, xhr.responseText);
    xhr.onerror = e => callback(e, null);
  };

  return window.CDP;
}

async function getScrollbarSize() {
  return SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => {
    const scrollbarHeight = {};
    const scrollbarWidth = {};

    content.windowUtils.getScrollbarSize(
      false,
      scrollbarWidth,
      scrollbarHeight
    );
    return {
      width: scrollbarWidth.value,
      height: scrollbarHeight.value,
    };
  });
}

function getTargets(CDP) {
  return new Promise((resolve, reject) => {
    CDP.List(null, (err, targets) => {
      if (err) {
        reject(err);
        return;
      }
      resolve(targets);
    });
  });
}

// Wait for all Target.targetCreated events. One for each tab.
async function getDiscoveredTargets(Target, options = {}) {
  const { discover = true, filter } = options;

  const targets = [];
  const unsubscribe = Target.targetCreated(target => {
    targets.push(target.targetInfo);
  });

  await Target.setDiscoverTargets({
    discover,
    filter,
  }).finally(() => unsubscribe());

  return targets;
}

async function openTab(Target, options = {}) {
  const { activate = false } = options;

  info("Create a new tab and wait for the target to be created");
  const targetCreated = Target.targetCreated();
  const newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser);
  const { targetInfo } = await targetCreated;

  is(targetInfo.type, "page");

  if (activate) {
    await Target.activateTarget({
      targetId: targetInfo.targetId,
    });
    info(`New tab with target id ${targetInfo.targetId} created and activated`);
  } else {
    info(`New tab with target id ${targetInfo.targetId} created`);
  }

  return { targetInfo, newTab };
}

async function openWindow(Target, options = {}) {
  const { activate = false } = options;

  info("Create a new window and wait for the target to be created");
  const targetCreated = Target.targetCreated();
  const newWindow = await BrowserTestUtils.openNewBrowserWindow();
  const newTab = newWindow.gBrowser.selectedTab;
  const { targetInfo } = await targetCreated;
  is(targetInfo.type, "page");

  if (activate) {
    await Target.activateTarget({
      targetId: targetInfo.targetId,
    });
    info(
      `New window with target id ${targetInfo.targetId} created and activated`
    );
  } else {
    info(`New window with target id ${targetInfo.targetId} created`);
  }

  return { targetInfo, newWindow, newTab };
}

/** Creates a data URL for the given source document. */
function toDataURL(src, doctype = "html") {
  let doc, mime;
  switch (doctype) {
    case "html":
      mime = "text/html;charset=utf-8";
      doc = `<!doctype html>\n<meta charset=utf-8>\n${src}`;
      break;
    default:
      throw new Error("Unexpected doctype: " + doctype);
  }

  return `data:${mime},${encodeURIComponent(doc)}`;
}

function convertArgument(arg) {
  if (typeof arg === "bigint") {
    return { unserializableValue: `${arg.toString()}n` };
  }
  if (Object.is(arg, -0)) {
    return { unserializableValue: "-0" };
  }
  if (Object.is(arg, Infinity)) {
    return { unserializableValue: "Infinity" };
  }
  if (Object.is(arg, -Infinity)) {
    return { unserializableValue: "-Infinity" };
  }
  if (Object.is(arg, NaN)) {
    return { unserializableValue: "NaN" };
  }

  return { value: arg };
}

async function evaluate(client, contextId, pageFunction, ...args) {
  const { Runtime } = client;

  if (typeof pageFunction === "string") {
    return Runtime.evaluate({
      expression: pageFunction,
      contextId,
      returnByValue: true,
      awaitPromise: true,
    });
  } else if (typeof pageFunction === "function") {
    return Runtime.callFunctionOn({
      functionDeclaration: pageFunction.toString(),
      executionContextId: contextId,
      arguments: args.map(convertArgument),
      returnByValue: true,
      awaitPromise: true,
    });
  }
  throw new Error("pageFunction: expected 'string' or 'function'");
}

/**
 * Load a given URL in the currently selected tab
 */

async function loadURL(url, expectedURL = undefined) {
  expectedURL = expectedURL || url;

  const browser = gBrowser.selectedTab.linkedBrowser;
  const loaded = BrowserTestUtils.browserLoaded(browser, true, expectedURL);

  BrowserTestUtils.startLoadingURIString(browser, url);
  await loaded;
}

/**
 * Enable the Runtime domain
 */

async function enableRuntime(client) {
  const { Runtime } = client;

  // Enable watching for new execution context
  await Runtime.enable();
  info("Runtime domain has been enabled");

  // Calling Runtime.enable will emit executionContextCreated for the existing contexts
  const { context } = await Runtime.executionContextCreated();
  ok(!!context.id, "The execution context has an id");
  ok(context.auxData.isDefault, "The execution context is the default one");
  ok(!!context.auxData.frameId, "The execution context has a frame id set");

  return context;
}

/**
 * Retrieve the value of a property on the content window.
 */

function getContentProperty(prop) {
  info(`Retrieve ${prop} on the content window`);
  return SpecialPowers.spawn(
    gBrowser.selectedBrowser,
    [prop],
    _prop => content[_prop]
  );
}

/**
 * Retrieve all frames for the current tab as flattened list.
 *
 * @returns {Map<number, Frame>}
 *     Flattened list of frames as Map
 */

async function getFlattenedFrameTree(client) {
  const { Page } = client;

  function flatten(frames) {
    return frames.reduce((result, current) => {
      result.set(current.frame.id, current.frame);
      if (current.childFrames) {
        const frames = flatten(current.childFrames);
        result = new Map([...result, ...frames]);
      }
      return result;
    }, new Map());
  }

  const { frameTree } = await Page.getFrameTree();
  return flatten(Array(frameTree));
}

/**
 * Return a new promise, which resolves after ms have been elapsed
 */

function timeoutPromise(ms) {
  return new Promise(resolve => {
    window.setTimeout(resolve, ms);
  });
}

/** Fail a test. */
function fail(message) {
  ok(false, message);
}

/**
 * Create a stream with the specified contents.
 *
 * @param {string} contents
 *     Contents of the file.
 * @param {object} options
 * @param {string=} options.path
 *     Path of the file. Defaults to the temporary directory.
 * @param {boolean=} options.remove
 *     If true, automatically remove the file after the test. Defaults to true.
 *
 * @returns {Promise<Stream>}
 */

async function createFileStream(contents, options = {}) {
  let { path = null, remove = true } = options;

  if (!path) {
    path = await IOUtils.createUniqueFile(
      PathUtils.tempDir,
      "remote-agent.txt"
    );
  }

  await IOUtils.writeUTF8(path, contents);

  const stream = new Stream(path);
  if (remove) {
    registerCleanupFunction(() => stream.destroy());
  }

  return stream;
}

async function throwScriptError(options = {}) {
  const { inContent = true } = options;

  const addScriptErrorInternal = options => {
    const {
      flag = Ci.nsIScriptError.errorFlag,
      innerWindowId = content.windowGlobalChild.innerWindowId,
    } = options;

    const scriptError = Cc["@mozilla.org/scripterror;1"].createInstance(
      Ci.nsIScriptError
    );
    scriptError.initWithWindowID(
      options.text,
      options.sourceName || "sourceName",
      options.lineNumber || 0,
      options.columnNumber || 0,
      flag,
      options.category || "javascript",
      innerWindowId
    );
    Services.console.logMessage(scriptError);
  };

  if (inContent) {
    SpecialPowers.spawn(
      gBrowser.selectedBrowser,
      [options],
      addScriptErrorInternal
    );
  } else {
    options.innerWindowId = window.windowGlobalChild.innerWindowId;
    addScriptErrorInternal(options);
  }
}

class RecordEvents {
  /**
   * A timeline of events chosen by calls to `addRecorder`.
   * Call `configure`` for each client event you want to record.
   * Then `await record(someTimeout)` to record a timeline that you
   * can make assertions about.
   *
   * ```js
   * const history = new RecordEvents(expectedNumberOfEvents);
   *
   * history.addRecorder({
   *   event: Runtime.executionContextDestroyed,
   *   eventName: "Runtime.executionContextDestroyed",
   *   messageFn: payload => {
   *     return `Received Runtime.executionContextDestroyed for id ${payload.executionContextId}`;
   *   },
   * });
   * ```
   *
   * @param {number} total
   *     Number of expected events. Stop recording when this number is exceeded.
   */

  constructor(total) {
    this.events = [];
    this.promises = new Set();
    this.subscriptions = new Set();
    this.total = total;
  }

  /**
   * Configure an event to be recorded and logged.
   * The recording stops once we accumulate more than the expected
   * total of all configured events.
   *
   * @param {object} options
   * @param {CDPEvent} options.event
   *     https://github.com/cyrus-and/chrome-remote-interface#clientdomaineventcallback
   * @param {string} options.eventName
   *     Name to use for reporting.
   * @param {Function=} options.callback
   *     ({ eventName, payload }) => {} to be called when each event is received
   * @param {function(payload):string=} options.messageFn
   */

  addRecorder(options = {}) {
    const {
      event,
      eventName,
      messageFn = () => `Recorded ${eventName}`,
      callback,
    } = options;

    const promise = new Promise(resolve => {
      const unsubscribe = event(payload => {
        info(messageFn(payload));
        this.events.push({ eventName, payload, index: this.events.length });
        callback?.({ eventName, payload, index: this.events.length - 1 });
        if (this.events.length > this.total) {
          this.subscriptions.delete(unsubscribe);
          unsubscribe();
          resolve(this.events);
        }
      });
      this.subscriptions.add(unsubscribe);
    });

    this.promises.add(promise);
  }

  /**
   * Register a promise to await while recording the timeline. The returned
   * callback resolves the registered promise and adds `step`
   * to the timeline, along with an associated payload, if provided.
   *
   * @param {string} step
   * @returns {Function} callback
   */

  addPromise(step) {
    let callback;
    const promise = new Promise(resolve => {
      callback = value => {
        resolve();
        info(`Recorded ${step}`);
        this.events.push({
          eventName: step,
          payload: value,
          index: this.events.length,
        });
        return value;
      };
    });

    this.promises.add(promise);
    return callback;
  }

  /**
   * Record events until we hit the timeout or the expected total is exceeded.
   *
   * @param {number=} timeout
   *     Timeout in milliseconds. Defaults to 1000.
   *
   * @returns {Array<{ eventName, payload, index }>} Recorded events
   */

  async record(timeout = TIMEOUT_EVENTS) {
    await Promise.race([Promise.all(this.promises), timeoutPromise(timeout)]);
    for (const unsubscribe of this.subscriptions) {
      unsubscribe();
    }
    return this.events;
  }

  /**
   * Filter events based on predicate
   *
   * @param {Function} predicate
   *
   * @returns {Array<{ eventName, payload, index }>}
   *     The list of events matching the filter.
   */

  filter(predicate) {
    return this.events.filter(predicate);
  }

  /**
   * Find first occurrence of the given event.
   *
   * @param {string} eventName
   *
   * @returns {{ eventName, payload, index }} The event, if any.
   */

  findEvent(eventName) {
    const event = this.events.find(el => el.eventName == eventName);
    if (event) {
      return event;
    }
    return {};
  }

  /**
   * Find given events.
   *
   * @param {string} eventName
   *
   * @returns {Array<{ eventName, payload, index }>}
   *     The events, if any.
   */

  findEvents(eventName) {
    return this.events.filter(event => event.eventName == eventName);
  }

  /**
   * Find index of first occurrence of the given event.
   *
   * @param {string} eventName
   *
   * @returns {number} The event index, -1 if not found.
   */

  indexOf(eventName) {
    const event = this.events.find(el => el.eventName == eventName);
    if (event) {
      return event.index;
    }
    return -1;
  }
}

Messung V0.5
C=91 H=98 G=94

¤ Dauer der Verarbeitung: 0.30 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.