merge fx-team into m-c
authorGavin Sharp <gavin@gavinsharp.com>
Fri, 31 Jan 2014 22:37:45 -0800
changeset 166431 8840c133115a9c261fddfcfa91b8ff64ef92363d
parent 166413 99fd8753c8dd5a4c8578ca7fcd923eeede607182 (current diff)
parent 166430 c21d44250e0c3b35bf23abac778cc63207ccf89f (diff)
child 166432 5376a2ce7b29722a45424d34f79f7ba2c4ce1b14
child 166448 b8b35ef1ea88e46d3b618f59657b3b005cf67c37
push id26121
push usergsharp@mozilla.com
push dateSat, 01 Feb 2014 06:40:04 +0000
treeherdermozilla-central@8840c133115a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.0a1
first release with
nightly linux32
8840c133115a / 29.0a1 / 20140201030204 / files
nightly linux64
8840c133115a / 29.0a1 / 20140201030204 / files
nightly mac
8840c133115a / 29.0a1 / 20140201030204 / files
nightly win64
8840c133115a / 29.0a1 / 20140201030204 / files
nightly win32
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win64
merge fx-team into m-c
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1338,15 +1338,19 @@ pref("network.disable.ipc.security", tru
 
 // CustomizableUI debug logging.
 pref("browser.uiCustomization.debug", false);
 
 // The URL where remote content that composes the UI for Firefox Accounts should
 // be fetched. Must use HTTPS.
 pref("identity.fxaccounts.remote.uri", "https://accounts.firefox.com/?service=sync&context=fx_desktop_v1");
 
+// The URL where remote content that forces re-authentication for Firefox Accounts
+// should be fetched.  Must use HTTPS.
+pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v1");
+
 // The URL we take the user to when they opt to "manage" their Firefox Account.
 // Note that this will always need to be in the same TLD as the
 // "identity.fxaccounts.remote.uri" pref.
 pref("identity.fxaccounts.settings.uri", "https://accounts.firefox.com/settings");
 
 // The URL of the Firefox Accounts auth server backend
 pref("identity.fxaccounts.auth.uri", "https://api.accounts.firefox.com/v1");
--- a/browser/base/content/aboutaccounts/aboutaccounts.js
+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
@@ -53,51 +53,51 @@ function sha256(str) {
   hasher.update(data, data.length);
 
   return hasher.finish(true);
 }
 
 function promptForRelink(acctName) {
   let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
   let continueLabel = sb.GetStringFromName("continue.label");
-  let title = sb.GetStringFromName("relink.verify.title");
-  let description = sb.formatStringFromName("relink.verify.description",
+  let title = sb.GetStringFromName("relinkVerify.title");
+  let description = sb.formatStringFromName("relinkVerify.description",
                                             [acctName], 1);
-  let body = sb.GetStringFromName("relink.verify.heading") +
+  let body = sb.GetStringFromName("relinkVerify.heading") +
              "\n\n" + description;
   let ps = Services.prompt;
   let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
                     (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
                     ps.BUTTON_POS_1_DEFAULT;
   let pressed = Services.prompt.confirmEx(window, title, body, buttonFlags,
                                      continueLabel, null, null, null,
                                      {});
   return pressed == 0; // 0 is the "continue" button
 }
 
 let wrapper = {
   iframe: null,
 
-  init: function () {
+  init: function (url=null) {
     let weave = Cc["@mozilla.org/weave/service;1"]
                   .getService(Ci.nsISupports)
                   .wrappedJSObject;
 
     // Don't show about:accounts with FxA disabled.
     if (!weave.fxAccountsEnabled) {
       document.body.remove();
       return;
     }
 
     let iframe = document.getElementById("remote");
     this.iframe = iframe;
     iframe.addEventListener("load", this);
 
     try {
-      iframe.src = fxAccounts.getAccountsURI();
+      iframe.src = url || fxAccounts.getAccountsURI();
     } catch (e) {
       error("Couldn't init Firefox Account wrapper: " + e.message);
     }
   },
 
   handleEvent: function (evt) {
     switch (evt.type) {
       case "load":
@@ -231,21 +231,24 @@ function getStarted() {
   show("remote");
 }
 
 function openPrefs() {
   window.openPreferences("paneSync");
 }
 
 function init() {
-  let signinQuery = window.location.href.match(/signin=true$/);
-
-  if (signinQuery) {
+  if (window.location.href.contains("action=signin")) {
     show("remote");
     wrapper.init();
+  } else if (window.location.href.contains("action=reauth")) {
+    fxAccounts.promiseAccountsForceSigninURI().then(url => {
+      show("remote");
+      wrapper.init(url);
+    });
   } else {
     // Check if we have a local account
     fxAccounts.getSignedInUser().then(user => {
       if (user) {
         show("stage");
         show("manage");
         let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
         document.title = sb.GetStringFromName("manage.pageTitle");
--- a/browser/base/content/aboutaccounts/aboutaccounts.xhtml
+++ b/browser/base/content/aboutaccounts/aboutaccounts.xhtml
@@ -10,17 +10,17 @@
   <!ENTITY % aboutAccountsDTD SYSTEM "chrome://browser/locale/aboutAccounts.dtd">
   %aboutAccountsDTD;
   <!ENTITY % syncBrandDTD SYSTEM "chrome://browser/locale/syncBrand.dtd">
   %syncBrandDTD;
 ]>
 
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
-   <title>&aboutAccounts.pageTitle;</title>
+   <title>&syncBrand.fullName.label;</title>
    <meta name="viewport" content="width=device-width"/>
 
 
    <link rel="icon" type="image/png" id="favicon"
          href="chrome://branding/content/icon32.png"/>
    <link rel="stylesheet"
      href="chrome://browser/content/aboutaccounts/fonts.css"
      type="text/css" />
@@ -34,33 +34,33 @@
      href="chrome://browser/content/aboutaccounts/aboutaccounts.css"
      type="text/css" />
   </head>
   <body>
     <div id="stage">
 
       <div id="manage">
         <header>
-          <h1>&syncBrand.fxa-plural.label;</h1>
-
-          <h2>&syncBrand.shortName.label;</h2>
+          <h1><span>&syncBrand.fullName.label;</span></h1>
+          <h2><span>&aboutAccounts.welcome;</span></h2>
         </header>
 
         <section>
             <div class="graphic graphic-sync-intro"> </div>
 
             <div class="button-row">
-              <a class="button" href="#" onclick="openPrefs()">Manage</a>
+              <a class="button" href="#" onclick="openPrefs()">&aboutAccountsConfig.manage.label;</a>
             </div>
         </section>
       </div>
 
       <div id="intro">
         <header>
-          <h1>&aboutAccounts.pageTitle;</h1>
+          <h1><span>&syncBrand.fullName.label;</span></h1>
+          <h2><span>&aboutAccounts.welcome;</span></h2>
         </header>
 
         <section>
             <div class="graphic graphic-sync-intro"> </div>
 
             <div class="description">&aboutAccountsConfig.description;</div>
 
             <div class="button-row">
--- a/browser/base/content/aboutaccounts/fonts.css
+++ b/browser/base/content/aboutaccounts/fonts.css
@@ -1,31 +1,25 @@
 @font-face {
   font-family: 'Fira Sans';
   font-style: normal;
   font-weight: 400;
   src: local('Fira Sans'),
        local('FiraSans'),
-       url('fonts/firasans-regular.woff') format('woff'),
-       /*url('/fonts/latin/firasans-regular.ttf') format('truetype'),*/
-       /*url('/fonts/latin/firasans-regular.svg#Fira Sans') format('svg');*/
+       url('fonts/firasans-regular.woff') format('woff');
 }
 @font-face {
   font-family: 'Fira Sans';
   font-style: normal;
   font-weight: 300;
   src: local('Fira Sans Light'),
        local('FiraSansLight'),
-       url('fonts/firasans-light.woff') format('woff'),
-       /*url('/fonts/latin/firasans-light.ttf') format('truetype'),*/
-       /*url('/fonts/latin/firasans-light.svg#Fira Sans') format('svg');*/
+       url('fonts/firasans-light.woff') format('woff');
 }
 @font-face {
   font-family: 'Clear Sans';
   font-style: normal;
   font-weight: 400;
   src: local('Clear Sans'),
        local('ClearSans'),
-       url('fonts/clearsans-regular.woff') format('woff'),
-       /*url('/fonts/latin/clearsans-regular.ttf') format('truetype'),*/
-       /*url('/fonts/latin/clearsans-regular.svg#Clear Sans') format('svg');*/
+       url('fonts/clearsans-regular.woff') format('woff');
 }
 
--- a/browser/base/content/browser-customization.js
+++ b/browser/base/content/browser-customization.js
@@ -95,17 +95,16 @@ let CustomizationHandler = {
     CombinedStopReload.init();
     CombinedBackForward.init();
     UpdateUrlbarSearchSplitterState();
 
     // Update the urlbar
     if (gURLBar) {
       URLBarSetURI();
       XULBrowserWindow.asyncUpdateUI();
-      BookmarkingUI.updateStarState();
     }
 
     // Re-enable parts of the UI we disabled during the dialog
     let menubar = document.getElementById("main-menubar");
     for (let childNode of menubar.childNodes)
       childNode.setAttribute("disabled", false);
     let cmd = document.getElementById("cmd_CustomizeToolbars");
     cmd.removeAttribute("disabled");
--- a/browser/base/content/browser-fxaccounts.js
+++ b/browser/base/content/browser-fxaccounts.js
@@ -181,26 +181,31 @@ let gFxAccounts = {
           this.button.setAttribute("signedin", "true");
           this.button.setAttribute("label", userData.email);
           this.button.setAttribute("tooltiptext", userData.email);
         }
       }
     });
   },
 
-  toggle: function (event) {
+  onMenuPanelCommand: function (event) {
     if (event.originalTarget.hasAttribute("signedin")) {
       this.openPreferences();
     } else {
-      this.openSignInPage();
+      this.openAccountsPage();
     }
 
     PanelUI.hide();
   },
 
   openPreferences: function () {
     openPreferences("paneSync");
   },
 
-  openSignInPage: function () {
+  openAccountsPage: function () {
     switchToTabHavingURI("about:accounts", true);
+  },
+
+  openSignInAgainPage: function () {
+    // FIXME: This should actually show the pre-filled username version of about:accounts?
+    switchToTabHavingURI("about:accounts?signin=true", true);
   }
 };
--- a/browser/base/content/browser-menubar.inc
+++ b/browser/base/content/browser-menubar.inc
@@ -357,28 +357,28 @@
     <menupopup id="bookmarksMenuPopup"
 #ifndef XP_MACOSX
                placespopup="true"
 #endif
                context="placesContext"
                openInTabs="children"
                oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
                onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
-               onpopupshowing="PlacesCommandHook.updateBookmarkAllTabsCommand();
+               onpopupshowing="BookmarkingUI.onMainMenuPopupShowing(event);
                                if (!this.parentNode._placesView)
                                  new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
                tooltip="bhTooltip" popupsinherittooltip="true">
       <menuitem id="bookmarksShowAll"
                 label="&showAllBookmarks2.label;"
                 command="Browser:ShowAllBookmarks"
                 key="manBookmarkKb"/>
       <menuseparator id="organizeBookmarksSeparator"/>
       <menuitem id="menu_bookmarkThisPage"
-                label="&bookmarkThisPageCmd.label;"
                 command="Browser:AddBookmarkAs"
+                observes="bookmarkThisPageBroadcaster"
                 key="addBookmarkAsKb"/>
       <menuitem id="subscribeToPageMenuitem"
 #ifndef XP_MACOSX
                 class="menuitem-iconic"
 #endif
                 label="&subscribeToPageMenuitem.label;"
                 oncommand="return FeedHandler.subscribeToFeed(null, event);"
                 onclick="checkForMiddleClick(this, event);"
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -954,49 +954,58 @@ let PlacesToolbarHelper = {
 //// BookmarkingUI
 
 /**
  * Handles the bookmarks menu-button in the toolbar.
  */
 
 let BookmarkingUI = {
   get button() {
+    delete this.button;
     let widgetGroup = CustomizableUI.getWidget("bookmarks-menu-button");
-    if (widgetGroup.areaType == CustomizableUI.TYPE_TOOLBAR) {
-      return widgetGroup.forWindow(window).node;
-    }
-    return null;
+    return this.button = widgetGroup.forWindow(window).node;
   },
 
+  /* Can't make this a self-deleting getter because it's anonymous content
+   * and might lose/regain bindings at some point. */
   get star() {
-    let button = this.button;
-    return button && document.getAnonymousElementByAttribute(button, "anonid",
-                                                             "button");
+    return document.getAnonymousElementByAttribute(this.button, "anonid",
+                                                   "button");
   },
 
   get anchor() {
+    if (!this._shouldUpdateStarState()) {
+      return null;
+    }
     let widget = CustomizableUI.getWidget("bookmarks-menu-button")
                                .forWindow(window);
     if (widget.overflowed)
       return widget.anchor;
 
-    let star = this.star;
-    return star && document.getAnonymousElementByAttribute(star, "class",
-                                                           "toolbarbutton-icon");
+    return document.getAnonymousElementByAttribute(this.star, "class",
+                                                   "toolbarbutton-icon");
+  },
+
+  get broadcaster() {
+    delete this.broadcaster;
+    let broadcaster = document.getElementById("bookmarkThisPageBroadcaster");
+    return this.broadcaster = broadcaster;
   },
 
   STATUS_UPDATING: -1,
   STATUS_UNSTARRED: 0,
   STATUS_STARRED: 1,
   get status() {
+    if (!this._shouldUpdateStarState()) {
+      return this.STATUS_UNSTARRED;
+    }
     if (this._pendingStmt)
       return this.STATUS_UPDATING;
-    let button = this.button;
-    return button && button.hasAttribute("starred") ? this.STATUS_STARRED
-                                                    : this.STATUS_UNSTARRED;
+    return this.button.hasAttribute("starred") ? this.STATUS_STARRED
+                                               : this.STATUS_UNSTARRED;
   },
 
   get _starredTooltip()
   {
     delete this._starredTooltip;
     return this._starredTooltip =
       gNavigatorBundle.getString("starButtonOn.tooltip");
   },
@@ -1004,16 +1013,25 @@ let BookmarkingUI = {
   get _unstarredTooltip()
   {
     delete this._unstarredTooltip;
     return this._unstarredTooltip =
       gNavigatorBundle.getString("starButtonOff.tooltip");
   },
 
   /**
+   * The type of the area in which the button is currently located.
+   * When in the panel, we don't update the button's icon.
+   */
+  _currentAreaType: null,
+  _shouldUpdateStarState: function() {
+    return this._currentAreaType == CustomizableUI.TYPE_TOOLBAR;
+  },
+
+  /**
    * The popup contents must be updated when the user customizes the UI, or
    * changes the personal toolbar collapsed status.  In such a case, any needed
    * change should be handled in the popupshowing helper, for performance
    * reasons.
    */
   _popupNeedsUpdate: true,
   onToolbarVisibilityChange: function BUI_onToolbarVisibilityChange() {
     this._popupNeedsUpdate = true;
@@ -1064,94 +1082,105 @@ let BookmarkingUI = {
       insertionPoint: ".panel-subview-footer"
     });
   },
 
   /**
    * Handles star styling based on page proxy state changes.
    */
   onPageProxyStateChanged: function BUI_onPageProxyStateChanged(aState) {
-    if (!this.star) {
+    if (!this._shouldUpdateStarState()) {
       return;
     }
 
     if (aState == "invalid") {
       this.star.setAttribute("disabled", "true");
       this.button.removeAttribute("starred");
       this.button.setAttribute("buttontooltiptext", "");
     }
     else {
       this.star.removeAttribute("disabled");
+      this._updateStar();
     }
     this._updateToolbarStyle();
   },
 
+  _updateCustomizationState: function BUI__updateCustomizationState() {
+    let placement = CustomizableUI.getPlacementOfWidget("bookmarks-menu-button");
+    this._currentAreaType = placement && CustomizableUI.getAreaType(placement.area);
+  },
+
   _updateToolbarStyle: function BUI__updateToolbarStyle() {
-    let button = this.button;
-    if (!button)
-      return;
-
-    let personalToolbar = document.getElementById("PersonalToolbar");
-    let onPersonalToolbar = button.parentNode == personalToolbar ||
-                            button.parentNode.parentNode == personalToolbar;
+    let onPersonalToolbar = false;
+    if (this._currentAreaType == CustomizableUI.TYPE_TOOLBAR) {
+      let personalToolbar = document.getElementById("PersonalToolbar");
+      onPersonalToolbar = this.button.parentNode == personalToolbar ||
+                          this.button.parentNode.parentNode == personalToolbar;
+    }
 
     if (onPersonalToolbar) {
-      button.classList.add("bookmark-item");
-      button.classList.remove("toolbarbutton-1");
+      this.button.classList.add("bookmark-item");
+      this.button.classList.remove("toolbarbutton-1");
     }
     else {
-      button.classList.remove("bookmark-item");
-      button.classList.add("toolbarbutton-1");
+      this.button.classList.remove("bookmark-item");
+      this.button.classList.add("toolbarbutton-1");
     }
   },
 
   _uninitView: function BUI__uninitView() {
     // When an element with a placesView attached is removed and re-inserted,
     // XBL reapplies the binding causing any kind of issues and possible leaks,
     // so kill current view and let popupshowing generate a new one.
-    let button = this.button;
-    if (button && button._placesView)
-      button._placesView.uninit();
+    if (this.button._placesView)
+      this.button._placesView.uninit();
   },
 
   customizeStart: function BUI_customizeStart() {
     this._uninitView();
   },
 
   customizeChange: function BUI_customizeChange() {
+    let usedToUpdateStarState = this._shouldUpdateStarState();
+    this._updateCustomizationState();
+    if (usedToUpdateStarState != this._shouldUpdateStarState()) {
+      this.updateStarState();
+    }
     this._updateToolbarStyle();
   },
 
   customizeDone: function BUI_customizeDone() {
     this.onToolbarVisibilityChange();
     this._updateToolbarStyle();
   },
 
   init: function() {
     CustomizableUI.addListener(this);
+    this._updateCustomizationState();
   },
 
   _hasBookmarksObserver: false,
   uninit: function BUI_uninit() {
+    this._updateBookmarkPageMenuItem(true);
     CustomizableUI.removeListener(this);
 
     this._uninitView();
 
     if (this._hasBookmarksObserver) {
       PlacesUtils.removeLazyBookmarkObserver(this);
     }
 
     if (this._pendingStmt) {
       this._pendingStmt.cancel();
       delete this._pendingStmt;
     }
   },
 
   updateStarState: function BUI_updateStarState() {
-    if (!this.button || (this._uri && gBrowser.currentURI.equals(this._uri))) {
+    if (this._uri && gBrowser.currentURI.equals(this._uri)) {
       return;
     }
 
     // Reset tracked values.
     this._uri = gBrowser.currentURI;
     this._itemIds = [];
 
     if (this._pendingStmt) {
@@ -1190,184 +1219,224 @@ let BookmarkingUI = {
         }
       }
 
       delete this._pendingStmt;
     }, this);
   },
 
   _updateStar: function BUI__updateStar() {
-    let button = this.button;
-    if (!button)
+    if (!this._shouldUpdateStarState()) {
+      if (this.button.hasAttribute("starred")) {
+        this.button.removeAttribute("starred");
+        this.button.removeAttribute("buttontooltiptext");
+      }
       return;
+    }
 
-    if (this._itemIds.length > 0) {
-      button.setAttribute("starred", "true");
-      button.setAttribute("buttontooltiptext", this._starredTooltip);
+    if (this._itemIds && this._itemIds.length > 0) {
+      this.button.setAttribute("starred", "true");
+      this.button.setAttribute("buttontooltiptext", this._starredTooltip);
     }
     else {
-      button.removeAttribute("starred");
-      button.setAttribute("buttontooltiptext", this._unstarredTooltip);
+      this.button.removeAttribute("starred");
+      this.button.setAttribute("buttontooltiptext", this._unstarredTooltip);
     }
   },
 
+  /**
+   * forceReset is passed when we're destroyed and the label should go back
+   * to the default (Bookmark This Page) for OS X.
+   */
+  _updateBookmarkPageMenuItem: function BUI__updateBookmarkPageMenuItem(forceReset) {
+    let isStarred = !forceReset && this._itemIds && this._itemIds.length > 0;
+    let label = isStarred ? "editlabel" : "bookmarklabel";
+    this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label));
+  },
+
+  onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
+    this._updateBookmarkPageMenuItem();
+    PlacesCommandHook.updateBookmarkAllTabsCommand();
+  },
+
   onCommand: function BUI_onCommand(aEvent) {
     if (aEvent.target != aEvent.currentTarget) {
       return;
     }
 
     // Handle special case when the button is in the panel.
-    let widgetGroup = CustomizableUI.getWidget("bookmarks-menu-button");
-    let widget = widgetGroup.forWindow(window);
-    if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
+    let widget = CustomizableUI.getWidget("bookmarks-menu-button")
+                               .forWindow(window);
+    if (this._currentAreaType == CustomizableUI.TYPE_MENU_PANEL) {
       let view = document.getElementById("PanelUI-bookmarks");
-      view.addEventListener("ViewShowing", this.onPanelMenuViewShowing);
-      view.addEventListener("ViewHiding", this.onPanelMenuViewHiding);
+      view.addEventListener("ViewShowing", this);
+      view.addEventListener("ViewHiding", this);
       widget.node.setAttribute("closemenu", "none");
       PanelUI.showSubView("PanelUI-bookmarks", widget.node,
                           CustomizableUI.AREA_PANEL);
       return;
     }
-    else if (widget.overflowed) {
+    if (widget.overflowed) {
       // Allow to close the panel if the page is already bookmarked, cause
       // we are going to open the edit bookmark panel.
       if (this._itemIds.length > 0)
         widget.node.removeAttribute("closemenu");
       else
         widget.node.setAttribute("closemenu", "none");
     }
 
     // Ignore clicks on the star if we are updating its state.
     if (!this._pendingStmt) {
       PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
     }
   },
 
+  handleEvent: function BUI_handleEvent(aEvent) {
+    switch (aEvent.type) {
+      case "ViewShowing":
+        this.onPanelMenuViewShowing(aEvent);
+        break;
+      case "ViewHiding":
+        this.onPanelMenuViewHiding(aEvent);
+        break;
+    }
+  },
+
   onPanelMenuViewShowing: function BUI_onViewShowing(aEvent) {
+    this._updateBookmarkPageMenuItem();
     // Update checked status of the toolbar toggle.
     let viewToolbar = document.getElementById("panelMenu_viewBookmarksToolbar");
     let personalToolbar = document.getElementById("PersonalToolbar");
     if (personalToolbar.collapsed)
       viewToolbar.removeAttribute("checked");
     else
       viewToolbar.setAttribute("checked", "true");
     // Setup the Places view.
     this._panelMenuView = new PlacesPanelMenuView("place:folder=BOOKMARKS_MENU",
                                                   "panelMenu_bookmarksMenu",
                                                   "panelMenu_bookmarksMenu", {
                                                     extraClasses: {
                                                       mainLevel: "subviewbutton"
                                                     }
                                                   });
+    aEvent.target.removeEventListener("ViewShowing", this);
   },
 
   onPanelMenuViewHiding: function BUI_onViewHiding(aEvent) {
     this._panelMenuView.uninit();
     delete this._panelMenuView;
+    aEvent.target.removeEventListener("ViewHiding", this);
   },
 
   onPanelMenuViewCommand: function BUI_onPanelMenuViewCommand(aEvent, aView) {
     let target = aEvent.originalTarget;
     if (!target._placesNode)
       return;
     if (PlacesUtils.nodeIsContainer(target._placesNode))
       PlacesCommandHook.showPlacesOrganizer([ "BookmarksMenu", target._placesNode.itemId ]);
     else
       PlacesUIUtils.openNodeWithEvent(target._placesNode, aEvent, aView);
     PanelUI.hide();
   },
 
   // nsINavBookmarkObserver
   onItemAdded: function BUI_onItemAdded(aItemId, aParentId, aIndex, aItemType,
                                         aURI) {
-    if (!this.button) {
-      return;
-    }
-
     if (aURI && aURI.equals(this._uri)) {
       // If a new bookmark has been added to the tracked uri, register it.
       if (this._itemIds.indexOf(aItemId) == -1) {
         this._itemIds.push(aItemId);
-        this._updateStar();
+        // Only need to update the UI if it wasn't marked as starred before:
+        if (this._itemIds.length == 1) {
+          this._updateStar();
+        }
       }
     }
   },
 
   onItemRemoved: function BUI_onItemRemoved(aItemId) {
-    if (!this.button) {
-      return;
-    }
-
     let index = this._itemIds.indexOf(aItemId);
     // If one of the tracked bookmarks has been removed, unregister it.
     if (index != -1) {
       this._itemIds.splice(index, 1);
-      this._updateStar();
+      // Only need to update the UI if the page is no longer starred
+      if (this._itemIds.length == 0) {
+        this._updateStar();
+      }
     }
   },
 
   onItemChanged: function BUI_onItemChanged(aItemId, aProperty,
                                             aIsAnnotationProperty, aNewValue) {
-    if (!this.button) {
-      return;
-    }
-
     if (aProperty == "uri") {
       let index = this._itemIds.indexOf(aItemId);
       // If the changed bookmark was tracked, check if it is now pointing to
       // a different uri and unregister it.
       if (index != -1 && aNewValue != this._uri.spec) {
         this._itemIds.splice(index, 1);
-        this._updateStar();
+        // Only need to update the UI if the page is no longer starred
+        if (this._itemIds.length == 0) {
+          this._updateStar();
+        }
       }
       // If another bookmark is now pointing to the tracked uri, register it.
       else if (index == -1 && aNewValue == this._uri.spec) {
         this._itemIds.push(aItemId);
-        this._updateStar();
+        // Only need to update the UI if it wasn't marked as starred before:
+        if (this._itemIds.length == 1) {
+          this._updateStar();
+        }
       }
     }
   },
 
   onBeginUpdateBatch: function () {},
   onEndUpdateBatch: function () {},
   onBeforeItemRemoved: function () {},
   onItemVisited: function () {},
   onItemMoved: function () {},
 
   // CustomizableUI events:
   _starButtonLabel: null,
-  _starButtonOverflowedLabel: null,
+  get _starButtonOverflowedLabel() {
+    delete this._starButtonOverflowedLabel;
+    this._starButtonOverflowedLabel =
+      gNavigatorBundle.getString("starButtonOverflowed.label");
+  },
+  get _starButtonOverflowedStarredLabel() {
+    delete this._starButtonOverflowedStarredLabel;
+    this._starButtonOverflowedStarredLabel =
+      gNavigatorBundle.getString("starButtonOverflowedStarred.label");
+  },
   onWidgetOverflow: function(aNode, aContainer) {
     let win = aNode.ownerDocument.defaultView;
     if (aNode.id != "bookmarks-menu-button" || win != window)
       return;
 
-    if (!this._starButtonOverflowedLabel) {
-      this._starButtonOverflowedLabel = gNavigatorBundle.getString(
-                                        "starButtonOverflowed.label");
-    }
-
     let currentLabel = aNode.getAttribute("label");
     if (!this._starButtonLabel)
       this._starButtonLabel = currentLabel;
 
-    if (currentLabel == this._starButtonLabel)
-      aNode.setAttribute("label", this._starButtonOverflowedLabel);
+    if (currentLabel == this._starButtonLabel) {
+      let desiredLabel = this._itemIds.length > 0 ? this._starButtonOverflowedStarredLabel
+                                                 : this._starButtonOverflowedLabel;
+      aNode.setAttribute("label", desiredLabel);
+    }
   },
 
   onWidgetUnderflow: function(aNode, aContainer) {
     let win = aNode.ownerDocument.defaultView;
     if (aNode.id != "bookmarks-menu-button" || win != window)
       return;
 
     // If the button hasn't been in the overflow panel before, we may ignore
     // this event.
-    if (!this._starButtonOverflowedLabel || !this._starButtonLabel)
+    if (!this._starButtonLabel)
       return;
 
-    if (aNode.getAttribute("label") == this._starButtonOverflowedLabel)
+    if (aNode.getAttribute("label") != this._starButtonLabel)
       aNode.setAttribute("label", this._starButtonLabel);
   },
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsINavBookmarkObserver
   ])
 };
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -135,16 +135,21 @@
                  type="checkbox" group="sidebar"
                  sidebarurl="chrome://browser/content/history/history-panel.xul"
                  oncommand="toggleSidebar('viewHistorySidebar');"/>
 
     <broadcaster id="viewWebPanelsSidebar" autoCheck="false"
                  type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/web-panels.xul"
                  oncommand="toggleSidebar('viewWebPanelsSidebar');"/>
 
+    <broadcaster id="bookmarkThisPageBroadcaster"
+                 label="&bookmarkThisPageCmd.label;"
+                 bookmarklabel="&bookmarkThisPageCmd.label;"
+                 editlabel="&editThisBookmarkCmd.label;"/>
+
     <!-- popup blocking menu items -->
     <broadcaster id="blockedPopupAllowSite"
                  accesskey="&allowPopups.accesskey;"
                  oncommand="gPopupBlockerObserver.toggleAllowPopupsForSite(event);"/>
     <broadcaster id="blockedPopupEditSettings"
 #ifdef XP_WIN
                  label="&editPopupSettings.label;"
 #else
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -384,58 +384,51 @@
       </hbox>
       <hbox pack="center">
         <button id="ctrlTab-showAll" class="ctrlTab-preview" noicon="true"/>
       </hbox>
     </panel>
 
     <!-- Sync Panel -->
     <panel id="sync-start-panel" class="sync-panel" type="arrow" hidden="true"
-           noautofocus="true" level="top" onclick="this.hidePopup();">
+           noautofocus="true" level="top" onclick="this.hidePopup();"
+           flip="slide">
       <hbox class="sync-panel-outer">
         <image class="sync-panel-icon"/>
         <vbox class="sync-panel-inner">
           <description id="sync-start-panel-title"
-                       value="&syncStartPanel.title;"/>
+                       value="&syncStartPanel.heading;"/>
           <description id="sync-start-panel-subtitle">
 #ifdef XP_UNIX
             &syncStartPanel.subTitleUnix;
 #else
             &syncStartPanel.subTitle;
 #endif
           </description>
         </vbox>
       </hbox>
     </panel>
 
     <!-- Sync Error Panel -->
     <panel id="sync-error-panel" class="sync-panel" type="arrow" hidden="true"
-           noautofocus="true" level="top" onclick="this.hidePopup();">
+           noautofocus="true" level="top" onclick="this.hidePopup();"
+           flip="slide">
       <hbox class="sync-panel-outer">
         <image class="sync-panel-icon"/>
         <vbox class="sync-panel-inner">
           <description id="sync-error-panel-title"
-                       value="&syncErrorPanel.title;"/>
+                       value="&syncErrorPanel.heading;"/>
           <description id="sync-error-panel-subtitle"
                        value="&syncErrorPanel.subTitle;"/>
           <hbox class="sync-panel-button-box">
-            <button class="sync-panel-button"
-#ifdef XP_UNIX
-                    label="&syncErrorPanel.prefButtonUnix.label;"
-                    accesskey="&syncErrorPanel.prefButtonUnix.accesskey;"
-#else
-                    label="&syncErrorPanel.prefButton.label;"
-                    accesskey="&syncErrorPanel.prefButton.accesskey;"
-#endif
-                    onclick="gFxAccounts.openPreferences();"/>
             <spacer flex="1"/>
             <button class="sync-panel-button"
                     label="&syncErrorPanel.signInButton.label;"
                     accesskey="&syncErrorPanel.signInButton.accesskey;"
-                    onclick="gFxAccounts.openSignInPage();"/>
+                    onclick="gFxAccounts.openSignInAgainPage();"/>
           </hbox>
         </vbox>
       </hbox>
     </panel>
 
     <!-- Bookmarks and history tooltip -->
     <tooltip id="bhTooltip"/>
 
--- a/browser/base/content/sync/customize.xul
+++ b/browser/base/content/sync/customize.xul
@@ -27,19 +27,19 @@
       <preference id="engine.passwords" name="services.sync.engine.passwords" type="bool"/>
       <preference id="engine.addons"    name="services.sync.engine.addons"    type="bool"/>
       <preference id="engine.prefs"     name="services.sync.engine.prefs"     type="bool"/>
     </preferences>
 
     <label id="sync-customize-title" value="&syncCustomize.title;"/>
     <description id="sync-customize-subtitle"
 #ifdef XP_UNIX
-                 value="&syncCustomize.subTitleUnix;"
+                 value="&syncCustomizeUnix.description;"
 #else
-                 value="&syncCustomize.subTitle;"
+                 value="&syncCustomize.description;"
 #endif
                  />
 
     <checkbox label="&engine.bookmarks.label;"
               accesskey="&engine.bookmarks.accesskey;"
               preference="engine.bookmarks"/>
     <checkbox label="&engine.history.label;"
               accesskey="&engine.history.accesskey;"
--- a/browser/base/content/test/general/browser_aboutAccounts.js
+++ b/browser/base/content/test/general/browser_aboutAccounts.js
@@ -6,27 +6,25 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
   "resource://gre/modules/FxAccounts.jsm");
 
 registerCleanupFunction(function() {
   // Ensure we don't pollute prefs for next tests.
-  Services.prefs.clearUserPref("identity.fxaccounts.enabled");
   Services.prefs.clearUserPref("identity.fxaccounts.remote.uri");
 });
 
 let gTests = [
 
 {
   desc: "Test the remote commands",
   setup: function ()
   {
-    Services.prefs.setBoolPref("identity.fxaccounts.enabled", true);
     Services.prefs.setCharPref("identity.fxaccounts.remote.uri",
                                "https://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
   },
   run: function ()
   {
     let deferred = Promise.defer();
 
     let results = 0;
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -45,25 +45,28 @@ browser.jar:
         content/browser/abouthome/restore@2x.png       (content/abouthome/restore@2x.png)
         content/browser/abouthome/restore-large@2x.png (content/abouthome/restore-large@2x.png)
         content/browser/abouthome/mozilla@2x.png       (content/abouthome/mozilla@2x.png)
 #ifdef MOZ_SERVICES_HEALTHREPORT
         content/browser/abouthealthreport/abouthealth.xhtml   (content/abouthealthreport/abouthealth.xhtml)
         content/browser/abouthealthreport/abouthealth.js      (content/abouthealthreport/abouthealth.js)
         content/browser/abouthealthreport/abouthealth.css     (content/abouthealthreport/abouthealth.css)
 #endif
-        content/browser/aboutaccounts/aboutaccounts.xhtml   (content/aboutaccounts/aboutaccounts.xhtml)
-        content/browser/aboutaccounts/aboutaccounts.js      (content/aboutaccounts/aboutaccounts.js)
-        content/browser/aboutaccounts/aboutaccounts.css     (content/aboutaccounts/aboutaccounts.css)
-        content/browser/aboutaccounts/main.css              (content/aboutaccounts/main.css)
-        content/browser/aboutaccounts/normalize.css         (content/aboutaccounts/normalize.css)
-        content/browser/aboutaccounts/fonts.css             (content/aboutaccounts/fonts.css)
-        content/browser/aboutaccounts/images/fox.png             (content/aboutaccounts/images/fox.png)
-        content/browser/aboutaccounts/images/graphic_sync_intro.png             (content/aboutaccounts/images/graphic_sync_intro.png)
-        content/browser/aboutaccounts/images/graphic_sync_intro@2x.png             (content/aboutaccounts/images/graphic_sync_intro@2x.png)
+        content/browser/aboutaccounts/aboutaccounts.xhtml                     (content/aboutaccounts/aboutaccounts.xhtml)
+        content/browser/aboutaccounts/aboutaccounts.js                        (content/aboutaccounts/aboutaccounts.js)
+        content/browser/aboutaccounts/aboutaccounts.css                       (content/aboutaccounts/aboutaccounts.css)
+        content/browser/aboutaccounts/main.css                                (content/aboutaccounts/main.css)
+        content/browser/aboutaccounts/normalize.css                           (content/aboutaccounts/normalize.css)
+        content/browser/aboutaccounts/fonts.css                               (content/aboutaccounts/fonts.css)
+        content/browser/aboutaccounts/fonts/clearsans-regular.woff            (content/aboutaccounts/fonts/clearsans-regular.woff)
+        content/browser/aboutaccounts/fonts/firasans-light.woff               (content/aboutaccounts/fonts/firasans-light.woff)
+        content/browser/aboutaccounts/fonts/firasans-regular.woff             (content/aboutaccounts/fonts/firasans-regular.woff)
+        content/browser/aboutaccounts/images/fox.png                          (content/aboutaccounts/images/fox.png)
+        content/browser/aboutaccounts/images/graphic_sync_intro.png           (content/aboutaccounts/images/graphic_sync_intro.png)
+        content/browser/aboutaccounts/images/graphic_sync_intro@2x.png        (content/aboutaccounts/images/graphic_sync_intro@2x.png)
 
         content/browser/aboutRobots-icon.png          (content/aboutRobots-icon.png)
         content/browser/aboutRobots-widget-left.png   (content/aboutRobots-widget-left.png)
         content/browser/aboutSocialError.xhtml        (content/aboutSocialError.xhtml)
         content/browser/aboutTabCrashed.js            (content/aboutTabCrashed.js)
         content/browser/aboutTabCrashed.xhtml         (content/aboutTabCrashed.xhtml)
 *       content/browser/browser.css                   (content/browser.css)
 *       content/browser/browser.js                    (content/browser.js)
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -13,17 +13,17 @@
       <vbox id="PanelUI-contents-scroller">
         <vbox id="PanelUI-contents" class="panelUI-grid"/>
       </vbox>
 
       <footer id="PanelUI-footer">
         <toolbarbutton id="PanelUI-fxa-status"
                        defaultlabel="&fxaSignIn.label;"
                        errorlabel="&fxaSignInError.label;"
-                       oncommand="gFxAccounts.toggle(event);"
+                       oncommand="gFxAccounts.onMenuPanelCommand(event);"
                        hidden="true"/>
 
         <hbox id="PanelUI-footer-inner">
           <toolbarbutton id="PanelUI-customize" label="&appMenuCustomize.label;"
                          exitLabel="&appMenuCustomizeExit.label;"
                          tooltiptext="&appMenuCustomize.tooltip;"
                          exitTooltiptext="&appMenuCustomizeExit.tooltip;"
                          closemenu="none"
@@ -83,18 +83,18 @@
                      class="panel-subview-footer subviewbutton"
                      label="&appMenuHistory.showAll.label;"
                      oncommand="PlacesCommandHook.showPlacesOrganizer('History'); CustomizableUI.hidePanelForNode(this);"/>
     </panelview>
 
     <panelview id="PanelUI-bookmarks" flex="1" class="PanelUI-subView">
       <label value="&bookmarksMenu.label;" class="panel-subview-header"/>
       <toolbarbutton id="panelMenuBookmarkThisPage"
-                     label="&bookmarkThisPageCmd.label;"
                      class="subviewbutton"
+                     observes="bookmarkThisPageBroadcaster"
                      command="Browser:AddBookmarkAs"
                      onclick="PanelUI.hide();"/>
       <toolbarseparator/>
       <toolbarbutton id="panelMenu_viewBookmarksSidebar"
                      label="&viewBookmarksSidebar2.label;"
                      class="subviewbutton"
                      oncommand="toggleSidebar('viewBookmarksSidebar'); PanelUI.hide();">
         <observes element="viewBookmarksSidebar" attribute="checked"/>
--- a/browser/components/preferences/sync.js
+++ b/browser/components/preferences/sync.js
@@ -251,18 +251,22 @@ let gSyncPane = {
       gSyncUtils._openLink(url);
       return;
     }
     win.switchToTabHavingURI(url, true);
     // seeing as we are doing this in a tab we close the prefs dialog.
     window.close();
   },
 
+  signIn: function() {
+    this.openContentInBrowser("about:accounts?action=signin");
+  },
+
   reSignIn: function() {
-    this.openContentInBrowser("about:accounts");
+    this.openContentInBrowser("about:accounts?action=reauth");
   },
 
   manageFirefoxAccount: function() {
     let url = Services.prefs.getCharPref("identity.fxaccounts.settings.uri");
     this.openContentInBrowser(url);
   },
 
   verifyFirefoxAccount: function() {
@@ -285,27 +289,29 @@ let gSyncPane = {
     this.openContentInBrowser(url);
   },
 
   unlinkFirefoxAccount: function(confirm) {
     if (confirm) {
       // We use a string bundle shared with aboutAccounts.
       let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
       let continueLabel = sb.GetStringFromName("continue.label");
-      let title = sb.GetStringFromName("unlink.verify.title");
-      let body = sb.GetStringFromName("unlink.verify.heading") +
+      let title = sb.GetStringFromName("disconnect.verify.title");
+      let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
+      let brandShortName = brandBundle.GetStringFromName("brandShortName");
+      let body = sb.GetStringFromName("disconnect.verify.heading") +
                  "\n\n" +
-                 sb.GetStringFromName("unlink.verify.description");
+                 sb.formatStringFromName("disconnect.verify.description",
+                                         [brandShortName], 1);
       let ps = Services.prompt;
       let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
                         (ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
                         ps.BUTTON_POS_1_DEFAULT;
       let pressed = Services.prompt.confirmEx(window, title, body, buttonFlags,
-                                         continueLabel, null, null, null,
-                                         {});
+                                              continueLabel, null, null, null, {});
       if (pressed != 0) { // 0 is the "continue" button
         return;
       }
     }
     Components.utils.import('resource://gre/modules/FxAccounts.jsm');
     fxAccounts.signOut().then(() => {
       this.updateWeavePrefs();
     });
--- a/browser/components/preferences/sync.xul
+++ b/browser/components/preferences/sync.xul
@@ -183,41 +183,44 @@
           <spacer flex="1"/>
           <p>&determiningStatus.label;</p>
           <spacer flex="1"/>
         </vbox>
 
         <vbox id="noFxaAccount">
           <description>&welcome.description;</description>
           <label class="text-link"
-                 onclick="gSyncPane.openContentInBrowser('about:accounts?signin=true'); return false;"
-                 value="&welcome.startButton.label;"/>
+                 onclick="gSyncPane.signIn(); return false;"
+                 value="&welcome.createAccount.label;"/>
+          <label class="text-link"
+                 onclick="gSyncPane.signIn(); return false;"
+                 value="&welcome.signIn.label;"/>
           <spacer flex="1"/>
           <label class="text-link"
                  onclick="gSyncPane.openOldSyncSupportPage(); return false;"
                  value="&welcome.useOldSync.label;"/>
           <spacer flex="10"/>
         </vbox>
 
         <vbox id="hasFxaAccount">
           <groupbox id="fxaGroup">
-            <caption label="&syncBrand.fxa-singular.label;"/>
+            <caption label="&syncBrand.fxAccount.label;"/>
 
             <deck id="fxaLoginStatus">
 
               <!-- logged in and verified and all is good -->
               <hbox flex="1">
                 <label id="fxaEmailAddress1"/>
                 <label class="text-link"
                        onclick="gSyncPane.manageFirefoxAccount();"
                        value="&manage.label;"/>
                 <spacer flex="1"/>
                 <vbox align="end">
                   <button onclick="gSyncPane.unlinkFirefoxAccount(true);"
-                          label="&unlink.label;" />
+                          label="&disconnect.label;" />
                 </vbox>
               </hbox>
 
               <!-- logged in to an unverified account -->
               <hbox flex="1">
                 <description>
                   &signedInUnverified.beforename.label;
                   <span id="fxaEmailAddress2"></span>
--- a/browser/devtools/sourceeditor/autocomplete.js
+++ b/browser/devtools/sourceeditor/autocomplete.js
@@ -40,16 +40,32 @@ function setupAutoCompletion(ctx, walker
     "Shift-Tab": cm => {
       if (popup && popup.isOpen) {
         cycleSuggestions(ed, true);
         return;
       }
 
       return win.CodeMirror.Pass;
     },
+    "Up": cm => {
+      if (popup && popup.isOpen) {
+        cycleSuggestions(ed, true);
+        return;
+      }
+
+      return win.CodeMirror.Pass;
+    },
+    "Down": cm => {
+      if (popup && popup.isOpen) {
+        cycleSuggestions(ed);
+        return;
+      }
+
+      return win.CodeMirror.Pass;
+    },
   };
   keyMap[Editor.accel("Space")] = cm => autoComplete(ctx);
   cm.addKeyMap(keyMap);
 
   cm.on("keydown", (cm, e) => onEditorKeypress(ed, e));
   ed.on("change", () => autoComplete(ctx));
   ed.on("destroy", () => {
     cm.off("keydown", (cm, e) => onEditorKeypress(ed, e));
@@ -142,27 +158,27 @@ function cycleSuggestions(ed, reverse) {
 
 /**
  * onkeydown handler for the editor instance to prevent autocompleting on some
  * keypresses.
  */
 function onEditorKeypress(ed, event) {
   let private = privates.get(ed);
   switch (event.keyCode) {
-    case event.DOM_VK_UP:
-    case event.DOM_VK_DOWN:
+    case event.DOM_VK_ESCAPE:
+      if (private.popup.isOpen)
+        event.preventDefault();
     case event.DOM_VK_LEFT:
     case event.DOM_VK_RIGHT:
     case event.DOM_VK_HOME:
     case event.DOM_VK_END:
     case event.DOM_VK_BACK_SPACE:
     case event.DOM_VK_DELETE:
     case event.DOM_VK_ENTER:
     case event.DOM_VK_RETURN:
-    case event.DOM_VK_ESCAPE:
       private.doNotAutocomplete = true;
       private.popup.hidePopup();
       break;
 
     default:
       private.doNotAutocomplete = false;
   }
 }
--- a/browser/devtools/styleeditor/test/browser_styleeditor_autocomplete.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_autocomplete.js
@@ -17,33 +17,39 @@ const AUTOCOMPLETION_PREF = "devtools.st
 //   1 to check whether the selected suggestion is inserted into the editor or not
 // ]
 let TEST_CASES = [
   ['VK_RIGHT', -1],
   ['VK_RIGHT', -1],
   ['VK_RIGHT', -1],
   ['VK_RIGHT', -1],
   [-1, 1, 0],
+  ['VK_LEFT', -1],
+  ['VK_RIGHT', -1],
   ['VK_DOWN', -1],
   ['VK_RIGHT', -1],
   ['VK_RIGHT', -1],
   ['VK_RIGHT', -1],
   [-1, MAX_SUGGESTIONS, 0],
   ['VK_END', -1],
   ['VK_RETURN', -1],
   ['b', MAX_SUGGESTIONS, 0],
   ['a', 11, 0],
-  ['VK_TAB', 11, 0, 1],
+  ['VK_DOWN', 11, 0, 1],
   ['VK_TAB', 11, 1, 1],
   [':', -1],
   ['b', 9, 0],
   ['l', 4, 0],
   ['VK_TAB', 4, 0, 1],
+  ['VK_DOWN', 4, 1, 1],
+  ['VK_UP', 4, 0, 1],
   ['VK_TAB', 4, 1, 1],
   ['VK_TAB', 4, 2, 1],
+  ['VK_LEFT', -1],
+  ['VK_RIGHT', -1],
   ['VK_DOWN', -1],
   ['VK_RETURN', -1],
   ['b', 2, 0],
   ['u', 1, 0],
   ['VK_TAB', -1],
   ['{', -1],
   ['VK_HOME', -1],
   ['VK_DOWN', -1],
@@ -97,22 +103,23 @@ function testState() {
 
   if (key == -1) {
     info("pressing Ctrl + Space to get result: [" + TEST_CASES[index] +
          "] for index " + index);
     gEditor.once("after-suggest", checkState);
     key = " ";
     mods.accelKey = true;
   }
-  else if (/(down|left|right|return|home|end)/ig.test(key)) {
+  else if (/(left|right|return|home|end)/ig.test(key) ||
+           (key == "VK_DOWN" && !gPopup.isOpen)) {
     info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
          "] for index " + index);
     gEditor.once("cursorActivity", checkState);
   }
-  else if (key == "VK_TAB") {
+  else if (key == "VK_TAB" || key == "VK_UP" || key == "VK_DOWN") {
     info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
          "] for index " + index);
     gEditor.once("suggestion-entered", checkState);
   }
   else {
     info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
          "] for index " + index);
     gEditor.once("after-suggest", checkState);
@@ -134,18 +141,17 @@ function checkState() {
         let { preLabel, label } = gPopup.getItemAtIndex(current);
         let { line, ch } = gEditor.getCursor();
         let lineText = gEditor.getText(line);
         is(lineText.substring(ch - label.length, ch), label,
            "Current suggestion from the popup is inserted into the editor.");
       }
     }
     else {
-      ok(gPopup._panel.state != "open" && gPopup._panel.state != "showing",
-         "Popup is closed for index " + index);
+      ok(!gPopup.isOpen, "Popup is closed for index " + index);
     }
     index++;
     testState();
   });
 }
 
 function testAutocompletionDisabled() {
   gBrowser.removeCurrentTab();
--- a/browser/locales/en-US/chrome/browser/aboutAccounts.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutAccounts.dtd
@@ -1,9 +1,10 @@
 <!-- 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/. -->
 
-<!ENTITY aboutAccounts.pageTitle "Welcome to Sync">
+<!ENTITY aboutAccounts.welcome "Welcome to &syncBrand.shortName.label;">
 
 <!ENTITY aboutAccountsConfig.description "Sign in to sync your tabs, bookmarks, passwords &amp; more.">
 <!ENTITY aboutAccountsConfig.startButton.label "Get started">
 <!ENTITY aboutAccountsConfig.useOldSync.label "Using an older version of Sync?">
+<!ENTITY aboutAccountsConfig.manage.label "Manage">
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -93,25 +93,21 @@ These should match what Safari and other
 <!ENTITY fullScreenCmd.label "Full Screen">
 <!ENTITY fullScreenCmd.accesskey "F">
 <!ENTITY fullScreenCmd.macCommandKey "f">
 <!ENTITY showAllTabsCmd.label "Show All Tabs">
 <!ENTITY showAllTabsCmd.accesskey "A">
 
 <!ENTITY fxaSignIn.label "Sign in to &syncBrand.shortName.label;">
 <!ENTITY fxaSignInError.label "Reconnect to &syncBrand.shortName.label;">
-<!ENTITY syncStartPanel.title "&brandShortName; is now syncing.">
+<!ENTITY syncStartPanel.heading "&brandShortName; is now syncing">
 <!ENTITY syncStartPanel.subTitle "You can manage &syncBrand.shortName.label; in Options.">
 <!ENTITY syncStartPanel.subTitleUnix "You can manage &syncBrand.shortName.label; in Preferences.">
-<!ENTITY syncErrorPanel.title "Cannot connect to &syncBrand.shortName.label;">
+<!ENTITY syncErrorPanel.heading "Cannot connect to &syncBrand.shortName.label;">
 <!ENTITY syncErrorPanel.subTitle "Please sign in to resume syncing.">
-<!ENTITY syncErrorPanel.prefButton.label "Options">
-<!ENTITY syncErrorPanel.prefButton.accesskey "O">
-<!ENTITY syncErrorPanel.prefButtonUnix.label "Preferences">
-<!ENTITY syncErrorPanel.prefButtonUnix.accesskey "P">
 <!ENTITY syncErrorPanel.signInButton.label "Sign In">
 <!ENTITY syncErrorPanel.signInButton.accesskey "S">
 
 
 <!ENTITY fullScreenMinimize.tooltip "Minimize">
 <!ENTITY fullScreenRestore.tooltip "Restore">
 <!ENTITY fullScreenClose.tooltip "Close">
 <!ENTITY fullScreenAutohide.label "Hide Toolbars">
@@ -126,16 +122,17 @@ These should match what Safari and other
 <!ENTITY leaveDOMFullScreen.accesskey "u">
 
 <!ENTITY closeWindow.label "Close Window">
 <!ENTITY closeWindow.accesskey "d">
 
 <!ENTITY bookmarksMenu.label "Bookmarks">
 <!ENTITY bookmarksMenu.accesskey "B">
 <!ENTITY bookmarkThisPageCmd.label "Bookmark This Page">
+<!ENTITY editThisBookmarkCmd.label "Edit This Bookmark">
 <!ENTITY bookmarkThisPageCmd.commandkey "d">
 <!ENTITY markPageCmd.commandkey "l">
 <!ENTITY sharePageCmd.label "Share This Page">
 <!ENTITY sharePageCmd.commandkey "S">
 <!ENTITY sharePageCmd.accesskey "s">
 <!ENTITY shareLinkCmd.label "Share This Link">
 <!ENTITY shareLinkCmd.accesskey "s">
 <!ENTITY shareImageCmd.label "Share This Image">
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -235,16 +235,17 @@ refreshBlocked.redirectLabel=%S prevente
 # General bookmarks button
 # LOCALIZATION NOTE (bookmarksMenuButton.tooltip):
 # %S is the keyboard shortcut for "Show All Bookmarks"
 bookmarksMenuButton.tooltip=Show your bookmarks (%S)
 # Star button
 starButtonOn.tooltip=Edit this bookmark
 starButtonOff.tooltip=Bookmark this page
 starButtonOverflowed.label=Bookmark This Page
+starButtonOverflowedStarred.label=Edit This Bookmark
 
 # Print button tooltip on OS X
 # LOCALIZATION NOTE (printButton.tooltip):
 # Use the unicode ellipsis char, \u2026,
 # or use "..." if \u2026 doesn't suit traditions in your locale.
 # %S is the keyboard shortcut for "Print"
 printButton.tooltip=Print this page… (%S)
 
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -135,9 +135,9 @@ syncUnlinkConfirm.label=Unlink
 featureEnableRequiresRestart=%S must restart to enable this feature.
 featureDisableRequiresRestart=%S must restart to disable this feature.
 shouldRestartTitle=Restart %S
 
 ###Preferences::Sync::Firefox Accounts
 firefoxAccountsVerificationSentTitle=Verification Sent
 # LOCALIZATION NOTE: %S = user's email address.
 firefoxAccountsVerificationSentHeading=A verification link has been sent to %S
-firefoxAccountVerificationSentDescription=Please check your email and click the verification link to begin syncing.
+firefoxAccountVerificationSentDescription=Please check your email and click the link to begin syncing.
--- a/browser/locales/en-US/chrome/browser/preferences/sync.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/sync.dtd
@@ -43,25 +43,27 @@
 <!ENTITY unlinkDevice.label           "Unlink This Device">
 
 <!-- Footer stuff -->
 <!ENTITY prefs.tosLink.label        "Terms of Service">
 <!ENTITY prefs.ppLink.label         "Privacy Policy">
 
 <!-- Firefox Accounts stuff -->
 <!ENTITY fxaPrivacyNotice.link.label "Privacy Notice">
-<!ENTITY determiningStatus.label    "Determining your Firefox Account status…">
+<!ENTITY determiningStatus.label     "Determining your Firefox Account status…">
 <!ENTITY signedInUnverified.beforename.label "">
 <!ENTITY signedInUnverified.aftername.label "is not verified.">
 
 <!ENTITY signedInLoginFailure.beforename.label "Please sign in to reconnect">
 <!ENTITY signedInLoginFailure.aftername.label "">
 
 <!ENTITY notSignedIn.label           "You are not signed in.">
 <!ENTITY signIn.label                "Sign in">
 <!ENTITY manage.label                "Manage">
-<!ENTITY unlink.label                "Unlink this Browser…">
+<!ENTITY disconnect.label            "Disconnect…">
 <!ENTITY verify.label                "Verify Email">
 <!ENTITY forget.label                "Forget this Email">
 
 <!ENTITY welcome.description "Access your tabs, bookmarks, passwords and more wherever you use &brandShortName;.">
-<!ENTITY welcome.startButton.label "Sign in or Create an Account">
+<!ENTITY welcome.signIn.label "Sign In">
+<!ENTITY welcome.createAccount.label "Create Account">
+
 <!ENTITY welcome.useOldSync.label "Using an older version of Sync?">
--- a/browser/locales/en-US/chrome/browser/syncBrand.dtd
+++ b/browser/locales/en-US/chrome/browser/syncBrand.dtd
@@ -1,8 +1,7 @@
 <!-- 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/. -->
 
 <!ENTITY syncBrand.shortName.label  "Sync">
 <!ENTITY syncBrand.fullName.label   "Firefox Sync">
-<!ENTITY syncBrand.fxa-singular.label "Firefox Account">
-<!ENTITY syncBrand.fxa-plural.label "Firefox Accounts">
+<!ENTITY syncBrand.fxAccount.label  "Firefox Account">
--- a/browser/locales/en-US/chrome/browser/syncCustomize.dtd
+++ b/browser/locales/en-US/chrome/browser/syncCustomize.dtd
@@ -1,18 +1,18 @@
 <!-- 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/. -->
 
 <!ENTITY syncCustomize.dialog.title       "Sync Selection">
 <!ENTITY syncCustomize.acceptButton.label "Start">
 
 <!ENTITY syncCustomize.title              "What would you like to sync?">
-<!ENTITY syncCustomize.subTitle           "You can manage this selection in Options.">
-<!ENTITY syncCustomize.subTitleUnix       "You can manage this selection in Preferences.">
+<!ENTITY syncCustomize.description        "You can change this selection in Options.">
+<!ENTITY syncCustomizeUnix.description    "You can change this selection in Preferences.">
 
 <!--
   These engine names are the same as in browser/preferences/sync.dtd except
   for the last two that are marked as being specific to Desktop browsers.
 -->
 <!ENTITY engine.bookmarks.label           "Bookmarks">
 <!ENTITY engine.bookmarks.accesskey       "m">
 <!ENTITY engine.history.label             "History">
--- a/browser/locales/en-US/chrome/browser/syncSetup.properties
+++ b/browser/locales/en-US/chrome/browser/syncSetup.properties
@@ -47,19 +47,20 @@ wipeClient.change2.label = Firefox Sync 
 wipeRemote.change2.label = Firefox Sync will now replace all of the browser data in your Sync account with the data on this device.
 existingAccount.change.label = You can change this preference by selecting Sync Options below.
 
 # Several other strings are used (via Weave.Status.login), but they come from
 #  /services/sync
 
 # Firefox Accounts based setup.
 continue.label = Continue
-unlink.verify.title = Unlink Browser
-unlink.verify.heading = Are you sure?
-unlink.verify.description = This browser will stop syncing with your other computers, but won't delete any of your local browsing data.
+disconnect.verify.title = Disconnect
+disconnect.verify.heading = Are you sure?
+# LOCALIZATION NOTE (disconnect.verify.description): %S will be replaced with
+# brandShortName
+disconnect.verify.description = %S will stop syncing with your account, but won’t delete any of your browsing data on this computer.
 
-relink.verify.title = Merge Warning
-relink.verify.heading = Are you sure you want to sign in to Sync?
-# LOCALIZATION NOTE (relink.verify.description): Email address of a user previously signed into sync.
-relink.verify.description = A different user was previously signed in to Sync on this device. Signing in will merge this browser's bookmarks, passwords and other settings with %S
-
+relinkVerify.title = Merge Warning
+relinkVerify.heading = Are you sure you want to sign in to Sync?
+# LOCALIZATION NOTE (relinkVerify.description): Email address of a user previously signed into sync.
+relinkVerify.description = A different user was previously signed in to Sync on this computer. Signing in will merge this browser’s bookmarks, passwords and other settings with %S
 
 manage.pageTitle = Manage Sync
--- a/browser/metro/base/content/startui/FirstRunOverlay.xul
+++ b/browser/metro/base/content/startui/FirstRunOverlay.xul
@@ -15,55 +15,56 @@
 
 <overlay id="firstrun"
          xmlns:html="http://www.w3.org/1999/xhtml"
          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <hbox id="start-container">
     <box id="instruction-topsites-box" insertafter="start-topsites" class="firstrun">
       <box id="instruction-topsites">
-        <vbox align="center">
+        <vbox class="instruction-content-container">
+          <image class="instruction-arrow arrow-left-straight" />
           <image class="instruction-arrow arrow-left" />
-          <label class="instruction-label" value="&firstRunTopSites.label;"/>
+          <label class="instruction-label">&firstRunTopSites.label;</label>
         </vbox>
       </box>
     </box>
 
     <vbox id="firstrun-welcome" insertafter="instruction-topsites-box" class="firstrun" align="center" pack="center">
       <image class="welcome-image" />
       <label class="welcome-title" value="&firstRunWelcome.label;"/>
       <label class="welcome-subtitle" value="&firstRunDifferent.label;"/>
     </vbox>
 
     <box insertafter="start-bookmarks" class="firstrun">
       <box id="instruction-bookmarks">
-        <vbox align="center">
+        <vbox class="instruction-content-container" align="center">
           <image class="instruction-arrow arrow-right" />
-          <label class="instruction-label" value="&firstRunBookmarks.label;"/>
+          <label class="instruction-label">&firstRunBookmarks.label;</label>
         </vbox>
       </box>
     </box>
 
     <box id="instruction-history-container" insertafter="start-history" class="firstrun">
       <box id="instruction-history">
-        <vbox align="center">
+        <vbox class="instruction-content-container" align="center">
           <image class="instruction-arrow arrow-right" />
-          <label class="instruction-label" value="&firstRunHistory.label;"/>
+          <label class="instruction-label">&firstRunHistory.label;</label>
         </vbox>
       </box>
     </box>
 
     <box id="instruction-tabs" class="firstrun">
-      <vbox align="center">
+      <vbox class="instruction-content-container" align="center">
         <image class="instruction-arrow arrow-top" />
-        <label class="instruction-label" value="&firstRunTabs.label;"/>
+        <label class="instruction-label">&firstRunTabs.label;</label>
       </vbox>
     </box>
 
     <box id="instruction-menu" class="firstrun">
-      <hbox>
-        <label class="instruction-label" value="&firstRunMenu.label;"/>
+      <hbox class="instruction-content-container" align="right">
+        <label class="instruction-label">&firstRunMenu.label;</label>
         <image class="instruction-arrow arrow-down" />
       </hbox>
     </box>
 
  </hbox>
 </overlay>
\ No newline at end of file
--- a/browser/metro/theme/firstrun.css
+++ b/browser/metro/theme/firstrun.css
@@ -68,16 +68,20 @@ below instruction */
   font-size: 16px;
   color: #808080;
   line-height: 22px;
   padding-top: 10px;
 }
 
 /* Instructions ---------------------- */
 
+.instruction-content-container {
+    width: 380px;
+}
+
 .instruction-label {
   font-size: 16px;
   color: #808080;
   line-height: 22px;
 }
 
 .instruction-arrow {
   width: 76px;
@@ -89,69 +93,106 @@ below instruction */
   background-image: url("chrome://browser/skin/images/arrow-top.png");
 }
 
 .instruction-arrow.arrow-down {
   transform: rotate(180deg);
 }
 
 .instruction-arrow.arrow-left,
-.instruction-arrow.arrow-right {
+.instruction-arrow.arrow-right,
+.instruction-arrow.arrow-left-straight,
+.instruction-arrow.arrow-right-straight {
   background-image: url("chrome://browser/skin/images/arrow-left.png");
 }
 
 .instruction-arrow.arrow-right {
   transform: rotate(180deg) scaleY(-1);
 }
 
+.instruction-arrow.arrow-left-straight {
+  transform: rotate(220deg) scaleX(-1);
+}
+
+.instruction-arrow.arrow-right-straight {
+  transform: rotate(-220deg);
+}
+
 #instruction-tabs {
   position: absolute;
   top: 10px;
   transform: translateX(calc(630px - 50%));
 }
 
 #start-container[viewstate="portrait"] #instruction-tabs {
   transform: translateX(calc(50vw - 50%));
 }
 
 #start-container[viewstate="landscape"] #instruction-topsites {
   position: absolute;
-  bottom: 20px;
-  transform: translateX(-50%);
-  margin-left: 20px;
+  bottom: 15%;
+}
+
+#start-container[viewstate="landscape"] #instruction-topsites .instruction-content-container {
+  -moz-box-align: start;
+}
+
+#start-container[viewstate="portrait"] #instruction-topsites-box {
+  margin-bottom: 20px;
+}
+
+#start-container[viewstate="portrait"] #instruction-topsites .instruction-content-container {
+  -moz-box-align: center;
 }
 
-#start-container[viewstate="portrait"] #instruction-topsites {
-  transform: translateX(calc(150px + 50%)) translateY(-60px);
+#start-container[viewstate="portrait"] #instruction-topsites .arrow-left-straight {
+  display: none;
+}
+
+#start-container[viewstate="portrait"] #instruction-topsites .arrow-left {
+  display: block;
+}
+
+#start-container[viewstate="landscape"] #instruction-topsites .arrow-left-straight {
+  display: block;
+}
+
+#start-container[viewstate="landscape"] #instruction-topsites .arrow-left {
+  display: none;
 }
 
 #start-container[viewstate="landscape"] #instruction-history,
 #start-container[viewstate="landscape"] #instruction-bookmarks {
   position: absolute;
   top: 310px;
   transform: translateX(calc(-300px - 50%));
 }
 
 #start-container[viewstate="portrait"] #instruction-history,
-#start-container[viewstate="portrait"] #instruction-bookmarks {
+#start-container[viewstate="portrait"] #instruction-bookmarks,
+#start-container[viewstate="portrait"] #instruction-topsites {
   transform: translateX(-55px);
 }
 
 #start-container[viewstate="landscape"] #instruction-menu {
   position: fixed;
   bottom: 16px;
   right: 37px;
 }
 
 #start-container[viewstate="portrait"] #instruction-menu {
   position: absolute;
   right: 37px;
   transform: translateY(40px);
 }
 
