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