Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/devtools/client/performance-new/test/browser/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 26 kB image not shown  

Quelle  helpers.js   Sprache: JAVA

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

/**
 * Wait for a single requestAnimationFrame tick.
 */

function tick() {
  return new Promise(resolve => requestAnimationFrame(resolve));
}

/**
 * It can be confusing when waiting for something asynchronously. This function
 * logs out a message periodically (every 1 second) in order to create helpful
 * log messages.
 * @param {string} message
 * @returns {Function}
 */

function createPeriodicLogger() {
  let startTime = Date.now();
  let lastCount = 0;
  let lastMessage = null;

  return message => {
    if (lastMessage === message) {
      // The messages are the same, check if we should log them.
      const now = Date.now();
      const count = Math.floor((now - startTime) / 1000);
      if (count !== lastCount) {
        info(
          `${message} (After ${count} ${count === 1 ? "second" : "seconds"})`
        );
        lastCount = count;
      }
    } else {
      // The messages are different, log them now, and reset the waiting time.
      info(message);
      startTime = Date.now();
      lastCount = 0;
      lastMessage = message;
    }
  };
}

/**
 * Wait until a condition is fullfilled.
 * @param {Function} condition
 * @param {string?} logMessage
 * @return The truthy result of the condition.
 */

async function waitUntil(condition, message) {
  const logPeriodically = createPeriodicLogger();

  // Loop through the condition.
  while (true) {
    if (message) {
      logPeriodically(message);
    }
    const result = condition();
    if (result) {
      return result;
    }

    await tick();
  }
}

/**
 * This function looks inside of a container for some element that has a label.
 * It runs in a loop every requestAnimationFrame until it finds the element. If
 * it doesn't find the element it throws an error.
 *
 * @param {Element} container
 * @param {string} label
 * @returns {Promise<HTMLElement>}
 */

function getElementByLabel(container, label) {
  return waitUntil(
    () => container.querySelector(`[label="${label}"]`),
    `Trying to find the button with the label "${label}".`
  );
}
/* exported getElementByLabel */

/**
 * This function looks inside of a container for some element that has a tooltip.
 * It runs in a loop every requestAnimationFrame until it finds the element. If
 * it doesn't find the element it throws an error.
 *
 * @param {Element} container
 * @param {string} tooltip
 * @returns {Promise<HTMLElement>}
 */

function getElementByTooltip(container, tooltip) {
  return waitUntil(
    () => container.querySelector(`[tooltiptext="${tooltip}"]`),
    `Trying to find the button with the tooltip "${tooltip}".`
  );
}
/* exported getElementByTooltip */

/**
 * This function will select a node from the XPath.
 * @returns {HTMLElement?}
 */

function getElementByXPath(document, path) {
  return document.evaluate(
    path,
    document,
    null,
    XPathResult.FIRST_ORDERED_NODE_TYPE,
    null
  ).singleNodeValue;
}
/* exported getElementByXPath */

/**
 * This function looks inside of a document for some element that contains
 * the given text. It runs in a loop every requestAnimationFrame until it
 * finds the element. If it doesn't find the element it throws an error.
 *
 * @param {HTMLDocument} document
 * @param {string} text
 * @returns {Promise<HTMLElement>}
 */

async function getElementFromDocumentByText(document, text) {
  // Fallback on aria-label if there are no results for the text xpath.
  const xpath = `//*[contains(text(), '${text}')] | //*[contains(@aria-label, '${text}')]`;
  return waitUntil(
    () => getElementByXPath(document, xpath),
    `Trying to find the element with the text "${text}".`
  );
}
/* exported getElementFromDocumentByText */

/**
 * This function is similar to getElementFromDocumentByText, but it immediately
 * returns and does not wait for an element to exist.
 * @param {HTMLDocument} document
 * @param {string} text
 * @returns {HTMLElement?}
 */

function maybeGetElementFromDocumentByText(document, text) {
  info(`Immediately trying to find the element with the text "${text}".`);
  const xpath = `//*[contains(text(), '${text}')]`;
  return getElementByXPath(document, xpath);
}
/* exported maybeGetElementFromDocumentByText */

/**
 * Make sure the profiler popup is enabled.
 */

