tor-browser

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

browser_editabletext.js (9345B)


      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 /* import-globals-from ../../mochitest/states.js */
      8 loadScripts({ name: "states.js", dir: MOCHITESTS_DIR });
      9 
     10 async function testEditable(browser, acc, aBefore = "", aAfter = "") {
     11  async function resetInput() {
     12    if (acc.childCount <= 1) {
     13      return;
     14    }
     15 
     16    let emptyInputEvent = waitForEvent(EVENT_TEXT_VALUE_CHANGE, "input");
     17    await invokeContentTask(browser, [], async () => {
     18      content.document.getElementById("input").innerHTML = "";
     19    });
     20 
     21    await emptyInputEvent;
     22  }
     23 
     24  // ////////////////////////////////////////////////////////////////////////
     25  // insertText
     26  await testInsertText(acc, "hello", 0, aBefore.length);
     27  await isFinalValueCorrect(browser, acc, [aBefore, "hello", aAfter]);
     28  await testInsertText(acc, "ma ", 0, aBefore.length);
     29  await isFinalValueCorrect(browser, acc, [aBefore, "ma hello", aAfter]);
     30  await testInsertText(acc, "ma", 2, aBefore.length);
     31  await isFinalValueCorrect(browser, acc, [aBefore, "mama hello", aAfter]);
     32  await testInsertText(acc, " hello", 10, aBefore.length);
     33  await isFinalValueCorrect(browser, acc, [
     34    aBefore,
     35    "mama hello hello",
     36    aAfter,
     37  ]);
     38 
     39  // ////////////////////////////////////////////////////////////////////////
     40  // deleteText
     41  await testDeleteText(acc, 0, 5, aBefore.length);
     42  await isFinalValueCorrect(browser, acc, [aBefore, "hello hello", aAfter]);
     43  await testDeleteText(acc, 5, 6, aBefore.length);
     44  await isFinalValueCorrect(browser, acc, [aBefore, "hellohello", aAfter]);
     45  await testDeleteText(acc, 5, 10, aBefore.length);
     46  await isFinalValueCorrect(browser, acc, [aBefore, "hello", aAfter]);
     47  await testDeleteText(acc, 0, 5, aBefore.length);
     48  await isFinalValueCorrect(browser, acc, [aBefore, "", aAfter]);
     49 
     50  // XXX: clipboard operation tests don't work well with editable documents.
     51  if (acc.role == ROLE_DOCUMENT) {
     52    return;
     53  }
     54 
     55  await resetInput();
     56 
     57  // copyText and pasteText
     58  await testInsertText(acc, "hello", 0, aBefore.length);
     59  await isFinalValueCorrect(browser, acc, [aBefore, "hello", aAfter]);
     60 
     61  await testCopyText(acc, 0, 1, aBefore.length, browser, "h");
     62  await testPasteText(acc, 1, aBefore.length);
     63  await isFinalValueCorrect(browser, acc, [aBefore, "hhello", aAfter]);
     64 
     65  await testCopyText(acc, 5, 6, aBefore.length, browser, "o");
     66  await testPasteText(acc, 6, aBefore.length);
     67  await isFinalValueCorrect(browser, acc, [aBefore, "hhelloo", aAfter]);
     68 
     69  await testCopyText(acc, 2, 3, aBefore.length, browser, "e");
     70  await testPasteText(acc, 1, aBefore.length);
     71  await isFinalValueCorrect(browser, acc, [aBefore, "hehelloo", aAfter]);
     72 
     73  // cut & paste
     74  await testCutText(acc, 0, 1, aBefore.length);
     75  await isFinalValueCorrect(browser, acc, [aBefore, "ehelloo", aAfter]);
     76  await testPasteText(acc, 2, aBefore.length);
     77  await isFinalValueCorrect(browser, acc, [aBefore, "ehhelloo", aAfter]);
     78 
     79  await testCutText(acc, 3, 4, aBefore.length);
     80  await isFinalValueCorrect(browser, acc, [aBefore, "ehhlloo", aAfter]);
     81  await testPasteText(acc, 6, aBefore.length);
     82  await isFinalValueCorrect(browser, acc, [aBefore, "ehhlloeo", aAfter]);
     83 
     84  await testCutText(acc, 0, 8, aBefore.length);
     85  await isFinalValueCorrect(browser, acc, [aBefore, "", aAfter]);
     86 
     87  await resetInput();
     88 
     89  // ////////////////////////////////////////////////////////////////////////
     90  // setTextContents
     91  await testSetTextContents(acc, "hello", aBefore.length, [
     92    EVENT_TEXT_INSERTED,
     93  ]);
     94  await isFinalValueCorrect(browser, acc, [aBefore, "hello", aAfter]);
     95  await testSetTextContents(acc, "katze", aBefore.length, [
     96    EVENT_TEXT_REMOVED,
     97    EVENT_TEXT_INSERTED,
     98  ]);
     99  await isFinalValueCorrect(browser, acc, [aBefore, "katze", aAfter]);
    100 }
    101 
    102 addAccessibleTask(
    103  `<input id="input"/>`,
    104  async function (browser, docAcc) {
    105    await testEditable(browser, findAccessibleChildByID(docAcc, "input"));
    106  },
    107  { chrome: true, topLevel: true }
    108 );
    109 
    110 addAccessibleTask(
    111  `<div id="input" contenteditable="true" role="textbox"></div>`,
    112  async function (browser, docAcc) {
    113    await testEditable(
    114      browser,
    115      findAccessibleChildByID(docAcc, "input"),
    116      "",
    117      ""
    118    );
    119  },
    120  { chrome: true, topLevel: false /* bug 1834129 */ }
    121 );
    122 
    123 addAccessibleTask(
    124  `<style>
    125  #input::after {
    126    content: "pseudo element";
    127  }
    128 </style>
    129 <div id="input" contenteditable="true" role="textbox"></div>`,
    130  async function (browser, docAcc) {
    131    await testEditable(
    132      browser,
    133      findAccessibleChildByID(docAcc, "input"),
    134      "",
    135      "pseudo element"
    136    );
    137  },
    138  { chrome: true, topLevel: false /* bug 1834129 */ }
    139 );
    140 
    141 addAccessibleTask(
    142  `<style>
    143  #input::before {
    144    content: "pseudo element";
    145  }
    146 </style>
    147 <div id="input" contenteditable="true" role="textbox"></div>`,
    148  async function (browser, docAcc) {
    149    await testEditable(
    150      browser,
    151      findAccessibleChildByID(docAcc, "input"),
    152      "pseudo element"
    153    );
    154  },
    155  { chrome: true, topLevel: false /* bug 1834129 */ }
    156 );
    157 
    158 addAccessibleTask(
    159  `<style>
    160  #input::before {
    161    content: "before";
    162  }
    163  #input::after {
    164    content: "after";
    165  }
    166 </style>
    167 <div id="input" contenteditable="true" role="textbox"></div>`,
    168  async function (browser, docAcc) {
    169    await testEditable(
    170      browser,
    171      findAccessibleChildByID(docAcc, "input"),
    172      "before",
    173      "after"
    174    );
    175  },
    176  { chrome: true, topLevel: false /* bug 1834129 */ }
    177 );
    178 
    179 addAccessibleTask(
    180  `<style>
    181  br {
    182    position: fixed;
    183  }
    184 </style>
    185 <div id="input" contenteditable="true" role="textbox"></div>`,
    186  async function (browser, docAcc) {
    187    document.execCommand("insertText", false, "a");
    188    document.execCommand("delete");
    189    await testEditable(browser, findAccessibleChildByID(docAcc, "input"));
    190  },
    191  { chrome: true, topLevel: false /* bug 1834129 */ }
    192 );
    193 
    194 addAccessibleTask(
    195  `<style>
    196  #input {
    197    white-space: pre;
    198  }
    199  #input::before {
    200    content: "before";
    201  }
    202  #input::after {
    203    content: "after";
    204  }
    205 </style>
    206 <div id="input" contenteditable="plaintext-only" role="textbox"></div>`,
    207  async function (browser, docAcc) {
    208    await testEditable(
    209      browser,
    210      findAccessibleChildByID(docAcc, "input"),
    211      "before",
    212      "after"
    213    );
    214  },
    215  { chrome: true, topLevel: false /* bug 1834129 */ }
    216 );
    217 
    218 addAccessibleTask(
    219  ``,
    220  async function (browser, docAcc) {
    221    await testEditable(browser, docAcc);
    222  },
    223  {
    224    chrome: true,
    225    topLevel: true,
    226    contentDocBodyAttrs: { contentEditable: "true" },
    227  }
    228 );
    229 
    230 /**
    231 * Test PasteText replacement of selected text.
    232 */
    233 addAccessibleTask(
    234  `<input id="input" value="abcdef">`,
    235  async function testPasteTextReplace(browser, docAcc) {
    236    const input = findAccessibleChildByID(docAcc, "input");
    237    let focused = waitForEvent(EVENT_FOCUS, input);
    238    info("Focusing input");
    239    input.takeFocus();
    240    await focused;
    241    info("Copying ef");
    242    input.QueryInterface(nsIAccessibleEditableText);
    243    let selected = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, input);
    244    input.copyText(4, 6);
    245    await selected;
    246    info("Selecting bc");
    247    selected = waitForEvent(EVENT_TEXT_SELECTION_CHANGED, input);
    248    await invokeContentTask(browser, [], () => {
    249      const inputDom = content.document.getElementById("input");
    250      inputDom.selectionStart = 1;
    251      inputDom.selectionEnd = 3;
    252    });
    253    await selected;
    254    info("Pasting at caret");
    255    let changed = waitForEvents([
    256      [EVENT_TEXT_REMOVED, input],
    257      [EVENT_TEXT_INSERTED, input],
    258      [EVENT_TEXT_VALUE_CHANGE, input],
    259    ]);
    260    input.pasteText(nsIAccessibleText.TEXT_OFFSET_CARET);
    261    await changed;
    262    is(input.value, "aefdef", "input value correct after pasting");
    263  }
    264 );
    265 
    266 addAccessibleTask(
    267  `<div id="editable" contenteditable="true"><p id="p">one</p></div>`,
    268  async function testNoRoleEditable(browser, docAcc) {
    269    const editable = findAccessibleChildByID(docAcc, "editable");
    270    is(editable.value, "one", "initial value correct");
    271    ok(true, "Set initial text");
    272    await invokeContentTask(browser, [], () => {
    273      content.document.getElementById("p").firstChild.data = "two";
    274    });
    275    await untilCacheIs(() => editable.value, "two", "value changed correctly");
    276 
    277    function isMultiline() {
    278      let extState = {};
    279      editable.getState({}, extState);
    280      return (
    281        !!(extState.value & EXT_STATE_MULTI_LINE) &&
    282        !(extState.value & EXT_STATE_SINGLE_LINE)
    283      );
    284    }
    285 
    286    ok(isMultiline(), "Editable is in multiline state");
    287    await invokeSetAttribute(browser, "editable", "aria-multiline", "false");
    288    await untilCacheOk(() => !isMultiline(), "editable is in singleline state");
    289 
    290    await invokeSetAttribute(browser, "editable", "aria-multiline");
    291    await untilCacheOk(() => isMultiline(), "editable is in multi-line again");
    292 
    293    await invokeSetAttribute(browser, "editable", "contenteditable");
    294    await untilCacheOk(() => {
    295      let extState = {};
    296      editable.getState({}, extState);
    297      return (
    298        !(extState.value & EXT_STATE_MULTI_LINE) &&
    299        !(extState.value & EXT_STATE_SINGLE_LINE)
    300      );
    301    }, "editable should have neither multi-line nor single-line state");
    302  },
    303  {
    304    chrome: true,
    305    topLevel: true,
    306  }
    307 );