Back out changeset 29c172213ff7 (bug 853151) because of Ts and Tp5 regressions
authorMatt Brubeck <mbrubeck@mozilla.com>
Fri, 03 May 2013 10:59:43 -0700
changeset 130766 414f3b4cf66931b413156b4530557c43626f6c94
parent 130765 d2688e330aa03e6d4356c07576c0596588e3cf06
child 130767 0a441b303a0032c64a5c81f96d44a2fd5489d5fd
push id1579
push userphilringnalda@gmail.com
push dateSat, 04 May 2013 04:38:04 +0000
treeherderfx-team@a56432a42a41 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs853151
milestone23.0a1
backs out29c172213ff7b3818073db1edc590a9c0c53ba28
Back out changeset 29c172213ff7 (bug 853151) because of Ts and Tp5 regressions
browser/base/content/browser-context.inc
browser/base/content/browser-sets.inc
browser/base/content/browser-social.js
browser/base/content/browser.js
browser/base/content/browser.xul
browser/base/content/nsContextMenu.js
browser/base/content/test/social/Makefile.in
browser/base/content/test/social/browser_social_markButton.js
browser/base/content/test/social/browser_social_multiprovider.js
browser/base/content/test/social/browser_social_shareButton.js
browser/base/content/test/social/head.js
browser/base/content/test/social/social_mark_image.png
browser/base/content/test/social/social_share_image.png
browser/base/content/test/social/social_worker.js
browser/locales/en-US/chrome/browser/browser.dtd
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/Social.jsm
browser/themes/linux/browser.css
browser/themes/osx/browser.css
browser/themes/windows/browser.css
toolkit/components/social/SocialService.jsm
toolkit/components/social/WorkerAPI.jsm
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -32,19 +32,16 @@
                 label="&openLinkInPrivateWindowCmd.label;"
                 accesskey="&openLinkInPrivateWindowCmd.accesskey;"
                 oncommand="gContextMenu.openLinkInPrivateWindow();"/>
       <menuseparator id="context-sep-open"/>
       <menuitem id="context-bookmarklink"
                 label="&bookmarkThisLinkCmd.label;"
                 accesskey="&bookmarkThisLinkCmd.accesskey;"
                 oncommand="gContextMenu.bookmarkLink();"/>
-      <menuitem id="context-marklink"
-                accesskey="&social.marklink.accesskey;"
-                oncommand="gContextMenu.markLink();"/>
       <menuitem id="context-savelink"
                 label="&saveLinkCmd.label;"
                 accesskey="&saveLinkCmd.accesskey;"
                 oncommand="gContextMenu.saveLink();"/>
       <menuitem id="context-copyemail"
                 label="&copyEmailCmd.label;"
                 accesskey="&copyEmailCmd.accesskey;"
                 oncommand="gContextMenu.copyEmail();"/>
@@ -220,19 +217,16 @@
                 label="&stopCmd.label;"
                 accesskey="&stopCmd.accesskey;"
                 command="Browser:Stop"/>
       <menuseparator id="context-sep-stop"/>
       <menuitem id="context-bookmarkpage"
                 label="&bookmarkPageCmd2.label;"
                 accesskey="&bookmarkPageCmd2.accesskey;"
                 oncommand="gContextMenu.bookmarkThisPage();"/>
-      <menuitem id="context-markpage"
-                accesskey="&social.markpage.accesskey;"
-                command="Social:TogglePageMark"/>
       <menuitem id="context-savepage"
                 label="&savePageCmd.label;"
                 accesskey="&savePageCmd.accesskey2;"
                 oncommand="gContextMenu.savePageAs();"/>
       <menuseparator id="context-sep-viewbgimage"/>
       <menuitem id="context-viewbgimage"
                 label="&viewBGImageCmd.label;"
                 accesskey="&viewBGImageCmd.accesskey;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -104,17 +104,18 @@
     <command id="Tools:DevToolsConnect" oncommand="gDevToolsBrowser.openConnectScreen(gBrowser)" disabled="true" hidden="true"/>
     <command id="Tools:Sanitize"
      oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
     <command id="Tools:PrivateBrowsing"
       oncommand="OpenBrowserWindow({private: true});"/>
     <command id="History:UndoCloseTab" oncommand="undoCloseTab();"/>
     <command id="History:UndoCloseWindow" oncommand="undoCloseWindow();"/>
     <command id="Browser:ToggleAddonBar" oncommand="toggleAddonBar();"/>
-    <command id="Social:TogglePageMark" oncommand="SocialMark.togglePageMark();" disabled="true"/>
+    <command id="Social:SharePage" oncommand="SocialShareButton.sharePage();" disabled="true"/>
+    <command id="Social:UnsharePage" oncommand="SocialShareButton.unsharePage();"/>
     <command id="Social:ToggleSidebar" oncommand="Social.toggleSidebar();"/>
     <command id="Social:ToggleNotifications" oncommand="Social.toggleNotifications();" hidden="true"/>
     <command id="Social:FocusChat" oncommand="SocialChatBar.focus();" hidden="true" disabled="true"/>
     <command id="Social:Toggle" oncommand="Social.toggle();" hidden="true"/>
     <command id="Social:Addons" oncommand="BrowserOpenAddonsMgr('addons://list/service');"/>
   </commandset>
 
   <commandset id="placesCommands">
@@ -344,18 +345,18 @@
     <key id="manBookmarkKb" key="&bookmarksGtkCmd.commandkey;" command="Browser:ShowAllBookmarks" modifiers="accel,shift"/>
 #endif
     <key id="viewBookmarksSidebarKb" key="&bookmarksCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
 #ifdef XP_WIN
 # Cmd+I is conventially mapped to Info on MacOS X, thus it should not be
 # overridden for other purposes there.
     <key id="viewBookmarksSidebarWinKb" key="&bookmarksWinCmd.commandkey;" command="viewBookmarksSidebar" modifiers="accel"/>
 #endif
-
-    <key id="markPage" key="&markPageCmd.commandkey;" command="Social:TogglePageMark" modifiers="accel,shift"/>
+    
+    <key id="sharePage" key="&sharePageCmd.commandkey;" command="Social:SharePage" modifiers="accel,shift"/>
     <key id="focusChatBar" key="&social.chatBar.commandkey;" command="Social:FocusChat" modifiers="accel,shift"/>
 
     <key id="key_stop" keycode="VK_ESCAPE" command="Browser:Stop"/>
 
 #ifdef XP_MACOSX
     <key id="key_stop_mac" modifiers="accel" key="&stopCmd.macCommandKey;" command="Browser:Stop"/>
 #endif
 
--- a/browser/base/content/browser-social.js
+++ b/browser/base/content/browser-social.js
@@ -1,17 +1,17 @@
 // 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/.
 
 // the "exported" symbols
 let SocialUI,
     SocialChatBar,
     SocialFlyout,
