From 43dd2781a0dc9dec7786c8517854ec3b9fee61de Mon Sep 17 00:00:00 2001 From: Jonas Jenwald Date: Sun, 7 Jun 2026 11:28:57 +0200 Subject: [PATCH] Prevent intermittent issues when invoking the `PDFPageView.prototype.#injectLinkAnnotations` method Looking at the coverage data there are cases where we attempt to insert inferred link-annotations *before* the annotationLayer has rendered, see [here](https://app.codecov.io/gh/mozilla/pdf.js/commit/23483658744d35fd56cf71b9129cd7d8718ecafd/blob/web/annotation_layer_builder.js?dropdown=coverage#L246), which shouldn't happen and why that's treated as an Error. This is most likely caused by the asynchronicity of all the relevant code, since the `Autolinker` functionality can only be invoked after both the annotationLayer *and* the textLayer have finished rendering. Given that those operations are asynchronous, by the time that they complete it's possible that the annotationLayer (and also the textLayer) has been replaced by a new instance. In that case we might thus attempt to inject inferred link-annotations before the "new" annotationLayer has rendered. To avoid this intermittent issue, we now ensure that the annotationLayer and textLayer haven't changed between those layers rendering and the `Autolinker` functionality being invoked. (If they did change, then a future `render` call will trigger the inferred link-annotations handling). --- web/pdf_page_view.js | 37 ++++++++++++++++--------------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 965fc29ab..58958f3d1 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -461,7 +461,9 @@ class PDFPageView extends BasePDFPageView { }); } - async #renderAnnotationLayer() { + async #renderAnnotationLayer(textLayerPromise = null) { + const { annotationLayer, textLayer } = this; + let error = null; try { await this.annotationLayer.render({ @@ -476,6 +478,10 @@ class PDFPageView extends BasePDFPageView { } finally { this.#dispatchLayerRendered("annotationlayerrendered", error); } + + if (this.#enableAutoLinking && textLayerPromise) { + this.#injectLinkAnnotations(textLayerPromise, annotationLayer, textLayer); + } } async #renderAnnotationEditorLayer() { @@ -532,9 +538,6 @@ class PDFPageView extends BasePDFPageView { } async #renderTextLayer() { - if (!this.textLayer) { - return; - } let error = null; try { await this.textLayer.render({ @@ -568,10 +571,6 @@ class PDFPageView extends BasePDFPageView { * aria-owns to work. */ async #renderStructTreeLayer() { - if (!this.textLayer) { - return; - } - const treeDom = await this.structTreeLayer?.render(); if (treeDom) { this.l10n.pause(); @@ -595,24 +594,25 @@ class PDFPageView extends BasePDFPageView { this._textHighlighter.enable(); } - async #injectLinkAnnotations(textLayerPromise) { + async #injectLinkAnnotations(textLayerPromise, annotationLayer, textLayer) { let error = null; try { await textLayerPromise; - if (!this.annotationLayer) { + if ( + annotationLayer !== this.annotationLayer || + textLayer !== this.textLayer + ) { return; // Rendering was cancelled while the textLayerPromise resolved. } - await this.annotationLayer.injectLinkAnnotations( + await annotationLayer.injectLinkAnnotations( Autolinker.processLinks(this) ); } catch (ex) { console.error("#injectLinkAnnotations:", ex); error = ex; } - if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { - this.#dispatchLayerRendered("linkannotationsadded", error); - } + this.#dispatchLayerRendered("linkannotationsadded", error); } _resetCanvas() { @@ -1192,14 +1192,10 @@ class PDFPageView extends BasePDFPageView { viewport.rawDims ); - const textLayerPromise = this.#renderTextLayer(); + const textLayerPromise = this.textLayer ? this.#renderTextLayer() : null; if (this.annotationLayer) { - await this.#renderAnnotationLayer(); - - if (this.#enableAutoLinking && this.annotationLayer && this.textLayer) { - await this.#injectLinkAnnotations(textLayerPromise); - } + await this.#renderAnnotationLayer(textLayerPromise); } this.drawLayer ||= new DrawLayerBuilder({ @@ -1216,7 +1212,6 @@ class PDFPageView extends BasePDFPageView { if (!annotationEditorUIManager) { return; } - if ( this.annotationLayer || this.#annotationMode === AnnotationMode.DISABLE