michael@0: /* Copyright 2012 Mozilla Foundation michael@0: * michael@0: * Licensed under the Apache License, Version 2.0 (the "License"); michael@0: * you may not use this file except in compliance with the License. michael@0: * You may obtain a copy of the License at michael@0: * michael@0: * http://www.apache.org/licenses/LICENSE-2.0 michael@0: * michael@0: * Unless required by applicable law or agreed to in writing, software michael@0: * distributed under the License is distributed on an "AS IS" BASIS, michael@0: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. michael@0: * See the License for the specific language governing permissions and michael@0: * limitations under the License. michael@0: */ michael@0: michael@0: var EXPORTED_SYMBOLS = ["PdfJs"]; michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: const Cr = Components.results; michael@0: const Cm = Components.manager; michael@0: const Cu = Components.utils; michael@0: michael@0: const PREF_PREFIX = 'pdfjs'; michael@0: const PREF_DISABLED = PREF_PREFIX + '.disabled'; michael@0: const PREF_MIGRATION_VERSION = PREF_PREFIX + '.migrationVersion'; michael@0: const PREF_PREVIOUS_ACTION = PREF_PREFIX + '.previousHandler.preferredAction'; michael@0: const PREF_PREVIOUS_ASK = PREF_PREFIX + '.previousHandler.alwaysAskBeforeHandling'; michael@0: const PREF_DISABLED_PLUGIN_TYPES = 'plugin.disable_full_page_plugin_for_types'; michael@0: const TOPIC_PDFJS_HANDLER_CHANGED = 'pdfjs:handlerChanged'; michael@0: const TOPIC_PLUGINS_LIST_UPDATED = "plugins-list-updated"; michael@0: const TOPIC_PLUGIN_INFO_UPDATED = "plugin-info-updated"; michael@0: const PDF_CONTENT_TYPE = 'application/pdf'; michael@0: michael@0: Cu.import('resource://gre/modules/XPCOMUtils.jsm'); michael@0: Cu.import('resource://gre/modules/Services.jsm'); michael@0: michael@0: let Svc = {}; michael@0: XPCOMUtils.defineLazyServiceGetter(Svc, 'mime', michael@0: '@mozilla.org/mime;1', michael@0: 'nsIMIMEService'); michael@0: XPCOMUtils.defineLazyServiceGetter(Svc, 'pluginHost', michael@0: '@mozilla.org/plugin/host;1', michael@0: 'nsIPluginHost'); michael@0: michael@0: function getBoolPref(aPref, aDefaultValue) { michael@0: try { michael@0: return Services.prefs.getBoolPref(aPref); michael@0: } catch (ex) { michael@0: return aDefaultValue; michael@0: } michael@0: } michael@0: michael@0: function getIntPref(aPref, aDefaultValue) { michael@0: try { michael@0: return Services.prefs.getIntPref(aPref); michael@0: } catch (ex) { michael@0: return aDefaultValue; michael@0: } michael@0: } michael@0: michael@0: function initializeDefaultPreferences() { michael@0: michael@0: var DEFAULT_PREFERENCES = { michael@0: showPreviousViewOnLoad: true, michael@0: defaultZoomValue: '', michael@0: ifAvailableShowOutlineOnLoad: false, michael@0: enableHandToolOnLoad: false, michael@0: enableWebGL: false michael@0: }; michael@0: michael@0: michael@0: var defaultBranch = Services.prefs.getDefaultBranch(PREF_PREFIX + '.'); michael@0: var defaultValue; michael@0: for (var key in DEFAULT_PREFERENCES) { michael@0: defaultValue = DEFAULT_PREFERENCES[key]; michael@0: switch (typeof defaultValue) { michael@0: case 'boolean': michael@0: defaultBranch.setBoolPref(key, defaultValue); michael@0: break; michael@0: case 'number': michael@0: defaultBranch.setIntPref(key, defaultValue); michael@0: break; michael@0: case 'string': michael@0: defaultBranch.setCharPref(key, defaultValue); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Register/unregister a constructor as a factory. michael@0: function Factory() {} michael@0: Factory.prototype = { michael@0: register: function register(targetConstructor) { michael@0: var proto = targetConstructor.prototype; michael@0: this._classID = proto.classID; michael@0: michael@0: var factory = XPCOMUtils._getFactory(targetConstructor); michael@0: this._factory = factory; michael@0: michael@0: var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); michael@0: registrar.registerFactory(proto.classID, proto.classDescription, michael@0: proto.contractID, factory); michael@0: }, michael@0: michael@0: unregister: function unregister() { michael@0: var registrar = Cm.QueryInterface(Ci.nsIComponentRegistrar); michael@0: registrar.unregisterFactory(this._classID, this._factory); michael@0: this._factory = null; michael@0: } michael@0: }; michael@0: michael@0: let PdfJs = { michael@0: QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver]), michael@0: _registered: false, michael@0: michael@0: init: function init() { michael@0: if (!getBoolPref(PREF_DISABLED, true)) { michael@0: this._migrate(); michael@0: } michael@0: michael@0: if (this.enabled) michael@0: this._ensureRegistered(); michael@0: else michael@0: this._ensureUnregistered(); michael@0: michael@0: // Listen for when pdf.js is completely disabled or a different pdf handler michael@0: // is chosen. michael@0: Services.prefs.addObserver(PREF_DISABLED, this, false); michael@0: Services.prefs.addObserver(PREF_DISABLED_PLUGIN_TYPES, this, false); michael@0: Services.obs.addObserver(this, TOPIC_PDFJS_HANDLER_CHANGED, false); michael@0: Services.obs.addObserver(this, TOPIC_PLUGINS_LIST_UPDATED, false); michael@0: Services.obs.addObserver(this, TOPIC_PLUGIN_INFO_UPDATED, false); michael@0: michael@0: initializeDefaultPreferences(); michael@0: }, michael@0: michael@0: _migrate: function migrate() { michael@0: const VERSION = 2; michael@0: var currentVersion = getIntPref(PREF_MIGRATION_VERSION, 0); michael@0: if (currentVersion >= VERSION) { michael@0: return; michael@0: } michael@0: // Make pdf.js the default pdf viewer on the first migration. michael@0: if (currentVersion < 1) { michael@0: this._becomeHandler(); michael@0: } michael@0: if (currentVersion < 2) { michael@0: // cleaning up of unused database preference (see #3994) michael@0: Services.prefs.clearUserPref(PREF_PREFIX + '.database'); michael@0: } michael@0: Services.prefs.setIntPref(PREF_MIGRATION_VERSION, VERSION); michael@0: }, michael@0: michael@0: _becomeHandler: function _becomeHandler() { michael@0: let handlerInfo = Svc.mime.getFromTypeAndExtension(PDF_CONTENT_TYPE, 'pdf'); michael@0: let prefs = Services.prefs; michael@0: if (handlerInfo.preferredAction !== Ci.nsIHandlerInfo.handleInternally && michael@0: handlerInfo.preferredAction !== false) { michael@0: // Store the previous settings of preferredAction and michael@0: // alwaysAskBeforeHandling in case we need to revert them in a hotfix that michael@0: // would turn pdf.js off. michael@0: prefs.setIntPref(PREF_PREVIOUS_ACTION, handlerInfo.preferredAction); michael@0: prefs.setBoolPref(PREF_PREVIOUS_ASK, handlerInfo.alwaysAskBeforeHandling); michael@0: } michael@0: michael@0: let handlerService = Cc['@mozilla.org/uriloader/handler-service;1']. michael@0: getService(Ci.nsIHandlerService); michael@0: michael@0: // Change and save mime handler settings. michael@0: handlerInfo.alwaysAskBeforeHandling = false; michael@0: handlerInfo.preferredAction = Ci.nsIHandlerInfo.handleInternally; michael@0: handlerService.store(handlerInfo); michael@0: michael@0: // Also disable any plugins for pdfs. michael@0: var stringTypes = ''; michael@0: var types = []; michael@0: if (prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) { michael@0: stringTypes = prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES); michael@0: } michael@0: if (stringTypes !== '') { michael@0: types = stringTypes.split(','); michael@0: } michael@0: michael@0: if (types.indexOf(PDF_CONTENT_TYPE) === -1) { michael@0: types.push(PDF_CONTENT_TYPE); michael@0: } michael@0: prefs.setCharPref(PREF_DISABLED_PLUGIN_TYPES, types.join(',')); michael@0: michael@0: // Update the category manager in case the plugins are already loaded. michael@0: let categoryManager = Cc["@mozilla.org/categorymanager;1"]; michael@0: categoryManager.getService(Ci.nsICategoryManager). michael@0: deleteCategoryEntry("Gecko-Content-Viewers", michael@0: PDF_CONTENT_TYPE, michael@0: false); michael@0: }, michael@0: michael@0: // nsIObserver michael@0: observe: function observe(aSubject, aTopic, aData) { michael@0: if (this.enabled) michael@0: this._ensureRegistered(); michael@0: else michael@0: this._ensureUnregistered(); michael@0: }, michael@0: michael@0: /** michael@0: * pdf.js is only enabled if it is both selected as the pdf viewer and if the michael@0: * global switch enabling it is true. michael@0: * @return {boolean} Wether or not it's enabled. michael@0: */ michael@0: get enabled() { michael@0: var disabled = getBoolPref(PREF_DISABLED, true); michael@0: if (disabled) { michael@0: return false; michael@0: } michael@0: michael@0: // the 'application/pdf' handler is selected as internal? michael@0: var handlerInfo = Svc.mime michael@0: .getFromTypeAndExtension(PDF_CONTENT_TYPE, 'pdf'); michael@0: if (handlerInfo.alwaysAskBeforeHandling || michael@0: handlerInfo.preferredAction !== Ci.nsIHandlerInfo.handleInternally) { michael@0: return false; michael@0: } michael@0: michael@0: // Check if we have disabled plugin handling of 'application/pdf' in prefs michael@0: if (Services.prefs.prefHasUserValue(PREF_DISABLED_PLUGIN_TYPES)) { michael@0: let disabledPluginTypes = michael@0: Services.prefs.getCharPref(PREF_DISABLED_PLUGIN_TYPES).split(','); michael@0: if (disabledPluginTypes.indexOf(PDF_CONTENT_TYPE) >= 0) { michael@0: return true; michael@0: } michael@0: } michael@0: michael@0: // Check if there is an enabled pdf plugin. michael@0: // Note: this check is performed last because getPluginTags() triggers costly michael@0: // plugin list initialization (bug 881575) michael@0: let tags = Cc["@mozilla.org/plugin/host;1"]. michael@0: getService(Ci.nsIPluginHost). michael@0: getPluginTags(); michael@0: let enabledPluginFound = tags.some(function(tag) { michael@0: if (tag.disabled) { michael@0: return false; michael@0: } michael@0: let mimeTypes = tag.getMimeTypes(); michael@0: return mimeTypes.some(function(mimeType) { michael@0: return mimeType === PDF_CONTENT_TYPE; michael@0: }); michael@0: }); michael@0: michael@0: // Use pdf.js if pdf plugin is not present or disabled michael@0: return !enabledPluginFound; michael@0: }, michael@0: michael@0: _ensureRegistered: function _ensureRegistered() { michael@0: if (this._registered) michael@0: return; michael@0: michael@0: this._pdfStreamConverterFactory = new Factory(); michael@0: Cu.import('resource://pdf.js/PdfStreamConverter.jsm'); michael@0: this._pdfStreamConverterFactory.register(PdfStreamConverter); michael@0: michael@0: this._pdfRedirectorFactory = new Factory(); michael@0: Cu.import('resource://pdf.js/PdfRedirector.jsm'); michael@0: this._pdfRedirectorFactory.register(PdfRedirector); michael@0: michael@0: Svc.pluginHost.registerPlayPreviewMimeType(PDF_CONTENT_TYPE, true, michael@0: 'data:application/x-moz-playpreview-pdfjs;,'); michael@0: michael@0: this._registered = true; michael@0: }, michael@0: michael@0: _ensureUnregistered: function _ensureUnregistered() { michael@0: if (!this._registered) michael@0: return; michael@0: michael@0: this._pdfStreamConverterFactory.unregister(); michael@0: Cu.unload('resource://pdf.js/PdfStreamConverter.jsm'); michael@0: delete this._pdfStreamConverterFactory; michael@0: michael@0: this._pdfRedirectorFactory.unregister(); michael@0: Cu.unload('resource://pdf.js/PdfRedirector.jsm'); michael@0: delete this._pdfRedirectorFactory; michael@0: michael@0: Svc.pluginHost.unregisterPlayPreviewMimeType(PDF_CONTENT_TYPE); michael@0: michael@0: this._registered = false; michael@0: } michael@0: }; michael@0: