tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

head.js (9833B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 "use strict";
      6 
      7 /* eslint no-unused-vars: [2, {"vars": "local"}] */
      8 
      9 Services.scriptloader.loadSubScript(
     10  "chrome://mochitests/content/browser/devtools/client/shared/test/shared-head.js",
     11  this
     12 );
     13 
     14 const {
     15  DevToolsClient,
     16 } = require("resource://devtools/client/devtools-client.js");
     17 const {
     18  ActorRegistry,
     19 } = require("resource://devtools/server/actors/utils/actor-registry.js");
     20 const {
     21  DevToolsServer,
     22 } = require("resource://devtools/server/devtools-server.js");
     23 
     24 const PATH = "browser/devtools/server/tests/browser/";
     25 const TEST_DOMAIN = "http://test1.example.org";
     26 const TEST_DOMAIN_HTTPS = "https://test1.example.org";
     27 const MAIN_DOMAIN = `${TEST_DOMAIN}/${PATH}`;
     28 const MAIN_DOMAIN_HTTPS = `${TEST_DOMAIN_HTTPS}/${PATH}`;
     29 const ALT_DOMAIN = "http://sectest1.example.org/" + PATH;
     30 const ALT_DOMAIN_SECURED = "https://sectest1.example.org:443/" + PATH;
     31 
     32 // GUID to be used as a separator in compound keys. This must match the same
     33 // constant in devtools/server/actors/resources/storage/index.js,
     34 // devtools/client/storage/ui.js and devtools/client/storage/test/head.js
     35 const SEPARATOR_GUID = "{9d414cc5-8319-0a04-0586-c0a6ae01670a}";
     36 
     37 // All tests are asynchronous.
     38 waitForExplicitFinish();
     39 
     40 // does almost the same thing as addTab, but directly returns an object
     41 async function addTabTarget(url) {
     42  info(`Adding a new tab with URL: ${url}`);
     43  const tab = (gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, url));
     44  await BrowserTestUtils.browserLoaded(tab.linkedBrowser);
     45  info(`Tab added a URL ${url} loaded`);
     46  return createAndAttachTargetForTab(tab);
     47 }
     48 
     49 async function initAnimationsFrontForUrl(url) {
     50  const { inspector, walker, target } = await initInspectorFront(url);
     51  const animations = await target.getFront("animations");
     52 
     53  return { inspector, walker, animations, target };
     54 }
     55 
     56 async function initLayoutFrontForUrl(url) {
     57  const { inspector, walker, target } = await initInspectorFront(url);
     58  const layout = await walker.getLayoutInspector();
     59 
     60  return { inspector, walker, layout, target };
     61 }
     62 
     63 async function initAccessibilityFrontsForUrl(
     64  url,
     65  { enableByDefault = true } = {}
     66 ) {
     67  const { inspector, walker, target } = await initInspectorFront(url);
     68  const parentAccessibility = await target.client.mainRoot.getFront(
     69    "parentaccessibility"
     70  );
     71  const accessibility = await target.getFront("accessibility");
     72  const a11yWalker = accessibility.accessibleWalkerFront;
     73  if (enableByDefault) {
     74    await parentAccessibility.enable();
     75  }
     76 
     77  return {
     78    inspector,
     79    walker,
     80    accessibility,
     81    parentAccessibility,
     82    a11yWalker,
     83    target,
     84  };
     85 }
     86 
     87 function initDevToolsServer() {
     88  try {
     89    // Sometimes devtools server does not get destroyed correctly by previous
     90    // tests.
     91    DevToolsServer.destroy();
     92  } catch (e) {
     93    info(`DevToolsServer destroy error: ${e}\n${e.stack}`);
     94  }
     95  DevToolsServer.init();
     96  DevToolsServer.registerAllActors();
     97 }
     98 
     99 async function initPerfFront() {
    100  initDevToolsServer();
    101  const client = new DevToolsClient(DevToolsServer.connectPipe());
    102  await waitUntilClientConnected(client);
    103  const front = await client.mainRoot.getFront("perf");
    104  return { front, client };
    105 }
    106 
    107 async function initInspectorFront(url) {
    108  const target = await addTabTarget(url);
    109  const inspector = await target.getFront("inspector");
    110  const walker = inspector.walker;
    111 
    112  return { inspector, walker, target };
    113 }
    114 
    115 /**
    116 * Wait until a DevToolsClient is connected.
    117 *
    118 * @param {DevToolsClient} client
    119 * @return {Promise} Resolves when connected.
    120 */
    121 function waitUntilClientConnected(client) {
    122  return client.once("connected");
    123 }
    124 
    125 /**
    126 * Wait for eventName on target.
    127 *
    128 * @param {object} target An observable object that either supports on/off or
    129 * addEventListener/removeEventListener
    130 * @param {string} eventName
    131 * @param {boolean} useCapture Optional, for addEventListener/removeEventListener
    132 * @return A promise that resolves when the event has been handled
    133 */
    134 function once(target, eventName, useCapture = false) {
    135  info("Waiting for event: '" + eventName + "' on " + target + ".");
    136 
    137  return new Promise(resolve => {
    138    for (const [add, remove] of [
    139      ["addEventListener", "removeEventListener"],
    140      ["addListener", "removeListener"],
    141      ["on", "off"],
    142    ]) {
    143      if (add in target && remove in target) {
    144        target[add](
    145          eventName,
    146          function onEvent(...aArgs) {
    147            info("Got event: '" + eventName + "' on " + target + ".");
    148            target[remove](eventName, onEvent, useCapture);
    149            resolve(...aArgs);
    150          },
    151          useCapture
    152        );
    153        break;
    154      }
    155    }
    156  });
    157 }
    158 
    159 /**
    160 * Forces GC, CC and Shrinking GC to get rid of disconnected docshells and
    161 * windows.
    162 */
    163 function forceCollections() {
    164  Cu.forceGC();
    165  Cu.forceCC();
    166  Cu.forceShrinkingGC();
    167 }
    168 
    169 registerCleanupFunction(function tearDown() {
    170  Services.cookies.removeAll();
    171 
    172  while (gBrowser.tabs.length > 1) {
    173    gBrowser.removeCurrentTab();
    174  }
    175 });
    176 
    177 function idleWait(time) {
    178  return DevToolsUtils.waitForTime(time);
    179 }
    180 
    181 function busyWait(time) {
    182  const start = Date.now();
    183  let stack;
    184  while (Date.now() - start < time) {
    185    stack = Components.stack; // eslint-disable-line no-unused-vars
    186  }
    187 }
    188 
    189 /**
    190 * Waits until a predicate returns true.
    191 *
    192 * @param function predicate
    193 *        Invoked once in a while until it returns true.
    194 * @param number interval [optional]
    195 *        How often the predicate is invoked, in milliseconds.
    196 */
    197 function waitUntil(predicate, interval = 10) {
    198  if (predicate()) {
    199    return Promise.resolve(true);
    200  }
    201  return new Promise(resolve => {
    202    setTimeout(function () {
    203      waitUntil(predicate).then(() => resolve(true));
    204    }, interval);
    205  });
    206 }
    207 
    208 function waitForMarkerType(
    209  front,
    210  types,
    211  predicate,
    212  unpackFun = (name, data) => data.markers,
    213  eventName = "timeline-data"
    214 ) {
    215  types = [].concat(types);
    216  predicate =
    217    predicate ||
    218    function () {
    219      return true;
    220    };
    221  let filteredMarkers = [];
    222 
    223  return new Promise(resolve => {
    224    info("Waiting for markers of type: " + types);
    225 
    226    function handler(name, data) {
    227      if (typeof name === "string" && name !== "markers") {
    228        return;
    229      }
    230 
    231      const markers = unpackFun(name, data);
    232      info("Got markers");
    233 
    234      filteredMarkers = filteredMarkers.concat(
    235        markers.filter(m => types.includes(m.name))
    236      );
    237 
    238      if (
    239        types.every(t => filteredMarkers.some(m => m.name === t)) &&
    240        predicate(filteredMarkers)
    241      ) {
    242        front.off(eventName, handler);
    243        resolve(filteredMarkers);
    244      }
    245    }
    246    front.on(eventName, handler);
    247  });
    248 }
    249 
    250 function getCookieId(name, domain, path, partitionKey = "") {
    251  return `${name}${SEPARATOR_GUID}${domain}${SEPARATOR_GUID}${path}${SEPARATOR_GUID}${partitionKey}`;
    252 }
    253 
    254 /**
    255 * Trigger DOM activity and wait for the corresponding accessibility event.
    256 *
    257 * @param  {object} emitter   Devtools event emitter, usually a front.
    258 * @param  {Sting} name       Accessibility event in question.
    259 * @param  {Function} handler Accessibility event handler function with checks.
    260 * @param  {Promise} task     A promise that resolves when DOM activity is done.
    261 */
    262 async function emitA11yEvent(emitter, name, handler, task) {
    263  const promise = emitter.once(name, handler);
    264  await task();
    265  await promise;
    266 }
    267 
    268 /**
    269 * Check that accessibilty front is correct and its attributes are also
    270 * up-to-date.
    271 *
    272 * @param  {object} front         Accessibility front to be tested.
    273 * @param  {object} expected      A map of a11y front properties to be verified.
    274 * @param  {object} expectedFront Expected accessibility front.
    275 */
    276 function checkA11yFront(front, expected, expectedFront) {
    277  ok(front, "The accessibility front is created");
    278 
    279  if (expectedFront) {
    280    is(front, expectedFront, "Matching accessibility front");
    281  }
    282 
    283  // Clone the front so we could modify some values for comparison.
    284  front = Object.assign(front);
    285  for (const key in expected) {
    286    if (key === "checks") {
    287      const { CONTRAST } = front[key];
    288      // Contrast values are rounded to two digits after the decimal point.
    289      if (CONTRAST && CONTRAST.value) {
    290        CONTRAST.value = parseFloat(CONTRAST.value.toFixed(2));
    291      }
    292    }
    293 
    294    if (["actions", "states", "attributes", "checks"].includes(key)) {
    295      SimpleTest.isDeeply(
    296        front[key],
    297        expected[key],
    298        `Accessible Front has correct ${key}`
    299      );
    300    } else {
    301      is(front[key], expected[key], `accessibility front has correct ${key}`);
    302    }
    303  }
    304 }
    305 
    306 function getA11yInitOrShutdownPromise() {
    307  return new Promise(resolve => {
    308    const observe = (subject, topic, data) => {
    309      Services.obs.removeObserver(observe, "a11y-init-or-shutdown");
    310      resolve(data);
    311    };
    312    Services.obs.addObserver(observe, "a11y-init-or-shutdown");
    313  });
    314 }
    315 
    316 /**
    317 * Wait for accessibility service to shut down. We consider it shut down when
    318 * an "a11y-init-or-shutdown" event is received with a value of "0".
    319 */
    320 async function waitForA11yShutdown(parentAccessibility) {
    321  await parentAccessibility.disable();
    322  if (!Services.appinfo.accessibilityEnabled) {
    323    return;
    324  }
    325 
    326  await getA11yInitOrShutdownPromise().then(data =>
    327    data === "0" ? Promise.resolve() : Promise.reject()
    328  );
    329 }
    330 
    331 /**
    332 * Wait for accessibility service to initialize. We consider it initialized when
    333 * an "a11y-init-or-shutdown" event is received with a value of "1".
    334 */
    335 async function waitForA11yInit() {
    336  if (Services.appinfo.accessibilityEnabled) {
    337    return;
    338  }
    339 
    340  await getA11yInitOrShutdownPromise().then(data =>
    341    data === "1" ? Promise.resolve() : Promise.reject()
    342  );
    343 }