async function makeSureProfilerPopupIsEnabled() {
  info("Make sure the profiler popup is enabled.");

  info("> Load the profiler menu button.");
  const { ProfilerMenuButton } = ChromeUtils.importESModule(
    "resource://devtools/client/performance-new/popup/menu-button.sys.mjs"
  );

  if (!ProfilerMenuButton.isInNavbar()) {
    // Make sure the feature flag is enabled.
    SpecialPowers.pushPrefEnv({
      set: [["devtools.performance.popup.feature-flag"true]],
    });

    info("> The menu button is not in the nav bar, add it.");
    ProfilerMenuButton.addToNavbar(document);

    await waitUntil(
      () => gBrowser.ownerDocument.getElementById("profiler-button"),
      "> Waiting until the profiler button is added to the browser."
    );

    await SimpleTest.promiseFocus(gBrowser.ownerGlobal);

    registerCleanupFunction(() => {
      info(
        "Clean up after the test by disabling the profiler popup menu button."
      );
      if (!ProfilerMenuButton.isInNavbar()) {
        throw new Error(
          "Expected the profiler popup to still be in the navbar during the test cleanup."
        );
      }
      ProfilerMenuButton.remove();
    });
  } else {
    info("> The menu button was already enabled.");
  }
}
/* exported makeSureProfilerPopupIsEnabled */

/**
 * XUL popups will fire the popupshown and popuphidden events. These will fire for
 * any type of popup in the browser. This function waits for one of those events, and
 * checks that the viewId of the popup is PanelUI-profiler
 *
 * @param {Window} window
 * @param {"popupshown" | "popuphidden"} eventName
 * @returns {Promise<void>}
 */

function waitForProfilerPopupEvent(window, eventName) {
  return new Promise(resolve => {
    function handleEvent(event) {
      if (event.target.getAttribute("viewId") === "PanelUI-profiler") {
        window.removeEventListener(eventName, handleEvent);
        resolve();
      }
    }
    window.addEventListener(eventName, handleEvent);
  });
}
/* exported waitForProfilerPopupEvent */

/**
 * Do not use this directly in a test. Prefer withPopupOpen and openPopupAndEnsureCloses.
 *
 * This function toggles the profiler menu button, and then uses user gestures
 * to click it open. It waits a tick to make sure it has a chance to initialize.
 * @param {Window} window
 * @return {Promise<void>}
 */

async function _toggleOpenProfilerPopup(window) {
  info("Toggle open the profiler popup.");

  info("> Find the profiler menu button.");
  const profilerDropmarker = window.document.getElementById(
    "profiler-button-dropmarker"
  );
  if (!profilerDropmarker) {
    throw new Error(
      "Could not find the profiler button dropmarker in the toolbar."
    );
  }

  const popupShown = waitForProfilerPopupEvent(window, "popupshown");

  info("> Trigger a click on the profiler button dropmarker.");
  await EventUtils.synthesizeMouseAtCenter(profilerDropmarker, {}, window);

  if (profilerDropmarker.getAttribute("open") !== "true") {
    throw new Error(
      "This test assumes that the button will have an open=true attribute after clicking it."
    );
  }

  info("> Wait for the popup to be shown.");
  await popupShown;
  // Also wait a tick in case someone else is subscribing to the "popupshown" event
  // and is doing synchronous work with it.
  await tick();
}

/**
 * Do not use this directly in a test. Prefer withPopupOpen.
 *
 * This function uses a keyboard shortcut to close the profiler popup.
 * @param {Window} window
 * @return {Promise<void>}
 */

async function _closePopup(window) {
  const popupHiddenPromise = waitForProfilerPopupEvent(window, "popuphidden");
  info("> Trigger an escape key to hide the popup");
  EventUtils.synthesizeKey("KEY_Escape");

  info("> Wait for the popup to be hidden.");
  await popupHiddenPromise;
  // Also wait a tick in case someone else is subscribing to the "popuphidden" event
  // and is doing synchronous work with it.
  await tick();
}

/**
 * Perform some action on the popup, and close it afterwards.
 * @param {Window} window
 * @param {() => Promise<void>} callback
 */

async function withPopupOpen(window, callback) {
  await _toggleOpenProfilerPopup(window);
  await callback();
  await _closePopup(window);
}
/* exported withPopupOpen */

