diff --git a/src/display/api.js b/src/display/api.js index 815e7ee23..84f7c2d83 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -629,39 +629,48 @@ const isValidExplicitDest = _isValidExplicitDest.bind( class PDFDocumentLoadingTask { static #docId = 0; - constructor() { - this._capability = Promise.withResolvers(); - this._transport = null; - this._worker = null; + /** + * @private + */ + _capability = Promise.withResolvers(); - /** - * Unique identifier for the document loading task. - * @type {string} - */ - this.docId = `d${PDFDocumentLoadingTask.#docId++}`; + /** + * @private + */ + _transport = null; - /** - * Whether the loading task is destroyed or not. - * @type {boolean} - */ - this.destroyed = false; + /** + * @private + */ + _worker = null; - /** - * Callback to request a password if a wrong or no password was provided. - * The callback receives two parameters: a function that should be called - * with the new password, and a reason (see {@link PasswordResponses}). - * @type {function} - */ - this.onPassword = null; + /** + * Unique identifier for the document loading task. + * @type {string} + */ + docId = `d${PDFDocumentLoadingTask.#docId++}`; - /** - * Callback to be able to monitor the loading progress of the PDF file - * (necessary to implement e.g. a loading bar). - * The callback receives an {@link OnProgressParameters} argument. - * @type {function} - */ - this.onProgress = null; - } + /** + * Whether the loading task is destroyed or not. + * @type {boolean} + */ + destroyed = false; + + /** + * Callback to request a password if a wrong or no password was provided. + * The callback receives two parameters: a function that should be called + * with the new password, and a reason (see {@link PasswordResponses}). + * @type {function} + */ + onPassword = null; + + /** + * Callback to be able to monitor the loading progress of the PDF file + * (necessary to implement e.g. a loading bar). + * The callback receives an {@link OnProgressParameters} argument. + * @type {function} + */ + onProgress = null; /** * Promise for document loading task completion. @@ -699,6 +708,16 @@ class PDFDocumentLoadingTask { this._worker?.destroy(); this._worker = null; } + + /** + * Attempt to fetch the raw data of the PDF document, when e.g. + * - An exception was thrown during document initialization. + * - An `onPassword` callback is delaying initialization. + * @returns {Promise} + */ + async getData() { + return this._transport.getData(); + } } /** diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index 3acc4c38f..2f0fdc040 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -828,6 +828,47 @@ describe("api", function () { await loadingTask.destroy(); }); + + it("gets data, on failure, from `PDFDocumentLoadingTask`-instance", async function () { + const typedArrayPdf = await DefaultFileReaderFactory.fetch({ + path: TEST_PDFS_PATH + "issue6010_1.pdf", + }); + + // Sanity check to make sure that we fetched the entire PDF file. + expect(typedArrayPdf instanceof Uint8Array).toEqual(true); + expect(typedArrayPdf.length).toEqual(1116); + + const loadingTask = getDocument(typedArrayPdf.slice()); + expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true); + + let passwordData = null; + // Attach the callback that is used to request a password; + // similarly to how the default viewer handles passwords. + loadingTask.onPassword = async (updatePassword, reason) => { + passwordData = await loadingTask.getData(); + + updatePassword(new Error("Should reject the loadingTask.")); + }; + + try { + await loadingTask.promise; + + // Shouldn't get here. + expect(false).toEqual(true); + } catch (ex) { + expect(ex instanceof PasswordException).toEqual(true); + expect(ex.code).toEqual(PasswordResponses.NEED_PASSWORD); + } + + // Ensure that the raw PDF document can be fetched while + // an `onPassword` callback is delaying initialization... + expect(passwordData).toEqual(typedArrayPdf); + // ... and once an exception has stopped initialization. + const data = await loadingTask.getData(); + expect(data).toEqual(typedArrayPdf); + + await loadingTask.destroy(); + }); }); describe("PDFWorker", function () { diff --git a/web/app.js b/web/app.js index 05cb832f3..eed21ddef 100644 --- a/web/app.js +++ b/web/app.js @@ -1153,7 +1153,9 @@ const PDFViewerApplication = { async download() { let data; try { - data = await this.pdfDocument.getData(); + data = await (this.pdfDocument + ? this.pdfDocument.getData() + : this.pdfLoadingTask.getData()); } catch { // When the PDF document isn't ready, simply download using the URL. }