+#start-container #instruction-menu .instruction-label {
+    max-width: 300px;
+}
+
 #start-container[viewstate="landscape"] #start-history {
   padding-left: 50px;
 }
 
 /* Higher resolution images ---------------------- */
 
 @media (min-resolution: @min_res_140pc@) {
   /* Load 140% image when scaled by 140% */
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -1677,35 +1677,32 @@ toolbarbutton.chevron > .toolbarbutton-i
   -moz-appearance: button;
   color: ButtonText;
   padding: 0 3px;
   margin-top: 10px;
 }
 
 /* Sync Panel */
 
-.sync-panel {
-  padding: 10px;
-}
-
 .sync-panel-icon {
   width: 32px;
   background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
 }
 
 .sync-panel-inner {
   width: 0;
   padding-left: 10px;
 }
 
 .sync-panel-button-box {
   margin-top: 1em;
 }
 
-#sync-error-panel-title {
+#sync-error-panel-title,
+#sync-start-panel-title {
   font-weight: bold;
 }
 
 #sync-start-panel-subtitle,
 #sync-error-panel-subtitle {
   margin: 0;
 }
 
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -957,20 +957,16 @@ toolbar .toolbarbutton-1:not([type="menu
     -moz-image-region: rect(0px, 256px, 64px, 192px);
   }
 
   #bookmarks-menu-button[cui-areatype="menu-panel"],
   toolbarpaletteitem[place="palette"] > #bookmarks-menu-button {
     -moz-image-region: rect(0px, 320px, 64px, 256px);
   }
 