/**
 * This function opens the profiler popup, but also ensures that something else closes
 * it before the end of the test. This is useful for tests that trigger the profiler
 * popup to close through an implicit action, like opening a tab.
 *
 * @param {Window} window
 * @param {() => Promise<void>} callback
 */

async function openPopupAndEnsureCloses(window, callback) {
  await _toggleOpenProfilerPopup(window);
  // We want to ensure the popup gets closed by the test, during the callback.
  const popupHiddenPromise = waitForProfilerPopupEvent(window, "popuphidden");
  await callback();
  info("> Verifying that the popup was closed by the test.");
  await popupHiddenPromise;
}
/* exported openPopupAndEnsureCloses */

/**
 * This function overwrites the default profiler.firefox.com URL for tests. This
 * ensures that the tests do not attempt to access external URLs.
 * The origin needs to be on the allowlist in validateProfilerWebChannelUrl,
 * otherwise the WebChannel won't work. ("https://example.com" is on that list.)
 *
 * @param {string} origin - For example: https://example.com
 * @param {string} pathname - For example: /my/testing/frontend.html
 * @returns {Promise}
 */

function setProfilerFrontendUrl(origin, pathname) {
  return SpecialPowers.pushPrefEnv({
    set: [
      // Make sure observer and testing function run in the same process
      ["devtools.performance.recording.ui-base-url", origin],
      ["devtools.performance.recording.ui-base-url-path", pathname],
    ],
  });
}
/* exported setProfilerFrontendUrl */

/**
 * This function checks the document title of a tab to see what the state is.
 * This creates a simple messaging mechanism between the content page and the
 * test harness. This function runs in a loop every requestAnimationFrame, and
 * checks for a sucess title. In addition, an "initialTitle" and "errorTitle"
 * can be specified for nicer test output.
 * @param {object}
 *   {
 *     initialTitle: string,
 *     successTitle: string,
 *     errorTitle: string
 *   }
 */

async function checkTabLoadedProfile({
  initialTitle,
  successTitle,
  errorTitle,
}) {
  const logPeriodically = createPeriodicLogger();

  info("Attempting to see if the selected tab can receive a profile.");

  return waitUntil(() => {
    switch (gBrowser.selectedTab.label) {
      case initialTitle:
        logPeriodically(`> Waiting for the profile to be received.`);
        return false;
      case successTitle:
        ok(true"The profile was successfully injected to the page");
        BrowserTestUtils.removeTab(gBrowser.selectedTab);
        return true;
      case errorTitle:
        throw new Error(
          "The fake frontend indicated that there was an error injecting the profile."
        );
      default:
        logPeriodically(`> Waiting for the fake frontend tab to be loaded.`);
        return false;
    }
  });
}
/* exported checkTabLoadedProfile */

/**
 * This function checks the url of a tab so we can assert the frontend's url
 * with our expected url. This function runs in a loop every
 * requestAnimationFrame, and checks for a initialTitle. Asserts as soon as it
 * finds that title. We don't have to look for success title or error title
 * since we only care about the url.
 * @param {{
 *     initialTitle: string,
 *     successTitle: string,
 *     errorTitle: string,
 *     expectedUrl: string
 *   }}
 */

async function waitForTabUrl({
  initialTitle,
  successTitle,
  errorTitle,
  expectedUrl,
}) {
  const logPeriodically = createPeriodicLogger();

  info(`Waiting for the selected tab to have the url "${expectedUrl}".`);

  return waitUntil(() => {
    switch (gBrowser.selectedTab.label) {
      case initialTitle:
      case successTitle:
        if (gBrowser.currentURI.spec === expectedUrl) {
          ok(true, `The selected tab has the url ${expectedUrl}`);
          BrowserTestUtils.removeTab(gBrowser.selectedTab);
          return true;
        }
        throw new Error(
          `Found a different url on the fake frontend: ${gBrowser.currentURI.spec} (expecting ${expectedUrl})`
        );
      case errorTitle:
        throw new Error(
          "The fake frontend indicated that there was an error injecting the profile."
        );
      default:
        logPeriodically(`> Waiting for the fake frontend tab to be loaded.`);
        return false;
    }
  });
}
/* exported waitForTabUrl */

/**
 * This function checks the document title of a tab as an easy way to pass
 * messages from a content page to the mochitest.
 * @param {string} title
 */

