tor-browser

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

browser_caching_actions.js (10389B)


      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 const gClickEvents = ["mousedown", "mouseup", "click"];
      8 
      9 const gActionDescrMap = {
     10  jump: "Jump",
     11  press: "Press",
     12  check: "Check",
     13  uncheck: "Uncheck",
     14  select: "Select",
     15  open: "Open",
     16  close: "Close",
     17  switch: "Switch",
     18  click: "Click",
     19  collapse: "Collapse",
     20  expand: "Expand",
     21  activate: "Activate",
     22  cycle: "Cycle",
     23  clickAncestor: "Click ancestor",
     24 };
     25 
     26 async function testActions(browser, docAcc, id, expectedActions, domEvents) {
     27  info(`Testing element ${id}`);
     28  const acc = findAccessibleChildByID(docAcc, id);
     29  is(acc.actionCount, expectedActions.length, "Correct action count");
     30 
     31  let actionNames = [];
     32  let actionDescriptions = [];
     33  for (let i = 0; i < acc.actionCount; i++) {
     34    actionNames.push(acc.getActionName(i));
     35    actionDescriptions.push(acc.getActionDescription(i));
     36  }
     37 
     38  is(actionNames.join(","), expectedActions.join(","), "Correct action names");
     39  is(
     40    actionDescriptions.join(","),
     41    expectedActions.map(a => gActionDescrMap[a]).join(","),
     42    "Correct action descriptions"
     43  );
     44 
     45  if (!domEvents) {
     46    return;
     47  }
     48 
     49  // We need to set up the listener, and wait for the promise in two separate
     50  // content tasks.
     51  await invokeContentTask(browser, [id, domEvents], (_id, _domEvents) => {
     52    let promises = _domEvents.map(
     53      evtName =>
     54        new Promise(resolve => {
     55          const listener = e => {
     56            if (e.target.id == _id) {
     57              content.removeEventListener(evtName, listener);
     58              resolve(42);
     59            }
     60          };
     61          content.addEventListener(evtName, listener);
     62        })
     63    );
     64    content.evtPromise = Promise.all(promises);
     65  });
     66 
     67  acc.doAction(0);
     68 
     69  let eventFired = await invokeContentTask(browser, [], async () => {
     70    await content.evtPromise;
     71    content.evtPromise = null;
     72    return true;
     73  });
     74 
     75  ok(eventFired, `DOM events fired '${domEvents}'`);
     76 }
     77 
     78 addAccessibleTask(
     79  `<ul>
     80    <li id="li_clickable1" data-event="click">Clickable list item</li>
     81    <li id="li_clickable2" data-event="mousedown">Clickable list item</li>
     82    <li id="li_clickable3" data-event="mouseup">Clickable list item</li>
     83  </ul>
     84 
     85  <img id="onclick_img" data-event="click"
     86        src="http://example.com/a11y/accessible/tests/mochitest/moz.png">
     87 
     88  <a id="link1" href="#">linkable textleaf accessible</a>
     89  <div id="link2" data-event="click">linkable textleaf accessible</div>
     90 
     91  <a id="link3" href="#">
     92    <img id="link3img" alt="image in link"
     93          src="http://example.com/a11y/accessible/tests/mochitest/moz.png">
     94  </a>
     95 
     96  <a href="about:mozilla" id="link4" target="_blank" rel="opener">
     97    <img src="../moz.png" id="link4img">
     98  </a>
     99  <a id="link5" data-event="mousedown">
    100    <img src="../moz.png" id="link5img">
    101  </a>
    102  <a id="link6" data-event="click">
    103    <img src="../moz.png" id="link6img">
    104  </a>
    105  <a id="link7" data-event="mouseup">
    106    <img src="../moz.png" id="link7img">
    107  </a>
    108 
    109  <div>
    110    <label for="TextBox_t2" id="label1">
    111      <span>Explicit</span>
    112    </label>
    113    <input name="in2" id="TextBox_t2" type="text" maxlength="17">
    114  </div>
    115 
    116  <div data-event="click"><p id="p_in_clickable_div">p in clickable div</p></div>
    117 
    118  <img id="map_img" usemap="#map" src="http://example.com/a11y/accessible/tests/mochitest/moz.png" alt="map_img">
    119  <map name="map">
    120    <!-- These coords are deliberately small so that the area does not include
    121      the center of the image.
    122      -->
    123    <area id="area" href="#" shape="rect" coords="0,0,2,2" alt="area">
    124  </map>
    125  `,
    126  async function (browser, docAcc) {
    127    is(docAcc.actionCount, 0, "Doc should not have any actions");
    128 
    129    const _testActions = async (id, expectedActions, domEvents) => {
    130      await testActions(browser, docAcc, id, expectedActions, domEvents);
    131    };
    132 
    133    await _testActions("li_clickable1", ["click"], gClickEvents);
    134    await _testActions("li_clickable2", ["click"], gClickEvents);
    135    await _testActions("li_clickable3", ["click"], gClickEvents);
    136 
    137    await _testActions("onclick_img", ["click"], gClickEvents);
    138    await _testActions("link1", ["jump"], gClickEvents);
    139    await _testActions("link2", ["click"], gClickEvents);
    140    await _testActions("link3", ["jump"], gClickEvents);
    141    await _testActions("link3img", ["clickAncestor"], gClickEvents);
    142    await _testActions("link4", ["jump"], gClickEvents);
    143    await _testActions("link4img", ["clickAncestor"], gClickEvents);
    144    await _testActions("link5", ["click"], gClickEvents);
    145    await _testActions("link5img", ["clickAncestor"], gClickEvents);
    146    await _testActions("link6", ["click"], gClickEvents);
    147    await _testActions("link6img", ["clickAncestor"], gClickEvents);
    148    await _testActions("link7", ["click"], gClickEvents);
    149    await _testActions("link7img", ["clickAncestor"], gClickEvents);
    150    await _testActions("label1", ["click"], gClickEvents);
    151    await _testActions("p_in_clickable_div", ["clickAncestor"], gClickEvents);
    152    await _testActions("area", ["jump"], gClickEvents);
    153 
    154    await invokeContentTask(browser, [], () => {
    155      content.document.getElementById("li_clickable1").onclick = null;
    156    });
    157 
    158    let acc = findAccessibleChildByID(docAcc, "li_clickable1");
    159    await untilCacheIs(() => acc.actionCount, 0, "li has no actions");
    160    let thrown = false;
    161    try {
    162      acc.doAction(0);
    163    } catch (e) {
    164      thrown = true;
    165    }
    166    ok(thrown, "doAction should throw exception");
    167 
    168    // Remove 'for' from label
    169    await invokeContentTask(browser, [], () => {
    170      content.document.getElementById("label1").removeAttribute("for");
    171    });
    172    acc = findAccessibleChildByID(docAcc, "label1");
    173    await untilCacheIs(() => acc.actionCount, 0, "label has no actions");
    174    thrown = false;
    175    try {
    176      acc.doAction(0);
    177      ok(false, "doAction should throw exception");
    178    } catch (e) {
    179      thrown = true;
    180    }
    181    ok(thrown, "doAction should throw exception");
    182 
    183    // Add 'longdesc' to image
    184    await invokeContentTask(browser, [], () => {
    185      content.document
    186        .getElementById("onclick_img")
    187        // eslint-disable-next-line @microsoft/sdl/no-insecure-url
    188        .setAttribute("longdesc", "http://example.com");
    189    });
    190    acc = findAccessibleChildByID(docAcc, "onclick_img");
    191    await untilCacheIs(() => acc.actionCount, 2, "img has 2 actions");
    192    await _testActions("onclick_img", ["click", "showlongdesc"]);
    193 
    194    // Remove 'onclick' from image with 'longdesc'
    195    await invokeContentTask(browser, [], () => {
    196      content.document.getElementById("onclick_img").onclick = null;
    197    });
    198    acc = findAccessibleChildByID(docAcc, "onclick_img");
    199    await untilCacheIs(() => acc.actionCount, 1, "img has 1 actions");
    200    await _testActions("onclick_img", ["showlongdesc"]);
    201 
    202    // Remove 'href' from link and test linkable child
    203    let link1Acc = findAccessibleChildByID(docAcc, "link1");
    204    is(
    205      link1Acc.firstChild.getActionName(0),
    206      "clickAncestor",
    207      "linkable child has clickAncestor action"
    208    );
    209    let onRecreation = waitForEvents({
    210      expected: [
    211        [EVENT_HIDE, link1Acc],
    212        [EVENT_SHOW, "link1"],
    213      ],
    214    });
    215    await invokeContentTask(browser, [], () => {
    216      let link1 = content.document.getElementById("link1");
    217      link1.removeAttribute("href");
    218    });
    219    await onRecreation;
    220    link1Acc = findAccessibleChildByID(docAcc, "link1");
    221    await untilCacheIs(() => link1Acc.actionCount, 0, "link has no actions");
    222    is(link1Acc.firstChild.actionCount, 0, "linkable child's actions removed");
    223 
    224    // Add a click handler to the body. Ensure it propagates to descendants.
    225    await invokeContentTask(browser, [], () => {
    226      content.document.body.onclick = () => {};
    227    });
    228    await untilCacheIs(() => docAcc.actionCount, 1, "Doc has 1 action");
    229    await _testActions("link1", ["clickAncestor"]);
    230 
    231    await invokeContentTask(browser, [], () => {
    232      content.document.body.onclick = null;
    233    });
    234    await untilCacheIs(() => docAcc.actionCount, 0, "Doc has no actions");
    235    is(link1Acc.actionCount, 0, "link has no actions");
    236 
    237    // Add a click handler to the root element. Ensure it propagates to
    238    // descendants.
    239    await invokeContentTask(browser, [], () => {
    240      content.document.documentElement.onclick = () => {};
    241    });
    242    await untilCacheIs(() => docAcc.actionCount, 1, "Doc has 1 action");
    243    await _testActions("link1", ["clickAncestor"]);
    244  },
    245  {
    246    chrome: true,
    247    topLevel: true,
    248    iframe: true,
    249    remoteIframe: true,
    250    contentSetup: async function contentSetup() {
    251      // Attach dummy event handlers here, because inline event handler attributes
    252      // will be blocked in the chrome context.
    253      for (const el of content.document.querySelectorAll("[data-event]")) {
    254        el["on" + el.dataset.event] = () => {};
    255      }
    256    },
    257  }
    258 );
    259 
    260 /**
    261 * Test access key.
    262 */
    263 addAccessibleTask(
    264  `
    265 <button id="noKey">noKey</button>
    266 <button id="key" accesskey="a">key</button>
    267  `,
    268  async function (browser, docAcc) {
    269    const noKey = findAccessibleChildByID(docAcc, "noKey");
    270    is(noKey.accessKey, "", "noKey has no accesskey");
    271    const key = findAccessibleChildByID(docAcc, "key");
    272    is(key.accessKey, MAC ? "⌃⌥a" : "Alt+Shift+a", "key has correct accesskey");
    273 
    274    info("Changing accesskey");
    275    await invokeContentTask(browser, [], () => {
    276      content.document.getElementById("key").accessKey = "b";
    277    });
    278    await untilCacheIs(
    279      () => key.accessKey,
    280      MAC ? "⌃⌥b" : "Alt+Shift+b",
    281      "Correct accesskey after change"
    282    );
    283 
    284    info("Removing accesskey");
    285    await invokeContentTask(browser, [], () => {
    286      content.document.getElementById("key").removeAttribute("accesskey");
    287    });
    288    await untilCacheIs(
    289      () => key.accessKey,
    290      "",
    291      "Empty accesskey after removal"
    292    );
    293 
    294    info("Adding accesskey");
    295    await invokeContentTask(browser, [], () => {
    296      content.document.getElementById("key").accessKey = "c";
    297    });
    298    await untilCacheIs(
    299      () => key.accessKey,
    300      MAC ? "⌃⌥c" : "Alt+Shift+c",
    301      "Correct accesskey after addition"
    302    );
    303  },
    304  {
    305    chrome: true,
    306    topLevel: true,
    307    iframe: false, // Bug 1796846
    308    remoteIframe: false, // Bug 1796846
    309  }
    310 );