diff --git a/web/pdf_attachment_viewer.js b/web/pdf_attachment_viewer.js index 529c77121..0ba8715ff 100644 --- a/web/pdf_attachment_viewer.js +++ b/web/pdf_attachment_viewer.js @@ -55,10 +55,14 @@ class PDFAttachmentViewer { this.container.textContent = ""; if (!keepRenderedCapability) { - // NOTE: The *only* situation in which the `_renderedCapability` should - // not be replaced is when appending file attachment annotations. + // The only situation in which the `_renderedCapability` should *not* be + // replaced is when appending FileAttachment annotations. this._renderedCapability = createPromiseCapability(); } + if (this._pendingDispatchEvent) { + clearTimeout(this._pendingDispatchEvent); + } + this._pendingDispatchEvent = null; } /** @@ -67,6 +71,25 @@ class PDFAttachmentViewer { _dispatchEvent(attachmentsCount) { this._renderedCapability.resolve(); + if (this._pendingDispatchEvent) { + clearTimeout(this._pendingDispatchEvent); + this._pendingDispatchEvent = null; + } + if (attachmentsCount === 0) { + // Delay the event when no "regular" attachments exist, to allow time for + // parsing of any FileAttachment annotations that may be present on the + // *initially* rendered page; this reduces the likelihood of temporarily + // disabling the attachmentsView when the `PDFSidebar` handles the event. + this._pendingDispatchEvent = setTimeout(() => { + this.eventBus.dispatch("attachmentsloaded", { + source: this, + attachmentsCount: 0, + }); + this._pendingDispatchEvent = null; + }); + return; + } + this.eventBus.dispatch("attachmentsloaded", { source: this, attachmentsCount, @@ -129,23 +152,22 @@ class PDFAttachmentViewer { * @param {PDFAttachmentViewerRenderParameters} params */ render({ attachments, keepRenderedCapability = false }) { - let attachmentsCount = 0; - if (this.attachments) { this.reset(keepRenderedCapability === true); } this.attachments = attachments || null; if (!attachments) { - this._dispatchEvent(attachmentsCount); + this._dispatchEvent(/* attachmentsCount = */ 0); return; } const names = Object.keys(attachments).sort(function (a, b) { return a.toLowerCase().localeCompare(b.toLowerCase()); }); - attachmentsCount = names.length; + const attachmentsCount = names.length; + const fragment = document.createDocumentFragment(); for (let i = 0; i < attachmentsCount; i++) { const item = attachments[names[i]]; const filename = removeNullCharacters(getFilenameFromUrl(item.filename)); @@ -164,8 +186,9 @@ class PDFAttachmentViewer { } div.appendChild(button); - this.container.appendChild(div); + fragment.appendChild(div); } + this.container.appendChild(fragment); this._dispatchEvent(attachmentsCount); } @@ -175,7 +198,12 @@ class PDFAttachmentViewer { * @private */ _appendAttachment({ id, filename, content }) { - this._renderedCapability.promise.then(() => { + const renderedPromise = this._renderedCapability.promise; + + renderedPromise.then(() => { + if (renderedPromise !== this._renderedCapability.promise) { + return; // The FileAttachment annotation belongs to a previous document. + } let attachments = this.attachments; if (!attachments) { diff --git a/web/pdf_sidebar.js b/web/pdf_sidebar.js index 9122d5d3c..512fbc850 100644 --- a/web/pdf_sidebar.js +++ b/web/pdf_sidebar.js @@ -445,31 +445,17 @@ class PDFSidebar { }); this.eventBus._on("attachmentsloaded", evt => { - if (evt.attachmentsCount) { - this.attachmentsButton.disabled = false; + const attachmentsCount = evt.attachmentsCount; + this.attachmentsButton.disabled = !attachmentsCount; + + if (attachmentsCount) { this._showUINotification(SidebarView.ATTACHMENTS); - return; + } else if (this.active === SidebarView.ATTACHMENTS) { + // If the attachments view was opened during document load, switch away + // from it if it turns out that the document has no attachments. + this.switchView(SidebarView.THUMBS); } - - // Attempt to avoid temporarily disabling, and switching away from, the - // attachment view for documents that do not contain proper attachments - // but *only* FileAttachment annotations. Hence we defer those operations - // slightly to allow time for parsing any FileAttachment annotations that - // may be present on the *initially* rendered page of the document. - Promise.resolve().then(() => { - if (this.attachmentsView.hasChildNodes()) { - // FileAttachment annotations were appended to the attachment view. - return; - } - this.attachmentsButton.disabled = true; - - if (this.active === SidebarView.ATTACHMENTS) { - // If the attachment view was opened during document load, switch away - // from it if it turns out that the document has no attachments. - this.switchView(SidebarView.THUMBS); - } - }); }); // Update the thumbnailViewer, if visible, when exiting presentation mode.