async function waitForTabTitle(title) {
  const logPeriodically = createPeriodicLogger();

  info(`Waiting for the selected tab to have the title "${title}".`);

  return waitUntil(() => {
    if (gBrowser.selectedTab.label === title) {
      ok(true, `The selected tab has the title ${title}`);
      return true;
    }
    logPeriodically(`> Waiting for the tab title to change.`);
    return false;
  });
}
/* exported waitForTabTitle */

/**
 * Open about:profiling in a new tab, and output helpful log messages.
 *
 * @template T
 * @param {(Document) => T} callback
 * @returns {Promise<T>}
 */

function withAboutProfiling(callback) {
  info("Begin to open about:profiling in a new tab.");
  return BrowserTestUtils.withNewTab(
    "about:profiling",
    async contentBrowser => {
      info("about:profiling is now open in a tab.");
      await TestUtils.waitForCondition(
        () =>
          contentBrowser.contentDocument.getElementById("root")
            .firstElementChild,
        "Document's root has been populated"
      );
      return callback(contentBrowser.contentDocument);
    }
  );
}
/* exported withAboutProfiling */

/**
 * Open DevTools and view the performance-new tab. After running the callback, clean
 * up the test.
 *
 * @param {string} [url="about:blank"] url for the new tab
 * @param {(Document, Document) => unknown} callback: the first parameter is the
 *                                          devtools panel's document, the
 *                                          second parameter is the opened tab's
 *                                          document.
 * @param {Window} [aWindow] The browser's window object we target
 * @returns {Promise<void>}
 */

async function withDevToolsPanel(url, callback, aWindow = window) {
  if (typeof url === "function") {
    aWindow = callback ?? window;
    callback = url;
    url = "about:blank";
  }

  const { gBrowser } = aWindow;

  const {
    gDevTools,
  } = require("resource://devtools/client/framework/devtools.js");

  info(`Create a new tab with url "${url}".`);
  const tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, url);

  info("Begin to open the DevTools and the performance-new panel.");
  const toolbox = await gDevTools.showToolboxForTab(tab, {
    toolId: "performance",
  });

  const { document } = toolbox.getCurrentPanel().panelWin;

  info("The performance-new panel is now open and ready to use.");
  await callback(document, tab.linkedBrowser.contentDocument);

  info("About to remove the about:blank tab");
  await toolbox.destroy();

  // The previous asynchronous functions 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();

  // Take care to register the TabClose event before we call removeTab, to avoid
  // race issues.
  const waitForClosingPromise = BrowserTestUtils.waitForTabClosing(tab);
  BrowserTestUtils.removeTab(tab);
  info("Requested closing the about:blank tab, waiting...");
  await waitForClosingPromise;
  info("The about:blank tab is now removed.");
}
/* exported withDevToolsPanel */

/**
 * Start and stop the profiler to get the current active configuration. This is
 * done programmtically through the nsIProfiler interface, rather than through click
 * interactions, since the about:profiling page does not include buttons to control
 * the recording.
 *
 * @returns {Object}
 */

function getActiveConfiguration() {
  const BackgroundJSM = ChromeUtils.importESModule(
    "resource://devtools/client/performance-new/shared/background.sys.mjs"
  );

  const { startProfiler, stopProfiler } = BackgroundJSM;

  info("Start the profiler with the current about:profiling configuration.");
  startProfiler("aboutprofiling");

  // Immediately pause the sampling, to make sure the test runs fast. The profiler
  // only needs to be started to initialize the configuration.
  Services.profiler.Pause();

  const { activeConfiguration } = Services.profiler;
  if (!activeConfiguration) {
    throw new Error(
      "Expected to find an active configuration for the profile."
    );
  }

  info("Stop the profiler after getting the active configuration.");
  stopProfiler();

  return activeConfiguration;
}
/* exported getActiveConfiguration */

/**
 * Start the profiler programmatically and check that the active configuration has
 * a feature enabled
 *
 * @param {string} feature
 * @return {boolean}
 */

function activeConfigurationHasFeature(feature) {
  const { features } = getActiveConfiguration();
  return features.includes(feature);
}
/* exported activeConfigurationHasFeature */

/**
 * Start the profiler programmatically and check that the active configuration is
 * tracking a thread.
 *
 * @param {string} thread
 * @return {boolean}
 */

