[api-minor] Convert getOpenAction to return data in a Map

Compared to regular Objects there's a number of advantages to using Maps:
 - They support proper iteration.
 - They have a simple way to check for the existence of data.
 - They have a simple/efficient way to check the number of elements.

If this functionality was added today, I cannot imagine that we'd choose an Object for this data.
This commit is contained in:
Jonas Jenwald 2026-07-23 13:52:02 +02:00
parent 0c8f67059e
commit c474a97932
4 changed files with 15 additions and 24 deletions

View File

@ -19,7 +19,6 @@ import {
DocumentActionEventType, DocumentActionEventType,
FormatError, FormatError,
info, info,
objectSize,
PermissionFlag, PermissionFlag,
shadow, shadow,
stringToUTF8String, stringToUTF8String,
@ -1111,7 +1110,7 @@ class Catalog {
get openAction() { get openAction() {
const obj = this.#catDict.get("OpenAction"); const obj = this.#catDict.get("OpenAction");
const openAction = Object.create(null); const openAction = new Map();
if (obj instanceof Dict) { if (obj instanceof Dict) {
// Convert the OpenAction dictionary into a format that works with // Convert the OpenAction dictionary into a format that works with
@ -1123,18 +1122,14 @@ class Catalog {
Catalog.parseDestDictionary({ destDict, resultObj }); Catalog.parseDestDictionary({ destDict, resultObj });
if (Array.isArray(resultObj.dest)) { if (Array.isArray(resultObj.dest)) {
openAction.dest = resultObj.dest; openAction.set("dest", resultObj.dest);
} else if (resultObj.action) { } else if (resultObj.action) {
openAction.action = resultObj.action; openAction.set("action", resultObj.action);
} }
} else if (isValidExplicitDest(obj)) { } else if (isValidExplicitDest(obj)) {
openAction.dest = obj; openAction.set("dest", obj);
} }
return shadow( return shadow(this, "openAction", openAction.size ? openAction : null);
this,
"openAction",
objectSize(openAction) > 0 ? openAction : null
);
} }
/** /**

View File

@ -829,9 +829,9 @@ class PDFDocumentProxy {
} }
/** /**
* @returns {Promise<any | null>} A promise that is resolved with an {Array} * @returns {Promise<Map | null>} A promise that is resolved with a {Map}
* containing the destination, or `null` when no open action is present * containing a destination or action, or `null` when no open action is
* in the PDF. * present in the PDF.
*/ */
getOpenAction() { getOpenAction() {
return this._transport.getOpenAction(); return this._transport.getOpenAction();

View File

@ -1774,11 +1774,9 @@ describe("api", function () {
it("gets non-default open action (with destination)", async function () { it("gets non-default open action (with destination)", async function () {
const openAction = await pdfDocument.getOpenAction(); const openAction = await pdfDocument.getOpenAction();
expect(openAction.dest).toEqual([ expect(openAction).toEqual(
{ num: 15, gen: 0 }, new Map([["dest", [{ num: 15, gen: 0 }, { name: "FitH" }, null]]])
{ name: "FitH" }, );
null,
]);
expect(openAction.action).toBeUndefined(); expect(openAction.action).toBeUndefined();
}); });
@ -1796,16 +1794,14 @@ describe("api", function () {
const promise1 = loadingTask1.promise const promise1 = loadingTask1.promise
.then(pdfDoc => pdfDoc.getOpenAction()) .then(pdfDoc => pdfDoc.getOpenAction())
.then(function (openAction) { .then(function (openAction) {
expect(openAction.dest).toBeUndefined(); expect(openAction).toEqual(new Map([["action", "Print"]]));
expect(openAction.action).toEqual("Print");
return loadingTask1.destroy(); return loadingTask1.destroy();
}); });
const promise2 = loadingTask2.promise const promise2 = loadingTask2.promise
.then(pdfDoc => pdfDoc.getOpenAction()) .then(pdfDoc => pdfDoc.getOpenAction())
.then(function (openAction) { .then(function (openAction) {
expect(openAction.dest).toBeUndefined(); expect(openAction).toEqual(new Map([["action", "Print"]]));
expect(openAction.action).toEqual("Print");
return loadingTask2.destroy(); return loadingTask2.destroy();
}); });

View File

@ -1589,7 +1589,7 @@ const PDFViewerApplication = {
this._initializePdfHistory({ this._initializePdfHistory({
fingerprint: pdfDocument.fingerprints[0], fingerprint: pdfDocument.fingerprints[0],
viewOnLoad, viewOnLoad,
initialDest: openAction?.dest, initialDest: openAction?.get("dest"),
}); });
const initialBookmark = this.initialBookmark; const initialBookmark = this.initialBookmark;
@ -1788,7 +1788,7 @@ const PDFViewerApplication = {
if (pdfDocument !== this.pdfDocument) { if (pdfDocument !== this.pdfDocument) {
return; // The document was closed while the auto print data resolved. return; // The document was closed while the auto print data resolved.
} }
let triggerAutoPrint = openAction?.action === "Print"; let triggerAutoPrint = openAction?.get("action") === "Print";
if (jsActions) { if (jsActions) {
console.warn("Warning: JavaScript support is not enabled"); console.warn("Warning: JavaScript support is not enabled");