-  #bookmarks-menu-button[starred][cui-areatype="menu-panel"] {
-    -moz-image-region: rect(0px, 384px, 64px, 320px);
-  }
-
   #history-panelmenu[cui-areatype="menu-panel"],
   toolbarpaletteitem[place="palette"] > #history-panelmenu {
     -moz-image-region: rect(0px, 448px, 64px, 384px);
   }
 
   #downloads-button[cui-areatype="menu-panel"],
   toolbarpaletteitem[place="palette"] > #downloads-button {
     -moz-image-region: rect(0px, 512px, 64px, 448px);
@@ -3596,20 +3592,16 @@ toolbarbutton.chevron > .toolbarbutton-m
 }
 
 #ctrlTab-showAll {
   margin-top: .5em;
 }
 
 /* Sync Panels */
 
-.sync-panel {
-  padding: 10px;
-}
-
 .sync-panel-icon {
   width: 32px;
   background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
 }
 
 @media (min-resolution: 2dppx) {
   .sync-panel-icon {
     background: url("chrome://browser/content/abouthome/sync@2x.png") top left no-repeat;
@@ -3636,17 +3628,18 @@ toolbarbutton.chevron > .toolbarbutton-m
 .sync-panel-button:hover:active {
   @hudButtonPressed@
 }
 
 .sync-panel-button:-moz-focusring {
   @hudButtonFocused@
 }
 
-#sync-error-panel-title {
+#sync-error-panel-title,
+#sync-start-panel-title {
   font-weight: bold;
 }
 
 #sync-start-panel-subtitle,
 #sync-error-panel-subtitle {
   margin: 0;
 }
 