function activeConfigurationHasThread(thread) {
  const { threads } = getActiveConfiguration();
  return threads.includes(thread);
}
/* exported activeConfigurationHasThread */

/**
 * Use user driven events to start the profiler, and then get the active configuration
 * of the profiler. This is similar to functions in the head.js file, but is specific
 * for the DevTools situation. The UI complains if the profiler stops unexpectedly.
 *
 * @param {Document} document
 * @param {string} feature
 * @returns {boolean}
 */

async function devToolsActiveConfigurationHasFeature(document, feature) {
  info("Get the active configuration of the profiler via user driven events.");
  const start = await getActiveButtonFromText(document, "Start recording");
  info("Click the button to start recording.");
  start.click();

  // Get the cancel button first, so that way we know the profile has actually
  // been recorded.
  const cancel = await getActiveButtonFromText(document, "Cancel recording");

  const { activeConfiguration } = Services.profiler;
  if (!activeConfiguration) {
    throw new Error(
      "Expected to find an active configuration for the profile."
    );
  }

  info("Click the cancel button to discard the profile..");
  cancel.click();

  // Wait until the start button is back.
  await getActiveButtonFromText(document, "Start recording");

  return activeConfiguration.features.includes(feature);
}
/* exported devToolsActiveConfigurationHasFeature */

/**
 * This adapts the expectation using the current build's available profiler
 * features.
 * @param {string} fixture It can be either already trimmed or untrimmed.
 * @returns {string}
 */

function _adaptCustomPresetExpectationToCustomBuild(fixture) {
  const supportedFeatures = Services.profiler.GetFeatures();
  info("Supported features are: " + supportedFeatures.join(", "));

  // Some platforms do not support stack walking, we can adjust the passed
  // fixture so that tests are passing in these platforms too.
  // Most notably MacOS outside of Nightly and DevEdition.
  if (!supportedFeatures.includes("stackwalk")) {
    info(
      "Supported features do not include stackwalk, let's remove the Native Stacks from the expected output."
    );
    fixture = fixture.replace(/^.*Native Stacks.*\n/m, "");
  }

  return fixture;
}

/**
 * Get the content of the preset description.
 * @param {Element} devtoolsDocument
 * @returns {string}
 */

function getDevtoolsCustomPresetContent(devtoolsDocument) {
  return devtoolsDocument.querySelector(".perf-presets-custom").innerText;
}
/* exported getDevtoolsCustomPresetContent */

/**
 * This checks if the content of the preset description equals the fixture in
 * string form.
 * @param {Element} devtoolsDocument
 * @param {string} fixture
 */

function checkDevtoolsCustomPresetContent(devtoolsDocument, fixture) {
  // This removes all indentations and any start or end new line and other space characters.
  fixture = fixture.replace(/^\s+/gm, "").trim();
  // This removes unavailable features from the fixture content.
  fixture = _adaptCustomPresetExpectationToCustomBuild(fixture);
  is(getDevtoolsCustomPresetContent(devtoolsDocument), fixture);
}
/* exported checkDevtoolsCustomPresetContent */

/**
 * Selects an element with some given text, then it walks up the DOM until it finds
 * an input or select element via a call to querySelector.
 *
 * @param {Document} document
 * @param {string} text
 * @param {HTMLInputElement}
 */

async function getNearestInputFromText(document, text) {
  const textElement = await getElementFromDocumentByText(document, text);
  if (textElement.control) {
    // This is a label, just grab the input.
    return textElement.control;
  }
  // A non-label node
  let next = textElement;
  while ((next = next.parentElement)) {
    const input = next.querySelector("input, select");
    if (input) {
      return input;
    }
  }
  throw new Error("Could not find an input or select near the text element.");
}
/* exported getNearestInputFromText */

/**
 * Grabs the closest button element from a given snippet of text, and make sure
 * the button is not disabled.
 *
 * @param {Document} document
 * @param {string} text
 * @param {HTMLButtonElement}
 */

async function getActiveButtonFromText(document, text) {
  // This could select a span inside the button, or the button itself.
  let button = await getElementFromDocumentByText(document, text);

  while (button.tagName !== "button") {
    // Walk up until a button element is found.
    button = button.parentElement;
    if (!button) {
      throw new Error(`Unable to find a button from the text "${text}"`);
    }
  }

  await waitUntil(
    () => !button.disabled,
    "Waiting until the button is not disabled."
  );

  return button;
}
/* exported getActiveButtonFromText */

