[api-minor] Ensure that PDFDocumentProxy.hasJSActions won't fail if MissingDataExceptions are thrown during the associated worker-thread parsing

With the current implementation of `PDFDocument.hasJSActions`, in the worker-thread, we're not actually handling not-yet-loaded data correctly. This can thus fail in *two* different ways:
 - The `PDFDocument.fieldObjects` getter (and its helper method), while it may *return* a Promise, still fetches all of its data synchronously and it can thus throw a `MissingDataException` during parsing.
 - The `Catalog.jsActions` getter, which is completely synchronous, can obviously throw a `MissingDataException` during parsing.

If either of these cases occur currently, the `PDFDocumentProxy.hasJSActions` method in the API can either return a *rejected* Promise (which it never should) or possibly "hang" and never resolve.

*Please note:* While I've not *yet* seen this error in an actual PDF document, it can happen during loading if you're unlucky enough with e.g. the structure of the PDF document and/or the download speed offered by the server.
This patch is thus based on code-inspection *and* on manually throwing a `MissingDataException` on the first access of `Catalog.jsActions` to simulate this situation.

Finally, this patch adds a couple of *API* unit-tests for this (since none existed).
This commit is contained in:
Jonas Jenwald 2021-04-13 12:30:20 +02:00
parent 4aa27cc645
commit 2b2234fd5a
2 changed files with 39 additions and 13 deletions

View File

@ -1182,19 +1182,28 @@ class PDFDocument {
}
get hasJSActions() {
return shadow(
this,
"hasJSActions",
this.fieldObjects.then(fieldObjects => {
return (
(fieldObjects !== null &&
Object.values(fieldObjects).some(fieldObject =>
fieldObject.some(object => object.actions !== null)
)) ||
!!this.catalog.jsActions
);
})
);
const promise = this.pdfManager.ensure(this, "_parseHasJSActions");
return shadow(this, "hasJSActions", promise);
}
/**
* @private
*/
async _parseHasJSActions() {
const [catalogJsActions, fieldObjects] = await Promise.all([
this.pdfManager.ensureCatalog("jsActions"),
this.pdfManager.ensure(this, "fieldObjects"),
]);
if (catalogJsActions) {
return true;
}
if (fieldObjects) {
return Object.values(fieldObjects).some(fieldObject =>
fieldObject.some(object => object.actions !== null)
);
}
return false;
}
get calculationOrderIds() {

View File

@ -1014,6 +1014,23 @@ describe("api", function () {
.catch(done.fail);
});
it("gets hasJSActions, in document without javaScript", async function () {
const hasJSActions = await pdfDocument.hasJSActions();
expect(hasJSActions).toEqual(false);
});
it("gets hasJSActions, in document with javaScript", async function () {
const loadingTask = getDocument(
buildGetDocumentParams("doc_actions.pdf")
);
const pdfDoc = await loadingTask.promise;
const hasJSActions = await pdfDoc.hasJSActions();
expect(hasJSActions).toEqual(true);
await loadingTask.destroy();
});
it("gets JSActions (none)", function (done) {
const promise = pdfDocument.getJSActions();
promise