--- a/browser/themes/shared/menupanel.inc.css
+++ b/browser/themes/shared/menupanel.inc.css
@@ -10,20 +10,16 @@ toolbarpaletteitem[place="palette"] > #h
   -moz-image-region: rect(0px, 128px, 32px, 96px);
 }
 
 #bookmarks-menu-button[cui-areatype="menu-panel"],
 toolbarpaletteitem[place="palette"] > #bookmarks-menu-button {
   -moz-image-region: rect(0px, 160px, 32px, 128px);
 }
 
-#bookmarks-menu-button[starred][cui-areatype="menu-panel"] {
-  -moz-image-region: rect(0px, 192px, 32px, 160px);
-}
-
 #history-panelmenu[cui-areatype="menu-panel"],
 toolbarpaletteitem[place="palette"] > #history-panelmenu {
   -moz-image-region: rect(0px, 224px, 32px, 192px);
 }
 
 #downloads-button[cui-areatype="menu-panel"],
 toolbarpaletteitem[place="palette"] > #downloads-button {
   -moz-image-region: rect(0px, 256px, 32px, 224px);
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -2139,35 +2139,32 @@ toolbarbutton.bookmark-item[dragover="tr
 }
 
 #ctrlTab-showAll {
   margin-top: .5em;
 }
 
 /* Sync Panel */
 
