123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718 |
- /* Copyright 2022 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- const {
- closePages,
- getEditorSelector,
- getSelectedEditors,
- loadAndWait,
- waitForEvent,
- waitForSelectedEditor,
- waitForStorageEntries,
- } = require("./test_utils.js");
- const copyPaste = async page => {
- let promise = waitForEvent(page, "copy");
- await page.keyboard.down("Control");
- await page.keyboard.press("c");
- await page.keyboard.up("Control");
- await promise;
- await page.waitForTimeout(10);
- promise = waitForEvent(page, "paste");
- await page.keyboard.down("Control");
- await page.keyboard.press("v");
- await page.keyboard.up("Control");
- await promise;
- };
- describe("Editor", () => {
- describe("FreeText", () => {
- let pages;
- beforeAll(async () => {
- pages = await loadAndWait("aboutstacks.pdf", ".annotationEditorLayer");
- });
- afterAll(async () => {
- await closePages(pages);
- });
- it("must write a string in a FreeText editor", async () => {
- await Promise.all(
- pages.map(async ([browserName, page]) => {
- await page.click("#editorFreeText");
- const rect = await page.$eval(".annotationEditorLayer", el => {
- // With Chrome something is wrong when serializing a DomRect,
- // hence we extract the values and just return them.
- const { x, y } = el.getBoundingClientRect();
- return { x, y };
- });
- const data = "Hello PDF.js World !!";
- await page.mouse.click(rect.x + 100, rect.y + 100);
- await page.type(`${getEditorSelector(0)} .internal`, data);
- const editorRect = await page.$eval(getEditorSelector(0), el => {
- const { x, y, width, height } = el.getBoundingClientRect();
- return {
- x,
- y,
- width,
- height,
- };
- });
- // Commit.
- await page.mouse.click(
- editorRect.x,
- editorRect.y + 2 * editorRect.height
- );
- await waitForSelectedEditor(page, getEditorSelector(0));
- await waitForStorageEntries(page, 1);
- const content = await page.$eval(getEditorSelector(0), el =>
- el.innerText.trimEnd()
- );
- expect(content).withContext(`In ${browserName}`).toEqual(data);
- })
- );
- });
- it("must copy/paste", async () => {
- // Run sequentially to avoid clipboard issues.
- for (const [browserName, page] of pages) {
- const editorRect = await page.$eval(getEditorSelector(0), el => {
- const { x, y, width, height } = el.getBoundingClientRect();
- return { x, y, width, height };
- });
- // Select the editor created previously.
- await page.mouse.click(
- editorRect.x + editorRect.width / 2,
- editorRect.y + editorRect.height / 2
- );
- await waitForSelectedEditor(page, getEditorSelector(0));
- await copyPaste(page);
- await waitForStorageEntries(page, 2);
- const content = await page.$eval(getEditorSelector(0), el =>
- el.innerText.trimEnd()
- );
- let pastedContent = await page.$eval(getEditorSelector(1), el =>
- el.innerText.trimEnd()
- );
- expect(pastedContent).withContext(`In ${browserName}`).toEqual(content);
- await copyPaste(page);
- await waitForStorageEntries(page, 3);
- pastedContent = await page.$eval(getEditorSelector(2), el =>
- el.innerText.trimEnd()
- );
- expect(pastedContent).withContext(`In ${browserName}`).toEqual(content);
- }
- });
- it("must clear all", async () => {
- await Promise.all(
- pages.map(async ([browserName, page]) => {
- await page.keyboard.down("Control");
- await page.keyboard.press("a");
- await page.keyboard.up("Control");
- await page.keyboard.down("Control");
- await page.keyboard.press("Backspace");
- await page.keyboard.up("Control");
- for (const n of [0, 1, 2]) {
- const hasEditor = await page.evaluate(sel => {
- return !!document.querySelector(sel);
- }, getEditorSelector(n));
- expect(hasEditor).withContext(`In ${browserName}`).toEqual(false);
- }
- await waitForStorageEntries(page, 0);
- })
- );
- });
- it("must check that a paste has been undone", async () => {
- // Run sequentially to avoid clipboard issues.
- for (const [browserName, page] of pages) {
- const rect = await page.$eval(".annotationEditorLayer", el => {
- const { x, y } = el.getBoundingClientRect();
- return { x, y };
- });
- const data = "Hello PDF.js World !!";
- await page.mouse.click(rect.x + 100, rect.y + 100);
- await page.type(`${getEditorSelector(3)} .internal`, data);
- const editorRect = await page.$eval(getEditorSelector(3), el => {
- const { x, y, width, height } = el.getBoundingClientRect();
- return { x, y, width, height };
- });
- // Commit.
- await page.mouse.click(
- editorRect.x,
- editorRect.y + 2 * editorRect.height
- );
- // And select it again.
- await page.mouse.click(
- editorRect.x + editorRect.width / 2,
- editorRect.y + editorRect.height / 2
- );
- await waitForSelectedEditor(page, getEditorSelector(3));
- await copyPaste(page);
- let hasEditor = await page.evaluate(sel => {
- return !!document.querySelector(sel);
- }, getEditorSelector(4));
- expect(hasEditor).withContext(`In ${browserName}`).toEqual(true);
- await page.keyboard.down("Control");
- await page.keyboard.press("z");
- await page.keyboard.up("Control");
- await page.waitForTimeout(10);
- hasEditor = await page.evaluate(sel => {
- return !!document.querySelector(sel);
- }, getEditorSelector(4));
- expect(hasEditor).withContext(`In ${browserName}`).toEqual(false);
- for (let i = 0; i < 2; i++) {
- const promise = waitForEvent(page, "paste");
- await page.keyboard.down("Control");
- await page.keyboard.press("v");
- await page.keyboard.up("Control");
- await promise;
- await page.waitForTimeout(10);
- }
- let length = await page.evaluate(sel => {
- return document.querySelectorAll(sel).length;
- }, `${getEditorSelector(5)}, ${getEditorSelector(6)}`);
- expect(length).withContext(`In ${browserName}`).toEqual(2);
- for (let i = 0; i < 2; i++) {
- await page.keyboard.down("Control");
- await page.keyboard.press("z");
- await page.keyboard.up("Control");
- await page.waitForTimeout(10);
- }
- length = await page.evaluate(sel => {
- return document.querySelectorAll(sel).length;
- }, `${getEditorSelector(5)}, ${getEditorSelector(6)}`);
- expect(length).withContext(`In ${browserName}`).toEqual(0);
- }
- });
- it("must check that aria-owns is correct", async () => {
- await Promise.all(
- pages.map(async ([browserName, page]) => {
- const [stacksRect, oldAriaOwns] = await page.$eval(
- ".textLayer",
- el => {
- for (const span of el.querySelectorAll(
- `span[role="presentation"]`
- )) {
- if (span.innerText.includes("Stacks are simple to create")) {
- span.setAttribute("pdfjs", true);
- const { x, y, width, height } = span.getBoundingClientRect();
- return [
- { x, y, width, height },
- span.getAttribute("aria-owns"),
- ];
- }
- }
- return null;
- }
- );
- expect(oldAriaOwns).withContext(`In ${browserName}`).toEqual(null);
- const data = "Hello PDF.js World !!";
- await page.mouse.click(
- stacksRect.x + stacksRect.width + 1,
- stacksRect.y + stacksRect.height / 2
- );
- await page.type(`${getEditorSelector(7)} .internal`, data);
- // Commit.
- await page.keyboard.press("Escape");
- const ariaOwns = await page.$eval(".textLayer", el => {
- const span = el.querySelector(`span[pdfjs="true"]`);
- return span?.getAttribute("aria-owns") || null;
- });
- expect(ariaOwns.endsWith("_7-editor"))
- .withContext(`In ${browserName}`)
- .toEqual(true);
- })
- );
- });
- it("must check that right click doesn't select", async () => {
- await Promise.all(
- pages.map(async ([browserName, page]) => {
- const rect = await page.$eval(".annotationEditorLayer", el => {
- const { x, y } = el.getBoundingClientRect();
- return { x, y };
- });
- await page.keyboard.down("Control");
- await page.keyboard.press("a");
- await page.keyboard.up("Control");
- await page.keyboard.down("Control");
- await page.keyboard.press("Backspace");
- await page.keyboard.up("Control");
- const data = "Hello PDF.js World !!";
- await page.mouse.click(rect.x + 100, rect.y + 100);
- await page.type(`${getEditorSelector(8)} .internal`, data);
- const editorRect = await page.$eval(getEditorSelector(8), el => {
- const { x, y, width, height } = el.getBoundingClientRect();
- return { x, y, width, height };
- });
- // Commit.
- await page.keyboard.press("Escape");
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([8]);
- await page.keyboard.press("Escape");
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([]);
- await page.mouse.click(
- editorRect.x + editorRect.width / 2,
- editorRect.y + editorRect.height / 2
- );
- await waitForSelectedEditor(page, getEditorSelector(8));
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([8]);
- // Escape.
- await page.keyboard.press("Escape");
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([]);
- // TODO: uncomment that stuff once we've a way to dismiss
- // the context menu.
- /* await page.mouse.click(
- editorRect.x + editorRect.width / 2,
- editorRect.y + editorRect.height / 2,
- { button: "right" }
- ); */
- })
- );
- });
- });
- describe("FreeText (multiselection)", () => {
- let pages;
- beforeAll(async () => {
- pages = await loadAndWait("aboutstacks.pdf", ".annotationEditorLayer");
- });
- afterAll(async () => {
- await closePages(pages);
- });
- it("must select/unselect several editors and check copy, paste and delete operations", async () => {
- // Run sequentially to avoid clipboard issues.
- for (const [browserName, page] of pages) {
- await page.click("#editorFreeText");
- const rect = await page.$eval(".annotationEditorLayer", el => {
- // With Chrome something is wrong when serializing a DomRect,
- // hence we extract the values and just return them.
- const { x, y } = el.getBoundingClientRect();
- return { x, y };
- });
- const editorCenters = [];
- for (let i = 0; i < 4; i++) {
- const data = `FreeText ${i}`;
- await page.mouse.click(
- rect.x + (i + 1) * 100,
- rect.y + (i + 1) * 100
- );
- await page.type(`${getEditorSelector(i)} .internal`, data);
- const editorRect = await page.$eval(getEditorSelector(i), el => {
- const { x, y, width, height } = el.getBoundingClientRect();
- return {
- x,
- y,
- width,
- height,
- };
- });
- editorCenters.push({
- x: editorRect.x + editorRect.width / 2,
- y: editorRect.y + editorRect.height / 2,
- });
- // Commit.
- await page.mouse.click(
- editorRect.x,
- editorRect.y + 2 * editorRect.height
- );
- }
- await page.keyboard.down("Control");
- await page.keyboard.press("a");
- await page.keyboard.up("Control");
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([0, 1, 2, 3]);
- await page.keyboard.down("Control");
- await page.mouse.click(editorCenters[1].x, editorCenters[1].y);
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([0, 2, 3]);
- await page.mouse.click(editorCenters[2].x, editorCenters[2].y);
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([0, 3]);
- await page.mouse.click(editorCenters[1].x, editorCenters[1].y);
- await page.keyboard.up("Control");
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([0, 1, 3]);
- await copyPaste(page);
- // 0,1,3 are unselected and new pasted editors are selected.
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([4, 5, 6]);
- // No ctrl here, hence all are unselected and 2 is selected.
- await page.mouse.click(editorCenters[2].x, editorCenters[2].y);
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([2]);
- await page.mouse.click(editorCenters[1].x, editorCenters[1].y);
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([1]);
- await page.keyboard.down("Control");
- await page.mouse.click(editorCenters[3].x, editorCenters[3].y);
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([1, 3]);
- await page.keyboard.up("Control");
- // Delete 1 and 3.
- await page.keyboard.press("Backspace");
- await page.keyboard.down("Control");
- await page.keyboard.press("a");
- await page.keyboard.up("Control");
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([0, 2, 4, 5, 6]);
- // Create an empty editor.
- await page.mouse.click(rect.x + 700, rect.y + 100);
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([7]);
- // Set the focus to 2 and check that only 2 is selected.
- await page.mouse.click(editorCenters[2].x, editorCenters[2].y);
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([2]);
- // Create an empty editor.
- await page.mouse.click(rect.x + 700, rect.y + 100);
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([8]);
- // Dismiss it.
- await page.keyboard.press("Escape");
- // Select all.
- await page.keyboard.down("Control");
- await page.keyboard.press("a");
- await page.keyboard.up("Control");
- // Check that all the editors are correctly selected (and the focus
- // didn't move to the body when the empty editor was removed).
- expect(await getSelectedEditors(page))
- .withContext(`In ${browserName}`)
- .toEqual([0, 2, 4, 5, 6]);
- }
- });
- });
- describe("FreeText (bugs)", () => {
- let pages;
- beforeAll(async () => {
- pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer");
- });
- afterAll(async () => {
- await closePages(pages);
- });
- it("must serialize invisible annotations", async () => {
- await Promise.all(
- pages.map(async ([browserName, page]) => {
- await page.click("#editorFreeText");
- let currentId = 0;
- const expected = [];
- const oneToFourteen = [...new Array(14).keys()].map(x => x + 1);
- for (const pageNumber of oneToFourteen) {
- const pageSelector = `.page[data-page-number = "${pageNumber}"]`;
- await page.evaluate(selector => {
- const element = window.document.querySelector(selector);
- element.scrollIntoView();
- }, pageSelector);
- const annotationLayerSelector = `${pageSelector} > .annotationEditorLayer`;
- await page.waitForSelector(annotationLayerSelector, {
- visible: true,
- timeout: 0,
- });
- await page.waitForTimeout(50);
- if (![1, 14].includes(pageNumber)) {
- continue;
- }
- const rect = await page.$eval(annotationLayerSelector, el => {
- // With Chrome something is wrong when serializing a DomRect,
- // hence we extract the values and just return them.
- const { x, y } = el.getBoundingClientRect();
- return { x, y };
- });
- const data = `Hello PDF.js World !! on page ${pageNumber}`;
- expected.push(data);
- await page.mouse.click(rect.x + 100, rect.y + 100);
- await page.type(`${getEditorSelector(currentId)} .internal`, data);
- // Commit.
- await page.keyboard.press("Escape");
- await page.waitForTimeout(10);
- await waitForSelectedEditor(page, getEditorSelector(currentId));
- await waitForStorageEntries(page, currentId + 1);
- const content = await page.$eval(getEditorSelector(currentId), el =>
- el.innerText.trimEnd()
- );
- expect(content).withContext(`In ${browserName}`).toEqual(data);
- currentId += 1;
- await page.waitForTimeout(10);
- }
- const serialize = proprName =>
- page.evaluate(
- name =>
- [
- ...window.PDFViewerApplication.pdfDocument.annotationStorage.serializable.values(),
- ].map(x => x[name]),
- proprName
- );
- expect(await serialize("value"))
- .withContext(`In ${browserName}`)
- .toEqual(expected);
- expect(await serialize("fontSize"))
- .withContext(`In ${browserName}`)
- .toEqual([10, 10]);
- expect(await serialize("color"))
- .withContext(`In ${browserName}`)
- .toEqual([
- [0, 0, 0],
- [0, 0, 0],
- ]);
- // Increase the font size for all the annotations.
- // Select all.
- await page.keyboard.down("Control");
- await page.keyboard.press("a");
- await page.keyboard.up("Control");
- await page.waitForTimeout(10);
- page.evaluate(() => {
- window.PDFViewerApplication.eventBus.dispatch(
- "switchannotationeditorparams",
- {
- source: null,
- type: /* AnnotationEditorParamsType.FREETEXT_SIZE */ 1,
- value: 13,
- }
- );
- });
- await page.waitForTimeout(10);
- expect(await serialize("fontSize"))
- .withContext(`In ${browserName}`)
- .toEqual([13, 13]);
- // Change the colors for all the annotations.
- page.evaluate(() => {
- window.PDFViewerApplication.eventBus.dispatch(
- "switchannotationeditorparams",
- {
- source: null,
- type: /* AnnotationEditorParamsType.FREETEXT_COLOR */ 2,
- value: "#FF0000",
- }
- );
- });
- await page.waitForTimeout(10);
- expect(await serialize("color"))
- .withContext(`In ${browserName}`)
- .toEqual([
- [255, 0, 0],
- [255, 0, 0],
- ]);
- })
- );
- });
- });
- describe("issue 15789", () => {
- let pages;
- beforeAll(async () => {
- pages = await loadAndWait("issue15789.pdf", ".annotationEditorLayer");
- pages = await Promise.all(
- pages.map(async ([browserName, page]) => {
- await page.select("#scaleSelect", "1");
- return [browserName, page];
- })
- );
- });
- afterAll(async () => {
- await closePages(pages);
- });
- it("must take the media box into account", async () => {
- await Promise.all(
- pages.map(async ([browserName, page]) => {
- await page.click("#editorFreeText");
- let currentId = 0;
- for (let step = 0; step < 3; step++) {
- const rect = await page.$eval(".annotationEditorLayer", el => {
- // With Chrome something is wrong when serializing a DomRect,
- // hence we extract the values and just return them.
- const { x, y, width, height } = el.getBoundingClientRect();
- return { x, y, width, height };
- });
- const data = `Hello ${step}`;
- const x = rect.x + 0.1 * rect.width;
- const y = rect.y + 0.1 * rect.height;
- await page.mouse.click(x, y);
- await page.type(`${getEditorSelector(currentId)} .internal`, data);
- // Commit.
- await page.keyboard.press("Escape");
- await page.waitForTimeout(10);
- await page.evaluate(() => {
- document.getElementById("pageRotateCw").click();
- });
- currentId += 1;
- await page.waitForTimeout(10);
- }
- const serialize = proprName =>
- page.evaluate(
- name =>
- [
- ...window.PDFViewerApplication.pdfDocument.annotationStorage.serializable.values(),
- ].map(x => x[name]),
- proprName
- );
- const rects = (await serialize("rect")).map(rect =>
- rect.slice(0, 2).map(x => Math.floor(x))
- );
- const expected = [
- [-28, 695],
- [-38, -10],
- [501, -20],
- ];
- // Dimensions aren't exactly the same from a platform to an other
- // so we're a bit tolerant here with the numbers.
- // Anyway the goal is to check that the bottom left corner of the
- // media box is taken into account.
- // The pdf has a media box equals to [-99 -99 612.0 792.0].
- const diffs = rects.map(
- (rect, i) =>
- Math.abs(rect[0] - expected[i][0]) < 10 &&
- Math.abs(rect[1] - expected[i][1]) < 10
- );
- expect(diffs)
- .withContext(`In ${browserName}`)
- .toEqual([true, true, true]);
- })
- );
- });
- });
- });
|