-    SocialMark,
+    SocialShareButton,
     SocialMenu,
     SocialToolbar,
     SocialSidebar;
 
 (function() {
 
 // The minimum sizes for the auto-resize panel code.
 const PANEL_MIN_HEIGHT = 100;
@@ -20,34 +20,34 @@ const PANEL_MIN_WIDTH = 330;
 XPCOMUtils.defineLazyModuleGetter(this, "SharedFrame",
   "resource:///modules/SharedFrame.jsm");
 
 SocialUI = {
   // Called on delayed startup to initialize the UI
   init: function SocialUI_init() {
     Services.obs.addObserver(this, "social:ambient-notification-changed", false);
     Services.obs.addObserver(this, "social:profile-changed", false);
-    Services.obs.addObserver(this, "social:page-mark-config", 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.obs.addObserver(this, "social:providers-changed", false);
 
     Services.prefs.addObserver("social.sidebar.open", this, false);
     Services.prefs.addObserver("social.toast-notifications.enabled", this, false);
 
     gBrowser.addEventListener("ActivateSocialFeature", this._activationEventHandler.bind(this), true, true);
 
     // Called when we enter DOM full-screen mode.
     window.addEventListener("mozfullscreenchange", function () {
       SocialSidebar.update();
       SocialChatBar.update();
     });
 
     SocialChatBar.init();
-    SocialMark.init();
+    SocialShareButton.init();
     SocialMenu.init();
     SocialToolbar.init();
     SocialSidebar.init();
 
     if (!Social.initialized) {
       Social.init();
     } else {
       // social was previously initialized, so it's not going to notify us of
@@ -56,17 +56,17 @@ SocialUI = {
       this.observe(null, "social:provider-set", Social.provider ? Social.provider.origin : null);
     }
   },
 
   // Called on window unload
   uninit: function SocialUI_uninit() {
     Services.obs.removeObserver(this, "social:ambient-notification-changed");
     Services.obs.removeObserver(this, "social:profile-changed");
-    Services.obs.removeObserver(this, "social:page-mark-config");
+    Services.obs.removeObserver(this, "social:recommend-info-changed");
     Services.obs.removeObserver(this, "social:frameworker-error");
     Services.obs.removeObserver(this, "social:provider-set");
     Services.obs.removeObserver(this, "social:providers-changed");
 
     Services.prefs.removeObserver("social.sidebar.open", this);
     Services.prefs.removeObserver("social.toast-notifications.enabled", this);
   },
 
@@ -83,17 +83,17 @@ SocialUI = {
           // Social.provider has changed (possibly to null), update any state
           // which depends on it.
           this._updateActiveUI();
           this._updateMenuItems();
 
           SocialFlyout.unload();
           SocialChatBar.update();
           SocialSidebar.update();
-          SocialMark.update();
+          SocialShareButton.update();
           SocialToolbar.update();
           SocialMenu.populate();
           break;
         case "social:providers-changed":
           // the list of providers changed - this may impact the "active" UI.
           this._updateActiveUI();
           // and the multi-provider menu
           SocialToolbar.populateProviderMenus();
@@ -104,23 +104,23 @@ SocialUI = {
           if (this._matchesCurrentProvider(data)) {
             SocialToolbar.updateButton();
             SocialMenu.populate();
           }
           break;
         case "social:profile-changed":
           if (this._matchesCurrentProvider(data)) {
             SocialToolbar.updateProfile();
-            SocialMark.update();
+            SocialShareButton.update();
             SocialChatBar.update();
           }
           break;
-        case "social:page-mark-config":
+        case "social:recommend-info-changed":
           if (this._matchesCurrentProvider(data)) {
-            SocialMark.updateMarkState();
+            SocialShareButton.updateShareState();
           }
           break;
         case "social:frameworker-error":
           if (this.enabled && Social.provider.origin == data) {
             SocialSidebar.setSidebarErrorMessage();
           }
           break;
 
@@ -561,91 +561,156 @@ SocialFlyout = {
       // that the docShell of this frame is created
       panel.firstChild.clientTop;
       Social.setErrorListener(iframe, this.setFlyoutErrorMessage.bind(this))
     }
     this.yOffset = yOffset;
   }
 }
 
-SocialMark = {
+SocialShareButton = {
   // Called once, after window load, when the Social.provider object is initialized
   init: function SSB_init() {
   },
 
-  get button() {
-    return document.getElementById("social-mark-button");
+  // Called when the Social.provider changes
+  update: function() {
+    this._updateButtonHiddenState();
+    let profileRow = document.getElementById("unsharePopupHeader");
+    let profile = SocialUI.enabled ? Social.provider.profile : null;
+    if (profile && profile.displayName) {
+      profileRow.hidden = false;
+      let portrait = document.getElementById("socialUserPortrait");
+      if (profile.portrait) {
+        portrait.setAttribute("src", profile.portrait);
+      } else {
+        portrait.removeAttribute("src");
+      }
+      let displayName = document.getElementById("socialUserDisplayName");
+      displayName.setAttribute("label", profile.displayName);
+    } else {
+      profileRow.hidden = true;
+    }
   },
 
-  canMarkPage: function SSB_canMarkPage(aURI) {
+  get shareButton() {
+    return document.getElementById("share-button");
+  },
+  get unsharePopup() {
+    return document.getElementById("unsharePopup");
+  },
+
+  dismissUnsharePopup: function SSB_dismissUnsharePopup() {
+    this.unsharePopup.hidePopup();
+  },
+
+  canSharePage: function SSB_canSharePage(aURI) {
     // We only allow sharing of http or https
     return aURI && (aURI.schemeIs('http') || aURI.schemeIs('https'));
   },
 
-  // Called when the Social.provider changes
-  update: function SSB_updateButtonState() {
-    let markButton = this.button;
-    // always show button if provider supports marks
-    markButton.hidden = !SocialUI.enabled || Social.provider.pageMarkInfo == null;
-    markButton.disabled = markButton.hidden || !this.canMarkPage(gBrowser.currentURI);
+  _updateButtonHiddenState: function SSB_updateButtonHiddenState() {
+    let shareButton = this.shareButton;
+    if (shareButton)
+      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:TogglePageMark");
-    cmd.setAttribute("disabled", markButton.disabled ? "true" : "false");
+    let cmd = document.getElementById("Social:SharePage");
+    cmd.setAttribute("disabled", shareButton.hidden ? "true" : "false");
+  },
+
+  onClick: function SSB_onClick(aEvent) {
+    if (aEvent.button != 0)
+      return;
+
+    // Don't bubble to the textbox, to avoid unwanted selection of the address.
+    aEvent.stopPropagation();
+
+    this.sharePage();
+  },
+
+  panelShown: function SSB_panelShown(aEvent) {
+    function updateElement(id, attrs) {
+      let el = document.getElementById(id);
+      Object.keys(attrs).forEach(function(attr) {
+        el.setAttribute(attr, attrs[attr]);
+      });
+    }
+    let continueSharingButton = document.getElementById("unsharePopupContinueSharingButton");
+    continueSharingButton.focus();
+    let recommendInfo = Social.provider.recommendInfo;
+    updateElement("unsharePopupContinueSharingButton",
+                  {label: recommendInfo.messages.unshareCancelLabel,
+                   accesskey: recommendInfo.messages.unshareCancelAccessKey});
+    updateElement("unsharePopupStopSharingButton",
+                  {label: recommendInfo.messages.unshareConfirmLabel,
+                  accesskey: recommendInfo.messages.unshareConfirmAccessKey});
+    updateElement("socialUserPortrait",
+                  {"aria-label": recommendInfo.messages.portraitLabel});
+    updateElement("socialUserRecommendedText",
+                  {value: recommendInfo.messages.unshareLabel});
   },
 
-  togglePageMark: function(aCallback) {
-    if (this.button.disabled)
-      return;
-    this.toggleURIMark(gBrowser.currentURI, aCallback)
+  sharePage: function SSB_sharePage() {
+    this.unsharePopup.hidden = false;
+
+    let uri = gBrowser.currentURI;
+    if (!Social.isPageShared(uri)) {
+      Social.sharePage(uri);
+      this.updateShareState();
+    } else {
+      this.unsharePopup.openPopup(this.shareButton, "bottomcenter topright");
+    }
   },
-  
-  toggleURIMark: function(aURI, aCallback) {
-    let update = function(marked) {
-      this._updateMarkState(marked);
-      if (aCallback)
-        aCallback(marked);
-    }.bind(this);
-    Social.isURIMarked(aURI, function(marked) {
-      if (marked) {
-        Social.unmarkURI(aURI, update);
-      } else {
-        Social.markURI(aURI, update);
-      }
-    });
+
+  unsharePage: function SSB_unsharePage() {
+    Social.unsharePage(gBrowser.currentURI);
+    this.updateShareState();
+    this.dismissUnsharePopup();
   },
 
-  updateMarkState: function SSB_updateMarkState() {
-    this.update();
-    Social.isURIMarked(gBrowser.currentURI, this._updateMarkState.bind(this));
-  },
+  updateShareState: function SSB_updateShareState() {
+    this._updateButtonHiddenState();
+
+    let shareButton = this.shareButton;
+    let currentPageShared = shareButton && !shareButton.hidden && Social.isPageShared(gBrowser.currentURI);
 
-  _updateMarkState: function(currentPageMarked) {
-    // callback for isURIMarked
-    let markButton = this.button;
-    let pageMarkInfo = SocialUI.enabled ? Social.provider.pageMarkInfo : 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
+      // tracking this state)
+      let statusString = currentPageShared && recommendInfo ?
+                           recommendInfo.messages.sharedLabel : "";
+      status.setAttribute("value", statusString);
+    }
 
-    // Update the mark button, if present
-    if (!markButton || markButton.hidden || !pageMarkInfo)
+    // Update the share button, if present
+    if (!shareButton || shareButton.hidden)
       return;
 
     let imageURL;
-    if (!markButton.disabled && currentPageMarked) {
-      markButton.setAttribute("marked", "true");
-      markButton.setAttribute("label", pageMarkInfo.messages.markedLabel);
-      markButton.setAttribute("tooltiptext", pageMarkInfo.messages.markedTooltip);
-      imageURL = pageMarkInfo.images.marked;
+    if (currentPageShared) {
+      shareButton.setAttribute("shared", "true");
+      shareButton.setAttribute("tooltiptext", recommendInfo.messages.unshareTooltip);
+      imageURL = recommendInfo.images.unshare;
     } else {
-      markButton.removeAttribute("marked");
-      markButton.setAttribute("label", pageMarkInfo.messages.unmarkedLabel);
-      markButton.setAttribute("tooltiptext", pageMarkInfo.messages.unmarkedTooltip);
-      imageURL = pageMarkInfo.images.unmarked;
+      shareButton.removeAttribute("shared");
+      shareButton.setAttribute("tooltiptext", recommendInfo.messages.shareTooltip);
+      imageURL = recommendInfo.images.share;
     }
-    markButton.style.listStyleImage = "url(" + imageURL + ")";
+    shareButton.src = imageURL;
   }
 };
 
 SocialMenu = {
   init: function SocialMenu_init() {
   },
 
   populate: function SocialMenu_populate() {
@@ -724,22 +789,18 @@ SocialToolbar = {
       while (parent.hasChildNodes()) {
         let frame = parent.firstChild;
         SharedFrame.forgetGroup(frame.id);
         parent.removeChild(frame);
       }
 
       let tbi = document.getElementById("social-toolbar-item");
       if (tbi) {
-        // SocialMark is the last button allways
-        let next = SocialMark.button.previousSibling;
-        while (next != tbi.firstChild) {
-          tbi.removeChild(next);
-          next = SocialMark.button.previousSibling;
-        }
+        while (tbi.lastChild != tbi.firstChild)
+          tbi.removeChild(tbi.lastChild);
       }
     }
   },
 
   updateProfile: function SocialToolbar_updateProfile() {
     // Profile may not have been initialized yet, since it depends on a worker
     // response. In that case we'll be called again when it's available, via
     // social:profile-changed
@@ -877,17 +938,17 @@ SocialToolbar = {
       let ariaLabel = icon.label;
       // if there is a badge value, we must use a localizable string to insert it.
       if (badge)
         ariaLabel = gNavigatorBundle.getFormattedString("social.aria.toolbarButtonBadgeText",
                                                         [ariaLabel, badge]);
       toolbarButton.setAttribute("aria-label", ariaLabel);
     }
     let socialToolbarItem = document.getElementById("social-toolbar-item");
-    socialToolbarItem.insertBefore(toolbarButtons, SocialMark.button);
+    socialToolbarItem.appendChild(toolbarButtons);
 
     for (let frame of createdFrames) {
       if (frame.socialErrorListener) {
         frame.socialErrorListener.remove();
       }
       if (frame.docShell) {
         frame.docShell.isActive = false;
         Social.setErrorListener(frame, this.setPanelErrorMessage.bind(this));
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3400,17 +3400,17 @@ function BrowserToolboxCustomizeDone(aTo
   UpdateUrlbarSearchSplitterState();
   setUrlAndSearchBarWidthForConditionalForwardButton();
 
   // Update the urlbar
   if (gURLBar) {
     URLBarSetURI();
     XULBrowserWindow.asyncUpdateUI();
     BookmarksMenuButton.updateStarState();
-    SocialMark.updateMarkState();
+    SocialShareButton.updateShareState();
   }
 
   TabsInTitlebar.allowedBy("customizing-toolbars", true);
 
   // Re-enable parts of the UI we disabled during the dialog
   var menubar = document.getElementById("main-menubar");
   for (let childNode of menubar.childNodes)
     childNode.setAttribute("disabled", false);
@@ -3874,17 +3874,17 @@ var XULBrowserWindow = {
         this.reloadCommand.removeAttribute("disabled");
       }
 
       if (gURLBar) {
         URLBarSetURI(aLocationURI);
 
         // Update starring UI
         BookmarksMenuButton.updateStarState();
-        SocialMark.updateMarkState();
+        SocialShareButton.updateShareState();
       }
 
       // Show or hide browser chrome based on the whitelist
       if (this.hideChromeForLocation(location)) {
         document.documentElement.setAttribute("disablechrome", "true");
       } else {
         let ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
         if (ss.getTabValue(gBrowser.selectedTab, "appOrigin"))
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -200,16 +200,62 @@
                   autofocus="autofocus"
                   label="&social.ok.label;"
                   accesskey="&social.ok.accesskey;"
                   oncommand="SocialUI.activationPanel.hidePopup();"/>
         </hbox>
       </vbox>
     </panel>
 
+    <panel id="unsharePopup"
+           type="arrow"
+           orient="vertical"
+           ignorekeys="true"
+           hidden="true"
+           onpopupshown="SocialShareButton.panelShown(event);"
+           consumeoutsideclicks="true"
+           level="top">
+      <!-- Note that 'label', 'accesskey', 'value' and 'aria-label' attributes
+           for many of these elements are supplied by the provider and filled
+           in at runtime
+      -->
+      <row id="unsharePopupHeader" align="center">
+        <vbox align="center">
+          <image id="socialUserPortrait" onclick="SocialUI.showProfile();"/>
+        </vbox>
+        <vbox id="unsharePopupText">
+          <button id="socialUserDisplayName" pack="start"
+                  oncommand="SocialUI.showProfile();"/>
+          <spacer flex="1"/>
+          <label id="socialUserRecommendedText"/>
+        </vbox>
+      </row>
+      <hbox id="unsharePopupBottomButtons" pack="end">
+#ifdef XP_UNIX
+        <button id="unsharePopupStopSharingButton"
+                class="unsharePopupBottomButton"
+                command="Social:UnsharePage"/>
+        <button id="unsharePopupContinueSharingButton"
+                class="unsharePopupBottomButton"
+                default="true"
+                autofocus="autofocus"
+                oncommand="SocialShareButton.dismissUnsharePopup();"/>
+#else
+        <button id="unsharePopupContinueSharingButton"
+                class="unsharePopupBottomButton"
+                default="true"
+                autofocus="autofocus"
+                oncommand="SocialShareButton.dismissUnsharePopup();"/>
+        <button id="unsharePopupStopSharingButton"
+                class="unsharePopupBottomButton"
+                command="Social:UnsharePage"/>
+#endif
+      </hbox>
+    </panel>
+
     <panel id="social-notification-panel"
            class="social-panel"
            type="arrow"
            hidden="true"
            noautofocus="true"/>
     <panel id="social-flyout-panel"
            class="social-panel"
            onpopupshown="SocialFlyout.onShown()"
@@ -562,16 +608,23 @@
             <label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
           </box>
           <hbox id="urlbar-icons">
             <image id="page-report-button"
                    class="urlbar-icon"
                    hidden="true"
                    tooltiptext="&pageReportIcon.tooltip;"
                    onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
+
+            <label id="share-button-status" collapsed="true" role="status"/>
+            <image id="share-button"
+                   class="urlbar-icon"
+                   hidden="true"
+                   onclick="SocialShareButton.onClick(event);"/>
+
             <image id="go-button"
                    class="urlbar-icon"
                    tooltiptext="&goEndCap.tooltip;"
                    onclick="gURLBar.handleCommand(event);"/>
           </hbox>
           <toolbarbutton id="urlbar-go-button"
                          class="chromeclass-toolbar-additional"
                          onclick="gURLBar.handleCommand(event);"
@@ -744,19 +797,16 @@
             <menuseparator class="social-provider-menu" hidden="true"/>
             <menuitem class="social-addons-menuitem" command="Social:Addons"
                       label="&social.addons.label;"/>
             <menuitem label="&social.learnMore.label;"
                       accesskey="&social.learnMore.accesskey;"
                       oncommand="SocialUI.showLearnMore();"/>
           </menupopup>
         </toolbarbutton>
-        <toolbarbutton id="social-mark-button"
-                       class="toolbarbutton-1"
-                       command="Social:TogglePageMark"/>
       </toolbaritem>
 
       <hbox id="window-controls" hidden="true" pack="end">
         <toolbarbutton id="minimize-button"
                        tooltiptext="&fullScreenMinimize.tooltip;"
                        oncommand="window.minimize();"/>
 
         <toolbarbutton id="restore-button"
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -287,42 +287,16 @@ nsContextMenu.prototype = {
     }
 
     // BiDi UI
     this.showItem("context-sep-bidi", top.gBidiUI);
     this.showItem("context-bidi-text-direction-toggle",
                   this.onTextInput && top.gBidiUI);
     this.showItem("context-bidi-page-direction-toggle",
                   !this.onTextInput && top.gBidiUI);
-    
-    // SocialMarks
-    let marksEnabled = SocialUI.enabled && Social.provider.pageMarkInfo;
-    let enablePageMark = marksEnabled && !(this.isContentSelected ||
-                            this.onTextInput || this.onLink || this.onImage ||
-                            this.onVideo || this.onAudio || this.onSocial);
-    let enableLinkMark = marksEnabled && ((this.onLink && !this.onMailtoLink &&
-                                           !this.onSocial) || this.onPlainTextLink);
-    if (enablePageMark) {
-      Social.isURIMarked(gBrowser.currentURI, function(marked) {
-        let label = marked ? "social.unmarkpage.label" : "social.markpage.label";
-        let provider = Social.provider || Social.defaultProvider;
-        let menuLabel = gNavigatorBundle.getFormattedString(label, [provider.name]);
-        this.setItemAttr("context-markpage", "label", menuLabel);
-      }.bind(this));
-    }
-    this.showItem("context-markpage", enablePageMark);
-    if (enableLinkMark) {
-      Social.isURIMarked(this.linkURI, function(marked) {
-        let label = marked ? "social.unmarklink.label" : "social.marklink.label";
-        let provider = Social.provider || Social.defaultProvider;
-        let menuLabel = gNavigatorBundle.getFormattedString(label, [provider.name]);
-        this.setItemAttr("context-marklink", "label", menuLabel);
-      }.bind(this));
-    }
-    this.showItem("context-marklink", enableLinkMark);
   },
 
   initSpellingItems: function() {
     var canSpell = InlineSpellCheckerUI.canSpellCheck;
     var onMisspelling = InlineSpellCheckerUI.overMisspelling;
     var showUndo = canSpell && InlineSpellCheckerUI.canUndo();
     this.showItem("spell-check-enabled", canSpell);
     this.showItem("spell-separator", canSpell || this.onEditableArea);
@@ -1488,21 +1462,16 @@ nsContextMenu.prototype = {
     else {
       PlacesUIUtils.showBookmarkDialog({ action: "edit"
                                        , type: "bookmark"
                                        , itemId: itemId
                                        }, window.top);
     }
   },
 
-  markLink: function CM_markLink() {
-    // send link to social
-    SocialMark.toggleURIMark(this.linkURI);
-  },
-
   savePageAs: function CM_savePageAs() {
     saveDocument(this.browser.contentDocument);
   },
 
   printFrame: function CM_printFrame() {
     PrintUtils.print(this.target.ownerDocument.defaultView);
   },
 
--- a/browser/base/content/test/social/Makefile.in
+++ b/browser/base/content/test/social/Makefile.in
@@ -15,30 +15,30 @@ include $(DEPTH)/config/autoconf.mk
 		 blocklist.xml \
 		 blocklistEmpty.xml \
 		 browser_blocklist.js \
 		 browser_defaults.js \
 		 browser_addons.js \
                  browser_social_activation.js \
                  browser_social_perwindowPB.js \
                  browser_social_toolbar.js \
-                 browser_social_markButton.js \
+                 browser_social_shareButton.js \
                  browser_social_sidebar.js \
                  browser_social_flyout.js \
                  browser_social_mozSocial_API.js \
                  browser_social_isVisible.js \
                  browser_social_chatwindow.js \
                  browser_social_chatwindowfocus.js \
                  browser_social_multiprovider.js \
                  browser_social_errorPage.js \
                  browser_social_window.js \
                  social_activate.html \
                  social_activate_iframe.html \
                  social_panel.html \
-                 social_mark_image.png \
+                 social_share_image.png \
                  social_sidebar.html \
                  social_chat.html \
                  social_flyout.html \
                  social_window.html \
                  social_worker.js \
                  $(NULL)
 
 include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/base/content/test/social/browser_social_markButton.js
+++ /dev/null
@@ -1,210 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-let prefName = "social.enabled",
-    gFinishCB;
-
-function test() {
-  waitForExplicitFinish();
-
-  // Need to load a http/https/ftp/ftps page for the social mark button to appear
-  let tab = gBrowser.selectedTab = gBrowser.addTab("https://example.com", {skipAnimation: true});
-  tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
-    tab.linkedBrowser.removeEventListener("load", tabLoad, true);
-    executeSoon(tabLoaded);
-  }, true);
-
-  registerCleanupFunction(function() {
-    Services.prefs.clearUserPref(prefName);
-    gBrowser.removeTab(tab);
-  });
-}
-
-function tabLoaded() {
-  ok(Social, "Social module loaded");
-
-  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/moz.png"
-  };
-  runSocialTestWithProvider(manifest, function (finishcb) {
-    gFinishCB = finishcb;
-    testInitial();
-  });
-}
-
-function testInitial(finishcb) {
-  ok(Social.provider, "Social provider is active");
-  ok(Social.provider.enabled, "Social provider is enabled");
-  let port = Social.provider.getWorkerPort();
-  ok(port, "Social provider has a port to its FrameWorker");
-  port.close();
-
-  let markButton = SocialMark.button;
-  ok(markButton, "mark button exists");
-
-  // ensure the worker initialization and handshakes are all done and we
-  // have a profile and the worker has sent a page-mark-config msg.
-  waitForCondition(function() Social.provider.pageMarkInfo != null, function() {
-    is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' attribute before mark button is clicked");
-    // Check the strings from our worker actually ended up on the button.
-    is(markButton.getAttribute("tooltiptext"), "Mark this page", "check tooltip text is correct");
-    // Check the relative URL was resolved correctly (note this image has offsets of zero...)
-    is(markButton.style.listStyleImage, 'url("https://example.com/browser/browser/base/content/test/social/social_mark_image.png")', "check image url is correct");
-
-    // Test the mark button command handler
-    SocialMark.togglePageMark(function() {
-      is(markButton.hasAttribute("marked"), true, "mark button should have 'marked' attribute after mark button is clicked");
-      is(markButton.getAttribute("tooltiptext"), "Unmark this page", "check tooltip text is correct");
-      // Check the URL and offsets were applied correctly
-      is(markButton.style.listStyleImage, 'url("https://example.com/browser/browser/base/content/test/social/social_mark_image.png")', "check image url is correct");
-      SocialMark.togglePageMark(function() {
-        is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
-        executeSoon(function() {
-          testStillMarkedIn2Tabs();
-        });
-      });
-    });
-    markButton.click();
-  }, "provider didn't provide page-mark-config");
-}
-
-function testStillMarkedIn2Tabs() {
-  let toMark = "http://example.com";
-  let markUri = Services.io.newURI(toMark, null, null);
-  let markButton = SocialMark.button;
-  let initialTab = gBrowser.selectedTab;
-  is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' for the initial tab");
-  let tab1 = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tab1b = gBrowser.getBrowserForTab(tab1);
-
-  tab1b.addEventListener("load", function tabLoad(event) {
-    tab1b.removeEventListener("load", tabLoad, true);
-    let tab2 = gBrowser.selectedTab = gBrowser.addTab(toMark);
-    let tab2b = gBrowser.getBrowserForTab(tab2);
-    tab2b.addEventListener("load", function tabLoad(event) {
-      tab2b.removeEventListener("load", tabLoad, true);
-      // should start without either page being marked.
-      is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' before we've done anything");
-      Social.isURIMarked(markUri, function(marked) {
-        ok(!marked, "page is unmarked in annotations");
-        markButton.click();
-        waitForCondition(function() markButton.hasAttribute("marked"), function() {
-          Social.isURIMarked(markUri, function(marked) {
-            ok(marked, "page is marked in annotations");
-            // and switching to the first tab (with the same URL) should still reflect marked.
-            gBrowser.selectedTab = tab1;
-            is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
-            // but switching back the initial one should reflect not marked.
-            gBrowser.selectedTab = initialTab;
-            waitForCondition(function() !markButton.hasAttribute("marked"), function() {
-              gBrowser.selectedTab = tab1;
-    
-              SocialMark.togglePageMark(function() {
-                Social.isURIMarked(gBrowser.currentURI, function(marked) {
-                  ok(!marked, "page is unmarked in annotations");
-                  is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
-                  gBrowser.removeTab(tab1);
-                  gBrowser.removeTab(tab2);
-                  executeSoon(testStillMarkedAfterReopen);
-                });
-              });
-            }, "button has been unmarked");
-          });
-        }, "button has been marked");
-      });
-
-    }, true);
-  }, true);
-}
-
-function testStillMarkedAfterReopen() {
-  let toMark = "http://example.com";
-  let markButton = SocialMark.button;
-
-  is(markButton.hasAttribute("marked"), false, "Reopen: SocialMark button should not have 'marked' for the initial tab");
-  let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tabb = gBrowser.getBrowserForTab(tab);
-  tabb.addEventListener("load", function tabLoad(event) {
-    tabb.removeEventListener("load", tabLoad, true);
-    SocialMark.togglePageMark(function() {
-      is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
-      gBrowser.removeTab(tab);
-      // should be on the initial unmarked tab now.
-      waitForCondition(function() !markButton.hasAttribute("marked"), function() {
-        // now open the same URL - should be back to Marked.
-        tab = gBrowser.selectedTab = gBrowser.addTab(toMark, {skipAnimation: true});
-        tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
-          tab.linkedBrowser.removeEventListener("load", tabLoad, true);
-          executeSoon(function() {
-            is(markButton.hasAttribute("marked"), true, "New tab to previously marked URL should reflect marked state");
-            SocialMark.togglePageMark(function() {
-              gBrowser.removeTab(tab);
-              executeSoon(testOnlyMarkCertainUrlsTabSwitch);
-            });
-          });
-        }, true);
-      }, "button is now unmarked");
-    });
-  }, true);
-}
-
-function testOnlyMarkCertainUrlsTabSwitch() {
-  let toMark = "http://example.com";
-  let notSharable = "about:blank";
-  let markButton = SocialMark.button;
-  let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tabb = gBrowser.getBrowserForTab(tab);
-  tabb.addEventListener("load", function tabLoad(event) {
-    tabb.removeEventListener("load", tabLoad, true);
-    ok(!markButton.hidden, "SocialMark button not hidden for http url");
-    let tab2 = gBrowser.selectedTab = gBrowser.addTab(notSharable);
-    let tabb2 = gBrowser.getBrowserForTab(tab2);
-    tabb2.addEventListener("load", function tabLoad(event) {
-      tabb2.removeEventListener("load", tabLoad, true);
-      ok(markButton.disabled, "SocialMark button disabled for about:blank");
-      gBrowser.selectedTab = tab;
-      ok(!markButton.disabled, "SocialMark button re-shown when switching back to http: url");
-      gBrowser.selectedTab = tab2;
-      ok(markButton.disabled, "SocialMark button re-hidden when switching back to about:blank");
-      gBrowser.removeTab(tab);
-      gBrowser.removeTab(tab2);
-      executeSoon(testOnlyMarkCertainUrlsSameTab);
-    }, true);
-  }, true);
-}
-
-function testOnlyMarkCertainUrlsSameTab() {
-  let toMark = "http://example.com";
-  let notSharable = "about:blank";
-  let markButton = SocialMark.button;
-  let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tabb = gBrowser.getBrowserForTab(tab);
-  tabb.addEventListener("load", function tabLoad(event) {
-    tabb.removeEventListener("load", tabLoad, true);
-    ok(!markButton.disabled, "SocialMark button not disabled for http url");
-    tabb.addEventListener("load", function tabLoad(event) {
-      tabb.removeEventListener("load", tabLoad, true);
-      ok(markButton.disabled, "SocialMark button disabled for about:blank");
-      tabb.addEventListener("load", function tabLoad(event) {
-        tabb.removeEventListener("load", tabLoad, true);
-        ok(!markButton.disabled, "SocialMark button re-enabled http url");
-        gBrowser.removeTab(tab);
-        executeSoon(testDisable);
-      }, true);
-      tabb.loadURI(toMark);
-    }, true);
-    tabb.loadURI(notSharable);
-  }, true);
-}
-
-function testDisable() {
-  let markButton = SocialMark.button;
-  Services.prefs.setBoolPref(prefName, false);
-  is(markButton.hidden, true, "SocialMark button should be hidden when pref is disabled");
-  gFinishCB();
-}
--- a/browser/base/content/test/social/browser_social_multiprovider.js
+++ b/browser/base/content/test/social/browser_social_multiprovider.js
@@ -54,16 +54,20 @@ var tests = {
       });
       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");
 }
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/social/browser_social_shareButton.js
@@ -0,0 +1,328 @@
+/* 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/. */
+
+let prefName = "social.enabled",
+    gFinishCB;
+
+function test() {
+  waitForExplicitFinish();
+
+  // Need to load a http/https/ftp/ftps page for the social share button to appear
+  let tab = gBrowser.selectedTab = gBrowser.addTab("https://example.com", {skipAnimation: true});
+  tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
+    tab.linkedBrowser.removeEventListener("load", tabLoad, true);
+    executeSoon(tabLoaded);
+  }, true);
+
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref(prefName);
+    gBrowser.removeTab(tab);
+  });
+}
+
+function tabLoaded() {
+  ok(Social, "Social module loaded");
+
+  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/moz.png"
+  };
+  runSocialTestWithProvider(manifest, function (finishcb) {
+    gFinishCB = finishcb;
+    testInitial();
+  });
+}
+
+function testInitial(finishcb) {
+  ok(Social.provider, "Social provider is active");
+  ok(Social.provider.enabled, "Social provider is enabled");
+  let port = Social.provider.getWorkerPort();
+  ok(port, "Social provider has a port to its FrameWorker");
+  port.close();
+
+  let {shareButton, unsharePopup} = SocialShareButton;
+  ok(shareButton, "share button exists");
+  ok(unsharePopup, "share popup exists");
+
+  let okButton = document.getElementById("unsharePopupContinueSharingButton");
+  let undoButton = document.getElementById("unsharePopupStopSharingButton");
+  let shareStatusLabel = document.getElementById("share-button-status");
+
+  // ensure the worker initialization and handshakes are all done and we
+  // have a profile and the worker has responsed to the recommend-prompt msg.
+  waitForCondition(function() Social.provider.profile && Social.provider.recommendInfo != null, function() {
+    is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' attribute before share button is clicked");
+    // check dom values
+    let profile = Social.provider.profile;
+    let portrait = document.getElementById("socialUserPortrait").getAttribute("src");
+    is(profile.portrait, portrait, "portrait is set");
+    let displayName = document.getElementById("socialUserDisplayName");
+    is(displayName.label, profile.displayName, "display name is set");
+    ok(!document.getElementById("unsharePopupHeader").hidden, "user profile is visible");
+
+    // Check the strings from our worker actually ended up on the button.
+    is(shareButton.getAttribute("tooltiptext"), "Share this page", "check tooltip text is correct");
+    is(shareStatusLabel.getAttribute("value"), "", "check status label text is blank");
+    // Check the relative URL was resolved correctly (note this image has offsets of zero...)
+    is(shareButton.src, 'https://example.com/browser/browser/base/content/test/social/social_share_image.png', "check image url is correct");
+
+    // Test clicking the share button
+    shareButton.addEventListener("click", function listener() {
+      shareButton.removeEventListener("click", listener);
+      is(shareButton.hasAttribute("shared"), true, "Share button should have 'shared' attribute after share button is clicked");
+      is(shareButton.getAttribute("tooltiptext"), "Unshare this page", "check tooltip text is correct");
+      is(shareStatusLabel.getAttribute("value"), "This page has been shared", "check status label text is correct");
+      // Check the URL and offsets were applied correctly
+      is(shareButton.src, 'https://example.com/browser/browser/base/content/test/social/social_share_image.png', "check image url is correct");
+      executeSoon(testSecondClick.bind(window, testPopupOKButton));
+    });
+    shareButton.click();
+  }, "provider didn't provide user-recommend-prompt response");
+}
+
+function testSecondClick(nextTest) {
+  let {shareButton, unsharePopup} = SocialShareButton;
+  unsharePopup.addEventListener("popupshown", function listener() {
+    unsharePopup.removeEventListener("popupshown", listener);
+    ok(true, "popup was shown after second click");
+    executeSoon(nextTest);
+  });
+  shareButton.click();
+}
+
+function testPopupOKButton() {
+  let {shareButton, unsharePopup} = SocialShareButton;
+  let okButton = document.getElementById("unsharePopupContinueSharingButton");
+  unsharePopup.addEventListener("popuphidden", function listener() {
+    unsharePopup.removeEventListener("popuphidden", listener);
+    is(shareButton.hasAttribute("shared"), true, "Share button should still have 'shared' attribute after OK button is clicked");
+    executeSoon(testSecondClick.bind(window, testPopupUndoButton));
+  });
+  okButton.click();
+}
+
+function testPopupUndoButton() {
+  let {shareButton, unsharePopup} = SocialShareButton;
+  let undoButton = document.getElementById("unsharePopupStopSharingButton");
+  unsharePopup.addEventListener("popuphidden", function listener() {
+    unsharePopup.removeEventListener("popuphidden", listener);
+    is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' attribute after Undo button is clicked");
+    executeSoon(testShortcut);
+  });
+  undoButton.click();
+}
+
+function testShortcut() {
+  let keyTarget = window;
+  keyTarget.addEventListener("keyup", function listener() {
+    keyTarget.removeEventListener("keyup", listener);
+    executeSoon(checkShortcutWorked.bind(window, keyTarget));
+  });
+  EventUtils.synthesizeKey("l", {accelKey: true, shiftKey: true}, keyTarget);
+}
+
+function checkShortcutWorked(keyTarget) {
+  let {unsharePopup, shareButton} = SocialShareButton;
+  is(shareButton.hasAttribute("shared"), true, "Share button should be in the 'shared' state after keyboard shortcut is used");
+
+  // Test a second invocation of the shortcut
+  unsharePopup.addEventListener("popupshown", function listener() {
+    unsharePopup.removeEventListener("popupshown", listener);
+    ok(true, "popup was shown after second use of keyboard shortcut");
+    executeSoon(checkOKButton);
+  });
+  EventUtils.synthesizeKey("l", {accelKey: true, shiftKey: true}, keyTarget);
+}
+
+function checkOKButton() {
+  let okButton = document.getElementById("unsharePopupContinueSharingButton");
+  let undoButton = document.getElementById("unsharePopupStopSharingButton");
+  is(document.activeElement, okButton, "ok button should be focused by default");
+
+  // the undo button text, label text, access keys, etc  should be as
+  // specified by the provider.
+  function isEltAttr(eltid, attr, expected) {
+    is(document.getElementById(eltid).getAttribute(attr), expected,
+       "element '" + eltid + "' has correct value for attribute '" + attr + "'");
+  }
+  isEltAttr("socialUserRecommendedText", "value", "You have already shared this page");
+  isEltAttr("unsharePopupContinueSharingButton", "label", "Got it!");
+  isEltAttr("unsharePopupContinueSharingButton", "accesskey", "G");
+  isEltAttr("unsharePopupStopSharingButton", "label", "Unshare it!");
+  isEltAttr("unsharePopupStopSharingButton", "accesskey", "U");
+  isEltAttr("socialUserPortrait", "aria-label", "Your pretty face");
+
+  // This rest of particular test doesn't really apply on Mac, since buttons
+  // aren't focusable by default.
+  if (navigator.platform.contains("Mac")) {
+    executeSoon(testCloseBySpace);
+    return;
+  }
+
+  let displayName = document.getElementById("socialUserDisplayName");
+
+  // Linux has the buttons in the [unshare] [ok] order, so displayName will come first.
+  if (navigator.platform.contains("Linux")) {
+    checkNextInTabOrder(displayName, function () {
+      checkNextInTabOrder(undoButton, function () {
+        checkNextInTabOrder(okButton, testCloseBySpace);
+      });
+    });
+  } else {
+    checkNextInTabOrder(undoButton, function () {
+      checkNextInTabOrder(displayName, function () {
+        checkNextInTabOrder(okButton, testCloseBySpace);
+      });
+    });
+  }
+}
+
+function checkNextInTabOrder(element, next) {
+  function listener() {
+    element.removeEventListener("focus", listener);
+    is(document.activeElement, element, element.id + " should be next in tab order");
+    executeSoon(next);
+  }
+  element.addEventListener("focus", listener);
+  // Register a cleanup function to remove the listener in case this test fails
+  registerCleanupFunction(function () {
+    element.removeEventListener("focus", listener);
+  });
+  EventUtils.synthesizeKey("VK_TAB", {});
+}
+
+function testCloseBySpace() {
+  let unsharePopup = SocialShareButton.unsharePopup;
+  is(document.activeElement.id, "unsharePopupContinueSharingButton", "testCloseBySpace, the ok button should be focused");
+  unsharePopup.addEventListener("popuphidden", function listener() {
+    unsharePopup.removeEventListener("popuphidden", listener);
+    ok(true, "space closed the share popup");
+    executeSoon(testStillSharedIn2Tabs);
+  });
+  EventUtils.synthesizeKey("VK_SPACE", {});
+}
+
+function testStillSharedIn2Tabs() {
+  let toShare = "http://example.com";
+  let {shareButton} = SocialShareButton;
+  let initialTab = gBrowser.selectedTab;
+  if (shareButton.hasAttribute("shared")) {
+    SocialShareButton.unsharePage();
+  }
+  is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' for the initial tab");
+  let tab1 = gBrowser.selectedTab = gBrowser.addTab(toShare);
+  let tab1b = gBrowser.getBrowserForTab(tab1);
+
+  tab1b.addEventListener("load", function tabLoad(event) {
+    tab1b.removeEventListener("load", tabLoad, true);
+    let tab2 = gBrowser.selectedTab = gBrowser.addTab(toShare);
+    let tab2b = gBrowser.getBrowserForTab(tab2);
+    tab2b.addEventListener("load", function tabLoad(event) {
+      tab2b.removeEventListener("load", tabLoad, true);
+      // should start without either page being shared.
+      is(shareButton.hasAttribute("shared"), false, "Share button should not have 'shared' before we've done anything");
+      shareButton.click();
+      is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
+      // and switching to the first tab (with the same URL) should still reflect shared.
+      gBrowser.selectedTab = tab1;
+      is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
+      // but switching back the initial one should reflect not shared.
+      gBrowser.selectedTab = initialTab;
+      is(shareButton.hasAttribute("shared"), false, "Initial tab should not reflect shared");
+
+      gBrowser.selectedTab = tab1;
+      SocialShareButton.unsharePage();
+      gBrowser.removeTab(tab1);
+      gBrowser.removeTab(tab2);
+      executeSoon(testStillSharedAfterReopen);
+    }, true);
+  }, true);
+}
+
+function testStillSharedAfterReopen() {
+  let toShare = "http://example.com";
+  let {shareButton} = SocialShareButton;
+
+  is(shareButton.hasAttribute("shared"), false, "Reopen: Share button should not have 'shared' for the initial tab");
+  let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
+  let tabb = gBrowser.getBrowserForTab(tab);
+  tabb.addEventListener("load", function tabLoad(event) {
+    tabb.removeEventListener("load", tabLoad, true);
+    SocialShareButton.sharePage();
+    is(shareButton.hasAttribute("shared"), true, "Share button should reflect the share");
+    gBrowser.removeTab(tab);
+    // should be on the initial unshared tab now.
+    is(shareButton.hasAttribute("shared"), false, "Initial tab should be selected and be unshared.");
+    // now open the same URL - should be back to shared.
+    tab = gBrowser.selectedTab = gBrowser.addTab(toShare, {skipAnimation: true});
+    tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
+      tab.linkedBrowser.removeEventListener("load", tabLoad, true);
+      executeSoon(function() {
+        is(shareButton.hasAttribute("shared"), true, "New tab to previously shared URL should reflect shared");
+        SocialShareButton.unsharePage();
+        gBrowser.removeTab(tab);
+        executeSoon(testOnlyShareCertainUrlsTabSwitch);
+      });
+    }, true);
+  }, true);
+}
+
+function testOnlyShareCertainUrlsTabSwitch() {
+  let toShare = "http://example.com";
+  let notSharable = "about:blank";
+  let {shareButton} = SocialShareButton;
+  let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
+  let tabb = gBrowser.getBrowserForTab(tab);
+  tabb.addEventListener("load", function tabLoad(event) {
+    tabb.removeEventListener("load", tabLoad, true);
+    ok(!shareButton.hidden, "share button not hidden for http url");
+    let tab2 = gBrowser.selectedTab = gBrowser.addTab(notSharable);
+    let tabb2 = gBrowser.getBrowserForTab(tab2);
+    tabb2.addEventListener("load", function tabLoad(event) {
+      tabb2.removeEventListener("load", tabLoad, true);
+      ok(shareButton.hidden, "share button hidden for about:blank");
+      gBrowser.selectedTab = tab;
+      ok(!shareButton.hidden, "share button re-shown when switching back to http: url");
+      gBrowser.selectedTab = tab2;
+      ok(shareButton.hidden, "share button re-hidden when switching back to about:blank");
+      gBrowser.removeTab(tab);
+      gBrowser.removeTab(tab2);
+      executeSoon(testOnlyShareCertainUrlsSameTab);
+    }, true);
+  }, true);
+}
+
+function testOnlyShareCertainUrlsSameTab() {
+  let toShare = "http://example.com";
+  let notSharable = "about:blank";
+  let {shareButton} = SocialShareButton;
+  let tab = gBrowser.selectedTab = gBrowser.addTab(toShare);
+  let tabb = gBrowser.getBrowserForTab(tab);
+  tabb.addEventListener("load", function tabLoad(event) {
+    tabb.removeEventListener("load", tabLoad, true);
+    ok(!shareButton.hidden, "share button not hidden for http url");
+    tabb.addEventListener("load", function tabLoad(event) {
+      tabb.removeEventListener("load", tabLoad, true);
+      ok(shareButton.hidden, "share button hidden for about:blank");
+      tabb.addEventListener("load", function tabLoad(event) {
+        tabb.removeEventListener("load", tabLoad, true);
+        ok(!shareButton.hidden, "share button re-enabled http url");
+        gBrowser.removeTab(tab);
+        executeSoon(testDisable);
+      }, true);
+      tabb.loadURI(toShare);
+    }, true);
+    tabb.loadURI(notSharable);
+  }, true);
+}
+
+function testDisable() {
+  let shareButton = SocialShareButton.shareButton;
+  Services.prefs.setBoolPref(prefName, false);
+  is(shareButton.hidden, true, "Share button should be hidden when pref is disabled");
+  gFinishCB();
+}
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -180,35 +180,33 @@ function checkSocialUI(win) {
     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?");
 
-  let markVisible = enabled && provider.pageMarkInfo;
-  let canMark = markVisible && win.SocialMark.canMarkPage(win.gBrowser.currentURI);
-  isbool(!win.SocialMark.button.hidden, markVisible, "SocialMark button visible?");
-  isbool(!win.SocialMark.button.disabled, canMark, "SocialMark button enabled?");
+  let canShare = enabled && provider.recommendInfo && Social.haveLoggedInUser() && win.SocialShareButton.canSharePage(win.gBrowser.currentURI)
+  isbool(!win.SocialShareButton.shareButton.hidden, canShare, "share button visible?");
   isbool(!doc.getElementById("social-toolbar-item").hidden, active, "toolbar items visible?");
   if (active)
     is(win.SocialToolbar.button.style.listStyleImage, 'url("' + Social.defaultProvider.iconURL + '")', "toolbar button has provider icon");
   // the menus should always have the provider name
   if (provider) {
     for (let id of ["menu_socialSidebar", "menu_socialAmbientMenu"])
       is(document.getElementById(id).getAttribute("label"), Social.provider.name, "element has the provider name");
   }
 
   // and for good measure, check all the social commands.
   isbool(!doc.getElementById("Social:Toggle").hidden, active, "Social:Toggle visible?");
   isbool(!doc.getElementById("Social:ToggleNotifications").hidden, enabled, "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:TogglePageMark").getAttribute("disabled"), canMark ? "false" : "true", "Social:TogglePageMark enabled?");
+  is(doc.getElementById("Social:SharePage").getAttribute("disabled"), canShare ? "false" : "true", "Social:SharePage visible?");
 
   // broadcasters.
   isbool(!doc.getElementById("socialActiveBroadcaster").hidden, active, "socialActiveBroadcaster hidden?");
 }
 
 // blocklist testing
 function updateBlocklist(aCallback) {
   var blocklistNotifier = Cc["@mozilla.org/extensions/blocklist;1"]
rename from browser/base/content/test/social/social_mark_image.png
rename to browser/base/content/test/social/social_share_image.png
--- a/browser/base/content/test/social/social_worker.js
+++ b/browser/base/content/test/social/social_worker.js
@@ -95,44 +95,52 @@ onconnect = function(e) {
           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});
-        port.postMessage({
-          topic: "social.page-mark-config",
-          data: {
-            images: {
-              // this one is relative to test we handle relative ones.
-              marked: "/browser/browser/base/content/test/social/social_mark_image.png",
-              // absolute to check we handle them too.
-              unmarked: "https://example.com/browser/browser/base/content/test/social/social_mark_image.png"
-            },
-            messages: {
-              unmarkedTooltip: "Mark this page",
-              markedTooltip: "Unmark this page",
-              unmarkedLabel: "Mark",
-              markedLabel: "Unmark",
-            }
-          }
-        });
         break;
       case "test-ambient-notification":
         let icon = {
           name: "testIcon",
           iconURL: "chrome://browser/skin/Info.png",
           contentPanel: "https://example.com/browser/browser/base/content/test/social/social_panel.html",
           counter: 1
         };
         apiPort.postMessage({topic: "social.ambient-notification", data: icon});
         break;
       case "test-isVisible":
         sidebarPort.postMessage({topic: "test-isVisible"});
         break;
       case "test-isVisible-response":
         testPort.postMessage({topic: "got-isVisible-response", result: event.data.result});
         break;
+      case "social.user-recommend-prompt":
+        port.postMessage({
+          topic: "social.user-recommend-prompt-response",
+          data: {
+            images: {
+              // this one is relative to test we handle relative ones.
+              share: "browser/browser/base/content/test/social/social_share_image.png",
+              // absolute to check we handle them too.
+              unshare: "https://example.com/browser/browser/base/content/test/social/social_share_image.png"
+            },
+            messages: {
+              shareTooltip: "Share this page",
+              unshareTooltip: "Unshare this page",
+              sharedLabel: "This page has been shared",
+              unsharedLabel: "This page is no longer shared",
+              unshareLabel: "You have already shared this page",
+              portraitLabel: "Your pretty face",
+              unshareConfirmLabel: "Unshare it!",
+              unshareConfirmAccessKey: "U",
+              unshareCancelLabel: "Got it!",
+              unshareCancelAccessKey: "G"
+            }
+          }
+        });
+        break;
     }
   }
 }
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -110,17 +110,17 @@ These should match what Safari and other
 
 <!ENTITY closeWindow.label "Close Window">
 <!ENTITY closeWindow.accesskey "d">
 
 <!ENTITY bookmarksMenu.label "Bookmarks">
 <!ENTITY bookmarksMenu.accesskey "B">
 <!ENTITY bookmarkThisPageCmd.label "Bookmark This Page">
 <!ENTITY bookmarkThisPageCmd.commandkey "d">