/**
 * Wait until the profiler menu button is added.
 *
 * @returns Promise<void>
 */

async function waitForProfilerMenuButton() {
  info("Checking if the profiler menu button is enabled.");
  await waitUntil(
    () => gBrowser.ownerDocument.getElementById("profiler-button"),
    "> Waiting until the profiler button is added to the browser."
  );
}
/* exported waitForProfilerMenuButton */

/**
 * Make sure the profiler popup is disabled for the test.
 */

async function makeSureProfilerPopupIsDisabled() {
  info("Make sure the profiler popup is dsiabled.");

  info("> Load the profiler menu button module.");
  const { ProfilerMenuButton } = ChromeUtils.importESModule(
    "resource://devtools/client/performance-new/popup/menu-button.sys.mjs"
  );

  const isOriginallyInNavBar = ProfilerMenuButton.isInNavbar();

  if (isOriginallyInNavBar) {
    info("> The menu button is in the navbar, remove it for this test.");
    ProfilerMenuButton.remove();
  } else {
    info("> The menu button was not in the navbar yet.");
  }

  registerCleanupFunction(() => {
    info("Revert the profiler menu button to be back in its original place");
    if (isOriginallyInNavBar !== ProfilerMenuButton.isInNavbar()) {
      ProfilerMenuButton.remove();
    }
  });
}
/* exported makeSureProfilerPopupIsDisabled */

/**
 * Open the WebChannel test document, that will enable the profiler popup via
 * WebChannel.
 * @param {Function} callback
 */

function withWebChannelTestDocument(callback) {
  return BrowserTestUtils.withNewTab(
    {
      gBrowser,
      url: "https://example.com/browser/devtools/client/performance-new/test/browser/webchannel.html",
    },
    callback
  );
}
/* exported withWebChannelTestDocument */

// This has been stolen from the great library dom-testing-library.
// See https://github.com/testing-library/dom-testing-library/blob/91b9dc3b6f5deea88028e97aab15b3b9f3289a2a/src/events.js#L104-L123
// function written after some investigation here:
// https://github.com/facebook/react/issues/10135#issuecomment-401496776
function setNativeValue(element, value) {
  const { set: valueSetter } =
    Object.getOwnPropertyDescriptor(element, "value") || {};
  const prototype = Object.getPrototypeOf(element);
  const { set: prototypeValueSetter } =
    Object.getOwnPropertyDescriptor(prototype, "value") || {};
  if (prototypeValueSetter && valueSetter !== prototypeValueSetter) {
    prototypeValueSetter.call(element, value);
  } else {
    /* istanbul ignore if */
    // eslint-disable-next-line no-lonely-if -- Can't be ignored by istanbul otherwise
    if (valueSetter) {
      valueSetter.call(element, value);
    } else {
      throw new Error("The given element does not have a value setter");
    }
  }
}
/* exported setNativeValue */

/**
 * Set a React-friendly input value. Doing this the normal way doesn't work.
 * This reuses the previous function setNativeValue stolen from
 * dom-testing-library.
 *
 * See https://github.com/facebook/react/issues/10135
 *
 * @param {HTMLInputElement} input
 * @param {string} value
 */

function setReactFriendlyInputValue(input, value) {
  setNativeValue(input, value);

  // 'change' instead of 'input', see https://github.com/facebook/react/issues/11488#issuecomment-381590324
  input.dispatchEvent(new Event("change", { bubbles: true }));
}
/* exported setReactFriendlyInputValue */

/**
 * The recording state is the internal state machine that represents the async
 * operations that are going on in the profiler. This function sets up a helper
 * that will obtain the Redux store and query this internal state. This is useful
 * for unit testing purposes.
 *
 * @param {Document} document
 */

function setupGetRecordingState(document) {
  const selectors = require("resource://devtools/client/performance-new/store/selectors.js");
  const store = document.defaultView.gStore;
  if (!store) {
    throw new Error("Could not find the redux store on the window object.");
  }
  return () => selectors.getRecordingState(store.getState());
}
/* exported setupGetRecordingState */

Messung V0.5
C=88 H=91 G=89

¤ Dauer der Verarbeitung: 0.7 Sekunden  ¤

*© 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.