-.sync-panel {
-  padding: 10px;
-}
-
 .sync-panel-icon {
   width: 32px;
   background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
 }
 
 .sync-panel-inner {
   width: 0;
   padding-left: 10px;
 }
 
 .sync-panel-button-box {
   margin-top: 1em;
 }
 
-#sync-error-panel-title {
+#sync-error-panel-title,
+#sync-start-panel-title {
   font-weight: bold;
 }
 
 #sync-start-panel-subtitle,
 #sync-error-panel-subtitle {
   margin: 0;
 }
 
--- a/content/base/src/CSPUtils.jsm
+++ b/content/base/src/CSPUtils.jsm
@@ -398,17 +398,17 @@ CSPRep.fromString = function(aStr, self,
           // parse failure.
           uri.host;
 
           // warn about, but do not prohibit non-http and non-https schemes for
           // reporting URIs.  The spec allows unrestricted URIs resolved
           // relative to "self", but we should let devs know if the scheme is
           // abnormal and may fail a POST.
           if (!uri.schemeIs("http") && !uri.schemeIs("https")) {
-            cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotHttpsOrHttp",
+            cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotHttpsOrHttp2",
                                                      [uri.asciiSpec]));
           }
         } catch(e) {
           switch (e.result) {
             case Components.results.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS:
             case Components.results.NS_ERROR_HOST_IS_IP_ADDRESS:
               if (uri.host !== selfUri.host) {
                 cspWarn(aCSPR,
@@ -646,17 +646,17 @@ CSPRep.fromStringSpecCompliant = functio
           // parse failure.
           uri.host;
 
           // warn about, but do not prohibit non-http and non-https schemes for
           // reporting URIs.  The spec allows unrestricted URIs resolved
           // relative to "self", but we should let devs know if the scheme is
           // abnormal and may fail a POST.
           if (!uri.schemeIs("http") && !uri.schemeIs("https")) {
-            cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotHttpsOrHttp",
+            cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotHttpsOrHttp2",
                                                      [uri.asciiSpec]));
           }
         } catch(e) {
           switch (e.result) {
             case Components.results.NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS:
             case Components.results.NS_ERROR_HOST_IS_IP_ADDRESS:
               if (uri.host !== selfUri.host) {
                 cspWarn(aCSPR, CSPLocalizer.getFormatStr("pageCannotSendReportsTo",
--- a/dom/locales/en-US/chrome/security/csp.properties
+++ b/dom/locales/en-US/chrome/security/csp.properties
@@ -20,19 +20,19 @@ errorWas = error was: "%1$S"
 # %1$S is the report URI that could not be parsed
 couldNotParseReportURI = couldn't parse report URI: %1$S
 # LOCALIZATION NOTE (couldNotProcessUnknownDirective):
 # %1$S is the unknown directive
 couldNotProcessUnknownDirective = Couldn't process unknown directive '%1$S'
 # LOCALIZATION NOTE (ignoringUnknownOption):
 # %1$S is the option that could not be understood
 ignoringUnknownOption = Ignoring unknown option %1$S
-# LOCALIZATION NOTE (reportURInotHttpsOrHttp):
+# LOCALIZATION NOTE (reportURInotHttpsOrHttp2):
 # %1$S is the ETLD of the report URI that is not HTTP or HTTPS
-reportURInotHttpsOrHttp = The report URI (%1$) should be an HTTP or HTTPS URI.
+reportURInotHttpsOrHttp2 = The report URI (%1$S) should be an HTTP or HTTPS URI.
 # LOCALIZATION NOTE (pageCannotSendReportsTo):
 # %1$S is the URI of the page with the policy
 # %2$S is the report URI that could not be used
 pageCannotSendReportsTo = page on %1$S cannot send reports to %2$S
 allowOrDefaultSrcRequired = 'allow' or 'default-src' directive required but not present.  Reverting to "default-src 'none'"
 # LOCALIZATION NOTE (failedToParseUnrecognizedSource):
 # %1$S is the CSP Source that could not be parsed
 failedToParseUnrecognizedSource = Failed to parse unrecognized source %1$S
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -2477,17 +2477,21 @@ abstract public class BrowserApp extends
                 openUrlAndStopEditing(url, true);
             }
         }
     }
 
     // HomePager.OnUrlOpenListener
     @Override
     public void onUrlOpen(String url, EnumSet<OnUrlOpenListener.Flags> flags) {
-        if (!maybeSwitchToTab(url, flags)) {
+        if (flags.contains(OnUrlOpenListener.Flags.OPEN_WITH_INTENT)) {
+            Intent intent = new Intent(Intent.ACTION_VIEW);
+            intent.setData(Uri.parse(url));
+            startActivity(intent);
+        } else if (!maybeSwitchToTab(url, flags)) {
             openUrlAndStopEditing(url);
         }
     }
 
     // BrowserSearch.OnSearchListener
     @Override
     public void onSearch(SearchEngine engine, String text) {
         recordSearch(engine, "barsuggest");
--- a/mobile/android/base/home/DynamicPanel.java
+++ b/mobile/android/base/home/DynamicPanel.java
@@ -101,17 +101,17 @@ public class DynamicPanel extends HomeFr
         }
     }
 
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
         switch(mPanelConfig.getLayoutType()) {
             case FRAME:
                 final PanelDatasetHandler datasetHandler = new PanelDatasetHandler();
-                mLayout = new FramePanelLayout(getActivity(), mPanelConfig, datasetHandler);
+                mLayout = new FramePanelLayout(getActivity(), mPanelConfig, datasetHandler, mUrlOpenListener);
                 break;
 
             default:
                 throw new IllegalStateException("Unrecognized layout type in DynamicPanel");
         }
 
         Log.d(LOGTAG, "Created layout of type: " + mPanelConfig.getLayoutType());
 
--- a/mobile/android/base/home/FramePanelLayout.java
+++ b/mobile/android/base/home/FramePanelLayout.java
@@ -1,30 +1,31 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.home;
 
+import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.HomeConfig.PanelConfig;
 import org.mozilla.gecko.home.HomeConfig.ViewConfig;
 
 import android.content.Context;
 import android.util.Log;
 import android.view.View;
 
 class FramePanelLayout extends PanelLayout {
     private static final String LOGTAG = "GeckoFramePanelLayout";
 
     private final View mChildView;
     private final ViewConfig mChildConfig;
 
-    public FramePanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler) {
-        super(context, panelConfig, datasetHandler);
+    public FramePanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler, OnUrlOpenListener urlOpenListener) {
+        super(context, panelConfig, datasetHandler, urlOpenListener);
 
         // This layout can only hold one view so we simply
         // take the first defined view from PanelConfig.
         mChildConfig = panelConfig.getViewAt(0);
         if (mChildConfig == null) {
             throw new IllegalStateException("FramePanelLayout requires a view in PanelConfig");
         }
 
--- a/mobile/android/base/home/HomeListView.java
+++ b/mobile/android/base/home/HomeListView.java
@@ -25,17 +25,17 @@ import android.widget.ListView;
  */
 public class HomeListView extends ListView
                           implements OnItemLongClickListener {
 
     // ContextMenuInfo associated with the currently long pressed list item.
     private HomeContextMenuInfo mContextMenuInfo;
 
     // On URL open listener
-    private OnUrlOpenListener mUrlOpenListener;
+    protected OnUrlOpenListener mUrlOpenListener;
 
     // Top divider
     private boolean mShowTopDivider;
 
     // ContextMenuInfo maker
     private ContextMenuInfoFactory mContextMenuInfoFactory;
 
     public HomeListView(Context context) {
--- a/mobile/android/base/home/HomePager.java
+++ b/mobile/android/base/home/HomePager.java
@@ -63,17 +63,18 @@ public class HomePager extends ViewPager
     static final String LIST_TAG_READING_LIST = "reading_list";
     static final String LIST_TAG_TOP_SITES = "top_sites";
     static final String LIST_TAG_MOST_RECENT = "most_recent";
     static final String LIST_TAG_LAST_TABS = "last_tabs";
     static final String LIST_TAG_BROWSER_SEARCH = "browser_search";
 
     public interface OnUrlOpenListener {
         public enum Flags {
-            ALLOW_SWITCH_TO_TAB
+            ALLOW_SWITCH_TO_TAB,
+            OPEN_WITH_INTENT
         }
 
         public void onUrlOpen(String url, EnumSet<Flags> flags);
     }
 
     public interface OnNewTabsListener {
         public void onNewTabs(String[] urls);
     }
--- a/mobile/android/base/home/PanelLayout.java
+++ b/mobile/android/base/home/PanelLayout.java
@@ -1,15 +1,16 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko.home;
 
+import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.HomeConfig.PanelConfig;
 import org.mozilla.gecko.home.HomeConfig.ViewConfig;
 
 import android.content.Context;
 import android.database.Cursor;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.View;
@@ -53,16 +54,17 @@ import java.util.List;
  * {@code PanelLayout} subclasses are free to have any type of views to arrange
  * the panel views in different ways.
  */
 abstract class PanelLayout extends FrameLayout {
     private static final String LOGTAG = "GeckoPanelLayout";
 
     private final List<ViewEntry> mViewEntries;
     private final DatasetHandler mDatasetHandler;
+    private final OnUrlOpenListener mUrlOpenListener;
 
     /**
      * To be used by panel views to express that they are
      * backed by datasets.
      */
     public interface DatasetBacked {
         public void setDataset(Cursor cursor);
     }
@@ -81,20 +83,25 @@ abstract class PanelLayout extends Frame
         /**
          * Releases any resources associated with a previously loaded
          * dataset. It will do nothing if the dataset with the given ID
          * hasn't been loaded before.
          */
         public void resetDataset(String datasetId);
     }
 
-    public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler) {
+    public interface PanelView {
+        public void setOnUrlOpenListener(OnUrlOpenListener listener);
+    }
+
+    public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler, OnUrlOpenListener urlOpenListener) {
         super(context);
         mViewEntries = new ArrayList<ViewEntry>();
         mDatasetHandler = datasetHandler;
+        mUrlOpenListener = urlOpenListener;
     }
 
     /**
      * Delivers the dataset as a {@code Cursor} to be bound to the
      * panel views backed by it. This is used by the {@code DatasetHandler}
      * in response to a dataset request.
      */
     public final void deliverDataset(String datasetId, Cursor cursor) {
@@ -150,16 +157,18 @@ abstract class PanelLayout extends Frame
 
             default:
                 throw new IllegalStateException("Unrecognized view type in " + getClass().getSimpleName());
         }
 
         final ViewEntry entry = new ViewEntry(view, viewConfig);
         mViewEntries.add(entry);
 
+        ((PanelView) view).setOnUrlOpenListener(mUrlOpenListener);
+
         return view;
     }
 
     /**
      * Dispose any dataset references associated with the
      * given view.
      */
     protected final void disposePanelView(View view) {
@@ -223,9 +232,9 @@ abstract class PanelLayout extends Frame
         public View getView() {
             return mView;
         }
 
         public String getDatasetId() {
             return mViewConfig.getDatasetId();
         }
     }
-}
\ No newline at end of file
+}
--- a/mobile/android/base/home/PanelListView.java
+++ b/mobile/android/base/home/PanelListView.java
@@ -2,39 +2,46 @@
  * 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/. */
 
 package org.mozilla.gecko.home;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.home.HomeConfig.ViewConfig;
+import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
 import org.mozilla.gecko.home.PanelLayout.DatasetBacked;
+import org.mozilla.gecko.home.PanelLayout.PanelView;
+import org.mozilla.gecko.db.BrowserContract.HomeItems;
 
 import android.content.Context;
 import android.database.Cursor;
 import android.support.v4.widget.CursorAdapter;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.widget.AdapterView;
+
+import java.util.EnumSet;
 
 public class PanelListView extends HomeListView
-                           implements DatasetBacked {
+                           implements DatasetBacked, PanelView {
 
     private static final String LOGTAG = "GeckoPanelListView";
 
     private final PanelListAdapter mAdapter;
     private final ViewConfig mViewConfig;
 
     public PanelListView(Context context, ViewConfig viewConfig) {
         super(context);
         mViewConfig = viewConfig;
         mAdapter = new PanelListAdapter(context);
         setAdapter(mAdapter);
+        setOnItemClickListener(new PanelListItemClickListener());
     }
 
     @Override
     public void setDataset(Cursor cursor) {
         Log.d(LOGTAG, "Setting dataset: " + mViewConfig.getDatasetId());
         mAdapter.swapCursor(cursor);
     }
 
@@ -49,9 +56,24 @@ public class PanelListView extends HomeL
             row.updateFromCursor(cursor);
         }
 
         @Override
         public View newView(Context context, Cursor cursor, ViewGroup parent) {
             return LayoutInflater.from(parent.getContext()).inflate(R.layout.panel_list_row, parent, false);
         }
     }
-}
\ No newline at end of file
+
+    private class PanelListItemClickListener implements AdapterView.OnItemClickListener {
+        @Override
+        public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
+            Cursor cursor = mAdapter.getCursor();
+            if (cursor == null || !cursor.moveToPosition(position)) {
+                throw new IllegalStateException("Couldn't move cursor to position " + position);
+            }
+
+            int urlIndex = cursor.getColumnIndexOrThrow(HomeItems.URL);
+            final String url = cursor.getString(urlIndex);
+
+            mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.OPEN_WITH_INTENT));
+        }
+    }
+}
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -61,17 +61,17 @@
      We can't use android plural forms, sadly. See bug #753859. -->
 <!ENTITY num_tabs2 "&formatD; tabs">
 <!ENTITY new_tab_opened "New tab opened">
 
 <!ENTITY settings "Settings">
 <!ENTITY settings_title "Settings">
 <!ENTITY pref_category_advanced "Advanced">
 <!ENTITY pref_category_customize "Customize">
