pdf.js/web/app_options.js
Jonas Jenwald 5b94ed5487 Remove the disableCreateObjectURL option from web/app_options.js
Prior to PR 11601, the `disableCreateObjectURL` option was present on `getDocument` in the API, since it was (potentially) used when decoding JPEG images natively in the browser. Hence setting this option, which was done automatically using compatibility-code, were in some browsers necessary in order for e.g. JPEG images to be correctly rendered.

The downside of the `disableCreateObjectURL` option is that memory usage increases significantly, since we're forced to build and use `data:` URIs (rather than `blob:` URLs).
However, at this point in time the `disableCreateObjectURL` option is only necessary for *some* (non-essential) functionality in the default viewer; in particular:
 - The openfile functionality, used only when manually opening a new file in the default viewer.
 - The download functionality, used when downloading either the PDF document itself or its attached files (if such exists).
 - The print functionality, in the generic `PDFPrintService` implementation.

Hence neither the general PDF.js library, nor the *basic* functionality of the default viewer, depends on the `disableCreateObjectURL` option any more; which is why I'm thus proposing that we remove the option since using it is a performance footgun.

*Please note:* To not outright break currently "supported" browsers, which lack proper `URL.createObjectURL` support, this patch purposely keeps the compatibility-code to explicitly disable `URL.createObjectURL` usage *only* for browsers which are known to not work correctly.[1]

While it's certainly possible that there's additional, likely older, browsers with broken `URL.createObjectURL` support, the last time that these types of problems were reported was over *three* years ago.[2]
Hence in the *very* unlikely event that additional problems occur, as a result of these changes, we can either add a new case in the compatibility-code or simply declare the affected browser as unsupported.

---
[1] Which are IE11 (see issue 3977), and Google Chrome on iOS (see PR 8081).

[2] Given that `URL.createObjectURL` is used by default, you'd really expect more reports if these problems were widespread.
2020-08-10 15:56:30 +02:00

313 lines
7.5 KiB
JavaScript

/* Copyright 2018 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 { viewerCompatibilityParams } from "./viewer_compatibility.js";
const OptionKind = {
VIEWER: 0x02,
API: 0x04,
WORKER: 0x08,
PREFERENCE: 0x80,
};
/**
* PLEASE NOTE: To avoid introducing unnecessary dependencies, we specify the
* values below *explicitly* rather than relying on imported types.
*/
const defaultOptions = {
cursorToolOnLoad: {
/** @type {number} */
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
defaultUrl: {
/** @type {string} */
value: "compressed.tracemonkey-pldi-09.pdf",
kind: OptionKind.VIEWER,
},
defaultZoomValue: {
/** @type {string} */
value: "",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
disableHistory: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER,
},
disablePageLabels: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
/**
* The `disablePreferences` is, conditionally, defined below.
*/
enablePermissions: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
enablePrintAutoRotate: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
enableWebGL: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
externalLinkRel: {
/** @type {string} */
value: "noopener noreferrer nofollow",
kind: OptionKind.VIEWER,
},
externalLinkTarget: {
/** @type {number} */
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
historyUpdateUrl: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
ignoreDestinationZoom: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
imageResourcesPath: {
/** @type {string} */
value: "./images/",
kind: OptionKind.VIEWER,
},
/**
* The `locale` is, conditionally, defined below.
*/
maxCanvasPixels: {
/** @type {number} */
value: 16777216,
compatibility: viewerCompatibilityParams.maxCanvasPixels,
kind: OptionKind.VIEWER,
},
pdfBugEnabled: {
/** @type {boolean} */
value: typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION"),
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
printResolution: {
/** @type {number} */
value: 150,
kind: OptionKind.VIEWER,
},
renderer: {
/** @type {string} */
value: "canvas",
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
renderInteractiveForms: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
sidebarViewOnLoad: {
/** @type {number} */
value: -1,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
scrollModeOnLoad: {
/** @type {number} */
value: -1,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
spreadModeOnLoad: {
/** @type {number} */
value: -1,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
textLayerMode: {
/** @type {number} */
value: 1,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
useOnlyCssZoom: {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
viewOnLoad: {
/** @type {boolean} */
value: 0,
kind: OptionKind.VIEWER + OptionKind.PREFERENCE,
},
cMapPacked: {
/** @type {boolean} */
value: true,
kind: OptionKind.API,
},
cMapUrl: {
/** @type {string} */
value:
typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
? "../external/bcmaps/"
: "../web/cmaps/",
kind: OptionKind.API,
},
disableAutoFetch: {
/** @type {boolean} */
value: false,
kind: OptionKind.API + OptionKind.PREFERENCE,
},
disableFontFace: {
/** @type {boolean} */
value: false,
kind: OptionKind.API + OptionKind.PREFERENCE,
},
disableRange: {
/** @type {boolean} */
value: false,
kind: OptionKind.API + OptionKind.PREFERENCE,
},
disableStream: {
/** @type {boolean} */
value: false,
kind: OptionKind.API + OptionKind.PREFERENCE,
},
docBaseUrl: {
/** @type {string} */
value: "",
kind: OptionKind.API,
},
fontExtraProperties: {
/** @type {boolean} */
value: false,
kind: OptionKind.API,
},
isEvalSupported: {
/** @type {boolean} */
value: true,
kind: OptionKind.API,
},
maxImageSize: {
/** @type {number} */
value: -1,
kind: OptionKind.API,
},
pdfBug: {
/** @type {boolean} */
value: false,
kind: OptionKind.API,
},
verbosity: {
/** @type {number} */
value: 1,
kind: OptionKind.API,
},
workerPort: {
/** @type {Object} */
value: null,
kind: OptionKind.WORKER,
},
workerSrc: {
/** @type {string} */
value:
typeof PDFJSDev === "undefined" || !PDFJSDev.test("PRODUCTION")
? "../src/worker_loader.js"
: "../build/pdf.worker.js",
kind: OptionKind.WORKER,
},
};
if (
typeof PDFJSDev === "undefined" ||
PDFJSDev.test("!PRODUCTION || (GENERIC && !LIB)")
) {
defaultOptions.disablePreferences = {
/** @type {boolean} */
value: false,
kind: OptionKind.VIEWER,
};
defaultOptions.locale = {
/** @type {string} */
value: typeof navigator !== "undefined" ? navigator.language : "en-US",
kind: OptionKind.VIEWER,
};
}
const userOptions = Object.create(null);
class AppOptions {
constructor() {
throw new Error("Cannot initialize AppOptions.");
}
static get(name) {
const userOption = userOptions[name];
if (userOption !== undefined) {
return userOption;
}
const defaultOption = defaultOptions[name];
if (defaultOption !== undefined) {
return defaultOption.compatibility || defaultOption.value;
}
return undefined;
}
static getAll(kind = null) {
const options = Object.create(null);
for (const name in defaultOptions) {
const defaultOption = defaultOptions[name];
if (kind) {
if ((kind & defaultOption.kind) === 0) {
continue;
}
if (kind === OptionKind.PREFERENCE) {
const value = defaultOption.value,
valueType = typeof value;
if (
valueType === "boolean" ||
valueType === "string" ||
(valueType === "number" && Number.isInteger(value))
) {
options[name] = value;
continue;
}
throw new Error(`Invalid type for preference: ${name}`);
}
}
const userOption = userOptions[name];
options[name] =
userOption !== undefined
? userOption
: defaultOption.compatibility || defaultOption.value;
}
return options;
}
static set(name, value) {
userOptions[name] = value;
}
static remove(name) {
delete userOptions[name];
}
}
export { AppOptions, OptionKind };