diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js
index f0605a5ab0c..24845987daf 100644
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1588,6 +1588,15 @@ pref("services.push.serverURL", "wss://push.services.mozilla.com/");
pref("social.sidebar.unload_timeout_ms", 10000);
+// activation from inside of share panel is possible if activationPanelEnabled
+// is true. Pref'd off for release while usage testing is done through beta.
+#ifdef RELEASE_BUILD
+pref("social.share.activationPanelEnabled", false);
+#else
+pref("social.share.activationPanelEnabled", true);
+#endif
+pref("social.shareDirectory", "https://activations.cdn.mozilla.net/en-US/sharePanel.html");
+
pref("dom.identity.enabled", false);
// Block insecure active content on https pages
diff --git a/browser/base/content/aboutSocialError.xhtml b/browser/base/content/aboutSocialError.xhtml
index eba803a915c..5cb78accca6 100644
--- a/browser/base/content/aboutSocialError.xhtml
+++ b/browser/base/content/aboutSocialError.xhtml
@@ -43,12 +43,17 @@
function parseQueryString() {
let url = document.documentURI;
- let queryString = url.replace(/^about:socialerror\??/, "");
+ var searchParams = new URLSearchParams(url);
- let modeMatch = queryString.match(/mode=([^&]+)/);
- let mode = modeMatch && modeMatch[1] ? modeMatch[1] : "";
- let originMatch = queryString.match(/origin=([^&]+)/);
- config.origin = originMatch && originMatch[1] ? decodeURIComponent(originMatch[1]) : "";
+ let mode = searchParams.get("mode");
+ config.directory = searchParams.get("directory");
+ config.origin = searchParams.get("origin");
+ let encodedURL = searchParams.get("url");
+ let url = decodeURIComponent(encodedURL);
+ if (config.directory) {
+ let URI = Services.io.newURI(url, null, null);
+ config.origin = Services.scriptSecurityManager.getNoAppCodebasePrincipal(URI).origin;
+ }
switch (mode) {
case "compactInfo":
@@ -59,10 +64,6 @@
document.getElementById("btnCloseSidebar").style.display = 'none';
//intentional fall-through
case "tryAgain":
- let urlMatch = queryString.match(/url=([^&]+)/);
- let encodedURL = urlMatch && urlMatch[1] ? urlMatch[1] : "";
- let url = decodeURIComponent(encodedURL);
-
config.tryAgainCallback = loadQueryURL;
config.queryURL = url;
break;
@@ -80,7 +81,7 @@
let productName = brandBundle.GetStringFromName("brandShortName");
let provider = Social._getProviderFromOrigin(config.origin);
- let providerName = provider && provider.name;
+ let providerName = provider ? provider.name : config.origin;
// Sets up the error message
let msg = browserBundle.formatStringFromName("social.error.message", [productName, providerName], 2);
diff --git a/browser/base/content/browser-sets.inc b/browser/base/content/browser-sets.inc
index b9afa3ab09f..1cf108785da 100644
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -119,7 +119,7 @@
#endif
-
+
diff --git a/browser/base/content/browser-social.js b/browser/base/content/browser-social.js
index 638ed3275d9..9ca5456617c 100644
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -183,7 +183,7 @@ SocialUI = {
// about:home or the share panel, we bypass the enable prompt. Any website
// activation, such as from the activations directory or a providers website
// will still get the prompt.
- _activationEventHandler: function SocialUI_activationHandler(e, aBypassUserEnable=false) {
+ _activationEventHandler: function SocialUI_activationHandler(e, options={}) {
let targetDoc;
let node;
if (e.target instanceof HTMLDocument) {
@@ -197,7 +197,9 @@ SocialUI = {
if (!(targetDoc instanceof HTMLDocument))
return;
- if (!aBypassUserEnable && targetDoc.defaultView != content)
+ // The share panel iframe will not match "content" so it passes a bypass
+ // flag
+ if (!options.bypassContentCheck && targetDoc.defaultView != content)
return;
// If we are in PB mode, we silently do nothing (bug 829404 exists to
@@ -233,11 +235,25 @@ SocialUI = {
if (provider.sidebarURL) {
SocialSidebar.show(provider.origin);
}
+ if (provider.shareURL) {
+ // make this new provider the selected provider. If the panel hasn't
+ // been opened, we need to make the frame first.
+ SocialShare._createFrame();
+ SocialShare.iframe.setAttribute('src', 'data:text/plain;charset=utf8,');
+ SocialShare.iframe.setAttribute('origin', provider.origin);
+ // get the right button selected
+ SocialShare.populateProviderMenu();
+ if (SocialShare.panel.state == "open") {
+ SocialShare.sharePage(provider.origin);
+ }
+ }
if (provider.postActivationURL) {
- openUILinkIn(provider.postActivationURL, "tab");
+ // if activated from an open share panel, we load the landing page in
+ // a background tab
+ gBrowser.loadOneTab(provider.postActivationURL, {inBackground: SocialShare.panel.state == "open"});
}
});
- }, aBypassUserEnable);
+ }, options);
},
showLearnMore: function() {
@@ -290,10 +306,10 @@ SocialUI = {
// called on tab/urlbar/location changes and after customization. Update
// anything that is tab specific.
updateState: function() {
+ SocialShare.update();
if (!SocialUI.enabled)
return;
SocialMarks.update();
- SocialShare.update();
}
}
@@ -434,6 +450,12 @@ SocialFlyout = {
}
SocialShare = {
+ get _dynamicResizer() {
+ delete this._dynamicResizer;
+ this._dynamicResizer = new DynamicResizeWatcher();
+ return this._dynamicResizer;
+ },
+
// Share panel may be attached to the overflow or menu button depending on
// customization, we need to manage open state of the anchor.
get anchor() {
@@ -452,15 +474,27 @@ SocialShare = {
return this.panel.lastChild;
},
+ get activationPanelEnabled () {
+ // ability to pref off for release
+ return Services.prefs.getBoolPref("social.share.activationPanelEnabled");
+ },
+
+ _activationHandler: function(event) {
+ if (!SocialShare.activationPanelEnabled)
+ return;
+ SocialUI._activationEventHandler(event, { bypassContentCheck: true, bypassInstallPanel: true });
+ },
+
uninit: function () {
if (this.iframe) {
+ this.iframe.removeEventListener("ActivateSocialFeature", this._activationHandler, true, true);
this.iframe.remove();
}
},
_createFrame: function() {
let panel = this.panel;
- if (!SocialUI.enabled || this.iframe)
+ if (this.iframe)
return;
this.panel.hidden = false;
// create and initialize the panel for this window
@@ -472,6 +506,7 @@ SocialShare = {
iframe.setAttribute("disableglobalhistory", "true");
iframe.setAttribute("flex", "1");
panel.appendChild(iframe);
+ this.iframe.addEventListener("ActivateSocialFeature", this._activationHandler, true, true);
this.populateProviderMenu();
},
@@ -481,11 +516,19 @@ SocialShare = {
if (lastProviderOrigin) {
provider = Social._getProviderFromOrigin(lastProviderOrigin);
}
+ // if we are able to activate a provider we don't need to do anything fancy
+ // here, the user will land on the activation panel if no previously
+ // selected provider is available.
+ if (this.activationPanelEnabled)
+ return provider;
+
// if they have a provider selected in the sidebar use that for the initial
// default in share
if (!provider)
provider = SocialSidebar.provider;
- // if our provider has no shareURL, select the first one that does
+ // if our provider has no shareURL, select the first one that does. If we
+ // have no selected provider and activation is available, default to that
+ // panel.
if (!provider || !provider.shareURL) {
let providers = [p for (p of Social.providers) if (p.shareURL)];
provider = providers.length > 0 && providers[0];
@@ -498,17 +541,12 @@ SocialShare = {
return;
let providers = [p for (p of Social.providers) if (p.shareURL)];
let hbox = document.getElementById("social-share-provider-buttons");
- // selectable providers are inserted before the provider-menu seperator,
- // remove any menuitems in that area
- while (hbox.firstChild) {
+ // remove everything before the add-share-provider button (which should also
+ // be lastChild if any share providers were added)
+ let addButton = document.getElementById("add-share-provider");
+ while (hbox.firstChild != addButton) {
hbox.removeChild(hbox.firstChild);
}
- // reset our share toolbar
- // only show a selection if there is more than one
- if (!SocialUI.enabled || providers.length < 2) {
- this.panel.firstChild.hidden = true;
- return;
- }
let selectedProvider = this.getSelectedProvider();
for (let provider of providers) {
let button = document.createElement("toolbarbutton");
@@ -518,17 +556,16 @@ SocialShare = {
button.setAttribute("image", provider.iconURL);
button.setAttribute("tooltiptext", provider.name);
button.setAttribute("origin", provider.origin);
- button.setAttribute("oncommand", "SocialShare.sharePage(this.getAttribute('origin')); this.checked=true;");
+ button.setAttribute("oncommand", "SocialShare.sharePage(this.getAttribute('origin'));");
if (provider == selectedProvider) {
this.defaultButton = button;
}
- hbox.appendChild(button);
+ hbox.insertBefore(button, addButton);
}
if (!this.defaultButton) {
- this.defaultButton = hbox.firstChild
+ this.defaultButton = this.activationPanelEnabled ? addButton : hbox.firstChild;
}
this.defaultButton.setAttribute("checked", "true");
- this.panel.firstChild.hidden = false;
},
get shareButton() {
@@ -560,8 +597,8 @@ SocialShare = {
let shareButton = widget.forWindow(window).node;
// hidden state is based on available share providers and location of
// button. It's always visible and disabled in the customization palette.
- shareButton.hidden = !SocialUI.enabled || (widget.areaType &&
- [p for (p of Social.providers) if (p.shareURL)].length == 0);
+ shareButton.hidden = !this.activationPanelEnabled && (!SocialUI.enabled || (widget.areaType &&
+ [p for (p of Social.providers) if (p.shareURL)].length == 0));
let disabled = !widget.areaType || shareButton.hidden || !this.canSharePage(gBrowser.currentURI);
// 1. update the relevent command's disabled state so the keyboard
@@ -577,6 +614,9 @@ SocialShare = {
cmd.removeAttribute("disabled");
shareButton.removeAttribute("disabled");
}
+
+ // enable or disable the activation panel
+ document.getElementById("add-share-provider").hidden = !this.activationPanelEnabled;
},
_onclick: function() {
@@ -608,10 +648,15 @@ SocialShare = {
if (!iframe)
return;
- iframe.removeAttribute("src");
- iframe.webNavigation.loadURI("about:socialerror?mode=compactInfo&origin=" +
- encodeURIComponent(iframe.getAttribute("origin")),
- null, null, null, null);
+ let url;
+ let origin = iframe.getAttribute("origin");
+ if (!origin && this.activationPanelEnabled) {
+ // directory site is down
+ url = "about:socialerror?mode=tryAgainOnly&directory=1&url=" + encodeURIComponent(iframe.getAttribute("src"));
+ } else {
+ url = "about:socialerror?mode=compactInfo&origin=" + encodeURIComponent(origin);
+ }
+ iframe.webNavigation.loadURI(url, null, null, null, null);
sizeSocialPanelToContent(this.panel, iframe);
},
@@ -621,13 +666,6 @@ SocialShare = {
// will call sharePage with an origin for us to switch to.
this._createFrame();
let iframe = this.iframe;
- let provider;
- if (providerOrigin)
- provider = Social._getProviderFromOrigin(providerOrigin);
- else
- provider = this.getSelectedProvider();
- if (!provider || !provider.shareURL)
- return;
// graphData is an optional param that either defines the full set of data
// to be shared, or partial data about the current page. It is set by a call
@@ -659,20 +697,25 @@ SocialShare = {
}
this.currentShare = pageData;
+ let provider;
+ if (providerOrigin)
+ provider = Social._getProviderFromOrigin(providerOrigin);
+ else
+ provider = this.getSelectedProvider();
+ if (!provider || !provider.shareURL) {
+ this.showDirectory();
+ return;
+ }
+ // check the menu button
+ let hbox = document.getElementById("social-share-provider-buttons");
+ let btn = hbox.querySelector("[origin='" + provider.origin + "']");
+ btn.checked = true;
+
let shareEndpoint = OpenGraphBuilder.generateEndpointURL(provider.shareURL, pageData);
let size = provider.getPageSize("share");
if (size) {
- if (this._dynamicResizer) {
- this._dynamicResizer.stop();
- this._dynamicResizer = null;
- }
- let {width, height} = size;
- width += this.panel.boxObject.width - iframe.boxObject.width;
- height += this.panel.boxObject.height - iframe.boxObject.height;
- this.panel.sizeTo(width, height);
- } else {
- this._dynamicResizer = new DynamicResizeWatcher();
+ this._dynamicResizer.stop();
}
// if we've already loaded this provider/page share endpoint, we don't want
@@ -684,7 +727,7 @@ SocialShare = {
reload = shareEndpoint != iframe.contentDocument.location.spec;
}
if (!reload) {
- if (this._dynamicResizer)
+ if (!size)
this._dynamicResizer.start(this.panel, iframe);
iframe.docShell.isActive = true;
iframe.docShell.isAppTab = true;
@@ -702,7 +745,13 @@ SocialShare = {
// should close the window when done.
iframe.contentWindow.opener = iframe.contentWindow;
setTimeout(function() {
- if (SocialShare._dynamicResizer) { // may go null if hidden quickly
+ if (size) {
+ let panel = SocialShare.panel;
+ let {width, height} = size;
+ width += panel.boxObject.width - iframe.boxObject.width;
+ height += panel.boxObject.height - iframe.boxObject.height;
+ panel.sizeTo(width, height);
+ } else {
SocialShare._dynamicResizer.start(iframe.parentNode, iframe);
}
}, 0);
@@ -723,10 +772,32 @@ SocialShare = {
let uri = Services.io.newURI(shareEndpoint, null, null);
iframe.setAttribute("origin", provider.origin);
iframe.setAttribute("src", shareEndpoint);
+ this._openPanel();
+ },
+ showDirectory: function() {
+ let url = Services.prefs.getCharPref("social.shareDirectory");
+ this._createFrame();
+ let iframe = this.iframe;
+ iframe.removeAttribute("origin");
+ iframe.setAttribute("src", url);
+ iframe.addEventListener("load", function panelBrowserOnload(e) {
+ iframe.removeEventListener("load", panelBrowserOnload, true);
+ SocialShare._dynamicResizer.start(iframe.parentNode, iframe);
+
+ iframe.addEventListener("unload", function panelBrowserOnload(e) {
+ iframe.removeEventListener("unload", panelBrowserOnload, true);
+ SocialShare._dynamicResizer.stop();
+ }, true);
+
+ }, true);
+ this._openPanel();
+ },
+
+ _openPanel: function() {
let anchor = document.getAnonymousElementByAttribute(this.anchor, "class", "toolbarbutton-icon");
this.panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false);
- Social.setErrorListener(iframe, this.setErrorMessage.bind(this));
+ Social.setErrorListener(this.iframe, this.setErrorMessage.bind(this));
Services.telemetry.getHistogramById("SOCIAL_TOOLBAR_BUTTONS").add(0);
}
};
diff --git a/browser/base/content/browser.xul b/browser/base/content/browser.xul
index 353a7cbb71f..11198db1f6c 100644
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -246,7 +246,11 @@
onpopuphidden="SocialShare.onHidden()"
hidden="true">
-
+
+
+
diff --git a/browser/base/content/test/social/browser.ini b/browser/base/content/test/social/browser.ini
index f6e2a7d8135..57729df7e9c 100644
--- a/browser/base/content/test/social/browser.ini
+++ b/browser/base/content/test/social/browser.ini
@@ -11,6 +11,7 @@ support-files =
opengraph/shorturl_linkrel.html
microdata.html
share.html
+ share_activate.html
social_activate.html
social_activate_iframe.html
social_chat.html
diff --git a/browser/base/content/test/social/browser_share.js b/browser/base/content/test/social/browser_share.js
index 09a6837ee42..bd616bb4abb 100644
--- a/browser/base/content/test/social/browser_share.js
+++ b/browser/base/content/test/social/browser_share.js
@@ -10,10 +10,46 @@ let manifest = { // normal provider
iconURL: "https://example.com/browser/browser/base/content/test/general/moz.png",
shareURL: "https://example.com/browser/browser/base/content/test/social/share.html"
};
+let activationPage = "https://example.com/browser/browser/base/content/test/social/share_activate.html";
+
+function waitForProviderEnabled(cb) {
+ Services.obs.addObserver(function providerSet(subject, topic, data) {
+ Services.obs.removeObserver(providerSet, "social:provider-enabled");
+ info("social:provider-enabled observer was notified");
+ cb();
+ }, "social:provider-enabled", false);
+}
+
+function sendActivationEvent(callback) {
+ // hack Social.lastEventReceived so we don't hit the "too many events" check.
+ Social.lastEventReceived = 0;
+ let doc = SocialShare.iframe.contentDocument;
+ // if our test has a frame, use it
+ let button = doc.getElementById("activation");
+ ok(!!button, "got the activation button");
+ EventUtils.synthesizeMouseAtCenter(button, {}, doc.defaultView);
+ if (callback)
+ executeSoon(callback);
+}
+
+function waitForEvent(iframe, eventName, callback) {
+ iframe.addEventListener(eventName, function load() {
+ info("page load is "+iframe.contentDocument.location.href);
+ if (iframe.contentDocument.location.href != "data:text/plain;charset=utf8,") {
+ iframe.removeEventListener(eventName, load, true);
+ executeSoon(callback);
+ }
+ }, true);
+}
function test() {
waitForExplicitFinish();
-
+ Services.prefs.setCharPref("social.shareDirectory", activationPage);
+ registerCleanupFunction(function () {
+ Services.prefs.clearUserPref("social.directories");
+ Services.prefs.clearUserPref("social.shareDirectory");
+ Services.prefs.clearUserPref("social.share.activationPanelEnabled");
+ });
runSocialTests(tests);
}
@@ -75,11 +111,10 @@ let corpus = [
function loadURLInTab(url, callback) {
info("Loading tab with "+url);
let tab = gBrowser.selectedTab = gBrowser.addTab(url);
- tab.linkedBrowser.addEventListener("load", function listener() {
+ waitForEvent(tab.linkedBrowser, "load", () => {
is(tab.linkedBrowser.currentURI.spec, url, "tab loaded")
- tab.linkedBrowser.removeEventListener("load", listener, true);
- executeSoon(function() { callback(tab) });
- }, true);
+ callback(tab)
+ });
}
function hasoptions(testOptions, options) {
@@ -110,7 +145,6 @@ var tests = {
checkSocialUI();
// share should not be enabled since we only have about:blank page
let shareButton = SocialShare.shareButton;
- is(shareButton.disabled, true, "share button is disabled");
// verify the attribute for proper css
is(shareButton.getAttribute("disabled"), "true", "share button attribute is disabled");
// button should be visible
@@ -128,7 +162,6 @@ var tests = {
checkSocialUI();
// share should not be enabled since we only have about:blank page
let shareButton = SocialShare.shareButton;
- is(shareButton.disabled, false, "share button is enabled");
// verify the attribute for proper css
ok(!shareButton.hasAttribute("disabled"), "share button is enabled");
// button should be visible
@@ -149,7 +182,7 @@ var tests = {
function runOneTest() {
loadURLInTab(testData.url, function(tab) {
testTab = tab;
- SocialShare.sharePage();
+ SocialShare.sharePage(manifest.origin);
});
}
@@ -241,5 +274,46 @@ var tests = {
SocialShare.sharePage(manifest.origin, null, target);
});
});
+ },
+ testSharePanelActivation: function(next) {
+ let testTab;
+ // cleared in the cleanup function
+ Services.prefs.setCharPref("social.directories", "https://example.com");
+ Services.prefs.setBoolPref("social.share.activationPanelEnabled", true);
+ // make the iframe so we can wait on the load
+ SocialShare._createFrame();
+ let iframe = SocialShare.iframe;
+
+ waitForEvent(iframe, "load", () => {
+ waitForCondition(() => {
+ // sometimes the iframe is ready before the panel is open, we need to
+ // wait for both conditions
+ return SocialShare.panel.state == "open";
+ }, () => {
+ is(iframe.contentDocument.location.href, activationPage, "activation page loaded");
+ waitForProviderEnabled(() => {
+ let provider = Social._getProviderFromOrigin(manifest.origin);
+ let port = provider.getWorkerPort();
+ ok(!!port, "got port");
+ port.onmessage = function (e) {
+ let topic = e.data.topic;
+ info("got topic "+topic+"\n");
+ switch (topic) {
+ case "got-share-data-message":
+ ok(true, "share completed");
+ gBrowser.removeTab(testTab);
+ SocialService.uninstallProvider(manifest.origin, next);
+ break;
+ }
+ }
+ port.postMessage({topic: "test-init"});
+ });
+ sendActivationEvent();
+ }, "share panel did not open and load share page");
+ });
+ loadURLInTab(activationPage, function(tab) {
+ testTab = tab;
+ SocialShare.sharePage();
+ });
}
}
diff --git a/browser/base/content/test/social/share_activate.html b/browser/base/content/test/social/share_activate.html
new file mode 100644
index 00000000000..f53da0b288c
--- /dev/null
+++ b/browser/base/content/test/social/share_activate.html
@@ -0,0 +1,36 @@
+
+
+
+ Activation test
+
+
+
+
+nothing to see here
+
+
+
+
+
diff --git a/browser/locales/en-US/chrome/browser/browser.dtd b/browser/locales/en-US/chrome/browser/browser.dtd
index e18c817d2aa..0f9715f1a01 100644
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -140,6 +140,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
+
diff --git a/browser/modules/Social.jsm b/browser/modules/Social.jsm
index 54f03eefced..6301d2a6ec1 100644
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -363,7 +363,8 @@ SocialErrorListener.prototype = {
if (failure && aStatus != Components.results.NS_BINDING_ABORTED) {
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
let provider = Social._getProviderFromOrigin(this.iframe.getAttribute("origin"));
- provider.errorState = "content-error";
+ if (provider && !provider.errorState)
+ provider.errorState = "content-error";
this.setErrorMessage(aWebProgress.QueryInterface(Ci.nsIDocShell)
.chromeEventHandler);
}
@@ -373,7 +374,7 @@ SocialErrorListener.prototype = {
if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE) {
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
let provider = Social._getProviderFromOrigin(this.iframe.getAttribute("origin"));
- if (!provider.errorState)
+ if (provider && !provider.errorState)
provider.errorState = "content-error";
schedule(function() {
this.setErrorMessage(aWebProgress.QueryInterface(Ci.nsIDocShell)
diff --git a/browser/themes/osx/browser.css b/browser/themes/osx/browser.css
index f3d6501857b..519d13cc64e 100644
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1313,6 +1313,11 @@ toolbar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-ic
width: 16px;
}
+ #add-share-provider {
+ list-style-image: url(chrome://browser/skin/menuPanel-small@2x.png);
+ -moz-image-region: rect(0px, 192px, 32px, 160px);
+ }
+
#loop-call-button > .toolbarbutton-badge-container {
list-style-image: url("chrome://browser/skin/loop/toolbar@2x.png");
-moz-image-region: rect(0, 36px, 36px, 0);
diff --git a/browser/themes/shared/menupanel.inc.css b/browser/themes/shared/menupanel.inc.css
index 436a00faede..dfb1ce39bcc 100644
--- a/browser/themes/shared/menupanel.inc.css
+++ b/browser/themes/shared/menupanel.inc.css
@@ -218,3 +218,8 @@ toolbarpaletteitem[place="palette"] > #zoom-controls > #zoom-out-button {
toolbarpaletteitem[place="palette"] > #zoom-controls > #zoom-in-button {
-moz-image-region: rect(0px, 96px, 16px, 80px);
}
+
+#add-share-provider {
+ list-style-image: url(chrome://browser/skin/menuPanel-small.png);
+ -moz-image-region: rect(0px, 96px, 16px, 80px);
+}
\ No newline at end of file
diff --git a/toolkit/components/social/SocialService.jsm b/toolkit/components/social/SocialService.jsm
index 9dc6c623ee0..b98151f6c55 100644
--- a/toolkit/components/social/SocialService.jsm
+++ b/toolkit/components/social/SocialService.jsm
@@ -585,26 +585,24 @@ this.SocialService = {
action, [], options);
},
- installProvider: function(aDOMDocument, data, installCallback, aBypassUserEnable=false) {
+ installProvider: function(aDOMDocument, data, installCallback, options={}) {
let manifest;
let installOrigin = aDOMDocument.nodePrincipal.origin;
- if (data) {
- let installType = getOriginActivationType(installOrigin);
- // if we get data, we MUST have a valid manifest generated from the data
- manifest = this._manifestFromData(installType, data, aDOMDocument.nodePrincipal);
- if (!manifest)
- throw new Error("SocialService.installProvider: service configuration is invalid from " + aDOMDocument.location.href);
+ let installType = getOriginActivationType(installOrigin);
+ // if we get data, we MUST have a valid manifest generated from the data
+ manifest = this._manifestFromData(installType, data, aDOMDocument.nodePrincipal);
+ if (!manifest)
+ throw new Error("SocialService.installProvider: service configuration is invalid from " + aDOMDocument.location.href);
- let addon = new AddonWrapper(manifest);
- if (addon && addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
- throw new Error("installProvider: provider with origin [" +
- installOrigin + "] 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.
- installOrigin = manifest.origin;
- }
+ let addon = new AddonWrapper(manifest);
+ if (addon && addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
+ throw new Error("installProvider: provider with origin [" +
+ installOrigin + "] 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.
+ installOrigin = manifest.origin;
let id = getAddonIDFromOrigin(installOrigin);
AddonManager.getAddonByID(id, function(aAddon) {
@@ -613,7 +611,7 @@ this.SocialService = {
aAddon.userDisabled = false;
}
schedule(function () {
- this._installProvider(aDOMDocument, manifest, aBypassUserEnable, aManifest => {
+ this._installProvider(aDOMDocument, manifest, options, aManifest => {
this._notifyProviderListeners("provider-installed", aManifest.origin);
installCallback(aManifest);
});
@@ -621,43 +619,21 @@ this.SocialService = {
}.bind(this));
},
- _installProvider: function(aDOMDocument, manifest, aBypassUserEnable, installCallback) {
- let sourceURI = aDOMDocument.location.href;
- let installOrigin = aDOMDocument.nodePrincipal.origin;
+ _installProvider: function(aDOMDocument, manifest, options, installCallback) {
+ if (!manifest)
+ throw new Error("Cannot install provider without manifest data");
- let installType = getOriginActivationType(installOrigin);
- let installer;
- switch(installType) {
- case "foreign":
- if (!Services.prefs.getBoolPref("social.remote-install.enabled"))
- throw new Error("Remote install of services is disabled");
- if (!manifest)
- throw new Error("Cannot install provider without manifest data");
+ let installType = getOriginActivationType(aDOMDocument.nodePrincipal.origin);
+ if (installType == "foreign" && !Services.prefs.getBoolPref("social.remote-install.enabled"))
+ throw new Error("Remote install of services is disabled");
- installer = new AddonInstaller(sourceURI, manifest, installCallback);
- this._showInstallNotification(aDOMDocument, installer);
- break;
- case "internal":
- // double check here since "builtin" falls through this as well.
- aBypassUserEnable = installType == "internal" && manifest.oneclick;
- case "directory":
- // a manifest is requried, and will have been vetted by reviewers. We
- // also handle in-product installations without the verification step.
- if (aBypassUserEnable) {
- installer = new AddonInstaller(sourceURI, manifest, installCallback);
- installer.install();
- return;
- }
- // a manifest is required, we'll catch a missing manifest below.
- if (!manifest)
- throw new Error("Cannot install provider without manifest data");
- installer = new AddonInstaller(sourceURI, manifest, installCallback);
- this._showInstallNotification(aDOMDocument, installer);
- break;
- default:
- throw new Error("SocialService.installProvider: Invalid install type "+installType+"\n");
- break;
- }
+ let installer = new AddonInstaller(aDOMDocument.location.href, manifest, installCallback);
+ let bypassPanel = options.bypassInstallPanel ||
+ (installType == "internal" && manifest.oneclick);
+ if (bypassPanel)
+ installer.install();
+ else
+ this._showInstallNotification(aDOMDocument, installer);
},
createWrapper: function(manifest) {