Bug 809694: implement support for multiple social providers in the front-end, including basic UI to switch between them. Portions of the patch by Gavin Sharp <gavin@gavinsharp.com>, r=gavin, r=markh

--HG--
extra : transplant_source : %2B3%7F%05l%3C%0A%90%B4%F6%B22%172_5%EEl%B3g
This commit is contained in:
Shane Caraveo 2012-12-07 20:13:27 -08:00
parent f680cd8f8c
commit b2309ece07
18 changed files with 726 additions and 252 deletions

View File

@ -1165,14 +1165,15 @@ pref("pdfjs.previousHandler.alwaysAskBeforeHandling", false);
// (This is intentionally on the high side; see bug 746055.)
pref("image.mem.max_decoded_image_kb", 256000);
// Example social provider
// Default social providers
pref("social.manifest.facebook", "{\"origin\":\"https://www.facebook.com\",\"name\":\"Facebook Messenger\",\"workerURL\":\"https://www.facebook.com/desktop/fbdesktop2/socialfox/fbworker.js.php\",\"iconURL\":\"data:image/x-icon;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8%2F9hAAAAX0lEQVQ4jWP4%2F%2F8%2FAyUYTFhHzjgDxP9JxGeQDSBVMxgTbUBCxer%2Fr999%2BQ8DJBuArJksA9A10s8AXIBoA0B%2BR%2FY%2FjD%2BEwoBoA1yT5v3PbdmCE8MAshhID%2FUMoDgzUYIBj0Cgi7ar4coAAAAASUVORK5CYII%3D\",\"sidebarURL\":\"https://www.facebook.com/desktop/fbdesktop2/?socialfox=true\"}");
// Comma-separated list of nsIURI::prePaths that are allowed to activate
// built-in social functionality.
pref("social.activation.whitelist", "https://www.facebook.com");
pref("social.sidebar.open", true);
pref("social.sidebar.unload_timeout_ms", 10000);
pref("social.active", false);
pref("social.toast-notifications.enabled", true);
pref("dom.identity.enabled", false);

View File

@ -540,6 +540,7 @@
command="Social:FocusChat"
class="show-only-for-keyboard"/>
<menuseparator class="social-statusarea-separator"/>
<menuseparator class="social-provider-menu" hidden="true"/>
<menuitem class="social-toggle-menuitem" command="Social:Toggle"/>
<menuitem class="social-remove-menuitem" command="Social:Remove"/>
</menupopup>

View File