-<!ENTITY markPageCmd.commandkey "l">
+<!ENTITY sharePageCmd.commandkey "l">
 <!ENTITY subscribeToPageMenupopup.label "Subscribe to This Page">
 <!ENTITY subscribeToPageMenuitem.label "Subscribe to This Page…">
 <!ENTITY addCurPagesCmd.label "Bookmark All Tabs…">
 <!ENTITY showAllBookmarks2.label "Show All Bookmarks">
 <!ENTITY unsortedBookmarksCmd.label "Unsorted Bookmarks">
 <!ENTITY bookmarksToolbarChevron.tooltip "Show more bookmarks">
 
 <!ENTITY backCmd.label                "Back">
@@ -639,20 +639,16 @@ just addresses the organization to follo
 <!ENTITY social.learnMore.label "Learn more…">
 <!ENTITY social.learnMore.accesskey "l">
 <!ENTITY social.closeNotificationItem.label "Not Now">
 
 <!ENTITY social.chatBar.commandkey "c">
 <!ENTITY social.chatBar.label "Focus chats">
 <!ENTITY social.chatBar.accesskey "c">
 
-<!-- labels are set dynamically, see browser.properties -->
-<!ENTITY social.markpage.accesskey "m">
-<!ENTITY social.marklink.accesskey "M">
-
 <!ENTITY getUserMedia.selectCamera.label "Camera to share:">
 <!ENTITY getUserMedia.selectCamera.accesskey "C">
 <!ENTITY getUserMedia.selectMicrophone.label "Microphone to share:">
 <!ENTITY getUserMedia.selectMicrophone.accesskey "M">
 
 <!ENTITY webrtcIndicatorButton.label "Camera / Microphone Access">
 <!ENTITY webrtcIndicatorButton.tooltip "Display sites you are currently sharing your camera or microphone with">
 
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -381,23 +381,16 @@ service.install.learnmore=Learn More…
 
 # LOCALIZATION NOTE (social.turnOff.label): %S is the name of the social provider
 social.turnOff.label=Turn off %S
 social.turnOff.accesskey=T
 # LOCALIZATION NOTE (social.turnOn.label): %S is the name of the social provider
 social.turnOn.label=Turn on %S
 social.turnOn.accesskey=T
 
