diff options
Diffstat (limited to 'browser/modules')
-rw-r--r-- | browser/modules/BrowserUITelemetry.jsm | 10 | ||||
-rw-r--r-- | browser/modules/Social.jsm | 272 | ||||
-rw-r--r-- | browser/modules/SocialService.jsm | 1097 | ||||
-rw-r--r-- | browser/modules/moz.build | 2 |
4 files changed, 5 insertions, 1376 deletions
diff --git a/browser/modules/BrowserUITelemetry.jsm b/browser/modules/BrowserUITelemetry.jsm index 392462b45..a6a5789f4 100644 --- a/browser/modules/BrowserUITelemetry.jsm +++ b/browser/modules/BrowserUITelemetry.jsm @@ -694,7 +694,7 @@ this.BrowserUITelemetry = { "spell-undo-add-to-dictionary", "openlinkincurrent", "openlinkintab", "openlink", // "openlinkprivate" intentionally omitted for privacy reasons. See bug 1176391. - "bookmarklink", "sharelink", "savelink", + "bookmarklink", "savelink", "marklinkMenu", "copyemail", "copylink", "media-play", "media-pause", "media-mute", "media-unmute", "media-playbackrate", "media-playbackrate-050x", "media-playbackrate-100x", @@ -702,12 +702,12 @@ this.BrowserUITelemetry = { "media-showcontrols", "media-hidecontrols", "video-fullscreen", "leave-dom-fullscreen", "reloadimage", "viewimage", "viewvideo", "copyimage-contents", "copyimage", - "copyvideourl", "copyaudiourl", "saveimage", "shareimage", "sendimage", + "copyvideourl", "copyaudiourl", "saveimage", "sendimage", "setDesktopBackground", "viewimageinfo", "viewimagedesc", "savevideo", - "sharevideo", "saveaudio", "video-saveimage", "sendvideo", "sendaudio", - "ctp-play", "ctp-hide", "sharepage", "savepage", "pocket", "markpageMenu", + "saveaudio", "video-saveimage", "sendvideo", "sendaudio", + "ctp-play", "ctp-hide", "savepage", "pocket", "markpageMenu", "viewbgimage", "undo", "cut", "copy", "paste", "delete", "selectall", - "keywordfield", "searchselect", "shareselect", "frame", "showonlythisframe", + "keywordfield", "searchselect", "frame", "showonlythisframe", "openframeintab", "openframe", "reloadframe", "bookmarkframe", "saveframe", "printframe", "viewframesource", "viewframeinfo", "viewpartialsource-selection", "viewpartialsource-mathml", diff --git a/browser/modules/Social.jsm b/browser/modules/Social.jsm deleted file mode 100644 index 1569e0122..000000000 --- a/browser/modules/Social.jsm +++ /dev/null @@ -1,272 +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"; - -this.EXPORTED_SYMBOLS = ["Social", "OpenGraphBuilder", - "DynamicResizeWatcher", "sizeSocialPanelToContent"]; - -const Ci = Components.interfaces; -const Cc = Components.classes; -const Cu = Components.utils; - -// The minimum sizes for the auto-resize panel code, minimum size necessary to -// properly show the error page in the panel. -const PANEL_MIN_HEIGHT = 190; -const PANEL_MIN_WIDTH = 330; - -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); - -XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI", - "resource:///modules/CustomizableUI.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "SocialService", - "resource:///modules/SocialService.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata", - "resource://gre/modules/PageMetadata.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils", - "resource://gre/modules/PlacesUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "Promise", - "resource://gre/modules/Promise.jsm"); - - -this.Social = { - initialized: false, - lastEventReceived: 0, - providers: [], - _disabledForSafeMode: false, - - init: function Social_init() { - this._disabledForSafeMode = Services.appinfo.inSafeMode && this.enabled; - let deferred = Promise.defer(); - - if (this.initialized) { - deferred.resolve(true); - return deferred.promise; - } - this.initialized = true; - // if SocialService.hasEnabledProviders, retreive the providers so the - // front-end can generate UI - if (SocialService.hasEnabledProviders) { - // Retrieve the current set of providers, and set the current provider. - SocialService.getOrderedProviderList(function (providers) { - Social._updateProviderCache(providers); - Social._updateEnabledState(SocialService.enabled); - deferred.resolve(false); - }); - } else { - deferred.resolve(false); - } - - // Register an observer for changes to the provider list - SocialService.registerProviderListener(function providerListener(topic, origin, providers) { - // An engine change caused by adding/removing a provider should notify. - // any providers we receive are enabled in the AddonsManager - if (topic == "provider-installed" || topic == "provider-uninstalled") { - // installed/uninstalled do not send the providers param - Services.obs.notifyObservers(null, "social:" + topic, origin); - return; - } - if (topic == "provider-enabled") { - Social._updateProviderCache(providers); - Social._updateEnabledState(true); - Services.obs.notifyObservers(null, "social:" + topic, origin); - return; - } - if (topic == "provider-disabled") { - // a provider was removed from the list of providers, update states - Social._updateProviderCache(providers); - Social._updateEnabledState(providers.length > 0); - Services.obs.notifyObservers(null, "social:" + topic, origin); - return; - } - if (topic == "provider-update") { - // a provider has self-updated its manifest, we need to update our cache - // and reload the provider. - Social._updateProviderCache(providers); - let provider = Social._getProviderFromOrigin(origin); - provider.reload(); - } - }); - return deferred.promise; - }, - - _updateEnabledState: function(enable) { - for (let p of Social.providers) { - p.enabled = enable; - } - }, - - // Called to update our cache of providers and set the current provider - _updateProviderCache: function (providers) { - this.providers = providers; - Services.obs.notifyObservers(null, "social:providers-changed", null); - }, - - get enabled() { - return !this._disabledForSafeMode && this.providers.length > 0; - }, - - _getProviderFromOrigin: function (origin) { - for (let p of this.providers) { - if (p.origin == origin) { - return p; - } - } - return null; - }, - - getManifestByOrigin: function(origin) { - return SocialService.getManifestByOrigin(origin); - }, - - installProvider: function(data, installCallback, options={}) { - SocialService.installProvider(data, installCallback, options); - }, - - uninstallProvider: function(origin, aCallback) { - SocialService.uninstallProvider(origin, aCallback); - }, - - // Activation functionality - activateFromOrigin: function (origin, callback) { - // It's OK if the provider has already been activated - we still get called - // back with it. - SocialService.enableProvider(origin, callback); - } -}; - -function sizeSocialPanelToContent(panel, iframe, requestedSize) { - let doc = iframe.contentDocument; - if (!doc || !doc.body) { - return; - } - // We need an element to use for sizing our panel. See if the body defines - // an id for that element, otherwise use the body itself. - let body = doc.body; - let docEl = doc.documentElement; - let bodyId = body.getAttribute("contentid"); - if (bodyId) { - body = doc.getElementById(bodyId) || doc.body; - } - // offsetHeight/Width don't include margins, so account for that. - let cs = doc.defaultView.getComputedStyle(body); - let width = Math.max(PANEL_MIN_WIDTH, docEl.offsetWidth); - let height = Math.max(PANEL_MIN_HEIGHT, docEl.offsetHeight); - // if the panel is preloaded prior to being shown, cs will be null. in that - // case use the minimum size for the panel until it is shown. - if (cs) { - let computedHeight = parseInt(cs.marginTop) + body.offsetHeight + parseInt(cs.marginBottom); - height = Math.max(computedHeight, height); - let computedWidth = parseInt(cs.marginLeft) + body.offsetWidth + parseInt(cs.marginRight); - width = Math.max(computedWidth, width); - } - - // if our scrollHeight is still larger than the iframe, the css calculations - // above did not work for this site, increase the height. This can happen if - // the site increases its height for additional UI. - if (docEl.scrollHeight > iframe.boxObject.height) - height = docEl.scrollHeight; - - // if a size was defined in the manifest use it as a minimum - if (requestedSize) { - if (requestedSize.height) - height = Math.max(height, requestedSize.height); - if (requestedSize.width) - width = Math.max(width, requestedSize.width); - } - - // add the extra space used by the panel (toolbar, borders, etc) if the iframe - // has been loaded - if (iframe.boxObject.width && iframe.boxObject.height) { - // add extra space the panel needs if any - width += panel.boxObject.width - iframe.boxObject.width; - height += panel.boxObject.height - iframe.boxObject.height; - } - - // using panel.sizeTo will ignore css transitions, set size via style - if (Math.abs(panel.boxObject.width - width) >= 2) - panel.style.width = width + "px"; - if (Math.abs(panel.boxObject.height - height) >= 2) - panel.style.height = height + "px"; -} - -function DynamicResizeWatcher() { - this._mutationObserver = null; -} - -DynamicResizeWatcher.prototype = { - start: function DynamicResizeWatcher_start(panel, iframe, requestedSize) { - this.stop(); // just in case... - let doc = iframe.contentDocument; - this._mutationObserver = new iframe.contentWindow.MutationObserver((mutations) => { - sizeSocialPanelToContent(panel, iframe, requestedSize); - }); - // Observe anything that causes the size to change. - let config = {attributes: true, characterData: true, childList: true, subtree: true}; - this._mutationObserver.observe(doc, config); - // and since this may be setup after the load event has fired we do an - // initial resize now. - sizeSocialPanelToContent(panel, iframe, requestedSize); - }, - stop: function DynamicResizeWatcher_stop() { - if (this._mutationObserver) { - try { - this._mutationObserver.disconnect(); - } catch (ex) { - // may get "TypeError: can't access dead object" which seems strange, - // but doesn't seem to indicate a real problem, so ignore it... - } - this._mutationObserver = null; - } - } -} - - -this.OpenGraphBuilder = { - generateEndpointURL: function(URLTemplate, pageData) { - // support for existing oexchange style endpoints by supporting their - // querystring arguments. parse the query string template and do - // replacements where necessary the query names may be different than ours, - // so we could see u=%{url} or url=%{url} - let [endpointURL, queryString] = URLTemplate.split("?"); - let query = {}; - if (queryString) { - queryString.split('&').forEach(function (val) { - let [name, value] = val.split('='); - let p = /%\{(.+)\}/.exec(value); - if (!p) { - // preserve non-template query vars - query[name] = value; - } else if (pageData[p[1]]) { - if (p[1] == "previews") - query[name] = pageData[p[1]][0]; - else - query[name] = pageData[p[1]]; - } else if (p[1] == "body") { - // build a body for emailers - let body = ""; - if (pageData.title) - body += pageData.title + "\n\n"; - if (pageData.description) - body += pageData.description + "\n\n"; - if (pageData.text) - body += pageData.text + "\n\n"; - body += pageData.url; - query["body"] = body; - } - }); - // if the url template doesn't have title and no text was provided, add the title as the text. - if (!query.text && !query.title && pageData.title) { - query.text = pageData.title; - } - } - var str = []; - for (let p in query) - str.push(p + "=" + encodeURIComponent(query[p])); - if (str.length) - endpointURL = endpointURL + "?" + str.join("&"); - return endpointURL; - }, -}; 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) -]); diff --git a/browser/modules/moz.build b/browser/modules/moz.build index 852a4c911..a7bbbc258 100644 --- a/browser/modules/moz.build +++ b/browser/modules/moz.build @@ -35,8 +35,6 @@ EXTRA_JS_MODULES += [ 'Sanitizer.jsm', 'SelfSupportBackend.jsm', 'SitePermissions.jsm', - 'Social.jsm', - 'SocialService.jsm', 'TransientPrefs.jsm', 'URLBarZoom.jsm', 'webrtcUI.jsm', |