-<!ENTITY pref_category_search2 "Search settings">
+<!ENTITY pref_category_search3 "Search">
 <!ENTITY pref_category_display "Display">
 <!ENTITY pref_category_privacy_short "Privacy">
 <!ENTITY pref_category_vendor "&vendorShortName;">
 <!ENTITY pref_category_datareporting "Data choices">
 <!ENTITY pref_category_installed_search_engines "Installed search engines">
 <!ENTITY pref_category_add_search_providers "Add more search providers">
 <!ENTITY pref_category_search_restore_defaults "Restore search engines">
 <!ENTITY pref_search_restore_defaults "Restore defaults">
--- a/mobile/android/base/resources/xml-v11/preferences_customize.xml
+++ b/mobile/android/base/resources/xml-v11/preferences_customize.xml
@@ -4,46 +4,45 @@
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <!-- Changes should be mirrored to preferences_customize_tablet.xml. -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                   xmlns:gecko="http://schemas.android.com/apk/res-auto"
                   android:enabled="false">
 
+    <PreferenceScreen android:title="@string/pref_category_home"
+                      android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
+            <extra android:name="resource"
+                   android:value="preferences_home" />
+    </PreferenceScreen>
+
      <PreferenceScreen android:title="@string/pref_category_search"
                        android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
          <extra android:name="resource"
                 android:value="preferences_search"/>
      </PreferenceScreen>
 
