Bug 808215 - Disable social API in private windows for per-window Private Browsing builds. r=jaws,mixedpuppy, a=bajaj.
authorMark Hammond <mhammond@skippinet.com.au>
Wed, 06 Feb 2013 16:53:39 +1100
changeset 127429 ad19895208163464eee7694b9744b3e9ed2478b5
parent 127428 4af2713c8f5e0b4187fa04ad1977784429b29276
child 127430 6008ec29e703f3f1e2d9f60ad3681a7c2c79b016
push id2151
push userlsblakk@mozilla.com
push dateTue, 19 Feb 2013 18:06:57 +0000
treeherdermozilla-beta@4952e88741ec [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjaws, mixedpuppy, bajaj
bugs808215
milestone20.0a2
Bug 808215 - Disable social API in private windows for per-window Private Browsing builds. r=jaws,mixedpuppy, a=bajaj.
browser/base/content/browser-social.js
browser/base/content/test/social/Makefile.in
browser/base/content/test/social/browser_social.js
browser/base/content/test/social/browser_social_globalPB.js
browser/base/content/test/social/browser_social_perwindowPB.js
browser/base/content/test/social/head.js
browser/modules/Makefile.in
browser/modules/Social.jsm
toolkit/components/social/Makefile.in
toolkit/components/social/MozSocialAPI.jsm
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -13,16 +13,21 @@ let SocialUI = {
   // 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);
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    // this observer is necessary so things are also correctly updated
+    // when per-window PB isn't active
+    Services.obs.addObserver(this, "private-browsing", false);
+#endif
 
     Services.prefs.addObserver("social.sidebar.open", this, false);
     Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler, true, true);
 
     // Called when we enter DOM full-screen mode.
     window.addEventListener("mozfullscreenchange", function () {
@@ -36,16 +41,19 @@ let SocialUI = {
   // Called on window unload
   uninit: function SocialUI_uninit() {
     Services.obs.removeObserver(this, "social:pref-changed");
     Services.obs.removeObserver(this, "social:ambient-notification-changed");
     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");
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+    Services.obs.removeObserver(this, "private-browsing");
+#endif
 
     Services.prefs.removeObserver("social.sidebar.open", this);
     Services.prefs.removeObserver("social.toast-notifications.enabled", this);
   },
 
   // Called once, after window load, once Social.jsm's provider has been set.
   _providerReady: function SocialUI_providerReady() {
     this._updateActiveUI();
@@ -115,28 +123,36 @@ let SocialUI = {
           }
           break;
         case "social:recommend-info-changed":
           if (this._matchesCurrentProvider(data)) {
             SocialShareButton.updateShareState();
           }
           break;
         case "social:frameworker-error":
-          if (Social.provider && Social.provider.origin == data) {
+          if (this.enabled && Social.provider.origin == data) {
             SocialSidebar.setSidebarErrorMessage("frameworker-error");
           }
           break;
 
         case "nsPref:changed":
           if (data == "social.sidebar.open") {
             SocialSidebar.update();
           } else if (data == "social.toast-notifications.enabled") {
             SocialToolbar.updateButton();
           }
           break;
+
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+        case "private-browsing":
+          this._updateEnabledState();
+          this._updateActiveUI();
+          SocialToolbar.init();
+          break;
+#endif
       }
     } catch (e) {
       Components.utils.reportError(e + "\n" + e.stack);
       throw e;
     }
   },
 
   nonBrowserWindowInit: function SocialUI_nonBrowserInit() {
@@ -150,34 +166,41 @@ let SocialUI = {
       openUILinkIn(Social.provider.profile.profileURL, "tab");
     else {
       // XXX Bug 789585 will implement an API for provider-specified login pages.
       openUILinkIn(Social.provider.origin, "tab");
     }
   },
 
   _updateActiveUI: function SocialUI_updateActiveUI() {
+    // The "active" UI isn't dependent on there being a provider, just on
+    // social being "active" (but also chromeless/PB)
+    let enabled = Social.active && !this._chromeless
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+                  && !PrivateBrowsingUtils.isWindowPrivate(window)
+#endif
+        ;
     let broadcaster = document.getElementById("socialActiveBroadcaster");
-    broadcaster.hidden = !Social.active;
+    broadcaster.hidden = !enabled;
 
     if (!Social.provider)
       return;
 
     let toggleCommand = document.getElementById("Social:Toggle");
     // We only need to update the command itself - all our menu items use it.
     let label = gNavigatorBundle.getFormattedString(Social.provider.enabled ?
                                                       "social.turnOff.label" :
                                                       "social.turnOn.label",
                                                     [Social.provider.name]);
     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");
+    toggleCommand.setAttribute("hidden", enabled ? "false" : "true");
   },
 
   _updateMenuItems: function () {
     if (!Social.provider)
       return;
 
     // The View->Sidebar and Menubar->Tools menu.
     for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
@@ -198,16 +221,21 @@ let SocialUI = {
       return;
 
     // Check that the associated document's origin is in our whitelist
     let providerOrigin = targetDoc.nodePrincipal.origin;
     let whitelist = Services.prefs.getCharPref("social.activation.whitelist");
     if (whitelist.split(",").indexOf(providerOrigin) == -1)
       return;
 
+    // If we are in PB mode, we silently do nothing (bug 829404 exists to
+    // do something sensible here...)
+    if (PrivateBrowsingUtils.isWindowPrivate(window))
+      return;
+
     // If the last event was received < 1s ago, ignore this one
     let now = Date.now();
     if (now - Social.lastEventReceived < 1000)
       return;
     Social.lastEventReceived = now;
 
     // Keep track of the old provider in case of undo
     let oldOrigin = Social.provider ? Social.provider.origin : "";
@@ -280,31 +308,50 @@ let SocialUI = {
                 ps.BUTTON_TITLE_CANCEL * ps.BUTTON_POS_1 +
                 ps.BUTTON_POS_0_DEFAULT;
 
     let confirmationIndex = ps.confirmEx(null, dialogTitle, text, flags,
                                          okButtonText, null, null, null, {});
     if (confirmationIndex == 0) {
       Social.deactivateFromOrigin(Social.provider.origin);
     }
-  }
+  },
+
+  get _chromeless() {
+    // Is this a popup window that doesn't want chrome shown?
+    let docElem = document.documentElement;
+    let chromeless = docElem.getAttribute("chromehidden").indexOf("extrachrome") >= 0;
+    // This property is "fixed" for a window, so avoid doing the check above
+    // multiple times...
+    delete this._chromeless;
+    this._chromeless = chromeless;
+    return chromeless;
+  },
+
+  get enabled() {
+    // Returns whether social is enabled *for this window*.
+    if (this._chromeless
+#ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
+        || PrivateBrowsingUtils.isWindowPrivate(window)
+#endif
+       )
+      return false;
+    return !!(Social.active && Social.provider && Social.provider.enabled);
+  },
+
 }
 
 let SocialChatBar = {
   get chatbar() {
     return document.getElementById("pinnedchats");
   },
   // Whether the chatbar is available for this window.  Note that in full-screen
   // mode chats are available, but not shown.
   get isAvailable() {
-    if (!Social.haveLoggedInUser())
-      return false;
-    let docElem = document.documentElement;
-    let chromeless = docElem.getAttribute("chromehidden").indexOf("extrachrome") >= 0;
-    return Social.uiVisible && !chromeless;
+    return SocialUI.enabled && Social.haveLoggedInUser();
   },
   // Does this chatbar have any chats (whether minimized, collapsed or normal)
   get hasChats() {
     return !!this.chatbar.firstElementChild;
   },
   openChat: function(aProvider, aURL, aCallback, aMode) {
     if (this.isAvailable)
       this.chatbar.openChat(aProvider, aURL, aCallback, aMode);
@@ -389,17 +436,17 @@ let SocialFlyout = {
     let doc = this.panel.firstChild.contentDocument;
     let evt = doc.createEvent("CustomEvent");
     evt.initCustomEvent(name, true, true, {});
     doc.documentElement.dispatchEvent(evt);
   },
 
   _createFrame: function() {
     let panel = this.panel;
-    if (!Social.provider || panel.firstChild)
+    if (!SocialUI.enabled || panel.firstChild)
       return;
     // create and initialize the panel for this window
     let iframe = document.createElement("iframe");
     iframe.setAttribute("type", "content");
     iframe.setAttribute("class", "social-panel-frame");
     iframe.setAttribute("flex", "1");
     iframe.setAttribute("origin", Social.provider.origin);
     panel.appendChild(iframe);
@@ -466,17 +513,17 @@ let SocialFlyout = {
     this.panel.firstChild.docShell.isActive = false;
     this.dispatchPanelEvent("socialFrameHide");
   },
 
   open: function(aURL, yOffset, aCallback) {
     // Hide any other social panels that may be open.
     document.getElementById("social-notification-panel").hidePopup();
 
-    if (!Social.provider)
+    if (!SocialUI.enabled)
       return;
     let panel = this.panel;
     if (!panel.firstChild)
       this._createFrame();
     panel.hidden = false;
     let iframe = panel.firstChild;
 
     let src = iframe.getAttribute("src");
@@ -532,17 +579,17 @@ let SocialShareButton = {
       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;
+    let profile = SocialUI.enabled ? Social.provider.profile : null;
     if (profile && profile.displayName) {
       profileRow.hidden = false;
       let portrait = document.getElementById("socialUserPortrait");
       portrait.setAttribute("src", profile.portrait || "chrome://global/skin/icons/information-32.png");
       let displayName = document.getElementById("socialUserDisplayName");
       displayName.setAttribute("label", profile.displayName);
     } else {
       profileRow.hidden = true;
@@ -564,17 +611,17 @@ let SocialShareButton = {
   canSharePage: function SSB_canSharePage(aURI) {
     // We only allow sharing of http or https
     return aURI && (aURI.schemeIs('http') || aURI.schemeIs('https'));
   },
 
   updateButtonHiddenState: function SSB_updateButtonHiddenState() {
     let shareButton = this.shareButton;
     if (shareButton)
-      shareButton.hidden = !Social.uiVisible || Social.provider.recommendInfo == null ||
+      shareButton.hidden = !SocialUI.enabled || Social.provider.recommendInfo == null ||
                            !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");
     cmd.setAttribute("disabled", shareButton.hidden ? "true" : "false");
   },
@@ -630,17 +677,17 @@ let SocialShareButton = {
   },
 
   updateShareState: function SSB_updateShareState() {
     this.updateButtonHiddenState();
 
     let shareButton = this.shareButton;
     let currentPageShared = shareButton && !shareButton.hidden && Social.isPageShared(gBrowser.currentURI);
 
-    let recommendInfo = Social.provider ? Social.provider.recommendInfo : null;
+    let recommendInfo = SocialUI.enabled ? Social.provider.recommendInfo : null;
     // Provide a11y-friendly notification of share.
     let status = document.getElementById("share-button-status");
     if (status) {
       // XXX - this should also be capable of reflecting that the page was
       // unshared (ie, it needs to manage three-states: (1) nothing done, (2)
       // shared, (3) shared then unshared)
       // Note that we *do* have an appropriate string from the provider for
       // this (recommendInfo.messages.unsharedLabel) but currently lack a way of
@@ -672,20 +719,20 @@ var SocialMenu = {
   populate: function SocialMenu_populate() {
     let submenu = document.getElementById("menu_social-statusarea-popup");
     let ambientMenuItems = submenu.getElementsByClassName("ambient-menuitem");
     while (ambientMenuItems.length)
       submenu.removeChild(ambientMenuItems.item(0));
 
     let separator = document.getElementById("socialAmbientMenuSeparator");
     separator.hidden = true;
-    if (!Social.uiVisible)
+    let provider = SocialUI.enabled ? Social.provider : null;
+    if (!provider)
       return;
 
-    let provider = Social.provider;
     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);
@@ -713,17 +760,17 @@ var SocialToolbar = {
     removeCommand.setAttribute("accesskey", accesskey);
 
     this.updateProvider();
     this._dynamicResizer = new DynamicResizeWatcher();
   },
 
   // Called when the Social.provider changes
   updateProvider: function () {
-    if (!Social.provider)
+    if (!SocialUI.enabled)
       return;
     this.button.style.listStyleImage = "url(" + Social.provider.iconURL + ")";
     this.button.setAttribute("label", Social.provider.name);
     this.button.setAttribute("tooltiptext", Social.provider.name);
     this.updateButton();
     this.updateProfile();
     this.populateProviderMenus();
   },
@@ -731,17 +778,17 @@ var SocialToolbar = {
   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.uiVisible;
+    let socialEnabled = SocialUI.enabled;
     for (let className of ["social-statusarea-separator", "social-statusarea-user"]) {
       for (let element of document.getElementsByClassName(className))
         element.hidden = !socialEnabled;
     }
     let toggleNotificationsCommand = document.getElementById("Social:ToggleNotifications");
     toggleNotificationsCommand.setAttribute("hidden", !socialEnabled);
 
     if (!Social.haveLoggedInUser() || !socialEnabled) {
@@ -787,17 +834,17 @@ var SocialToolbar = {
     panel.hidden = false;
 
     let command = document.getElementById("Social:ToggleNotifications");
     command.setAttribute("checked", Services.prefs.getBoolPref("social.toast-notifications.enabled"));
 
     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 (!provider.enabled ||
+    if (!SocialUI.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
       // to nuke any cached icons we have and get out of here!
       Services.prefs.clearUserPref(CACHE_PREF_NAME);
       return;
     }
@@ -999,18 +1046,18 @@ var SocialToolbar = {
 
   _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) {
+    // only show a selection if enabled and there is more than one
+    if (!SocialUI.enabled || Social.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);
@@ -1041,25 +1088,17 @@ var SocialSidebar = {
     aDocShell.QueryInterface(Ci.nsIWebProgress)
              .addProgressListener(SocialSidebar.errorListener,
                                   Ci.nsIWebProgress.NOTIFY_STATE_REQUEST |
                                   Ci.nsIWebProgress.NOTIFY_LOCATION);
   },
 
   // Whether the sidebar can be shown for this window.
   get canShow() {
-    return Social.uiVisible && Social.provider.sidebarURL && !this.chromeless;
-  },
-
-  // Whether this is a "chromeless window" (e.g. popup window). We don't show
-  // the sidebar in these windows.
-  get chromeless() {
-    let docElem = document.documentElement;
-    return docElem.getAttribute('disablechrome') ||
-           docElem.getAttribute('chromehidden').contains("toolbar");
+    return SocialUI.enabled && Social.provider.sidebarURL;
   },
 
   // Whether the user has toggled the sidebar on (for windows where it can appear)
   get opened() {
     return Services.prefs.getBoolPref("social.sidebar.open") && !document.mozFullScreen;
   },
 
   setSidebarVisibilityState: function(aEnabled) {
--- a/browser/base/content/test/social/Makefile.in
+++ b/browser/base/content/test/social/Makefile.in
@@ -24,18 +24,22 @@ include $(DEPTH)/config/autoconf.mk
                  social_share_image.png \
                  social_sidebar.html \
                  social_chat.html \
                  social_flyout.html \
                  social_window.html \
                  social_worker.js \
                  $(NULL)
 
-ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+ifdef MOZ_PER_WINDOW_PRIVATE_BROWSING
 _BROWSER_FILES += \
-                browser_social.js \
+                browser_social_perwindowPB.js \
+                $(NULL)
+else
+_BROWSER_FILES += \
+                browser_social_globalPB.js \
                 $(NULL)
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 libs::	$(_BROWSER_FILES)
 	$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/browser/$(relativesrcdir)
rename from browser/base/content/test/social/browser_social.js
rename to browser/base/content/test/social/browser_social_globalPB.js
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social/browser_social_perwindowPB.js
@@ -0,0 +1,82 @@
+/* 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 openTab(win, url, callback) {
+  let newTab = win.gBrowser.addTab(url);
+  let tabBrowser = win.gBrowser.getBrowserForTab(newTab);
+  tabBrowser.addEventListener("load", function tabLoadListener() {
+    tabBrowser.removeEventListener("load", tabLoadListener, true);
+    win.gBrowser.selectedTab = newTab;
+    callback(newTab);
+  }, true)
+}
+
+// Tests for per-window private browsing.
+function openPBWindow(callback) {
+  let w = OpenBrowserWindow({private: true});
+  w.addEventListener("load", function loadListener() {
+    w.removeEventListener("load", loadListener);
+    openTab(w, "http://example.com", function() {
+      callback(w);
+    });
+  });
+}
+
+function postAndReceive(port, postTopic, receiveTopic, callback) {
+  port.onmessage = function(e) {
+    if (e.data.topic == receiveTopic)
+      callback();
+  }
+  port.postMessage({topic: postTopic});
+}
+
+function test() {
+  waitForExplicitFinish();
+
+  let manifest = { // normal provider
+    name: "provider 1",
+    origin: "https://example.com",
+    sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
+    workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js",
+    iconURL: "https://example.com/browser/browser/base/content/test/social/moz.png"
+  };
+  runSocialTestWithProvider(manifest, function (finishcb) {
+    openTab(window, "http://example.com", function(newTab) {
+      runSocialTests(tests, undefined, undefined, function() {
+        window.gBrowser.removeTab(newTab);
+        finishcb();
+      });
+    });
+  });
+}
+
+var tests = {
+  testPrivateBrowsing: function(next) {
+    let port = Social.provider.getWorkerPort();
+    ok(port, "provider has a port");
+    postAndReceive(port, "test-init", "test-init-done", function() {
+      // social features should all be enabled in the existing window.
+      info("checking main window ui");
+      ok(window.SocialUI.enabled, "social is enabled in normal window");
+      checkSocialUI(window);
+      // open a new private-window
+      openPBWindow(function(pbwin) {
+        // The provider should remain alive.
+        postAndReceive(port, "ping", "pong", function() {
+          // the new window should have no social features at all.
+          info("checking private window ui");
+          ok(!pbwin.SocialUI.enabled, "social is disabled in a PB window");
+          checkSocialUI(pbwin);
+          // but they should all remain enabled in the initial window
+          info("checking main window ui");
+          ok(window.SocialUI.enabled, "social is still enabled in normal window");
+          checkSocialUI(window);
+          // that's all folks...
+          pbwin.close();
+          next();
+        })
+      });
+    });
+  },
+}
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -143,8 +143,41 @@ function runSocialTests(tests, cbPreTest
           ok(false, "sub-test " + name + " failed: " + ex.toString() +"\n"+ex.stack);
           cleanupAndRunNextTest();
         }
       })
     });
   }
   runNextTest();
 }
+
+// A fairly large hammer which checks all aspects of the SocialUI for
+// internal consistency.
+function checkSocialUI(win) {
+  let win = win || window;
+  let doc = win.document;
+  let provider = Social.provider;
+  let enabled = win.SocialUI.enabled;
+  function isbool(a, b, msg) {
+    is(!!a, !!b, msg);
+  }
+  isbool(win.SocialSidebar.canShow, enabled, "social sidebar active?");
+  if (enabled)
+    isbool(win.SocialSidebar.opened, enabled, "social sidebar open?");
+  isbool(win.SocialChatBar.isAvailable, enabled && Social.haveLoggedInUser(), "chatbar available?");
+  isbool(!win.SocialChatBar.chatbar.hidden, enabled && Social.haveLoggedInUser(), "chatbar visible?");
+  isbool(!win.SocialShareButton.shareButton.hidden, enabled && provider.recommendInfo, "share button visible?");
+  isbool(!doc.getElementById("social-toolbar-item").hidden, enabled, "toolbar items visible?");
+  if (enabled)
+    todo_is(win.SocialToolbar.button.style.listStyleImage, 'url("' + provider.iconURL + '")', "Bug 821262 - toolbar button has provider icon");
+
+  // and for good measure, check all the social commands.
+  // Social:Remove - never disabled directly but parent nodes are
+  isbool(!doc.getElementById("Social:Toggle").hidden, enabled, "Social:Toggle visible?");
+  // Until bug 821262 is fixed, ToggleNotifications might not be updated correctly...
+  // isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "Bug 821262 - Social:ToggleNotifications visible?");
+  isbool(!doc.getElementById("Social:FocusChat").hidden, enabled && Social.haveLoggedInUser(), "Social:FocusChat visible?");
+  isbool(doc.getElementById("Social:FocusChat").getAttribute("disabled"), enabled ? "false" : "true", "Social:FocusChat disabled?");
+  is(doc.getElementById("Social:SharePage").getAttribute("disabled"), enabled && provider.recommendInfo ? "false" : "true", "Social:SharePage visible?");
+
+  // broadcasters.
+  isbool(!doc.getElementById("socialActiveBroadcaster").hidden, enabled, "socialActiveBroadcaster hidden?");
+}
--- a/browser/modules/Makefile.in
+++ b/browser/modules/Makefile.in
@@ -17,24 +17,27 @@ EXTRA_JS_MODULES = \
 	AboutHomeUtils.jsm \
 	BrowserNewTabPreloader.jsm \
 	openLocationLastURL.jsm \
 	NetworkPrioritizer.jsm \
 	NewTabUtils.jsm \
 	offlineAppCache.jsm \
 	SignInToWebsite.jsm \
 	TelemetryTimestamps.jsm \
-	Social.jsm \
 	webappsUI.jsm \
 	webrtcUI.jsm \
 	KeywordURLResetPrompter.jsm \
 	SharedFrame.jsm \
 	$(NULL)
 
-EXTRA_PP_JS_MODULES = RecentWindow.jsm
+EXTRA_PP_JS_MODULES = \
+	RecentWindow.jsm \
+	Social.jsm \
+	$(NULL)
+
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),windows)
 EXTRA_JS_MODULES += \
 	WindowsPreviewPerTab.jsm \
 	$(NULL)
 EXTRA_PP_JS_MODULES += \
 	WindowsJumpLists.jsm \
 	$(NULL)
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -79,17 +79,19 @@ this.Social = {
     this._disabledForSafeMode = Services.appinfo.inSafeMode && this.enabled;
 
     if (this.providers) {
       schedule(callback);
       return;
     }
 
     if (!this._addedObservers) {
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
       Services.obs.addObserver(this, "private-browsing", false);
+#endif
       Services.obs.addObserver(this, "social:pref-changed", false);
       this._addedObservers = true;
     }
 
     // Retrieve the current set of providers, and set the current provider.
     SocialService.getProviderList(function (providers) {
       // We don't want to notify about a provider change when we're setting
       // this.provider for the first time, so pass false here.
@@ -125,41 +127,40 @@ this.Social = {
           currentProvider.active = true;
         }
       } catch(ex) {}
     }
     this._setProvider(currentProvider, notifyProviderChange);
   },
 
   observe: function(aSubject, aTopic, aData) {
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
     if (aTopic == "private-browsing") {
       if (aData == "enter") {
         this._enabledBeforePrivateBrowsing = this.enabled;
         this.enabled = false;
       } else if (aData == "exit") {
         // if the user has explicitly re-enabled social in PB mode, then upon
         // leaving we want to tear the world down then reenable to prevent
         // information leaks during this transition.
         // The next 2 lines rely on the fact that setting this.enabled to
         // its current value doesn't actually do anything...
         this.enabled = false;
         this.enabled = this._enabledBeforePrivateBrowsing;
       }
-    } else if (aTopic == "social:pref-changed") {
+    } else
+#endif
+    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;
     }
   },
 
-  get uiVisible() {
-    return this.provider && this.provider.enabled;
-  },
-
   set enabled(val) {
     SocialService.enabled = val;
   },
   get enabled() {
     return SocialService.enabled;
   },
 
   get active() {
--- a/toolkit/components/social/Makefile.in
+++ b/toolkit/components/social/Makefile.in
@@ -10,16 +10,19 @@ VPATH     = @srcdir@
 include $(DEPTH)/config/autoconf.mk
 
 EXTRA_JS_MODULES = \
   FrameWorker.jsm \
   MessagePortBase.jsm \
   MessagePortWorker.js \
   SocialService.jsm \
   WorkerAPI.jsm \
+  $(NULL)
+
+EXTRA_PP_JS_MODULES = \
   MozSocialAPI.jsm \
   $(NULL)
 
 TEST_DIRS += \
   test \
   $(NULL)
 
 include $(topsrcdir)/config/rules.mk
--- a/toolkit/components/social/MozSocialAPI.jsm
+++ b/toolkit/components/social/MozSocialAPI.jsm
@@ -3,16 +3,19 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SocialService", "resource://gre/modules/SocialService.jsm");
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm");
+#endif
 
 this.EXPORTED_SYMBOLS = ["MozSocialAPI", "openChatWindow"];
 
 this.MozSocialAPI = {
   _enabled: false,
   _everEnabled: false,
   set enabled(val) {
     let enable = !!val;
@@ -35,17 +38,21 @@ this.MozSocialAPI = {
   }
 };
 
 // Called on document-element-inserted, checks that the API should be injected,
 // and then calls attachToWindow as appropriate
 function injectController(doc, topic, data) {
   try {
     let window = doc.defaultView;
-    if (!window)
+    if (!window
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+        || !PrivateBrowsingUtils.isWindowPrivate(window)
+#endif
+       )
       return;
 
     // Do not attempt to load the API into about: error pages
     if (doc.documentURIObject.scheme == "about") {
       return;
     }
 
     var containingBrowser = window.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -223,17 +230,22 @@ function getChromeWindow(contentWin) {
                    .getInterface(Ci.nsIWebNavigation)
                    .QueryInterface(Ci.nsIDocShellTreeItem)
                    .rootTreeItem
                    .QueryInterface(Ci.nsIInterfaceRequestor)
                    .getInterface(Ci.nsIDOMWindow);
 }
 
 function isWindowGoodForChats(win) {
-  return win.SocialChatBar && win.SocialChatBar.isAvailable;
+  return win.SocialChatBar
+         && win.SocialChatBar.isAvailable
+#ifndef MOZ_PER_WINDOW_PRIVATE_BROWSING
+         && !PrivateBrowsingUtils.isWindowPrivate(win)
+#endif
+         ;
 }
 
 function findChromeWindowForChats(preferredWindow) {
   if (preferredWindow && isWindowGoodForChats(preferredWindow))
     return preferredWindow;
   // no good - so let's go hunting.  We are now looking for a navigator:browser
   // window that is suitable and already has chats open, or failing that,
   // any suitable navigator:browser window.