/* 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. */ /* jshint esnext:true */ /* globals Components, Services, XPCOMUtils, PdfjsChromeUtils, PdfjsContentUtils, PdfStreamConverter */ 'use strict'; var EXPORTED_SYMBOLS = ['PdfJs']; const Cc = Components.classes; const Ci = Components.interfaces; const Cr = Components.results; const Cm = Components.manager; const Cu = Components.utils; 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_DISABLED_PLUGIN_TYPES = 'plugin.disable_full_page_plugin_for_types'; const TOPIC_PDFJS_HANDLER_CHANGED = 'pdfjs:handlerChanged'; const TOPIC_PLUGINS_LIST_UPDATED = 'plugins-list-updated'; const TOPIC_PLUGIN_INFO_UPDATED = 'plugin-info-updated'; const PDF_CONTENT_TYPE = 'application/pdf'; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); var Svc = {}; XPCOMUtils.defineLazyServiceGetter(Svc, 'mime', '@mozilla.org/mime;1', 'nsIMIMEService'); XPCOMUtils.defineLazyServiceGetter(Svc, 'pluginHost', '@mozilla.org/plugin/host;1', 'nsIPluginHost'); XPCOMUtils.defineLazyModuleGetter(this, 'PdfjsChromeUtils', 'resource://pdf.js/PdfjsChromeUtils.jsm'); XPCOMUtils.defineLazyModuleGetter(this, 'PdfjsContentUtils', 'resource://pdf.js/PdfjsContentUtils.jsm'); function getBoolPref(aPref, aDefaultValue) { try { return Services.prefs.getBoolPref(aPref); } catch (ex) { return aDefaultValue; } } function getIntPref(aPref, aDefaultValue) { try { return Services.prefs.getIntPref(aPref); } catch (ex) { return aDefaultValue; } } function isDefaultHandler() { if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) { return PdfjsContentUtils.isDefaultHandlerApp(); } return PdfjsChromeUtils.isDefaultHandlerApp(); } function initializeDefaultPreferences() { var DEFAULT_PREFERENCES = { "showPreviousViewOnLoad": true, "defaultZoomValue": "", "sidebarViewOnLoad": 0, "enableHandToolOnLoad": false, "enableWebGL": false, "pdfBugEnabled": false, "disableRange": false, "disableStream": false, "disableAutoFetch": false, "disableFontFace": false, "disableTextLayer": false, "useOnlyCssZoom": false, "externalLinkTarget": 0, "enhanceTextSelection": false, "renderInteractiveForms": false, "disablePageLabels": false } var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); var defaultValue; for (var key in DEFAULT_PREFERENCES) { defaultValue = DEFAULT_PREFERENCES[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; } } } // Register/unregister a constructor as a factory. function Factory() {} Factory.prototype = { register: function register(targetConstructor) { var proto = targetConstructor.prototype; this._classID = proto.classID; var factory = XPCOMUtils._getFactory(targetConstructor); this._factory = factory; var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); registrar.registerFactory(proto.classID, proto.classDescription, proto.contractID, factory); if (proto.classID2) { this._classID2 = proto.classID2; registrar.registerFactory(proto.classID2, proto.classDescription, proto.contractID2, factory); } }, unregister: function unregister() { var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); registrar.unregisterFactory(this._classID, this._factory); if (this._classID2) { registrar.unregisterFactory(this._classID2, this._factory); } this._factory = null; } }; var PdfJs = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), _registered: false, _initialized: false, init: function init(remote) { if (Services.appinfo.processType !== Services.appinfo.PROCESS_TYPE_DEFAULT) { throw new Error('PdfJs.init should only get called ' + 'in the parent process.'); } PdfjsChromeUtils.init(); if (!remote) { PdfjsContentUtils.init(); } this.initPrefs(); this.updateRegistration(); }, initPrefs: function initPrefs() { if (this._initialized) { return; } this._initialized = true; if (!getBoolPref(PREF_DISABLED, true)) { this._migrate(); } // Listen for when pdf.js is completely disabled or a different pdf handler // is chosen. Services.prefs.addObserver(PREF_DISABLED, this, false); Services.prefs.addObserver(PREF_DISABLED_PLUGIN_TYPES, this, false); Services.obs.addObserver(this, TOPIC_PDFJS_HANDLER_CHANGED, false); Services.obs.addObserver(this, TOPIC_PLUGINS_LIST_UPDATED, false); Services.obs.addObserver(this, TOPIC_PLUGIN_INFO_UPDATED, false); initializeDefaultPreferences(); }, updateRegistration: function updateRegistration() { if (this.enabled) { this._ensureRegistered(); } else { this._ensureUnregistered(); } }, uninit: function uninit() { if (this._initialized) { Services.prefs.removeObserver(PREF_DISABLED, this, false); Services.prefs.removeObserver(PREF_DISABLED_PLUGIN_TYPES, this, false); Services.obs.removeObserver(this, TOPIC_PDFJS_HANDLER_CHANGED, false); Services.obs.removeObserver(this, TOPIC_PLUGINS_LIST_UPDATED, false); Services.obs.removeObserver(this, TOPIC_PLUGIN_INFO_UPDATED, false); this._initialized = false; } this._ensureUnregistered(); }, _migrate: function migrate() { const VERSION = 2; var currentVersion = 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() { let handlerInfo = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, 'pdf'); let prefs = Services.prefs; if (handlerInfo.preferredAction !== Ci.nsIHandlerInfo.handleInternally && handlerInfo.preferredAction !== 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); } let handlerService = Cc['@mozilla.org/uriloader/handler-service;1']. getService(Ci.nsIHandlerService); // Change and save mime handler settings. handlerInfo.alwaysAskBeforeHandling = false; handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally; handlerService.store(handlerInfo); // Also disable any plugins for pdfs. var stringTypes = ''; var types = []; if (prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) { stringTypes = prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES); } if (stringTypes !== '') { types = stringTypes.split(','); } if (types.indexOf(PDF_CONTENT_TYPE) === -1) { types.push(PDF_CONTENT_TYPE); } prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES, types.join(',')); // Update the category manager in case the plugins are already loaded. let categoryManager = Cc['@mozilla.org/categorymanager;1']; categoryManager.getService(Ci.nsICategoryManager). deleteCategoryEntry('Gecko-Content-Viewers', PDF_CONTENT_TYPE, false); }, // nsIObserver observe: function observe(aSubject, aTopic, aData) { this.updateRegistration(); if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT) { let jsm = 'resource://pdf.js/PdfjsChromeUtils.jsm'; let PdfjsChromeUtils = Components.utils.import(jsm, {}).PdfjsChromeUtils; PdfjsChromeUtils.notifyChildOfSettingsChange(); } }, /** * pdf.js is only enabled if it is both selected as the pdf viewer and if the * global switch enabling it is true. * @return {boolean} Whether or not it's enabled. */ get enabled() { var disabled = getBoolPref(PREF_DISABLED, true); if (disabled) { return false; } // Check if the 'application/pdf' preview handler is configured properly. if (!isDefaultHandler()) { return false; } // Check if we have disabled plugin handling of 'application/pdf' in prefs if (Services.prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) { let disabledPluginTypes = Services.prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES).split(','); if (disabledPluginTypes.indexOf(PDF_CONTENT_TYPE) >= 0) { return true; } } // Check if there is an enabled pdf plugin. // Note: this check is performed last because getPluginTags() triggers // costly plugin list initialization (bug 881575) let tags = Cc['@mozilla.org/plugin/host;1']. getService(Ci.nsIPluginHost). getPluginTags(); let enabledPluginFound = tags.some(function(tag) { if (tag.disabled) { return false; } let mimeTypes = tag.getMimeTypes(); return mimeTypes.some(function(mimeType) { return mimeType === PDF_CONTENT_TYPE; }); }); // Use pdf.js if pdf plugin is not present or disabled return !enabledPluginFound; }, _ensureRegistered: function _ensureRegistered() { if (this._registered) { return; } this._pdfStreamConverterFactory = new Factory(); Cu.import('resource://pdf.js/PdfStreamConverter.jsm'); this._pdfStreamConverterFactory.register(PdfStreamConverter); this._registered = true; }, _ensureUnregistered: function _ensureUnregistered() { if (!this._registered) { return; } this._pdfStreamConverterFactory.unregister(); Cu.unload('resource://pdf.js/PdfStreamConverter.jsm'); delete this._pdfStreamConverterFactory; this._registered = false; } };