diff --git a/src/core/worker.js b/src/core/worker.js index 43f135189..35c36d4ea 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -308,6 +308,27 @@ class WorkerMessageHandler { return promise; } + async function getPassword(ex) { + if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { + assert( + ex instanceof PasswordException, + "getPassword - must be a `PasswordException`." + ); + } + const task = new WorkerTask(`PasswordException: response ${ex.code}`); + startWorkerTask(task); + + try { + const res = await handler.sendWithPromise("PasswordRequest", ex); + return res.password; + } finally { + // Ensure that any `catch` handler runs *before* removing the task. + Promise.resolve().then(() => { + finishWorkerTask(task); + }); + } + } + function setupDoc(data) { function onSuccess(doc) { ensureNotTerminated(); @@ -320,18 +341,12 @@ class WorkerMessageHandler { } if (ex instanceof PasswordException) { - const task = new WorkerTask(`PasswordException: response ${ex.code}`); - startWorkerTask(task); - - handler - .sendWithPromise("PasswordRequest", ex) - .then(function ({ password }) { - finishWorkerTask(task); + getPassword(ex) + .then(password => { pdfManager.updatePassword(password); pdfManagerReady(); }) - .catch(function () { - finishWorkerTask(task); + .catch(() => { handler.send("DocException", ex); }); } else { @@ -448,36 +463,23 @@ class WorkerMessageHandler { * Unique attachment identifier (required). */ async function (id) { + let passwordEx; + // Loop to prompt again after an incorrect password. while (true) { + const password = passwordEx ? await getPassword(passwordEx) : null; + try { + if (password) { + pdfManager.updatePassword(password); + } return await pdfManager.ensureCatalog("attachmentContent", [id]); - } catch (error) { - if (!(error instanceof PasswordException)) { - throw error; - } - - const task = new WorkerTask( - `PasswordException: response ${error.code}` - ); - startWorkerTask(task); - - try { - const { password } = await handler.sendWithPromise( - "PasswordRequest", - error - ); - try { - pdfManager.updatePassword(password); - } catch (exception) { - if (exception instanceof PasswordException) { - continue; - } - throw exception; - } - } finally { - finishWorkerTask(task); + } catch (ex) { + if (ex instanceof PasswordException) { + passwordEx = ex; + continue; } + throw ex; } } } @@ -648,23 +650,12 @@ class WorkerMessageHandler { warn("extractPages: XRefParseException."); } } else if (e instanceof PasswordException) { - const task = new WorkerTask( - `PasswordException: response ${e.code}` - ); - - startWorkerTask(task); - try { - const { password } = await handler.sendWithPromise( - "PasswordRequest", - e - ); + const password = await getPassword(e); manager.updatePassword(password); } catch { isValid = false; warn("extractPages: invalid password."); - } finally { - finishWorkerTask(task); } } else { isValid = false; diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index 4c69160fb..441421de6 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -1814,8 +1814,9 @@ describe("api", function () { it("gets encrypted attachments when password is requested on demand", async function () { const loadingTask = getDocument( - buildGetDocumentParams("encrypted-attachment.pdf") + buildGetDocumentParams("auth-event-ef-open.pdf") ); + const pdfDoc = await loadingTask.promise; let passwordRequests = 0; loadingTask.onPassword = (updatePassword, reason) => { @@ -1824,8 +1825,6 @@ describe("api", function () { updatePassword("000000"); }; - const pdfDoc = await loadingTask.promise; - const attachments = await pdfDoc.getAttachments(); const { description, filename, rawFilename } = attachments.get("attachment.pdf"); @@ -1843,8 +1842,9 @@ describe("api", function () { it("re-prompts for encrypted attachments after incorrect passwords", async function () { const loadingTask = getDocument( - buildGetDocumentParams("encrypted-attachment.pdf") + buildGetDocumentParams("auth-event-ef-open.pdf") ); + const pdfDoc = await loadingTask.promise; /** @type {Array} */ const reasons = []; @@ -1858,8 +1858,6 @@ describe("api", function () { updatePassword("000000"); }; - const pdfDoc = await loadingTask.promise; - const attachments = await pdfDoc.getAttachments(); const { description, filename, rawFilename } = attachments.get("attachment.pdf"); @@ -1886,24 +1884,21 @@ describe("api", function () { ); let embeddedLoadingTask = null; - try { - const pdfDoc = await loadingTask.promise; - const attachments = await pdfDoc.getAttachments(); - const attachment = attachments.get("attachment.pdf"); + const pdfDoc = await loadingTask.promise; + const attachments = await pdfDoc.getAttachments(); + const attachment = attachments.get("attachment.pdf"); - expect(attachment).toBeDefined(); - expect(attachment.filename).toEqual("attachment.pdf"); + expect(attachment).toBeDefined(); + expect(attachment.filename).toEqual("attachment.pdf"); - const content = await pdfDoc.getAttachmentContent("attachment.pdf"); - expect(content).toBeInstanceOf(Uint8Array); + const content = await pdfDoc.getAttachmentContent("attachment.pdf"); + expect(content).toBeInstanceOf(Uint8Array); - embeddedLoadingTask = getDocument({ data: content }); - const embeddedPdfDoc = await embeddedLoadingTask.promise; - expect(embeddedPdfDoc.numPages).toBe(1); - } finally { - await embeddedLoadingTask?.destroy(); - await loadingTask.destroy(); - } + embeddedLoadingTask = getDocument({ data: content }); + const embeddedPdfDoc = await embeddedLoadingTask.promise; + expect(embeddedPdfDoc.numPages).toBe(1); + + await Promise.all([embeddedLoadingTask.destroy(), loadingTask.destroy()]); }); it("gets javascript with printing instructions (JS action)", async function () {