1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-20 15:18:08 +02:00

[api-minor] Replace the PromiseCapability with Promise.withResolvers()

This replaces our custom `PromiseCapability`-class with the new native `Promise.withResolvers()` functionality, which does *almost* the same thing[1]; please see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers

The only difference is that `PromiseCapability` also had a `settled`-getter, which was however not widely used and the call-sites can either be removed or re-factored to avoid it. In particular:
 - In `src/display/api.js` we can tweak the `PDFObjects`-class to use a "special" initial data-value and just compare against that, in order to replace the `settled`-state.
 - In `web/app.js` we change the only case to manually track the `settled`-state, which should hopefully be OK given how this is being used.
 - In `web/pdf_outline_viewer.js` we can remove the `settled`-checks, since the code should work just fine without it. The only thing that could potentially happen is that we try to `resolve` a Promise multiple times, which is however *not* a problem since the value of a Promise cannot be changed once fulfilled or rejected.
 - In `web/pdf_viewer.js` we can remove the `settled`-checks, since the code should work fine without them:
     - For the `_onePageRenderedCapability` case the `settled`-check is used in a `EventBus`-listener which is *removed* on its first (valid) invocation.
     - For the `_pagesCapability` case the `settled`-check is used in a print-related helper that works just fine with "only" the other checks.
 - In `test/unit/api_spec.js` we can change the few relevant cases to manually track the `settled`-state, since this is both simple and *test-only* code.

