Merge pull request #19943 from Snuffleupagus/PDFWorker-create

Replace `PDFWorker.fromPort` with a generic `PDFWorker.create` method
This commit is contained in:
Tim van der Meij 2025-05-17 19:12:38 +02:00 committed by GitHub
commit 91679ece4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 70 additions and 53 deletions

View File

@ -38,14 +38,15 @@ import {
PrintAnnotationStorage, PrintAnnotationStorage,
SerializableEmpty, SerializableEmpty,
} from "./annotation_storage.js"; } from "./annotation_storage.js";
import { FontFaceObject, FontLoader } from "./font_loader.js";
import { import {
deprecated,
isDataScheme, isDataScheme,
isValidFetchUrl, isValidFetchUrl,
PageViewport, PageViewport,
RenderingCancelledException, RenderingCancelledException,
StatTimer, StatTimer,
} from "./display_utils.js"; } from "./display_utils.js";
import { FontFaceObject, FontLoader } from "./font_loader.js";
import { MessageHandler, wrapReason } from "../shared/message_handler.js"; import { MessageHandler, wrapReason } from "../shared/message_handler.js";
import { import {
NodeCanvasFactory, NodeCanvasFactory,
@ -383,15 +384,12 @@ function getDocument(src = {}) {
}; };
if (!worker) { if (!worker) {
const workerParams = {
verbosity,
port: GlobalWorkerOptions.workerPort,
};
// Worker was not provided -- creating and owning our own. If message port // Worker was not provided -- creating and owning our own. If message port
// is specified in global worker options, using it. // is specified in global worker options, using it.
worker = workerParams.port worker = PDFWorker.create({
? PDFWorker.fromPort(workerParams) verbosity,
: new PDFWorker(workerParams); port: GlobalWorkerOptions.workerPort,
});
task._worker = worker; task._worker = worker;
} }
@ -2093,6 +2091,14 @@ class LoopbackPort {
* @param {PDFWorkerParameters} params - The worker initialization parameters. * @param {PDFWorkerParameters} params - The worker initialization parameters.
*/ */
class PDFWorker { class PDFWorker {
#capability = Promise.withResolvers();
#messageHandler = null;
#port = null;
#webWorker = null;
static #fakeWorkerId = 0; static #fakeWorkerId = 0;
static #isWorkerDisabled = false; static #isWorkerDisabled = false;
@ -2131,6 +2137,16 @@ class PDFWorker {
new Blob([wrapper], { type: "text/javascript" }) new Blob([wrapper], { type: "text/javascript" })
); );
}; };
this.fromPort = params => {
deprecated(
"`PDFWorker.fromPort` - please use `PDFWorker.create` instead."
);
if (!params?.port) {
throw new Error("PDFWorker.fromPort - invalid method signature.");
}
return this.create(params);
};
} }
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) { if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
@ -2150,20 +2166,24 @@ class PDFWorker {
this.destroyed = false; this.destroyed = false;
this.verbosity = verbosity; this.verbosity = verbosity;
this._readyCapability = Promise.withResolvers();
this._port = null;
this._webWorker = null;
this._messageHandler = null;
if (port) { if (port) {
if (PDFWorker.#workerPorts.has(port)) { if (PDFWorker.#workerPorts.has(port)) {
throw new Error("Cannot use more than one PDFWorker per port."); throw new Error("Cannot use more than one PDFWorker per port.");
} }
PDFWorker.#workerPorts.set(port, this); PDFWorker.#workerPorts.set(port, this);
this._initializeFromPort(port); this.#initializeFromPort(port);
return; } else {
this.#initialize();
}
if (typeof PDFJSDev === "undefined" || PDFJSDev.test("TESTING")) {
// For testing purposes.
Object.defineProperty(this, "_webWorker", {
get() {
return this.#webWorker;
},
});
} }
this._initialize();
} }
/** /**
@ -2171,13 +2191,13 @@ class PDFWorker {
* @type {Promise<void>} * @type {Promise<void>}
*/ */
get promise() { get promise() {
return this._readyCapability.promise; return this.#capability.promise;
} }
#resolve() { #resolve() {
this._readyCapability.resolve(); this.#capability.resolve();
// Send global setting, e.g. verbosity level. // Send global setting, e.g. verbosity level.
this._messageHandler.send("configure", { this.#messageHandler.send("configure", {
verbosity: this.verbosity, verbosity: this.verbosity,
}); });
} }
@ -2187,7 +2207,7 @@ class PDFWorker {
* @type {Worker} * @type {Worker}
*/ */
get port() { get port() {
return this._port; return this.#port;
} }
/** /**
@ -2195,20 +2215,20 @@ class PDFWorker {
* @type {MessageHandler} * @type {MessageHandler}
*/ */
get messageHandler() { get messageHandler() {
return this._messageHandler; return this.#messageHandler;
} }
_initializeFromPort(port) { #initializeFromPort(port) {
this._port = port; this.#port = port;
this._messageHandler = new MessageHandler("main", "worker", port); this.#messageHandler = new MessageHandler("main", "worker", port);
this._messageHandler.on("ready", function () { this.#messageHandler.on("ready", () => {
// Ignoring "ready" event -- MessageHandler should already be initialized // Ignoring "ready" event -- MessageHandler should already be initialized
// and ready to accept messages. // and ready to accept messages.
}); });
this.#resolve(); this.#resolve();
} }
_initialize() { #initialize() {
// If worker support isn't disabled explicit and the browser has worker // If worker support isn't disabled explicit and the browser has worker
// support, create a new web worker and test if it/the browser fulfills // support, create a new web worker and test if it/the browser fulfills
// all requirements to run parts of pdf.js in a web worker. // all requirements to run parts of pdf.js in a web worker.
@ -2218,7 +2238,7 @@ class PDFWorker {
PDFWorker.#isWorkerDisabled || PDFWorker.#isWorkerDisabled ||
PDFWorker.#mainThreadWorkerMessageHandler PDFWorker.#mainThreadWorkerMessageHandler
) { ) {
this._setupFakeWorker(); this.#setupFakeWorker();
return; return;
} }
let { workerSrc } = PDFWorker; let { workerSrc } = PDFWorker;
@ -2243,11 +2263,11 @@ class PDFWorker {
messageHandler.destroy(); messageHandler.destroy();
worker.terminate(); worker.terminate();
if (this.destroyed) { if (this.destroyed) {
this._readyCapability.reject(new Error("Worker was destroyed")); this.#capability.reject(new Error("Worker was destroyed"));
} else { } else {
// Fall back to fake worker if the termination is caused by an // Fall back to fake worker if the termination is caused by an
// error (e.g. NetworkError / SecurityError). // error (e.g. NetworkError / SecurityError).
this._setupFakeWorker(); this.#setupFakeWorker();
} }
}; };
@ -2255,7 +2275,7 @@ class PDFWorker {
worker.addEventListener( worker.addEventListener(
"error", "error",
() => { () => {
if (!this._webWorker) { if (!this.#webWorker) {
// Worker failed to initialize due to an error. Clean up and fall // Worker failed to initialize due to an error. Clean up and fall
// back to the fake worker. // back to the fake worker.
terminateEarly(); terminateEarly();
@ -2270,9 +2290,9 @@ class PDFWorker {
terminateEarly(); terminateEarly();
return; return;
} }
this._messageHandler = messageHandler; this.#messageHandler = messageHandler;
this._port = worker; this.#port = worker;
this._webWorker = worker; this.#webWorker = worker;
this.#resolve(); this.#resolve();
}); });
@ -2287,7 +2307,7 @@ class PDFWorker {
sendTest(); sendTest();
} catch { } catch {
// We need fallback to a faked worker. // We need fallback to a faked worker.
this._setupFakeWorker(); this.#setupFakeWorker();
} }
}); });
@ -2307,10 +2327,10 @@ class PDFWorker {
} }
// Either workers are not supported or have thrown an exception. // Either workers are not supported or have thrown an exception.
// Thus, we fallback to a faked worker. // Thus, we fallback to a faked worker.
this._setupFakeWorker(); this.#setupFakeWorker();
} }
_setupFakeWorker() { #setupFakeWorker() {
if (!PDFWorker.#isWorkerDisabled) { if (!PDFWorker.#isWorkerDisabled) {
warn("Setting up fake worker."); warn("Setting up fake worker.");
PDFWorker.#isWorkerDisabled = true; PDFWorker.#isWorkerDisabled = true;
@ -2319,11 +2339,11 @@ class PDFWorker {
PDFWorker._setupFakeWorkerGlobal PDFWorker._setupFakeWorkerGlobal
.then(WorkerMessageHandler => { .then(WorkerMessageHandler => {
if (this.destroyed) { if (this.destroyed) {
this._readyCapability.reject(new Error("Worker was destroyed")); this.#capability.reject(new Error("Worker was destroyed"));
return; return;
} }
const port = new LoopbackPort(); const port = new LoopbackPort();
this._port = port; this.#port = port;
// All fake workers use the same port, making id unique. // All fake workers use the same port, making id unique.
const id = `fake${PDFWorker.#fakeWorkerId++}`; const id = `fake${PDFWorker.#fakeWorkerId++}`;
@ -2333,11 +2353,11 @@ class PDFWorker {
const workerHandler = new MessageHandler(id + "_worker", id, port); const workerHandler = new MessageHandler(id + "_worker", id, port);
WorkerMessageHandler.setup(workerHandler, port); WorkerMessageHandler.setup(workerHandler, port);
this._messageHandler = new MessageHandler(id, id + "_worker", port); this.#messageHandler = new MessageHandler(id, id + "_worker", port);
this.#resolve(); this.#resolve();
}) })
.catch(reason => { .catch(reason => {
this._readyCapability.reject( this.#capability.reject(
new Error(`Setting up fake worker failed: "${reason.message}".`) new Error(`Setting up fake worker failed: "${reason.message}".`)
); );
}); });
@ -2350,29 +2370,26 @@ class PDFWorker {
this.destroyed = true; this.destroyed = true;
// We need to terminate only web worker created resource. // We need to terminate only web worker created resource.
this._webWorker?.terminate(); this.#webWorker?.terminate();
this._webWorker = null; this.#webWorker = null;
PDFWorker.#workerPorts.delete(this._port); PDFWorker.#workerPorts.delete(this.#port);
this._port = null; this.#port = null;
this._messageHandler?.destroy(); this.#messageHandler?.destroy();
this._messageHandler = null; this.#messageHandler = null;
} }
/** /**
* @param {PDFWorkerParameters} params - The worker initialization parameters. * @param {PDFWorkerParameters} params - The worker initialization parameters.
* @returns {PDFWorker} * @returns {PDFWorker}
*/ */
static fromPort(params) { static create(params) {
if (!params?.port) { const cachedPort = this.#workerPorts.get(params?.port);
throw new Error("PDFWorker.fromPort - invalid method signature.");
}
const cachedPort = this.#workerPorts.get(params.port);
if (cachedPort) { if (cachedPort) {
if (cachedPort._pendingDestroy) { if (cachedPort._pendingDestroy) {
throw new Error( throw new Error(
"PDFWorker.fromPort - the worker is being destroyed.\n" + "PDFWorker.create - the worker is being destroyed.\n" +
"Please remember to await `PDFDocumentLoadingTask.destroy()`-calls." "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls."
); );
} }

View File

@ -1082,7 +1082,7 @@ describe("api", function () {
getDocument(tracemonkeyGetDocumentParams); getDocument(tracemonkeyGetDocumentParams);
}).toThrow( }).toThrow(
new Error( new Error(
"PDFWorker.fromPort - the worker is being destroyed.\n" + "PDFWorker.create - the worker is being destroyed.\n" +
"Please remember to await `PDFDocumentLoadingTask.destroy()`-calls." "Please remember to await `PDFDocumentLoadingTask.destroy()`-calls."
) )
); );