diff --git a/test/integration/autolinker_spec.mjs b/test/integration/autolinker_spec.mjs index 4492fa556..5a8321924 100644 --- a/test/integration/autolinker_spec.mjs +++ b/test/integration/autolinker_spec.mjs @@ -13,7 +13,15 @@ * limitations under the License. */ -import { closePages, loadAndWait } from "./test_utils.mjs"; +import { closePages, createPromise, loadAndWait } from "./test_utils.mjs"; + +function waitForLinkAnnotations(page) { + return createPromise(page, resolve => { + window.PDFViewerApplication.eventBus.on("linkannotationsadded", resolve, { + once: true, + }); + }); +} describe("autolinker", function () { describe("bug1019475_2.pdf", function () { @@ -38,6 +46,7 @@ describe("autolinker", function () { it("must appropriately add link annotations when relevant", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForLinkAnnotations(page); const url = await page.$$eval( ".annotationLayer > .linkAnnotation > a", annotations => annotations.map(a => a.href) @@ -73,6 +82,7 @@ describe("autolinker", function () { it("must not add links when unnecessary", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForLinkAnnotations(page); const linkIds = await page.$$eval( ".annotationLayer > .linkAnnotation > a", annotations => @@ -106,6 +116,7 @@ describe("autolinker", function () { it("must not add links that overlap even if the URLs are different", async () => { await Promise.all( pages.map(async ([browserName, page]) => { + await waitForLinkAnnotations(page); const linkIds = await page.$$eval( ".annotationLayer > .linkAnnotation > a", annotations => @@ -121,4 +132,50 @@ describe("autolinker", function () { ); }); }); + + describe("PR 19470", function () { + let pages; + + beforeAll(async () => { + pages = await loadAndWait( + "bug1019475_2.pdf", + ".annotationLayer", + null, + null, + { + enableAutoLinking: true, + } + ); + }); + + afterAll(async () => { + await closePages(pages); + }); + + it("must not repeatedly add link annotations redundantly", async () => { + await Promise.all( + pages.map(async ([browserName, page]) => { + await waitForLinkAnnotations(page); + let url = await page.$$eval( + ".annotationLayer > .linkAnnotation > a", + annotations => annotations.map(a => a.href) + ); + expect(url.length).withContext(`In ${browserName}`).toEqual(1); + + await page.evaluate(() => + window.PDFViewerApplication.pdfViewer.updateScale({ + drawingDelay: -1, + scaleFactor: 2, + }) + ); + await waitForLinkAnnotations(page); + url = await page.$$eval( + ".annotationLayer > .linkAnnotation > a", + annotations => annotations.map(a => a.href) + ); + expect(url.length).withContext(`In ${browserName}`).toEqual(1); + }) + ); + }); + }); }); diff --git a/web/annotation_layer_builder.js b/web/annotation_layer_builder.js index 24c4668c2..ba6ad0f9b 100644 --- a/web/annotation_layer_builder.js +++ b/web/annotation_layer_builder.js @@ -77,6 +77,8 @@ class AnnotationLayerBuilder { #eventAbortController = null; + #linksInjected = false; + /** * @param {AnnotationLayerBuilderOptions} options */ @@ -235,9 +237,10 @@ class AnnotationLayerBuilder { "`render` method must be called before `injectLinkAnnotations`." ); } - if (this._cancelled) { + if (this._cancelled || this.#linksInjected) { return; } + this.#linksInjected = true; const newLinks = this.#annotations.length ? this.#checkInferredLinks(inferredLinks)