-# LOCALIZATION NOTE (social.markpage.label): %S is the name of the social provider
-social.markpage.label=Send Page to %S
-social.unmarkpage.label=Remove Page from %S
-# LOCALIZATION NOTE (social.marklink.label): %S is the name of the social provider
-social.marklink.label=Send Link to %S
-social.unmarklink.label=Remove Link from %S
-
 # LOCALIZATION NOTE (social.error.message): %1$S is brandShortName (e.g. Firefox), %2$S is the name of the social provider
 social.error.message=%1$S is unable to connect with %2$S right now.
 social.error.tryAgain.label=Try Again
 social.error.tryAgain.accesskey=T
 social.error.closeSidebar.label=Close This Sidebar
 social.error.closeSidebar.accesskey=C
 
 # LOCALIZATION NOTE: %1$S is the label for the toolbar button, %2$S is the associated badge numbering that the social provider may provide.
--- a/browser/modules/Social.jsm
+++ b/browser/modules/Social.jsm
@@ -10,20 +10,16 @@ const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "SocialService",
   "resource://gre/modules/SocialService.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
-  "resource://gre/modules/PlacesUtils.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Promise",
-  "resource://gre/modules/commonjs/sdk/core/promise.js");
 
 // Add a pref observer for the enabled state
 function prefObserver(subject, topic, data) {
   let enable = Services.prefs.getBoolPref("social.enabled");
   if (enable && !Social.provider) {
     Social.provider = Social.defaultProvider;
   } else if (!enable && Social.provider) {
     Social.provider = null;
@@ -31,56 +27,16 @@ function prefObserver(subject, topic, da
 }
 
 Services.prefs.addObserver("social.enabled", prefObserver, false);
 Services.obs.addObserver(function xpcomShutdown() {
   Services.obs.removeObserver(xpcomShutdown, "xpcom-shutdown");
   Services.prefs.removeObserver("social.enabled", prefObserver);
 }, "xpcom-shutdown", false);
 
-function promiseSetAnnotation(aURI, providerList) {
-  let deferred = Promise.defer();
-
-  // Delaying to catch issues with asynchronous behavior while waiting
-  // to implement asynchronous annotations in bug 699844.
-  Services.tm.mainThread.dispatch(function() {
-    try {
-      if (providerList && providerList.length > 0) {
-        PlacesUtils.annotations.setPageAnnotation(
-          aURI, "social/mark", JSON.stringify(providerList), 0,
-          PlacesUtils.annotations.EXPIRE_WITH_HISTORY);
-      } else {
-        PlacesUtils.annotations.removePageAnnotation(aURI, "social/mark");
-      }
-    } catch(e) {
-      Cu.reportError("SocialAnnotation failed: " + e);
-    }
-    deferred.resolve();
-  }, Ci.nsIThread.DISPATCH_NORMAL);
-
-  return deferred.promise;
-}
-
-function promiseGetAnnotation(aURI) {
-  let deferred = Promise.defer();
-
-  // Delaying to catch issues with asynchronous behavior while waiting
-  // to implement asynchronous annotations in bug 699844.
-  Services.tm.mainThread.dispatch(function() {
-    let val = null;
-    try {
-      val = PlacesUtils.annotations.getPageAnnotation(aURI, "social/mark");
-    } catch (ex) { }
-
-    deferred.resolve(val);
-  }, Ci.nsIThread.DISPATCH_NORMAL);
-
-  return deferred.promise;
-}
-
 this.Social = {
   initialized: false,
   lastEventReceived: 0,
   providers: null,
   _disabledForSafeMode: false,
 
   get _currentProviderPref() {
     try {
@@ -252,116 +208,72 @@ this.Social = {
     let oldProvider = this._getProviderFromOrigin(oldOrigin);
     if (!oldProvider && this.providers.length)
       oldProvider = this.providers[0];
     this.provider = oldProvider;
     if (provider)
       SocialService.removeProvider(origin);
   },
 
-  // Page Marking functionality
-  _getMarkablePageUrl: function Social_getMarkablePageUrl(aURI) {
+  // Sharing functionality
+  _getShareablePageUrl: function Social_getShareablePageUrl(aURI) {
     let uri = aURI.clone();
     try {
       // Setting userPass on about:config throws.
       uri.userPass = "";
     } catch (e) {}
     return uri.spec;
   },
 
-  isURIMarked: function(aURI, aCallback) {
-    promiseGetAnnotation(aURI).then(function(val) {
-      if (val) {
-        let providerList = JSON.parse(val);
-        val = providerList.indexOf(this.provider.origin) >= 0;
-      }
-      aCallback(!!val);
-    }.bind(this));
+  isPageShared: function Social_isPageShared(aURI) {
+    let url = this._getShareablePageUrl(aURI);
+    return this._sharedUrls.hasOwnProperty(url);
   },
 
-  markURI: function(aURI, aCallback) {
+  sharePage: function Social_sharePage(aURI) {
     // this should not be called if this.provider or the port is null
     if (!this.provider) {
-      Cu.reportError("Can't mark a page when no provider is current");
+      Cu.reportError("Can't share a page when no provider is current");
       return;
     }
     let port = this.provider.getWorkerPort();
     if (!port) {
-      Cu.reportError("Can't mark page as no provider port is available");
+      Cu.reportError("Can't share page as no provider port is available");
       return;
     }
-
-    // update or set our annotation
-    promiseGetAnnotation(aURI).then(function(val) {
+    let url = this._getShareablePageUrl(aURI);
+    this._sharedUrls[url] = true;
+    port.postMessage({
+      topic: "social.user-recommend",
+      data: { url: url }
+    });
+    port.close();
+  },
 
-      let providerList = val ? JSON.parse(val) : [];
-      let marked = providerList.indexOf(this.provider.origin) >= 0;
-      if (marked)
-        return;
-      providerList.push(this.provider.origin);
-      // we allow marking links in a page that may not have been visited yet.
-      // make sure there is a history entry for the uri, then annotate it.
-      let place = {
-        uri: aURI,
-        visits: [{
-          visitDate: Date.now() + 1000,
-          transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
-        }]
-      };
-      PlacesUtils.asyncHistory.updatePlaces(place, {
-        handleError: function () Cu.reportError("couldn't update history for socialmark annotation"),
-        handleResult: function () {},
-        handleCompletion: function () {
-          promiseSetAnnotation(aURI, providerList).then();
-          // post to the provider
-          let url = this._getMarkablePageUrl(aURI);
-          port.postMessage({
-            topic: "social.page-mark",
-            data: { url: url, 'marked': true }
-          });
-          port.close();
-          if (aCallback)
-            schedule(function() { aCallback(true); } );
-        }.bind(this)
-      });
-    }.bind(this));
-  },
-  
-  unmarkURI: function(aURI, aCallback) {
+  unsharePage: function Social_unsharePage(aURI) {
     // this should not be called if this.provider or the port is null
     if (!this.provider) {
-      Cu.reportError("Can't mark a page when no provider is current");
+      Cu.reportError("Can't unshare a page when no provider is current");
       return;
     }
     let port = this.provider.getWorkerPort();
     if (!port) {
-      Cu.reportError("Can't mark page as no provider port is available");
+      Cu.reportError("Can't unshare page as no provider port is available");
       return;
     }
+    let url = this._getShareablePageUrl(aURI);
+    delete this._sharedUrls[url];
+    port.postMessage({
+      topic: "social.user-unrecommend",
+      data: { url: url }
+    });
+    port.close();
+  },
 
-    // set our annotation
-    promiseGetAnnotation(aURI).then(function(val) {
-      let providerList = val ? JSON.parse(val) : [];
-      let marked = providerList.indexOf(this.provider.origin) >= 0;
-      if (marked) {
-        // remove the annotation
-        providerList.splice(providerList.indexOf(this.provider.origin), 1);
-        promiseSetAnnotation(aURI, providerList).then();
-      }
-      // post to the provider regardless
-      let url = this._getMarkablePageUrl(aURI);
-      port.postMessage({
-        topic: "social.page-mark",
-        data: { url: url, 'marked': false }
-      });
-      port.close();
-      if (aCallback)
-        schedule(function() { aCallback(false); } );
-    }.bind(this));
-  },
+  _sharedUrls: {},
 
   setErrorListener: function(iframe, errorHandler) {
     if (iframe.socialErrorListener)
       return iframe.socialErrorListener;
     return new SocialErrorListener(iframe, errorHandler);
   }
 };
 
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1430,20 +1430,60 @@ richlistitem[type~="action"][actiontype=
 
 /* Popup blocker button */
 #page-report-button {
   list-style-image: url("chrome://browser/skin/Info.png");
 }
 
 /* social recommending panel */
 
-#social-mark-button {
+#share-button {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
+#socialUserPortrait {
+  width: 48px;
+  height: 48px;
+  list-style-image:url("chrome://global/skin/icons/information-32.png");
+}
+
+#socialUserDisplayName,
+#socialUserPortrait {
+  cursor: pointer;
+}
+
+#socialUserDisplayName {
+  -moz-appearance: none;
+  border: none;
+  background-color: transparent;
+  margin-top: 0;
+  padding-top: 0;
+  font-size: 130%;
+  font-weight: bold;
+}
+
+#socialUserDisplayName > .button-box {
+  -moz-padding-start: 0;
+  padding-top: 0;
+  border-top-width: 0;
+}
+
+#socialUserDisplayName:hover {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+#unsharePopupText {
+  height: 48px;
+}
+
+#unsharePopupBottomButtons {
+  margin-top: 1em;
+}
+
 /* bookmarks menu-button */
 
 #bookmarks-menu-button {
   list-style-image: url("chrome://browser/skin/Toolbar.png");
   -moz-image-region: rect(0px 216px 24px 192px);
 }
 
 #bookmarks-menu-button[starred] {
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -1652,20 +1652,60 @@ window[tabsontop="false"] richlistitem[t
   #page-report-button:hover:active,
   #page-report-button[open="true"] {
     -moz-image-region: rect(0, 64px, 32px, 32px);
   }
 }
 
 /* social recommending panel */
 
