diff --git a/src/display/editor/editor.js b/src/display/editor/editor.js index c0d4be0750e37..dff21f93d42f5 100644 --- a/src/display/editor/editor.js +++ b/src/display/editor/editor.js @@ -581,7 +581,11 @@ class AnnotationEditor { // undo/redo so we must commit it before. this.commit(); } - this.parent.remove(this); + if (this.parent) { + this.parent.remove(this); + } else { + this._uiManager.removeEditor(this); + } } /** @@ -638,6 +642,9 @@ class AnnotationEditor { */ set isEditing(value) { this.#isEditing = value; + if (!this.parent) { + return; + } if (value) { this.parent.setSelected(this); this.parent.setActiveEditor(this); diff --git a/src/display/editor/freetext.js b/src/display/editor/freetext.js index eee66d6060010..70b2268f93ac2 100644 --- a/src/display/editor/freetext.js +++ b/src/display/editor/freetext.js @@ -307,8 +307,10 @@ class FreeTextEditor extends AnnotationEditor { /** @inheritdoc */ remove() { this.isEditing = false; - this.parent.setEditingState(true); - this.parent.div.classList.add("freeTextEditing"); + if (this.parent) { + this.parent.setEditingState(true); + this.parent.div.classList.add("freeTextEditing"); + } super.remove(); } diff --git a/test/integration/freetext_editor_spec.js b/test/integration/freetext_editor_spec.js index 023c44f9352c9..a3c4633a53ee7 100644 --- a/test/integration/freetext_editor_spec.js +++ b/test/integration/freetext_editor_spec.js @@ -639,7 +639,7 @@ describe("FreeText Editor", () => { await page.click("#editorFreeText"); let currentId = 0; const expected = []; - const oneToFourteen = [...new Array(14).keys()].map(x => x + 1); + const oneToFourteen = Array.from(new Array(14).keys(), x => x + 1); for (const pageNumber of oneToFourteen) { const pageSelector = `.page[data-page-number = "${pageNumber}"]`; @@ -1262,4 +1262,88 @@ describe("FreeText Editor", () => { ); }); }); + + describe("FreeText (remove)", () => { + let pages; + + beforeAll(async () => { + pages = await loadAndWait("tracemonkey.pdf", ".annotationEditorLayer"); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must delete invisible annotations", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await page.click("#editorFreeText"); + let currentId = 0; + const oneToFourteen = Array.from(new Array(14).keys(), 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}`; + 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); + + currentId += 1; + } + + // Select all. + await page.keyboard.down("Control"); + await page.keyboard.press("a"); + await page.keyboard.up("Control"); + await page.waitForTimeout(10); + + const serialize = () => + page.evaluate(() => { + const { map } = + window.PDFViewerApplication.pdfDocument.annotationStorage + .serializable; + return map ? Array.from(map.values(), x => x.pageIndex) : []; + }); + + expect(await serialize()) + .withContext(`In ${browserName}`) + .toEqual([0, 13]); + + // Delete + await page.keyboard.press("Backspace"); + await page.waitForTimeout(10); + + expect(await serialize()) + .withContext(`In ${browserName}`) + .toEqual([]); + }) + ); + }); + }); });