diff options
Diffstat (limited to 'browser/modules/SocialService.jsm')
-rw-r--r-- | browser/modules/SocialService.jsm | 1097 |
1 files changed, 0 insertions, 1097 deletions
diff --git a/browser/modules/SocialService.jsm b/browser/modules/SocialService.jsm deleted file mode 100644 index 95f5e0259..000000000 --- a/browser/modules/SocialService.jsm +++ /dev/null @@ -1,1097 +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/. */ - -this.EXPORTED_SYMBOLS = ["SocialService"]; - -const { classes: Cc, interfaces: Ci, utils: Cu } = Components; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/AddonManager.jsm"); -Cu.import("resource://gre/modules/PlacesUtils.jsm"); - -const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties"; -const ADDON_TYPE_SERVICE = "service"; -const ID_SUFFIX = "@services.mozilla.org"; -const STRING_TYPE_NAME = "type.%ID%.name"; - -XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm"); - -XPCOMUtils.defineLazyServiceGetter(this, "etld", - "@mozilla.org/network/effective-tld-service;1", - "nsIEffectiveTLDService"); - -/** - * The SocialService is the public API to social providers - it tracks which - * providers are installed and enabled, and is the entry-point for access to - * the provider itself. - */ - -// Internal helper methods and state -var SocialServiceInternal = { - get enabled() { - return this.providerArray.length > 0; - }, - - get providerArray() { - return Object.keys(this.providers).map(origin => this.providers[origin]); - }, - *manifestsGenerator() { - // Retrieve the manifests of installed providers from prefs - let MANIFEST_PREFS = Services.prefs.getBranch("social.manifest."); - let prefs = MANIFEST_PREFS.getChildList("", []); - for (let pref of prefs) { - // we only consider manifests in user level prefs to be *installed* - if (!MANIFEST_PREFS.prefHasUserValue(pref)) - continue; - try { - var manifest = JSON.parse(MANIFEST_PREFS.getComplexValue(pref, Ci.nsISupportsString).data); - if (manifest && typeof(manifest) == "object" && manifest.origin) - yield manifest; - } catch (err) { - Cu.reportError("SocialService: failed to load manifest: " + pref + - ", exception: " + err); - } - } - }, - get manifests() { - return this.manifestsGenerator(); - }, - getManifestPrefname: function(origin) { - // Retrieve the prefname for a given origin/manifest. - // If no existing pref, return a generated prefname. - let MANIFEST_PREFS = Services.prefs.getBranch("social.manifest."); - let prefs = MANIFEST_PREFS.getChildList("", []); - for (let pref of prefs) { - try { - var manifest = JSON.parse(MANIFEST_PREFS.getComplexValue(pref, Ci.nsISupportsString).data); - if (manifest.origin == origin) { - return pref; - } - } catch (err) { - Cu.reportError("SocialService: failed to load manifest: " + pref + - ", exception: " + err); - } - } - let originUri = Services.io.newURI(origin, null, null); - return originUri.hostPort.replace('.', '-'); - }, - orderedProviders: function(aCallback) { - if (SocialServiceInternal.providerArray.length < 2) { - schedule(function () { - aCallback(SocialServiceInternal.providerArray); - }); - return; - } - // query moz_hosts for frecency. since some providers may not have a - // frecency entry, we need to later sort on our own. We use the providers - // object below as an easy way to later record the frecency on the provider - // object from the query results. - let hosts = []; - let providers = {}; - - for (let p of SocialServiceInternal.providerArray) { - p.frecency = 0; - providers[p.domain] = p; - hosts.push(p.domain); - } - - // cannot bind an array to stmt.params so we have to build the string - let stmt = PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase) - .DBConnection.createAsyncStatement( - "SELECT host, frecency FROM moz_hosts WHERE host IN (" + - hosts.map(host => '"' + host + '"').join(",") + ") " - ); - - try { - stmt.executeAsync({ - handleResult: function(aResultSet) { - let row; - while ((row = aResultSet.getNextRow())) { - let rh = row.getResultByName("host"); - let frecency = row.getResultByName("frecency"); - providers[rh].frecency = parseInt(frecency) || 0; - } - }, - handleError: function(aError) { - Cu.reportError(aError.message + " (Result = " + aError.result + ")"); - }, - handleCompletion: function(aReason) { - // the query may not have returned all our providers, so we have - // stamped the frecency on the provider and sort here. This makes sure - // all enabled providers get sorted even with frecency zero. - let providerList = SocialServiceInternal.providerArray; - // reverse sort - aCallback(providerList.sort((a, b) => b.frecency - a.frecency)); - } - }); - } finally { - stmt.finalize(); - } - } -}; - -XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () { - initService(); - let providers = {}; - for (let manifest of this.manifests) { - try { - if (ActiveProviders.has(manifest.origin)) { - // enable the api when a provider is enabled - let provider = new SocialProvider(manifest); - providers[provider.origin] = provider; - } - } catch (err) { - Cu.reportError("SocialService: failed to load provider: " + manifest.origin + - ", exception: " + err); - } - } - return providers; -}); - -function getOriginActivationType(origin) { - // if this is an about uri, treat it as a directory - let URI = Services.io.newURI(origin, null, null); - let principal = Services.scriptSecurityManager.createCodebasePrincipal(URI, {}); - if (Services.scriptSecurityManager.isSystemPrincipal(principal) || origin == "moz-safe-about:home") { - return "internal"; - } - - let directories = Services.prefs.getCharPref("social.directories").split(','); - if (directories.indexOf(origin) >= 0) - return "directory"; - - return "foreign"; -} - -var ActiveProviders = { - get _providers() { - delete this._providers; - this._providers = {}; - try { - let pref = Services.prefs.getComplexValue("social.activeProviders", - Ci.nsISupportsString); - this._providers = JSON.parse(pref); - } catch (ex) {} - return this._providers; - }, - - has: function (origin) { - return (origin in this._providers); - }, - - add: function (origin) { - this._providers[origin] = 1; - this._deferredTask.arm(); - }, - - delete: function (origin) { - delete this._providers[origin]; - this._deferredTask.arm(); - }, - - flush: function () { - this._deferredTask.disarm(); - this._persist(); - }, - - get _deferredTask() { - delete this._deferredTask; - return this._deferredTask = new DeferredTask(this._persist.bind(this), 0); - }, - - _persist: function () { - let string = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - string.data = JSON.stringify(this._providers); - Services.prefs.setComplexValue("social.activeProviders", - Ci.nsISupportsString, string); - } -}; - -function migrateSettings() { - let activeProviders, enabled; - try { - activeProviders = Services.prefs.getCharPref("social.activeProviders"); - } catch (e) { - // not set, we'll check if we need to migrate older prefs - } - if (Services.prefs.prefHasUserValue("social.enabled")) { - enabled = Services.prefs.getBoolPref("social.enabled"); - } - if (activeProviders) { - // migration from fx21 to fx22 or later - // ensure any *builtin* provider in activeproviders is in user level prefs - for (let origin in ActiveProviders._providers) { - let prefname; - let manifest; - let defaultManifest; - try { - prefname = getPrefnameFromOrigin(origin); - manifest = JSON.parse(Services.prefs.getComplexValue(prefname, Ci.nsISupportsString).data); - } catch (e) { - // Our preference is missing or bad, remove from ActiveProviders and - // continue. This is primarily an error-case and should only be - // reached by either messing with preferences or hitting the one or - // two days of nightly that ran into it, so we'll flush right away. - ActiveProviders.delete(origin); - ActiveProviders.flush(); - continue; - } - let needsUpdate = !manifest.updateDate; - // fx23 may have built-ins with shareURL - try { - defaultManifest = Services.prefs.getDefaultBranch(null) - .getComplexValue(prefname, Ci.nsISupportsString).data; - defaultManifest = JSON.parse(defaultManifest); - } catch (e) { - // not a built-in, continue - } - if (defaultManifest) { - if (defaultManifest.shareURL && !manifest.shareURL) { - manifest.shareURL = defaultManifest.shareURL; - needsUpdate = true; - } - if (defaultManifest.version && (!manifest.version || defaultManifest.version > manifest.version)) { - manifest = defaultManifest; - needsUpdate = true; - } - } - if (needsUpdate) { - // the provider was installed with an older build, so we will update the - // timestamp and ensure the manifest is in user prefs - delete manifest.builtin; - // we're potentially updating for share, so always mark the updateDate - manifest.updateDate = Date.now(); - if (!manifest.installDate) - manifest.installDate = 0; // we don't know when it was installed - - let string = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - string.data = JSON.stringify(manifest); - Services.prefs.setComplexValue(prefname, Ci.nsISupportsString, string); - } - // as of fx 29, we no longer rely on social.enabled. migration from prior - // versions should disable all service addons if social.enabled=false - if (enabled === false) { - ActiveProviders.delete(origin); - } - } - ActiveProviders.flush(); - Services.prefs.clearUserPref("social.enabled"); - return; - } - - // primary migration from pre-fx21 - let active; - try { - active = Services.prefs.getBoolPref("social.active"); - } catch (e) {} - if (!active) - return; - - // primary difference from SocialServiceInternal.manifests is that we - // only read the default branch here. - let manifestPrefs = Services.prefs.getDefaultBranch("social.manifest."); - let prefs = manifestPrefs.getChildList("", []); - for (let pref of prefs) { - try { - let manifest; - try { - manifest = JSON.parse(manifestPrefs.getComplexValue(pref, Ci.nsISupportsString).data); - } catch (e) { - // bad or missing preference, we wont update this one. - continue; - } - if (manifest && typeof(manifest) == "object" && manifest.origin) { - // our default manifests have been updated with the builtin flags as of - // fx22, delete it so we can set the user-pref - delete manifest.builtin; - if (!manifest.updateDate) { - manifest.updateDate = Date.now(); - manifest.installDate = 0; // we don't know when it was installed - } - - let string = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString); - string.data = JSON.stringify(manifest); - // pref here is just the branch name, set the full pref name - Services.prefs.setComplexValue("social.manifest." + pref, Ci.nsISupportsString, string); - ActiveProviders.add(manifest.origin); - ActiveProviders.flush(); - // social.active was used at a time that there was only one - // builtin, we'll assume that is still the case - return; - } - } catch (err) { - Cu.reportError("SocialService: failed to load manifest: " + pref + ", exception: " + err); - } - } -} - -function initService() { - Services.obs.addObserver(function xpcomShutdown() { - ActiveProviders.flush(); - SocialService._providerListeners = null; - Services.obs.removeObserver(xpcomShutdown, "xpcom-shutdown"); - }, "xpcom-shutdown", false); - - try { - migrateSettings(); - } catch (e) { - // no matter what, if migration fails we do not want to render social - // unusable. Worst case scenario is that, when upgrading Firefox, previously - // enabled providers are not migrated. - Cu.reportError("Error migrating social settings: " + e); - } -} - -function schedule(callback) { - Services.tm.mainThread.dispatch(callback, Ci.nsIThread.DISPATCH_NORMAL); -} - -// Public API -this.SocialService = { - get hasEnabledProviders() { - // used as an optimization during startup, can be used to check if further - // initialization should be done (e.g. creating the instances of - // SocialProvider and turning on UI). ActiveProviders may have changed and - // not yet flushed so we check the active providers array - for (let p in ActiveProviders._providers) { - return true; - } - return false; - }, - get enabled() { - return SocialServiceInternal.enabled; - }, - set enabled(val) { - throw new Error("not allowed to set SocialService.enabled"); - }, - - // Enables a provider, the manifest must already exist in prefs. The provider - // may or may not have previously been added. onDone is always called - // - with null if no such provider exists, or the activated provider on - // success. - enableProvider: function enableProvider(origin, onDone) { - if (SocialServiceInternal.providers[origin]) { - schedule(function() { - onDone(SocialServiceInternal.providers[origin]); - }); - return; - } - let manifest = SocialService.getManifestByOrigin(origin); - if (manifest) { - let addon = new AddonWrapper(manifest); - AddonManagerPrivate.callAddonListeners("onEnabling", addon, false); - addon.pendingOperations |= AddonManager.PENDING_ENABLE; - this.addProvider(manifest, onDone); - addon.pendingOperations -= AddonManager.PENDING_ENABLE; - AddonManagerPrivate.callAddonListeners("onEnabled", addon); - return; - } - schedule(function() { - onDone(null); - }); - }, - - // Adds a provider given a manifest, and returns the added provider. - addProvider: function addProvider(manifest, onDone) { - if (SocialServiceInternal.providers[manifest.origin]) - throw new Error("SocialService.addProvider: provider with this origin already exists"); - - // enable the api when a provider is enabled - let provider = new SocialProvider(manifest); - SocialServiceInternal.providers[provider.origin] = provider; - ActiveProviders.add(provider.origin); - - this.getOrderedProviderList(function (providers) { - this._notifyProviderListeners("provider-enabled", provider.origin, providers); - if (onDone) - onDone(provider); - }.bind(this)); - }, - - // Removes a provider with the given origin, and notifies when the removal is - // complete. - disableProvider: function disableProvider(origin, onDone) { - if (!(origin in SocialServiceInternal.providers)) - throw new Error("SocialService.disableProvider: no provider with origin " + origin + " exists!"); - - let provider = SocialServiceInternal.providers[origin]; - let manifest = SocialService.getManifestByOrigin(origin); - let addon = manifest && new AddonWrapper(manifest); - if (addon) { - AddonManagerPrivate.callAddonListeners("onDisabling", addon, false); - addon.pendingOperations |= AddonManager.PENDING_DISABLE; - } - provider.enabled = false; - - ActiveProviders.delete(provider.origin); - - delete SocialServiceInternal.providers[origin]; - - if (addon) { - // we have to do this now so the addon manager ui will update an uninstall - // correctly. - addon.pendingOperations -= AddonManager.PENDING_DISABLE; - AddonManagerPrivate.callAddonListeners("onDisabled", addon); - } - - this.getOrderedProviderList(function (providers) { - this._notifyProviderListeners("provider-disabled", origin, providers); - if (onDone) - onDone(); - }.bind(this)); - }, - - // Returns a single provider object with the specified origin. The provider - // must be "installed" (ie, in ActiveProviders) - getProvider: function getProvider(origin, onDone) { - schedule((function () { - onDone(SocialServiceInternal.providers[origin] || null); - }).bind(this)); - }, - - // Returns an unordered array of installed providers - getProviderList: function(onDone) { - schedule(function () { - onDone(SocialServiceInternal.providerArray); - }); - }, - - getManifestByOrigin: function(origin) { - for (let manifest of SocialServiceInternal.manifests) { - if (origin == manifest.origin) { - return manifest; - } - } - return null; - }, - - // Returns an array of installed providers, sorted by frecency - getOrderedProviderList: function(onDone) { - SocialServiceInternal.orderedProviders(onDone); - }, - - getOriginActivationType: function (origin) { - return getOriginActivationType(origin); - }, - - _providerListeners: new Map(), - registerProviderListener: function registerProviderListener(listener) { - this._providerListeners.set(listener, 1); - }, - unregisterProviderListener: function unregisterProviderListener(listener) { - this._providerListeners.delete(listener); - }, - - _notifyProviderListeners: function (topic, origin, providers) { - for (let [listener, ] of this._providerListeners) { - try { - listener(topic, origin, providers); - } catch (ex) { - Components.utils.reportError("SocialService: provider listener threw an exception: " + ex); - } - } - }, - - _manifestFromData: function(type, data, installOrigin) { - let featureURLs = ['shareURL']; - let resolveURLs = featureURLs.concat(['postActivationURL']); - - if (type == 'directory' || type == 'internal') { - // directory provided manifests must have origin in manifest, use that - if (!data['origin']) { - Cu.reportError("SocialService.manifestFromData directory service provided manifest without origin."); - return null; - } - installOrigin = data.origin; - } - // force/fixup origin - let URI = Services.io.newURI(installOrigin, null, null); - let principal = Services.scriptSecurityManager.createCodebasePrincipal(URI, {}); - data.origin = principal.origin; - - // iconURL and name are required - let providerHasFeatures = featureURLs.some(url => data[url]); - if (!providerHasFeatures) { - Cu.reportError("SocialService.manifestFromData manifest missing required urls."); - return null; - } - if (!data['name'] || !data['iconURL']) { - Cu.reportError("SocialService.manifestFromData manifest missing name or iconURL."); - return null; - } - for (let url of resolveURLs) { - if (data[url]) { - try { - let resolved = Services.io.newURI(principal.URI.resolve(data[url]), null, null); - if (!(resolved.schemeIs("http") || resolved.schemeIs("https"))) { - Cu.reportError("SocialService.manifestFromData unsupported scheme '" + resolved.scheme + "' for " + principal.origin); - return null; - } - data[url] = resolved.spec; - } catch (e) { - Cu.reportError("SocialService.manifestFromData unable to resolve '" + url + "' for " + principal.origin); - return null; - } - } - } - return data; - }, - - _showInstallNotification: function(data, aAddonInstaller) { - let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties"); - let browserBundle = Services.strings.createBundle("chrome://browser/locale/browser.properties"); - - // internal/directory activations need to use the manifest origin, any other - // use the domain activation is occurring on - let url = data.url; - if (data.installType == "internal" || data.installType == "directory") { - url = data.manifest.origin; - } - let requestingURI = Services.io.newURI(url, null, null); - let productName = brandBundle.GetStringFromName("brandShortName"); - - let message = browserBundle.formatStringFromName("service.install.description", - [requestingURI.host, productName], 2); - - let action = { - label: browserBundle.GetStringFromName("service.install.ok.label"), - accessKey: browserBundle.GetStringFromName("service.install.ok.accesskey"), - callback: function() { - aAddonInstaller.install(); - }, - }; - - let options = { - learnMoreURL: Services.urlFormatter.formatURLPref("app.support.baseURL") + "social-api", - }; - let anchor = "servicesInstall-notification-icon"; - let notificationid = "servicesInstall"; - data.window.PopupNotifications.show(data.window.gBrowser.selectedBrowser, - notificationid, message, anchor, - action, [], options); - }, - - installProvider: function(data, installCallback, options={}) { - data.installType = getOriginActivationType(data.origin); - // if we get data, we MUST have a valid manifest generated from the data - let manifest = this._manifestFromData(data.installType, data.manifest, data.origin); - if (!manifest) - throw new Error("SocialService.installProvider: service configuration is invalid from " + data.url); - - let addon = new AddonWrapper(manifest); - if (addon && addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) - throw new Error("installProvider: provider with origin [" + - data.origin + "] is blocklisted"); - // manifestFromData call above will enforce correct origin. To support - // activation from about: uris, we need to be sure to use the updated - // origin on the manifest. - data.manifest = manifest; - let id = getAddonIDFromOrigin(manifest.origin); - AddonManager.getAddonByID(id, function(aAddon) { - if (aAddon && aAddon.userDisabled) { - aAddon.cancelUninstall(); - aAddon.userDisabled = false; - } - schedule(function () { - try { - this._installProvider(data, options, aManifest => { - this._notifyProviderListeners("provider-installed", aManifest.origin); - installCallback(aManifest); - }); - } catch (e) { - Cu.reportError("Activation failed: " + e); - installCallback(null); - } - }.bind(this)); - }.bind(this)); - }, - - _installProvider: function(data, options, installCallback) { - if (!data.manifest) - throw new Error("Cannot install provider without manifest data"); - - if (data.installType == "foreign" && !Services.prefs.getBoolPref("social.remote-install.enabled")) - throw new Error("Remote install of services is disabled"); - - // if installing from any website, the install must happen over https. - // "internal" are installs from about:home or similar - if (data.installType != "internal" && !Services.io.newURI(data.origin, null, null).schemeIs("https")) { - throw new Error("attempt to activate provider over unsecured channel: " + data.origin); - } - - let installer = new AddonInstaller(data.url, data.manifest, installCallback); - let bypassPanel = options.bypassInstallPanel || - (data.installType == "internal" && data.manifest.oneclick); - if (bypassPanel) - installer.install(); - else - this._showInstallNotification(data, installer); - }, - - createWrapper: function(manifest) { - return new AddonWrapper(manifest); - }, - - /** - * updateProvider is used from the worker to self-update. Since we do not - * have knowledge of the currently selected provider here, we will notify - * the front end to deal with any reload. - */ - updateProvider: function(aUpdateOrigin, aManifest) { - let installType = this.getOriginActivationType(aUpdateOrigin); - // if we get data, we MUST have a valid manifest generated from the data - let manifest = this._manifestFromData(installType, aManifest, aUpdateOrigin); - if (!manifest) - throw new Error("SocialService.installProvider: service configuration is invalid from " + aUpdateOrigin); - - // overwrite the preference - let string = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - string.data = JSON.stringify(manifest); - Services.prefs.setComplexValue(getPrefnameFromOrigin(manifest.origin), Ci.nsISupportsString, string); - - // overwrite the existing provider then notify the front end so it can - // handle any reload that might be necessary. - if (ActiveProviders.has(manifest.origin)) { - let provider = SocialServiceInternal.providers[manifest.origin]; - provider.enabled = false; - provider = new SocialProvider(manifest); - SocialServiceInternal.providers[provider.origin] = provider; - // update the cache and ui, reload provider if necessary - this.getOrderedProviderList(providers => { - this._notifyProviderListeners("provider-update", provider.origin, providers); - }); - } - - }, - - uninstallProvider: function(origin, aCallback) { - let manifest = SocialService.getManifestByOrigin(origin); - let addon = new AddonWrapper(manifest); - addon.uninstall(aCallback); - } -}; - -/** - * The SocialProvider object represents a social provider. - * - * @constructor - * @param {jsobj} object representing the manifest file describing this provider - * @param {bool} boolean indicating whether this provider is "built in" - */ -function SocialProvider(input) { - if (!input.name) - throw new Error("SocialProvider must be passed a name"); - if (!input.origin) - throw new Error("SocialProvider must be passed an origin"); - - let addon = new AddonWrapper(input); - if (addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED) - throw new Error("SocialProvider: provider with origin [" + - input.origin + "] is blocklisted"); - - this.name = input.name; - this.iconURL = input.iconURL; - this.icon32URL = input.icon32URL; - this.icon64URL = input.icon64URL; - this.shareURL = input.shareURL; - this.postActivationURL = input.postActivationURL; - this.origin = input.origin; - let originUri = Services.io.newURI(input.origin, null, null); - this.principal = Services.scriptSecurityManager.createCodebasePrincipal(originUri, {}); - this.ambientNotificationIcons = {}; - this.errorState = null; - this.frecency = 0; - - try { - this.domain = etld.getBaseDomainFromHost(originUri.host); - } catch (e) { - this.domain = originUri.host; - } -} - -SocialProvider.prototype = { - reload: function() { - // calling terminate/activate does not set the enabled state whereas setting - // enabled will call terminate/activate - this.enabled = false; - this.enabled = true; - Services.obs.notifyObservers(null, "social:provider-reload", this.origin); - }, - - // Provider enabled/disabled state. - _enabled: false, - get enabled() { - return this._enabled; - }, - set enabled(val) { - let enable = !!val; - if (enable == this._enabled) - return; - - this._enabled = enable; - - if (enable) { - this._activate(); - } else { - this._terminate(); - } - }, - - get manifest() { - return SocialService.getManifestByOrigin(this.origin); - }, - - getPageSize: function(name) { - let manifest = this.manifest; - if (manifest && manifest.pageSize) - return manifest.pageSize[name]; - return undefined; - }, - - // Internal helper methods - _activate: function _activate() { - }, - - _terminate: function _terminate() { - this.errorState = null; - }, - - /** - * Checks if a given URI is of the same origin as the provider. - * - * Returns true or false. - * - * @param {URI or string} uri - */ - isSameOrigin: function isSameOrigin(uri, allowIfInheritsPrincipal) { - if (!uri) - return false; - if (typeof uri == "string") { - try { - uri = Services.io.newURI(uri, null, null); - } catch (ex) { - // an invalid URL can't be loaded! - return false; - } - } - try { - this.principal.checkMayLoad( - uri, // the thing to check. - false, // reportError - we do our own reporting when necessary. - allowIfInheritsPrincipal - ); - return true; - } catch (ex) { - return false; - } - }, - - /** - * Resolve partial URLs for a provider. - * - * Returns nsIURI object or null on failure - * - * @param {string} url - */ - resolveUri: function resolveUri(url) { - try { - let fullURL = this.principal.URI.resolve(url); - return Services.io.newURI(fullURL, null, null); - } catch (ex) { - Cu.reportError("mozSocial: failed to resolve window URL: " + url + "; " + ex); - return null; - } - } -}; - -function getAddonIDFromOrigin(origin) { - let originUri = Services.io.newURI(origin, null, null); - return originUri.host + ID_SUFFIX; -} - -function getPrefnameFromOrigin(origin) { - return "social.manifest." + SocialServiceInternal.getManifestPrefname(origin); -} - -function AddonInstaller(sourceURI, aManifest, installCallback) { - aManifest.updateDate = Date.now(); - // get the existing manifest for installDate - let manifest = SocialService.getManifestByOrigin(aManifest.origin); - let isNewInstall = !manifest; - if (manifest && manifest.installDate) - aManifest.installDate = manifest.installDate; - else - aManifest.installDate = aManifest.updateDate; - - this.sourceURI = sourceURI; - this.install = function() { - let addon = this.addon; - if (isNewInstall) { - AddonManagerPrivate.callInstallListeners("onExternalInstall", null, addon, null, false); - AddonManagerPrivate.callAddonListeners("onInstalling", addon, false); - } - - let string = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - string.data = JSON.stringify(aManifest); - Services.prefs.setComplexValue(getPrefnameFromOrigin(aManifest.origin), Ci.nsISupportsString, string); - - if (isNewInstall) { - AddonManagerPrivate.callAddonListeners("onInstalled", addon); - } - installCallback(aManifest); - }; - this.cancel = function() { - Services.prefs.clearUserPref(getPrefnameFromOrigin(aManifest.origin)); - }; - this.addon = new AddonWrapper(aManifest); -} - -var SocialAddonProvider = { - startup: function() {}, - - shutdown: function() {}, - - updateAddonAppDisabledStates: function() { - // we wont bother with "enabling" services that are released from blocklist - for (let manifest of SocialServiceInternal.manifests) { - try { - if (ActiveProviders.has(manifest.origin)) { - let addon = new AddonWrapper(manifest); - if (addon.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) { - SocialService.disableProvider(manifest.origin); - } - } - } catch (e) { - Cu.reportError(e); - } - } - }, - - getAddonByID: function(aId, aCallback) { - for (let manifest of SocialServiceInternal.manifests) { - if (aId == getAddonIDFromOrigin(manifest.origin)) { - aCallback(new AddonWrapper(manifest)); - return; - } - } - aCallback(null); - }, - - getAddonsByTypes: function(aTypes, aCallback) { - if (aTypes && aTypes.indexOf(ADDON_TYPE_SERVICE) == -1) { - aCallback([]); - return; - } - aCallback([...SocialServiceInternal.manifests].map(a => new AddonWrapper(a))); - }, - - removeAddon: function(aAddon, aCallback) { - AddonManagerPrivate.callAddonListeners("onUninstalling", aAddon, false); - aAddon.pendingOperations |= AddonManager.PENDING_UNINSTALL; - Services.prefs.clearUserPref(getPrefnameFromOrigin(aAddon.manifest.origin)); - aAddon.pendingOperations -= AddonManager.PENDING_UNINSTALL; - AddonManagerPrivate.callAddonListeners("onUninstalled", aAddon); - SocialService._notifyProviderListeners("provider-uninstalled", aAddon.manifest.origin); - if (aCallback) - schedule(aCallback); - } -}; - - -function AddonWrapper(aManifest) { - this.manifest = aManifest; - this.id = getAddonIDFromOrigin(this.manifest.origin); - this._pending = AddonManager.PENDING_NONE; -} -AddonWrapper.prototype = { - get type() { - return ADDON_TYPE_SERVICE; - }, - - get appDisabled() { - return this.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED; - }, - - set softDisabled(val) { - this.userDisabled = val; - }, - - get softDisabled() { - return this.userDisabled; - }, - - get isCompatible() { - return true; - }, - - get isPlatformCompatible() { - return true; - }, - - get scope() { - return AddonManager.SCOPE_PROFILE; - }, - - get foreignInstall() { - return false; - }, - - isCompatibleWith: function(appVersion, platformVersion) { - return true; - }, - - get providesUpdatesSecurely() { - return true; - }, - - get blocklistState() { - return Services.blocklist.getAddonBlocklistState(this); - }, - - get blocklistURL() { - return Services.blocklist.getAddonBlocklistURL(this); - }, - - get screenshots() { - return []; - }, - - get pendingOperations() { - return this._pending || AddonManager.PENDING_NONE; - }, - set pendingOperations(val) { - this._pending = val; - }, - - get operationsRequiringRestart() { - return AddonManager.OP_NEEDS_RESTART_NONE; - }, - - get size() { - return null; - }, - - get permissions() { - let permissions = 0; - // any "user defined" manifest can be removed - if (Services.prefs.prefHasUserValue(getPrefnameFromOrigin(this.manifest.origin))) - permissions = AddonManager.PERM_CAN_UNINSTALL; - if (!this.appDisabled) { - if (this.userDisabled) { - permissions |= AddonManager.PERM_CAN_ENABLE; - } else { - permissions |= AddonManager.PERM_CAN_DISABLE; - } - } - return permissions; - }, - - findUpdates: function(listener, reason, appVersion, platformVersion) { - if ("onNoCompatibilityUpdateAvailable" in listener) - listener.onNoCompatibilityUpdateAvailable(this); - if ("onNoUpdateAvailable" in listener) - listener.onNoUpdateAvailable(this); - if ("onUpdateFinished" in listener) - listener.onUpdateFinished(this); - }, - - get isActive() { - return ActiveProviders.has(this.manifest.origin); - }, - - get name() { - return this.manifest.name; - }, - get version() { - return this.manifest.version ? this.manifest.version.toString() : ""; - }, - - get iconURL() { - return this.manifest.icon32URL ? this.manifest.icon32URL : this.manifest.iconURL; - }, - get icon64URL() { - return this.manifest.icon64URL; - }, - get icons() { - let icons = { - 16: this.manifest.iconURL - }; - if (this.manifest.icon32URL) - icons[32] = this.manifest.icon32URL; - if (this.manifest.icon64URL) - icons[64] = this.manifest.icon64URL; - return icons; - }, - - get description() { - return this.manifest.description; - }, - get homepageURL() { - return this.manifest.homepageURL; - }, - get defaultLocale() { - return this.manifest.defaultLocale; - }, - get selectedLocale() { - return this.manifest.selectedLocale; - }, - - get installDate() { - return this.manifest.installDate ? new Date(this.manifest.installDate) : null; - }, - get updateDate() { - return this.manifest.updateDate ? new Date(this.manifest.updateDate) : null; - }, - - get creator() { - return new AddonManagerPrivate.AddonAuthor(this.manifest.author); - }, - - get userDisabled() { - return this.appDisabled || !ActiveProviders.has(this.manifest.origin); - }, - - set userDisabled(val) { - if (val == this.userDisabled) - return val; - if (val) { - SocialService.disableProvider(this.manifest.origin); - } else if (!this.appDisabled) { - SocialService.enableProvider(this.manifest.origin); - } - return val; - }, - - uninstall: function(aCallback) { - let prefName = getPrefnameFromOrigin(this.manifest.origin); - if (Services.prefs.prefHasUserValue(prefName)) { - if (ActiveProviders.has(this.manifest.origin)) { - SocialService.disableProvider(this.manifest.origin, function() { - SocialAddonProvider.removeAddon(this, aCallback); - }.bind(this)); - } else { - SocialAddonProvider.removeAddon(this, aCallback); - } - } else { - schedule(aCallback); - } - }, - - cancelUninstall: function() { - this._pending -= AddonManager.PENDING_UNINSTALL; - AddonManagerPrivate.callAddonListeners("onOperationCancelled", this); - } -}; - - -AddonManagerPrivate.registerProvider(SocialAddonProvider, [ - new AddonManagerPrivate.AddonType(ADDON_TYPE_SERVICE, URI_EXTENSION_STRINGS, - STRING_TYPE_NAME, - AddonManager.VIEW_TYPE_LIST, 10000) -]); |