-#social-mark-button {
+#share-button {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
+#socialUserPortrait {
+  width: 48px;
+  height: 48px;
+  list-style-image:url("chrome://global/skin/icons/information-32.png");
+}
+
+#socialUserDisplayName,
+#socialUserPortrait {
+  cursor: pointer;
+}
+
+#socialUserDisplayName {
+  -moz-appearance: none;
+  border: none;
+  background-color: transparent;
+  margin: 1px;
+  padding: 0;
+  font-size: 130%;
+  font-weight: bold;
+}
+
+#socialUserDisplayName > .button-box {
+  -moz-padding-start: 0;
+  padding-top: 0;
+  border-top-width: 0;
+}
+
+#socialUserDisplayName:hover {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+#unsharePopupText {
+  height: 48px;
+}
+
+#unsharePopupBottomButtons {
+  margin-top: 1em;
+}
+
 /* bookmarks menu-button */
 
 #bookmarks-menu-button {
   -moz-image-region: rect(0px 500px 20px 480px);
 }
 
 #bookmarks-menu-button[starred] {
   -moz-image-region: rect(20px 500px 40px 480px);
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -1710,20 +1710,60 @@ richlistitem[type~="action"][actiontype=
 
 #page-report-button:hover:active,
 #page-report-button[open="true"] {
   -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 /* social recommending panel */
 
-#social-mark-button {
+#share-button {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
+#socialUserPortrait {
+  width: 48px;
+  height: 48px;
+  list-style-image:url("chrome://global/skin/icons/information-32.png");
+}
+
+#socialUserDisplayName,
+#socialUserPortrait {
+  cursor: pointer;
+}
+
+#socialUserDisplayName {
+  -moz-appearance: none;
+  border: none;
+  background-color: transparent;
+  margin-top: 0;
+  padding-top: 0;
+  font-size: 130%;
+  font-weight: bold;
+}
+
+#socialUserDisplayName > .button-box {
+  -moz-padding-start: 0;
+  padding-top: 0;
+  border-top-width: 0;
+}
+
+#socialUserDisplayName:hover {
+  color: -moz-nativehyperlinktext;
+  text-decoration: underline;
+}
+
+#unsharePopupText {
+  height: 48px;
+}
+
+#unsharePopupBottomButtons {
+  margin-top: 1em;
+}
+
 /* bookmarks menu-button */
 
 #bookmarks-menu-button {
   -moz-image-region: rect(0px 378px 18px 360px);
 }
 
 #bookmarks-menu-button[starred] {
   -moz-image-region: rect(18px 378px 36px 360px);
