diff options
Diffstat (limited to 'toolkit/mozapps/webextensions/internal/PluginProvider.jsm')
-rw-r--r-- | toolkit/mozapps/webextensions/internal/PluginProvider.jsm | 600 |
1 files changed, 0 insertions, 600 deletions
diff --git a/toolkit/mozapps/webextensions/internal/PluginProvider.jsm b/toolkit/mozapps/webextensions/internal/PluginProvider.jsm deleted file mode 100644 index 075159a9a..000000000 --- a/toolkit/mozapps/webextensions/internal/PluginProvider.jsm +++ /dev/null @@ -1,600 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -"use strict"; - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cu = Components.utils; - -this.EXPORTED_SYMBOLS = []; - -Cu.import("resource://gre/modules/AddonManager.jsm"); -/* globals AddonManagerPrivate*/ -Cu.import("resource://gre/modules/Services.jsm"); - -const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties"; -const STRING_TYPE_NAME = "type.%ID%.name"; -const LIST_UPDATED_TOPIC = "plugins-list-updated"; -const FLASH_MIME_TYPE = "application/x-shockwave-flash"; - -Cu.import("resource://gre/modules/Log.jsm"); -const LOGGER_ID = "addons.plugins"; - -// Create a new logger for use by the Addons Plugin Provider -// (Requires AddonManager.jsm) -var logger = Log.repository.getLogger(LOGGER_ID); - -function getIDHashForString(aStr) { - // return the two-digit hexadecimal code for a byte - let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2); - - let hasher = Cc["@mozilla.org/security/hash;1"]. - createInstance(Ci.nsICryptoHash); - hasher.init(Ci.nsICryptoHash.MD5); - let stringStream = Cc["@mozilla.org/io/string-input-stream;1"]. - createInstance(Ci.nsIStringInputStream); - stringStream.data = aStr ? aStr : "null"; - hasher.updateFromStream(stringStream, -1); - - // convert the binary hash data to a hex string. - let binary = hasher.finish(false); - let hash = Array.from(binary, c => toHexString(c.charCodeAt(0))); - hash = hash.join("").toLowerCase(); - return "{" + hash.substr(0, 8) + "-" + - hash.substr(8, 4) + "-" + - hash.substr(12, 4) + "-" + - hash.substr(16, 4) + "-" + - hash.substr(20) + "}"; -} - -var PluginProvider = { - get name() { - return "PluginProvider"; - }, - - // A dictionary mapping IDs to names and descriptions - plugins: null, - - startup: function() { - Services.obs.addObserver(this, LIST_UPDATED_TOPIC, false); - Services.obs.addObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, false); - }, - - /** - * Called when the application is shutting down. Only necessary for tests - * to be able to simulate a shutdown. - */ - shutdown: function() { - this.plugins = null; - Services.obs.removeObserver(this, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED); - Services.obs.removeObserver(this, LIST_UPDATED_TOPIC); - }, - - observe: function(aSubject, aTopic, aData) { - switch (aTopic) { - case AddonManager.OPTIONS_NOTIFICATION_DISPLAYED: - this.getAddonByID(aData, function(plugin) { - if (!plugin) - return; - - let libLabel = aSubject.getElementById("pluginLibraries"); - libLabel.textContent = plugin.pluginLibraries.join(", "); - - let typeLabel = aSubject.getElementById("pluginMimeTypes"), types = []; - for (let type of plugin.pluginMimeTypes) { - let extras = [type.description.trim(), type.suffixes]. - filter(x => x).join(": "); - types.push(type.type + (extras ? " (" + extras + ")" : "")); - } - typeLabel.textContent = types.join(",\n"); - let showProtectedModePref = canDisableFlashProtectedMode(plugin); - aSubject.getElementById("pluginEnableProtectedMode") - .setAttribute("collapsed", showProtectedModePref ? "" : "true"); - }); - break; - case LIST_UPDATED_TOPIC: - if (this.plugins) - this.updatePluginList(); - break; - } - }, - - /** - * Creates a PluginWrapper for a plugin object. - */ - buildWrapper: function(aPlugin) { - return new PluginWrapper(aPlugin.id, - aPlugin.name, - aPlugin.description, - aPlugin.tags); - }, - - /** - * Called to get an Addon with a particular ID. - * - * @param aId - * The ID of the add-on to retrieve - * @param aCallback - * A callback to pass the Addon to - */ - getAddonByID: function(aId, aCallback) { - if (!this.plugins) - this.buildPluginList(); - - if (aId in this.plugins) - aCallback(this.buildWrapper(this.plugins[aId])); - else - aCallback(null); - }, - - /** - * Called to get Addons of a particular type. - * - * @param aTypes - * An array of types to fetch. Can be null to get all types. - * @param callback - * A callback to pass an array of Addons to - */ - getAddonsByTypes: function(aTypes, aCallback) { - if (aTypes && aTypes.indexOf("plugin") < 0) { - aCallback([]); - return; - } - - if (!this.plugins) - this.buildPluginList(); - - let results = []; - - for (let id in this.plugins) - this.getAddonByID(id, (addon) => results.push(addon)); - - aCallback(results); - }, - - /** - * Called to get Addons that have pending operations. - * - * @param aTypes - * An array of types to fetch. Can be null to get all types - * @param aCallback - * A callback to pass an array of Addons to - */ - getAddonsWithOperationsByTypes: function(aTypes, aCallback) { - aCallback([]); - }, - - /** - * Called to get the current AddonInstalls, optionally restricting by type. - * - * @param aTypes - * An array of types or null to get all types - * @param aCallback - * A callback to pass the array of AddonInstalls to - */ - getInstallsByTypes: function(aTypes, aCallback) { - aCallback([]); - }, - - /** - * Builds a list of the current plugins reported by the plugin host - * - * @return a dictionary of plugins indexed by our generated ID - */ - getPluginList: function() { - let tags = Cc["@mozilla.org/plugin/host;1"]. - getService(Ci.nsIPluginHost). - getPluginTags({}); - - let list = {}; - let seenPlugins = {}; - for (let tag of tags) { - if (!(tag.name in seenPlugins)) - seenPlugins[tag.name] = {}; - if (!(tag.description in seenPlugins[tag.name])) { - let plugin = { - id: getIDHashForString(tag.name + tag.description), - name: tag.name, - description: tag.description, - tags: [tag] - }; - - seenPlugins[tag.name][tag.description] = plugin; - list[plugin.id] = plugin; - } - else { - seenPlugins[tag.name][tag.description].tags.push(tag); - } - } - - return list; - }, - - /** - * Builds the list of known plugins from the plugin host - */ - buildPluginList: function() { - this.plugins = this.getPluginList(); - }, - - /** - * Updates the plugins from the plugin host by comparing the current plugins - * to the last known list sending out any necessary API notifications for - * changes. - */ - updatePluginList: function() { - let newList = this.getPluginList(); - - let lostPlugins = Object.keys(this.plugins).filter(id => !(id in newList)). - map(id => this.buildWrapper(this.plugins[id])); - let newPlugins = Object.keys(newList).filter(id => !(id in this.plugins)). - map(id => this.buildWrapper(newList[id])); - let matchedIDs = Object.keys(newList).filter(id => id in this.plugins); - - // The plugin host generates new tags for every plugin after a scan and - // if the plugin's filename has changed then the disabled state won't have - // been carried across, send out notifications for anything that has - // changed (see bug 830267). - let changedWrappers = []; - for (let id of matchedIDs) { - let oldWrapper = this.buildWrapper(this.plugins[id]); - let newWrapper = this.buildWrapper(newList[id]); - - if (newWrapper.isActive != oldWrapper.isActive) { - AddonManagerPrivate.callAddonListeners(newWrapper.isActive ? - "onEnabling" : "onDisabling", - newWrapper, false); - changedWrappers.push(newWrapper); - } - } - - // Notify about new installs - for (let plugin of newPlugins) { - AddonManagerPrivate.callInstallListeners("onExternalInstall", null, - plugin, null, false); - AddonManagerPrivate.callAddonListeners("onInstalling", plugin, false); - } - - // Notify for any plugins that have vanished. - for (let plugin of lostPlugins) - AddonManagerPrivate.callAddonListeners("onUninstalling", plugin, false); - - this.plugins = newList; - - // Signal that new installs are complete - for (let plugin of newPlugins) - AddonManagerPrivate.callAddonListeners("onInstalled", plugin); - - // Signal that enables/disables are complete - for (let wrapper of changedWrappers) { - AddonManagerPrivate.callAddonListeners(wrapper.isActive ? - "onEnabled" : "onDisabled", - wrapper); - } - - // Signal that uninstalls are complete - for (let plugin of lostPlugins) - AddonManagerPrivate.callAddonListeners("onUninstalled", plugin); - } -}; - -function isFlashPlugin(aPlugin) { - for (let type of aPlugin.pluginMimeTypes) { - if (type.type == FLASH_MIME_TYPE) { - return true; - } - } - return false; -} -// Protected mode is win32-only, not win64 -function canDisableFlashProtectedMode(aPlugin) { - return isFlashPlugin(aPlugin) && Services.appinfo.XPCOMABI == "x86-msvc"; -} - -const wrapperMap = new WeakMap(); -let pluginFor = wrapper => wrapperMap.get(wrapper); - -/** - * The PluginWrapper wraps a set of nsIPluginTags to provide the data visible to - * public callers through the API. - */ -function PluginWrapper(id, name, description, tags) { - wrapperMap.set(this, { id, name, description, tags }); -} - -PluginWrapper.prototype = { - get id() { - return pluginFor(this).id; - }, - - get type() { - return "plugin"; - }, - - get name() { - return pluginFor(this).name; - }, - - get creator() { - return null; - }, - - get description() { - return pluginFor(this).description.replace(/<\/?[a-z][^>]*>/gi, " "); - }, - - get version() { - let { tags: [tag] } = pluginFor(this); - return tag.version; - }, - - get homepageURL() { - let { description } = pluginFor(this); - if (/<A\s+HREF=[^>]*>/i.test(description)) - return /<A\s+HREF=["']?([^>"'\s]*)/i.exec(description)[1]; - return null; - }, - - get isActive() { - let { tags: [tag] } = pluginFor(this); - return !tag.blocklisted && !tag.disabled; - }, - - get appDisabled() { - let { tags: [tag] } = pluginFor(this); - return tag.blocklisted; - }, - - get userDisabled() { - let { tags: [tag] } = pluginFor(this); - if (tag.disabled) - return true; - - if ((Services.prefs.getBoolPref("plugins.click_to_play") && tag.clicktoplay) || - this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE || - this.blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) - return AddonManager.STATE_ASK_TO_ACTIVATE; - - return false; - }, - - set userDisabled(val) { - let previousVal = this.userDisabled; - if (val === previousVal) - return val; - - let { tags } = pluginFor(this); - - for (let tag of tags) { - if (val === true) - tag.enabledState = Ci.nsIPluginTag.STATE_DISABLED; - else if (val === false) - tag.enabledState = Ci.nsIPluginTag.STATE_ENABLED; - else if (val == AddonManager.STATE_ASK_TO_ACTIVATE) - tag.enabledState = Ci.nsIPluginTag.STATE_CLICKTOPLAY; - } - - // If 'userDisabled' was 'true' and we're going to a state that's not - // that, we're enabling, so call those listeners. - if (previousVal === true && val !== true) { - AddonManagerPrivate.callAddonListeners("onEnabling", this, false); - AddonManagerPrivate.callAddonListeners("onEnabled", this); - } - - // If 'userDisabled' was not 'true' and we're going to a state where - // it is, we're disabling, so call those listeners. - if (previousVal !== true && val === true) { - AddonManagerPrivate.callAddonListeners("onDisabling", this, false); - AddonManagerPrivate.callAddonListeners("onDisabled", this); - } - - // If the 'userDisabled' value involved AddonManager.STATE_ASK_TO_ACTIVATE, - // call the onPropertyChanged listeners. - if (previousVal == AddonManager.STATE_ASK_TO_ACTIVATE || - val == AddonManager.STATE_ASK_TO_ACTIVATE) { - AddonManagerPrivate.callAddonListeners("onPropertyChanged", this, ["userDisabled"]); - } - - return val; - }, - - get blocklistState() { - let { tags: [tag] } = pluginFor(this); - let bs = Cc["@mozilla.org/extensions/blocklist;1"]. - getService(Ci.nsIBlocklistService); - return bs.getPluginBlocklistState(tag); - }, - - get blocklistURL() { - let { tags: [tag] } = pluginFor(this); - let bs = Cc["@mozilla.org/extensions/blocklist;1"]. - getService(Ci.nsIBlocklistService); - return bs.getPluginBlocklistURL(tag); - }, - - get size() { - function getDirectorySize(aFile) { - let size = 0; - let entries = aFile.directoryEntries.QueryInterface(Ci.nsIDirectoryEnumerator); - let entry; - while ((entry = entries.nextFile)) { - if (entry.isSymlink() || !entry.isDirectory()) - size += entry.fileSize; - else - size += getDirectorySize(entry); - } - entries.close(); - return size; - } - - let size = 0; - let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile); - for (let tag of pluginFor(this).tags) { - file.initWithPath(tag.fullpath); - if (file.isDirectory()) - size += getDirectorySize(file); - else - size += file.fileSize; - } - return size; - }, - - get pluginLibraries() { - let libs = []; - for (let tag of pluginFor(this).tags) - libs.push(tag.filename); - return libs; - }, - - get pluginFullpath() { - let paths = []; - for (let tag of pluginFor(this).tags) - paths.push(tag.fullpath); - return paths; - }, - - get pluginMimeTypes() { - let types = []; - for (let tag of pluginFor(this).tags) { - let mimeTypes = tag.getMimeTypes({}); - let mimeDescriptions = tag.getMimeDescriptions({}); - let extensions = tag.getExtensions({}); - for (let i = 0; i < mimeTypes.length; i++) { - let type = {}; - type.type = mimeTypes[i]; - type.description = mimeDescriptions[i]; - type.suffixes = extensions[i]; - - types.push(type); - } - } - return types; - }, - - get installDate() { - let date = 0; - for (let tag of pluginFor(this).tags) { - date = Math.max(date, tag.lastModifiedTime); - } - return new Date(date); - }, - - get scope() { - let { tags: [tag] } = pluginFor(this); - let path = tag.fullpath; - // Plugins inside the application directory are in the application scope - let dir = Services.dirsvc.get("APlugns", Ci.nsIFile); - if (path.startsWith(dir.path)) - return AddonManager.SCOPE_APPLICATION; - - // Plugins inside the profile directory are in the profile scope - dir = Services.dirsvc.get("ProfD", Ci.nsIFile); - if (path.startsWith(dir.path)) - return AddonManager.SCOPE_PROFILE; - - // Plugins anywhere else in the user's home are in the user scope, - // but not all platforms have a home directory. - try { - dir = Services.dirsvc.get("Home", Ci.nsIFile); - if (path.startsWith(dir.path)) - return AddonManager.SCOPE_USER; - } catch (e) { - if (!e.result || e.result != Components.results.NS_ERROR_FAILURE) - throw e; - // Do nothing: missing "Home". - } - - // Any other locations are system scope - return AddonManager.SCOPE_SYSTEM; - }, - - get pendingOperations() { - return AddonManager.PENDING_NONE; - }, - - get operationsRequiringRestart() { - return AddonManager.OP_NEEDS_RESTART_NONE; - }, - - get permissions() { - let { tags: [tag] } = pluginFor(this); - let permissions = 0; - if (tag.isEnabledStateLocked) { - return permissions; - } - if (!this.appDisabled) { - - if (this.userDisabled !== true) - permissions |= AddonManager.PERM_CAN_DISABLE; - - let blocklistState = this.blocklistState; - let isCTPBlocklisted = - (blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE || - blocklistState == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE); - - if (this.userDisabled !== AddonManager.STATE_ASK_TO_ACTIVATE && - (Services.prefs.getBoolPref("plugins.click_to_play") || - isCTPBlocklisted)) { - permissions |= AddonManager.PERM_CAN_ASK_TO_ACTIVATE; - } - - if (this.userDisabled !== false && !isCTPBlocklisted) { - permissions |= AddonManager.PERM_CAN_ENABLE; - } - } - return permissions; - }, - - get optionsType() { - if (canDisableFlashProtectedMode(this)) { - return AddonManager.OPTIONS_TYPE_INLINE; - } - return AddonManager.OPTIONS_TYPE_INLINE_INFO; - }, - - get optionsURL() { - return "chrome://mozapps/content/extensions/pluginPrefs.xul"; - }, - - get updateDate() { - return this.installDate; - }, - - get isCompatible() { - return true; - }, - - get isPlatformCompatible() { - return true; - }, - - get providesUpdatesSecurely() { - return true; - }, - - get foreignInstall() { - return true; - }, - - isCompatibleWith: function(aAppVersion, aPlatformVersion) { - return true; - }, - - findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) { - if ("onNoCompatibilityUpdateAvailable" in aListener) - aListener.onNoCompatibilityUpdateAvailable(this); - if ("onNoUpdateAvailable" in aListener) - aListener.onNoUpdateAvailable(this); - if ("onUpdateFinished" in aListener) - aListener.onUpdateFinished(this); - } -}; - -AddonManagerPrivate.registerProvider(PluginProvider, [ - new AddonManagerPrivate.AddonType("plugin", URI_EXTENSION_STRINGS, - STRING_TYPE_NAME, - AddonManager.VIEW_TYPE_LIST, 6000, - AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) -]); |