Merge pull request #13882 from Snuffleupagus/PDFWorker-rm-closure
[api-minor] Remove the closure from the `PDFWorker` class, in the `src/display/api.js` file
This commit is contained in:
commit
036b81496e
@ -1938,48 +1938,319 @@ class LoopbackPort {
|
|||||||
* the constants from {@link VerbosityLevel} should be used.
|
* the constants from {@link VerbosityLevel} should be used.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/** @type {any} */
|
const PDFWorkerUtil = {
|
||||||
const PDFWorker = (function PDFWorkerClosure() {
|
isWorkerDisabled: false,
|
||||||
const pdfWorkerPorts = new WeakMap();
|
fallbackWorkerSrc: null,
|
||||||
let isWorkerDisabled = false;
|
fakeWorkerId: 0,
|
||||||
let fallbackWorkerSrc;
|
};
|
||||||
let nextFakeWorkerId = 0;
|
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
|
||||||
let fakeWorkerCapability;
|
// eslint-disable-next-line no-undef
|
||||||
|
if (isNodeJS && typeof __non_webpack_require__ === "function") {
|
||||||
|
// Workers aren't supported in Node.js, force-disabling them there.
|
||||||
|
PDFWorkerUtil.isWorkerDisabled = true;
|
||||||
|
|
||||||
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
|
PDFWorkerUtil.fallbackWorkerSrc = PDFJSDev.test("LIB")
|
||||||
// eslint-disable-next-line no-undef
|
? "../pdf.worker.js"
|
||||||
if (isNodeJS && typeof __non_webpack_require__ === "function") {
|
: "./pdf.worker.js";
|
||||||
// Workers aren't supported in Node.js, force-disabling them there.
|
} else if (typeof document === "object") {
|
||||||
isWorkerDisabled = true;
|
const pdfjsFilePath = document?.currentScript?.src;
|
||||||
|
if (pdfjsFilePath) {
|
||||||
fallbackWorkerSrc = PDFJSDev.test("LIB")
|
PDFWorkerUtil.fallbackWorkerSrc = pdfjsFilePath.replace(
|
||||||
? "../pdf.worker.js"
|
/(\.(?:min\.)?js)(\?.*)?$/i,
|
||||||
: "./pdf.worker.js";
|
".worker$1$2"
|
||||||
} else if (typeof document === "object") {
|
);
|
||||||
const pdfjsFilePath = document?.currentScript?.src;
|
|
||||||
if (pdfjsFilePath) {
|
|
||||||
fallbackWorkerSrc = pdfjsFilePath.replace(
|
|
||||||
/(\.(?:min\.)?js)(\?.*)?$/i,
|
|
||||||
".worker$1$2"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getWorkerSrc() {
|
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}");`;
|
||||||
|
return URL.createObjectURL(new Blob([wrapper]));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PDF.js web worker abstraction that controls the instantiation of PDF
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
class PDFWorker {
|
||||||
|
static get _workerPorts() {
|
||||||
|
return shadow(this, "_workerPorts", new WeakMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor({
|
||||||
|
name = null,
|
||||||
|
port = null,
|
||||||
|
verbosity = getVerbosityLevel(),
|
||||||
|
} = {}) {
|
||||||
|
if (port && PDFWorker._workerPorts.has(port)) {
|
||||||
|
throw new Error("Cannot use more than one PDFWorker per port.");
|
||||||
|
}
|
||||||
|
|
||||||
|
this.name = name;
|
||||||
|
this.destroyed = false;
|
||||||
|
this.postMessageTransfers = true;
|
||||||
|
this.verbosity = verbosity;
|
||||||
|
|
||||||
|
this._readyCapability = createPromiseCapability();
|
||||||
|
this._port = null;
|
||||||
|
this._webWorker = null;
|
||||||
|
this._messageHandler = null;
|
||||||
|
|
||||||
|
if (port) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
_initializeFromPort(port) {
|
||||||
|
this._port = port;
|
||||||
|
this._messageHandler = new MessageHandler("main", "worker", port);
|
||||||
|
this._messageHandler.on("ready", function () {
|
||||||
|
// Ignoring "ready" event -- MessageHandler should already be initialized
|
||||||
|
// and ready to accept messages.
|
||||||
|
});
|
||||||
|
this._readyCapability.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
_initialize() {
|
||||||
|
// 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
|
||||||
|
// all requirements to run parts of pdf.js in a web worker.
|
||||||
|
// Right now, the requirement is, that an Uint8Array is still an
|
||||||
|
// Uint8Array as it arrives on the worker. (Chrome added this with v.15.)
|
||||||
|
if (
|
||||||
|
typeof Worker !== "undefined" &&
|
||||||
|
!PDFWorkerUtil.isWorkerDisabled &&
|
||||||
|
!PDFWorker._mainThreadWorkerMessageHandler
|
||||||
|
) {
|
||||||
|
let workerSrc = PDFWorker.workerSrc;
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Wraps workerSrc path into blob URL, if the former does not belong
|
||||||
|
// to the same origin.
|
||||||
|
if (
|
||||||
|
typeof PDFJSDev !== "undefined" &&
|
||||||
|
PDFJSDev.test("GENERIC") &&
|
||||||
|
!isSameOrigin(window.location.href, workerSrc)
|
||||||
|
) {
|
||||||
|
workerSrc = PDFWorkerUtil.createCDNWrapper(
|
||||||
|
new URL(workerSrc, window.location).href
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Some versions of FF can't create a worker on localhost, see:
|
||||||
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
||||||
|
const worker = new Worker(workerSrc);
|
||||||
|
const messageHandler = new MessageHandler("main", "worker", worker);
|
||||||
|
const terminateEarly = () => {
|
||||||
|
worker.removeEventListener("error", onWorkerError);
|
||||||
|
messageHandler.destroy();
|
||||||
|
worker.terminate();
|
||||||
|
if (this.destroyed) {
|
||||||
|
this._readyCapability.reject(new Error("Worker was destroyed"));
|
||||||
|
} else {
|
||||||
|
// Fall back to fake worker if the termination is caused by an
|
||||||
|
// error (e.g. NetworkError / SecurityError).
|
||||||
|
this._setupFakeWorker();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onWorkerError = () => {
|
||||||
|
if (!this._webWorker) {
|
||||||
|
// Worker failed to initialize due to an error. Clean up and fall
|
||||||
|
// back to the fake worker.
|
||||||
|
terminateEarly();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
worker.addEventListener("error", onWorkerError);
|
||||||
|
|
||||||
|
messageHandler.on("test", data => {
|
||||||
|
worker.removeEventListener("error", onWorkerError);
|
||||||
|
if (this.destroyed) {
|
||||||
|
terminateEarly();
|
||||||
|
return; // worker was destroyed
|
||||||
|
}
|
||||||
|
if (data) {
|
||||||
|
// supportTypedArray
|
||||||
|
this._messageHandler = messageHandler;
|
||||||
|
this._port = worker;
|
||||||
|
this._webWorker = worker;
|
||||||
|
if (!data.supportTransfers) {
|
||||||
|
this.postMessageTransfers = false;
|
||||||
|
}
|
||||||
|
this._readyCapability.resolve();
|
||||||
|
// Send global setting, e.g. verbosity level.
|
||||||
|
messageHandler.send("configure", {
|
||||||
|
verbosity: this.verbosity,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._setupFakeWorker();
|
||||||
|
messageHandler.destroy();
|
||||||
|
worker.terminate();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
messageHandler.on("ready", data => {
|
||||||
|
worker.removeEventListener("error", onWorkerError);
|
||||||
|
if (this.destroyed) {
|
||||||
|
terminateEarly();
|
||||||
|
return; // worker was destroyed
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
sendTest();
|
||||||
|
} catch (e) {
|
||||||
|
// We need fallback to a faked worker.
|
||||||
|
this._setupFakeWorker();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const sendTest = () => {
|
||||||
|
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 {
|
||||||
|
messageHandler.send("test", testObj, [testObj.buffer]);
|
||||||
|
} catch (ex) {
|
||||||
|
warn("Cannot use postMessage transfers.");
|
||||||
|
testObj[0] = 0;
|
||||||
|
messageHandler.send("test", testObj);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
info("The worker has been disabled.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Either workers are disabled, not supported or have thrown an exception.
|
||||||
|
// Thus, we fallback to a faked worker.
|
||||||
|
this._setupFakeWorker();
|
||||||
|
}
|
||||||
|
|
||||||
|
_setupFakeWorker() {
|
||||||
|
if (!PDFWorkerUtil.isWorkerDisabled) {
|
||||||
|
warn("Setting up fake worker.");
|
||||||
|
PDFWorkerUtil.isWorkerDisabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
PDFWorker._setupFakeWorkerGlobal
|
||||||
|
.then(WorkerMessageHandler => {
|
||||||
|
if (this.destroyed) {
|
||||||
|
this._readyCapability.reject(new Error("Worker was destroyed"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const port = new LoopbackPort();
|
||||||
|
this._port = port;
|
||||||
|
|
||||||
|
// All fake workers use the same port, making id unique.
|
||||||
|
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.
|
||||||
|
const workerHandler = new MessageHandler(id + "_worker", id, port);
|
||||||
|
WorkerMessageHandler.setup(workerHandler, port);
|
||||||
|
|
||||||
|
const messageHandler = new MessageHandler(id, id + "_worker", port);
|
||||||
|
this._messageHandler = messageHandler;
|
||||||
|
this._readyCapability.resolve();
|
||||||
|
// Send global setting, e.g. verbosity level.
|
||||||
|
messageHandler.send("configure", {
|
||||||
|
verbosity: this.verbosity,
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(reason => {
|
||||||
|
this._readyCapability.reject(
|
||||||
|
new Error(`Setting up fake worker failed: "${reason.message}".`)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Destroys the worker instance.
|
||||||
|
*/
|
||||||
|
destroy() {
|
||||||
|
this.destroyed = true;
|
||||||
|
if (this._webWorker) {
|
||||||
|
// We need to terminate only web worker created resource.
|
||||||
|
this._webWorker.terminate();
|
||||||
|
this._webWorker = null;
|
||||||
|
}
|
||||||
|
PDFWorker._workerPorts.delete(this._port);
|
||||||
|
this._port = null;
|
||||||
|
if (this._messageHandler) {
|
||||||
|
this._messageHandler.destroy();
|
||||||
|
this._messageHandler = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {PDFWorkerParameters} params - The worker initialization parameters.
|
||||||
|
*/
|
||||||
|
static fromPort(params) {
|
||||||
|
if (!params?.port) {
|
||||||
|
throw new Error("PDFWorker.fromPort - invalid method signature.");
|
||||||
|
}
|
||||||
|
if (this._workerPorts.has(params.port)) {
|
||||||
|
return this._workerPorts.get(params.port);
|
||||||
|
}
|
||||||
|
return new PDFWorker(params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current `workerSrc`, when it exists.
|
||||||
|
* @type {string}
|
||||||
|
*/
|
||||||
|
static get workerSrc() {
|
||||||
if (GlobalWorkerOptions.workerSrc) {
|
if (GlobalWorkerOptions.workerSrc) {
|
||||||
return GlobalWorkerOptions.workerSrc;
|
return GlobalWorkerOptions.workerSrc;
|
||||||
}
|
}
|
||||||
if (typeof fallbackWorkerSrc !== "undefined") {
|
if (PDFWorkerUtil.fallbackWorkerSrc !== null) {
|
||||||
if (!isNodeJS) {
|
if (!isNodeJS) {
|
||||||
deprecated('No "GlobalWorkerOptions.workerSrc" specified.');
|
deprecated('No "GlobalWorkerOptions.workerSrc" specified.');
|
||||||
}
|
}
|
||||||
return fallbackWorkerSrc;
|
return PDFWorkerUtil.fallbackWorkerSrc;
|
||||||
}
|
}
|
||||||
throw new Error('No "GlobalWorkerOptions.workerSrc" specified.');
|
throw new Error('No "GlobalWorkerOptions.workerSrc" specified.');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMainThreadWorkerMessageHandler() {
|
static get _mainThreadWorkerMessageHandler() {
|
||||||
try {
|
try {
|
||||||
return globalThis.pdfjsWorker?.WorkerMessageHandler || null;
|
return globalThis.pdfjsWorker?.WorkerMessageHandler || null;
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
@ -1987,15 +2258,10 @@ const PDFWorker = (function PDFWorkerClosure() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads worker code into main-thread.
|
// Loads worker code into the main-thread.
|
||||||
function setupFakeWorkerGlobal() {
|
static get _setupFakeWorkerGlobal() {
|
||||||
if (fakeWorkerCapability) {
|
const loader = async () => {
|
||||||
return fakeWorkerCapability.promise;
|
const mainWorkerMessageHandler = this._mainThreadWorkerMessageHandler;
|
||||||
}
|
|
||||||
fakeWorkerCapability = createPromiseCapability();
|
|
||||||
|
|
||||||
const loader = async function () {
|
|
||||||
const mainWorkerMessageHandler = getMainThreadWorkerMessageHandler();
|
|
||||||
|
|
||||||
if (mainWorkerMessageHandler) {
|
if (mainWorkerMessageHandler) {
|
||||||
// The worker was already loaded using e.g. a `<script>` tag.
|
// The worker was already loaded using e.g. a `<script>` tag.
|
||||||
@ -2024,283 +2290,24 @@ const PDFWorker = (function PDFWorkerClosure() {
|
|||||||
// the Webpack warnings instead (telling users to ignore them).
|
// the Webpack warnings instead (telling users to ignore them).
|
||||||
//
|
//
|
||||||
// eslint-disable-next-line no-eval
|
// eslint-disable-next-line no-eval
|
||||||
const worker = eval("require")(getWorkerSrc());
|
const worker = eval("require")(this.workerSrc);
|
||||||
return worker.WorkerMessageHandler;
|
return worker.WorkerMessageHandler;
|
||||||
}
|
}
|
||||||
await loadScript(getWorkerSrc());
|
await loadScript(this.workerSrc);
|
||||||
return window.pdfjsWorker.WorkerMessageHandler;
|
return window.pdfjsWorker.WorkerMessageHandler;
|
||||||
};
|
};
|
||||||
loader().then(fakeWorkerCapability.resolve, fakeWorkerCapability.reject);
|
|
||||||
|
|
||||||
return fakeWorkerCapability.promise;
|
return shadow(this, "_setupFakeWorkerGlobal", loader());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
function createCDNWrapper(url) {
|
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("GENERIC")) {
|
||||||
// We will rely on blob URL's property to specify origin.
|
PDFWorker.getWorkerSrc = function () {
|
||||||
// We want this function to fail in case if createObjectURL or Blob do not
|
deprecated(
|
||||||
// exist or fail for some reason -- our Worker creation will fail anyway.
|
"`PDFWorker.getWorkerSrc()`, please use `PDFWorker.workerSrc` instead."
|
||||||
const wrapper = "importScripts('" + url + "');";
|
);
|
||||||
return URL.createObjectURL(new Blob([wrapper]));
|
return this.workerSrc;
|
||||||
}
|
};
|
||||||
|
}
|
||||||
/**
|
|
||||||
* PDF.js web worker abstraction that controls the instantiation of PDF
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
// eslint-disable-next-line no-shadow
|
|
||||||
class PDFWorker {
|
|
||||||
/**
|
|
||||||
* @param {PDFWorkerParameters} params - Worker initialization parameters.
|
|
||||||
*/
|
|
||||||
constructor({
|
|
||||||
name = null,
|
|
||||||
port = null,
|
|
||||||
verbosity = getVerbosityLevel(),
|
|
||||||
} = {}) {
|
|
||||||
if (port && pdfWorkerPorts.has(port)) {
|
|
||||||
throw new Error("Cannot use more than one PDFWorker per port");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.name = name;
|
|
||||||
this.destroyed = false;
|
|
||||||
this.postMessageTransfers = true;
|
|
||||||
this.verbosity = verbosity;
|
|
||||||
|
|
||||||
this._readyCapability = createPromiseCapability();
|
|
||||||
this._port = null;
|
|
||||||
this._webWorker = null;
|
|
||||||
this._messageHandler = null;
|
|
||||||
|
|
||||||
if (port) {
|
|
||||||
pdfWorkerPorts.set(port, this);
|
|
||||||
this._initializeFromPort(port);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
this._initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
get promise() {
|
|
||||||
return this._readyCapability.promise;
|
|
||||||
}
|
|
||||||
|
|
||||||
get port() {
|
|
||||||
return this._port;
|
|
||||||
}
|
|
||||||
|
|
||||||
get messageHandler() {
|
|
||||||
return this._messageHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
_initializeFromPort(port) {
|
|
||||||
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.
|
|
||||||
});
|
|
||||||
this._readyCapability.resolve();
|
|
||||||
}
|
|
||||||
|
|
||||||
_initialize() {
|
|
||||||
// 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
|
|
||||||
// all requirements to run parts of pdf.js in a web worker.
|
|
||||||
// Right now, the requirement is, that an Uint8Array is still an
|
|
||||||
// Uint8Array as it arrives on the worker. (Chrome added this with v.15.)
|
|
||||||
if (
|
|
||||||
typeof Worker !== "undefined" &&
|
|
||||||
!isWorkerDisabled &&
|
|
||||||
!getMainThreadWorkerMessageHandler()
|
|
||||||
) {
|
|
||||||
let workerSrc = getWorkerSrc();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Wraps workerSrc path into blob URL, if the former does not belong
|
|
||||||
// to the same origin.
|
|
||||||
if (
|
|
||||||
typeof PDFJSDev !== "undefined" &&
|
|
||||||
PDFJSDev.test("GENERIC") &&
|
|
||||||
!isSameOrigin(window.location.href, workerSrc)
|
|
||||||
) {
|
|
||||||
workerSrc = createCDNWrapper(
|
|
||||||
new URL(workerSrc, window.location).href
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Some versions of FF can't create a worker on localhost, see:
|
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
|
||||||
const worker = new Worker(workerSrc);
|
|
||||||
const messageHandler = new MessageHandler("main", "worker", worker);
|
|
||||||
const terminateEarly = () => {
|
|
||||||
worker.removeEventListener("error", onWorkerError);
|
|
||||||
messageHandler.destroy();
|
|
||||||
worker.terminate();
|
|
||||||
if (this.destroyed) {
|
|
||||||
this._readyCapability.reject(new Error("Worker was destroyed"));
|
|
||||||
} else {
|
|
||||||
// Fall back to fake worker if the termination is caused by an
|
|
||||||
// error (e.g. NetworkError / SecurityError).
|
|
||||||
this._setupFakeWorker();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onWorkerError = () => {
|
|
||||||
if (!this._webWorker) {
|
|
||||||
// Worker failed to initialize due to an error. Clean up and fall
|
|
||||||
// back to the fake worker.
|
|
||||||
terminateEarly();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
worker.addEventListener("error", onWorkerError);
|
|
||||||
|
|
||||||
messageHandler.on("test", data => {
|
|
||||||
worker.removeEventListener("error", onWorkerError);
|
|
||||||
if (this.destroyed) {
|
|
||||||
terminateEarly();
|
|
||||||
return; // worker was destroyed
|
|
||||||
}
|
|
||||||
if (data) {
|
|
||||||
// supportTypedArray
|
|
||||||
this._messageHandler = messageHandler;
|
|
||||||
this._port = worker;
|
|
||||||
this._webWorker = worker;
|
|
||||||
if (!data.supportTransfers) {
|
|
||||||
this.postMessageTransfers = false;
|
|
||||||
}
|
|
||||||
this._readyCapability.resolve();
|
|
||||||
// Send global setting, e.g. verbosity level.
|
|
||||||
messageHandler.send("configure", {
|
|
||||||
verbosity: this.verbosity,
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
this._setupFakeWorker();
|
|
||||||
messageHandler.destroy();
|
|
||||||
worker.terminate();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
messageHandler.on("ready", data => {
|
|
||||||
worker.removeEventListener("error", onWorkerError);
|
|
||||||
if (this.destroyed) {
|
|
||||||
terminateEarly();
|
|
||||||
return; // worker was destroyed
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
sendTest();
|
|
||||||
} catch (e) {
|
|
||||||
// We need fallback to a faked worker.
|
|
||||||
this._setupFakeWorker();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const sendTest = () => {
|
|
||||||
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 {
|
|
||||||
messageHandler.send("test", testObj, [testObj.buffer]);
|
|
||||||
} catch (ex) {
|
|
||||||
warn("Cannot use postMessage transfers.");
|
|
||||||
testObj[0] = 0;
|
|
||||||
messageHandler.send("test", testObj);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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'.
|
|
||||||
sendTest();
|
|
||||||
return;
|
|
||||||
} catch (e) {
|
|
||||||
info("The worker has been disabled.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Either workers are disabled, not supported or have thrown an exception.
|
|
||||||
// Thus, we fallback to a faked worker.
|
|
||||||
this._setupFakeWorker();
|
|
||||||
}
|
|
||||||
|
|
||||||
_setupFakeWorker() {
|
|
||||||
if (!isWorkerDisabled) {
|
|
||||||
warn("Setting up fake worker.");
|
|
||||||
isWorkerDisabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
setupFakeWorkerGlobal()
|
|
||||||
.then(WorkerMessageHandler => {
|
|
||||||
if (this.destroyed) {
|
|
||||||
this._readyCapability.reject(new Error("Worker was destroyed"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const port = new LoopbackPort();
|
|
||||||
this._port = port;
|
|
||||||
|
|
||||||
// All fake workers use the same port, making id unique.
|
|
||||||
const id = "fake" + nextFakeWorkerId++;
|
|
||||||
|
|
||||||
// If the main thread is our worker, setup the handling for the
|
|
||||||
// messages -- the main thread sends to it self.
|
|
||||||
const workerHandler = new MessageHandler(id + "_worker", id, port);
|
|
||||||
WorkerMessageHandler.setup(workerHandler, port);
|
|
||||||
|
|
||||||
const messageHandler = new MessageHandler(id, id + "_worker", port);
|
|
||||||
this._messageHandler = messageHandler;
|
|
||||||
this._readyCapability.resolve();
|
|
||||||
// Send global setting, e.g. verbosity level.
|
|
||||||
messageHandler.send("configure", {
|
|
||||||
verbosity: this.verbosity,
|
|
||||||
});
|
|
||||||
})
|
|
||||||
.catch(reason => {
|
|
||||||
this._readyCapability.reject(
|
|
||||||
new Error(`Setting up fake worker failed: "${reason.message}".`)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroys the worker instance.
|
|
||||||
*/
|
|
||||||
destroy() {
|
|
||||||
this.destroyed = true;
|
|
||||||
if (this._webWorker) {
|
|
||||||
// We need to terminate only web worker created resource.
|
|
||||||
this._webWorker.terminate();
|
|
||||||
this._webWorker = null;
|
|
||||||
}
|
|
||||||
pdfWorkerPorts.delete(this._port);
|
|
||||||
this._port = null;
|
|
||||||
if (this._messageHandler) {
|
|
||||||
this._messageHandler.destroy();
|
|
||||||
this._messageHandler = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {PDFWorkerParameters} params - The worker initialization
|
|
||||||
* parameters.
|
|
||||||
*/
|
|
||||||
static fromPort(params) {
|
|
||||||
if (!params || !params.port) {
|
|
||||||
throw new Error("PDFWorker.fromPort - invalid method signature.");
|
|
||||||
}
|
|
||||||
if (pdfWorkerPorts.has(params.port)) {
|
|
||||||
return pdfWorkerPorts.get(params.port);
|
|
||||||
}
|
|
||||||
return new PDFWorker(params);
|
|
||||||
}
|
|
||||||
|
|
||||||
static getWorkerSrc() {
|
|
||||||
return getWorkerSrc();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return PDFWorker;
|
|
||||||
})();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For internal use only.
|
* For internal use only.
|
||||||
|
@ -501,7 +501,7 @@ describe("api", function () {
|
|||||||
pending("Worker is not supported in Node.js.");
|
pending("Worker is not supported in Node.js.");
|
||||||
}
|
}
|
||||||
|
|
||||||
const workerSrc = PDFWorker.getWorkerSrc();
|
const workerSrc = PDFWorker.workerSrc;
|
||||||
expect(typeof workerSrc).toEqual("string");
|
expect(typeof workerSrc).toEqual("string");
|
||||||
expect(workerSrc).toEqual(GlobalWorkerOptions.workerSrc);
|
expect(workerSrc).toEqual(GlobalWorkerOptions.workerSrc);
|
||||||
});
|
});
|
||||||
|
@ -2139,7 +2139,7 @@ async function loadFakeWorker() {
|
|||||||
window.pdfjsWorker = await import("pdfjs/core/worker.js");
|
window.pdfjsWorker = await import("pdfjs/core/worker.js");
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
return loadScript(PDFWorker.getWorkerSrc());
|
return loadScript(PDFWorker.workerSrc);
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadAndEnablePDFBug(enabledTabs) {
|
function loadAndEnablePDFBug(enabledTabs) {
|
||||||
|
Loading…
Reference in New Issue
Block a user