--- a/toolkit/components/social/SocialService.jsm
+++ b/toolkit/components/social/SocialService.jsm
@@ -621,67 +621,72 @@ SocialProvider.prototype = {
   // no FrameWorker)
   // This distinction might be used to cache certain data between runs - eg,
   // browser-social.js caches the notification icons so they can be displayed
   // quickly at startup without waiting for the provider to initialize -
   // 'undefined' means 'ok to use cached values' versus 'null' meaning 'cached
   // values aren't to be used as the user is logged out'.
   profile: undefined,
 
-  // Contains the information necessary to support our page mark feature.
+  // Contains the information necessary to support our "recommend" feature.
   // null means no info yet provided (which includes the case of the provider
   // not supporting the feature) or the provided data is invalid.  Updated via
-  // the 'pageMarkInfo' setter and returned via the getter.
-  _pageMarkInfo: null,
-  get pageMarkInfo() {
-    return this._pageMarkInfo;
+  // the 'recommendInfo' setter and returned via the getter.
+  _recommendInfo: null,
+  get recommendInfo() {
+    return this._recommendInfo;
   },
-  set pageMarkInfo(data) {
-    // Accept *and validate* the page-mark-config message from the provider.
+  set recommendInfo(data) {
+    // Accept *and validate* the user-recommend-prompt-response message from
+    // the provider.
     let promptImages = {};
     let promptMessages = {};
     function reportError(reason) {
-      Cu.reportError("Invalid page-mark data from provider: " + reason + ": marking is disabled for this provider");
-      // and we explicitly reset the page-mark data to null to avoid stale
+      Cu.reportError("Invalid recommend data from provider: " + reason + ": sharing is disabled for this provider");
+      // and we explicitly reset the recommend data to null to avoid stale
       // data being used and notify our observers.
-      this._pageMarkInfo = null;
-      Services.obs.notifyObservers(null, "social:page-mark-config", this.origin);
+      this._recommendInfo = null;
+      Services.obs.notifyObservers(null, "social:recommend-info-changed", this.origin);
     }
     if (!data ||
         !data.images || typeof data.images != "object" ||
         !data.messages || typeof data.messages != "object") {
       reportError("data is missing valid 'images' or 'messages' elements");
       return;
     }
-    for (let sub of ["marked", "unmarked"]) {
+    for (let sub of ["share", "unshare"]) {
       let url = data.images[sub];
       if (!url || typeof url != "string" || url.length == 0) {
-        reportError('images["' + sub + '"] is not a valid string');
+        reportError('images["' + sub + '"] is missing or not a non-empty string');
         return;
       }
       // resolve potentially relative URLs but there is no same-origin check
       // for images to help providers utilize content delivery networks...
       // Also note no scheme checks are necessary - even a javascript: URL
       // is safe as gecko evaluates them in a sandbox.
       let imgUri = this.resolveUri(url);
       if (!imgUri) {
         reportError('images["' + sub + '"] is an invalid URL');
         return;
       }
       promptImages[sub] = imgUri.spec;
     }
-    for (let sub of ["markedTooltip", "unmarkedTooltip", "markedLabel", "unmarkedLabel"]) {
+    for (let sub of ["shareTooltip", "unshareTooltip",
+                     "sharedLabel", "unsharedLabel", "unshareLabel",
+                     "portraitLabel",
+                     "unshareConfirmLabel", "unshareConfirmAccessKey",
+                     "unshareCancelLabel", "unshareCancelAccessKey"]) {
       if (typeof data.messages[sub] != "string" || data.messages[sub].length == 0) {
         reportError('messages["' + sub + '"] is not a valid string');
         return;
       }
       promptMessages[sub] = data.messages[sub];
     }
-    this._pageMarkInfo = {images: promptImages, messages: promptMessages};
-    Services.obs.notifyObservers(null, "social:page-mark-config", this.origin);
+    this._recommendInfo = {images: promptImages, messages: promptMessages};
+    Services.obs.notifyObservers(null, "social:recommend-info-changed", this.origin);
   },
 
   // Map of objects describing the provider's notification icons, whose
   // properties include:
   //   name, iconURL, counter, contentPanel
   // See https://developer.mozilla.org/en-US/docs/Social_API
   ambientNotificationIcons: null,
 
--- a/toolkit/components/social/WorkerAPI.jsm
+++ b/toolkit/components/social/WorkerAPI.jsm
@@ -53,22 +53,24 @@ WorkerAPI.prototype = {
       getFrameWorkerHandle(this._provider.workerURL, null)._worker.reload();
       // the frameworker is going to be reloaded, send the initialization
       // so it can have the same startup sequence as if it were loaded
       // the first time.  This will be queued until the frameworker is ready.
       this._port.postMessage({topic: "social.initialize"});
     },
     "social.user-profile": function (data) {
       this._provider.updateUserProfile(data);
+      // get the info we need for 'recommend' support.
+      this._port.postMessage({topic: "social.user-recommend-prompt"});
     },
     "social.ambient-notification": function (data) {
       this._provider.setAmbientNotification(data);
     },
-    "social.page-mark-config": function(data) {
-      this._provider.pageMarkInfo = data;
+    "social.user-recommend-prompt-response": function(data) {
+      this._provider.recommendInfo = data;
     },
     "social.cookies-get": function(data) {
       let document = this._port._window.document;
       let cookies = document.cookie.split(";");
       let results = [];
       cookies.forEach(function(aCookie) {
         let [name, value] = aCookie.split("=");
         results.push({name: unescape(name.trim()),