---
[1] In browsers/environments that lack native support, note [the compatibility data](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/withResolvers#browser_compatibility), it'll be polyfilled via the `core-js` library (but only in `legacy` builds).
This commit is contained in:
Jonas Jenwald 2024-03-28 16:42:37 +01:00
parent 55db43966e
commit e4d0e84802
28 changed files with 159 additions and 252 deletions

View file

@ -46,7 +46,6 @@ import {
isPdfFile,
MissingPDFException,
PDFWorker,
PromiseCapability,
shadow,
UnexpectedResponseException,
version,
@ -92,7 +91,10 @@ const ViewOnLoad = {
const PDFViewerApplication = {
initialBookmark: document.location.hash.substring(1),
_initializedCapability: new PromiseCapability(),
_initializedCapability: {
...Promise.withResolvers(),
settled: false,
},
appConfig: null,
pdfDocument: null,
pdfLoadingTask: null,
@ -240,6 +242,7 @@ const PDFViewerApplication = {
this.bindEvents();
this.bindWindowEvents();
this._initializedCapability.settled = true;
this._initializedCapability.resolve();
},

View file

@ -35,39 +35,40 @@ const WaitOnType = {
* @param {WaitOnEventOrTimeoutParameters}
* @returns {Promise} A promise that is resolved with a {WaitOnType} value.
*/
function waitOnEventOrTimeout({ target, name, delay = 0 }) {
return new Promise(function (resolve, reject) {
if (
typeof target !== "object" ||
!(name && typeof name === "string") ||
!(Number.isInteger(delay) && delay >= 0)
) {
throw new Error("waitOnEventOrTimeout - invalid parameters.");
}
async function waitOnEventOrTimeout({ target, name, delay = 0 }) {
if (
typeof target !== "object" ||
!(name && typeof name === "string") ||
!(Number.isInteger(delay) && delay >= 0)
) {
throw new Error("waitOnEventOrTimeout - invalid parameters.");
}
const { promise, resolve } = Promise.withResolvers();
function handler(type) {
if (target instanceof EventBus) {
target._off(name, eventHandler);
} else {
target.removeEventListener(name, eventHandler);
}
if (timeout) {
clearTimeout(timeout);
}
resolve(type);
}
const eventHandler = handler.bind(null, WaitOnType.EVENT);
function handler(type) {
if (target instanceof EventBus) {
target._on(name, eventHandler);
target._off(name, eventHandler);
} else {
target.addEventListener(name, eventHandler);
target.removeEventListener(name, eventHandler);
}
const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
const timeout = setTimeout(timeoutHandler, delay);
});
if (timeout) {
clearTimeout(timeout);
}
resolve(type);
}
const eventHandler = handler.bind(null, WaitOnType.EVENT);
if (target instanceof EventBus) {
target._on(name, eventHandler);
} else {
target.addEventListener(name, eventHandler);
}
const timeoutHandler = handler.bind(null, WaitOnType.TIMEOUT);
const timeout = setTimeout(timeoutHandler, delay);
return promise;
}
/**

View file

@ -13,7 +13,7 @@
* limitations under the License.
*/
import { PasswordResponses, PromiseCapability } from "pdfjs-lib";
import { PasswordResponses } from "pdfjs-lib";
/**
* @typedef {Object} PasswordPromptOptions
@ -64,10 +64,8 @@ class PasswordPrompt {
}
async open() {
if (this.#activeCapability) {
await this.#activeCapability.promise;
}
this.#activeCapability = new PromiseCapability();
await this.#activeCapability?.promise;
this.#activeCapability = Promise.withResolvers();
try {
await this.overlayManager.open(this.dialog);

View file

@ -13,8 +13,8 @@
* limitations under the License.
*/
import { getFilenameFromUrl, PromiseCapability } from "pdfjs-lib";
import { BaseTreeViewer } from "./base_tree_viewer.js";
import { getFilenameFromUrl } from "pdfjs-lib";
import { waitOnEventOrTimeout } from "./event_utils.js";
/**
@ -50,7 +50,7 @@ class PDFAttachmentViewer extends BaseTreeViewer {
if (!keepRenderedCapability) {
// The only situation in which the `_renderedCapability` should *not* be
// replaced is when appending FileAttachment annotations.
this._renderedCapability = new PromiseCapability();
this._renderedCapability = Promise.withResolvers();
}
this._pendingDispatchEvent = false;
}

View file

@ -14,7 +14,7 @@
*/
import { getPageSizeInches, isPortraitOrientation } from "./ui_utils.js";
import { PDFDateString, PromiseCapability } from "pdfjs-lib";
import { PDFDateString } from "pdfjs-lib";
const DEFAULT_FIELD_CONTENT = "-";
@ -200,7 +200,7 @@ class PDFDocumentProperties {
this.pdfDocument = null;
this.#fieldData = null;
this._dataAvailableCapability = new PromiseCapability();
this._dataAvailableCapability = Promise.withResolvers();
this._currentPageNumber = 1;
this._pagesRotation = 0;
}

View file

@ -19,7 +19,6 @@
import { binarySearchFirstItem, scrollIntoView } from "./ui_utils.js";
import { getCharacterType, getNormalizeWithNFKC } from "./pdf_find_utils.js";
import { PromiseCapability } from "pdfjs-lib";
const FindState = {
FOUND: 0,
@ -576,7 +575,7 @@ class PDFFindController {
clearTimeout(this._findTimeout);
this._findTimeout = null;
this._firstPageCapability = new PromiseCapability();
this._firstPageCapability = Promise.withResolvers();
}
/**
@ -836,14 +835,14 @@ class PDFFindController {
return;
}
let promise = Promise.resolve();
let deferred = Promise.resolve();
const textOptions = { disableNormalization: true };
for (let i = 0, ii = this._linkService.pagesCount; i < ii; i++) {
const extractTextCapability = new PromiseCapability();
this._extractTextPromises[i] = extractTextCapability.promise;
const { promise, resolve } = Promise.withResolvers();
this._extractTextPromises[i] = promise;
// eslint-disable-next-line arrow-body-style
promise = promise.then(() => {
deferred = deferred.then(() => {
return this._pdfDocument
.getPage(i + 1)
.then(pdfPage => pdfPage.getTextContent(textOptions))
@ -864,7 +863,7 @@ class PDFFindController {
this._pageDiffs[i],
this._hasDiacritics[i],
] = normalize(strBuf.join(""));
extractTextCapability.resolve();
resolve();
},
reason => {
console.error(
@ -875,7 +874,7 @@ class PDFFindController {
this._pageContents[i] = "";
this._pageDiffs[i] = null;
this._hasDiacritics[i] = false;
extractTextCapability.resolve();
resolve();
}
);
});

View file

@ -14,7 +14,6 @@
*/
import { BaseTreeViewer } from "./base_tree_viewer.js";
import { PromiseCapability } from "pdfjs-lib";
import { SidebarView } from "./ui_utils.js";
/**
@ -54,14 +53,9 @@ class PDFOutlineViewer extends BaseTreeViewer {
// If the capability is still pending, see the `_dispatchEvent`-method,
// we know that the `currentOutlineItem`-button can be enabled here.
if (
this._currentOutlineItemCapability &&
!this._currentOutlineItemCapability.settled
) {
this._currentOutlineItemCapability.resolve(
/* enabled = */ this._isPagesLoaded
);
}
this._currentOutlineItemCapability?.resolve(
/* enabled = */ this._isPagesLoaded
);
});
this.eventBus._on("sidebarviewchanged", evt => {
this._sidebarView = evt.view;
@ -76,12 +70,7 @@ class PDFOutlineViewer extends BaseTreeViewer {
this._currentPageNumber = 1;
this._isPagesLoaded = null;
if (
this._currentOutlineItemCapability &&
!this._currentOutlineItemCapability.settled
) {
this._currentOutlineItemCapability.resolve(/* enabled = */ false);
}
this._currentOutlineItemCapability?.resolve(/* enabled = */ false);
this._currentOutlineItemCapability = null;
}
@ -89,7 +78,7 @@ class PDFOutlineViewer extends BaseTreeViewer {
* @private
*/
_dispatchEvent(outlineCount) {
this._currentOutlineItemCapability = new PromiseCapability();
this._currentOutlineItemCapability = Promise.withResolvers();
if (
outlineCount === 0 ||
this._pdfDocument?.loadingParams.disableAutoFetch
@ -307,7 +296,7 @@ class PDFOutlineViewer extends BaseTreeViewer {
if (this._pageNumberToDestHashCapability) {
return this._pageNumberToDestHashCapability.promise;
}
this._pageNumberToDestHashCapability = new PromiseCapability();
this._pageNumberToDestHashCapability = Promise.withResolvers();
const pageNumberToDestHash = new Map(),
pageNumberNesting = new Map();

View file

@ -16,7 +16,7 @@
/** @typedef {import("./event_utils").EventBus} EventBus */
import { apiPageLayoutToViewerModes, RenderingStates } from "./ui_utils.js";
import { PromiseCapability, shadow } from "pdfjs-lib";
import { shadow } from "pdfjs-lib";
/**
* @typedef {Object} PDFScriptingManagerOptions
@ -199,7 +199,7 @@ class PDFScriptingManager {
return;
}
await this.#willPrintCapability?.promise;
this.#willPrintCapability = new PromiseCapability();
this.#willPrintCapability = Promise.withResolvers();
try {
await this.#scripting.dispatchEventInSandbox({
id: "doc",
@ -344,7 +344,7 @@ class PDFScriptingManager {
visitedPages = this._visitedPages;
if (initialize) {
this.#closeCapability = new PromiseCapability();
this.#closeCapability = Promise.withResolvers();
}
if (!this.#closeCapability) {
return; // Scripting isn't fully initialized yet.
@ -406,7 +406,7 @@ class PDFScriptingManager {
}
#initScripting() {
this.#destroyCapability = new PromiseCapability();
this.#destroyCapability = Promise.withResolvers();
if (this.#scripting) {
throw new Error("#initScripting: Scripting already exists.");

View file

@ -34,7 +34,6 @@ import {
AnnotationMode,
PermissionFlag,
PixelsPerInch,
PromiseCapability,
shadow,
version,
} from "pdfjs-lib";
@ -362,10 +361,7 @@ class PDFViewer {
get pageViewsReady() {
// Prevent printing errors when 'disableAutoFetch' is set, by ensuring
// that *all* pages have in fact been completely loaded.
return (
this._pagesCapability.settled &&
this._pages.every(pageView => pageView?.pdfPage)
);
return this._pages.every(pageView => pageView?.pdfPage);
}
/**
@ -823,7 +819,7 @@ class PDFViewer {
this.eventBus._on("pagerender", this._onBeforeDraw);
this._onAfterDraw = evt => {
if (evt.cssTransform || this._onePageRenderedCapability.settled) {
if (evt.cssTransform) {
return;
}
this._onePageRenderedCapability.resolve({ timestamp: evt.timestamp });
@ -1075,9 +1071,9 @@ class PDFViewer {
this._location = null;
this._pagesRotation = 0;
this._optionalContentConfigPromise = null;
this._firstPageCapability = new PromiseCapability();
this._onePageRenderedCapability = new PromiseCapability();
this._pagesCapability = new PromiseCapability();
this._firstPageCapability = Promise.withResolvers();
this._onePageRenderedCapability = Promise.withResolvers();
this._pagesCapability = Promise.withResolvers();
this._scrollMode = ScrollMode.VERTICAL;
this._previousScrollMode = ScrollMode.UNKNOWN;
this._spreadMode = SpreadMode.NONE;

View file

@ -58,7 +58,6 @@ const {
PDFWorker,
PermissionFlag,
PixelsPerInch,
PromiseCapability,
RenderingCancelledException,
renderTextLayer,
setLayerDimensions,
@ -107,7 +106,6 @@ export {
PDFWorker,
PermissionFlag,
PixelsPerInch,
PromiseCapability,
RenderingCancelledException,
renderTextLayer,
setLayerDimensions,