tor-browser

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

browser_input.js (7235B)


      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 function selectedTextEventPromises(stateChangeType, id) {
      8  return [
      9    waitForMacEventWithInfo("AXSelectedTextChanged", (elem, info) => {
     10      return (
     11        info.AXTextStateChangeType == stateChangeType &&
     12        elem.getAttributeValue("AXDOMIdentifier") == "body"
     13      );
     14    }),
     15    waitForMacEventWithInfo("AXSelectedTextChanged", (elem, info) => {
     16      return (
     17        info.AXTextStateChangeType == stateChangeType &&
     18        elem.getAttributeValue("AXDOMIdentifier") == id
     19      );
     20    }),
     21  ];
     22 }
     23 
     24 async function testInput(browser, accDoc, id = "input") {
     25  let input = getNativeInterface(accDoc, id);
     26 
     27  is(input.getAttributeValue("AXDescription"), "Name", "Correct input label");
     28  is(input.getAttributeValue("AXTitle"), "", "Correct input title");
     29  is(input.getAttributeValue("AXValue"), "Elmer Fudd", "Correct input value");
     30  is(
     31    input.getAttributeValue("AXNumberOfCharacters"),
     32    10,
     33    "Correct length of value"
     34  );
     35 
     36  ok(input.attributeNames.includes("AXSelectedText"), "Has AXSelectedText");
     37  ok(
     38    input.attributeNames.includes("AXSelectedTextRange"),
     39    "Has AXSelectedTextRange"
     40  );
     41 
     42  let evt = Promise.all([
     43    waitForMacEvent("AXFocusedUIElementChanged", id),
     44    ...selectedTextEventPromises(AXTextStateChangeTypeSelectionMove, id),
     45  ]);
     46  await SpecialPowers.spawn(browser, [id], domId => {
     47    content.document.getElementById(domId).focus();
     48  });
     49  await evt;
     50 
     51  evt = Promise.all(
     52    selectedTextEventPromises(AXTextStateChangeTypeSelectionExtend, id)
     53  );
     54  await SpecialPowers.spawn(browser, [id], domId => {
     55    let elm = content.document.getElementById(domId);
     56    if (elm.setSelectionRange) {
     57      elm.setSelectionRange(6, 9);
     58    } else {
     59      let r = new content.Range();
     60      let textNode = elm.firstElementChild.firstChild;
     61      r.setStart(textNode, 6);
     62      r.setEnd(textNode, 9);
     63 
     64      let s = content.getSelection();
     65      s.removeAllRanges();
     66      s.addRange(r);
     67    }
     68  });
     69  await evt;
     70 
     71  is(
     72    input.getAttributeValue("AXSelectedText"),
     73    "Fud",
     74    "Correct text is selected"
     75  );
     76 
     77  Assert.deepEqual(
     78    input.getAttributeValue("AXSelectedTextRange"),
     79    [6, 3],
     80    "correct range selected"
     81  );
     82 
     83  ok(
     84    input.isAttributeSettable("AXSelectedTextRange"),
     85    "AXSelectedTextRange is settable"
     86  );
     87 
     88  evt = Promise.all(
     89    selectedTextEventPromises(AXTextStateChangeTypeSelectionExtend, id)
     90  );
     91  input.setAttributeValue("AXSelectedTextRange", NSRange(1, 7));
     92  await evt;
     93 
     94  Assert.deepEqual(
     95    input.getAttributeValue("AXSelectedTextRange"),
     96    [1, 7],
     97    "correct range selected"
     98  );
     99 
    100  is(
    101    input.getAttributeValue("AXSelectedText"),
    102    "lmer Fu",
    103    "Correct text is selected"
    104  );
    105 
    106  let domSelection = await SpecialPowers.spawn(browser, [], () => {
    107    let elm = content.document.querySelector("input#input");
    108    if (elm) {
    109      return elm.value.substring(elm.selectionStart, elm.selectionEnd);
    110    }
    111 
    112    return content.getSelection().toString();
    113  });
    114 
    115  is(domSelection, "lmer Fu", "correct DOM selection");
    116 
    117  is(
    118    input.getParameterizedAttributeValue("AXStringForRange", NSRange(3, 5)),
    119    "er Fu",
    120    "AXStringForRange works"
    121  );
    122 }
    123 
    124 /**
    125 * Input selection test
    126 */
    127 addAccessibleTask(
    128  `<input aria-label="Name" id="input" value="Elmer Fudd">`,
    129  testInput
    130 );
    131 
    132 /**
    133 * contenteditable selection test with no role
    134 */
    135 addAccessibleTask(
    136  `<div aria-label="Name" id="no-role-editable" contenteditable>
    137     <p>Elmer Fudd</p>
    138   </div>
    139   <div aria-label="Name" id="no-role-editable-single-line" aria-multiline="false" contenteditable>
    140     <p>Elmer Fudd</p>
    141   </div>`,
    142  async (browser, accDoc) => {
    143    await testInput(browser, accDoc, "no-role-editable");
    144    const noRoleEditable = getNativeInterface(accDoc, "no-role-editable");
    145    is(
    146      noRoleEditable.getAttributeValue("AXRole"),
    147      "AXTextArea",
    148      "Correct role for multi-line contenteditable with no role"
    149    );
    150 
    151    await testInput(browser, accDoc, "no-role-editable-single-line");
    152    const noRoleEditableSingleLine = getNativeInterface(
    153      accDoc,
    154      "no-role-editable-single-line"
    155    );
    156    is(
    157      noRoleEditableSingleLine.getAttributeValue("AXRole"),
    158      "AXTextField",
    159      "Correct role for single-line contenteditable with no role"
    160    );
    161  }
    162 );
    163 
    164 /**
    165 * contenteditable selection test
    166 */
    167 addAccessibleTask(
    168  `<div aria-label="Name" tabindex="0" role="textbox" aria-multiline="true" id="input" contenteditable>
    169     <p>Elmer Fudd</p>
    170   </div>`,
    171  testInput
    172 );
    173 
    174 /**
    175 * test contenteditable with selection that extends past editable part
    176 */
    177 addAccessibleTask(
    178  `<span aria-label="Name"
    179         tabindex="0"
    180         role="textbox"
    181         id="input"
    182         contenteditable>Elmer Fudd</span> <span id="notinput">is the name</span>`,
    183  async (browser, accDoc) => {
    184    let evt = Promise.all([
    185      waitForMacEvent("AXFocusedUIElementChanged", "input"),
    186      waitForMacEvent("AXSelectedTextChanged", "body"),
    187      waitForMacEvent("AXSelectedTextChanged", "input"),
    188    ]);
    189    await SpecialPowers.spawn(browser, [], () => {
    190      content.document.getElementById("input").focus();
    191    });
    192    await evt;
    193 
    194    evt = waitForEvent(EVENT_TEXT_CARET_MOVED);
    195    await SpecialPowers.spawn(browser, [], () => {
    196      let input = content.document.getElementById("input");
    197      let notinput = content.document.getElementById("notinput");
    198 
    199      let r = new content.Range();
    200      r.setStart(input.firstChild, 4);
    201      r.setEnd(notinput.firstChild, 6);
    202 
    203      let s = content.getSelection();
    204      s.removeAllRanges();
    205      s.addRange(r);
    206    });
    207    await evt;
    208 
    209    let input = getNativeInterface(accDoc, "input");
    210 
    211    is(
    212      input.getAttributeValue("AXSelectedText"),
    213      "r Fudd",
    214      "Correct text is selected in #input"
    215    );
    216 
    217    is(
    218      stringForRange(
    219        input,
    220        input.getAttributeValue("AXSelectedTextMarkerRange")
    221      ),
    222      "r Fudd is the",
    223      "Correct text is selected in document"
    224    );
    225  }
    226 );
    227 
    228 /**
    229 * test nested content editables and their ancestor getters.
    230 */
    231 addAccessibleTask(
    232  `<div id="outer" role="textbox" contenteditable="true">
    233     <p id="p">Bob <a href="#" id="link">Loblaw's</a></p>
    234     <div id="inner" role="textbox" contenteditable="true">
    235       Law <a href="#" id="inner_link">Blog</a>
    236     </div>
    237   </div>`,
    238  (browser, accDoc) => {
    239    let link = getNativeInterface(accDoc, "link");
    240    let innerLink = getNativeInterface(accDoc, "inner_link");
    241 
    242    let idmatches = (elem, id) => {
    243      is(elem.getAttributeValue("AXDOMIdentifier"), id, "Matches ID");
    244    };
    245 
    246    idmatches(link.getAttributeValue("AXEditableAncestor"), "outer");
    247    idmatches(link.getAttributeValue("AXFocusableAncestor"), "outer");
    248    idmatches(link.getAttributeValue("AXHighestEditableAncestor"), "outer");
    249 
    250    idmatches(innerLink.getAttributeValue("AXEditableAncestor"), "inner");
    251    idmatches(innerLink.getAttributeValue("AXFocusableAncestor"), "inner");
    252    idmatches(
    253      innerLink.getAttributeValue("AXHighestEditableAncestor"),
    254      "outer"
    255    );
    256  }
    257 );