diff --git a/toolkit/components/pdfjs/content/PdfJs.jsm b/toolkit/components/pdfjs/content/PdfJs.jsm deleted file mode 100644 --- a/toolkit/components/pdfjs/content/PdfJs.jsm +++ /dev/null @@ -1,268 +0,0 @@ -/* 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. - */ - -"use strict"; - -var EXPORTED_SYMBOLS = ["PdfJs"]; - -const PREF_PREFIX = "pdfjs"; -const PREF_DISABLED = PREF_PREFIX + ".disabled"; -const PREF_MIGRATION_VERSION = PREF_PREFIX + ".migrationVersion"; -const PREF_PREVIOUS_ACTION = PREF_PREFIX + ".previousHandler.preferredAction"; -const PREF_PREVIOUS_ASK = - PREF_PREFIX + ".previousHandler.alwaysAskBeforeHandling"; -const PREF_ISDEFAULT_CACHE_STATE = PREF_PREFIX + ".enabledCache.state"; -const TOPIC_PDFJS_HANDLER_CHANGED = "pdfjs:handlerChanged"; -const PDF_CONTENT_TYPE = "application/pdf"; - -const { XPCOMUtils } = ChromeUtils.import( - "resource://gre/modules/XPCOMUtils.jsm" -); -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); - -var Svc = {}; -XPCOMUtils.defineLazyServiceGetter( - Svc, - "mime", - "@mozilla.org/mime;1", - "nsIMIMEService" -); -XPCOMUtils.defineLazyServiceGetter( - Svc, - "handlerService", - "@mozilla.org/uriloader/handler-service;1", - "nsIHandlerService" -); -ChromeUtils.defineModuleGetter( - this, - "PdfJsDefaultPreferences", - "resource://pdf.js/PdfJsDefaultPreferences.jsm" -); - -function initializeDefaultPreferences() { - var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + "."); - var defaultValue; - for (var key in PdfJsDefaultPreferences) { - // Skip prefs that are already defined, so we can enable/disable things - // in all.js. - let prefType = defaultBranch.getPrefType(key); - if (prefType !== Ci.nsIPrefBranch.PREF_INVALID) { - continue; - } - defaultValue = PdfJsDefaultPreferences[key]; - switch (typeof defaultValue) { - case "boolean": - defaultBranch.setBoolPref(key, defaultValue); - break; - case "number": - defaultBranch.setIntPref(key, defaultValue); - break; - case "string": - defaultBranch.setCharPref(key, defaultValue); - break; - } - } -} - -// We're supposed to get this type of thing from the OS, and generally we do. -// But doing so is expensive, so on startup paths we can use this to make the -// handler service get and store the Right Thing (it just goes into a JSON -// file) and avoid the performance issues. -const gPdfFakeHandlerInfo = { - QueryInterface: ChromeUtils.generateQI(["nsIMIMEInfo"]), - getFileExtensions() { - return ["pdf"]; - }, - possibleApplicationHandlers: Cc["@mozilla.org/array;1"].createInstance( - Ci.nsIMutableArray - ), - extensionExists(ext) { - return ext == "pdf"; - }, - alwaysAskBeforeHandling: false, - preferredAction: Ci.nsIHandlerInfo.handleInternally, - type: PDF_CONTENT_TYPE, -}; - -var PdfJs = { - QueryInterface: ChromeUtils.generateQI(["nsIObserver"]), - _initialized: false, - _cachedIsDefault: true, - - init: function init(isNewProfile) { - if ( - Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT - ) { - throw new Error( - "PdfJs.init should only get called in the parent process." - ); - } - this.initPrefs(); - this.checkIsDefault(isNewProfile); - }, - - initPrefs: function initPrefs() { - if (this._initialized) { - return; - } - this._initialized = true; - - if (this._prefDisabled) { - this._unbecomeHandler(); - } else { - this._migrate(); - } - - // Listen for when a different pdf handler is chosen. - Services.obs.addObserver(this, TOPIC_PDFJS_HANDLER_CHANGED); - - initializeDefaultPreferences(); - }, - - uninit: function uninit() { - if (this._initialized) { - Services.obs.removeObserver(this, TOPIC_PDFJS_HANDLER_CHANGED); - this._initialized = false; - } - }, - - _migrate: function migrate() { - const VERSION = 2; - var currentVersion = Services.prefs.getIntPref(PREF_MIGRATION_VERSION, 0); - if (currentVersion >= VERSION) { - return; - } - // Make pdf.js the default pdf viewer on the first migration. - if (currentVersion < 1) { - this._becomeHandler(); - } - if (currentVersion < 2) { - // cleaning up of unused database preference (see #3994) - Services.prefs.clearUserPref(PREF_PREFIX + ".database"); - } - Services.prefs.setIntPref(PREF_MIGRATION_VERSION, VERSION); - }, - - _becomeHandler: function _becomeHandler() { - // Normally, this happens in the first run at some point, where the - // handler service doesn't have any info on user preferences yet. - // Don't bother storing old defaults in this case, as they're - // meaningless anyway. - if (!Svc.handlerService.exists(gPdfFakeHandlerInfo)) { - // Store the requisite info into the DB, and nothing else: - Svc.handlerService.store(gPdfFakeHandlerInfo); - } else { - let handlerInfo = Svc.mime.getFromTypeAndExtension( - PDF_CONTENT_TYPE, - "pdf" - ); - let prefs = Services.prefs; - if ( - handlerInfo.preferredAction !== Ci.nsIHandlerInfo.handleInternally && - handlerInfo.alwaysAskBeforeHandling !== false - ) { - // Store the previous settings of preferredAction and - // alwaysAskBeforeHandling in case we need to revert them in a hotfix that - // would turn pdf.js off. - prefs.setIntPref(PREF_PREVIOUS_ACTION, handlerInfo.preferredAction); - prefs.setBoolPref( - PREF_PREVIOUS_ASK, - handlerInfo.alwaysAskBeforeHandling - ); - } - - // Change and save mime handler settings. - handlerInfo.alwaysAskBeforeHandling = false; - handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally; - Svc.handlerService.store(handlerInfo); - } - }, - - _unbecomeHandler: function _unbecomeHandler() { - let handlerInfo = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, "pdf"); - if (handlerInfo.preferredAction === Ci.nsIHandlerInfo.handleInternally) { - // If PDFJS is disabled, but we're still marked to handleInternally, - // either put it back to what it was, or remove it. - if (Services.prefs.prefHasUserValue(PREF_PREVIOUS_ACTION)) { - handlerInfo.preferredAction = Services.prefs.getIntPref( - PREF_PREVIOUS_ACTION - ); - handlerInfo.alwaysAskBeforeHandling = Services.prefs.getBoolPref( - PREF_PREVIOUS_ASK - ); - Svc.handlerService.store(handlerInfo); - } else { - Svc.handlerService.remove(handlerInfo); - // Clear migration pref so the handler comes back if reenabled - Services.prefs.clearUserPref(PREF_MIGRATION_VERSION); - } - } - }, - - /** - * @param isNewProfile used to decide whether we need to check the - * handler service to see if the user configured - * pdfs differently. If we're on a new profile, - * there's no need to check. - */ - _isDefault(isNewProfile) { - let { processType, PROCESS_TYPE_DEFAULT } = Services.appinfo; - if (processType !== PROCESS_TYPE_DEFAULT) { - throw new Error( - "isDefault should only get called in the parent process." - ); - } - - if (this._prefDisabled) { - return false; - } - - // Don't bother with the handler service on a new profile: - if (isNewProfile) { - return true; - } - // Check if the 'application/pdf' preview handler is configured properly. - let handlerInfo = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, "pdf"); - return ( - !handlerInfo.alwaysAskBeforeHandling && - handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally - ); - }, - - checkIsDefault(isNewProfile) { - this._cachedIsDefault = this._isDefault(isNewProfile); - Services.prefs.setBoolPref( - PREF_ISDEFAULT_CACHE_STATE, - this._cachedIsDefault - ); - }, - - cachedIsDefault() { - return this._cachedIsDefault; - }, - - // nsIObserver - observe(aSubject, aTopic, aData) { - this.checkIsDefault(); - }, -}; - -XPCOMUtils.defineLazyPreferenceGetter( - PdfJs, - "_prefDisabled", - PREF_DISABLED, - false, - () => PdfJs.checkIsDefault() -); diff --git a/toolkit/components/pdfjs/content/PdfJsNetwork.jsm b/toolkit/components/pdfjs/content/PdfJsNetwork.jsm deleted file mode 100644 --- a/toolkit/components/pdfjs/content/PdfJsNetwork.jsm +++ /dev/null @@ -1,226 +0,0 @@ -/* 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. - */ - -"use strict"; - -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); - -var EXPORTED_SYMBOLS = ["NetworkManager"]; - -// eslint-disable-next-line no-unused-vars -function log(aMsg) { - var msg = "PdfJsNetwork.jsm: " + (aMsg.join ? aMsg.join("") : aMsg); - Services.console.logStringMessage(msg); -} - -var NetworkManager = (function NetworkManagerClosure() { - const OK_RESPONSE = 200; - const PARTIAL_CONTENT_RESPONSE = 206; - - function getArrayBuffer(xhr) { - var data = xhr.response; - if (typeof data !== "string") { - return data; - } - var length = data.length; - var array = new Uint8Array(length); - for (var i = 0; i < length; i++) { - array[i] = data.charCodeAt(i) & 0xff; - } - return array.buffer; - } - - class NetworkManagerClass { - constructor(url, args) { - this.url = url; - args = args || {}; - this.isHttp = /^https?:/i.test(url); - this.httpHeaders = (this.isHttp && args.httpHeaders) || {}; - this.withCredentials = args.withCredentials || false; - this.getXhr = - args.getXhr || - function NetworkManager_getXhr() { - return new XMLHttpRequest(); - }; - - this.currXhrId = 0; - this.pendingRequests = Object.create(null); - } - - requestRange(begin, end, listeners) { - var args = { - begin, - end, - }; - for (var prop in listeners) { - args[prop] = listeners[prop]; - } - return this.request(args); - } - - requestFull(listeners) { - return this.request(listeners); - } - - request(args) { - var xhr = this.getXhr(); - var xhrId = this.currXhrId++; - var pendingRequest = (this.pendingRequests[xhrId] = { - xhr, - }); - - xhr.open("GET", this.url); - xhr.withCredentials = this.withCredentials; - for (var property in this.httpHeaders) { - var value = this.httpHeaders[property]; - if (typeof value === "undefined") { - continue; - } - xhr.setRequestHeader(property, value); - } - if (this.isHttp && "begin" in args && "end" in args) { - var rangeStr = args.begin + "-" + (args.end - 1); - xhr.setRequestHeader("Range", "bytes=" + rangeStr); - pendingRequest.expectedStatus = 206; - xhr.channel.QueryInterface(Ci.nsIHttpChannel).redirectionLimit = 0; - } else { - pendingRequest.expectedStatus = 200; - } - - xhr.responseType = "arraybuffer"; - - if (args.onError) { - xhr.onerror = function(evt) { - args.onError(xhr.status); - }; - } - xhr.onreadystatechange = this.onStateChange.bind(this, xhrId); - xhr.onprogress = this.onProgress.bind(this, xhrId); - - pendingRequest.onHeadersReceived = args.onHeadersReceived; - pendingRequest.onDone = args.onDone; - pendingRequest.onError = args.onError; - pendingRequest.onProgress = args.onProgress; - - xhr.send(null); - - return xhrId; - } - - onProgress(xhrId, evt) { - var pendingRequest = this.pendingRequests[xhrId]; - if (!pendingRequest) { - // Maybe abortRequest was called... - return; - } - - var onProgress = pendingRequest.onProgress; - if (onProgress) { - onProgress(evt); - } - } - - onStateChange(xhrId, evt) { - var pendingRequest = this.pendingRequests[xhrId]; - if (!pendingRequest) { - // Maybe abortRequest was called... - return; - } - - var xhr = pendingRequest.xhr; - if (xhr.readyState >= 2 && pendingRequest.onHeadersReceived) { - pendingRequest.onHeadersReceived(); - delete pendingRequest.onHeadersReceived; - } - - if (xhr.readyState !== 4) { - return; - } - - if (!(xhrId in this.pendingRequests)) { - // The XHR request might have been aborted in onHeadersReceived() - // callback, in which case we should abort request - return; - } - - delete this.pendingRequests[xhrId]; - - // success status == 0 can be on ftp, file and other protocols - if (xhr.status === 0 && this.isHttp) { - if (pendingRequest.onError) { - pendingRequest.onError(xhr.status); - } - return; - } - var xhrStatus = xhr.status || OK_RESPONSE; - - // From http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.35.2: - // "A server MAY ignore the Range header". This means it's possible to - // get a 200 rather than a 206 response from a range request. - var ok_response_on_range_request = - xhrStatus === OK_RESPONSE && - pendingRequest.expectedStatus === PARTIAL_CONTENT_RESPONSE; - - if ( - !ok_response_on_range_request && - xhrStatus !== pendingRequest.expectedStatus - ) { - if (pendingRequest.onError) { - pendingRequest.onError(xhr.status); - } - return; - } - - var chunk = getArrayBuffer(xhr); - if (xhrStatus === PARTIAL_CONTENT_RESPONSE) { - var rangeHeader = xhr.getResponseHeader("Content-Range"); - var matches = /bytes (\d+)-(\d+)\/(\d+)/.exec(rangeHeader); - var begin = parseInt(matches[1], 10); - pendingRequest.onDone({ - begin, - chunk, - }); - } else if (chunk) { - pendingRequest.onDone({ - begin: 0, - chunk, - }); - } else if (pendingRequest.onError) { - pendingRequest.onError(xhr.status); - } - } - - hasPendingRequests() { - for (var xhrId in this.pendingRequests) { - return true; - } - return false; - } - - abortAllRequests() { - for (var xhrId in this.pendingRequests) { - this.abortRequest(xhrId | 0); - } - } - - abortRequest(xhrId) { - var xhr = this.pendingRequests[xhrId].xhr; - delete this.pendingRequests[xhrId]; - xhr.abort(); - } - } - - return NetworkManagerClass; -})(); diff --git a/toolkit/components/pdfjs/content/PdfJsTelemetry.jsm b/toolkit/components/pdfjs/content/PdfJsTelemetry.jsm deleted file mode 100644 --- a/toolkit/components/pdfjs/content/PdfJsTelemetry.jsm +++ /dev/null @@ -1,87 +0,0 @@ -/* Copyright 2013 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. - */ -/* eslint max-len: ["error", 100] */ - -"use strict"; - -var EXPORTED_SYMBOLS = ["PdfJsTelemetry"]; - -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); - -var PdfJsTelemetry = { - onViewerIsUsed(isAttachment) { - Services.telemetry.scalarAdd("pdf.viewer.used", 1); - if (isAttachment) { - Services.telemetry.scalarAdd("pdf.viewer.is_attachment", 1); - } - }, - onFallbackError(featureId) { - let histogram = Services.telemetry.getHistogramById( - "PDF_VIEWER_FALLBACK_ERROR" - ); - histogram.add(featureId ?? "unknown"); - }, - onDocumentSize(size) { - let histogram = Services.telemetry.getHistogramById( - "PDF_VIEWER_DOCUMENT_SIZE_KB" - ); - histogram.add(size / 1024); - }, - onDocumentVersion(versionId) { - let histogram = Services.telemetry.getHistogramById( - "PDF_VIEWER_DOCUMENT_VERSION_2" - ); - histogram.add(versionId); - }, - onDocumentGenerator(generatorId) { - let histogram = Services.telemetry.getHistogramById( - "PDF_VIEWER_DOCUMENT_GENERATOR_2" - ); - histogram.add(generatorId); - }, - onEmbed(isObject) { - let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_EMBED_2"); - histogram.add(isObject ? "object_embed" : "iframe"); - }, - onFontType(fontTypeId) { - let histogram = Services.telemetry.getHistogramById( - "PDF_VIEWER_FONT_TYPES_2" - ); - histogram.add(fontTypeId); - }, - onForm(formType) { - let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_FORM_2"); - histogram.add(formType); - }, - onPrint() { - Services.telemetry.scalarAdd("pdf.viewer.print", 1); - }, - onStreamType(streamTypeId) { - let histogram = Services.telemetry.getHistogramById( - "PDF_VIEWER_STREAM_TYPES_2" - ); - histogram.add(streamTypeId); - }, - onTimeToView(ms) { - let histogram = Services.telemetry.getHistogramById( - "PDF_VIEWER_TIME_TO_VIEW_MS" - ); - histogram.add(ms); - }, - onTagged(tagged) { - let histogram = Services.telemetry.getHistogramById("PDF_VIEWER_TAGGED"); - histogram.add(tagged); - }, -}; diff --git a/toolkit/components/pdfjs/content/PdfSandbox.jsm b/toolkit/components/pdfjs/content/PdfSandbox.jsm deleted file mode 100644 --- a/toolkit/components/pdfjs/content/PdfSandbox.jsm +++ /dev/null @@ -1,95 +0,0 @@ -/* Copyright 2020 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. - */ - -"use strict"; - -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { SandboxSupportBase } = ChromeUtils.import( - "resource://pdf.js/build/pdf.sandbox.external.js" -); - -const EXPORTED_SYMBOLS = ["PdfSandbox"]; - -class SandboxSupport extends SandboxSupportBase { - constructor(win, sandbox) { - super(win); - this.sandbox = sandbox; - } - exportValueToSandbox(val) { - return Cu.cloneInto(val, this.sandbox); - } - - importValueFromSandbox(val) { - return Cu.cloneInto(val, this.win); - } - - createErrorForSandbox(errorMessage) { - return new this.sandbox.Error(errorMessage); - } -} - -class PdfSandbox { - constructor(window, data) { - this.window = window; - const sandbox = Cu.Sandbox(null, { - sandboxName: "PDF.js scripting sandbox", - sameZoneAs: window, - wantXrays: true, - wantGlobalProperties: [], - wantComponents: false, - wantExportHelpers: false, - }); - this.sandbox = sandbox; - this.support = new SandboxSupport(window, sandbox); - - const sandboxInit = Cu.createObjectIn(sandbox); - sandboxInit.data = Cu.cloneInto(data, sandbox); - - sandbox.callExternalFunction = Cu.exportFunction( - this.support.createSandboxExternals(), - sandbox - ); - - try { - // Run the code in the bundle - Services.scriptloader.loadSubScript( - "resource://pdf.js/build/pdf.scripting.js", - sandbox - ); - - this.support.commFun = sandbox.pdfjsScripting.wrappedJSObject.initSandbox( - sandboxInit - ); - } catch (error) { - // error belongs to the sandbox so create a new one - const msg = error.message; - this.destroy(); - throw new Error(msg); - } - } - - dispatchEvent(event) { - this.support.callSandboxFunction("dispatchEvent", event); - } - - destroy() { - if (this.sandbox) { - this.support.destroy(); - this.support = null; - Cu.nukeSandbox(this.sandbox); - this.sandbox = null; - } - } -} diff --git a/toolkit/components/pdfjs/content/PdfStreamConverter.jsm b/toolkit/components/pdfjs/content/PdfStreamConverter.jsm deleted file mode 100644 --- a/toolkit/components/pdfjs/content/PdfStreamConverter.jsm +++ /dev/null @@ -1,1368 +0,0 @@ -/* 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. - */ - -"use strict"; - -var EXPORTED_SYMBOLS = ["PdfStreamConverter"]; - -const PDFJS_EVENT_ID = "pdf.js.message"; -const PREF_PREFIX = "pdfjs"; -const PDF_VIEWER_ORIGIN = "resource://pdf.js"; -const PDF_VIEWER_WEB_PAGE = "resource://pdf.js/web/viewer.html"; -const MAX_NUMBER_OF_PREFS = 50; -const MAX_STRING_PREF_LENGTH = 128; -const PDF_CONTENT_TYPE = "application/pdf"; - -const { XPCOMUtils } = ChromeUtils.import( - "resource://gre/modules/XPCOMUtils.jsm" -); -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); -const { AppConstants } = ChromeUtils.import( - "resource://gre/modules/AppConstants.jsm" -); - -ChromeUtils.defineModuleGetter( - this, - "AsyncPrefs", - "resource://gre/modules/AsyncPrefs.jsm" -); -ChromeUtils.defineModuleGetter( - this, - "NetUtil", - "resource://gre/modules/NetUtil.jsm" -); - -ChromeUtils.defineModuleGetter( - this, - "NetworkManager", - "resource://pdf.js/PdfJsNetwork.jsm" -); - -ChromeUtils.defineModuleGetter( - this, - "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm" -); - -ChromeUtils.defineModuleGetter( - this, - "PdfJsTelemetry", - "resource://pdf.js/PdfJsTelemetry.jsm" -); - -ChromeUtils.defineModuleGetter(this, "PdfJs", "resource://pdf.js/PdfJs.jsm"); - -ChromeUtils.defineModuleGetter( - this, - "PdfSandbox", - "resource://pdf.js/PdfSandbox.jsm" -); - -XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]); - -var Svc = {}; -XPCOMUtils.defineLazyServiceGetter( - Svc, - "mime", - "@mozilla.org/mime;1", - "nsIMIMEService" -); -XPCOMUtils.defineLazyServiceGetter( - Svc, - "handlers", - "@mozilla.org/uriloader/handler-service;1", - "nsIHandlerService" -); - -XPCOMUtils.defineLazyGetter(this, "gOurBinary", () => { - let file = Services.dirsvc.get("XREExeF", Ci.nsIFile); - // Make sure to get the .app on macOS - if (AppConstants.platform == "macosx") { - while (file) { - if (/\.app\/?$/i.test(file.leafName)) { - break; - } - file = file.parent; - } - } - return file; -}); - -function getBoolPref(pref, def) { - try { - return Services.prefs.getBoolPref(pref); - } catch (ex) { - return def; - } -} - -function getIntPref(pref, def) { - try { - return Services.prefs.getIntPref(pref); - } catch (ex) { - return def; - } -} - -function getStringPref(pref, def) { - try { - return Services.prefs.getStringPref(pref); - } catch (ex) { - return def; - } -} - -function log(aMsg) { - if (!getBoolPref(PREF_PREFIX + ".pdfBugEnabled", false)) { - return; - } - var msg = "PdfStreamConverter.js: " + (aMsg.join ? aMsg.join("") : aMsg); - Services.console.logStringMessage(msg); - dump(msg + "\n"); -} - -function getDOMWindow(aChannel, aPrincipal) { - var requestor = aChannel.notificationCallbacks - ? aChannel.notificationCallbacks - : aChannel.loadGroup.notificationCallbacks; - var win = requestor.getInterface(Ci.nsIDOMWindow); - // Ensure the window wasn't navigated to something that is not PDF.js. - if (!win.document.nodePrincipal.equals(aPrincipal)) { - return null; - } - return win; -} - -function getActor(window) { - try { - return window.windowGlobalChild.getActor("Pdfjs"); - } catch (ex) { - return null; - } -} - -function getLocalizedStrings(path) { - var stringBundle = Services.strings.createBundle( - "chrome://pdf.js/locale/" + path - ); - - var map = {}; - for (let string of stringBundle.getSimpleEnumeration()) { - var key = string.key, - property = "textContent"; - var i = key.lastIndexOf("."); - if (i >= 0) { - property = key.substring(i + 1); - key = key.substring(0, i); - } - if (!(key in map)) { - map[key] = {}; - } - map[key][property] = string.value; - } - return map; -} - -function isValidMatchesCount(data) { - if (typeof data !== "object" || data === null) { - return false; - } - const { current, total } = data; - if ( - typeof total !== "number" || - total < 0 || - typeof current !== "number" || - current < 0 || - current > total - ) { - return false; - } - return true; -} - -// PDF data storage -function PdfDataListener(length) { - this.length = length; // less than 0, if length is unknown - this.buffers = []; - this.loaded = 0; -} - -PdfDataListener.prototype = { - append: function PdfDataListener_append(chunk) { - // In most of the cases we will pass data as we receive it, but at the - // beginning of the loading we may accumulate some data. - this.buffers.push(chunk); - this.loaded += chunk.length; - if (this.length >= 0 && this.length < this.loaded) { - this.length = -1; // reset the length, server is giving incorrect one - } - this.onprogress(this.loaded, this.length >= 0 ? this.length : void 0); - }, - readData: function PdfDataListener_readData() { - if (this.buffers.length === 0) { - return null; - } - if (this.buffers.length === 1) { - return this.buffers.pop(); - } - // There are multiple buffers that need to be combined into a single - // buffer. - let combinedLength = 0; - for (let buffer of this.buffers) { - combinedLength += buffer.length; - } - let combinedArray = new Uint8Array(combinedLength); - let writeOffset = 0; - while (this.buffers.length) { - let buffer = this.buffers.shift(); - combinedArray.set(buffer, writeOffset); - writeOffset += buffer.length; - } - return combinedArray; - }, - get isDone() { - return !!this.isDataReady; - }, - finish: function PdfDataListener_finish() { - this.isDataReady = true; - if (this.oncompleteCallback) { - this.oncompleteCallback(this.readData()); - } - }, - error: function PdfDataListener_error(errorCode) { - this.errorCode = errorCode; - if (this.oncompleteCallback) { - this.oncompleteCallback(null, errorCode); - } - }, - onprogress() {}, - get oncomplete() { - return this.oncompleteCallback; - }, - set oncomplete(value) { - this.oncompleteCallback = value; - if (this.isDataReady) { - value(this.readData()); - } - if (this.errorCode) { - value(null, this.errorCode); - } - }, -}; - -/** - * All the privileged actions. - */ -class ChromeActions { - constructor(domWindow, contentDispositionFilename) { - this.domWindow = domWindow; - this.contentDispositionFilename = contentDispositionFilename; - this.telemetryState = { - documentInfo: false, - firstPageInfo: false, - streamTypesUsed: {}, - fontTypesUsed: {}, - fallbackErrorsReported: {}, - }; - this.sandbox = null; - this.unloadListener = null; - } - - createSandbox(data, sendResponse) { - function sendResp(res) { - if (sendResponse) { - sendResponse(res); - } - return res; - } - - if (!getBoolPref(PREF_PREFIX + ".enableScripting", false)) { - return sendResp(false); - } - - if (this.sandbox !== null) { - return sendResp(true); - } - - try { - this.sandbox = new PdfSandbox(this.domWindow, data); - } catch (err) { - // If there's an error here, it means that something is really wrong - // on pdf.js side during sandbox initialization phase. - Cu.reportError(err); - return sendResp(false); - } - - this.unloadListener = () => { - this.destroySandbox(); - }; - this.domWindow.addEventListener("unload", this.unloadListener); - - return sendResp(true); - } - - dispatchEventInSandbox(event) { - if (this.sandbox) { - this.sandbox.dispatchEvent(event); - } - } - - destroySandbox() { - if (this.sandbox) { - this.domWindow.removeEventListener("unload", this.unloadListener); - this.sandbox.destroy(); - this.sandbox = null; - } - } - - isInPrivateBrowsing() { - return PrivateBrowsingUtils.isContentWindowPrivate(this.domWindow); - } - - getWindowOriginAttributes() { - try { - return this.domWindow.document.nodePrincipal.originAttributes; - } catch (err) { - return {}; - } - } - - download(data, sendResponse) { - var self = this; - var originalUrl = data.originalUrl; - var blobUrl = data.blobUrl || originalUrl; - // The data may not be downloaded so we need just retry getting the pdf with - // the original url. - var originalUri = NetUtil.newURI(originalUrl); - var filename = data.filename; - if ( - typeof filename !== "string" || - (!/\.pdf$/i.test(filename) && !data.isAttachment) - ) { - filename = "document.pdf"; - } - var blobUri = NetUtil.newURI(blobUrl); - - // If the download was triggered from the ctrl/cmd+s or "Save Page As" - // or the download button, launch the "Save As" dialog. - const saveOnDownload = getBoolPref( - "browser.download.improvements_to_download_panel", - false - ); - - if ( - data.sourceEventType == "save" || - (saveOnDownload && data.sourceEventType == "download") - ) { - let actor = getActor(this.domWindow); - actor.sendAsyncMessage("PDFJS:Parent:saveURL", { - blobUrl, - filename, - }); - return; - } - - // The download is from the fallback bar or the download button, so trigger - // the open dialog to make it easier for users to save in the downloads - // folder or launch a different PDF viewer. - var extHelperAppSvc = Cc[ - "@mozilla.org/uriloader/external-helper-app-service;1" - ].getService(Ci.nsIExternalHelperAppService); - - var docIsPrivate = this.isInPrivateBrowsing(); - var netChannel = NetUtil.newChannel({ - uri: blobUri, - loadUsingSystemPrincipal: true, - }); - if ( - "nsIPrivateBrowsingChannel" in Ci && - netChannel instanceof Ci.nsIPrivateBrowsingChannel - ) { - netChannel.setPrivate(docIsPrivate); - } - NetUtil.asyncFetch(netChannel, function(aInputStream, aResult) { - if (!Components.isSuccessCode(aResult)) { - if (sendResponse) { - sendResponse(true); - } - return; - } - // Create a nsIInputStreamChannel so we can set the url on the channel - // so the filename will be correct. - var channel = Cc[ - "@mozilla.org/network/input-stream-channel;1" - ].createInstance(Ci.nsIInputStreamChannel); - channel.QueryInterface(Ci.nsIChannel); - try { - // contentDisposition/contentDispositionFilename is readonly before FF18 - channel.contentDisposition = Ci.nsIChannel.DISPOSITION_ATTACHMENT; - if (self.contentDispositionFilename && !data.isAttachment) { - channel.contentDispositionFilename = self.contentDispositionFilename; - } else { - channel.contentDispositionFilename = filename; - } - } catch (e) {} - channel.setURI(originalUri); - channel.loadInfo = netChannel.loadInfo; - channel.contentStream = aInputStream; - if ( - "nsIPrivateBrowsingChannel" in Ci && - channel instanceof Ci.nsIPrivateBrowsingChannel - ) { - channel.setPrivate(docIsPrivate); - } - - var listener = { - extListener: null, - onStartRequest(aRequest) { - var loadContext = self.domWindow.docShell.QueryInterface( - Ci.nsILoadContext - ); - this.extListener = extHelperAppSvc.doContent( - data.isAttachment ? "application/octet-stream" : PDF_CONTENT_TYPE, - aRequest, - loadContext, - false - ); - this.extListener.onStartRequest(aRequest); - }, - onStopRequest(aRequest, aStatusCode) { - if (this.extListener) { - this.extListener.onStopRequest(aRequest, aStatusCode); - } - // Notify the content code we're done downloading. - if (sendResponse) { - sendResponse(false); - } - }, - onDataAvailable(aRequest, aDataInputStream, aOffset, aCount) { - this.extListener.onDataAvailable( - aRequest, - aDataInputStream, - aOffset, - aCount - ); - }, - }; - - channel.asyncOpen(listener); - }); - } - - getLocale() { - return Services.locale.requestedLocale || "en-US"; - } - - getStrings() { - try { - // Lazy initialization of localizedStrings - this.localizedStrings ||= getLocalizedStrings("viewer.properties"); - - return this.localizedStrings; - } catch (e) { - log("Unable to retrieve localized strings: " + e); - return null; - } - } - - supportsIntegratedFind() { - // Integrated find is only supported when we're not in a frame - return this.domWindow.windowGlobalChild.browsingContext.parent === null; - } - - supportsDocumentFonts() { - var prefBrowser = getIntPref("browser.display.use_document_fonts", 1); - var prefGfx = getBoolPref("gfx.downloadable_fonts.enabled", true); - return !!prefBrowser && prefGfx; - } - - supportedMouseWheelZoomModifierKeys() { - return { - ctrlKey: getIntPref("mousewheel.with_control.action", 3) === 3, - metaKey: getIntPref("mousewheel.with_meta.action", 1) === 3, - }; - } - - isInAutomation() { - return Cu.isInAutomation; - } - - reportTelemetry(data) { - var probeInfo = JSON.parse(data); - switch (probeInfo.type) { - case "documentInfo": - if (!this.telemetryState.documentInfo) { - PdfJsTelemetry.onDocumentVersion(probeInfo.version); - PdfJsTelemetry.onDocumentGenerator(probeInfo.generator); - if (probeInfo.formType) { - PdfJsTelemetry.onForm(probeInfo.formType); - } - this.telemetryState.documentInfo = true; - } - break; - case "pageInfo": - if (!this.telemetryState.firstPageInfo) { - PdfJsTelemetry.onTimeToView(probeInfo.timestamp); - this.telemetryState.firstPageInfo = true; - } - break; - case "documentStats": - // documentStats can be called several times for one documents. - // if stream/font types are reported, trying not to submit the same - // enumeration value multiple times. - var documentStats = probeInfo.stats; - if (!documentStats || typeof documentStats !== "object") { - break; - } - var i, - streamTypes = documentStats.streamTypes, - key; - var STREAM_TYPE_ID_LIMIT = 20; - i = 0; - for (key in streamTypes) { - if (++i > STREAM_TYPE_ID_LIMIT) { - break; - } - if (!this.telemetryState.streamTypesUsed[key]) { - PdfJsTelemetry.onStreamType(key); - this.telemetryState.streamTypesUsed[key] = true; - } - } - var fontTypes = documentStats.fontTypes; - var FONT_TYPE_ID_LIMIT = 20; - i = 0; - for (key in fontTypes) { - if (++i > FONT_TYPE_ID_LIMIT) { - break; - } - if (!this.telemetryState.fontTypesUsed[key]) { - PdfJsTelemetry.onFontType(key); - this.telemetryState.fontTypesUsed[key] = true; - } - } - break; - case "print": - PdfJsTelemetry.onPrint(); - break; - case "unsupportedFeature": - if (!this.telemetryState.fallbackErrorsReported[probeInfo.featureId]) { - PdfJsTelemetry.onFallbackError(probeInfo.featureId); - this.telemetryState.fallbackErrorsReported[ - probeInfo.featureId - ] = true; - } - break; - case "tagged": - PdfJsTelemetry.onTagged(probeInfo.tagged); - break; - } - } - - /** - * @param {Object} args - Object with `featureId` and `url` properties. - * @param {function} sendResponse - Callback function. - */ - fallback(args, sendResponse) { - sendResponse(false); - } - - updateFindControlState(data) { - if (!this.supportsIntegratedFind()) { - return; - } - // Verify what we're sending to the findbar. - var result = data.result; - var findPrevious = data.findPrevious; - var findPreviousType = typeof findPrevious; - if ( - typeof result !== "number" || - result < 0 || - result > 3 || - (findPreviousType !== "undefined" && findPreviousType !== "boolean") - ) { - return; - } - // Allow the `matchesCount` property to be optional, and ensure that - // it's valid before including it in the data sent to the findbar. - let matchesCount = null; - if (isValidMatchesCount(data.matchesCount)) { - matchesCount = data.matchesCount; - } - // Same for the `rawQuery` property. - let rawQuery = null; - if (typeof data.rawQuery === "string") { - rawQuery = data.rawQuery; - } - - let actor = getActor(this.domWindow); - actor?.sendAsyncMessage("PDFJS:Parent:updateControlState", { - result, - findPrevious, - matchesCount, - rawQuery, - }); - } - - updateFindMatchesCount(data) { - if (!this.supportsIntegratedFind()) { - return; - } - // Verify what we're sending to the findbar. - if (!isValidMatchesCount(data)) { - return; - } - - let actor = getActor(this.domWindow); - actor?.sendAsyncMessage("PDFJS:Parent:updateMatchesCount", data); - } - - setPreferences(prefs, sendResponse) { - var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + "."); - var numberOfPrefs = 0; - var prefValue, prefName; - for (var key in prefs) { - if (++numberOfPrefs > MAX_NUMBER_OF_PREFS) { - log( - "setPreferences - Exceeded the maximum number of preferences " + - "that is allowed to be set at once." - ); - break; - } else if (!defaultBranch.getPrefType(key)) { - continue; - } - prefValue = prefs[key]; - prefName = PREF_PREFIX + "." + key; - switch (typeof prefValue) { - case "boolean": - AsyncPrefs.set(prefName, prefValue); - break; - case "number": - AsyncPrefs.set(prefName, prefValue); - break; - case "string": - if (prefValue.length > MAX_STRING_PREF_LENGTH) { - log( - "setPreferences - Exceeded the maximum allowed length " + - "for a string preference." - ); - } else { - AsyncPrefs.set(prefName, prefValue); - } - break; - } - } - if (sendResponse) { - sendResponse(true); - } - } - - getPreferences(prefs, sendResponse) { - var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + "."); - var currentPrefs = {}, - numberOfPrefs = 0; - var prefValue, prefName; - for (var key in prefs) { - if (++numberOfPrefs > MAX_NUMBER_OF_PREFS) { - log( - "getPreferences - Exceeded the maximum number of preferences " + - "that is allowed to be fetched at once." - ); - break; - } else if (!defaultBranch.getPrefType(key)) { - continue; - } - prefValue = prefs[key]; - prefName = PREF_PREFIX + "." + key; - switch (typeof prefValue) { - case "boolean": - currentPrefs[key] = getBoolPref(prefName, prefValue); - break; - case "number": - currentPrefs[key] = getIntPref(prefName, prefValue); - break; - case "string": - currentPrefs[key] = getStringPref(prefName, prefValue); - break; - } - } - let result = JSON.stringify(currentPrefs); - if (sendResponse) { - sendResponse(result); - } - return result; - } -} - -/** - * This is for range requests. - */ -class RangedChromeActions extends ChromeActions { - constructor( - domWindow, - contentDispositionFilename, - originalRequest, - rangeEnabled, - streamingEnabled, - dataListener - ) { - super(domWindow, contentDispositionFilename); - this.dataListener = dataListener; - this.originalRequest = originalRequest; - this.rangeEnabled = rangeEnabled; - this.streamingEnabled = streamingEnabled; - - this.pdfUrl = originalRequest.URI.spec; - this.contentLength = originalRequest.contentLength; - - // Pass all the headers from the original request through - var httpHeaderVisitor = { - headers: {}, - visitHeader(aHeader, aValue) { - if (aHeader === "Range") { - // When loading the PDF from cache, firefox seems to set the Range - // request header to fetch only the unfetched portions of the file - // (e.g. 'Range: bytes=1024-'). However, we want to set this header - // manually to fetch the PDF in chunks. - return; - } - this.headers[aHeader] = aValue; - }, - }; - if (originalRequest.visitRequestHeaders) { - originalRequest.visitRequestHeaders(httpHeaderVisitor); - } - - var self = this; - var xhr_onreadystatechange = function xhr_onreadystatechange() { - if (this.readyState === 1) { - // LOADING - var netChannel = this.channel; - // override this XMLHttpRequest's OriginAttributes with our cached parent window's - // OriginAttributes, as we are currently running under the SystemPrincipal - this.setOriginAttributes(self.getWindowOriginAttributes()); - if ( - "nsIPrivateBrowsingChannel" in Ci && - netChannel instanceof Ci.nsIPrivateBrowsingChannel - ) { - var docIsPrivate = self.isInPrivateBrowsing(); - netChannel.setPrivate(docIsPrivate); - } - } - }; - var getXhr = function getXhr() { - var xhr = new XMLHttpRequest(); - xhr.addEventListener("readystatechange", xhr_onreadystatechange); - return xhr; - }; - - this.networkManager = new NetworkManager(this.pdfUrl, { - httpHeaders: httpHeaderVisitor.headers, - getXhr, - }); - - // If we are in range request mode, this means we manually issued xhr - // requests, which we need to abort when we leave the page - domWindow.addEventListener("unload", function unload(e) { - domWindow.removeEventListener(e.type, unload); - self.abortLoading(); - }); - } - - initPassiveLoading() { - let data, done; - if (!this.streamingEnabled) { - this.originalRequest.cancel(Cr.NS_BINDING_ABORTED); - this.originalRequest = null; - data = this.dataListener.readData(); - done = this.dataListener.isDone; - this.dataListener = null; - } else { - data = this.dataListener.readData(); - done = this.dataListener.isDone; - - this.dataListener.onprogress = (loaded, total) => { - this.domWindow.postMessage( - { - pdfjsLoadAction: "progressiveRead", - loaded, - total, - chunk: this.dataListener.readData(), - }, - PDF_VIEWER_ORIGIN - ); - }; - this.dataListener.oncomplete = () => { - if (!done && this.dataListener.isDone) { - this.domWindow.postMessage( - { - pdfjsLoadAction: "progressiveDone", - }, - PDF_VIEWER_ORIGIN - ); - } - this.dataListener = null; - }; - } - - this.domWindow.postMessage( - { - pdfjsLoadAction: "supportsRangedLoading", - rangeEnabled: this.rangeEnabled, - streamingEnabled: this.streamingEnabled, - pdfUrl: this.pdfUrl, - length: this.contentLength, - data, - done, - filename: this.contentDispositionFilename, - }, - PDF_VIEWER_ORIGIN - ); - - return true; - } - - requestDataRange(args) { - if (!this.rangeEnabled) { - return; - } - - var begin = args.begin; - var end = args.end; - var domWindow = this.domWindow; - // TODO(mack): Support error handler. We're not currently not handling - // errors from chrome code for non-range requests, so this doesn't - // seem high-pri - this.networkManager.requestRange(begin, end, { - onDone: function RangedChromeActions_onDone(aArgs) { - domWindow.postMessage( - { - pdfjsLoadAction: "range", - begin: aArgs.begin, - chunk: aArgs.chunk, - }, - PDF_VIEWER_ORIGIN - ); - }, - onProgress: function RangedChromeActions_onProgress(evt) { - domWindow.postMessage( - { - pdfjsLoadAction: "rangeProgress", - loaded: evt.loaded, - }, - PDF_VIEWER_ORIGIN - ); - }, - }); - } - - abortLoading() { - this.networkManager.abortAllRequests(); - if (this.originalRequest) { - this.originalRequest.cancel(Cr.NS_BINDING_ABORTED); - this.originalRequest = null; - } - this.dataListener = null; - } -} - -/** - * This is for a single network stream. - */ -class StandardChromeActions extends ChromeActions { - constructor( - domWindow, - contentDispositionFilename, - originalRequest, - dataListener - ) { - super(domWindow, contentDispositionFilename); - this.originalRequest = originalRequest; - this.dataListener = dataListener; - } - - initPassiveLoading() { - if (!this.dataListener) { - return false; - } - - this.dataListener.onprogress = (loaded, total) => { - this.domWindow.postMessage( - { - pdfjsLoadAction: "progress", - loaded, - total, - }, - PDF_VIEWER_ORIGIN - ); - }; - - this.dataListener.oncomplete = (data, errorCode) => { - this.domWindow.postMessage( - { - pdfjsLoadAction: "complete", - data, - errorCode, - filename: this.contentDispositionFilename, - }, - PDF_VIEWER_ORIGIN - ); - - this.dataListener = null; - this.originalRequest = null; - }; - - return true; - } - - abortLoading() { - if (this.originalRequest) { - this.originalRequest.cancel(Cr.NS_BINDING_ABORTED); - this.originalRequest = null; - } - this.dataListener = null; - } -} - -/** - * Event listener to trigger chrome privileged code. - */ -class RequestListener { - constructor(actions) { - this.actions = actions; - } - - // Receive an event and synchronously or asynchronously responds. - receive(event) { - var message = event.target; - var doc = message.ownerDocument; - var action = event.detail.action; - var data = event.detail.data; - var sync = event.detail.sync; - var actions = this.actions; - if (!(action in actions)) { - log("Unknown action: " + action); - return; - } - var response; - if (sync) { - response = actions[action].call(this.actions, data); - event.detail.response = Cu.cloneInto(response, doc.defaultView); - } else { - if (!event.detail.responseExpected) { - doc.documentElement.removeChild(message); - response = null; - } else { - response = function sendResponse(aResponse) { - try { - var listener = doc.createEvent("CustomEvent"); - let detail = Cu.cloneInto({ response: aResponse }, doc.defaultView); - listener.initCustomEvent("pdf.js.response", true, false, detail); - return message.dispatchEvent(listener); - } catch (e) { - // doc is no longer accessible because the requestor is already - // gone. unloaded content cannot receive the response anyway. - return false; - } - }; - } - actions[action].call(this.actions, data, response); - } - } -} - -function PdfStreamConverter() {} - -PdfStreamConverter.prototype = { - QueryInterface: ChromeUtils.generateQI([ - "nsIStreamConverter", - "nsIStreamListener", - "nsIRequestObserver", - ]), - - /* - * This component works as such: - * 1. asyncConvertData stores the listener - * 2. onStartRequest creates a new channel, streams the viewer - * 3. If range requests are supported: - * 3.1. Leave the request open until the viewer is ready to switch to - * range requests. - * - * If range rquests are not supported: - * 3.1. Read the stream as it's loaded in onDataAvailable to send - * to the viewer - * - * The convert function just returns the stream, it's just the synchronous - * version of asyncConvertData. - */ - - // nsIStreamConverter::convert - convert(aFromStream, aFromType, aToType, aCtxt) { - throw Components.Exception("", Cr.NS_ERROR_NOT_IMPLEMENTED); - }, - - // nsIStreamConverter::asyncConvertData - asyncConvertData(aFromType, aToType, aListener, aCtxt) { - if (aCtxt && aCtxt instanceof Ci.nsIChannel) { - aCtxt.QueryInterface(Ci.nsIChannel); - } - // We need to check if we're supposed to convert here, because not all - // asyncConvertData consumers will call getConvertedType first: - this.getConvertedType(aFromType, aCtxt); - - // Store the listener passed to us - this.listener = aListener; - }, - - _usableHandler(handlerInfo) { - let { preferredApplicationHandler } = handlerInfo; - if ( - !preferredApplicationHandler || - !(preferredApplicationHandler instanceof Ci.nsILocalHandlerApp) - ) { - return false; - } - preferredApplicationHandler.QueryInterface(Ci.nsILocalHandlerApp); - // We have an app, grab the executable - let { executable } = preferredApplicationHandler; - if (!executable) { - return false; - } - return !executable.equals(gOurBinary); - }, - - /* - * Check if the user wants to use PDF.js. Returns true if PDF.js should - * handle PDFs, and false if not. Will always return true on non-parent - * processes. - * - * If the user has selected to open PDFs with a helper app, and we are that - * helper app, or if the user has selected the OS default, and we are that - * OS default, reset the preference back to pdf.js . - * - */ - _validateAndMaybeUpdatePDFPrefs() { - let { processType, PROCESS_TYPE_DEFAULT } = Services.appinfo; - // If we're not in the parent, or are the default, then just say yes. - if (processType != PROCESS_TYPE_DEFAULT || PdfJs.cachedIsDefault()) { - return { shouldOpen: true }; - } - - // OK, PDF.js might not be the default. Find out if we've misled the user - // into making Firefox an external handler or if we're the OS default and - // Firefox is set to use the OS default: - let mime = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, "pdf"); - // The above might throw errors. We're deliberately letting those bubble - // back up, where they'll tell the stream converter not to use us. - - if (!mime) { - // This shouldn't happen, but we can't fix what isn't there. Assume - // we're OK to handle with PDF.js - return { shouldOpen: true }; - } - - const { saveToDisk, useHelperApp, useSystemDefault } = Ci.nsIHandlerInfo; - let { preferredAction, alwaysAskBeforeHandling } = mime; - // return this info so getConvertedType can use it. - let rv = { alwaysAskBeforeHandling, shouldOpen: false }; - // If the user has indicated they want to be asked or want to save to - // disk, we shouldn't render inline immediately: - if (alwaysAskBeforeHandling || preferredAction == saveToDisk) { - return rv; - } - // If we have usable helper app info, don't use PDF.js - if (preferredAction == useHelperApp && this._usableHandler(mime)) { - return rv; - } - // If we want the OS default and that's not Firefox, don't use PDF.js - if (preferredAction == useSystemDefault && !mime.isCurrentAppOSDefault()) { - return rv; - } - rv.shouldOpen = true; - // Log that we're doing this to help debug issues if people end up being - // surprised by this behaviour. - Cu.reportError("Found unusable PDF preferences. Fixing back to PDF.js"); - - mime.preferredAction = Ci.nsIHandlerInfo.handleInternally; - mime.alwaysAskBeforeHandling = false; - Svc.handlers.store(mime); - return true; - }, - - getConvertedType(aFromType, aChannel) { - const HTML = "text/html"; - let channelURI = aChannel?.URI; - // We can be invoked for application/octet-stream; check if we want the - // channel first: - if (aFromType != "application/pdf") { - let ext = channelURI?.QueryInterface(Ci.nsIURL).fileExtension; - let isPDF = ext.toLowerCase() == "pdf"; - let browsingContext = aChannel?.loadInfo.targetBrowsingContext; - let toplevelOctetStream = - aFromType == "application/octet-stream" && - browsingContext && - !browsingContext.parent; - if ( - !isPDF || - !toplevelOctetStream || - !getBoolPref(PREF_PREFIX + ".handleOctetStream", false) - ) { - throw new Components.Exception( - "Ignore PDF.js for this download.", - Cr.NS_ERROR_FAILURE - ); - } - // fall through, this appears to be a pdf. - } - - let { - alwaysAskBeforeHandling, - shouldOpen, - } = this._validateAndMaybeUpdatePDFPrefs(); - - if (shouldOpen) { - return HTML; - } - // Hm, so normally, no pdfjs. However... if this is a file: channel there - // are some edge-cases. - if (channelURI?.schemeIs("file")) { - // If we're loaded with system principal, we were likely handed the PDF - // by the OS or directly from the URL bar. Assume we should load it: - let triggeringPrincipal = aChannel.loadInfo?.triggeringPrincipal; - if (triggeringPrincipal?.isSystemPrincipal) { - return HTML; - } - - // If we're loading from a file: link, load it in PDF.js unless the user - // has told us they always want to open/save PDFs. - // This is because handing off the choice to open in Firefox itself - // through the dialog doesn't work properly and making it work is - // non-trivial (see https://bugzilla.mozilla.org/show_bug.cgi?id=1680147#c3 ) - // - and anyway, opening the file is what we do for *all* - // other file types we handle internally (and users can then use other UI - // to save or open it with other apps from there). - if (triggeringPrincipal?.schemeIs("file") && alwaysAskBeforeHandling) { - return HTML; - } - } - - throw new Components.Exception("Can't use PDF.js", Cr.NS_ERROR_FAILURE); - }, - - // nsIStreamListener::onDataAvailable - onDataAvailable(aRequest, aInputStream, aOffset, aCount) { - if (!this.dataListener) { - return; - } - - var binaryStream = this.binaryStream; - binaryStream.setInputStream(aInputStream); - let chunk = new ArrayBuffer(aCount); - binaryStream.readArrayBuffer(aCount, chunk); - this.dataListener.append(new Uint8Array(chunk)); - }, - - // nsIRequestObserver::onStartRequest - onStartRequest(aRequest) { - // Setup the request so we can use it below. - var isHttpRequest = false; - try { - aRequest.QueryInterface(Ci.nsIHttpChannel); - isHttpRequest = true; - } catch (e) {} - - var rangeRequest = false; - var streamRequest = false; - if (isHttpRequest) { - var contentEncoding = "identity"; - try { - contentEncoding = aRequest.getResponseHeader("Content-Encoding"); - } catch (e) {} - - var acceptRanges; - try { - acceptRanges = aRequest.getResponseHeader("Accept-Ranges"); - } catch (e) {} - - var hash = aRequest.URI.ref; - var isPDFBugEnabled = getBoolPref(PREF_PREFIX + ".pdfBugEnabled", false); - rangeRequest = - contentEncoding === "identity" && - acceptRanges === "bytes" && - aRequest.contentLength >= 0 && - !getBoolPref(PREF_PREFIX + ".disableRange", false) && - (!isPDFBugEnabled || !hash.toLowerCase().includes("disablerange=true")); - streamRequest = - contentEncoding === "identity" && - aRequest.contentLength >= 0 && - !getBoolPref(PREF_PREFIX + ".disableStream", false) && - (!isPDFBugEnabled || - !hash.toLowerCase().includes("disablestream=true")); - } - - aRequest.QueryInterface(Ci.nsIChannel); - - aRequest.QueryInterface(Ci.nsIWritablePropertyBag); - - var contentDisposition = aRequest.DISPOSITION_INLINE; - var contentDispositionFilename; - try { - contentDisposition = aRequest.contentDisposition; - contentDispositionFilename = aRequest.contentDispositionFilename; - } catch (e) {} - - // Change the content type so we don't get stuck in a loop. - aRequest.setProperty("contentType", aRequest.contentType); - aRequest.contentType = "text/html"; - if (isHttpRequest) { - // We trust PDF viewer, using no CSP - aRequest.setResponseHeader("Content-Security-Policy", "", false); - aRequest.setResponseHeader( - "Content-Security-Policy-Report-Only", - "", - false - ); - // The viewer does not need to handle HTTP Refresh header. - aRequest.setResponseHeader("Refresh", "", false); - } - - PdfJsTelemetry.onViewerIsUsed( - contentDisposition == aRequest.DISPOSITION_ATTACHMENT - ); - PdfJsTelemetry.onDocumentSize(aRequest.contentLength); - - // The document will be loaded via the stream converter as html, - // but since we may have come here via a download or attachment - // that was opened directly, force the content disposition to be - // inline so that the html document will be loaded normally instead - // of going to the helper service. - aRequest.contentDisposition = Ci.nsIChannel.DISPOSITION_FORCE_INLINE; - - // Creating storage for PDF data - var contentLength = aRequest.contentLength; - this.dataListener = new PdfDataListener(contentLength); - this.binaryStream = Cc["@mozilla.org/binaryinputstream;1"].createInstance( - Ci.nsIBinaryInputStream - ); - - // Create a new channel that is viewer loaded as a resource. - var channel = NetUtil.newChannel({ - uri: PDF_VIEWER_WEB_PAGE, - loadUsingSystemPrincipal: true, - }); - - var listener = this.listener; - var dataListener = this.dataListener; - // Proxy all the request observer calls, when it gets to onStopRequest - // we can get the dom window. We also intentionally pass on the original - // request(aRequest) below so we don't overwrite the original channel and - // trigger an assertion. - var proxy = { - onStartRequest(request) { - listener.onStartRequest(aRequest); - }, - onDataAvailable(request, inputStream, offset, count) { - listener.onDataAvailable(aRequest, inputStream, offset, count); - }, - onStopRequest(request, statusCode) { - var domWindow = getDOMWindow(channel, resourcePrincipal); - if (!Components.isSuccessCode(statusCode) || !domWindow) { - // The request may have been aborted and the document may have been - // replaced with something that is not PDF.js, abort attaching. - listener.onStopRequest(aRequest, statusCode); - return; - } - var actions; - if (rangeRequest || streamRequest) { - actions = new RangedChromeActions( - domWindow, - contentDispositionFilename, - aRequest, - rangeRequest, - streamRequest, - dataListener - ); - } else { - actions = new StandardChromeActions( - domWindow, - contentDispositionFilename, - aRequest, - dataListener - ); - } - var requestListener = new RequestListener(actions); - domWindow.document.addEventListener( - PDFJS_EVENT_ID, - function(event) { - requestListener.receive(event); - }, - false, - true - ); - - let actor = getActor(domWindow); - actor?.init(actions.supportsIntegratedFind()); - - listener.onStopRequest(aRequest, statusCode); - - if (domWindow.windowGlobalChild.browsingContext.parent) { - // This will need to be changed when fission supports object/embed (bug 1614524) - var isObjectEmbed = domWindow.frameElement - ? domWindow.frameElement.tagName == "OBJECT" || - domWindow.frameElement.tagName == "EMBED" - : false; - PdfJsTelemetry.onEmbed(isObjectEmbed); - } - }, - }; - - // Keep the URL the same so the browser sees it as the same. - channel.originalURI = aRequest.URI; - channel.loadGroup = aRequest.loadGroup; - channel.loadInfo.originAttributes = aRequest.loadInfo.originAttributes; - - // We can use the resource principal when data is fetched by the chrome, - // e.g. useful for NoScript. Make make sure we reuse the origin attributes - // from the request channel to keep isolation consistent. - var uri = NetUtil.newURI(PDF_VIEWER_WEB_PAGE); - var resourcePrincipal = Services.scriptSecurityManager.createContentPrincipal( - uri, - aRequest.loadInfo.originAttributes - ); - // Remember the principal we would have had before we mess with it. - let originalPrincipal = Services.scriptSecurityManager.getChannelResultPrincipal( - aRequest - ); - aRequest.owner = resourcePrincipal; - aRequest.setProperty("noPDFJSPrincipal", originalPrincipal); - - channel.asyncOpen(proxy); - }, - - // nsIRequestObserver::onStopRequest - onStopRequest(aRequest, aStatusCode) { - if (!this.dataListener) { - // Do nothing - return; - } - - if (Components.isSuccessCode(aStatusCode)) { - this.dataListener.finish(); - } else { - this.dataListener.error(aStatusCode); - } - delete this.dataListener; - delete this.binaryStream; - }, -}; diff --git a/toolkit/components/pdfjs/content/PdfjsChild.jsm b/toolkit/components/pdfjs/content/PdfjsChild.jsm deleted file mode 100644 --- a/toolkit/components/pdfjs/content/PdfjsChild.jsm +++ /dev/null @@ -1,58 +0,0 @@ -/* 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. - */ - -"use strict"; - -var EXPORTED_SYMBOLS = ["PdfjsChild"]; - -class PdfjsChild extends JSWindowActorChild { - init(supportsFind) { - if (supportsFind) { - this.sendAsyncMessage("PDFJS:Parent:addEventListener"); - } - } - - dispatchEvent(type, detail) { - const contentWindow = this.contentWindow; - const forward = contentWindow.document.createEvent("CustomEvent"); - forward.initCustomEvent(type, true, true, detail); - contentWindow.dispatchEvent(forward); - } - - receiveMessage(msg) { - switch (msg.name) { - case "PDFJS:Child:handleEvent": { - let detail = Cu.cloneInto(msg.data.detail, this.contentWindow); - this.dispatchEvent(msg.data.type, detail); - break; - } - - case "PDFJS:ZoomIn": - case "PDFJS:ZoomOut": - case "PDFJS:ZoomReset": - case "PDFJS:Save": { - const type = msg.name.split("PDFJS:")[1].toLowerCase(); - this.dispatchEvent(type, null); - break; - } - - case "PDFJS:Child:fallbackDownload": - if (this.fallbackCallback) { - this.fallbackCallback(msg.data.download); - } - break; - } - } -} diff --git a/toolkit/components/pdfjs/content/PdfjsParent.jsm b/toolkit/components/pdfjs/content/PdfjsParent.jsm deleted file mode 100644 --- a/toolkit/components/pdfjs/content/PdfjsParent.jsm +++ /dev/null @@ -1,350 +0,0 @@ -/* 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. - */ - -"use strict"; - -var EXPORTED_SYMBOLS = ["PdfjsParent"]; - -const { XPCOMUtils } = ChromeUtils.import( - "resource://gre/modules/XPCOMUtils.jsm" -); - -const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm"); - -ChromeUtils.defineModuleGetter( - this, - "SetClipboardSearchString", - "resource://gre/modules/Finder.jsm" -); - -ChromeUtils.defineModuleGetter( - this, - "PrivateBrowsingUtils", - "resource://gre/modules/PrivateBrowsingUtils.jsm" -); - -var Svc = {}; -XPCOMUtils.defineLazyServiceGetter( - Svc, - "mime", - "@mozilla.org/mime;1", - "nsIMIMEService" -); - -XPCOMUtils.defineLazyPreferenceGetter( - this, - "matchesCountLimit", - "accessibility.typeaheadfind.matchesCountLimit" -); - -let gFindTypes = [ - "find", - "findagain", - "findhighlightallchange", - "findcasesensitivitychange", - "findbarclose", - "finddiacriticmatchingchange", -]; - -class PdfjsParent extends JSWindowActorParent { - constructor() { - super(); - this._boundToFindbar = null; - this._findFailedString = null; - } - - didDestroy() { - this._removeEventListener(); - } - - receiveMessage(aMsg) { - switch (aMsg.name) { - case "PDFJS:Parent:displayWarning": - this._displayWarning(aMsg); - break; - - case "PDFJS:Parent:updateControlState": - return this._updateControlState(aMsg); - case "PDFJS:Parent:updateMatchesCount": - return this._updateMatchesCount(aMsg); - case "PDFJS:Parent:addEventListener": - return this._addEventListener(); - case "PDFJS:Parent:saveURL": - return this._saveURL(aMsg); - } - return undefined; - } - - /* - * Internal - */ - - get browser() { - return this.browsingContext.top.embedderElement; - } - - _saveURL(aMsg) { - const data = aMsg.data; - this.browser.ownerGlobal.saveURL( - data.blobUrl /* aURL */, - data.filename /* aFileName */, - null /* aFilePickerTitleKey */, - true /* aShouldBypassCache */, - false /* aSkipPrompt */, - null /* aReferrerInfo */, - null /* aCookieJarSettings*/, - null /* aSourceDocument */, - PrivateBrowsingUtils.isBrowserPrivate( - this.browser - ) /* aIsContentWindowPrivate */, - Services.scriptSecurityManager.getSystemPrincipal() /* aPrincipal */ - ); - } - - _updateControlState(aMsg) { - let data = aMsg.data; - let browser = this.browser; - let tabbrowser = browser.getTabBrowser(); - let tab = tabbrowser.getTabForBrowser(browser); - tabbrowser.getFindBar(tab).then(fb => { - if (!fb) { - // The tab or window closed. - return; - } - fb.updateControlState(data.result, data.findPrevious); - - if ( - data.result === Ci.nsITypeAheadFind.FIND_FOUND || - data.result === Ci.nsITypeAheadFind.FIND_WRAPPED || - (data.result === Ci.nsITypeAheadFind.FIND_PENDING && - !this._findFailedString) - ) { - this._findFailedString = null; - SetClipboardSearchString(data.rawQuery); - } else if (!this._findFailedString) { - this._findFailedString = data.rawQuery; - SetClipboardSearchString(data.rawQuery); - } - - const matchesCount = this._requestMatchesCount(data.matchesCount); - fb.onMatchesCountResult(matchesCount); - }); - } - - _updateMatchesCount(aMsg) { - let data = aMsg.data; - let browser = this.browser; - let tabbrowser = browser.getTabBrowser(); - let tab = tabbrowser.getTabForBrowser(browser); - tabbrowser.getFindBar(tab).then(fb => { - if (!fb) { - // The tab or window closed. - return; - } - const matchesCount = this._requestMatchesCount(data); - fb.onMatchesCountResult(matchesCount); - }); - } - - _requestMatchesCount(data) { - if (!data) { - return { current: 0, total: 0 }; - } - let result = { - current: data.current, - total: data.total, - limit: typeof matchesCountLimit === "number" ? matchesCountLimit : 0, - }; - if (result.total > result.limit) { - result.total = -1; - } - return result; - } - - handleEvent(aEvent) { - const type = aEvent.type; - // Handle the tab find initialized event specially: - if (type == "TabFindInitialized") { - let browser = aEvent.target.linkedBrowser; - this._hookupEventListeners(browser); - aEvent.target.removeEventListener(type, this); - return; - } - - if (type == "SwapDocShells") { - this._removeEventListener(); - let newBrowser = aEvent.detail; - newBrowser.addEventListener( - "EndSwapDocShells", - evt => { - this._hookupEventListeners(newBrowser); - }, - { once: true } - ); - return; - } - - // Ignore events findbar events which arrive while the Pdfjs document is in - // the BFCache. - if (this.windowContext.isInBFCache) { - return; - } - - // To avoid forwarding the message as a CPOW, create a structured cloneable - // version of the event for both performance, and ease of usage, reasons. - let detail = null; - if (type !== "findbarclose") { - detail = { - query: aEvent.detail.query, - caseSensitive: aEvent.detail.caseSensitive, - entireWord: aEvent.detail.entireWord, - highlightAll: aEvent.detail.highlightAll, - findPrevious: aEvent.detail.findPrevious, - matchDiacritics: aEvent.detail.matchDiacritics, - }; - } - - let browser = aEvent.currentTarget.browser; - if (!this._boundToFindbar) { - throw new Error( - "FindEventManager was not bound for the current browser." - ); - } - browser.sendMessageToActor( - "PDFJS:Child:handleEvent", - { type, detail }, - "Pdfjs" - ); - aEvent.preventDefault(); - } - - _addEventListener() { - let browser = this.browser; - if (this._boundToFindbar) { - throw new Error( - "FindEventManager was bound 2nd time without unbinding it first." - ); - } - - this._hookupEventListeners(browser); - } - - /** - * Either hook up all the find event listeners if a findbar exists, - * or listen for a find bar being created and hook up event listeners - * when it does get created. - */ - _hookupEventListeners(aBrowser) { - let tabbrowser = aBrowser.getTabBrowser(); - let tab = tabbrowser.getTabForBrowser(aBrowser); - let findbar = tabbrowser.getCachedFindBar(tab); - if (findbar) { - // And we need to start listening to find events. - for (var i = 0; i < gFindTypes.length; i++) { - var type = gFindTypes[i]; - findbar.addEventListener(type, this, true); - } - this._boundToFindbar = findbar; - } else { - tab.addEventListener("TabFindInitialized", this); - } - aBrowser.addEventListener("SwapDocShells", this); - return !!findbar; - } - - _removeEventListener() { - let browser = this.browser; - - // make sure the listener has been removed. - let findbar = this._boundToFindbar; - if (findbar) { - // No reason to listen to find events any longer. - for (var i = 0; i < gFindTypes.length; i++) { - var type = gFindTypes[i]; - findbar.removeEventListener(type, this, true); - } - } else if (browser) { - // If we registered a `TabFindInitialized` listener which never fired, - // make sure we remove it. - let tabbrowser = browser.getTabBrowser(); - let tab = tabbrowser.getTabForBrowser(browser); - tab?.removeEventListener("TabFindInitialized", this); - } - - this._boundToFindbar = null; - - // Clean up any SwapDocShells event listeners. - browser?.removeEventListener("SwapDocShells", this); - } - - /* - * Display a notification warning when the renderer isn't sure - * a pdf displayed correctly. - */ - _displayWarning(aMsg) { - let data = aMsg.data; - let browser = this.browser; - - let tabbrowser = browser.getTabBrowser(); - let notificationBox = tabbrowser.getNotificationBox(browser); - - // Flag so we don't send the message twice, since if the user clicks - // "open with different viewer" both the button callback and - // eventCallback will be called. - let messageSent = false; - let sendMessage = download => { - // Don't send a response again if we already responded when the button was - // clicked. - if (messageSent) { - return; - } - try { - this.sendAsyncMessage("PDFJS:Child:fallbackDownload", { download }); - messageSent = true; - } catch (ex) { - // Ignore any exception if it is related to the child - // getting destroyed before the message can be sent. - if (!/JSWindowActorParent cannot send at the moment/.test(ex.message)) { - throw ex; - } - } - }; - let buttons = [ - { - label: data.label, - accessKey: data.accessKey, - callback() { - sendMessage(true); - }, - }, - ]; - notificationBox.appendNotification( - "pdfjs-fallback", - { - label: data.message, - priority: notificationBox.PRIORITY_INFO_MEDIUM, - eventCallback: eventType => { - // Currently there is only one event "removed" but if there are any other - // added in the future we still only care about removed at the moment. - if (eventType !== "removed") { - return; - } - sendMessage(false); - }, - }, - buttons - ); - } -} diff --git a/toolkit/components/pdfjs/content/build/pdf.js b/toolkit/components/pdfjs/content/build/pdf.js --- a/toolkit/components/pdfjs/content/build/pdf.js +++ b/toolkit/components/pdfjs/content/build/pdf.js @@ -1073,11 +1073,11 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.isNodeJS = void 0; -const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser"); +const isNodeJS = false; exports.isNodeJS = isNodeJS; /***/ }), /* 4 */ /***/ ((__unused_webpack_module, exports, __w_pdfjs_require__) => { @@ -1340,11 +1340,11 @@ source.contentDispositionFilename = pdfDataRangeTransport.contentDispositionFilename; } const workerId = await worker.messageHandler.sendWithPromise("GetDocRequest", { docId, - apiVersion: '2.14.224', + apiVersion: '2.14.257', source: { data: source.data, url: source.url, password: source.password, disableAutoFetch: source.disableAutoFetch, @@ -3360,13 +3360,13 @@ } } } -const version = '2.14.224'; +const version = '2.14.257'; exports.version = version; -const build = '2be19e828'; +const build = 'da4f7dfcd'; exports.build = build; /***/ }), /* 5 */ /***/ ((__unused_webpack_module, exports, __w_pdfjs_require__) => { @@ -4392,10 +4392,12 @@ var _pattern_helper = __w_pdfjs_require__(11); var _image_utils = __w_pdfjs_require__(12); +var _is_node = __w_pdfjs_require__(3); + var _display_utils = __w_pdfjs_require__(5); const MIN_FONT_SIZE = 16; const MAX_FONT_SIZE = 100; const MAX_GROUP_SIZE = 4096; @@ -4693,25 +4695,69 @@ } } } +function drawImageAtIntegerCoords(ctx, srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH) { + const [a, b, c, d, tx, ty] = ctx.mozCurrentTransform; + + if (b === 0 && c === 0) { + const tlX = destX * a + tx; + const rTlX = Math.round(tlX); + const tlY = destY * d + ty; + const rTlY = Math.round(tlY); + const brX = (destX + destW) * a + tx; + const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; + const brY = (destY + destH) * d + ty; + const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; + ctx.setTransform(Math.sign(a), 0, 0, Math.sign(d), rTlX, rTlY); + ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rWidth, rHeight); + ctx.setTransform(a, b, c, d, tx, ty); + return [rWidth, rHeight]; + } + + if (a === 0 && d === 0) { + const tlX = destY * c + tx; + const rTlX = Math.round(tlX); + const tlY = destX * b + ty; + const rTlY = Math.round(tlY); + const brX = (destY + destH) * c + tx; + const rWidth = Math.abs(Math.round(brX) - rTlX) || 1; + const brY = (destX + destW) * b + ty; + const rHeight = Math.abs(Math.round(brY) - rTlY) || 1; + ctx.setTransform(0, Math.sign(b), Math.sign(c), 0, rTlX, rTlY); + ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, 0, 0, rHeight, rWidth); + ctx.setTransform(a, b, c, d, tx, ty); + return [rHeight, rWidth]; + } + + ctx.drawImage(srcImg, srcX, srcY, srcW, srcH, destX, destY, destW, destH); + const scaleX = Math.hypot(a, b); + const scaleY = Math.hypot(c, d); + return [scaleX * destW, scaleY * destH]; +} + function compileType3Glyph(imgData) { + const { + width, + height + } = imgData; + + if (!COMPILE_TYPE3_GLYPHS || width > MAX_SIZE_TO_COMPILE || height > MAX_SIZE_TO_COMPILE) { + return null; + } + const POINT_TO_PROCESS_LIMIT = 1000; const POINT_TYPES = new Uint8Array([0, 2, 4, 0, 1, 0, 5, 4, 8, 10, 0, 8, 0, 2, 1, 0]); - const width = imgData.width, - height = imgData.height, - width1 = width + 1; - let i, ii, j, j0; - const points = new Uint8Array(width1 * (height + 1)); - const lineSize = width + 7 & ~7, - data0 = imgData.data; - const data = new Uint8Array(lineSize * height); - let pos = 0; - - for (i = 0, ii = data0.length; i < ii; i++) { - const elem = data0[i]; + const width1 = width + 1; + let points = new Uint8Array(width1 * (height + 1)); + let i, j, j0; + const lineSize = width + 7 & ~7; + let data = new Uint8Array(lineSize * height), + pos = 0; + + for (const elem of imgData.data) { let mask = 128; while (mask > 0) { data[pos++] = elem & mask ? 0 : 255; mask >>= 1; @@ -4797,11 +4843,17 @@ if (count > POINT_TO_PROCESS_LIMIT) { return null; } const steps = new Int32Array([0, width1, -1, 0, -width1, 0, 0, 0, 1]); - const outlines = []; + let path, outlines, coords; + + if (!_is_node.isNodeJS) { + path = new Path2D(); + } else { + outlines = []; + } for (i = 0; count && i <= height; i++) { let p = i * width1; const end = p + width; @@ -4811,11 +4863,16 @@ if (p === end) { continue; } - const coords = [p % width1, i]; + if (path) { + path.moveTo(p % width1, i); + } else { + coords = [p % width1, i]; + } + const p0 = p; let type = points[p]; do { const step = steps[type]; @@ -4832,37 +4889,52 @@ } else { type = pp & 0x33 * type >> 4; points[p] &= type >> 2 | type << 2; } - coords.push(p % width1, p / width1 | 0); + if (path) { + path.lineTo(p % width1, p / width1 | 0); + } else { + coords.push(p % width1, p / width1 | 0); + } if (!points[p]) { --count; } } while (p0 !== p); - outlines.push(coords); + if (!path) { + outlines.push(coords); + } + --i; } + data = null; + points = null; + const drawOutline = function (c) { c.save(); c.scale(1 / width, -1 / height); c.translate(0, -height); - c.beginPath(); - - for (let k = 0, kk = outlines.length; k < kk; k++) { - const o = outlines[k]; - c.moveTo(o[0], o[1]); - - for (let l = 2, ll = o.length; l < ll; l += 2) { - c.lineTo(o[l], o[l + 1]); - } - } - - c.fill(); + + if (path) { + c.fill(path); + } else { + c.beginPath(); + + for (const o of outlines) { + c.moveTo(o[0], o[1]); + + for (let l = 2, ll = o.length; l < ll; l += 2) { + c.lineTo(o[l], o[l + 1]); + } + } + + c.fill(); + } + c.beginPath(); c.restore(); }; return drawOutline; @@ -5627,12 +5699,12 @@ const cord2 = _util.Util.applyTransform([width, height], maskToCanvas); const rect = _util.Util.normalizeRect([cord1[0], cord1[1], cord2[0], cord2[1]]); - const drawnWidth = Math.ceil(rect[2] - rect[0]); - const drawnHeight = Math.ceil(rect[3] - rect[1]); + const drawnWidth = Math.round(rect[2] - rect[0]) || 1; + const drawnHeight = Math.round(rect[3] - rect[1]) || 1; const fillCanvas = this.cachedCanvases.getCanvas("fillCanvas", drawnWidth, drawnHeight, true); const fillCtx = fillCanvas.context; const offsetX = Math.min(cord1[0], cord2[0]); const offsetY = Math.min(cord1[1], cord2[1]); fillCtx.translate(-offsetX, -offsetY); @@ -5646,11 +5718,11 @@ cache.set(cacheKey, scaled); } } fillCtx.imageSmoothingEnabled = getImageSmoothingEnabled(fillCtx.mozCurrentTransform, img.interpolate); - fillCtx.drawImage(scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height); + drawImageAtIntegerCoords(fillCtx, scaled, 0, 0, scaled.width, scaled.height, 0, 0, width, height); fillCtx.globalCompositeOperation = "source-in"; const inverse = _util.Util.transform(fillCtx.mozCurrentTransformInverse, [1, 0, 0, 1, -offsetX, -offsetY]); fillCtx.fillStyle = isPatternFill ? fillColor.getPattern(ctx, this, inverse, _pattern_helper.PathType.FILL) : fillColor; @@ -6845,29 +6917,21 @@ const count = img.count; img = this.getObject(img.data, img); img.count = count; const ctx = this.ctx; - const width = img.width, - height = img.height; const glyph = this.processingType3; - if (COMPILE_TYPE3_GLYPHS && glyph && glyph.compiled === undefined) { - if (width <= MAX_SIZE_TO_COMPILE && height <= MAX_SIZE_TO_COMPILE) { - glyph.compiled = compileType3Glyph({ - data: img.data, - width, - height - }); - } else { - glyph.compiled = null; - } - } - - if (glyph?.compiled) { - glyph.compiled(ctx); - return; + if (glyph) { + if (glyph.compiled === undefined) { + glyph.compiled = compileType3Glyph(img); + } + + if (glyph.compiled) { + glyph.compiled(ctx); + return; + } } const mask = this._createMaskCanvas(img); const maskCanvas = mask.canvas; @@ -6927,11 +6991,11 @@ maskCtx.fillRect(0, 0, width, height); maskCtx.restore(); ctx.save(); ctx.transform.apply(ctx, image.transform); ctx.scale(1, -1); - ctx.drawImage(maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1); + drawImageAtIntegerCoords(ctx, maskCanvas.canvas, 0, 0, width, height, 0, -1, 1, 1); ctx.restore(); } this.compose(); } @@ -7002,20 +7066,20 @@ } const scaled = this._scaleImage(imgToPaint, ctx.mozCurrentTransformInverse); ctx.imageSmoothingEnabled = getImageSmoothingEnabled(ctx.mozCurrentTransform, imgData.interpolate); - ctx.drawImage(scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height); + const [rWidth, rHeight] = drawImageAtIntegerCoords(ctx, scaled.img, 0, 0, scaled.paintWidth, scaled.paintHeight, 0, -height, width, height); if (this.imageLayer) { const position = this.getCanvasPosition(0, -height); this.imageLayer.appendImage({ imgData, left: position[0], top: position[1], - width: width / ctx.mozCurrentTransformInverse[0], - height: height / ctx.mozCurrentTransformInverse[3] + width: rWidth, + height: rHeight }); } this.compose(); this.restore(); @@ -7036,11 +7100,11 @@ for (let i = 0, ii = map.length; i < ii; i++) { const entry = map[i]; ctx.save(); ctx.transform.apply(ctx, entry.transform); ctx.scale(1, -1); - ctx.drawImage(tmpCanvas.canvas, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1); + drawImageAtIntegerCoords(ctx, tmpCanvas.canvas, entry.x, entry.y, entry.w, entry.h, 0, -1, 1, 1); if (this.imageLayer) { const position = this.getCanvasPosition(entry.x, entry.y); this.imageLayer.appendImage({ imgData, @@ -7260,19 +7324,21 @@ exports.TilingPattern = exports.PathType = void 0; exports.getShadingPattern = getShadingPattern; var _util = __w_pdfjs_require__(1); +var _is_node = __w_pdfjs_require__(3); + const PathType = { FILL: "Fill", STROKE: "Stroke", SHADING: "Shading" }; exports.PathType = PathType; function applyBoundingBox(ctx, bbox) { - if (!bbox || typeof Path2D === "undefined") { + if (!bbox || _is_node.isNodeJS) { return; } const width = bbox[2] - bbox[0]; const height = bbox[3] - bbox[1]; @@ -12636,14 +12702,14 @@ var _svg = __w_pdfjs_require__(23); var _xfa_layer = __w_pdfjs_require__(21); -const pdfjsVersion = '2.14.224'; -const pdfjsBuild = '2be19e828'; +const pdfjsVersion = '2.14.257'; +const pdfjsBuild = 'da4f7dfcd'; ; })(); /******/ return __webpack_exports__; /******/ })() ; }); \ No newline at end of file diff --git a/toolkit/components/pdfjs/content/build/pdf.scripting.js b/toolkit/components/pdfjs/content/build/pdf.scripting.js --- a/toolkit/components/pdfjs/content/build/pdf.scripting.js +++ b/toolkit/components/pdfjs/content/build/pdf.scripting.js @@ -4954,13 +4954,13 @@ } })); var _initialization = __w_pdfjs_require__(1); -const pdfjsVersion = '2.14.224'; -const pdfjsBuild = '2be19e828'; +const pdfjsVersion = '2.14.257'; +const pdfjsBuild = 'da4f7dfcd'; })(); /******/ return __webpack_exports__; /******/ })() ; }); \ No newline at end of file diff --git a/toolkit/components/pdfjs/content/build/pdf.worker.js b/toolkit/components/pdfjs/content/build/pdf.worker.js --- a/toolkit/components/pdfjs/content/build/pdf.worker.js +++ b/toolkit/components/pdfjs/content/build/pdf.worker.js @@ -115,11 +115,11 @@ let terminated = false; let cancelXHRs = null; const WorkerTasks = []; const verbosity = (0, _util.getVerbosityLevel)(); const apiVersion = docParams.apiVersion; - const workerVersion = '2.14.224'; + const workerVersion = '2.14.257'; if (apiVersion !== workerVersion) { throw new Error(`The API version "${apiVersion}" does not match ` + `the Worker version "${workerVersion}".`); } @@ -1730,11 +1730,11 @@ Object.defineProperty(exports, "__esModule", ({ value: true })); exports.isNodeJS = void 0; -const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser"); +const isNodeJS = false; exports.isNodeJS = isNodeJS; /***/ }), /* 5 */ /***/ ((__unused_webpack_module, exports, __w_pdfjs_require__) => { @@ -2490,22 +2490,21 @@ } return this.bytes[this.pos++]; } - getBytes(length, forceClamped = false) { + getBytes(length) { const bytes = this.bytes; const pos = this.pos; const strEnd = this.end; if (!length) { if (strEnd > this.progressiveDataLength) { this.ensureRange(pos, strEnd); } - const subarray = bytes.subarray(pos, strEnd); - return forceClamped ? new Uint8ClampedArray(subarray) : subarray; + return bytes.subarray(pos, strEnd); } let end = pos + length; if (end > strEnd) { @@ -2515,12 +2514,11 @@ if (end > this.progressiveDataLength) { this.ensureRange(pos, end); } this.pos = end; - const subarray = bytes.subarray(pos, end); - return forceClamped ? new Uint8ClampedArray(subarray) : subarray; + return bytes.subarray(pos, end); } getByteRange(begin, end) { if (begin < 0) { begin = 0; @@ -3428,11 +3426,11 @@ getByte() { (0, _util.unreachable)("Abstract method `getByte` called"); } - getBytes(length, forceClamped = false) { + getBytes(length) { (0, _util.unreachable)("Abstract method `getBytes` called"); } peekByte() { const peekedByte = this.getByte(); @@ -3442,12 +3440,12 @@ } return peekedByte; } - peekBytes(length, forceClamped = false) { - const bytes = this.getBytes(length, forceClamped); + peekBytes(length) { + const bytes = this.getBytes(length); this.pos -= bytes.length; return bytes; } getUint16() { @@ -3472,11 +3470,11 @@ getByteRange(begin, end) { (0, _util.unreachable)("Abstract method `getByteRange` called"); } getString(length) { - return (0, _util.bytesToString)(this.getBytes(length, false)); + return (0, _util.bytesToString)(this.getBytes(length)); } skip(n) { this.pos += n || 1; } @@ -3540,29 +3538,27 @@ } return this.bytes[this.pos++]; } - getBytes(length, forceClamped = false) { + getBytes(length) { const bytes = this.bytes; const pos = this.pos; const strEnd = this.end; if (!length) { - const subarray = bytes.subarray(pos, strEnd); - return forceClamped ? new Uint8ClampedArray(subarray) : subarray; + return bytes.subarray(pos, strEnd); } let end = pos + length; if (end > strEnd) { end = strEnd; } this.pos = end; - const subarray = bytes.subarray(pos, end); - return forceClamped ? new Uint8ClampedArray(subarray) : subarray; + return bytes.subarray(pos, end); } getByteRange(begin, end) { if (begin < 0) { begin = 0; @@ -10654,11 +10650,11 @@ let imgData, args; if (imageMask) { const interpolate = dict.get("I", "Interpolate"); const bitStrideLength = w + 7 >> 3; - const imgArray = image.getBytes(bitStrideLength * h, true); + const imgArray = image.getBytes(bitStrideLength * h); const decode = dict.getArray("D", "Decode"); if (this.parsingType3Font) { imgData = _image.PDFImage.createRawMask({ imgArray, @@ -17131,11 +17127,11 @@ } return this.buffer[this.pos++]; } - getBytes(length, forceClamped = false) { + getBytes(length) { const pos = this.pos; let end; if (length) { this.ensureBuffer(pos + length); @@ -17157,12 +17153,11 @@ end = this.bufferLength; } this.pos = end; - const subarray = this.buffer.subarray(pos, end); - return forceClamped && !(subarray instanceof Uint8ClampedArray) ? new Uint8ClampedArray(subarray) : subarray; + return this.buffer.subarray(pos, end); } reset() { this.pos = 0; } @@ -40366,11 +40361,11 @@ maxLineHeight = Math.max(maxLineHeight, img.height); } const imgWidth = Math.max(maxX, currentX) + IMAGE_PADDING; const imgHeight = currentY + maxLineHeight + IMAGE_PADDING; - const imgData = new Uint8ClampedArray(imgWidth * imgHeight * 4); + const imgData = new Uint8Array(imgWidth * imgHeight * 4); const imgRowSize = imgWidth << 2; for (let q = 0; q < count; q++) { const data = argsArray[iFirstPIIXO + (q << 2)][0].data; const rowSize = map[q].w << 2; @@ -40883,11 +40878,10 @@ switch (fnArray[i]) { case _util.OPS.paintInlineImageXObject: case _util.OPS.paintInlineImageXObjectGroup: case _util.OPS.paintImageMaskXObject: const arg = argsArray[i][0]; - ; if (!arg.cached && arg.data && arg.data.buffer instanceof ArrayBuffer) { transfers.push(arg.data.buffer); } @@ -41206,19 +41200,15 @@ let data, i; if (imageIsFromDecodeStream && (!inverseDecode || haveFullData)) { data = imgArray; } else if (!inverseDecode) { - data = new Uint8ClampedArray(actualLength); - data.set(imgArray); - } else { - data = new Uint8ClampedArray(computedLength); + data = new Uint8Array(imgArray); + } else { + data = new Uint8Array(computedLength); data.set(imgArray); - - for (i = actualLength; i < computedLength; i++) { - data[i] = 0xff; - } + data.fill(0xff, actualLength); } if (inverseDecode) { for (i = 0; i < actualLength; i++) { data[i] ^= 0xff; @@ -41514,11 +41504,10 @@ const numComps = this.numComps; const originalWidth = this.width; const originalHeight = this.height; const bpc = this.bpc; const rowBytes = originalWidth * numComps * bpc + 7 >> 3; - let imgArray; if (!forceRGBA) { let kind; if (this.colorSpace.name === "DeviceGray" && bpc === 1) { @@ -41527,19 +41516,11 @@ kind = _util.ImageKind.RGB_24BPP; } if (kind && !this.smask && !this.mask && drawWidth === originalWidth && drawHeight === originalHeight) { imgData.kind = kind; - imgArray = this.getImageBytes(originalHeight * rowBytes); - - if (this.image instanceof _decode_stream.DecodeStream) { - imgData.data = imgArray; - } else { - const newArray = new Uint8ClampedArray(imgArray.length); - newArray.set(imgArray); - imgData.data = newArray; - } + imgData.data = this.getImageBytes(originalHeight * rowBytes, {}); if (this.needsDecode) { (0, _util.assert)(kind === _util.ImageKind.GRAYSCALE_1BPP, "PDFImage.createImageData: The image must be grayscale."); const buffer = imgData.data; @@ -41559,17 +41540,23 @@ imageLength *= 3; case "DeviceRGB": case "DeviceCMYK": imgData.kind = _util.ImageKind.RGB_24BPP; - imgData.data = this.getImageBytes(imageLength, drawWidth, drawHeight, true); + imgData.data = this.getImageBytes(imageLength, { + drawWidth, + drawHeight, + forceRGB: true + }); return imgData; } } } - imgArray = this.getImageBytes(originalHeight * rowBytes); + const imgArray = this.getImageBytes(originalHeight * rowBytes, { + internal: true + }); const actualHeight = 0 | imgArray.length / rowBytes * drawHeight / originalHeight; const comps = this.getComponents(imgArray); let alpha01, maybeUndoPreblend; if (!forceRGBA && !this.smask && !this.mask) { @@ -41607,11 +41594,13 @@ const width = this.width; const height = this.height; const bpc = this.bpc; const rowBytes = width * numComps * bpc + 7 >> 3; - const imgArray = this.getImageBytes(height * rowBytes); + const imgArray = this.getImageBytes(height * rowBytes, { + internal: true + }); const comps = this.getComponents(imgArray); let i, length; if (bpc === 1) { length = width * height; @@ -41639,16 +41628,28 @@ for (i = 0; i < length; ++i) { buffer[i] = scale * comps[i]; } } - getImageBytes(length, drawWidth, drawHeight, forceRGB = false) { + getImageBytes(length, { + drawWidth, + drawHeight, + forceRGB = false, + internal = false + }) { this.image.reset(); this.image.drawWidth = drawWidth || this.width; this.image.drawHeight = drawHeight || this.height; this.image.forceRGB = !!forceRGB; - return this.image.getBytes(length, true); + const imageBytes = this.image.getBytes(length); + + if (internal || this.image instanceof _decode_stream.DecodeStream) { + return imageBytes; + } + + (0, _util.assert)(imageBytes instanceof Uint8Array, 'PDFImage.getImageBytes: Unsupported "imageBytes" type.'); + return new Uint8Array(imageBytes); } } exports.PDFImage = PDFImage; @@ -42293,11 +42294,11 @@ if (obj instanceof _name_number_tree.NameTree) { for (const [key, value] of obj.getAll()) { const dest = fetchDestination(value); if (dest) { - dests[key] = dest; + dests[(0, _util.stringToPDFString)(key)] = dest; } } } else if (obj instanceof _primitives.Dict) { obj.forEach(function (key, value) { const dest = fetchDestination(value); @@ -42720,11 +42721,11 @@ for (const [key, value] of nameTree.getAll()) { if (!xfaImages) { xfaImages = new _primitives.Dict(this.xref); } - xfaImages.set(key, value); + xfaImages.set((0, _util.stringToPDFString)(key), value); } } return (0, _util.shadow)(this, "xfaImages", xfaImages); } @@ -42760,11 +42761,11 @@ if (obj instanceof _primitives.Dict && obj.has("JavaScript")) { const nameTree = new _name_number_tree.NameTree(obj.getRaw("JavaScript"), this.xref); for (const [key, value] of nameTree.getAll()) { - appendIfJavaScriptDict(key, value); + appendIfJavaScriptDict((0, _util.stringToPDFString)(key), value); } } const openAction = this._catDict.get("OpenAction"); @@ -43353,11 +43354,13 @@ if (dest) { if (dest instanceof _primitives.Name) { dest = dest.name; } - if (typeof dest === "string" || Array.isArray(dest)) { + if (typeof dest === "string") { + resultObj.dest = (0, _util.stringToPDFString)(dest); + } else if (Array.isArray(dest)) { resultObj.dest = dest; } } } @@ -43411,13 +43414,15 @@ } if (obj.has("Kids")) { const kids = obj.get("Kids"); - for (let i = 0, ii = kids.length; i < ii; i++) { - const kid = kids[i]; - + if (!Array.isArray(kids)) { + continue; + } + + for (const kid of kids) { if (processed.has(kid)) { throw new _util.FormatError(`Duplicate entry in "${this._type}" tree.`); } queue.push(kid); @@ -43474,11 +43479,11 @@ if (key < xref.fetchIfRef(limits[0])) { r = m - 1; } else if (key > xref.fetchIfRef(limits[1])) { l = m + 1; } else { - kidsOrEntries = xref.fetchIfRef(kids[m]); + kidsOrEntries = kid; break; } } if (l > r) { @@ -62405,13 +62410,13 @@ } })); var _worker = __w_pdfjs_require__(1); -const pdfjsVersion = '2.14.224'; -const pdfjsBuild = '2be19e828'; +const pdfjsVersion = '2.14.257'; +const pdfjsBuild = 'da4f7dfcd'; })(); /******/ return __webpack_exports__; /******/ })() ; }); \ No newline at end of file diff --git a/toolkit/components/pdfjs/content/web/images/toolbarButton-openFile.svg b/toolkit/components/pdfjs/content/web/images/toolbarButton-openFile.svg deleted file mode 100644 --- a/toolkit/components/pdfjs/content/web/images/toolbarButton-openFile.svg +++ /dev/null @@ -1,4 +0,0 @@ - - \ No newline at end of file diff --git a/toolkit/components/pdfjs/content/web/viewer.html b/toolkit/components/pdfjs/content/web/viewer.html --- a/toolkit/components/pdfjs/content/web/viewer.html +++ b/toolkit/components/pdfjs/content/web/viewer.html @@ -118,93 +118,90 @@
@@ -218,38 +215,35 @@