diff --git a/gulpfile.mjs b/gulpfile.mjs index 6f9aa8ca15c42..034807f2d1874 100644 --- a/gulpfile.mjs +++ b/gulpfile.mjs @@ -99,6 +99,13 @@ const AUTOPREFIXER_CONFIG = { // Default Babel targets used for generic, components, minified-pre const BABEL_TARGETS = ENV_TARGETS.join(", "); +const BABEL_PRESET_ENV_OPTS = Object.freeze({ + corejs: "3.36.1", + exclude: ["web.structured-clone"], + shippedProposals: true, + useBuiltIns: "usage", +}); + const DEFINES = Object.freeze({ SKIP_BABEL: true, TESTING: undefined, @@ -303,17 +310,7 @@ function createWebpackConfig( const babelPresets = skipBabel ? undefined - : [ - [ - "@babel/preset-env", - { - corejs: "3.36.1", - exclude: ["web.structured-clone"], - shippedProposals: true, - useBuiltIns: "usage", - }, - ], - ]; + : [["@babel/preset-env", BABEL_PRESET_ENV_OPTS]]; const babelPlugins = [ [ babelPluginPDFJSPreprocessor, @@ -1542,7 +1539,12 @@ function buildLibHelper(bundleDefines, inputStream, outputDir) { sourceType: "module", presets: skipBabel ? undefined - : [["@babel/preset-env", { loose: false, modules: false }]], + : [ + [ + "@babel/preset-env", + { ...BABEL_PRESET_ENV_OPTS, loose: false, modules: false }, + ], + ], plugins: [[babelPluginPDFJSPreprocessor, ctx]], targets: BABEL_TARGETS, }).code; diff --git a/src/core/chunked_stream.js b/src/core/chunked_stream.js index 635474953f6fa..b4ebe5b64c53d 100644 --- a/src/core/chunked_stream.js +++ b/src/core/chunked_stream.js @@ -14,7 +14,7 @@ */ import { arrayBuffersToBytes, MissingDataException } from "./core_utils.js"; -import { assert, PromiseCapability } from "../shared/util.js"; +import { assert } from "../shared/util.js"; import { Stream } from "./stream.js"; class ChunkedStream extends Stream { @@ -273,7 +273,7 @@ class ChunkedStreamManager { this.progressiveDataLength = 0; this.aborted = false; - this._loadedStreamCapability = new PromiseCapability(); + this._loadedStreamCapability = Promise.withResolvers(); } sendRequest(begin, end) { @@ -347,7 +347,7 @@ class ChunkedStreamManager { return Promise.resolve(); } - const capability = new PromiseCapability(); + const capability = Promise.withResolvers(); this._promisesByRequest.set(requestId, capability); const chunksToRequest = []; diff --git a/src/core/evaluator.js b/src/core/evaluator.js index 9d5ad52db1020..f71cfe96036cd 100644 --- a/src/core/evaluator.js +++ b/src/core/evaluator.js @@ -25,7 +25,6 @@ import { isArrayEqual, normalizeUnicode, OPS, - PromiseCapability, shadow, stringToPDFString, TextRenderingMode, @@ -1270,7 +1269,7 @@ class PartialEvaluator { return this.fontCache.get(font.cacheKey); } - const fontCapability = new PromiseCapability(); + const { promise, resolve } = Promise.withResolvers(); let preEvaluatedFont; try { @@ -1328,10 +1327,10 @@ class PartialEvaluator { // keys. Also, since `fontRef` is used when getting cached fonts, // we'll not accidentally match fonts cached with the `fontID`. if (fontRefIsRef) { - this.fontCache.put(fontRef, fontCapability.promise); + this.fontCache.put(fontRef, promise); } else { font.cacheKey = `cacheKey_${fontID}`; - this.fontCache.put(font.cacheKey, fontCapability.promise); + this.fontCache.put(font.cacheKey, promise); } // Keep track of each font we translated so the caller can @@ -1340,7 +1339,7 @@ class PartialEvaluator { this.translateFont(preEvaluatedFont) .then(translatedFont => { - fontCapability.resolve( + resolve( new TranslatedFont({ loadedName: font.loadedName, font: translatedFont, @@ -1350,10 +1349,10 @@ class PartialEvaluator { ); }) .catch(reason => { - // TODO fontCapability.reject? + // TODO reject? warn(`loadFont - translateFont failed: "${reason}".`); - fontCapability.resolve( + resolve( new TranslatedFont({ loadedName: font.loadedName, font: new ErrorFont( @@ -1364,7 +1363,7 @@ class PartialEvaluator { }) ); }); - return fontCapability.promise; + return promise; } buildPath(operatorList, fn, args, parsingText = false) { diff --git a/src/core/worker.js b/src/core/worker.js index e407577785cd3..631bb52df9f26 100644 --- a/src/core/worker.js +++ b/src/core/worker.js @@ -22,7 +22,6 @@ import { isNodeJS, MissingPDFException, PasswordException, - PromiseCapability, setVerbosityLevel, stringToPDFString, UnexpectedResponseException, @@ -48,7 +47,7 @@ class WorkerTask { constructor(name) { this.name = name; this.terminated = false; - this._capability = new PromiseCapability(); + this._capability = Promise.withResolvers(); } get finished() { @@ -212,7 +211,7 @@ class WorkerMessageHandler { password, rangeChunkSize, }; - const pdfManagerCapability = new PromiseCapability(); + const pdfManagerCapability = Promise.withResolvers(); let newPdfManager; if (data) { diff --git a/src/display/api.js b/src/display/api.js index 25c9cca50dd4a..45a3d7a65bb57 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -28,7 +28,6 @@ import { MAX_IMAGE_SIZE_TO_CACHE, MissingPDFException, PasswordException, - PromiseCapability, RenderingIntentFlag, setVerbosityLevel, shadow, @@ -577,7 +576,7 @@ class PDFDocumentLoadingTask { static #docId = 0; constructor() { - this._capability = new PromiseCapability(); + this._capability = Promise.withResolvers(); this._transport = null; this._worker = null; @@ -674,7 +673,7 @@ class PDFDataRangeTransport { this._progressListeners = []; this._progressiveReadListeners = []; this._progressiveDoneListeners = []; - this._readyCapability = new PromiseCapability(); + this._readyCapability = Promise.withResolvers(); } /** @@ -1461,7 +1460,7 @@ class PDFPageProxy { // If there's no displayReadyCapability yet, then the operatorList // was never requested before. Make the request and create the promise. if (!intentState.displayReadyCapability) { - intentState.displayReadyCapability = new PromiseCapability(); + intentState.displayReadyCapability = Promise.withResolvers(); intentState.operatorList = { fnArray: [], argsArray: [], @@ -1588,7 +1587,7 @@ class PDFPageProxy { if (!intentState.opListReadCapability) { opListTask = Object.create(null); opListTask.operatorListChanged = operatorListChanged; - intentState.opListReadCapability = new PromiseCapability(); + intentState.opListReadCapability = Promise.withResolvers(); (intentState.renderTasks ||= new Set()).add(opListTask); intentState.operatorList = { fnArray: [], @@ -2049,7 +2048,7 @@ class PDFWorker { this.destroyed = false; this.verbosity = verbosity; - this._readyCapability = new PromiseCapability(); + this._readyCapability = Promise.withResolvers(); this._port = null; this._webWorker = null; this._messageHandler = null; @@ -2365,7 +2364,7 @@ class WorkerTransport { this._networkStream = networkStream; this._fullReader = null; this._lastProgress = null; - this.downloadInfoCapability = new PromiseCapability(); + this.downloadInfoCapability = Promise.withResolvers(); this.setupMessageHandler(); @@ -2471,7 +2470,7 @@ class WorkerTransport { } this.destroyed = true; - this.destroyCapability = new PromiseCapability(); + this.destroyCapability = Promise.withResolvers(); this.#passwordCapability?.reject( new Error("Worker was destroyed during onPassword callback") @@ -2562,7 +2561,7 @@ class WorkerTransport { }); messageHandler.on("ReaderHeadersReady", data => { - const headersCapability = new PromiseCapability(); + const headersCapability = Promise.withResolvers(); const fullReader = this._fullReader; fullReader.headersReady.then(() => { // If stream or range are disabled, it's our only way to report @@ -2677,7 +2676,7 @@ class WorkerTransport { }); messageHandler.on("PasswordRequest", exception => { - this.#passwordCapability = new PromiseCapability(); + this.#passwordCapability = Promise.withResolvers(); if (loadingTask.onPassword) { const updatePassword = password => { @@ -3089,6 +3088,8 @@ class WorkerTransport { } } +const INITIAL_DATA = Symbol("INITIAL_DATA"); + /** * A PDF document and page is built of many objects. E.g. there are objects for * fonts, images, rendering code, etc. These objects may get processed inside of @@ -3105,8 +3106,8 @@ class PDFObjects { */ #ensureObj(objId) { return (this.#objs[objId] ||= { - capability: new PromiseCapability(), - data: null, + ...Promise.withResolvers(), + data: INITIAL_DATA, }); } @@ -3127,7 +3128,7 @@ class PDFObjects { // not required to be resolved right now. if (callback) { const obj = this.#ensureObj(objId); - obj.capability.promise.then(() => callback(obj.data)); + obj.promise.then(() => callback(obj.data)); return null; } // If there isn't a callback, the user expects to get the resolved data @@ -3135,7 +3136,7 @@ class PDFObjects { const obj = this.#objs[objId]; // If there isn't an object yet or the object isn't resolved, then the // data isn't ready yet! - if (!obj?.capability.settled) { + if (!obj || obj.data === INITIAL_DATA) { throw new Error(`Requesting object that isn't resolved yet ${objId}.`); } return obj.data; @@ -3147,7 +3148,7 @@ class PDFObjects { */ has(objId) { const obj = this.#objs[objId]; - return obj?.capability.settled ?? false; + return !!obj && obj.data !== INITIAL_DATA; } /** @@ -3159,7 +3160,7 @@ class PDFObjects { resolve(objId, data = null) { const obj = this.#ensureObj(objId); obj.data = data; - obj.capability.resolve(); + obj.resolve(); } clear() { @@ -3172,9 +3173,9 @@ class PDFObjects { *[Symbol.iterator]() { for (const objId in this.#objs) { - const { capability, data } = this.#objs[objId]; + const { data } = this.#objs[objId]; - if (!capability.settled) { + if (data === INITIAL_DATA) { continue; } yield [objId, data]; @@ -3283,7 +3284,7 @@ class InternalRenderTask { this._useRequestAnimationFrame = useRequestAnimationFrame === true && typeof window !== "undefined"; this.cancelled = false; - this.capability = new PromiseCapability(); + this.capability = Promise.withResolvers(); this.task = new RenderTask(this); // caching this-bound methods this._cancelBound = this.cancel.bind(this); diff --git a/src/display/fetch_stream.js b/src/display/fetch_stream.js index 0504f1d9a8ef6..00bfa1e428639 100644 --- a/src/display/fetch_stream.js +++ b/src/display/fetch_stream.js @@ -13,12 +13,7 @@ * limitations under the License. */ -import { - AbortException, - assert, - PromiseCapability, - warn, -} from "../shared/util.js"; +import { AbortException, assert, warn } from "../shared/util.js"; import { createResponseStatusError, extractFilenameFromHeader, @@ -118,7 +113,7 @@ class PDFFetchStreamReader { const source = stream.source; this._withCredentials = source.withCredentials || false; this._contentLength = source.length; - this._headersCapability = new PromiseCapability(); + this._headersCapability = Promise.withResolvers(); this._disableRange = source.disableRange || false; this._rangeChunkSize = source.rangeChunkSize; if (!this._rangeChunkSize && !this._disableRange) { @@ -223,7 +218,7 @@ class PDFFetchStreamRangeReader { this._loaded = 0; const source = stream.source; this._withCredentials = source.withCredentials || false; - this._readCapability = new PromiseCapability(); + this._readCapability = Promise.withResolvers(); this._isStreamingSupported = !source.disableStream; this._abortController = new AbortController(); diff --git a/src/display/network.js b/src/display/network.js index f9c4d3ff9060a..65f6ec20519b9 100644 --- a/src/display/network.js +++ b/src/display/network.js @@ -13,7 +13,7 @@ * limitations under the License. */ -import { assert, PromiseCapability, stringToBytes } from "../shared/util.js"; +import { assert, stringToBytes } from "../shared/util.js"; import { createResponseStatusError, extractFilenameFromHeader, @@ -255,7 +255,7 @@ class PDFNetworkStreamFullRequestReader { }; this._url = source.url; this._fullRequestId = manager.requestFull(args); - this._headersReceivedCapability = new PromiseCapability(); + this._headersReceivedCapability = Promise.withResolvers(); this._disableRange = source.disableRange || false; this._contentLength = source.length; // Optional this._rangeChunkSize = source.rangeChunkSize; @@ -375,7 +375,7 @@ class PDFNetworkStreamFullRequestReader { if (this._done) { return { value: undefined, done: true }; } - const requestCapability = new PromiseCapability(); + const requestCapability = Promise.withResolvers(); this._requests.push(requestCapability); return requestCapability.promise; } @@ -466,7 +466,7 @@ class PDFNetworkStreamRangeRequestReader { if (this._done) { return { value: undefined, done: true }; } - const requestCapability = new PromiseCapability(); + const requestCapability = Promise.withResolvers(); this._requests.push(requestCapability); return requestCapability.promise; } diff --git a/src/display/node_stream.js b/src/display/node_stream.js index 4c028a5c5fc34..5c583ee2c3c30 100644 --- a/src/display/node_stream.js +++ b/src/display/node_stream.js @@ -18,7 +18,6 @@ import { assert, isNodeJS, MissingPDFException, - PromiseCapability, } from "../shared/util.js"; import { extractFilenameFromHeader, @@ -128,8 +127,8 @@ class BaseFullReader { this._isRangeSupported = !source.disableRange; this._readableStream = null; - this._readCapability = new PromiseCapability(); - this._headersCapability = new PromiseCapability(); + this._readCapability = Promise.withResolvers(); + this._headersCapability = Promise.withResolvers(); } get headersReady() { @@ -163,7 +162,7 @@ class BaseFullReader { const chunk = this._readableStream.read(); if (chunk === null) { - this._readCapability = new PromiseCapability(); + this._readCapability = Promise.withResolvers(); return this.read(); } this._loaded += chunk.length; @@ -230,7 +229,7 @@ class BaseRangeReader { this.onProgress = null; this._loaded = 0; this._readableStream = null; - this._readCapability = new PromiseCapability(); + this._readCapability = Promise.withResolvers(); const source = stream.source; this._isStreamingSupported = !source.disableStream; } @@ -250,7 +249,7 @@ class BaseRangeReader { const chunk = this._readableStream.read(); if (chunk === null) { - this._readCapability = new PromiseCapability(); + this._readCapability = Promise.withResolvers(); return this.read(); } this._loaded += chunk.length; diff --git a/src/display/text_layer.js b/src/display/text_layer.js index 22be2748c8dbe..5394f0dc2d29d 100644 --- a/src/display/text_layer.js +++ b/src/display/text_layer.js @@ -16,7 +16,7 @@ /** @typedef {import("./display_utils").PageViewport} PageViewport */ /** @typedef {import("./api").TextContent} TextContent */ -import { AbortException, PromiseCapability, Util } from "../shared/util.js"; +import { AbortException, Util } from "../shared/util.js"; import { setLayerDimensions } from "./display_utils.js"; /** @@ -326,7 +326,7 @@ class TextLayerRenderTask { this._reader = null; this._textDivProperties = textDivProperties || new WeakMap(); this._canceled = false; - this._capability = new PromiseCapability(); + this._capability = Promise.withResolvers(); this._layoutTextParams = { prevFontSize: null, prevFontFamily: null, @@ -426,21 +426,21 @@ class TextLayerRenderTask { * @private */ _render() { - const capability = new PromiseCapability(); + const { promise, resolve, reject } = Promise.withResolvers(); let styleCache = Object.create(null); if (this._isReadableStream) { const pump = () => { this._reader.read().then(({ value, done }) => { if (done) { - capability.resolve(); + resolve(); return; } Object.assign(styleCache, value.styles); this._processItems(value.items, styleCache); pump(); - }, capability.reject); + }, reject); }; this._reader = this._textContentSource.getReader(); @@ -448,12 +448,12 @@ class TextLayerRenderTask { } else if (this._textContentSource) { const { items, styles } = this._textContentSource; this._processItems(items, styles); - capability.resolve(); + resolve(); } else { throw new Error('No "textContentSource" parameter specified.'); } - capability.promise.then(() => { + promise.then(() => { styleCache = null; render(this); }, this._capability.reject); diff --git a/src/display/transport_stream.js b/src/display/transport_stream.js index afedadac79f9f..36bef6dc10eb0 100644 --- a/src/display/transport_stream.js +++ b/src/display/transport_stream.js @@ -18,7 +18,7 @@ // eslint-disable-next-line max-len /** @typedef {import("../interfaces").IPDFStreamRangeReader} IPDFStreamRangeReader */ -import { assert, PromiseCapability } from "../shared/util.js"; +import { assert } from "../shared/util.js"; import { isPdfFile } from "./display_utils.js"; /** @implements {IPDFStream} */ @@ -235,7 +235,7 @@ class PDFDataTransportStreamReader { if (this._done) { return { value: undefined, done: true }; } - const requestCapability = new PromiseCapability(); + const requestCapability = Promise.withResolvers(); this._requests.push(requestCapability); return requestCapability.promise; } @@ -300,7 +300,7 @@ class PDFDataTransportStreamRangeReader { if (this._done) { return { value: undefined, done: true }; } - const requestCapability = new PromiseCapability(); + const requestCapability = Promise.withResolvers(); this._requests.push(requestCapability); return requestCapability.promise; } diff --git a/src/pdf.js b/src/pdf.js index 257b39634908e..63ec9acb07f97 100644 --- a/src/pdf.js +++ b/src/pdf.js @@ -39,7 +39,6 @@ import { OPS, PasswordResponses, PermissionFlag, - PromiseCapability, shadow, UnexpectedResponseException, Util, @@ -119,7 +118,6 @@ export { PDFWorker, PermissionFlag, PixelsPerInch, - PromiseCapability, RenderingCancelledException, renderTextLayer, setLayerDimensions, diff --git a/src/shared/message_handler.js b/src/shared/message_handler.js index 8770d28890cd1..e5cf00b91c21a 100644 --- a/src/shared/message_handler.js +++ b/src/shared/message_handler.js @@ -18,7 +18,6 @@ import { assert, MissingPDFException, PasswordException, - PromiseCapability, UnexpectedResponseException, UnknownErrorException, unreachable, @@ -190,7 +189,7 @@ class MessageHandler { */ sendWithPromise(actionName, data, transfers) { const callbackId = this.callbackId++; - const capability = new PromiseCapability(); + const capability = Promise.withResolvers(); this.callbackCapabilities[callbackId] = capability; try { this.comObj.postMessage( @@ -228,7 +227,7 @@ class MessageHandler { return new ReadableStream( { start: controller => { - const startCapability = new PromiseCapability(); + const startCapability = Promise.withResolvers(); this.streamControllers[streamId] = { controller, startCall: startCapability, @@ -252,7 +251,7 @@ class MessageHandler { }, pull: controller => { - const pullCapability = new PromiseCapability(); + const pullCapability = Promise.withResolvers(); this.streamControllers[streamId].pullCall = pullCapability; comObj.postMessage({ sourceName, @@ -268,7 +267,7 @@ class MessageHandler { cancel: reason => { assert(reason instanceof Error, "cancel must have a valid reason"); - const cancelCapability = new PromiseCapability(); + const cancelCapability = Promise.withResolvers(); this.streamControllers[streamId].cancelCall = cancelCapability; this.streamControllers[streamId].isClosed = true; comObj.postMessage({ @@ -305,7 +304,7 @@ class MessageHandler { // so when it changes from positive to negative, // set ready as unresolved promise. if (lastDesiredSize > 0 && this.desiredSize <= 0) { - this.sinkCapability = new PromiseCapability(); + this.sinkCapability = Promise.withResolvers(); this.ready = this.sinkCapability.promise; } comObj.postMessage( @@ -349,7 +348,7 @@ class MessageHandler { }); }, - sinkCapability: new PromiseCapability(), + sinkCapability: Promise.withResolvers(), onPull: null, onCancel: null, isCancelled: false, diff --git a/src/shared/util.js b/src/shared/util.js index 5fa1af0395ef0..722a7ed63c283 100644 --- a/src/shared/util.js +++ b/src/shared/util.js @@ -1031,43 +1031,6 @@ function getModificationDate(date = new Date()) { return buffer.join(""); } -class PromiseCapability { - #settled = false; - - constructor() { - /** - * @type {Promise} The Promise object. - */ - this.promise = new Promise((resolve, reject) => { - /** - * @type {function} Fulfills the Promise. - */ - this.resolve = data => { - this.#settled = true; - resolve(data); - }; - - /** - * @type {function} Rejects the Promise. - */ - this.reject = reason => { - if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { - assert(reason instanceof Error, 'Expected valid "reason" argument.'); - } - this.#settled = true; - reject(reason); - }; - }); - } - - /** - * @type {boolean} If the Promise has been fulfilled/rejected. - */ - get settled() { - return this.#settled; - } -} - let NormalizeRegex = null; let NormalizationMap = null; function normalizeUnicode(str) { @@ -1154,7 +1117,6 @@ export { PasswordException, PasswordResponses, PermissionFlag, - PromiseCapability, RenderingIntentFlag, setVerbosityLevel, shadow, diff --git a/test/driver.js b/test/driver.js index 780c16742bd9f..e47a48a5d29c2 100644 --- a/test/driver.js +++ b/test/driver.js @@ -22,7 +22,6 @@ const { GlobalWorkerOptions, Outliner, PixelsPerInch, - PromiseCapability, renderTextLayer, shadow, XfaLayer, @@ -1112,7 +1111,7 @@ class Driver { } _send(url, message) { - const capability = new PromiseCapability(); + const { promise, resolve } = Promise.withResolvers(); this.inflight.textContent = this.inFlightRequests++; fetch(url, { @@ -1129,18 +1128,18 @@ class Driver { } this.inFlightRequests--; - capability.resolve(); + resolve(); }) .catch(reason => { console.warn(`Driver._send failed (${url}): ${reason}`); this.inFlightRequests--; - capability.resolve(); + resolve(); this._send(url, message); }); - return capability.promise; + return promise; } } diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index d9cbdfa463fa0..3f5faf666de3b 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -26,7 +26,6 @@ import { PasswordException, PasswordResponses, PermissionFlag, - PromiseCapability, UnknownErrorException, } from "../../src/shared/util.js"; import { @@ -156,13 +155,11 @@ describe("api", function () { const loadingTask = getDocument(basicApiGetDocumentParams); expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true); - const progressReportedCapability = new PromiseCapability(); + const progressReportedCapability = Promise.withResolvers(); // Attach the callback that is used to report loading progress; // similarly to how viewer.js works. loadingTask.onProgress = function (progressData) { - if (!progressReportedCapability.settled) { - progressReportedCapability.resolve(progressData); - } + progressReportedCapability.resolve(progressData); }; const data = await Promise.all([ @@ -218,7 +215,7 @@ describe("api", function () { const loadingTask = getDocument(typedArrayPdf); expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true); - const progressReportedCapability = new PromiseCapability(); + const progressReportedCapability = Promise.withResolvers(); loadingTask.onProgress = function (data) { progressReportedCapability.resolve(data); }; @@ -248,7 +245,7 @@ describe("api", function () { const loadingTask = getDocument(arrayBufferPdf); expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true); - const progressReportedCapability = new PromiseCapability(); + const progressReportedCapability = Promise.withResolvers(); loadingTask.onProgress = function (data) { progressReportedCapability.resolve(data); }; @@ -306,8 +303,14 @@ describe("api", function () { const loadingTask = getDocument(buildGetDocumentParams("pr6531_1.pdf")); expect(loadingTask instanceof PDFDocumentLoadingTask).toEqual(true); - const passwordNeededCapability = new PromiseCapability(); - const passwordIncorrectCapability = new PromiseCapability(); + const passwordNeededCapability = { + ...Promise.withResolvers(), + settled: false, + }; + const passwordIncorrectCapability = { + ...Promise.withResolvers(), + settled: false, + }; // Attach the callback that is used to request a password; // similarly to how the default viewer handles passwords. loadingTask.onPassword = function (updatePassword, reason) { @@ -315,6 +318,7 @@ describe("api", function () { reason === PasswordResponses.NEED_PASSWORD && !passwordNeededCapability.settled ) { + passwordNeededCapability.settled = true; passwordNeededCapability.resolve(); updatePassword("qwerty"); // Provide an incorrect password. @@ -324,6 +328,7 @@ describe("api", function () { reason === PasswordResponses.INCORRECT_PASSWORD && !passwordIncorrectCapability.settled ) { + passwordIncorrectCapability.settled = true; passwordIncorrectCapability.resolve(); updatePassword("asdfasdf"); // Provide the correct password. diff --git a/test/unit/message_handler_spec.js b/test/unit/message_handler_spec.js index 37ecb355432d0..35ddacb71f5c0 100644 --- a/test/unit/message_handler_spec.js +++ b/test/unit/message_handler_spec.js @@ -15,7 +15,6 @@ import { AbortException, - PromiseCapability, UnknownErrorException, } from "../../src/shared/util.js"; import { LoopbackPort } from "../../src/display/api.js"; @@ -336,7 +335,7 @@ describe("message_handler", function () { it("should ignore any pull after close is called", async function () { let log = ""; const port = new LoopbackPort(); - const capability = new PromiseCapability(); + const { promise, resolve } = Promise.withResolvers(); const messageHandler2 = new MessageHandler("worker", "main", port); messageHandler2.on("fakeHandler", (data, sink) => { sink.onPull = function () { @@ -350,7 +349,7 @@ describe("message_handler", function () { log += "1"; sink.enqueue([1, 2, 3, 4], 4); }); - return capability.promise.then(() => { + return promise.then(() => { sink.close(); }); }); @@ -371,8 +370,8 @@ describe("message_handler", function () { await sleep(10); expect(log).toEqual("01"); - capability.resolve(); - await capability.promise; + resolve(); + await promise; let result = await reader.read(); expect(result.value).toEqual([1, 2, 3, 4]); diff --git a/test/unit/pdf_spec.js b/test/unit/pdf_spec.js index b47656cf90f23..a4108a47eef3b 100644 --- a/test/unit/pdf_spec.js +++ b/test/unit/pdf_spec.js @@ -29,7 +29,6 @@ import { OPS, PasswordResponses, PermissionFlag, - PromiseCapability, shadow, UnexpectedResponseException, Util, @@ -105,7 +104,6 @@ const expectedAPI = Object.freeze({ PDFWorker, PermissionFlag, PixelsPerInch, - PromiseCapability, RenderingCancelledException, renderTextLayer, setLayerDimensions, diff --git a/test/unit/util_spec.js b/test/unit/util_spec.js index 0df3137574ba8..b3d6df9638da5 100644 --- a/test/unit/util_spec.js +++ b/test/unit/util_spec.js @@ -17,7 +17,6 @@ import { bytesToString, createValidAbsoluteUrl, getModificationDate, - PromiseCapability, string32, stringToBytes, stringToPDFString, @@ -223,37 +222,6 @@ describe("util", function () { }); }); - describe("PromiseCapability", function () { - it("should resolve with correct data", async function () { - const promiseCapability = new PromiseCapability(); - expect(promiseCapability.settled).toEqual(false); - - promiseCapability.resolve({ test: "abc" }); - - const data = await promiseCapability.promise; - expect(promiseCapability.settled).toEqual(true); - expect(data).toEqual({ test: "abc" }); - }); - - it("should reject with correct reason", async function () { - const promiseCapability = new PromiseCapability(); - expect(promiseCapability.settled).toEqual(false); - - promiseCapability.reject(new Error("reason")); - - try { - await promiseCapability.promise; - - // Shouldn't get here. - expect(false).toEqual(true); - } catch (reason) { - expect(promiseCapability.settled).toEqual(true); - expect(reason instanceof Error).toEqual(true); - expect(reason.message).toEqual("reason"); - } - }); - }); - describe("getModificationDate", function () { it("should get a correctly formatted date", function () { const date = new Date(Date.UTC(3141, 5, 9, 2, 6, 53)); diff --git a/web/app.js b/web/app.js index ffc58bfa227d2..f2d5087f405a2 100644 --- a/web/app.js +++ b/web/app.js @@ -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(); }, diff --git a/web/event_utils.js b/web/event_utils.js index e1d32728819d8..ffc3c858a9c26 100644 --- a/web/event_utils.js +++ b/web/event_utils.js @@ -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."); - } - - function handler(type) { - if (target instanceof EventBus) { - target._off(name, eventHandler); - } else { - target.removeEventListener(name, eventHandler); - } - - if (timeout) { - clearTimeout(timeout); - } - resolve(type); - } +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(); - 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; } /** diff --git a/web/password_prompt.js b/web/password_prompt.js index 8643071528b5b..f48912329abf4 100644 --- a/web/password_prompt.js +++ b/web/password_prompt.js @@ -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); diff --git a/web/pdf_attachment_viewer.js b/web/pdf_attachment_viewer.js index d99cfa985632d..f1106176efb9d 100644 --- a/web/pdf_attachment_viewer.js +++ b/web/pdf_attachment_viewer.js @@ -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; } diff --git a/web/pdf_document_properties.js b/web/pdf_document_properties.js index f67ab0e30b2b5..0b029b7cbe836 100644 --- a/web/pdf_document_properties.js +++ b/web/pdf_document_properties.js @@ -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; } diff --git a/web/pdf_find_controller.js b/web/pdf_find_controller.js index 99de915824471..8b9f3d9aeda13 100644 --- a/web/pdf_find_controller.js +++ b/web/pdf_find_controller.js @@ -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(); } ); }); diff --git a/web/pdf_outline_viewer.js b/web/pdf_outline_viewer.js index 70c629a715ee3..02e4d5f2769ea 100644 --- a/web/pdf_outline_viewer.js +++ b/web/pdf_outline_viewer.js @@ -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(); diff --git a/web/pdf_scripting_manager.js b/web/pdf_scripting_manager.js index f47d30d6f764a..f0c8ce91eddbe 100644 --- a/web/pdf_scripting_manager.js +++ b/web/pdf_scripting_manager.js @@ -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."); diff --git a/web/pdf_viewer.js b/web/pdf_viewer.js index 268af114c5b25..fd9ff4d8f4913 100644 --- a/web/pdf_viewer.js +++ b/web/pdf_viewer.js @@ -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; diff --git a/web/pdfjs.js b/web/pdfjs.js index aff0e95cccc4e..670df4e073c23 100644 --- a/web/pdfjs.js +++ b/web/pdfjs.js @@ -58,7 +58,6 @@ const { PDFWorker, PermissionFlag, PixelsPerInch, - PromiseCapability, RenderingCancelledException, renderTextLayer, setLayerDimensions, @@ -107,7 +106,6 @@ export { PDFWorker, PermissionFlag, PixelsPerInch, - PromiseCapability, RenderingCancelledException, renderTextLayer, setLayerDimensions,