[api-minor] Remove the closure from the PDFWorker
class, in the src/display/api.js
file
This patch removes the only remaining closure in the `src/display/api.js` file, utilizing a similar approach as used in lots of other parts of the code-base, which results in a small decrease in the size of the *build* `pdf.js` file. Given that `PDFWorker` is exposed through the *public* API, this complicates things somewhat since there's a couple of worker-related properties that really should stay *private*. Initially, while working on PR 13813, I believed that we'd need support for private (static) class fields in order to get rid of this closure, however I've managed to come up with what's hopefully deemed an acceptable work-around here. Furthermore, some helper functions were simply moved into the `PDFWorker` class as static methods, thus simplifying the overall implementation (e.g. we don't need to manually cache the Promise in the `PDFWorker._setupFakeWorkerGlobal`-method). Finally, as part of this re-factoring a number of missing JSDoc-comments were added which *together* with the removal of the closure significantly improves the `gulp jsdoc` output for the `PDFWorker` class. *Please note:* This patch is tagged with `api-minor` since it deprecates `PDFWorker.getWorkerSrc()` in favor of the shorter `PDFWorker.workerSrc`, with the fallback limited to `GENERIC` builds.
This commit is contained in:
parent
3d18c76a53
commit
1cf9405281
@ -1913,109 +1913,37 @@ class LoopbackPort {
|
||||
* the constants from {@link VerbosityLevel} should be used.
|
||||
*/
|
||||
|
||||
/** @type {any} */
|
||||
const PDFWorker = (function PDFWorkerClosure() {
|
||||
const pdfWorkerPorts = new WeakMap();
|
||||
let isWorkerDisabled = false;
|
||||
let fallbackWorkerSrc;
|
||||
let nextFakeWorkerId = 0;
|
||||
let fakeWorkerCapability;
|
||||
|
||||
const PDFWorkerUtil = {
|
||||
isWorkerDisabled: false,
|
||||
fallbackWorkerSrc: null,
|
||||
fakeWorkerId: 0,
|
||||
};
|
||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
|
||||
// eslint-disable-next-line no-undef
|
||||
if (isNodeJS && typeof __non_webpack_require__ === "function") {
|
||||
// Workers aren't supported in Node.js, force-disabling them there.
|
||||
isWorkerDisabled = true;
|
||||
PDFWorkerUtil.isWorkerDisabled = true;
|
||||
|
||||
fallbackWorkerSrc = PDFJSDev.test("LIB")
|
||||
PDFWorkerUtil.fallbackWorkerSrc = PDFJSDev.test("LIB")
|
||||
? "../pdf.worker.js"
|
||||
: "./pdf.worker.js";
|
||||
} else if (typeof document === "object") {
|
||||
const pdfjsFilePath = document?.currentScript?.src;
|
||||
if (pdfjsFilePath) {
|
||||
fallbackWorkerSrc = pdfjsFilePath.replace(
|
||||
PDFWorkerUtil.fallbackWorkerSrc = pdfjsFilePath.replace(
|
||||
/(\.(?:min\.)?js)(\?.*)?$/i,
|
||||
".worker$1$2"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getWorkerSrc() {
|
||||
if (GlobalWorkerOptions.workerSrc) {
|
||||
return GlobalWorkerOptions.workerSrc;
|
||||
}
|
||||
if (typeof fallbackWorkerSrc !== "undefined") {
|
||||
if (!isNodeJS) {
|
||||
deprecated('No "GlobalWorkerOptions.workerSrc" specified.');
|
||||
}
|
||||
return fallbackWorkerSrc;
|
||||
}
|
||||
throw new Error('No "GlobalWorkerOptions.workerSrc" specified.');
|
||||
}
|
||||
|
||||
function getMainThreadWorkerMessageHandler() {
|
||||
try {
|
||||
return globalThis.pdfjsWorker?.WorkerMessageHandler || null;
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Loads worker code into main-thread.
|
||||
function setupFakeWorkerGlobal() {
|
||||
if (fakeWorkerCapability) {
|
||||
return fakeWorkerCapability.promise;
|
||||
}
|
||||
fakeWorkerCapability = createPromiseCapability();
|
||||
|
||||
const loader = async function () {
|
||||
const mainWorkerMessageHandler = getMainThreadWorkerMessageHandler();
|
||||
|
||||
if (mainWorkerMessageHandler) {
|
||||
// The worker was already loaded using e.g. a `<script>` tag.
|
||||
return mainWorkerMessageHandler;
|
||||
}
|
||||
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")) {
|
||||
const worker = await import("pdfjs/core/worker.js");
|
||||
return worker.WorkerMessageHandler;
|
||||
}
|
||||
if (
|
||||
PDFJSDev.test("GENERIC") &&
|
||||
isNodeJS &&
|
||||
// eslint-disable-next-line no-undef
|
||||
typeof __non_webpack_require__ === "function"
|
||||
) {
|
||||
// Since bundlers, such as Webpack, cannot be told to leave `require`
|
||||
// statements alone we are thus forced to jump through hoops in order
|
||||
// to prevent `Critical dependency: ...` warnings in third-party
|
||||
// deployments of the built `pdf.js`/`pdf.worker.js` files; see
|
||||
// https://github.com/webpack/webpack/issues/8826
|
||||
//
|
||||
// The following hack is based on the assumption that code running in
|
||||
// Node.js won't ever be affected by e.g. Content Security Policies that
|
||||
// prevent the use of `eval`. If that ever occurs, we should revert this
|
||||
// to a normal `__non_webpack_require__` statement and simply document
|
||||
// the Webpack warnings instead (telling users to ignore them).
|
||||
//
|
||||
// eslint-disable-next-line no-eval
|
||||
const worker = eval("require")(getWorkerSrc());
|
||||
return worker.WorkerMessageHandler;
|
||||
}
|
||||
await loadScript(getWorkerSrc());
|
||||
return window.pdfjsWorker.WorkerMessageHandler;
|
||||
};
|
||||
loader().then(fakeWorkerCapability.resolve, fakeWorkerCapability.reject);
|
||||
|
||||
return fakeWorkerCapability.promise;
|
||||
}
|
||||
|
||||
function createCDNWrapper(url) {
|
||||
PDFWorkerUtil.createCDNWrapper = function (url) {
|
||||
// We will rely on blob URL's property to specify origin.
|
||||
// We want this function to fail in case if createObjectURL or Blob do not
|
||||
// exist or fail for some reason -- our Worker creation will fail anyway.
|
||||
const wrapper = "importScripts('" + url + "');";
|
||||
const wrapper = `importScripts("${url}");`;
|
||||
return URL.createObjectURL(new Blob([wrapper]));
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2023,19 +1951,21 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
* documents. Message handlers are used to pass information from the main
|
||||
* thread to the worker thread and vice versa. If the creation of a web
|
||||
* worker is not possible, a "fake" worker will be used instead.
|
||||
*
|
||||
* @param {PDFWorkerParameters} params - The worker initialization parameters.
|
||||
*/
|
||||
// eslint-disable-next-line no-shadow
|
||||
class PDFWorker {
|
||||
/**
|
||||
* @param {PDFWorkerParameters} params - Worker initialization parameters.
|
||||
*/
|
||||
static get _workerPorts() {
|
||||
return shadow(this, "_workerPorts", new WeakMap());
|
||||
}
|
||||
|
||||
constructor({
|
||||
name = null,
|
||||
port = null,
|
||||
verbosity = getVerbosityLevel(),
|
||||
} = {}) {
|
||||
if (port && pdfWorkerPorts.has(port)) {
|
||||
throw new Error("Cannot use more than one PDFWorker per port");
|
||||
if (port && PDFWorker._workerPorts.has(port)) {
|
||||
throw new Error("Cannot use more than one PDFWorker per port.");
|
||||
}
|
||||
|
||||
this.name = name;
|
||||
@ -2049,21 +1979,33 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
this._messageHandler = null;
|
||||
|
||||
if (port) {
|
||||
pdfWorkerPorts.set(port, this);
|
||||
PDFWorker._workerPorts.set(port, this);
|
||||
this._initializeFromPort(port);
|
||||
return;
|
||||
}
|
||||
this._initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Promise for worker initialization completion.
|
||||
* @type {Promise<void>}
|
||||
*/
|
||||
get promise() {
|
||||
return this._readyCapability.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current `workerPort`, when it exists.
|
||||
* @type {Worker}
|
||||
*/
|
||||
get port() {
|
||||
return this._port;
|
||||
}
|
||||
|
||||
/**
|
||||
* The current MessageHandler-instance.
|
||||
* @type {MessageHandler}
|
||||
*/
|
||||
get messageHandler() {
|
||||
return this._messageHandler;
|
||||
}
|
||||
@ -2072,8 +2014,8 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
this._port = port;
|
||||
this._messageHandler = new MessageHandler("main", "worker", port);
|
||||
this._messageHandler.on("ready", function () {
|
||||
// Ignoring 'ready' event -- MessageHandler shall be already initialized
|
||||
// and ready to accept the messages.
|
||||
// Ignoring "ready" event -- MessageHandler should already be initialized
|
||||
// and ready to accept messages.
|
||||
});
|
||||
this._readyCapability.resolve();
|
||||
}
|
||||
@ -2086,10 +2028,10 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
// Uint8Array as it arrives on the worker. (Chrome added this with v.15.)
|
||||
if (
|
||||
typeof Worker !== "undefined" &&
|
||||
!isWorkerDisabled &&
|
||||
!getMainThreadWorkerMessageHandler()
|
||||
!PDFWorkerUtil.isWorkerDisabled &&
|
||||
!PDFWorker._mainThreadWorkerMessageHandler
|
||||
) {
|
||||
let workerSrc = getWorkerSrc();
|
||||
let workerSrc = PDFWorker.workerSrc;
|
||||
|
||||
try {
|
||||
// Wraps workerSrc path into blob URL, if the former does not belong
|
||||
@ -2099,7 +2041,7 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
PDFJSDev.test("GENERIC") &&
|
||||
!isSameOrigin(window.location.href, workerSrc)
|
||||
) {
|
||||
workerSrc = createCDNWrapper(
|
||||
workerSrc = PDFWorkerUtil.createCDNWrapper(
|
||||
new URL(workerSrc, window.location).href
|
||||
);
|
||||
}
|
||||
@ -2171,9 +2113,7 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
});
|
||||
|
||||
const sendTest = () => {
|
||||
const testObj = new Uint8Array([
|
||||
this.postMessageTransfers ? 255 : 0,
|
||||
]);
|
||||
const testObj = new Uint8Array([this.postMessageTransfers ? 255 : 0]);
|
||||
// Some versions of Opera throw a DATA_CLONE_ERR on serializing the
|
||||
// typed array. Also, checking if we can use transfers.
|
||||
try {
|
||||
@ -2185,10 +2125,9 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
}
|
||||
};
|
||||
|
||||
// It might take time for worker to initialize (especially when AMD
|
||||
// loader is used). We will try to send test immediately, and then
|
||||
// when 'ready' message will arrive. The worker shall process only
|
||||
// first received 'test'.
|
||||
// It might take time for the worker to initialize. We will try to send
|
||||
// the "test" message immediately, and once the "ready" message arrives.
|
||||
// The worker shall process only the first received "test" message.
|
||||
sendTest();
|
||||
return;
|
||||
} catch (e) {
|
||||
@ -2201,12 +2140,12 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
}
|
||||
|
||||
_setupFakeWorker() {
|
||||
if (!isWorkerDisabled) {
|
||||
if (!PDFWorkerUtil.isWorkerDisabled) {
|
||||
warn("Setting up fake worker.");
|
||||
isWorkerDisabled = true;
|
||||
PDFWorkerUtil.isWorkerDisabled = true;
|
||||
}
|
||||
|
||||
setupFakeWorkerGlobal()
|
||||
PDFWorker._setupFakeWorkerGlobal
|
||||
.then(WorkerMessageHandler => {
|
||||
if (this.destroyed) {
|
||||
this._readyCapability.reject(new Error("Worker was destroyed"));
|
||||
@ -2216,7 +2155,7 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
this._port = port;
|
||||
|
||||
// All fake workers use the same port, making id unique.
|
||||
const id = "fake" + nextFakeWorkerId++;
|
||||
const id = `fake${PDFWorkerUtil.fakeWorkerId++}`;
|
||||
|
||||
// If the main thread is our worker, setup the handling for the
|
||||
// messages -- the main thread sends to it self.
|
||||
@ -2248,7 +2187,7 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
this._webWorker.terminate();
|
||||
this._webWorker = null;
|
||||
}
|
||||
pdfWorkerPorts.delete(this._port);
|
||||
PDFWorker._workerPorts.delete(this._port);
|
||||
this._port = null;
|
||||
if (this._messageHandler) {
|
||||
this._messageHandler.destroy();
|
||||
@ -2257,25 +2196,93 @@ const PDFWorker = (function PDFWorkerClosure() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {PDFWorkerParameters} params - The worker initialization
|
||||
* parameters.
|
||||
* @param {PDFWorkerParameters} params - The worker initialization parameters.
|
||||
*/
|
||||
static fromPort(params) {
|
||||
if (!params || !params.port) {
|
||||
if (!params?.port) {
|
||||
throw new Error("PDFWorker.fromPort - invalid method signature.");
|
||||
}
|
||||
if (pdfWorkerPorts.has(params.port)) {
|
||||
return pdfWorkerPorts.get(params.port);
|
||||
if (this._workerPorts.has(params.port)) {
|
||||
return this._workerPorts.get(params.port);
|
||||
}
|
||||
return new PDFWorker(params);
|
||||
}
|
||||
|
||||
static getWorkerSrc() {
|
||||
return getWorkerSrc();
|
||||
/**
|
||||
* The current `workerSrc`, when it exists.
|
||||
* @type {string}
|
||||
*/
|
||||
static get workerSrc() {
|
||||
if (GlobalWorkerOptions.workerSrc) {
|
||||
return GlobalWorkerOptions.workerSrc;
|
||||
}
|
||||
if (PDFWorkerUtil.fallbackWorkerSrc !== null) {
|
||||
if (!isNodeJS) {
|
||||
deprecated('No "GlobalWorkerOptions.workerSrc" specified.');
|
||||
}
|
||||
return PDFWorkerUtil.fallbackWorkerSrc;
|
||||
}
|
||||
throw new Error('No "GlobalWorkerOptions.workerSrc" specified.');
|
||||
}
|
||||
|
||||
static get _mainThreadWorkerMessageHandler() {
|
||||
try {
|
||||
return globalThis.pdfjsWorker?.WorkerMessageHandler || null;
|
||||
} catch (ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return PDFWorker;
|
||||
})();
|
||||
|
||||
// Loads worker code into the main-thread.
|
||||
static get _setupFakeWorkerGlobal() {
|
||||
const loader = async () => {
|
||||
const mainWorkerMessageHandler = this._mainThreadWorkerMessageHandler;
|
||||
|
||||
if (mainWorkerMessageHandler) {
|
||||
// The worker was already loaded using e.g. a `<script>` tag.
|
||||
return mainWorkerMessageHandler;
|
||||
}
|
||||
if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")) {
|
||||
const worker = await import("pdfjs/core/worker.js");
|
||||
return worker.WorkerMessageHandler;
|
||||
}
|
||||
if (
|
||||
PDFJSDev.test("GENERIC") &&
|
||||
isNodeJS &&
|
||||
// eslint-disable-next-line no-undef
|
||||
typeof __non_webpack_require__ === "function"
|
||||
) {
|
||||
// Since bundlers, such as Webpack, cannot be told to leave `require`
|
||||
// statements alone we are thus forced to jump through hoops in order
|
||||
// to prevent `Critical dependency: ...` warnings in third-party
|
||||
// deployments of the built `pdf.js`/`pdf.worker.js` files; see
|
||||
// https://github.com/webpack/webpack/issues/8826
|
||||
//
|
||||
// The following hack is based on the assumption that code running in
|
||||
// Node.js won't ever be affected by e.g. Content Security Policies that
|
||||
// prevent the use of `eval`. If that ever occurs, we should revert this
|
||||
// to a normal `__non_webpack_require__` statement and simply document
|
||||
// the Webpack warnings instead (telling users to ignore them).
|
||||
//
|
||||
// eslint-disable-next-line no-eval
|
||||
const worker = eval("require")(this.workerSrc);
|
||||
return worker.WorkerMessageHandler;
|
||||
}
|
||||
await loadScript(this.workerSrc);
|
||||
return window.pdfjsWorker.WorkerMessageHandler;
|
||||
};
|
||||
|
||||
return shadow(this, "_setupFakeWorkerGlobal", loader());
|
||||
}
|
||||
}
|
||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
|
||||
PDFWorker.getWorkerSrc = function () {
|
||||
deprecated(
|
||||
"`PDFWorker.getWorkerSrc()`, please use `PDFWorker.workerSrc` instead."
|
||||
);
|
||||
return this.workerSrc;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* For internal use only.
|
||||
|
@ -501,7 +501,7 @@ describe("api", function () {
|
||||
pending("Worker is not supported in Node.js.");
|
||||
}
|
||||
|
||||
const workerSrc = PDFWorker.getWorkerSrc();
|
||||
const workerSrc = PDFWorker.workerSrc;
|
||||
expect(typeof workerSrc).toEqual("string");
|
||||
expect(workerSrc).toEqual(GlobalWorkerOptions.workerSrc);
|
||||
});
|
||||
|
@ -2139,7 +2139,7 @@ async function loadFakeWorker() {
|
||||
window.pdfjsWorker = await import("pdfjs/core/worker.js");
|
||||
return undefined;
|
||||
}
|
||||
return loadScript(PDFWorker.getWorkerSrc());
|
||||
return loadScript(PDFWorker.workerSrc);
|
||||
}
|
||||
|
||||
function loadAndEnablePDFBug(enabledTabs) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user