@ -10,17 +10,17 @@ XPCOMUtils.defineLazyModuleGetter(this, "SharedFrame",
"resource:///modules/SharedFrame.jsm");
let SocialUI = {
// Called on delayed startup to initialize UI
// Called on delayed startup to initialize the UI
init: function SocialUI_init() {
Services.obs.addObserver(this, "social:pref-changed", false);
Services.obs.addObserver(this, "social:ambient-notification-changed", false);
Services.obs.addObserver(this, "social:profile-changed", false);
Services.obs.addObserver(this, "social:recommend-info-changed", false);
Services.obs.addObserver(this, "social:frameworker-error", false);
Services.obs.addObserver(this, "social:provider-set", false);
Services.prefs.addObserver("social.sidebar.open", this, false);
Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
Services.prefs.addObserver("social.active", this, false);
gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler, true, true);
@ -40,14 +40,106 @@ let SocialUI = {
Services.obs.removeObserver(this, "social:profile-changed");
Services.obs.removeObserver(this, "social:recommend-info-changed");
Services.obs.removeObserver(this, "social:frameworker-error");
Services.obs.removeObserver(this, "social:provider-set");
Services.prefs.removeObserver("social.sidebar.open", this);
Services.prefs.removeObserver("social.toast-notifications.enabled", this);
Services.prefs.removeObserver("social.active", this);
},
// Called once, after window load, once Social.jsm's provider has been set.
_providerReady: function SocialUI_providerReady() {
this._updateActiveUI();
this._updateMenuItems();
SocialChatBar.update();
SocialShareButton.init();
SocialMenu.populate();
SocialToolbar.init();
SocialSidebar.init();
},
// Social.provider has changed, update any state that depends on it.
// Note: this method is not called when Social.provider is first set, during
// the first window load.
_updateProvider: function () {
// XXX audit for handling nullness of social.provider
this._updateActiveUI();
this._updateMenuItems();
SocialChatBar.update();
SocialShareButton.updateProvider();
SocialMenu.populate();
SocialToolbar.updateProvider();
SocialSidebar.update();
},
// The entire feature is being turned on/off.
_updateEnabledState: function () {
this._updateActiveUI();
SocialChatBar.update();
SocialSidebar.update();
SocialShareButton.updateButtonHiddenState();
SocialMenu.populate();
SocialToolbar.updateButtonHiddenState();
SocialToolbar.populateProviderMenus();
},
_matchesCurrentProvider: function (origin) {
return Social.provider && Social.provider.origin == origin;
},
observe: function SocialUI_observe(subject, topic, data) {
// Exceptions here sometimes don't get reported properly, report them
// manually :(
try {
switch (topic) {
case "social:provider-set":
this._updateProvider();
break;
case "social:pref-changed":
this._updateEnabledState();
break;
// Provider-specific notifications
case "social:ambient-notification-changed":
if (this._matchesCurrentProvider(data)) {
SocialToolbar.updateButton();
SocialMenu.populate();
}
break;
case "social:profile-changed":
if (this._matchesCurrentProvider(data)) {
SocialToolbar.updateProfile();
SocialShareButton.updateProfileInfo();
SocialChatBar.update();
}
break;
case "social:recommend-info-changed":
if (this._matchesCurrentProvider(data)) {
SocialShareButton.updateShareState();
}
break;
case "social:frameworker-error":
if (Social.provider && Social.provider.origin == data) {
SocialSidebar.setSidebarErrorMessage("frameworker-error");
}
break;
case "nsPref:changed":
if (data == "social.sidebar.open") {
SocialSidebar.update();
}
break;
}
} catch (e) {
Components.utils.reportError(e + e.stack);
throw e;
}
},
// Miscellaneous helpers
showProfile: function SocialUI_showProfile() {
if (this.haveLoggedInUser())
if (Social.haveLoggedInUser())
openUILinkIn(Social.provider.profile.profileURL, "tab");
else {
// XXX Bug 789585 will implement an API for provider-specified login pages.
@ -55,104 +147,39 @@ let SocialUI = {
}
},
observe: function SocialUI_observe(subject, topic, data) {
switch (topic) {
case "social:pref-changed":
// Exceptions here sometimes don't get reported properly, report them
// manually :(
try {
this.updateToggleCommand();
SocialShareButton.updateButtonHiddenState();
SocialToolbar.updateButtonHiddenState();
SocialSidebar.update();
SocialChatBar.update();
SocialFlyout.unload();
SocialMenu.populate();
} catch (e) {
Components.utils.reportError(e);
throw e;
}
break;
case "social:ambient-notification-changed":
SocialToolbar.updateButton();
SocialMenu.populate();
break;
case "social:profile-changed":
SocialToolbar.updateProfile();
SocialShareButton.updateProfileInfo();
SocialChatBar.update();
break;
case "social:recommend-info-changed":
SocialShareButton.updateShareState();
break;
case "social:frameworker-error":
if (Social.provider) {
Social.errorState = "frameworker-error";
SocialSidebar.setSidebarErrorMessage("frameworker-error");
}
break;
case "nsPref:changed":
this.updateActiveBroadcaster();
this.updateToggleCommand();
SocialSidebar.update();
SocialToolbar.updateButton();
SocialMenu.populate();
break;
}
},
_updateActiveUI: function SocialUI_updateActiveUI() {
let broadcaster = document.getElementById("socialActiveBroadcaster");
broadcaster.hidden = !Social.active;
get toggleCommand() {
return document.getElementById("Social:Toggle");
},
// Called once Social.jsm's provider has been set
_providerReady: function SocialUI_providerReady() {
// If we couldn't find a provider, nothing to do here.
if (!Social.provider)
return;
this.updateToggleCommand();
// The View->Sidebar and Menubar->Tools menu.
for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
document.getElementById(id).setAttribute("label", Social.provider.name);
SocialToolbar.init();
SocialShareButton.init();
SocialSidebar.init();
SocialMenu.populate();
SocialChatBar.update();
this.updateActiveBroadcaster();
},
updateToggleCommand: function SocialUI_updateToggleCommand() {
if (!Social.provider)
return;
let toggleCommand = this.toggleCommand;
let toggleCommand = document.getElementById("Social:Toggle");
// We only need to update the command itself - all our menu items use it.
let enabled = Services.prefs.getBoolPref("social.enabled");
let label = gNavigatorBundle.getFormattedString(enabled ? "social.turnOff.label" : "social.turnOn.label",
let label = gNavigatorBundle.getFormattedString(Social.provider.enabled ?
"social.turnOff.label" :
"social.turnOn.label",
[Social.provider.name]);
let accesskey = gNavigatorBundle.getString(enabled ? "social.turnOff.accesskey" : "social.turnOn.accesskey");
let accesskey = gNavigatorBundle.getString(Social.provider.enabled ?
"social.turnOff.accesskey" :
"social.turnOn.accesskey");
toggleCommand.setAttribute("label", label);
toggleCommand.setAttribute("accesskey", accesskey);
toggleCommand.setAttribute("hidden", Social.active ? "false" : "true");
},
updateActiveBroadcaster: function SocialUI_updateActiveBroadcaster() {
let broadcaster = document.getElementById("socialActiveBroadcaster");
broadcaster.hidden = !Social.active;
_updateMenuItems: function () {
if (!Social.provider)
return;
// The View->Sidebar and Menubar->Tools menu.
for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
document.getElementById(id).setAttribute("label", Social.provider.name);
},
// This handles "ActivateSocialFeature" events fired against content documents
// in this window.
_activationEventHandler: function SocialUI_activationHandler(e) {
// Nothing to do if Social is already enabled, or we don't have a provider
// to enable yet.
if (Social.enabled || !Social.provider)
return;
let targetDoc = e.target;
// Event must be fired against the document
@ -164,9 +191,9 @@ let SocialUI = {
return;
// Check that the associated document's origin is in our whitelist
let prePath = targetDoc.documentURIObject.prePath;
let providerOrigin = targetDoc.nodePrincipal.origin;
let whitelist = Services.prefs.getCharPref("social.activation.whitelist");
if (whitelist.split(",").indexOf(prePath) == -1)
if (whitelist.split(",").indexOf(providerOrigin) == -1)
return;
// If the last event was received < 1s ago, ignore this one
@ -175,34 +202,44 @@ let SocialUI = {
return;
Social.lastEventReceived = now;
// Keep track of the old provider in case of undo
let oldOrigin = Social.provider ? Social.provider.origin : "";
// Enable the social functionality, and indicate that it was activated
Social.active = true;
let provider = Social.activateFromOrigin(providerOrigin);
// Provider to activate may not have been found
if (!provider)
return;
// Show a warning, allow undoing the activation
let description = document.getElementById("social-activation-message");
let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
let message = gNavigatorBundle.getFormattedString("social.activated.description",
[Social.provider.name, brandShortName]);
[provider.name, brandShortName]);
description.value = message;
SocialUI.notificationPanel.hidden = false;
let notificationPanel = SocialUI.notificationPanel;
// Set the origin being activated and the previously active one, to allow undo
notificationPanel.setAttribute("origin", provider.origin);
notificationPanel.setAttribute("oldorigin", oldOrigin);
// Show the panel
notificationPanel.hidden = false;
setTimeout(function () {
SocialUI.notificationPanel.openPopup(SocialToolbar.button, "bottomcenter topright");
}.bind(this), 0);
},
get notificationPanel() {
return document.getElementById("socialActivatedNotification")
notificationPanel.openPopup(SocialToolbar.button, "bottomcenter topright");
}, 0);
},
undoActivation: function SocialUI_undoActivation() {
Social.active = false;
let origin = this.notificationPanel.getAttribute("origin");
let oldOrigin = this.notificationPanel.getAttribute("oldorigin");
Social.deactivateFromOrigin(origin, oldOrigin);
this.notificationPanel.hidePopup();
},
haveLoggedInUser: function SocialUI_haveLoggedInUser() {
return !!(Social.provider && Social.provider.profile && Social.provider.profile.userName);
get notificationPanel() {
return document.getElementById("socialActivatedNotification");
},
closeSocialPanelForLinkTraversal: function (target, linkNode) {
@ -238,8 +275,9 @@ let SocialUI = {
let confirmationIndex = ps.confirmEx(null, dialogTitle, text, flags,
okButtonText, null, null, null, {});
if (confirmationIndex == 0)
Social.active = false;
if (confirmationIndex == 0) {
Social.deactivateFromOrigin(Social.provider.origin);
}
}
}
@ -250,7 +288,7 @@ let SocialChatBar = {
// Whether the chatbar is available for this window. Note that in full-screen
// mode chats are available, but not shown.
get isAvailable() {
if (!SocialUI.haveLoggedInUser())
if (!Social.haveLoggedInUser())
return false;
let docElem = document.documentElement;
let chromeless = docElem.getAttribute("chromehidden").indexOf("extrachrome") >= 0;
@ -477,10 +515,19 @@ let SocialFlyout = {
let SocialShareButton = {
// Called once, after window load, when the Social.provider object is initialized
init: function SSB_init() {
this.updateProvider();
},
// Called when the Social.provider changes
updateProvider: function () {
this.updateButtonHiddenState();
if (!Social.provider)
return;
this.updateProfileInfo();
},
// Called when the provider's profile info changes (or when the provider
// changes, via updateProvider)
updateProfileInfo: function SSB_updateProfileInfo() {
let profileRow = document.getElementById("unsharePopupHeader");
let profile = Social.provider.profile;
@ -493,9 +540,7 @@ let SocialShareButton = {
} else {
profileRow.hidden = true;
this.updateButtonHiddenState();
return;
}
this.updateShareState();
},
get shareButton() {
@ -518,8 +563,9 @@ let SocialShareButton = {
let shareButton = this.shareButton;
if (shareButton)
shareButton.hidden = !Social.uiVisible || Social.provider.recommendInfo == null ||
!SocialUI.haveLoggedInUser() ||
!Social.haveLoggedInUser() ||
!this.canSharePage(gBrowser.currentURI);
// also update the relevent command's disabled state so the keyboard
// shortcut only works when available.
let cmd = document.getElementById("Social:SharePage");
@ -577,8 +623,6 @@ let SocialShareButton = {
},
updateShareState: function SSB_updateShareState() {
// we might have been called due to a location change, and the new location
// might change the state of "can this url be shared"
this.updateButtonHiddenState();
let shareButton = this.shareButton;
@ -626,31 +670,32 @@ var SocialMenu = {
let separator = document.getElementById("socialAmbientMenuSeparator");
separator.hidden = true;
if (!Social.uiVisible)
return;
let provider = Social.provider;
if (provider && provider.enabled) {
let iconNames = Object.keys(provider.ambientNotificationIcons);
for (let name of iconNames) {
let icon = provider.ambientNotificationIcons[name];
if (!icon.label || !icon.menuURL)
continue;
separator.hidden = false;
let menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", icon.label);
menuitem.classList.add("ambient-menuitem");
menuitem.addEventListener("command", function() {
openUILinkIn(icon.menuURL, "tab");
}, false);
submenu.insertBefore(menuitem, separator);
}
let iconNames = Object.keys(provider.ambientNotificationIcons);
for (let name of iconNames) {
let icon = provider.ambientNotificationIcons[name];
if (!icon.label || !icon.menuURL)
continue;
separator.hidden = false;
let menuitem = document.createElement("menuitem");
menuitem.setAttribute("label", icon.label);
menuitem.classList.add("ambient-menuitem");
menuitem.addEventListener("command", function() {
openUILinkIn(icon.menuURL, "tab");
}, false);
submenu.insertBefore(menuitem, separator);
}
}
};
// XXX Need to audit that this is being initialized correctly
var SocialToolbar = {
// Called once, after window load, when the Social.provider object is initialized
// Called once, after window load, when the Social.provider object is
// initialized.
init: function SocialToolbar_init() {
this.button.setAttribute("image", Social.provider.iconURL);
let brandShortName = document.getElementById("bundle_brand").getString("brandShortName");
let label = gNavigatorBundle.getFormattedString("social.remove.label",
[brandShortName]);
@ -660,18 +705,29 @@ var SocialToolbar = {
removeCommand.setAttribute("label", label);
removeCommand.setAttribute("accesskey", accesskey);
this.updateProvider();
this._dynamicResizer = new DynamicResizeWatcher();
},
// Called when the Social.provider changes
updateProvider: function () {
if (!Social.provider)
return;
this.button.setAttribute("image", Social.provider.iconURL);
this.updateButton();
this.updateProfile();
this._dynamicResizer = new DynamicResizeWatcher();
this.populateProviderMenus();
},
get button() {
return document.getElementById("social-provider-button");
},
// Note: this doesn't actually handle hiding the toolbar button,
// socialActiveBroadcaster is responsible for that.
updateButtonHiddenState: function SocialToolbar_updateButtonHiddenState() {
let tbi = document.getElementById("social-toolbar-item");
let socialEnabled = Social.enabled;
let socialEnabled = Social.uiVisible;
for (let className of ["social-statusarea-separator", "social-statusarea-user"]) {
for (let element of document.getElementsByClassName(className))
element.hidden = !socialEnabled;
@ -679,7 +735,7 @@ var SocialToolbar = {
let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications");
toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
if (!SocialUI.haveLoggedInUser() || !socialEnabled) {
if (!Social.haveLoggedInUser() || !socialEnabled) {
let parent = document.getElementById("social-notification-panel");
while (parent.hasChildNodes()) {
let frame = parent.firstChild;
@ -700,9 +756,8 @@ var SocialToolbar = {
let userPortrait = profile.portrait || "chrome://global/skin/icons/information-32.png";
let userDetailsBroadcaster = document.getElementById("socialBroadcaster_userDetails");
let loggedInStatusValue = profile.userName ?
profile.userName :
userDetailsBroadcaster.getAttribute("notLoggedInLabel");;
let loggedInStatusValue = profile.userName ||
userDetailsBroadcaster.getAttribute("notLoggedInLabel");
// "image" and "label" are used by Mac's native menus that do not render the menuitem's children
// elements. "src" and "value" are used by the image/label children on the other platforms.
@ -713,6 +768,7 @@ var SocialToolbar = {
userDetailsBroadcaster.setAttribute("label", loggedInStatusValue);
},
// XXX doesn't this need to be called for profile changes, given its use of provider.profile?
updateButton: function SocialToolbar_updateButton() {
this.updateButtonHiddenState();
let provider = Social.provider;
@ -728,8 +784,8 @@ var SocialToolbar = {
const CACHE_PREF_NAME = "social.cached.ambientNotificationIcons";
// provider.profile == undefined means no response yet from the provider
// to tell us whether the user is logged in or not.
if (!Social.provider || !Social.provider.enabled ||
(!SocialUI.haveLoggedInUser() && provider.profile !== undefined)) {
if (!provider.enabled ||
(!Social.haveLoggedInUser() && provider.profile !== undefined)) {
// Either no enabled provider, or there is a provider and it has
// responded with a profile and the user isn't loggedin. The icons
// etc have already been removed by updateButtonHiddenState, so we want
@ -931,6 +987,41 @@ var SocialToolbar = {
encodeURIComponent(src), null, null, null, null);
let panel = aNotificationFrame.parentNode;
sizeSocialPanelToContent(panel, aNotificationFrame);
},
populateProviderMenus: function SocialToolbar_renderProviderMenus() {
let providerMenuSeps = document.getElementsByClassName("social-provider-menu");
let activeProviders = [p for (p of Social.providers) if (p.active)];
for (let providerMenuSep of providerMenuSeps)
this._populateProviderMenu(providerMenuSep, activeProviders);
},
_populateProviderMenu: function SocialToolbar_renderProviderMenu(providerMenuSep, providers) {
let menu = providerMenuSep.parentNode;
// selectable providers are inserted before the provider-menu seperator,
// remove any menuitems in that area
while (providerMenuSep.previousSibling.nodeName == "menuitem") {
menu.removeChild(providerMenuSep.previousSibling);
}
// only show a selection if there is more than one
if (!Social.enabled || providers.length < 2) {
providerMenuSep.hidden = true;
return;
}
for (let provider of providers) {
let menuitem = document.createElement("menuitem");
menuitem.className = "menuitem-iconic social-provider-menuitem";
menuitem.setAttribute("image", provider.iconURL);
menuitem.setAttribute("label", provider.name);
menuitem.setAttribute("origin", provider.origin);
if (provider == Social.provider) {
menuitem.setAttribute("checked", "true");
} else {
menuitem.setAttribute("oncommand", "Social.setProviderByOrigin(this.getAttribute('origin'));");
}
menu.insertBefore(menuitem, providerMenuSep);
}
providerMenuSep.hidden = false;
}
}
@ -996,6 +1087,12 @@ var SocialSidebar = {
command.setAttribute("checked", !hideSidebar);
let sbrowser = document.getElementById("social-sidebar-browser");
if (Social.provider)
sbrowser.setAttribute("origin", Social.provider.origin);
else
sbrowser.removeAttribute("origin");
if (hideSidebar) {
sbrowser.removeEventListener("load", SocialSidebar._loadListener, true);
this.setSidebarVisibilityState(false);
@ -1011,14 +1108,13 @@ var SocialSidebar = {
);
}
} else {
if (Social.errorState == "frameworker-error") {
if (Social.provider.errorState == "frameworker-error") {
SocialSidebar.setSidebarErrorMessage("frameworker-error");
return;
}
// Make sure the right sidebar URL is loaded
if (sbrowser.getAttribute("origin") != Social.provider.origin) {
sbrowser.setAttribute("origin", Social.provider.origin);
if (sbrowser.getAttribute("src") != Social.provider.sidebarURL) {
sbrowser.setAttribute("src", Social.provider.sidebarURL);
sbrowser.addEventListener("load", SocialSidebar._loadListener, true);
} else {
@ -1094,7 +1190,7 @@ SocialErrorListener.prototype = {
onLocationChange: function SPL_onLocationChange(aWebProgress, aRequest, aLocation, aFlags) {
let failure = aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_ERROR_PAGE;
if (failure && Social.errorState != "frameworker-error") {
if (failure && Social.provider.errorState != "frameworker-error") {
aRequest.cancel(Components.results.NS_BINDING_ABORTED);
window.setTimeout(function(self) {
self.setErrorMessage(aWebProgress);
@ -1114,7 +1210,7 @@ SocialErrorListener.prototype = {
case "sidebar":
// a frameworker error "trumps" a sidebar error.
let reason = Social.errorState ? Social.errorState : "sidebar-error";
let reason = Social.provider.errorState || "sidebar-error";
SocialSidebar.setSidebarErrorMessage(reason);
break;

View File

@ -194,7 +194,7 @@
<button id="social-undoactivation-button"
label="&social.activated.undobutton.label;"
accesskey="&social.activated.undobutton.accesskey;"
onclick="SocialUI.undoActivation();"/>
onclick="SocialUI.undoActivation(this);"/>
<button default="true"
autofocus="autofocus"
label="&social.ok.label;"
@ -209,7 +209,7 @@
<button id="social-undoactivation-button"
label="&social.activated.undobutton.label;"
accesskey="&social.activated.undobutton.accesskey;"
onclick="SocialUI.undoActivation();"/>
onclick="SocialUI.undoActivation(this);"/>
#endif
</hbox>
</vbox>
@ -679,6 +679,7 @@
label="&social.toggleNotifications.label;"
accesskey="&social.toggleNotifications.accesskey;"/>
<menuseparator class="social-statusarea-separator"/>
<menuseparator class="social-provider-menu" hidden="true"/>
<menuitem class="social-toggle-menuitem" command="Social:Toggle"/>
<menuitem class="social-remove-menuitem" command="Social:Remove"/>
</menupopup>

View File

@ -294,6 +294,7 @@ _BROWSER_FILES = \
browser_social_mozSocial_API.js \
browser_social_isVisible.js \
browser_social_chatwindow.js \
browser_social_multiprovider.js \
social_panel.html \
social_share_image.png \
social_sidebar.html \

View File

@ -0,0 +1,104 @@
/* 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/. */
function test() {
waitForExplicitFinish();
runSocialTestWithProvider(gProviders, function (finishcb) {
runSocialTests(tests, undefined, undefined, finishcb);
});
}
let gProviders = [
{
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social_sidebar.html?provider1",
workerURL: "https://example.com/browser/browser/base/content/test/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
},
{
name: "provider 2",
origin: "https://test1.example.com",
sidebarURL: "https://test1.example.com/browser/browser/base/content/test/social_sidebar.html?provider2",
workerURL: "https://test1.example.com/browser/browser/base/content/test/social_worker.js",
iconURL: "chrome://branding/content/icon48.png"
}
];
var tests = {
testProviderSwitch: function(next) {
function checkProviderMenu(selectedProvider) {
let menu = document.getElementById("social-statusarea-popup");
let menuProviders = menu.querySelectorAll(".social-provider-menuitem");
is(menuProviders.length, gProviders.length, "correct number of providers listed in the menu");
// Find the selectedProvider's menu item
let el = menu.getElementsByAttribute("origin", selectedProvider.origin);
is(el.length, 1, "selected provider menu item exists");
is(el[0].getAttribute("checked"), "true", "selected provider menu item is checked");
}
checkProviderMenu(gProviders[0]);
// Now wait for the initial provider profile to be set
waitForProviderLoad(function() {
checkUIStateMatchesProvider(gProviders[0]);
// Now activate "provider 2"
observeProviderSet(function () {
waitForProviderLoad(function() {
checkUIStateMatchesProvider(gProviders[1]);
next();
});
});
Social.activateFromOrigin("https://test1.example.com");
});
}
}
function checkUIStateMatchesProvider(provider) {
let profileData = getExpectedProfileData(provider);
// Bug 789863 - share button uses 'displayName', toolbar uses 'userName'
// Check the "share button"
let displayNameEl = document.getElementById("socialUserDisplayName");
is(displayNameEl.getAttribute("label"), profileData.displayName, "display name matches provider profile");
// The toolbar
let loginStatus = document.getElementsByClassName("social-statusarea-loggedInStatus");
for (let label of loginStatus) {
is(label.value, profileData.userName, "username name matches provider profile");
}
// Sidebar
is(document.getElementById("social-sidebar-browser").getAttribute("src"), provider.sidebarURL, "side bar URL is set");
}
function getExpectedProfileData(provider) {
// This data is defined in social_worker.js
if (provider.origin == "https://test1.example.com") {
return {
displayName: "Test1 User",
userName: "tester"
};
}
return {
displayName: "Kuma Lisa",
userName: "trickster"
};
}
function observeProviderSet(cb) {
Services.obs.addObserver(function providerSet(subject, topic, data) {
Services.obs.removeObserver(providerSet, "social:provider-set");
info("social:provider-set observer was notified");
// executeSoon to let the browser UI observers run first
executeSoon(cb);
}, "social:provider-set", false);
}
function waitForProviderLoad(cb) {
waitForCondition(function() {
return Social.provider.profile &&
Social.provider.profile.displayName;
}, cb, "waitForProviderLoad: provider profile was not set");
}

View File

@ -57,9 +57,14 @@ function doTest(finishcb) {
Social.toggleSidebar();
});
// Now toggle it off
info("Toggling sidebar off");
Social.toggleSidebar();
// Wait until the side bar loads
waitForCondition(function () {
return document.getElementById("social-sidebar-browser").docShellIsActive;
}, function () {
// Now toggle it off
info("Toggling sidebar off");
Social.toggleSidebar();
});
}
// XXX test sidebar in popup

View File

@ -113,50 +113,60 @@ function getTestPlugin() {
function runSocialTestWithProvider(manifest, callback) {
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
let manifests = Array.isArray(manifest) ? manifest : [manifest];
// Check that none of the provider's content ends up in history.
registerCleanupFunction(function () {
for (let what of ['sidebarURL', 'workerURL', 'iconURL']) {
if (manifest[what]) {
ensureSocialUrlNotRemembered(manifest[what]);
manifests.forEach(function (m) {
for (let what of ['sidebarURL', 'workerURL', 'iconURL']) {
if (m[what]) {
ensureSocialUrlNotRemembered(m[what]);
}
}
}
});
});
info("runSocialTestWithProvider: " + manifest.toSource());
info("runSocialTestWithProvider: " + manifests.toSource());
let oldProvider;
SocialService.addProvider(manifest, function(provider) {
info("runSocialTestWithProvider: provider added");
oldProvider = Social.provider;
Social.provider = provider;
let providersAdded = 0;
let firstProvider;
manifests.forEach(function (m) {
SocialService.addProvider(m, function(provider) {
provider.active = true;
// Now that we've set the UI's provider, enable the social functionality
Services.prefs.setBoolPref("social.enabled", true);
Services.prefs.setBoolPref("social.active", true);
providersAdded++;
info("runSocialTestWithProvider: provider added");
// Need to re-call providerReady since it is actually called before the test
// framework is loaded and the provider state won't be set in the browser yet.
SocialUI._providerReady();
registerCleanupFunction(function () {
// if one test happens to fail, it is likely finishSocialTest will not
// be called, causing most future social tests to also fail as they
// attempt to add a provider which already exists - so work
// around that by also attempting to remove the test provider.
try {
SocialService.removeProvider(provider.origin, finish);
} catch (ex) {
;
// we want to set the first specified provider as the UI's provider
if (provider.origin == manifests[0].origin) {
firstProvider = provider;
}
Social.provider = oldProvider;
Services.prefs.clearUserPref("social.enabled");
Services.prefs.clearUserPref("social.active");
});
function finishSocialTest() {
SocialService.removeProvider(provider.origin, finish);
}
callback(finishSocialTest);
// If we've added all the providers we need, call the callback to start
// the tests (and give it a callback it can call to finish them)
if (providersAdded == manifests.length) {
// Set the UI's provider and enable the feature
Social.provider = firstProvider;
Social.enabled = true;
registerCleanupFunction(function () {
// if one test happens to fail, it is likely finishSocialTest will not
// be called, causing most future social tests to also fail as they
// attempt to add a provider which already exists - so work
// around that by also attempting to remove the test provider.
manifests.forEach(function (m) {
try {
SocialService.removeProvider(m.origin, finish);
} catch (ex) {}
});
Services.prefs.clearUserPref("social.enabled");
});
function finishSocialTest() {
SocialService.removeProvider(provider.origin, finish);
}
callback(finishSocialTest);
}
});
});
}

View File

@ -81,13 +81,24 @@ onconnect = function(e) {
break;
case "social.initialize":
// This is the workerAPI port, respond and set up a notification icon.
// For multiprovider tests, we support acting like different providers
// based on the domain we load from.
apiPort = port;
let profile = {
portrait: "https://example.com/portrait.jpg",
userName: "trickster",
displayName: "Kuma Lisa",
profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
};
let profile;
if (location.href.indexOf("https://test1.example.com") == 0) {
profile = {
portrait: "https://test1.example.com/portrait.jpg",
userName: "tester",
displayName: "Test1 User",
};
} else {
profile = {
portrait: "https://example.com/portrait.jpg",
userName: "trickster",
displayName: "Kuma Lisa",
profileURL: "http://en.wikipedia.org/wiki/Kuma_Lisa"
};
}
port.postMessage({topic: "social.user-profile", data: profile});
break;
case "test-ambient-notification":

View File

@ -18,28 +18,115 @@ XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
this.Social = {
lastEventReceived: 0,
provider: null,
providers: null,
_disabledForSafeMode: false,
get _currentProviderPref() {
try {
return Services.prefs.getComplexValue("social.provider.current",
Ci.nsISupportsString).data;
} catch (ex) {}
return null;
},
set _currentProviderPref(val) {
let string = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
string.data = val;
Services.prefs.setComplexValue("social.provider.current",
Ci.nsISupportsString, string);
},
_provider: null,
get provider() {
return this._provider;
},
set provider(val) {
// Changes triggered by the public setter should notify of an engine change.
this._setProvider(val, true);
},
// Sets the current provider and enables and activates it. Also disables the
// previously set provider, and optionally notifies observers of the change.
_setProvider: function (provider, notify) {
if (this._provider == provider)
return;
if (provider && !provider.active)
throw new Error("Social.provider cannot be set to an inactive provider.");
// Disable the previous provider, if any, since we want only one provider to
// be enabled at once.
if (this._provider)
this._provider.enabled = false;
this._provider = provider;
if (this._provider) {
if (this.enabled)
this._provider.enabled = true;
this._currentProviderPref = this._provider.origin;
} else {
Services.prefs.clearUserPref("social.provider.current");
}
if (notify) {
let origin = this._provider && this._provider.origin;
Services.obs.notifyObservers(null, "social:provider-set", origin);
}
},
init: function Social_init(callback) {
this._disabledForSafeMode = Services.appinfo.inSafeMode && this.enabled;
if (this.provider) {
if (this.providers) {
schedule(callback);
return;
}
if (!this._addedPrivateBrowsingObserver) {
if (!this._addedObservers) {
Services.obs.addObserver(this, "private-browsing", false);
this._addedPrivateBrowsingObserver = true;
Services.obs.addObserver(this, "social:pref-changed", false);
this._addedObservers = true;
}
// Eventually this might want to retrieve a specific provider, but for now
// just use the first available.
// Retrieve the current set of providers, and set the current provider.
SocialService.getProviderList(function (providers) {
if (providers.length)
this.provider = providers[0];
// We don't want to notify about a provider change when we're setting
// this.provider for the first time, so pass false here.
this._updateProviderCache(providers, false);
callback();
}.bind(this));
// Register an observer for changes to the provider list
SocialService.registerProviderListener(function providerListener(topic, data) {
// An engine change caused by adding/removing a provider should notify
if (topic == "provider-added" || topic == "provider-removed")
this._updateProviderCache(data, true);
}.bind(this));
},
// Called to update our cache of providers and set the current provider
_updateProviderCache: function (providers, notifyProviderChange) {
this.providers = providers;
// Set our current provider
let currentProviderPref = this._currentProviderPref;
let currentProvider;
if (this._currentProviderPref) {
currentProvider = this._getProviderFromOrigin(this._currentProviderPref);
} else {
// Migrate data from previous single-provider builds where we used
// social.active to indicate that the first available provider should be
// used.
try {
let active = Services.prefs.getBoolPref("social.active");
if (active) {
Services.prefs.clearUserPref("social.active");
currentProvider = providers[0];
}
} catch(ex) {}
}
this._setProvider(currentProvider, notifyProviderChange);
},
observe: function(aSubject, aTopic, aData) {
@ -56,6 +143,11 @@ this.Social = {
this.enabled = false;
this.enabled = this._enabledBeforePrivateBrowsing;
}
} else if (aTopic == "social:pref-changed") {
// Make sure our provider's enabled state matches the overall state of the
// social components.
if (this.provider)
this.provider.enabled = this.enabled;
}
},
@ -64,9 +156,6 @@ this.Social = {
},
set enabled(val) {
if (!val) {
delete this.errorState;
}
SocialService.enabled = val;
},
get enabled() {
@ -74,11 +163,7 @@ this.Social = {
},
get active() {
return Services.prefs.getBoolPref("social.active");
},
set active(val) {
this.enabled = !!val;
Services.prefs.setBoolPref("social.active", !!val);
return this.provider && this.providers.some(function (p) p.active);
},
toggle: function Social_toggle() {
@ -96,6 +181,52 @@ this.Social = {
Services.prefs.setBoolPref("social.toast-notifications.enabled", !prefValue);
},
haveLoggedInUser: function () {
return !!(this.provider && this.provider.profile && this.provider.profile.userName);
},
setProviderByOrigin: function (origin) {
this.provider = this._getProviderFromOrigin(origin);
},
_getProviderFromOrigin: function (origin) {
for (let p of this.providers) {
if (p.origin == origin) {
return p;
}
}
return null;
},
// Activation functionality
activateFromOrigin: function (origin) {
let provider = this._getProviderFromOrigin(origin);
if (provider) {
// No need to activate again if we're already active
if (provider == this.provider && provider.active)
return null;
provider.active = true;
this.provider = provider;
Social.enabled = true;
}
return provider;
},
deactivateFromOrigin: function (origin, oldOrigin) {
let provider = this._getProviderFromOrigin(origin);
if (provider && provider == this.provider) {
this.provider.active = false;
// Set the provider to the previously-selected provider (in case of undo),
// or to the first available provider otherwise.
this.provider = this._getProviderFromOrigin(oldOrigin);
if (!this.provider)
this.provider = this.providers.filter(function (p) p.active)[0];
if (!this.provider) // Still no provider found, disable
this.enabled = false;
}
},
// Sharing functionality
_getShareablePageUrl: function Social_getShareablePageUrl(aURI) {
let uri = aURI.clone();

View File

@ -14,9 +14,14 @@
"use strict";
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/MessagePortBase.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
"resource://gre/modules/SocialService.jsm");
this.EXPORTED_SYMBOLS = ["getFrameWorkerHandle"];
var workerCache = {}; // keyed by URL.
@ -25,7 +30,7 @@ var _nextPortId = 1;
// Retrieves a reference to a WorkerHandle associated with a FrameWorker and a
// new ClientPort.
this.getFrameWorkerHandle =
function getFrameWorkerHandle(url, clientWindow, name) {
function getFrameWorkerHandle(url, clientWindow, name, origin) {
// first create the client port we are going to use. Later we will
// message the worker to create the worker port.
let portid = _nextPortId++;
@ -34,7 +39,7 @@ this.getFrameWorkerHandle =
let existingWorker = workerCache[url];
if (!existingWorker) {
// setup the worker and add this connection to the pending queue
let worker = new FrameWorker(url, name);
let worker = new FrameWorker(url, name, origin);
worker.pendingPorts.push(clientPort);
existingWorker = workerCache[url] = worker;
} else {
@ -64,13 +69,14 @@ this.getFrameWorkerHandle =
* the script does not have a full DOM but is instead run in a sandbox
* that has a select set of methods cloned from the URL's domain.
*/
function FrameWorker(url, name) {
function FrameWorker(url, name, origin) {
this.url = url;
this.name = name || url;
this.ports = {};
this.pendingPorts = [];
this.loaded = false;
this.reloading = false;
this.origin = origin;
this.frame = makeHiddenFrame();
this.load();
@ -192,7 +198,7 @@ FrameWorker.prototype = {
let scriptText = workerWindow.document.body.textContent.trim();
if (!scriptText) {
Cu.reportError("FrameWorker: Empty worker script received");
Services.obs.notifyObservers(null, "social:frameworker-error", worker.url);
notifyWorkerError(worker);
return;
}
@ -204,7 +210,7 @@ FrameWorker.prototype = {
}
catch (e) {
Cu.reportError("FrameWorker: Error injecting port code into content side of the worker: " + e + "\n" + e.stack);
Services.obs.notifyObservers(null, "social:frameworker-error", worker.url);
notifyWorkerError(worker);
return;
}
@ -214,7 +220,7 @@ FrameWorker.prototype = {
}
catch (e) {
Cu.reportError("FrameWorker: Error setting up event listener for chrome side of the worker: " + e + "\n" + e.stack);
Services.obs.notifyObservers(null, "social:frameworker-error", worker.url);
notifyWorkerError();
return;
}
@ -225,7 +231,7 @@ FrameWorker.prototype = {
Cu.reportError("FrameWorker: Error evaluating worker script for " + worker.name + ": " + e + "; " +
(e.lineNumber ? ("Line #" + e.lineNumber) : "") +
(e.stack ? ("\n" + e.stack) : ""));
Services.obs.notifyObservers(null, "social:frameworker-error", worker.url);
notifyWorkerError(worker);
return;
}
@ -354,7 +360,7 @@ function initClientMessageHandler(worker, workerWindow) {
case "port-connection-error":
// onconnect failed, we cannot connect the port, the worker has
// become invalid
Services.obs.notifyObservers(null, "social:frameworker-error", worker.url);
notifyWorkerError(worker);
break;
case "port-close":
// the worker side of the port was closed, so close this side too.
@ -470,3 +476,13 @@ ClientPort.prototype = {
// port will still get "entangled" quickly enough to deliver the messages.
}
}
function notifyWorkerError(worker) {
// Try to retrieve the worker's associated provider, if it has one, to set its
// error state.
SocialService.getProvider(worker.origin, function (provider) {
if (provider)
provider.errorState = "frameworker-error";
Services.obs.notifyObservers(null, "social:frameworker-error", worker.origin);
});
}

View File

@ -12,6 +12,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "getFrameWorkerHandle", "resource://gre/modules/FrameWorker.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WorkerAPI", "resource://gre/modules/WorkerAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MozSocialAPI", "resource://gre/modules/MozSocialAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", "resource://gre/modules/DeferredTask.jsm");
/**
* The SocialService is the public API to social providers - it tracks which
@ -27,6 +28,50 @@ let SocialServiceInternal = {
}
};
let 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.start();
},
delete: function (origin) {
delete this._providers[origin];
this._deferredTask.start();
},
flush: function () {
this._deferredTask.flush();
},
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 initService() {
// Add a pref observer for the enabled state
function prefObserver(subject, topic, data) {
@ -34,6 +79,8 @@ function initService() {
}
Services.prefs.addObserver("social.enabled", prefObserver, false);
Services.obs.addObserver(function xpcomShutdown() {
ActiveProviders.flush();
SocialService._providerListeners = null;
Services.obs.removeObserver(xpcomShutdown, "xpcom-shutdown");
Services.prefs.removeObserver("social.enabled", prefObserver);
}, "xpcom-shutdown", false);
@ -65,7 +112,7 @@ XPCOMUtils.defineLazyGetter(SocialServiceInternal, "providers", function () {
try {
var manifest = JSON.parse(MANIFEST_PREFS.getCharPref(pref));
if (manifest && typeof(manifest) == "object") {
let provider = new SocialProvider(manifest, appinfo.inSafeMode ? false : SocialServiceInternal.enabled);
let provider = new SocialProvider(manifest);
providers[provider.origin] = provider;
}
} catch (err) {
@ -99,10 +146,12 @@ this.SocialService = {
this._setEnabled(enable);
},
_setEnabled: function _setEnabled(enable) {
if (!enable)
SocialServiceInternal.providerArray.forEach(function (p) p.enabled = false);
if (enable == SocialServiceInternal.enabled)
return;
SocialServiceInternal.providerArray.forEach(function (p) p.enabled = enable);
SocialServiceInternal.enabled = enable;
MozSocialAPI.enabled = enable;
Services.obs.notifyObservers(null, "social:pref-changed", enable ? "enabled" : "disabled");
@ -114,12 +163,15 @@ this.SocialService = {
if (SocialServiceInternal.providers[manifest.origin])
throw new Error("SocialService.addProvider: provider with this origin already exists");
let provider = new SocialProvider(manifest, SocialServiceInternal.enabled);
let provider = new SocialProvider(manifest);
SocialServiceInternal.providers[provider.origin] = provider;
schedule(function () {
onDone(provider);
});
this._notifyProviderListeners("provider-added",
SocialServiceInternal.providerArray);
if (onDone)
onDone(provider);
}.bind(this));
},
// Removes a provider with the given origin, and notifies when the removal is
@ -131,10 +183,16 @@ this.SocialService = {
let provider = SocialServiceInternal.providers[origin];
provider.enabled = false;
ActiveProviders.delete(provider.origin);
delete SocialServiceInternal.providers[origin];
if (onDone)
schedule(onDone);
schedule(function () {
this._notifyProviderListeners("provider-removed",
SocialServiceInternal.providerArray);
if (onDone)
onDone();
}.bind(this));
},
// Returns a single provider object with the specified origin.
@ -149,6 +207,24 @@ this.SocialService = {
schedule(function () {
onDone(SocialServiceInternal.providerArray);
});
},
_providerListeners: new Map(),
registerProviderListener: function registerProviderListener(listener) {
this._providerListeners.set(listener, 1);
},
unregisterProviderListener: function unregisterProviderListener(listener) {
this._providerListeners.delete(listener);
},
_notifyProviderListeners: function (topic, data) {
for (let [listener, ] of this._providerListeners) {
try {
listener(topic, data);
} catch (ex) {
Components.utils.reportError("SocialService: provider listener threw an exception: " + ex);
}
}
}
};
@ -158,9 +234,8 @@ this.SocialService = {
*
* @constructor
* @param {jsobj} object representing the manifest file describing this provider
* @param {bool} whether the provider should be initially enabled (defaults to true)
*/
function SocialProvider(input, enabled) {
function SocialProvider(input) {
if (!input.name)
throw new Error("SocialProvider must be passed a name");
if (!input.origin)
@ -172,17 +247,14 @@ function SocialProvider(input, enabled) {
this.sidebarURL = input.sidebarURL;
this.origin = input.origin;
this.ambientNotificationIcons = {};
// If enabled is |undefined|, default to true.
this._enabled = !(enabled == false);
if (this._enabled)
this._activate();
this.errorState = null;
this._active = ActiveProviders.has(this.origin);
}
SocialProvider.prototype = {
// Provider enabled/disabled state. Disabled providers do not have active
// connections to their FrameWorkers.
_enabled: true,
_enabled: false,
get enabled() {
return this._enabled;
},
@ -200,6 +272,18 @@ SocialProvider.prototype = {
}
},
_active: false,
get active() {
return this._active;
},
set active(val) {
this._active = val;
if (val)
ActiveProviders.add(this.origin);
else
ActiveProviders.delete(this.origin);
},
// Reference to a workerAPI object for this provider. Null if the provider has
// no FrameWorker, or is disabled.
workerAPI: null,
@ -339,7 +423,7 @@ SocialProvider.prototype = {
_terminate: function _terminate() {
if (this.workerURL) {
try {
getFrameWorkerHandle(this.workerURL, null).terminate();
getFrameWorkerHandle(this.workerURL).terminate();
} catch (e) {
Cu.reportError("SocialProvider FrameWorker termination failed: " + e);
}
@ -347,6 +431,7 @@ SocialProvider.prototype = {
if (this.workerAPI) {
this.workerAPI.terminate();
}
this.errorState = null;
this.workerAPI = null;
this.profile = undefined;
},
@ -362,6 +447,7 @@ SocialProvider.prototype = {
getWorkerPort: function getWorkerPort(window) {
if (!this.workerURL || !this.enabled)
return null;
return getFrameWorkerHandle(this.workerURL, window).port;
return getFrameWorkerHandle(this.workerURL, window,
"SocialProvider:" + this.origin, this.origin).port;
}
}

View File

@ -14,6 +14,9 @@ function test() {
ensureSocialEnabled();
SocialService.addProvider(manifest, function (provider) {
// enable the provider
provider.enabled = true;
ok(provider.enabled, "provider is initially enabled");
let port = provider.getWorkerPort();
ok(port, "should be able to get a port from enabled provider");

View File

@ -464,17 +464,17 @@ let tests = {
ioService.offline = true;
}
},
testMissingWorker: function(cbnext) {
let worker = getFrameWorkerHandle(url, undefined, "testMissingWorker");
Services.obs.addObserver(function handleError() {
Services.obs.removeObserver(handleError, "social:frameworker-error");
ok(true, "social:frameworker-error was handled");
worker.terminate();
cbnext();
}, 'social:frameworker-error', false);
// don't ever create this file! We want a 404.
let url = "https://example.com/browser/toolkit/components/social/test/browser/worker_is_missing.js";
let worker = getFrameWorkerHandle(url, undefined, "testMissingWorker");
Services.obs.addObserver(function handleError(subj, topic, data) {
Services.obs.removeObserver(handleError, "social:frameworker-error");
is(data, worker._worker.origin, "social:frameworker-error was handled");
worker.terminate();
cbnext();
}, 'social:frameworker-error', false);
worker.port.onmessage = function(e) {
ok(false, "social:frameworker-error was handled");
cbnext();
@ -484,11 +484,11 @@ let tests = {
testNoConnectWorker: function(cbnext) {
let worker = getFrameWorkerHandle(makeWorkerUrl(function () {}),
undefined, "testNoConnectWorker");
Services.obs.addObserver(function handleError() {
Services.obs.addObserver(function handleError(subj, topic, data) {
Services.obs.removeObserver(handleError, "social:frameworker-error");
ok(true, "social:frameworker-error was handled");
worker.terminate();
cbnext();
is(data, worker._worker.origin, "social:frameworker-error was handled");
worker.terminate();
cbnext();
}, 'social:frameworker-error', false);
worker.port.onmessage = function(e) {
ok(false, "social:frameworker-error was handled");
@ -499,11 +499,11 @@ let tests = {
testEmptyWorker: function(cbnext) {
let worker = getFrameWorkerHandle("data:application/javascript;charset=utf-8,",
undefined, "testEmptyWorker");
Services.obs.addObserver(function handleError() {
Services.obs.addObserver(function handleError(subj, topic, data) {
Services.obs.removeObserver(handleError, "social:frameworker-error");
ok(true, "social:frameworker-error was handled");
worker.terminate();
cbnext();
is(data, worker._worker.origin, "social:frameworker-error was handled");
worker.terminate();
cbnext();
}, 'social:frameworker-error', false);
worker.port.onmessage = function(e) {
ok(false, "social:frameworker-error was handled");
@ -519,11 +519,11 @@ let tests = {
}
let worker = getFrameWorkerHandle(makeWorkerUrl(run),
undefined, "testWorkerConnectError");
Services.obs.addObserver(function handleError() {
Services.obs.addObserver(function handleError(subj, topic, data) {
Services.obs.removeObserver(handleError, "social:frameworker-error");
ok(true, "social:frameworker-error was handled");
worker.terminate();
cbnext();
is(data, worker._worker.origin, "social:frameworker-error was handled");
worker.terminate();
cbnext();
}, 'social:frameworker-error', false);
worker.port.onmessage = function(e) {
ok(false, "social:frameworker-error was handled");

View File

@ -79,6 +79,7 @@ function ensureProvider(workerFunction, cb) {
ensureSocialEnabled();
SocialService.addProvider(manifest, function (p) {
p.enabled = true;
cb(p);
});
}
@ -133,6 +134,7 @@ let tests = {
Services.obs.addObserver(observer, "social-test:notification-alert", false);
let port = provider.getWorkerPort();
ok(port, "got port from worker");
port.onmessage = function(e) {
if (e.data.topic == "test.done") {
ok(e.data.data, "check the test worked");

View File

@ -16,6 +16,7 @@ function test() {
ensureSocialEnabled();
SocialService.addProvider(manifest, function (p) {
p.enabled = true;
provider = p;
runTests(tests, undefined, undefined, function () {
SocialService.removeProvider(provider.origin, finish);

View File

@ -54,18 +54,19 @@ function testGetProviderList(manifests, next) {
let providerIdx = providers.map(function (p) p.origin).indexOf(manifests[i].origin);
let provider = providers[providerIdx];
do_check_true(!!provider);
do_check_true(provider.enabled);
do_check_false(provider.enabled);
do_check_eq(provider.workerURL, manifests[i].workerURL);
do_check_eq(provider.name, manifests[i].name);
}
}
function testEnabled(manifests, next) {
// Check that providers are disabled by default
let providers = yield SocialService.getProviderList(next);
do_check_true(providers.length >= manifests.length);
do_check_true(SocialService.enabled);
providers.forEach(function (provider) {
do_check_true(provider.enabled);
do_check_false(provider.enabled);
});
let notificationDisabledCorrect = false;
@ -74,12 +75,16 @@ function testEnabled(manifests, next) {
notificationDisabledCorrect = data == "disabled";
}, "social:pref-changed", false);
// enable one of the added providers
providers[providers.length-1].enabled = true;
// now disable the service and check that it disabled that provider (and all others for good measure)
SocialService.enabled = false;
do_check_true(notificationDisabledCorrect);
do_check_true(!Services.prefs.getBoolPref("social.enabled"));
do_check_true(!SocialService.enabled);
providers.forEach(function (provider) {
do_check_true(!provider.enabled);
do_check_false(provider.enabled);
});
// Check that setting the pref directly updates things accordingly
@ -93,8 +98,9 @@ function testEnabled(manifests, next) {
do_check_true(notificationEnabledCorrect);
do_check_true(SocialService.enabled);
// Enabling the service should not enable providers
providers.forEach(function (provider) {
do_check_true(provider.enabled);
do_check_false(provider.enabled);
});
}

View File

@ -60,7 +60,6 @@ const PREFS_WHITELIST = [
"print.",
"privacy.",
"security.",
"social.active",
"social.enabled",
"svg.",
"toolkit.startup.recent_crashes",