tor-browser

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

browser_content_command_insert_text.js (8692B)


      1 /* Any copyright is dedicated to the Public Domain.
      2   http://creativecommons.org/publicdomain/zero/1.0/ */
      3 
      4 "use strict";
      5 
      6 const { CustomizableUITestUtils } = ChromeUtils.importESModule(
      7  "resource://testing-common/CustomizableUITestUtils.sys.mjs"
      8 );
      9 const { ContentTaskUtils } = ChromeUtils.importESModule(
     10  "resource://testing-common/ContentTaskUtils.sys.mjs"
     11 );
     12 const gCUITestUtils = new CustomizableUITestUtils(window);
     13 const gDOMWindowUtils = EventUtils._getDOMWindowUtils(window);
     14 let searchBar;
     15 
     16 add_setup(async function () {
     17  searchBar = await gCUITestUtils.addSearchBar();
     18  registerCleanupFunction(() => {
     19    gCUITestUtils.removeSearchBar();
     20  });
     21 });
     22 
     23 function promiseResettingSearchBarAndFocus() {
     24  const waitForFocusInSearchBar = BrowserTestUtils.waitForEvent(
     25    searchBar.inputField,
     26    "focus"
     27  );
     28  searchBar.inputField.focus();
     29  searchBar.inputField.value = "";
     30  return Promise.all([
     31    waitForFocusInSearchBar,
     32    TestUtils.waitForCondition(
     33      () =>
     34        gDOMWindowUtils.IMEStatus === Ci.nsIDOMWindowUtils.IME_STATUS_ENABLED &&
     35        gDOMWindowUtils.inputContextOrigin ===
     36          Ci.nsIDOMWindowUtils.INPUT_CONTEXT_ORIGIN_MAIN
     37    ),
     38  ]);
     39 }
     40 
     41 function promiseIMEStateEnabledByRemote() {
     42  return TestUtils.waitForCondition(
     43    () =>
     44      gDOMWindowUtils.IMEStatus === Ci.nsIDOMWindowUtils.IME_STATUS_ENABLED &&
     45      gDOMWindowUtils.inputContextOrigin ===
     46        Ci.nsIDOMWindowUtils.INPUT_CONTEXT_ORIGIN_CONTENT
     47  );
     48 }
     49 
     50 function promiseContentTick(browser) {
     51  return SpecialPowers.spawn(browser, [], async () => {
     52    await new Promise(r => {
     53      content.requestAnimationFrame(() => {
     54        content.requestAnimationFrame(r);
     55      });
     56    });
     57  });
     58 }
     59 
     60 add_task(async function test_text_editor_in_chrome() {
     61  await promiseResettingSearchBarAndFocus();
     62 
     63  let events = [];
     64  function logEvent(event) {
     65    events.push(event);
     66  }
     67  searchBar.inputField.addEventListener("beforeinput", logEvent);
     68  gDOMWindowUtils.sendContentCommandEvent("insertText", null, "XYZ");
     69 
     70  is(
     71    searchBar.inputField.value,
     72    "XYZ",
     73    "The string should be inserted into the focused search bar"
     74  );
     75  is(
     76    events.length,
     77    1,
     78    "One beforeinput event should be fired in the searchbar"
     79  );
     80  is(events[0]?.inputType, "insertText", 'inputType should be "insertText"');
     81  is(events[0]?.data, "XYZ", 'inputType should be "XYZ"');
     82  is(events[0]?.cancelable, true, "beforeinput event should be cancelable");
     83  searchBar.inputField.removeEventListener("beforeinput", logEvent);
     84 
     85  searchBar.inputField.blur();
     86 });
     87 
     88 add_task(async function test_text_editor_in_content() {
     89  for (const test of [
     90    {
     91      tag: "input",
     92      target: "input",
     93      nonTarget: "input + input",
     94      page: 'data:text/html,<input value="abc"><input value="def">',
     95    },
     96    {
     97      tag: "textarea",
     98      target: "textarea",
     99      nonTarget: "textarea + textarea",
    100      page: "data:text/html,<textarea>abc</textarea><textarea>def</textarea>",
    101    },
    102  ]) {
    103    // Once, move focus to chrome's searchbar.
    104    await promiseResettingSearchBarAndFocus();
    105 
    106    await BrowserTestUtils.withNewTab(test.page, async browser => {
    107      await SpecialPowers.spawn(browser, [test], async function (aTest) {
    108        content.window.focus();
    109        await ContentTaskUtils.waitForCondition(() =>
    110          content.document.hasFocus()
    111        );
    112        const target = content.document.querySelector(aTest.target);
    113        target.focus();
    114        target.selectionStart = target.selectionEnd = 2;
    115        content.document.documentElement.scrollTop; // Flush pending things
    116      });
    117 
    118      await promiseIMEStateEnabledByRemote();
    119      const waitForBeforeInputEvent = SpecialPowers.spawn(
    120        browser,
    121        [test],
    122        async function (aTest) {
    123          await new Promise(resolve => {
    124            content.document.querySelector(aTest.target).addEventListener(
    125              "beforeinput",
    126              event => {
    127                is(
    128                  event.inputType,
    129                  "insertText",
    130                  `The inputType of beforeinput event fired on <${aTest.target}> should be "insertText"`
    131                );
    132                is(
    133                  event.data,
    134                  "XYZ",
    135                  `The data of beforeinput event fired on <${aTest.target}> should be "XYZ"`
    136                );
    137                is(
    138                  event.cancelable,
    139                  true,
    140                  `The beforeinput event fired on <${aTest.target}> should be cancelable`
    141                );
    142                resolve();
    143              },
    144              { once: true }
    145            );
    146          });
    147        }
    148      );
    149      const waitForInputEvent = BrowserTestUtils.waitForContentEvent(
    150        browser,
    151        "input"
    152      );
    153      await promiseContentTick(browser); // Ensure "input" event listener in the remote process
    154      gDOMWindowUtils.sendContentCommandEvent("insertText", null, "XYZ");
    155      await waitForBeforeInputEvent;
    156      await waitForInputEvent;
    157 
    158      await SpecialPowers.spawn(browser, [test], async function (aTest) {
    159        is(
    160          content.document.querySelector(aTest.target).value,
    161          "abXYZc",
    162          `The string should be inserted into the focused <${aTest.target}> element`
    163        );
    164        is(
    165          content.document.querySelector(aTest.nonTarget).value,
    166          "def",
    167          `The string should not be inserted into the non-focused <${aTest.nonTarget}> element`
    168        );
    169      });
    170    });
    171 
    172    is(
    173      searchBar.inputField.value,
    174      "",
    175      "The string should not be inserted into the previously focused search bar"
    176    );
    177  }
    178 });
    179 
    180 add_task(async function test_html_editor_in_content() {
    181  for (const test of [
    182    {
    183      mode: "contenteditable",
    184      target: "div",
    185      page: "data:text/html,<div contenteditable>abc</div>",
    186    },
    187    {
    188      mode: "designMode",
    189      target: "div",
    190      page: "data:text/html,<div>abc</div>",
    191    },
    192  ]) {
    193    // Once, move focus to chrome's searchbar.
    194    await promiseResettingSearchBarAndFocus();
    195 
    196    await BrowserTestUtils.withNewTab(test.page, async browser => {
    197      await SpecialPowers.spawn(browser, [test], async function (aTest) {
    198        content.window.focus();
    199        await ContentTaskUtils.waitForCondition(() =>
    200          content.document.hasFocus()
    201        );
    202        const target = content.document.querySelector(aTest.target);
    203        if (aTest.mode == "designMode") {
    204          content.document.designMode = "on";
    205          content.window.focus();
    206        } else {
    207          target.focus();
    208        }
    209        content.window.getSelection().collapse(target.firstChild, 2);
    210        content.document.documentElement.scrollTop; // Flush pending things
    211      });
    212 
    213      await promiseIMEStateEnabledByRemote();
    214      const waitForBeforeInputEvent = SpecialPowers.spawn(
    215        browser,
    216        [test],
    217        async function (aTest) {
    218          await new Promise(resolve => {
    219            const eventTarget =
    220              aTest.mode === "designMode"
    221                ? content.document
    222                : content.document.querySelector(aTest.target);
    223            eventTarget.addEventListener(
    224              "beforeinput",
    225              event => {
    226                is(
    227                  event.inputType,
    228                  "insertText",
    229                  `The inputType of beforeinput event fired on ${aTest.mode} editor should be "insertText"`
    230                );
    231                is(
    232                  event.data,
    233                  "XYZ",
    234                  `The data of beforeinput event fired on ${aTest.mode} editor should be "XYZ"`
    235                );
    236                is(
    237                  event.cancelable,
    238                  true,
    239                  `The beforeinput event fired on ${aTest.mode} editor should be cancelable`
    240                );
    241                resolve();
    242              },
    243              { once: true }
    244            );
    245          });
    246        }
    247      );
    248      const waitForInputEvent = BrowserTestUtils.waitForContentEvent(
    249        browser,
    250        "input"
    251      );
    252      await promiseContentTick(browser); // Ensure "input" event listener in the remote process
    253      gDOMWindowUtils.sendContentCommandEvent("insertText", null, "XYZ");
    254      await waitForBeforeInputEvent;
    255      await waitForInputEvent;
    256 
    257      await SpecialPowers.spawn(browser, [test], async function (aTest) {
    258        is(
    259          content.document.querySelector(aTest.target).innerHTML,
    260          "abXYZc",
    261          `The string should be inserted into the focused ${aTest.mode} editor`
    262        );
    263      });
    264    });
    265 
    266    is(
    267      searchBar.inputField.value,
    268      "",
    269      "The string should not be inserted into the previously focused search bar"
    270    );
    271  }
    272 });