-
-    <PreferenceScreen android:title="@string/pref_category_home"
-                      android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
-            <extra android:name="resource"
-                   android:value="preferences_home" />
-    </PreferenceScreen>
+    <ListPreference android:key="android.not_a_preference.restoreSession3"
+                    android:title="@string/pref_restore"
+                    android:defaultValue="quit"
+                    android:entries="@array/pref_restore_entries"
+                    android:entryValues="@array/pref_restore_values"
+                    android:persistent="true" />
 
     <org.mozilla.gecko.preferences.AndroidImportPreference
                   android:key="android.not_a_preference.import_android"
                   gecko:entries="@array/pref_import_android_entries"
                   gecko:entryKeys="@array/pref_import_android_keys"
                   gecko:initialValues="@array/pref_import_android_values"
                   android:title="@string/pref_import_android"
                   android:positiveButtonText="@string/bookmarkhistory_button_import"
                   android:negativeButtonText="@string/button_cancel"
                   android:persistent="false" />
 
-    <ListPreference android:key="android.not_a_preference.restoreSession3"
-                    android:title="@string/pref_restore"
-                    android:defaultValue="quit"
-                    android:entries="@array/pref_restore_entries"
-                    android:entryValues="@array/pref_restore_values"
-                    android:persistent="true" />
-
    <ListPreference android:key="app.update.autodownload"
                    android:title="@string/pref_update_autodownload"
                    android:entries="@array/pref_update_autodownload_entries"
                    android:entryValues="@array/pref_update_autodownload_values"
                    android:persistent="false" />
 
 </PreferenceScreen>
 
--- a/mobile/android/base/resources/xml-v11/preferences_customize_tablet.xml
+++ b/mobile/android/base/resources/xml-v11/preferences_customize_tablet.xml
@@ -11,45 +11,45 @@
                   xmlns:gecko="http://schemas.android.com/apk/res-auto"
                   android:title="@string/pref_category_customize"
                   android:enabled="false">
 
     <org.mozilla.gecko.preferences.SyncPreference android:key="android.not_a_preference.sync"
                                                   android:title="@string/pref_sync"
                                                   android:persistent="false" />
 
+    <PreferenceScreen android:title="@string/pref_category_home"
+                      android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
+            <extra android:name="resource"
+                   android:value="preferences_home" />
+    </PreferenceScreen>
+
     <PreferenceScreen android:title="@string/pref_category_search"
                       android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
         <extra android:name="resource"
                android:value="preferences_search"/>
     </PreferenceScreen>
 
-    <PreferenceScreen android:title="@string/pref_category_home"
-                      android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
-            <extra android:name="resource"
-                   android:value="preferences_home" />
-    </PreferenceScreen>
+    <ListPreference android:key="android.not_a_preference.restoreSession3"
+                    android:title="@string/pref_restore"
+                    android:defaultValue="quit"
+                    android:entries="@array/pref_restore_entries"
+                    android:entryValues="@array/pref_restore_values"
+                    android:persistent="true" />
 
     <org.mozilla.gecko.preferences.AndroidImportPreference
             android:key="android.not_a_preference.import_android"
             gecko:entries="@array/pref_import_android_entries"
             gecko:entryKeys="@array/pref_import_android_keys"
             gecko:initialValues="@array/pref_import_android_values"
             android:title="@string/pref_import_android"
             android:positiveButtonText="@string/bookmarkhistory_button_import"
             android:negativeButtonText="@string/button_cancel"
             android:persistent="false" />
 
-    <ListPreference android:key="android.not_a_preference.restoreSession3"
-                    android:title="@string/pref_restore"
-                    android:defaultValue="quit"
-                    android:entries="@array/pref_restore_entries"
-                    android:entryValues="@array/pref_restore_values"
-                    android:persistent="true" />
-
     <ListPreference android:key="app.update.autodownload"
                     android:title="@string/pref_update_autodownload"
                     android:entries="@array/pref_update_autodownload_entries"
                     android:entryValues="@array/pref_update_autodownload_values"
                     android:persistent="false" />
 
 </PreferenceScreen>
 
--- a/mobile/android/base/resources/xml/preferences_customize.xml
+++ b/mobile/android/base/resources/xml/preferences_customize.xml
@@ -2,52 +2,54 @@
 <!-- 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/. -->
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
                   xmlns:gecko="http://schemas.android.com/apk/res-auto"
                   android:enabled="false">
 
+    <PreferenceScreen android:title="@string/pref_category_home" >
+        <intent android:action="android.intent.action.VIEW"
+                android:targetPackage="@string/android_package_name"
+                android:targetClass="org.mozilla.gecko.preferences.GeckoPreferences" >
+            <extra
+                android:name="resource"
+                android:value="preferences_home" />
+        </intent>
+    </PreferenceScreen>
+
     <PreferenceScreen android:title="@string/pref_category_search" >
         <intent android:action="android.intent.action.VIEW"
                 android:targetPackage="@string/android_package_name"
                 android:targetClass="org.mozilla.gecko.preferences.GeckoPreferences" >
             <extra
                 android:name="resource"
                 android:value="preferences_search" />
         </intent>
     </PreferenceScreen>
 
-    <PreferenceScreen android:title="@string/pref_category_home" >
-        <intent android:action="android.intent.action.VIEW"
-                android:targetPackage="@string/android_package_name"
-                android:targetClass="org.mozilla.gecko.preferences.GeckoPreferences" >
-            <extra
-                android:name="resource"
-                android:value="preferences_home" />
-        </intent>
-    </PreferenceScreen>
+    <ListPreference android:key="android.not_a_preference.restoreSession3"
+                    android:title="@string/pref_restore"
+                    android:defaultValue="quit"
+                    android:entries="@array/pref_restore_entries"
+                    android:entryValues="@array/pref_restore_values"
+                    android:persistent="true" />
+
 
     <org.mozilla.gecko.preferences.AndroidImportPreference
                   android:key="android.not_a_preference.import_android"
                   gecko:entries="@array/pref_import_android_entries"
                   gecko:entryKeys="@array/pref_import_android_keys"
                   gecko:initialValues="@array/pref_import_android_values"
                   android:title="@string/pref_import_android"
                   android:positiveButtonText="@string/bookmarkhistory_button_import"
                   android:negativeButtonText="@string/button_cancel"
                   android:persistent="false" />
 
-    <ListPreference android:key="android.not_a_preference.restoreSession3"
-                    android:title="@string/pref_restore"
-                    android:defaultValue="quit"
-                    android:entries="@array/pref_restore_entries"
-                    android:entryValues="@array/pref_restore_values"
-                    android:persistent="true" />
 
    <ListPreference android:key="app.update.autodownload"
                    android:title="@string/pref_update_autodownload"
                    android:entries="@array/pref_update_autodownload_entries"
                    android:entryValues="@array/pref_update_autodownload_values"
                    android:persistent="false" />
 
 </PreferenceScreen>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -88,17 +88,17 @@
   <string name="media_play">&media_play;</string>
   <string name="media_pause">&media_pause;</string>
   <string name="media_stop">&media_stop;</string>
 
   <string name="settings">&settings;</string>
   <string name="settings_title">&settings_title;</string>
   <string name="pref_category_advanced">&pref_category_advanced;</string>
   <string name="pref_category_customize">&pref_category_customize;</string>
-  <string name="pref_category_search">&pref_category_search2;</string>
+  <string name="pref_category_search">&pref_category_search3;</string>
   <string name="pref_category_display">&pref_category_display;</string>
   <string name="pref_category_privacy_short">&pref_category_privacy_short;</string>
   <string name="pref_category_vendor">&pref_category_vendor;</string>
   <string name="pref_category_datareporting">&pref_category_datareporting;</string>
   <string name="pref_category_installed_search_engines">&pref_category_installed_search_engines;</string>
   <string name="pref_category_add_search_providers">&pref_category_add_search_providers;</string>
   <string name="pref_category_search_restore_defaults">&pref_category_search_restore_defaults;</string>
   <string name="pref_search_restore_defaults">&pref_search_restore_defaults;</string>
--- a/mobile/android/base/tests/testSettingsMenuItems.java
+++ b/mobile/android/base/tests/testSettingsMenuItems.java
@@ -26,20 +26,20 @@ public class testSettingsMenuItems exten
      * where defaultValue is optional, and there can be multiple options.
      *
      * These menu items are the ones that are always present - to test menu items that differ
      * based on build (e.g., release vs. nightly), add the items in <code>addConditionalSettings</code>. 
      */
 
     // Customize menu items.
     String[][] OPTIONS_CUSTOMIZE = {
-        { "Search settings", "", "Show search suggestions", "Installed search engines"},
         { "Home", "", "Panels" },
+        { "Search", "", "Show search suggestions", "Installed search engines"},
+        { "Tabs", "Don't restore after quitting " + BRAND_NAME, "Always restore", "Don't restore after quitting " + BRAND_NAME },
         { "Import from Android", "", "Bookmarks", "History", "Import" },
-        { "Tabs", "Don't restore after quitting " + BRAND_NAME, "Always restore", "Don't restore after quitting " + BRAND_NAME },
     };
 
     // Display menu items.
     String[][] OPTIONS_DISPLAY = {
         { "Text size" },
         { "Title bar", "Show page title", "Show page title", "Show page address" },
         { "Advanced" },
         { "Character encoding", "Don't show menu", "Show menu", "Don't show menu" },
--- a/services/common/hawk.js
+++ b/services/common/hawk.js
@@ -189,13 +189,17 @@ HawkClient.prototype = {
     };
 
     let extra = {
       now: this.now(),
       localtimeOffsetMsec: this.localtimeOffsetMsec,
     };
 
     let request = new HAWKAuthenticatedRESTRequest(uri, credentials, extra);
-    request[method](payloadObj, onComplete);
+    if (method == "post" || method == "put") {
+      request[method](payloadObj, onComplete);
+    } else {
+      request[method](onComplete);
+    }
 
     return deferred.promise;
   }
 }
--- a/services/common/rest.js
+++ b/services/common/rest.js
@@ -758,26 +758,32 @@ this.HAWKAuthenticatedRESTRequest =
   this.now = extra.now || Date.now();
   this.localtimeOffsetMsec = extra.localtimeOffsetMsec || 0;
   this._log.trace("local time, offset: " + this.now + ", " + (this.localtimeOffsetMsec));
 };
 HAWKAuthenticatedRESTRequest.prototype = {
   __proto__: RESTRequest.prototype,
 
   dispatch: function dispatch(method, data, onComplete, onProgress) {
+    let contentType = "text/plain";
+    if (method == "POST" || method == "PUT") {
+      contentType = "application/json";
+    }
     if (this.credentials) {
       let options = {
         now: this.now,
         localtimeOffsetMsec: this.localtimeOffsetMsec,
         credentials: this.credentials,
         payload: data && JSON.stringify(data) || "",
-        contentType: "application/json; charset=utf-8",
+        contentType: contentType,
       };
       let header = CryptoUtils.computeHAWK(this.uri, method, options);
       this.setHeader("Authorization", header.field);
       this._log.trace("hawk auth header: " + header.field);
     }
 
+    this.setHeader("Content-Type", contentType);
+
     return RESTRequest.prototype.dispatch.call(
       this, method, data, onComplete, onProgress
     );
   }
 };
--- a/services/common/tests/unit/head_helpers.js
+++ b/services/common/tests/unit/head_helpers.js
@@ -173,12 +173,11 @@ function uninstallFakePAC() {
   Cm.nsIComponentRegistrar.unregisterFactory(CID, PACSystemSettings);
 }
 
 // We want to ensure the legacy provider is used for most of these tests; the
 // tests that know how to deal with the Firefox Accounts identity hack things
 // to ensure that still works.
 function setDefaultIdentityConfig() {
   Cu.import("resource://gre/modules/Services.jsm");
-  Services.prefs.setBoolPref("identity.fxaccounts.enabled", false);
-//  Services.prefs.setBoolPref("services.sync.fxaccounts.enabled", false);
+  Services.prefs.setBoolPref("services.sync.fxaccounts.enabled", false);
 }
 setDefaultIdentityConfig();
--- a/services/fxaccounts/FxAccounts.jsm
+++ b/services/fxaccounts/FxAccounts.jsm
@@ -380,25 +380,16 @@ InternalMethods.prototype = {
     return this.fxAccountsClient.resendVerificationEmail(data.sessionToken);
   },
 
   notifyObservers: function(topic) {
     log.debug("Notifying observers of " + topic);
     Services.obs.notifyObservers(null, topic, null);
   },
 
