Given that this file represents the official API, it's difficult to avoid it becoming fairly large as we add new functionality. However, it also contains a couple of smaller (and internal) helpers that we can move into a new utils-file. Also, we inline the `DEFAULT_RANGE_CHUNK_SIZE` constant since it's only used *once* and its value has never been changed in over a decade.
168 lines
4.3 KiB
JavaScript
168 lines
4.3 KiB
JavaScript
/* Copyright 2012 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
import {
|
|
_isValidExplicitDest,
|
|
isNodeJS,
|
|
stringToBytes,
|
|
warn,
|
|
} from "../shared/util.js";
|
|
|
|
function getUrlProp(val) {
|
|
if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) {
|
|
return null; // The 'url' is unused with `PDFDataRangeTransport`.
|
|
}
|
|
if (val instanceof URL) {
|
|
return val.href;
|
|
}
|
|
if (typeof val === "string") {
|
|
if (
|
|
typeof PDFJSDev !== "undefined" &&
|
|
PDFJSDev.test("GENERIC") &&
|
|
isNodeJS
|
|
) {
|
|
return val; // Use the url as-is in Node.js environments.
|
|
}
|
|
|
|
// The full path is required in the 'url' field.
|
|
const url = URL.parse(val, window.location);
|
|
if (url) {
|
|
return url.href;
|
|
}
|
|
}
|
|
throw new Error(
|
|
"Invalid PDF url data: " +
|
|
"either string or URL-object is expected in the url property."
|
|
);
|
|
}
|
|
|
|
function getDataProp(val) {
|
|
// Converting string or array-like data to Uint8Array.
|
|
if (
|
|
typeof PDFJSDev !== "undefined" &&
|
|
PDFJSDev.test("GENERIC") &&
|
|
isNodeJS &&
|
|
typeof Buffer !== "undefined" && // eslint-disable-line no-undef
|
|
val instanceof Buffer // eslint-disable-line no-undef
|
|
) {
|
|
throw new Error(
|
|
"Please provide binary data as `Uint8Array`, rather than `Buffer`."
|
|
);
|
|
}
|
|
if (val instanceof Uint8Array && val.byteLength === val.buffer.byteLength) {
|
|
// Use the data as-is when it's already a Uint8Array that completely
|
|
// "utilizes" its underlying ArrayBuffer, to prevent any possible
|
|
// issues when transferring it to the worker-thread.
|
|
return val;
|
|
}
|
|
if (typeof val === "string") {
|
|
return stringToBytes(val);
|
|
}
|
|
if (
|
|
val instanceof ArrayBuffer ||
|
|
ArrayBuffer.isView(val) ||
|
|
(typeof val === "object" && !isNaN(val?.length))
|
|
) {
|
|
return new Uint8Array(val);
|
|
}
|
|
throw new Error(
|
|
"Invalid PDF binary data: either TypedArray, " +
|
|
"string, or array-like object is expected in the data property."
|
|
);
|
|
}
|
|
|
|
function getFactoryUrlProp(val) {
|
|
if (typeof val !== "string") {
|
|
return null;
|
|
}
|
|
if (val.endsWith("/")) {
|
|
return val;
|
|
}
|
|
throw new Error(`Invalid factory url: "${val}" must include trailing slash.`);
|
|
}
|
|
|
|
const isRefProxy = v =>
|
|
typeof v === "object" &&
|
|
Number.isInteger(v?.num) &&
|
|
v.num >= 0 &&
|
|
Number.isInteger(v?.gen) &&
|
|
v.gen >= 0;
|
|
|
|
const isNameProxy = v => typeof v === "object" && typeof v?.name === "string";
|
|
|
|
const isValidExplicitDest = _isValidExplicitDest.bind(
|
|
null,
|
|
/* validRef = */ isRefProxy,
|
|
/* validName = */ isNameProxy
|
|
);
|
|
|
|
class LoopbackPort {
|
|
#listeners = new Map();
|
|
|
|
#deferred = Promise.resolve();
|
|
|
|
postMessage(obj, transfer) {
|
|
const event = {
|
|
data: structuredClone(obj, transfer ? { transfer } : null),
|
|
};
|
|
|
|
this.#deferred.then(() => {
|
|
for (const [listener] of this.#listeners) {
|
|
listener.call(this, event);
|
|
}
|
|
});
|
|
}
|
|
|
|
addEventListener(name, listener, options = null) {
|
|
let rmAbort = null;
|
|
if (options?.signal instanceof AbortSignal) {
|
|
const { signal } = options;
|
|
if (signal.aborted) {
|
|
warn("LoopbackPort - cannot use an `aborted` signal.");
|
|
return;
|
|
}
|
|
const onAbort = () => this.removeEventListener(name, listener);
|
|
rmAbort = () => signal.removeEventListener("abort", onAbort);
|
|
|
|
signal.addEventListener("abort", onAbort);
|
|
}
|
|
this.#listeners.set(listener, rmAbort);
|
|
}
|
|
|
|
removeEventListener(name, listener) {
|
|
const rmAbort = this.#listeners.get(listener);
|
|
rmAbort?.();
|
|
|
|
this.#listeners.delete(listener);
|
|
}
|
|
|
|
terminate() {
|
|
for (const [, rmAbort] of this.#listeners) {
|
|
rmAbort?.();
|
|
}
|
|
this.#listeners.clear();
|
|
}
|
|
}
|
|
|
|
export {
|
|
getDataProp,
|
|
getFactoryUrlProp,
|
|
getUrlProp,
|
|
isNameProxy,
|
|
isRefProxy,
|
|
isValidExplicitDest,
|
|
LoopbackPort,
|
|
};
|