Backed out changeset f594e6f00208 (bug 1940883) for causing crashes in bug 1941164.
[gecko.git] / toolkit / components / pdfjs / test / browser_pdfjs_editing_contextmenu.js
blobe2c8d097812b35503933d990c513c9ee1185679e
1 /* Any copyright is dedicated to the Public Domain.
2  * http://creativecommons.org/publicdomain/zero/1.0/ */
4 const RELATIVE_DIR = "toolkit/components/pdfjs/test/";
5 const TESTROOT = "http://example.com/browser/" + RELATIVE_DIR;
7 // This is a modified version from browser_contextmenuFillLogins.js.
8 async function openContextMenuAt(browser, x, y) {
9   const contextMenu = document.getElementById("contentAreaContextMenu");
11   const contextMenuShownPromise = BrowserTestUtils.waitForEvent(
12     contextMenu,
13     "popupshown"
14   );
16   // Synthesize a contextmenu event to actually open the context menu.
17   await BrowserTestUtils.synthesizeMouseAtPoint(
18     x,
19     y,
20     {
21       type: "contextmenu",
22       button: 2,
23     },
24     browser
25   );
27   await contextMenuShownPromise;
28   return contextMenu;
31 /**
32  * Open a context menu and get the pdfjs entries
33  * @param {Object} browser
34  * @param {Object} box
35  * @returns {Promise<Map<string,HTMLElement>>} the pdfjs menu entries.
36  */
37 function getContextMenuItems(browser, box) {
38   return new Promise(resolve => {
39     setTimeout(async () => {
40       const { x, y, width, height } = box;
41       const menuitems = [
42         "context-pdfjs-undo",
43         "context-pdfjs-redo",
44         "context-sep-pdfjs-redo",
45         "context-pdfjs-cut",
46         "context-pdfjs-copy",
47         "context-pdfjs-paste",
48         "context-pdfjs-delete",
49         "context-pdfjs-selectall",
50         "context-sep-pdfjs-selectall",
51         "context-pdfjs-highlight-selection",
52       ];
54       await openContextMenuAt(browser, x + width / 2, y + height / 2);
55       const results = new Map();
56       const doc = browser.ownerDocument;
57       for (const menuitem of menuitems) {
58         const item = doc.getElementById(menuitem);
59         results.set(menuitem, item || null);
60       }
62       resolve(results);
63     }, 0);
64   });
67 /**
68  * Open a context menu on the element corresponding to the given selector
69  * and returs the pdfjs menu entries.
70  * @param {Object} browser
71  * @param {string} selector
72  * @returns {Promise<Map<string,HTMLElement>>} the pdfjs menu entries.
73  */
74 async function getContextMenuItemsOn(browser, selector) {
75   const box = await SpecialPowers.spawn(
76     browser,
77     [selector],
78     async function (selector) {
79       const element = content.document.querySelector(selector);
80       const { x, y, width, height } = element.getBoundingClientRect();
81       return { x, y, width, height };
82     }
83   );
84   return getContextMenuItems(browser, box);
87 /**
88  * Hide the context menu.
89  * @param {Object} browser
90  */
91 async function hideContextMenu(browser) {
92   await new Promise(resolve =>
93     setTimeout(async () => {
94       const doc = browser.ownerDocument;
95       const contextMenu = doc.getElementById("contentAreaContextMenu");
97       const popupHiddenPromise = BrowserTestUtils.waitForEvent(
98         contextMenu,
99         "popuphidden"
100       );
101       contextMenu.hidePopup();
102       await popupHiddenPromise;
103       resolve();
104     }, 0)
105   );
108 async function clickOnItem(browser, items, entry) {
109   const editingPromise = BrowserTestUtils.waitForContentEvent(
110     browser,
111     "editingaction",
112     false,
113     null,
114     true
115   );
116   const contextMenu = document.getElementById("contentAreaContextMenu");
117   contextMenu.activateItem(items.get(entry));
118   await editingPromise;
122  * Asserts that the enabled pdfjs menuitems are the expected ones.
123  * @param {Map<string,HTMLElement>} menuitems
124  * @param {Array<string>} expected
125  */
126 function assertMenuitems(menuitems, expected) {
127   Assert.deepEqual(
128     [...menuitems.values()]
129       .filter(
130         elmt =>
131           !elmt.id.includes("-sep-") &&
132           !elmt.hidden &&
133           [null, "false"].includes(elmt.getAttribute("disabled"))
134       )
135       .map(elmt => elmt.id),
136     expected
137   );
140 async function waitAndCheckEmptyContextMenu(browser) {
141   // check that PDF is opened with internal viewer
142   await waitForPdfJSAllLayers(browser, TESTROOT + "file_pdfjs_test.pdf", [
143     ["annotationEditorLayer", "annotationLayer", "textLayer", "canvasWrapper"],
144     ["annotationEditorLayer", "textLayer", "canvasWrapper"],
145   ]);
147   const spanBox = await getSpanBox(browser, "and found references");
148   const menuitems = await getContextMenuItems(browser, spanBox);
150   // Nothing have been edited, hence the context menu doesn't contain any
151   // pdf entries.
152   Assert.ok(
153     [...menuitems.values()].every(elmt => elmt.hidden),
154     "No visible pdf menuitem"
155   );
156   await hideContextMenu(browser);
159 // Text copy, paste, undo, redo, delete and select all in using the context
160 // menu.
161 add_task(async function test_copy_paste_undo_redo() {
162   makePDFJSHandler();
164   await BrowserTestUtils.withNewTab(
165     { gBrowser, url: "about:blank" },
166     async function (browser) {
167       SpecialPowers.clipboardCopyString("");
169       await SpecialPowers.pushPrefEnv({
170         set: [["pdfjs.annotationEditorMode", 0]],
171       });
173       await waitAndCheckEmptyContextMenu(browser);
174       const spanBox = await getSpanBox(browser, "and found references");
176       await enableEditor(browser, "FreeText", 1);
177       await addFreeText(browser, "hello", spanBox);
179       // Unselect.
180       await escape(browser);
182       info("Wait for the editor to be unselected");
183       await BrowserTestUtils.waitForCondition(
184         async () => (await countElements(browser, ".selectedEditor")) !== 1
185       );
186       Assert.equal(await countElements(browser, ".selectedEditor"), 0);
188       let menuitems = await getContextMenuItems(browser, spanBox);
189       assertMenuitems(menuitems, [
190         "context-pdfjs-undo", // Last created editor is undoable
191         "context-pdfjs-selectall", // and selectable.
192       ]);
193       // Undo.
194       await clickOnItem(browser, menuitems, "context-pdfjs-undo");
196       await BrowserTestUtils.waitForCondition(
197         async () => (await countElements(browser, ".freeTextEditor")) !== 2
198       );
200       Assert.equal(
201         await countElements(browser, ".freeTextEditor"),
202         1,
203         "The FreeText editor must have been removed"
204       );
206       menuitems = await getContextMenuItems(browser, spanBox);
208       // The editor removed thanks to "undo" is now redoable
209       assertMenuitems(menuitems, [
210         "context-pdfjs-redo",
211         "context-pdfjs-selectall",
212       ]);
213       await clickOnItem(browser, menuitems, "context-pdfjs-redo");
215       await BrowserTestUtils.waitForCondition(
216         async () => (await countElements(browser, ".freeTextEditor")) !== 1
217       );
219       Assert.equal(
220         await countElements(browser, ".freeTextEditor"),
221         2,
222         "The FreeText editor must have been added back"
223       );
225       await clickOn(browser, "#pdfjs_internal_editor_0");
226       menuitems = await getContextMenuItemsOn(
227         browser,
228         "#pdfjs_internal_editor_0"
229       );
231       assertMenuitems(menuitems, [
232         "context-pdfjs-undo",
233         "context-pdfjs-cut",
234         "context-pdfjs-copy",
235         "context-pdfjs-delete",
236         "context-pdfjs-selectall",
237       ]);
239       await clickOnItem(browser, menuitems, "context-pdfjs-cut");
241       await BrowserTestUtils.waitForCondition(
242         async () => (await countElements(browser, ".freeTextEditor")) !== 2
243       );
245       Assert.equal(
246         await countElements(browser, ".freeTextEditor"),
247         1,
248         "The FreeText editor must have been cut"
249       );
251       menuitems = await getContextMenuItems(browser, spanBox);
252       assertMenuitems(menuitems, [
253         "context-pdfjs-undo",
254         "context-pdfjs-paste",
255         "context-pdfjs-selectall",
256       ]);
258       await clickOnItem(browser, menuitems, "context-pdfjs-paste");
260       await BrowserTestUtils.waitForCondition(
261         async () => (await countElements(browser, ".freeTextEditor")) !== 1
262       );
264       Assert.equal(
265         await countElements(browser, ".freeTextEditor"),
266         2,
267         "The FreeText editor must have been pasted"
268       );
270       await clickOn(browser, "#pdfjs_internal_editor_1");
271       menuitems = await getContextMenuItemsOn(
272         browser,
273         "#pdfjs_internal_editor_1"
274       );
276       assertMenuitems(menuitems, [
277         "context-pdfjs-undo",
278         "context-pdfjs-cut",
279         "context-pdfjs-copy",
280         "context-pdfjs-paste",
281         "context-pdfjs-delete",
282         "context-pdfjs-selectall",
283       ]);
285       await clickOnItem(browser, menuitems, "context-pdfjs-delete");
287       await BrowserTestUtils.waitForCondition(
288         async () => (await countElements(browser, ".freeTextEditor")) !== 2
289       );
291       Assert.equal(
292         await countElements(browser, ".freeTextEditor"),
293         1,
294         "The FreeText editor must have been deleted"
295       );
297       menuitems = await getContextMenuItems(browser, spanBox);
298       await clickOnItem(browser, menuitems, "context-pdfjs-paste");
300       await BrowserTestUtils.waitForCondition(
301         async () => (await countElements(browser, ".freeTextEditor")) !== 1
302       );
304       Assert.equal(
305         await countElements(browser, ".freeTextEditor"),
306         2,
307         "The FreeText editor must have been pasted"
308       );
310       await clickOn(browser, "#pdfjs_internal_editor_2");
311       menuitems = await getContextMenuItemsOn(
312         browser,
313         "#pdfjs_internal_editor_2"
314       );
316       await clickOnItem(browser, menuitems, "context-pdfjs-copy");
318       menuitems = await getContextMenuItemsOn(
319         browser,
320         "#pdfjs_internal_editor_2"
321       );
322       await clickOnItem(browser, menuitems, "context-pdfjs-paste");
324       await BrowserTestUtils.waitForCondition(
325         async () => (await countElements(browser, ".freeTextEditor")) !== 2
326       );
328       Assert.equal(
329         await countElements(browser, ".freeTextEditor"),
330         3,
331         "The FreeText editor must have been pasted"
332       );
334       menuitems = await getContextMenuItems(browser, spanBox);
335       await clickOnItem(browser, menuitems, "context-pdfjs-selectall");
336       menuitems = await getContextMenuItems(browser, spanBox);
337       await clickOnItem(browser, menuitems, "context-pdfjs-delete");
339       await BrowserTestUtils.waitForCondition(
340         async () => (await countElements(browser, ".freeTextEditor")) !== 3
341       );
343       Assert.equal(
344         await countElements(browser, ".freeTextEditor"),
345         0,
346         "All the FreeText editors must have been deleted"
347       );
349       await waitForPdfJSClose(browser);
350       await SpecialPowers.popPrefEnv();
351     }
352   );
355 add_task(async function test_highlight_selection() {
356   makePDFJSHandler();
358   await BrowserTestUtils.withNewTab(
359     { gBrowser, url: "about:blank" },
360     async function (browser) {
361       await SpecialPowers.pushPrefEnv({
362         set: [["pdfjs.annotationEditorMode", 0]],
363       });
365       await waitAndCheckEmptyContextMenu(browser);
366       const spanBox = await getSpanBox(browser, "and found references");
368       const changePromise = BrowserTestUtils.waitForContentEvent(
369         browser,
370         "annotationeditorstateschanged",
371         false,
372         null,
373         true
374       );
375       await clickAt(
376         browser,
377         spanBox.x + spanBox.width / 2,
378         spanBox.y + spanBox.height / 2,
379         2
380       );
381       await changePromise;
382       await TestUtils.waitForTick();
384       const mozBox = await getSpanBox(browser, "Mozilla automated testing");
385       const menuitems = await getContextMenuItems(browser, mozBox);
387       assertMenuitems(menuitems, ["context-pdfjs-highlight-selection"]);
389       const telemetryPromise = BrowserTestUtils.waitForContentEvent(
390         browser,
391         "reporttelemetry",
392         false,
393         null,
394         true
395       );
396       await clickOnItem(
397         browser,
398         menuitems,
399         "context-pdfjs-highlight-selection"
400       );
401       await telemetryPromise;
403       Assert.equal(
404         await countElements(browser, ".highlightEditor"),
405         1,
406         "An highlight editor must have been added"
407       );
409       await waitForPdfJSClose(browser);
410     }
411   );