-  /**
-   * Give xpcshell tests an override point for duration testing. This is
-   * necessary because the tests need to manipulate the date in order to
-   * simulate certificate expiration.
-   */
-  now: function() {
-    return Date.now();
-  },
-
   pollEmailStatus: function pollEmailStatus(sessionToken, why) {
     let myGenerationCount = this.generationCount;
     log.debug("entering pollEmailStatus: " + why + " " + myGenerationCount);
     if (why == "start") {
       // If we were already polling, stop and start again.  This could happen
       // if the user requested the verification email to be resent while we
       // were already polling for receipt of an earlier email.
       this.pollTimeRemaining = this.POLL_SESSION;
@@ -484,16 +475,30 @@ this.FxAccounts = function(mockInternal)
   if (mockInternal) {
     // Exposes the internal object for testing only.
     this.internal = internal;
   }
 }
 this.FxAccounts.prototype = Object.freeze({
   version: DATA_FORMAT_VERSION,
 
+  now: function() {
+    if (this.internal) {
+      return this.internal.now();
+    }
+    return internal.now();
+  },
+
+  get localtimeOffsetMsec() {
+    if (this.internal) {
+      return this.internal.localtimeOffsetMsec;
+    }
+    return internal.localtimeOffsetMsec;
+  },
+
   // set() makes sure that polling is happening, if necessary.
   // get() does not wait for verification, and returns an object even if
   // unverified. The caller of get() must check .verified .
   // The "fxaccounts:onverified" event will fire only when the verified
   // state goes from false to true, so callers must register their observer
   // and then call get(). In particular, it will not fire when the account
   // was found to be verified in a previous boot: if our stored state says
   // the account is verified, the event will never fire. So callers must do:
@@ -615,17 +620,16 @@ this.FxAccounts.prototype = Object.freez
   getKeys: function() {
     return internal.getKeys();
   },
 
   whenVerified: function(userData) {
     return internal.whenVerified(userData);
   },
 
-
   /**
    * Sign the current user out.
    *
    * @return Promise
    *         The promise is rejected if a storage error occurs.
    */
   signOut: function signOut() {
     return internal.signOut();
@@ -633,17 +637,36 @@ this.FxAccounts.prototype = Object.freez
 
   // Return the URI of the remote UI flows.
   getAccountsURI: function() {
     let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.uri");
     if (!/^https:/.test(url)) { // Comment to un-break emacs js-mode highlighting
       throw new Error("Firefox Accounts server must use HTTPS");
     }
     return url;
+  },
+
+  // Returns a promise that resolves with the URL to use to force a re-signin
+  // of the current account.
+  promiseAccountsForceSigninURI: function() {
+    let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.force_auth.uri");
+    if (!/^https:/.test(url)) { // Comment to un-break emacs js-mode highlighting
+      throw new Error("Firefox Accounts server must use HTTPS");
+    }
+    // but we need to append the email address onto a query string.
+    return this.getSignedInUser().then(accountData => {
+      if (!accountData) {
+        return null;
+      }
+      let newQueryPortion = url.indexOf("?") == -1 ? "?" : "&";
+      newQueryPortion += "email=" + encodeURIComponent(accountData.email);
+      return url + newQueryPortion;
+    });
   }
+
 });
 
 /**
  * JSONStorage constructor that creates instances that may set/get
  * to a specified file, in a directory that will be created if it
  * doesn't exist.
  *
  * @param options {
--- a/services/sync/Weave.js
+++ b/services/sync/Weave.js
@@ -61,34 +61,16 @@ WeaveService.prototype = {
   ensureLoaded: function () {
     Components.utils.import("resource://services-sync/main.js");
 
     // Side-effect of accessing the service is that it is instantiated.
     Weave.Service;
   },
 
   get fxAccountsEnabled() {
-    // first check if Firefox accounts is available at all.  This is so we can
-    // get this landed without forcing Fxa to be used (and require nightly
-    // testers to manually set this pref)
-    // Once we decide we want Fxa to be available, we just remove this block
-    // (although a fly in this ointment is tests - it might be that we must
-    // just set this as a pref with a default of true)
-    let fxAccountsAvailable;
-    try {
-      fxAccountsAvailable = Services.prefs.getBoolPref("identity.fxaccounts.enabled");
-    } catch (_) {
-    }
-    if (!fxAccountsAvailable) {
-      // Currently we don't support toggling this pref after initialization, so
-      // inject the pref value as a regular boolean.
-      delete this.fxAccountsEnabled;
-      this.fxAccountsEnabled = false;
-      return false;
-    }
     // work out what identity manager to use.  This is stored in a preference;
     // if the preference exists, we trust it.
     let fxAccountsEnabled;
     try {
       fxAccountsEnabled = Services.prefs.getBoolPref("services.sync.fxaccounts.enabled");
     } catch (_) {
       // That pref doesn't exist - so let's assume this is a first-run.
       // If sync already appears configured, we assume it's for the legacy
--- a/services/sync/modules/browserid_identity.js
+++ b/services/sync/modules/browserid_identity.js
@@ -41,16 +41,25 @@ function deriveKeyBundle(kB) {
   let out = CryptoUtils.hkdf(kB, undefined,
                              "identity.mozilla.com/picl/v1/oldsync", 2*32);
   let bundle = new BulkKeyBundle();
   // [encryptionKey, hmacKey]
   bundle.keyPair = [out.slice(0, 32), out.slice(32, 64)];
   return bundle;
 }
 
+/*
+  General authentication error for abstracting authentication
+  errors from multiple sources (e.g., from FxAccounts, TokenServer)
+    'message' is a string with a description of the error
+*/
+function AuthenticationError(message) {
+  this.message = message || "";
+}
+
 this.BrowserIDManager = function BrowserIDManager() {
   this._fxaService = fxAccounts;
   this._tokenServerClient = new TokenServerClient();
   // will be a promise that resolves when we are ready to authenticate
   this.whenReadyToAuthenticate = null;
   this._log = Log.repository.getLogger("Sync.BrowserIDManager");
   this._log.addAppender(new Log.DumpAppender());
   this._log.Level = Log.Level[Svc.Prefs.get("log.logger.identity")] || Log.Level.Error;
@@ -81,16 +90,17 @@ this.BrowserIDManager.prototype = {
     } catch (e) {
       return false;
     }
   },
 
   initialize: function() {
     Services.obs.addObserver(this, fxAccountsCommon.ONLOGIN_NOTIFICATION, false);
     Services.obs.addObserver(this, fxAccountsCommon.ONLOGOUT_NOTIFICATION, false);
+    Services.obs.addObserver(this, "weave:service:logout:finish", false);
     return this.initializeWithCurrentIdentity();
   },
 
   initializeWithCurrentIdentity: function(isInitialSync=false) {
     this._log.trace("initializeWithCurrentIdentity");
     Components.utils.import("resource://services-sync/main.js");
 
     // Reset the world before we do anything async.
@@ -140,18 +150,17 @@ this.BrowserIDManager.prototype = {
           Svc.Prefs.set("firstSync", "resetClient");
           Services.obs.notifyObservers(null, "weave:service:setup-complete", null);
           Weave.Utils.nextTick(Weave.Service.sync, Weave.Service);
         }
       }).then(null, err => {
         this._shouldHaveSyncKeyBundle = true; // but we probably don't have one...
         this.whenReadyToAuthenticate.reject(err);
         // report what failed...
-        this._log.error("Background fetch for key bundle failed: " + err);
-        throw err;
+        this._log.error("Background fetch for key bundle failed: " + err.message);
       });
       // and we are done - the fetch continues on in the background...
     }).then(null, err => {
       this._log.error("Processing logged in account: " + err.message);
     });
   },
 
   observe: function (subject, topic, data) {
@@ -163,16 +172,24 @@ this.BrowserIDManager.prototype = {
     case fxAccountsCommon.ONLOGOUT_NOTIFICATION:
       Components.utils.import("resource://services-sync/main.js");
       // Setting .username calls resetCredentials which drops the key bundle
       // and resets _shouldHaveSyncKeyBundle.
       this.username = "";
       this._account = null;
       Weave.Service.logout();
       break;
+
+    case "weave:service:logout:finish":
+      // This signals an auth error with the storage server,
+      // or that the user unlinked her account from the browser.
+      // Either way, we clear our auth token. In the case of an
+      // auth error, this will force the fetch of a new one.
+      this._token = null;
+      break;
     }
   },
 
    /**
    * Compute the sha256 of the message bytes.  Return bytes.
    */
   _sha256: function(message) {
     let hasher = Cc["@mozilla.org/security/hash;1"]
@@ -189,21 +206,21 @@ this.BrowserIDManager.prototype = {
   _computeXClientState: function(kBbytes) {
     return CommonUtils.bytesAsHex(this._sha256(kBbytes).slice(0, 16), false);
   },
 
   /**
    * Provide override point for testing token expiration.
    */
   _now: function() {
-    return this._fxaService.internal.now()
+    return this._fxaService.now()
   },
 
   get _localtimeOffsetMsec() {
-    return this._fxaService.internal.localtimeOffsetMsec;
+    return this._fxaService.localtimeOffsetMsec;
   },
 
   get account() {
     return this._account;
   },
 
   /**
    * Sets the active account name.
@@ -359,20 +376,22 @@ this.BrowserIDManager.prototype = {
       return null;
     }
     return userData;
   },
 
   _fetchSyncKeyBundle: function() {
     // Fetch a sync token for the logged in user from the token server.
     return this._fxaService.getKeys().then(userData => {
-      // unlikely, but if the logged in user somehow changed between these
-      // calls we better fail.
-      if (!userData || userData.email !== this.account) {
-        throw new Error("The currently logged-in user has changed.");
+      // Unlikely, but if the logged in user somehow changed between these
+      // calls we better fail. TODO: add tests for these
+      if (!userData) {
+        throw new AuthenticationError("No userData in _fetchSyncKeyBundle");
+      } else if (userData.email !== this.account) {
+        throw new AuthenticationError("Unexpected user change in _fetchSyncKeyBundle");
       }
       return this._fetchTokenForUser(userData).then(token => {
         this._token = token;
         // Set the username to be the uid returned by the token server.
         this.username = this._token.uid.toString();
         // both Jelly and FxAccounts give us kA/kB as hex.
         let kB = Utils.hexToBytes(userData.kB);
         this._syncKeyBundle = deriveKeyBundle(kB);
@@ -392,39 +411,64 @@ this.BrowserIDManager.prototype = {
     let headers = {"X-Client-State": this._computeXClientState(kBbytes)};
     log.info("Fetching Sync token from: " + tokenServerURI);
 
     function getToken(tokenServerURI, assertion) {
       let deferred = Promise.defer();
       let cb = function (err, token) {
         if (err) {
           log.info("TokenServerClient.getTokenFromBrowserIDAssertion() failed with: " + err.message);
-          return deferred.reject(err);
+          return deferred.reject(new AuthenticationError(err.message));
         } else {
           return deferred.resolve(token);
         }
       };
 
       client.getTokenFromBrowserIDAssertion(tokenServerURI, assertion, cb, headers);
       return deferred.promise;
     }
 
-    let audience = Services.io.newURI(tokenServerURI, null, null).prePath;
+    function getAssertion() {
+      let audience = Services.io.newURI(tokenServerURI, null, null).prePath;
+      return fxAccounts.getAssertion(audience).then(null, err => {
+        if (err.code === 401) {
+          throw new AuthenticationError("Unable to get assertion for user");
+        } else {
+          throw err;
+        }
+      });
+    };
+
     // wait until the account email is verified and we know that
     // getAssertion() will return a real assertion (not null).
     return this._fxaService.whenVerified(userData)
-      .then(() => this._fxaService.getAssertion(audience))
+      .then(() => getAssertion())
       .then(assertion => getToken(tokenServerURI, assertion))
       .then(token => {
-        token.expiration = this._now() + (token.duration * 1000);
+        // TODO: Make it be only 80% of the duration, so refresh the token
+        // before it actually expires. This is to avoid sync storage errors
+        // otherwise, we get a nasty notification bar briefly. Bug 966568.
+        token.expiration = this._now() + (token.duration * 1000) * 0.80;
         return token;
       })
       .then(null, err => {
-        Cu.reportError("Failed to fetch token: " + err);
-        // XXX - TODO - work out how to set sync to an error state.
+        // TODO: write tests to make sure that different auth error cases are handled here
+        // properly: auth error getting assertion, auth error getting token (invalid generation
+        // and client-state error)
+        if (err instanceof AuthenticationError) {
+          this._log.error("Authentication error in _fetchTokenForUser: " + err.message);
+          // Drop the sync key bundle, but still expect to have one.
+          // This will arrange for us to be in the right 'currentAuthState'
+          // such that UI will show the right error.
+          this._shouldHaveSyncKeyBundle = true;
+          this._syncKeyBundle = null;
+          Weave.Status.login = this.currentAuthState;
+          Services.obs.notifyObservers(null, "weave:service:login:error", null);
+        }
+        throw err;
       });
   },
 
   _fetchTokenForLoggedInUserSync: function() {
     let cb = Async.makeSpinningCallback();
 
     this._fxaService.getSignedInUser().then(userData => {
       this._fetchTokenForUser(userData).then(token => {
--- a/services/sync/tests/unit/test_browserid_identity.js
+++ b/services/sync/tests/unit/test_browserid_identity.js
@@ -131,23 +131,31 @@ add_test(function test_resourceAuthentic
   let fxa = new MockFxAccounts();
   fxa._now_is = now;
   fxa.internal.fxAccountsClient = fxaClient;
 
   // Picked up by the signed-in user module
   do_check_eq(fxa.internal.now(), now);
   do_check_eq(fxa.internal.localtimeOffsetMsec, localtimeOffsetMsec);
 
+  do_check_eq(fxa.now(), now);
+  do_check_eq(fxa.localtimeOffsetMsec, localtimeOffsetMsec);
+
   // Mocks within mocks...
   configureFxAccountIdentity(browseridManager, identityConfig);
   browseridManager._fxaService = fxa;
+
   do_check_eq(browseridManager._fxaService.internal.now(), now);
   do_check_eq(browseridManager._fxaService.internal.localtimeOffsetMsec,
       localtimeOffsetMsec);
 
+  do_check_eq(browseridManager._fxaService.now(), now);
+  do_check_eq(browseridManager._fxaService.localtimeOffsetMsec,
+      localtimeOffsetMsec);
+
   let request = new SyncStorageRequest("https://example.net/i/like/pie/");
   let authenticator = browseridManager.getResourceAuthenticator();
   let output = authenticator(request, 'GET');
   dump("output" + JSON.stringify(output));
   let authHeader = output.headers.authorization;
   do_check_true(authHeader.startsWith('Hawk'));
 
   // Skew correction is applied in the header and we're within the two-minute