Merge fx-team to m-c on a CLOSED TREE.
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 29 Jan 2014 15:18:45 -0500
changeset 181806 90bfbf075f3f4f2e49cfe4b7155289b00f2a05c8
parent 181785 27c8e496895aa887d4819161c04bbff6cf1f46c8 (current diff)
parent 181805 bb24d5ea7f897325bb0a043891fb4c3058bb5eec (diff)
child 181860 73eefb421e2afe6cc34a66d5bbb1896dbea13728
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge fx-team to m-c on a CLOSED TREE.
--- a/browser/base/content/aboutaccounts/aboutaccounts.js
+++ b/browser/base/content/aboutaccounts/aboutaccounts.js
@@ -4,24 +4,64 @@
 
 "use strict";
 
 const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FxAccounts.jsm");
 
+const PREF_LAST_FXA_USER = "identity.fxaccounts.lastSignedInUser";
+
 function log(msg) {
   //dump("FXA: " + msg + "\n");
 };
 
 function error(msg) {
   console.log("Firefox Account Error: " + msg + "\n");
 };
 
+function getPreviousAccountName() {
+  try {
+    return Services.prefs.getComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString).data;
+  } catch (_) {
+    return "";
+  }
+}
+
+function setPreviousAccountName(acctName) {
+  let string = Cc["@mozilla.org/supports-string;1"]
+               .createInstance(Ci.nsISupportsString);
+  string.data = acctName;
+  Services.prefs.setComplexValue(PREF_LAST_FXA_USER, Ci.nsISupportsString, string);
+}
+
+function needRelinkWarning(accountData) {
+  let prevAcct = getPreviousAccountName();
+  return prevAcct && prevAcct != accountData.email;
+}
+
+function promptForRelink() {
+  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",
+                                            [Services.prefs.getCharPref(PREF_LAST_FXA_USER)], 1);
+  let body = sb.GetStringFromName("relink.verify.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 () {
     let weave = Cc["@mozilla.org/weave/service;1"]
                   .getService(Ci.nsISupports)
                   .wrappedJSObject;
 
@@ -63,16 +103,33 @@ let wrapper = {
   onLogin: function (accountData) {
     log("Received: 'login'. Data:" + JSON.stringify(accountData));
 
     if (accountData.customizeSync) {
       Services.prefs.setBoolPref("services.sync.needsCustomization", true);
       delete accountData.customizeSync;
     }
 
+    // If the last fxa account used for sync isn't this account, we display
+    // a modal dialog checking they really really want to do this...
+    // (This is sync-specific, so ideally would be in sync's identity module,
+    // but it's a little more seamless to do here, and sync is currently the
+    // only fxa consumer, so...
+    if (needRelinkWarning(accountData) && !promptForRelink()) {
+      // we need to tell the page we successfully received the message, but
+      // then bail without telling fxAccounts
+      this.injectData("message", { status: "login" });
+      // and reload the page or else it remains in a "signed in" state.
+      window.location.reload();
+      return;
+    }
+
+    // Remember who it was so we can log out next time.
+    setPreviousAccountName(accountData.email);
+
     fxAccounts.setSignedInUser(accountData).then(
       () => {
         this.injectData("message", { status: "login" });
         // until we sort out a better UX, just leave the jelly page in place.
         // If the account email is not yet verified, it will tell the user to
         // go check their email, but then it will *not* change state after
         // the verification completes (the browser will begin syncing, but
         // won't notify the user). If the email has already been verified,
--- 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.fxa-plural.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>&aboutAccounts.pageTitle;</h1>
+          <h1>&syncBrand.fxa-plural.label;</h1>
 
           <h2>&syncBrand.shortName.label;</h2>
         </header>
 
         <section>
             <div class="graphic graphic-sync-intro"> </div>
 
             <div class="button-row">
               <a class="button" href="#" onclick="openPrefs()">Manage</a>
             </div>
         </section>
       </div>
 
       <div id="intro">
         <header>
-          <h1>&aboutAccounts.pageTitle;</h1>
+          <h1>&syncBrand.fxa-plural.label;</h1>
 
           <h2>&syncBrand.shortName.label;</h2>
         </header>
 
         <section>
             <div class="graphic graphic-sync-intro"> </div>
 
             <div class="description">&aboutAccountsSetup.description;</div>
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -1047,16 +1047,29 @@ let BookmarkingUI = {
     let viewToolbarMenuitem = getPlacesAnonymousElement("view-toolbar");
     if (viewToolbarMenuitem) {
       // Update View bookmarks toolbar checkbox menuitem.
       let personalToolbar = document.getElementById("PersonalToolbar");
       viewToolbarMenuitem.setAttribute("checked", !personalToolbar.collapsed);
     }
   },
 
+  attachPlacesView: function(event, node) {
+    // If the view is already there, bail out early.
+    if (node.parentNode._placesView)
+      return;
+
+    new PlacesMenu(event, "place:folder=BOOKMARKS_MENU", {
+      extraClasses: {
+        mainLevel: "subviewbutton"
+      },
+      insertionPoint: ".panel-subview-footer"
+    });
+  },
+
   /**
    * Handles star styling based on page proxy state changes.
    */
   onPageProxyStateChanged: function BUI_onPageProxyStateChanged(aState) {
     if (!this.star) {
       return;
     }
 
@@ -1234,17 +1247,21 @@ let BookmarkingUI = {
     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");
+                                                  "panelMenu_bookmarksMenu", {
+                                                    extraClasses: {
+                                                      mainLevel: "subviewbutton"
+                                                    }
+                                                  });
   },
 
   onPanelMenuViewHiding: function BUI_onViewHiding(aEvent) {
     this._panelMenuView.uninit();
     delete this._panelMenuView;
   },
 
   onPanelMenuViewCommand: function BUI_onPanelMenuViewCommand(aEvent, aView) {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -748,58 +748,59 @@
                        anchor="dropmarker"
                        ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
                        ondragover="PlacesMenuDNDHandler.onDragOver(event);"
                        ondragleave="PlacesMenuDNDHandler.onDragLeave(event);"
                        ondrop="PlacesMenuDNDHandler.onDrop(event);"
                        cui-areatype="toolbar"
                        oncommand="BookmarkingUI.onCommand(event);">
           <menupopup id="BMB_bookmarksPopup"
+                     class="cui-widget-panel cui-widget-panelview cui-widget-panelWithFooter"
                      placespopup="true"
                      context="placesContext"
                      openInTabs="children"
                      oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
                      onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
                      onpopupshowing="BookmarkingUI.onPopupShowing(event);
-                                     if (!this.parentNode._placesView)
-                                       new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
+                                     BookmarkingUI.attachPlacesView(event, this);"
                      tooltip="bhTooltip" popupsinherittooltip="true">
-            <menuitem id="BMB_bookmarksShowAll"
-                      label="&showAllBookmarks2.label;"
-                      command="Browser:ShowAllBookmarks"
-                      key="manBookmarkKb"/>
             <menuitem id="BMB_viewBookmarksSidebar"
+                      class="subviewbutton"
                       label="&viewBookmarksSidebar2.label;"
                       type="checkbox"
                       oncommand="toggleSidebar('viewBookmarksSidebar');">
               <observes element="viewBookmarksSidebar" attribute="checked"/>
             </menuitem>
             <menuseparator/>
             <menuitem id="BMB_subscribeToPageMenuitem"
 #ifndef XP_MACOSX
-                      class="menuitem-iconic"
+                      class="menuitem-iconic subviewbutton"
+#else
+                      class="subviewbutton"
 #endif
                       label="&subscribeToPageMenuitem.label;"
                       oncommand="return FeedHandler.subscribeToFeed(null, event);"
                       onclick="checkForMiddleClick(this, event);"
                       observes="singleFeedMenuitemState"/>
             <menu id="BMB_subscribeToPageMenupopup"
 #ifndef XP_MACOSX
-                  class="menu-iconic"
+                  class="menu-iconic subviewbutton"
+#else
+                  class="subviewbutton"
 #endif
                   label="&subscribeToPageMenupopup.label;"
                   observes="multipleFeedsMenuState">
               <menupopup id="BMB_subscribeToPageSubmenuMenupopup"
                          onpopupshowing="return FeedHandler.buildFeedList(event.target);"
                          oncommand="return FeedHandler.subscribeToFeed(null, event);"
                          onclick="checkForMiddleClick(this, event);"/>
             </menu>
             <menuseparator/>
             <menu id="BMB_bookmarksToolbar"
-                  class="menu-iconic bookmark-item"
+                  class="menu-iconic bookmark-item subviewbutton"
                   label="&personalbarCmd.label;"
                   container="true">
               <menupopup id="BMB_bookmarksToolbarPopup"
                          placespopup="true"
                          context="placesContext"
                          onpopupshowing="if (!this.parentNode._placesView)
                                            new PlacesMenu(event, 'place:folder=TOOLBAR');">
                 <menuitem id="BMB_viewBookmarksToolbar"
@@ -808,27 +809,32 @@
                           type="checkbox"
                           oncommand="onViewToolbarCommand(event)"
                           label="&viewBookmarksToolbar.label;"/>
                 <menuseparator/>
                 <!-- Bookmarks toolbar items -->
               </menupopup>
             </menu>
             <menu id="BMB_unsortedBookmarks"
-                  class="menu-iconic bookmark-item"
+                  class="menu-iconic bookmark-item subviewbutton"
                   label="&bookmarksMenuButton.unsorted.label;"
                   container="true">
               <menupopup id="BMB_unsortedBookmarksPopup"
                          placespopup="true"
                          context="placesContext"
                          onpopupshowing="if (!this.parentNode._placesView)
                                            new PlacesMenu(event, 'place:folder=UNFILED_BOOKMARKS');"/>
             </menu>
             <menuseparator/>
-            <!-- Bookmarks menu items -->
+            <!-- Bookmarks menu items will go here -->
+            <menuitem id="BMB_bookmarksShowAll"
+                      class="subviewbutton panel-subview-footer"
+                      label="&showAllBookmarks2.label;"
+                      command="Browser:ShowAllBookmarks"
+                      key="manBookmarkKb"/>
           </menupopup>
         </toolbarbutton>
 
         <!-- This is a placeholder for the Downloads Indicator.  It is visible
              during the customization of the toolbar, in the palette, and before
              the Downloads Indicator overlay is loaded. -->
         <toolbarbutton id="downloads-button" class="toolbarbutton-1 chromeclass-toolbar-additional"
                        oncommand="DownloadsIndicatorView.onCommand(event);"
--- a/browser/components/customizableui/content/jar.mn
+++ b/browser/components/customizableui/content/jar.mn
@@ -1,11 +1,11 @@
 # 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/.
 
 browser.jar:
   content/browser/customizableui/aboutCustomizing.xhtml
   content/browser/customizableui/panelUI.css
-  content/browser/customizableui/panelUI.js
+* content/browser/customizableui/panelUI.js
   content/browser/customizableui/panelUI.xml
   content/browser/customizableui/toolbar.xml
 
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -17,29 +17,33 @@
       <footer id="PanelUI-footer">
         <toolbarbutton id="PanelUI-fxa-status" label="&fxaSignIn.label;"
                        oncommand="gFxAccounts.toggle(event);"
                        hidden="true"/>
 
         <hbox id="PanelUI-footer-inner">
           <toolbarbutton id="PanelUI-customize" label="&appMenuCustomize.label;"
                          exitLabel="&appMenuCustomizeExit.label;"
+                         tooltiptext="&appMenuCustomize.tooltip;"
+                         exitTooltiptext="&appMenuCustomizeExit.tooltip;"
                          oncommand="gCustomizeMode.toggle();"/>
           <toolbarseparator/>
           <toolbarbutton id="PanelUI-help" label="&helpMenu.label;"
-                         tooltiptext="&helpMenu.label;"
+                         tooltiptext="&appMenuHelp.tooltip;"
                          oncommand="PanelUI.showHelpView(this.parentNode);"/>
           <toolbarseparator/>
           <toolbarbutton id="PanelUI-quit"
 #ifdef XP_WIN
                          label="&quitApplicationCmdWin.label;"
-                         tooltiptext="&quitApplicationCmdWin.label;"
+#else
+#ifdef XP_MACOSX
+                         label="&quitApplicationCmdMac.label;"
 #else
                          label="&quitApplicationCmd.label;"
-                         tooltiptext="&quitApplicationCmd.label;"
+#endif
 #endif
                          command="cmd_quitApplication"/>
         </hbox>
       </footer>
     </panelview>
 
     <panelview id="PanelUI-history" flex="1">
       <label value="&appMenuHistory.label;" class="panel-subview-header"/>
@@ -80,21 +84,16 @@
     <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"
                      command="Browser:AddBookmarkAs"
                      onclick="PanelUI.hide();"/>
       <toolbarseparator/>
-      <toolbarbutton id="panelMenu_showAllBookmarks"
-                     label="&showAllBookmarks2.label;"
-                     class="subviewbutton"
-                     command="Browser:ShowAllBookmarks"
-                     onclick="PanelUI.hide();"/>
       <toolbarbutton id="panelMenu_viewBookmarksSidebar"
                      label="&viewBookmarksSidebar2.label;"
                      class="subviewbutton"
                      oncommand="toggleSidebar('viewBookmarksSidebar'); PanelUI.hide();">
         <observes element="viewBookmarksSidebar" attribute="checked"/>
       </toolbarbutton>
       <toolbarbutton id="panelMenu_viewBookmarksToolbar"
                      label="&viewBookmarksToolbar.label;"
@@ -115,19 +114,23 @@
       <toolbaritem id="panelMenu_bookmarksMenu"
                    flex="1"
                    orient="vertical"
                    smoothscroll="false"
                    onclick="if (event.button == 1) BookmarkingUI.onPanelMenuViewCommand(event, this._placesView);"
                    oncommand="BookmarkingUI.onPanelMenuViewCommand(event, this._placesView);"
                    flatList="true"
                    tooltip="bhTooltip">
-        <!-- bookmarks menu items -->
+        <!-- bookmarks menu items will go here -->
       </toolbaritem>
-
+      <toolbarbutton id="panelMenu_showAllBookmarks"
+                     label="&showAllBookmarks2.label;"
+                     class="subviewbutton panel-subview-footer"
+                     command="Browser:ShowAllBookmarks"
+                     onclick="PanelUI.hide();"/>
     </panelview>
 
     <panelview id="PanelUI-socialapi" flex="1"/>
 
     <panelview id="PanelUI-feeds" flex="1" oncommand="FeedHandler.subscribeToFeed(null, event);">
       <label value="&feedsMenu.label;" class="panel-subview-header"/>
     </panelview>
 
--- a/browser/components/customizableui/content/panelUI.js
+++ b/browser/components/customizableui/content/panelUI.js
@@ -3,16 +3,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 XPCOMUtils.defineLazyModuleGetter(this, "CustomizableUI",
                                   "resource:///modules/CustomizableUI.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ScrollbarSampler",
                                   "resource:///modules/ScrollbarSampler.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Promise",
                                   "resource://gre/modules/Promise.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ShortcutUtils",
+                                  "resource://gre/modules/ShortcutUtils.jsm");
 /**
  * Maintains the state and dispatches events for the main menu panel.
  */
 
 const PanelUI = {
   /** Panel events that we listen for. **/
   get kEvents() ["popupshowing", "popupshown", "popuphiding", "popuphidden"],
   /**
@@ -231,16 +233,17 @@ const PanelUI = {
       } else {
         this.beginBatchUpdate();
         try {
           CustomizableUI.registerMenuPanel(this.contents);
         } finally {
           this.endBatchUpdate();
         }
       }
+      this._updateQuitTooltip();
       this.panel.hidden = false;
     }.bind(this)).then(null, Cu.reportError);
 
     return this._readyPromise;
   },
 
   /**
    * Switch the panel to the main view if it's not already
@@ -291,26 +294,32 @@ const PanelUI = {
       viewNode.dispatchEvent(evt);
       if (evt.defaultPrevented) {
         return;
       }
 
       let tempPanel = document.createElement("panel");
       tempPanel.setAttribute("type", "arrow");
       tempPanel.setAttribute("id", "customizationui-widget-panel");
+      tempPanel.setAttribute("class", "cui-widget-panel");
       tempPanel.setAttribute("level", "top");
       document.getElementById(CustomizableUI.AREA_NAVBAR).appendChild(tempPanel);
+      // If the view has a footer, set a convenience class on the panel.
+      tempPanel.classList.toggle("cui-widget-panelWithFooter",
+                                 viewNode.querySelector(".panel-subview-footer"));
 
       let multiView = document.createElement("panelmultiview");
       tempPanel.appendChild(multiView);
       multiView.setMainView(viewNode);
+      viewNode.classList.add("cui-widget-panelview");
       CustomizableUI.addPanelCloseListeners(tempPanel);
 
       let panelRemover = function() {
         tempPanel.removeEventListener("popuphidden", panelRemover);
+        viewNode.classList.remove("cui-widget-panelview");
         CustomizableUI.removePanelCloseListeners(tempPanel);
         let evt = new CustomEvent("ViewHiding", {detail: viewNode});
         viewNode.dispatchEvent(evt);
         aAnchor.open = false;
 
         this.multiView.appendChild(viewNode);
         tempPanel.parentElement.removeChild(tempPanel);
       }.bind(this);
@@ -411,17 +420,36 @@ const PanelUI = {
     }
     items.appendChild(fragment);
 
     this.addEventListener("command", PanelUI);
   },
 
   _onHelpViewHide: function(aEvent) {
     this.removeEventListener("command", PanelUI);
-  }
+  },
+
+  _updateQuitTooltip: function() {
+#ifndef XP_WIN
+#ifdef XP_MACOSX
+    let tooltipId = "quit-button.tooltiptext.mac";
+    let brands = Services.strings.createBundle("chrome://branding/locale/brand.properties");
+    let stringArgs = [brands.GetStringFromName("brandShortName")];
+#else
+    let tooltipId = "quit-button.tooltiptext.linux";
+    let stringArgs = [];
+#endif
+
+    let key = document.getElementById("key_quitApplication");
+    stringArgs.push(ShortcutUtils.prettifyShortcut(key));
+    let tooltipString = CustomizableUI.getLocalizedProperty({x: tooltipId}, "x", stringArgs);
+    let quitButton = document.getElementById("PanelUI-quit");
+    quitButton.setAttribute("tooltiptext", tooltipString);
+#endif
+  },
 };
 
 /**
  * Gets the currently selected locale for display.
  * @return  the selected locale or "en-US" if none is selected
  */
 function getLocale() {
   try {
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -1953,17 +1953,22 @@ let CustomizableUIInternal = {
     for (let [window, ] of gBuildWindows) {
       let windowCache = gSingleWrapperCache.get(window);
       if (windowCache) {
         windowCache.delete(aWidgetId);
       }
       let widgetNode = window.document.getElementById(aWidgetId) ||
                        window.gNavToolbox.palette.getElementsByAttribute("id", aWidgetId)[0];
       if (widgetNode) {
+        let container = widgetNode.parentNode
+        this.notifyListeners("onWidgetBeforeDOMChange", widgetNode, null,
+                             container, true);
         widgetNode.remove();
+        this.notifyListeners("onWidgetAfterDOMChange", widgetNode, null,
+                             container, true);
       }
       if (widget.type == "view") {
         let viewNode = window.document.getElementById(widget.viewId);
         if (viewNode) {
           for (let eventName of kSubviewEvents) {
             let handler = "on" + eventName;
             if (typeof widget[handler] == "function") {
               viewNode.removeEventListener(eventName, widget[handler], false);
@@ -3397,17 +3402,17 @@ OverflowableToolbar.prototype = {
   },
 
   _enable: function() {
     this._enabled = true;
     this.onOverflow();
   },
 
   onWidgetBeforeDOMChange: function(aNode, aNextNode, aContainer) {
-    if (aContainer != this._target) {
+    if (aContainer != this._target && aContainer != this._list) {
       return;
     }
     // When we (re)move an item, update all the items that come after it in the list
     // with the minsize *of the item before the to-be-removed node*. This way, we
     // ensure that we try to move items back as soon as that's possible.
     if (aNode.parentNode == this._list) {
       let updatedMinSize;
       if (aNode.previousSibling) {
@@ -3420,17 +3425,17 @@ OverflowableToolbar.prototype = {
       while (nextItem) {
         this._collapsed.set(nextItem.id, updatedMinSize);
         nextItem = nextItem.nextSibling;
       }
     }
   },
 
   onWidgetAfterDOMChange: function(aNode, aNextNode, aContainer) {
-    if (aContainer != this._target) {
+    if (aContainer != this._target && aContainer != this._list) {
       return;
     }
 
     let nowInBar = aNode.parentNode == aContainer;
     let nowOverflowed = aNode.parentNode == this._list;
     let wasOverflowed = this._collapsed.has(aNode.id);
 
     // If this wasn't overflowed before...
--- a/browser/components/customizableui/src/CustomizeMode.jsm
+++ b/browser/components/customizableui/src/CustomizeMode.jsm
@@ -169,16 +169,18 @@ CustomizeMode.prototype = {
       // while customizing.
       let mainView = window.PanelUI.mainView;
       let panelHolder = document.getElementById("customization-panelHolder");
       panelHolder.appendChild(mainView);
 
       let customizeButton = document.getElementById("PanelUI-customize");
       customizeButton.setAttribute("enterLabel", customizeButton.getAttribute("label"));
       customizeButton.setAttribute("label", customizeButton.getAttribute("exitLabel"));
+      customizeButton.setAttribute("enterTooltiptext", customizeButton.getAttribute("tooltiptext"));
+      customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("exitTooltiptext"));
 
       this._transitioning = true;
 
       let customizer = document.getElementById("customization-container");
       customizer.parentNode.selectedPanel = customizer;
       customizer.hidden = false;
 
       yield this._doTransition(true);
@@ -320,16 +322,18 @@ CustomizeMode.prototype = {
       this.dispatchToolboxEvent("customizationending");
 
       window.PanelUI.setMainView(window.PanelUI.mainView);
       window.PanelUI.menuButton.disabled = false;
 
       let customizeButton = document.getElementById("PanelUI-customize");
       customizeButton.setAttribute("exitLabel", customizeButton.getAttribute("label"));
       customizeButton.setAttribute("label", customizeButton.getAttribute("enterLabel"));
+      customizeButton.setAttribute("exitTooltiptext", customizeButton.getAttribute("tooltiptext"));
+      customizeButton.setAttribute("tooltiptext", customizeButton.getAttribute("enterTooltiptext"));
 
       // We have to use setAttribute/removeAttribute here instead of the
       // property because the XBL property will be set later, and right
       // now we'd be setting an expando, which breaks the XBL property.
       document.getElementById("PanelUI-help").removeAttribute("disabled");
       document.getElementById("PanelUI-quit").removeAttribute("disabled");
 
       // We need to set this._customizing to false before removing the tab
@@ -628,19 +632,16 @@ CustomizeMode.prototype = {
     if (aNode.hasAttribute("title")) {
       wrapper.setAttribute("title", aNode.getAttribute("title"));
     } else if (aNode.hasAttribute("label")) {
       wrapper.setAttribute("title", aNode.getAttribute("label"));
     }
 
     if (aNode.hasAttribute("flex")) {
       wrapper.setAttribute("flex", aNode.getAttribute("flex"));
-      if (aPlace == "palette") {
-        aNode.removeAttribute("flex");
-      }
     }
 
 
     const kPanelItemContextMenu = "customizationPanelItemContextMenu";
     const kPaletteItemContextMenu = "customizationPaletteItemContextMenu";
     let contextMenuAttrName = aNode.getAttribute("context") ? "context" :
                                 aNode.getAttribute("contextmenu") ? "contextmenu" : "";
     let currentContextMenu = aNode.getAttribute(contextMenuAttrName);
@@ -697,20 +698,16 @@ CustomizeMode.prototype = {
     if (aWrapper.hasAttribute("itemobserves")) {
       toolbarItem.setAttribute("observes", aWrapper.getAttribute("itemobserves"));
     }
 
     if (aWrapper.hasAttribute("itemchecked")) {
       toolbarItem.checked = true;
     }
 
-    if (aWrapper.hasAttribute("flex") && !toolbarItem.hasAttribute("flex")) {
-      toolbarItem.setAttribute("flex", aWrapper.getAttribute("flex"));
-    }
-
     if (aWrapper.hasAttribute("itemcommand")) {
       let commandID = aWrapper.getAttribute("itemcommand");
       toolbarItem.setAttribute("command", commandID);
 
       //XXX Bug 309953 - toolbarbuttons aren't in sync with their commands after customizing
       let command = this.document.getElementById(commandID);
       if (command && command.hasAttribute("disabled")) {
         toolbarItem.setAttribute("disabled", command.getAttribute("disabled"));
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -24,19 +24,16 @@ skip-if = os == "mac"
 [browser_890262_destroyWidget_after_add_to_panel.js]
 [browser_892955_isWidgetRemovable_for_removed_widgets.js]
 [browser_892956_destroyWidget_defaultPlacements.js]
 [browser_909779_overflow_toolbars_new_window.js]
 [browser_901207_searchbar_in_panel.js]
 [browser_913972_currentset_overflow.js]
 
 [browser_914138_widget_API_overflowable_toolbar.js]
-# Because of the specific widths, this test is fragile and has been disabled.
-# NB: it was designed for mac only, but started randomly failing there.
-skip-if = true
 
 [browser_914863_disabled_help_quit_buttons.js]
 [browser_918049_skipintoolbarset_dnd.js]
 [browser_923857_customize_mode_event_wrapping_during_reset.js]
 [browser_927717_customize_drag_empty_toolbar.js]
 [browser_932928_show_notice_when_palette_empty.js]
 
 [browser_934113_menubar_removable.js]
--- a/browser/components/customizableui/test/browser_914138_widget_API_overflowable_toolbar.js
+++ b/browser/components/customizableui/test/browser_914138_widget_API_overflowable_toolbar.js
@@ -4,16 +4,17 @@
 
 "use strict";
 
 let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
 let overflowList = document.getElementById(navbar.getAttribute("overflowtarget"));
 
 const kTestBtn1 = "test-addWidgetToArea-overflow";
 const kTestBtn2 = "test-removeWidgetFromArea-overflow";
+const kTestBtn3 = "test-createWidget-overflow";
 const kHomeBtn = "home-button";
 const kDownloadsBtn = "downloads-button";
 const kSearchBox = "search-container";
 const kStarBtn = "bookmarks-menu-button";
 
 let originalWindowWidth;
 
 // Adding a widget should add it next to the widget it's being inserted next to.
@@ -22,61 +23,46 @@ add_task(function() {
   createDummyXULButton(kTestBtn1, "Test");
   ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
   ok(CustomizableUI.inDefaultState, "Should start in default state.");
 
   window.resizeTo(400, window.outerHeight);
   yield waitForCondition(() => navbar.hasAttribute("overflowing"));
   ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
   ok(!navbar.querySelector("#" + kHomeBtn), "Home button should no longer be in the navbar");
-  ok(overflowList.querySelector("#" + kHomeBtn), "Home button should be overflowing");
+  let homeBtnNode = overflowList.querySelector("#" + kHomeBtn);
+  ok(homeBtnNode, "Home button should be overflowing");
+  ok(homeBtnNode && homeBtnNode.classList.contains("overflowedItem"), "Home button should have overflowedItem class");
 
   let placementOfHomeButton = CustomizableUI.getWidgetIdsInArea(navbar.id).indexOf(kHomeBtn);
   CustomizableUI.addWidgetToArea(kTestBtn1, navbar.id, placementOfHomeButton);
   ok(!navbar.querySelector("#" + kTestBtn1), "New button should not be in the navbar");
-  ok(overflowList.querySelector("#" + kTestBtn1), "New button should be overflowing");
-  let nextEl = document.getElementById(kTestBtn1).nextSibling;
+  let newButtonNode = overflowList.querySelector("#" + kTestBtn1);
+  ok(newButtonNode, "New button should be overflowing");
+  ok(newButtonNode && newButtonNode.classList.contains("overflowedItem"), "New button should have overflowedItem class");
+  let nextEl = newButtonNode && newButtonNode.nextSibling;
   is(nextEl && nextEl.id, kHomeBtn, "Test button should be next to home button.");
 
   window.resizeTo(originalWindowWidth, window.outerHeight);
   yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
   ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
   ok(navbar.querySelector("#" + kHomeBtn), "Home button should be in the navbar");
+  ok(homeBtnNode && !homeBtnNode.classList.contains("overflowedItem"), "Home button should no longer have overflowedItem class");
   ok(!overflowList.querySelector("#" + kHomeBtn), "Home button should no longer be overflowing");
   ok(navbar.querySelector("#" + kTestBtn1), "Test button should be in the navbar");
   ok(!overflowList.querySelector("#" + kTestBtn1), "Test button should no longer be overflowing");
+  ok(newButtonNode && !newButtonNode.classList.contains("overflowedItem"), "New button should no longer have overflowedItem class");
   let el = document.getElementById(kTestBtn1);
   if (el) {
     CustomizableUI.removeWidgetFromArea(kTestBtn1);
     el.remove();
   }
   window.resizeTo(originalWindowWidth, window.outerHeight);
 });
 
-// Removing a widget from the toolbar should try to move items back.
-add_task(function() {
-  // This is pretty weird. We're going to try to move only the home button into the overlay:
-  let downloadsBtn = document.getElementById(kDownloadsBtn);
-  // Guarantee overflow of too much stuff:
-  window.resizeTo(700, window.outerHeight);
-  let inc = 15;
-  while (window.outerWidth < originalWindowWidth &&
-         downloadsBtn.parentNode != navbar.customizationTarget) {
-    window.resizeTo(window.outerWidth + inc, window.outerHeight);
-    yield waitFor(500);
-  }
-  ok(overflowList.querySelector("#home-button"), "Home button should be overflowing");
-  CustomizableUI.removeWidgetFromArea("downloads-button");
-  is(document.getElementById("home-button").parentNode, navbar.customizationTarget, "Home button should move back.");
-  ok(!navbar.hasAttribute("overflowing"), "Navbar is no longer overflowing");
-  window.resizeTo(originalWindowWidth, window.outerHeight);
-  yield waitForCondition(function() !navbar.hasAttribute("overflowing"));
-  CustomizableUI.reset();
-});
-
 // Removing a widget should remove it from the overflow list if that is where it is, and update it accordingly.
 add_task(function() {
   createDummyXULButton(kTestBtn2, "Test");
   ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
   ok(CustomizableUI.inDefaultState, "Should start in default state.");
   CustomizableUI.addWidgetToArea(kTestBtn2, navbar.id);
   ok(!navbar.hasAttribute("overflowing"), "Should still have a non-overflowing toolbar.");
 
@@ -98,48 +84,48 @@ add_task(function() {
   let el = document.getElementById(kTestBtn2);
   if (el) {
     CustomizableUI.removeWidgetFromArea(kTestBtn2);
     el.remove();
   }
   window.resizeTo(originalWindowWidth, window.outerHeight);
 });
 
-// Overflow everything that can, then reorganize that list
+// Constructing a widget while overflown should set the right class on it.
 add_task(function() {
+  originalWindowWidth = window.outerWidth;
   ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
   ok(CustomizableUI.inDefaultState, "Should start in default state.");
 
-  window.resizeTo(360, window.outerHeight);
-  yield waitForCondition(() => navbar.getAttribute("overflowing") == "true");
-  ok(!navbar.querySelector("#" + kSearchBox), "Search container should be overflowing");
-  let placements = CustomizableUI.getWidgetIdsInArea(navbar.id);
-  let searchboxPlacement = placements.indexOf(kSearchBox);
-  CustomizableUI.moveWidgetWithinArea(kHomeBtn, searchboxPlacement);
-  yield waitForCondition(() => navbar.querySelector("#" + kHomeBtn));
-  ok(navbar.querySelector("#" + kHomeBtn), "Home button should have moved back");
-  let inc = 15;
-  window.resizeTo(640, window.outerHeight);
-  while (window.outerWidth < originalWindowWidth &&
-         !navbar.querySelector("#" + kSearchBox)) {
-    window.resizeTo(window.outerWidth + inc, window.outerHeight);
-    yield waitFor(500);
-  }
-  ok(!navbar.querySelector("#" + kStarBtn), "Star button should still be overflowed");
-  CustomizableUI.moveWidgetWithinArea(kStarBtn);
-  let starButtonOverflowed = overflowList.querySelector("#" + kStarBtn);
-  ok(starButtonOverflowed && !starButtonOverflowed.nextSibling, "Star button should be last item");
-  window.resizeTo(window.outerWidth + 15, window.outerHeight);
-  yield waitForCondition(() => navbar.querySelector("#" + kDownloadsBtn) && navbar.hasAttribute("overflowing"));
-  ok(navbar.hasAttribute("overflowing"), "navbar should still be overflowing");
-  CustomizableUI.moveWidgetWithinArea(kHomeBtn);
-  let homeButtonOverflowed = overflowList.querySelector("#" + kHomeBtn);
-  ok(homeButtonOverflowed, "Home button should be in overflow list");
-  ok(navbar.hasAttribute("overflowing"), "navbar should still be overflowing");
-  ok(homeButtonOverflowed && !homeButtonOverflowed.nextSibling, "Home button should be last item");
+  window.resizeTo(400, window.outerHeight);
+  yield waitForCondition(() => navbar.hasAttribute("overflowing"));
+  ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
+  ok(!navbar.querySelector("#" + kHomeBtn), "Home button should no longer be in the navbar");
+  let homeBtnNode = overflowList.querySelector("#" + kHomeBtn);
+  ok(homeBtnNode, "Home button should be overflowing");
+  ok(homeBtnNode && homeBtnNode.classList.contains("overflowedItem"), "Home button should have overflowedItem class");
+
+  let testBtnSpec = {id: kTestBtn3, label: "Overflowable widget test", defaultArea: "nav-bar"};
+  CustomizableUI.createWidget(testBtnSpec);
+  let testNode = overflowList.querySelector("#" + kTestBtn3);
+  ok(testNode, "Test button should be overflowing");
+  ok(testNode && testNode.classList.contains("overflowedItem"), "Test button should have overflowedItem class");
+
+  CustomizableUI.destroyWidget(kTestBtn3);
+  testNode = document.getElementById(kTestBtn3);
+  ok(!testNode, "Test button should be gone");
+
+  CustomizableUI.createWidget(testBtnSpec);
+  testNode = overflowList.querySelector("#" + kTestBtn3);
+  ok(testNode, "Test button should be overflowing");
+  ok(testNode && testNode.classList.contains("overflowedItem"), "Test button should have overflowedItem class");
+
+  CustomizableUI.removeWidgetFromArea(kTestBtn3);
+  testNode = document.getElementById(kTestBtn3);
+  ok(!testNode, "Test button should be gone");
+  CustomizableUI.destroyWidget(kTestBtn3);
   window.resizeTo(originalWindowWidth, window.outerHeight);
-  yield waitForCondition(() => !navbar.hasAttribute("overflowing"));
-  ok(!navbar.hasAttribute("overflowing"), "Should not have an overflowing toolbar.");
 });
 
 add_task(function asyncCleanup() {
+  window.resizeTo(originalWindowWidth, window.outerHeight);
   yield resetCustomization();
 });
--- a/browser/components/places/content/browserPlacesViews.js
+++ b/browser/components/places/content/browserPlacesViews.js
@@ -4,18 +4,19 @@
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 Components.utils.import("resource://gre/modules/Services.jsm");
 
 /**
  * The base view implements everything that's common to the toolbar and
  * menu views.
  */
-function PlacesViewBase(aPlace) {
+function PlacesViewBase(aPlace, aOptions) {
   this.place = aPlace;
+  this.options = aOptions;
   this._controller = new PlacesController(this);
   this._viewElt.controllers.appendController(this._controller);
 }
 
 PlacesViewBase.prototype = {
   // The xul element that holds the entire view.
   _viewElt: null,
   get viewElt() this._viewElt,
@@ -79,16 +80,29 @@ PlacesViewBase.prototype = {
     else {
       this._resultNode = null;
       delete this._domNodes;
     }
 
     return val;
   },
 
+  _options: null,
+  get options() this._options,
+  set options(val) {
+    if (!val)
+      val = {};
+
+    if (!("extraClasses" in val))
+      val.extraClasses = {};
+    this._options = val;
+
+    return val;
+  },
+
   /**
    * Gets the DOM node used for the given places node.
    *
    * @param aPlacesNode
    *        a places result node.
    * @throws if there is no DOM node set for aPlacesNode.
    */
   _getDOMNodeForPlacesNode:
@@ -347,16 +361,25 @@ PlacesViewBase.prototype = {
 
     return element;
   },
 
   _insertNewItemToPopup:
   function PVB__insertNewItemToPopup(aNewChild, aPopup, aBefore) {
     let element = this._createMenuItemForPlacesNode(aNewChild);
     let before = aBefore || aPopup._endMarker;
+
+    if (element.localName == "menuitem" || element.localName == "menu") {
+      let extraClasses = this.options.extraClasses;
+      if (aPopup == this._rootElt && typeof extraClasses.mainLevel == "string") {
+        element.classList.add(extraClasses.mainLevel);
+      } else if (typeof extraClasses.secondaryLevel == "string")
+        element.classList.add(extraClasses.secondaryLevel);
+    }
+
     aPopup.insertBefore(element, before);
     return element;
   },
 
   _setLivemarkSiteURIMenuItem:
   function PVB__setLivemarkSiteURIMenuItem(aPopup) {
     let livemarkInfo = this.controller.getCachedLivemarkInfo(aPopup._placesNode);
     let siteUrl = livemarkInfo && livemarkInfo.siteURI ?
@@ -777,19 +800,26 @@ PlacesViewBase.prototype = {
     if (aPopup._startMarker)
       return;
 
     // _startMarker is an hidden menuseparator that lives before places nodes.
     aPopup._startMarker = document.createElement("menuseparator");
     aPopup._startMarker.hidden = true;
     aPopup.insertBefore(aPopup._startMarker, aPopup.firstChild);
 
-    // _endMarker is an hidden menuseparator that lives after places nodes.
-    aPopup._endMarker = document.createElement("menuseparator");
-    aPopup._endMarker.hidden = true;
+    // _endMarker is a DOM node that lives after places nodes, specified with
+    // the 'insertionPoint' option or will be a hidden menuseparator.
+    let node = ("insertionPoint" in this.options) ?
+               aPopup.querySelector(this.options.insertionPoint) : null;
+    if (node) {
+      aPopup._endMarker = node;
+    } else {
+      aPopup._endMarker = document.createElement("menuseparator");
+      aPopup._endMarker.hidden = true;
+    }
     aPopup.appendChild(aPopup._endMarker);
 
     // Move the markers to the right position.
     let firstNonStaticNodeFound = false;
     for (let i = 0; i < aPopup.childNodes.length; i++) {
       let child = aPopup.childNodes[i];
       // Menus that have static content at the end, but are initially empty,
       // use a special "builder" attribute to figure out where to start
@@ -1665,34 +1695,34 @@ PlacesToolbar.prototype = {
     }
   }
 };
 
 /**
  * View for Places menus.  This object should be created during the first
  * popupshowing that's dispatched on the menu.
  */
-function PlacesMenu(aPopupShowingEvent, aPlace) {
+function PlacesMenu(aPopupShowingEvent, aPlace, aOptions) {
   this._rootElt = aPopupShowingEvent.target; // <menupopup>
   this._viewElt = this._rootElt.parentNode;   // <menu>
   this._viewElt._placesView = this;
   this._addEventListeners(this._rootElt, ["popupshowing", "popuphidden"], true);
   this._addEventListeners(window, ["unload"], false);
 
 #ifdef XP_MACOSX
   // Must walk up to support views in sub-menus, like Bookmarks Toolbar menu.
   for (let elt = this._viewElt.parentNode; elt; elt = elt.parentNode) {
     if (elt.localName == "menubar") {
       this._nativeView = true;
       break;
     }
   }
 #endif
 
-  PlacesViewBase.call(this, aPlace);
+  PlacesViewBase.call(this, aPlace, aOptions);
   this._onPopupShowing(aPopupShowingEvent);
 }
 
 PlacesMenu.prototype = {
   __proto__: PlacesViewBase.prototype,
 
   QueryInterface: function PM_QueryInterface(aIID) {
     if (aIID.equals(Ci.nsIDOMEventListener))
@@ -1745,22 +1775,23 @@ PlacesMenu.prototype = {
     // The autoopened attribute is set for folders which have been
     // automatically opened when dragged over.  Turn off this attribute
     // when the folder closes because it is no longer applicable.
     popup.removeAttribute("autoopened");
     popup.removeAttribute("dragstart");
   }
 };
 
-function PlacesPanelMenuView(aPlace, aViewId, aRootId) {
+function PlacesPanelMenuView(aPlace, aViewId, aRootId, aOptions) {
   this._viewElt = document.getElementById(aViewId);
   this._rootElt = document.getElementById(aRootId);
   this._viewElt._placesView = this;
+  this.options = aOptions;
 
-  PlacesViewBase.call(this, aPlace);
+  PlacesViewBase.call(this, aPlace, aOptions);
 }
 
 PlacesPanelMenuView.prototype = {
   __proto__: PlacesViewBase.prototype,
 
   QueryInterface: function PAMV_QueryInterface(aIID) {
     return PlacesViewBase.prototype.QueryInterface.apply(this, arguments);
   },
@@ -1775,17 +1806,20 @@ PlacesPanelMenuView.prototype = {
 
     let type = aChild.type;
     let button;
     if (type == Ci.nsINavHistoryResultNode.RESULT_TYPE_SEPARATOR) {
       button = document.createElement("toolbarseparator");
     }
     else {
       button = document.createElement("toolbarbutton");
-      button.className = "bookmark-item subviewbutton";
+      let className = "bookmark-item";
+      if (typeof this.options.extraClasses.mainLevel == "string")
+        className += " " + this.options.extraClasses.mainLevel;
+      button.className = className;
       button.setAttribute("label", aChild.title);
       let icon = aChild.icon;
       if (icon)
         button.setAttribute("image", icon);
 
       if (PlacesUtils.containerTypes.indexOf(type) != -1) {
         button.setAttribute("container", "true");
 
--- a/browser/components/preferences/sync.js
+++ b/browser/components/preferences/sync.js
@@ -273,16 +273,35 @@ let gSyncPane = {
   },
 
   openOldSyncSupportPage: function() {
     let url = Services.urlFormatter.formatURLPref('app.support.baseURL') + "old-sync"
     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") +
+                 "\n\n" +
+                 sb.GetStringFromName("unlink.verify.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,
+                                         {});
+      if (pressed != 0) { // 0 is the "continue" button
+        return;
+      }
+    }
     Components.utils.import('resource://gre/modules/FxAccounts.jsm');
     fxAccounts.signOut().then(() => {
       this.updateWeavePrefs();
     });
   },
 
   openQuotaDialog: function () {
     let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
--- a/browser/locales/en-US/chrome/browser/aboutAccounts.dtd
+++ b/browser/locales/en-US/chrome/browser/aboutAccounts.dtd
@@ -1,9 +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 aboutAccounts.pageTitle "&brandShortName; Accounts">
-
 <!ENTITY aboutAccountsSetup.description "Sign in to backup and sync your tabs, bookmarks, and more.">
 <!ENTITY aboutAccountsSetup.startButton.label "Get Started">
 <!ENTITY aboutAccountsSetup.useOldSync.label "Using Sync on an older version of &brandShortName;?">
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -331,22 +331,25 @@ These should match what Safari and other
 <!-- LOCALIZATION NOTE (historyUndoWindowMenu): see bug 394759 -->
 <!ENTITY historyUndoWindowMenu.label "Recently Closed Windows">
 <!ENTITY historyRestoreLastSession.label "Restore Previous Session">
 
 <!ENTITY showAllHistoryCmd2.label "Show All History">
 <!ENTITY showAllHistoryCmd.commandkey "H">
 
 <!ENTITY appMenuCustomize.label "Customize">
+<!ENTITY appMenuCustomize.tooltip "Customize the Menu and Toolbars">
 <!ENTITY appMenuCustomizeExit.label "Exit Customize">
+<!ENTITY appMenuCustomizeExit.tooltip "Finish Customizing">
 <!ENTITY appMenuHistory.label "History">
 <!ENTITY appMenuHistory.showAll.label "Show All History">
 <!ENTITY appMenuHistory.clearRecent.label "Clear Recent History…">
 <!ENTITY appMenuHistory.restoreSession.label "Restore Previous Session">
 <!ENTITY appMenuHistory.viewSidebar.label "View History Sidebar">
+<!ENTITY appMenuHelp.tooltip "Open Help Menu">
 
 <!ENTITY customizeMenu.addToToolbar.label "Add to Toolbar">
 <!ENTITY customizeMenu.addToToolbar.accesskey "A">
 <!ENTITY customizeMenu.addToPanel.label "Add to Menu">
 <!ENTITY customizeMenu.addToPanel.accesskey "M">
 <!ENTITY customizeMenu.moveToToolbar.label "Move to Toolbar">
 <!ENTITY customizeMenu.moveToToolbar.accesskey "o">
 <!-- LOCALIZATION NOTE (customizeMenu.moveToPanel.accesskey) can appear on the
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -491,8 +491,16 @@ mixedContentBlocked.unblock.label = Disa
 mixedContentBlocked.unblock.accesskey = D
 
 # LOCALIZATION NOTE - %S is brandShortName
 slowStartup.message = %S seems slow… to… start.
 slowStartup.helpButton.label = Learn How to Speed It Up
 slowStartup.helpButton.accesskey = L
 slowStartup.disableNotificationButton.label = Don't Tell Me Again
 slowStartup.disableNotificationButton.accesskey = A
+
+
+# LOCALIZATION NOTE(tipSection.tip0): %1$S will be replaced with the text defined
+# in tipSection.tip0.hint, %2$S will be replaced with a hyperlink containing the
+# text defined in tipSection.tip0.learnMore.
+tipSection.tip0 = %1$S: You can customize Firefox to work the way you do. Simply drag any of the above to the menu or toolbar. %2$S about customizing Firefox.
+tipSection.tip0.hint = Hint
+tipSection.tip0.learnMore = Learn more
--- a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
+++ b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
@@ -76,8 +76,14 @@ paste-button.tooltiptext = Paste
 feed-button.label = Subscribe
 feed-button.tooltiptext = Subscribe to this page…
 
 characterencoding-button.label = Character Encoding
 characterencoding-button.tooltiptext = Character encoding
 
 email-link-button.label = Email Link
 email-link-button.tooltiptext = Email Link
+
+# LOCALIZATION NOTE(quit-button.tooltiptext.linux): %S is the keyboard shortcut
+quit-button.tooltiptext.linux = Quit (%S)
+# LOCALIZATION NOTE(quit-button.tooltiptext.mac): %1$S is the brand name (e.g. Firefox),
+# %2$S is the keyboard shortcut
+quit-button.tooltiptext.mac = Quit %1$S (%2$S)
--- a/browser/locales/en-US/chrome/browser/syncSetup.properties
+++ b/browser/locales/en-US/chrome/browser/syncSetup.properties
@@ -44,8 +44,19 @@ newAccount.action.label = Firefox Sync i
 newAccount.change.label = You can choose exactly what to sync by selecting Sync Options below.
 resetClient.change2.label = Firefox Sync will now merge all this device's browser data into your Sync account.
 wipeClient.change2.label = Firefox Sync will now replace all of the browser data on this device with the data in your Sync account.
 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.
+
+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
--- a/browser/themes/osx/customizableui/panelUIOverlay.css
+++ b/browser/themes/osx/customizableui/panelUIOverlay.css
@@ -38,22 +38,21 @@
 
 .panelUI-grid .toolbarbutton-1 {
   margin-right: 0;
   margin-left: 0;
   margin-bottom: 0;
 }
 
 #BMB_bookmarksPopup > menu,
-#BMB_bookmarksPopup > menuitem {
+#BMB_bookmarksPopup > menuitem:not(.panel-subview-footer) {
   padding-top: 5px;
   padding-bottom: 5px;
 }
 
 /* Override OSX-specific toolkit styles for the bookmarks panel */
-#BMB_bookmarksPopup > menuitem > .menu-accel-container,
 #BMB_bookmarksPopup > menu > .menu-right {
   -moz-margin-end: 0;
 }
 
 .widget-overflow-list .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
   -moz-margin-start: 4px;
 }
--- a/browser/themes/shared/customizableui/customizeMode.inc.css
+++ b/browser/themes/shared/customizableui/customizeMode.inc.css
@@ -132,13 +132,17 @@ toolbarpaletteitem[place="toolbar"] {
   opacity: 1; /* To ensure these buttons always look enabled in customize mode */
 }
 
 #wrapper-zoom-controls[place="palette"] > #zoom-controls > #zoom-reset-button,
 #wrapper-zoom-controls[place="palette"] > #zoom-controls > #zoom-reset-button + separator {
   display: none;
 }
 
+#wrapper-personal-bookmarks:not([place="toolbar"]) > #personal-bookmarks {
+  -moz-box-pack: center;
+}
+
 #customization-palette > toolbarpaletteitem > label {
   text-align: center;
   margin-left: 0;
   margin-right: 0;
 }
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css
+++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css
@@ -45,16 +45,25 @@
   margin: -4px -4px 4px;
   box-shadow: 0 -1px 0 hsla(210,4%,10%,.08) inset;
   color: #797c80;
 }
 
 .subviewbutton.panel-subview-footer {
   margin: 4px -4px -4px;
   box-shadow: 0 1px 0 hsla(210,4%,10%,.08) inset;
+  border-radius: 0;
+}
+
+.cui-widget-panelview .panel-subview-header {
+  display: none;
+}
+
+.cui-widget-panelview .subviewbutton.panel-subview-footer {
+  margin: 4px 0 0;
 }
 
 #PanelUI-mainView {
   display: flex;
   flex-direction: column;
 }
 
 #app-extension-point-end > #PanelUI-menu-button {
@@ -71,18 +80,22 @@
   display: none;
 }
 #PanelUI-popup > arrowscrollbox > scrollbox {
   overflow: visible;
 }
 
 #PanelUI-popup > .panel-arrowcontainer > .panel-arrowcontent {
   overflow: hidden;
+  box-shadow: none;
+}
+
+#PanelUI-popup > .panel-arrowcontainer > .panel-arrowcontent,
+.cui-widget-panel > .panel-arrowcontainer > .panel-arrowcontent > .popup-internal-box {
   padding: 0;
-  box-shadow: none;
 }
 
 .panelUI-grid .toolbarbutton-menubutton-button > .toolbarbutton-multiline-text,
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-multiline-text {
   margin: 2px 0 0;
   text-align: center;
   -moz-hyphens: auto;
 }
@@ -93,22 +106,30 @@
 }
 
 #PanelUI-contents,
 .panel-mainview:not([panelid="PanelUI-popup"]) {
   max-width: @menuPanelWidth@;
 }
 
 panelview:not([mainview]) .toolbarbutton-text,
-#customizationui-widget-panel toolbarbutton > .toolbarbutton-text {
+.cui-widget-panel toolbarbutton > .toolbarbutton-text {
   text-align: start;
   -moz-padding-start: 8px;
   display: -moz-box;
 }
 
+.cui-widget-panel > .panel-arrowcontainer > .panel-arrowcontent {
+  padding: 4px 0;
+}
+
+.cui-widget-panel.cui-widget-panelWithFooter > .panel-arrowcontainer > .panel-arrowcontent {
+  padding-bottom: 0;
+}
+
 #PanelUI-contents {
   display: block;
   flex: auto;
   margin-left: auto;
   margin-right: auto;
   padding: .5em 0;
   max-width: @menuPanelWidth@;
 }
@@ -186,16 +207,18 @@ toolbaritem[cui-areatype="menu-panel"][s
 toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) > .toolbarbutton-text {
   text-align: center;
 }
 
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-icon,
 .customization-palette .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 .customization-palette .toolbarbutton-1 > .toolbarbutton-icon,
+.panelUI-grid #bookmarks-toolbar-placeholder > .toolbarbutton-icon,
+.customization-palette #bookmarks-toolbar-placeholder > .toolbarbutton-icon,
 .panel-customization-placeholder-child > .toolbarbutton-icon {
   min-width: 32px;
   min-height: 32px;
   margin: 4px;
 }
 
 toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
   -moz-box-flex: 1;
@@ -361,55 +384,57 @@ toolbarpaletteitem[place="palette"] > to
 }
 
 #customization-palette .toolbarbutton-multiline-text,
 #customization-palette .toolbarbutton-text {
   display: none;
 }
 
 panelview .toolbarbutton-1,
-panelview .subviewbutton,
+.subviewbutton,
 .widget-overflow-list .toolbarbutton-1,
 .customizationmode-button,
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button,
 #edit-controls@inAnyPanel@ > toolbarbutton,
-#zoom-controls@inAnyPanel@ > toolbarbutton,
-#BMB_bookmarksPopup > menu,
-#BMB_bookmarksPopup > menuitem {
+#zoom-controls@inAnyPanel@ > toolbarbutton {
   -moz-appearance: none;
   padding: 2px 6px;
   background-color: hsla(210,4%,10%,0);
   border-radius: 2px;
   border: 1px solid;
   border-color: hsla(210,4%,10%,0);
   transition-property: background-color, border-color;
   transition-duration: 150ms;
 }
 
-.PanelUI-subView .subviewbutton.panel-subview-footer {
+.subviewbutton.panel-subview-footer {
   border-radius: 0;
   border: none;
 }
 
-.PanelUI-subView .subviewbutton.panel-subview-footer > .toolbarbutton-text {
+.subviewbutton.panel-subview-footer > .toolbarbutton-text,
+.subviewbutton.panel-subview-footer > .menu-text {
   -moz-padding-start: 0;
   text-align: center;
 }
 
-.PanelUI-subView .subviewbutton:not(.panel-subview-footer) {
+.subviewbutton:not(.panel-subview-footer) {
   margin: 2px 0;
 }
 
-.PanelUI-subView .subviewbutton:not(.panel-subview-footer) > .toolbarbutton-text {
+.subviewbutton:not(.panel-subview-footer) > .toolbarbutton-text,
+/* Bookmark items need a more specific selector. */
+.PanelUI-subView .subviewbutton:not(.panel-subview-footer) > .menu-text,
+.PanelUI-subView .subviewbutton:not(.panel-subview-footer) > .menu-iconic-text {
   font-size: 1.1em;
 }
 
-.PanelUI-subView .subviewbutton.bookmark-item {
-  font-weight: normal;
-  color: inherit;
+.cui-widget-panelview .subviewbutton:not(.panel-subview-footer) {
+  margin-left: 4px;
+  margin-right: 4px;
 }
 
 .PanelUI-subView menuseparator,
 .PanelUI-subView toolbarseparator {
   -moz-margin-start: -5px;
   -moz-margin-end: -4px;
 }
 
@@ -418,53 +443,47 @@ panelview .toolbarbutton-1,
   margin-top: 6px;
 }
 
 .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button {
   border: 0;
 }
 
 panelview .toolbarbutton-1@buttonStateHover@,
-panelview .subviewbutton@buttonStateHover@,
+.subviewbutton@buttonStateHover@,
 .widget-overflow-list .toolbarbutton-1@buttonStateHover@,
 .customizationmode-button,
 #edit-controls@inAnyPanel@ > toolbarbutton@buttonStateHover@,
-#zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateHover@,
-#BMB_bookmarksPopup > menu@buttonStateHover@,
-#BMB_bookmarksPopup > menuitem@buttonStateHover@ {
+#zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateHover@ {
   background-color: hsla(210,4%,10%,.08);
   border-color: hsla(210,4%,10%,.1);
 }
 
-
 #edit-controls@inAnyPanel@@buttonStateHover@,
 #zoom-controls@inAnyPanel@@buttonStateHover@ {
   border-color: hsla(210,4%,10%,.1);
 }
 
 panelview .toolbarbutton-1@buttonStateActive@,
-panelview .subviewbutton@buttonStateActive@,
+.subviewbutton@buttonStateActive@,
 .customizationmode-button@buttonStateActive@,
 .widget-overflow-list .toolbarbutton-1@buttonStateActive@,
 #edit-controls@inAnyPanel@ > toolbarbutton@buttonStateActive@,
-#zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateActive@,
-#BMB_bookmarksPopup > menu@buttonStateActive@,
-#BMB_bookmarksPopup > menuitem@buttonStateActive@ {
+#zoom-controls@inAnyPanel@ > toolbarbutton@buttonStateActive@ {
   background-color: hsla(210,4%,10%,.15);
   border-color: hsla(210,4%,10%,.15);
   box-shadow: 0 1px 0 0 hsla(210,4%,10%,.05) inset;
 }
 
-#BMB_bookmarksPopup > menu,
-#BMB_bookmarksPopup > menuitem {
+#BMB_bookmarksPopup > .subviewbutton {
   font: inherit;
+  font-weight: normal;
 }
 
-#BMB_bookmarksPopup > menu:not([disabled="true"]),
-#BMB_bookmarksPopup > menuitem:not([disabled="true"]) {
+#BMB_bookmarksPopup > .subviewbutton:not([disabled="true"]) {
   color: inherit;
 }
 
 #BMB_bookmarksPopup > .panel-arrowcontainer > .panel-arrowcontent > .popup-internal-box > .autorepeatbutton-up,
 #BMB_bookmarksPopup > .panel-arrowcontainer > .panel-arrowcontent > .popup-internal-box > .autorepeatbutton-down {
   -moz-appearance: none;
   margin-top: 0;
   margin-bottom: 0;
@@ -473,16 +492,23 @@ panelview .subviewbutton@buttonStateActi
 panelview toolbarseparator,
 #BMB_bookmarksPopup > menuseparator {
   -moz-appearance: none;
   min-height: 0;
   border-top: 1px solid ThreeDShadow;
   margin: 5px 0;
 }
 
+.subviewbutton > .menu-accel-container {
+  -moz-box-pack: start;
+  -moz-margin-start: 10px;
+  -moz-margin-end: auto;
+  color: hsl(0,0%,50%);
+}
+
 #PanelUI-historyItems > toolbarbutton {
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 #PanelUI-recentlyClosedWindows > toolbarbutton > .toolbarbutton-icon,
 #PanelUI-recentlyClosedTabs > toolbarbutton > .toolbarbutton-icon,
 #PanelUI-historyItems > toolbarbutton > .toolbarbutton-icon {
   width: 16px;
@@ -503,18 +529,18 @@ toolbarpaletteitem[place="palette"] > #b
   display: none;
 }
 
 #search-container[cui-areatype="menu-panel"] {
   width: @menuPanelWidth@;
 }
 
 toolbarpaletteitem[place="palette"] > #search-container {
-  min-width: calc(@menuPanelWidth@ / 3);
-  width: calc(@menuPanelWidth@ / 3);
+  min-width: 7em;
+  width: 7em;
 }
 
 #edit-controls@inAnyPanel@,
 #zoom-controls@inAnyPanel@ {
   background-color: hsla(210,4%,10%,0);
   border-radius: 2px;
   border: 1px solid;
   border-color: hsla(210,4%,10%,0);
@@ -583,20 +609,24 @@ toolbarpaletteitem[place="palette"] > #s
   background-position: 0 0, 1px 0, 2px 0;
   background-repeat: no-repeat;
 }
 
 #widget-overflow > .panel-arrowcontainer > .panel-arrowcontent {
   padding: 0;
 }
 
+.cui-widget-panelview,
+#widget-overflow-scroller {
+  overflow-y: auto;
+  overflow-x: hidden;
+}
+
 #widget-overflow-scroller {
   max-height: 30em;
-  overflow-y: auto;
-  overflow-x: hidden;
   margin-top: 10px;
   margin-bottom: 10px;
 }
 
 #widget-overflow-list {
   width: @menuPanelWidth@;
   padding-left: 10px;
   padding-right: 10px;
@@ -647,17 +677,17 @@ toolbarpaletteitem[place="palette"] > #s
 .PanelUI-characterEncodingView-list > toolbarbutton[current] {
   -moz-padding-start: 4px;
 }
 
 #PanelUI-developerItems > toolbarbutton[checked="true"] > .toolbarbutton-text,
 #PanelUI-bookmarks > toolbarbutton[checked="true"] > .toolbarbutton-text,
 #PanelUI-history > toolbarbutton[checked="true"] > .toolbarbutton-text,
 .PanelUI-characterEncodingView-list > toolbarbutton[current] > .toolbarbutton-text,
-#customizationui-widget-panel .PanelUI-characterEncodingView-list > toolbarbutton[current] > .toolbarbutton-text {
+.cui-widget-panel .PanelUI-characterEncodingView-list > toolbarbutton[current] > .toolbarbutton-text {
   -moz-padding-start: 0px;
 }
 
 #BMB_bookmarksPopup > menuitem[checked="true"]::before,
 #PanelUI-bookmarks > toolbarbutton[checked="true"]::before,
 #PanelUI-history > toolbarbutton[checked="true"]::before,
 #PanelUI-developerItems > toolbarbutton[checked="true"]::before,
 .PanelUI-characterEncodingView-list > toolbarbutton[current]::before {
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -720,18 +720,21 @@ menuitem.bookmark-item {
 
 #home-button.bookmark-item {
   list-style-image: url("chrome://browser/skin/Toolbar.png");
 }
 #home-button.bookmark-item:-moz-lwtheme-brighttext {
   list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
 }
 
-#sync-button[status="active"] {
-  list-style-image: url("chrome://browser/skin/sync-throbber.png");
+#sync-button[cui-areatype="toolbar"][status="active"] {
+  /* !important because we need to override the glass selectors that trigger
+   * use of the Toolbar-inverted image. Those use a list of all primary toolbar
+   * buttons, so we can't easily fix those selectors. */
+  list-style-image: url("chrome://browser/skin/sync-throbber.png") !important;
   -moz-image-region: rect(0, 18px, 18px, 0);
 }
 
 /* tabview button & menu item */
 
 #menu_tabview {
   list-style-image: url(chrome://browser/skin/tabview/tabview.png);
   -moz-image-region: rect(1px, 89px, 17px, 73px);
--- a/browser/themes/windows/customizableui/panelUIOverlay.css
+++ b/browser/themes/windows/customizableui/panelUIOverlay.css
@@ -9,26 +9,16 @@
   padding-right: 12px;
 }
 
 #PanelUI-contents #zoom-in-btn {
   padding-left: 12px;
   padding-right: 12px;
 }
 
-#BMB_bookmarksPopup > menu,
-#BMB_bookmarksPopup > menuitem {
-  padding-top: 1px;
-  padding-bottom: 1px;
-}
-
-#BMB_bookmarksPopup > menu > .menu-text,
-#BMB_bookmarksPopup > menuitem > .menu-text,
-#BMB_bookmarksPopup > menu > .menu-iconic-text,
-#BMB_bookmarksPopup > menuitem > .menu-iconic-text,
 #BMB_bookmarksPopup > menuseparator {
   padding-top: 0;
   padding-bottom: 0;
 }
 
 .widget-overflow-list .toolbarbutton-1 > .toolbarbutton-menubutton-button {
   -moz-appearance: none;
   border: 0;
--- a/mobile/android/base/NotificationHelper.java
+++ b/mobile/android/base/NotificationHelper.java
@@ -18,16 +18,17 @@ import android.content.BroadcastReceiver
 import android.content.IntentFilter;
 import android.content.Context;
 import android.content.Intent;
 import android.graphics.Bitmap;
 import android.net.Uri;
 import android.support.v4.app.NotificationCompat;
 import android.util.Log;
 
+import java.util.Iterator;
 import java.util.Set;
 import java.util.HashSet;
 
 public final class NotificationHelper implements GeckoEventListener {
     public static final String NOTIFICATION_ID = "NotificationHelper_ID";
     private static final String LOGTAG = "GeckoNotificationManager";
     private static final String HELPER_NOTIFICATION = "helperNotif";
     private static final String HELPER_BROADCAST_ACTION = AppConstants.ANDROID_PACKAGE_NAME + ".helperBroadcastAction";
@@ -315,25 +316,31 @@ public final class NotificationHelper im
             args.put(ID_ATTR, id);
             args.put(EVENT_TYPE_ATTR, CLOSED_EVENT);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Notification:Event", args.toString()));
         } catch (JSONException ex) {
             Log.w(LOGTAG, "sendNotificationWasClosed: error building JSON notification arguments.", ex);
         }
     }
 
-    public void hideNotification(String id) {
+    private void closeNotification(String id) {
         GeckoAppShell.sNotificationClient.remove(id.hashCode());
-        mClearableNotifications.remove(id);
         sendNotificationWasClosed(id);
     }
 
+    public void hideNotification(String id) {
+        mClearableNotifications.remove(id);
+        closeNotification(id);
+    }
+
     private void clearAll() {
-        for (String id : mClearableNotifications) {
-            hideNotification(id);
+        for (Iterator<String> i = mClearableNotifications.iterator(); i.hasNext();) {
+            final String id = i.next();
+            i.remove();
+            closeNotification(id);
         }
     }
 
     public static void destroy() {
         if (mInstance != null) {
             mInstance.clearAll();
         }
     }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -250,17 +250,19 @@ size. -->
 <!ENTITY contextmenu_top_sites_pin "Pin Site">
 <!ENTITY contextmenu_top_sites_unpin "Unpin Site">
 <!ENTITY contextmenu_add_search_engine "Add a Search Engine">
 
 <!ENTITY pref_titlebar_mode "Title bar">
 <!ENTITY pref_titlebar_mode_title "Show page title">
 <!ENTITY pref_titlebar_mode_url "Show page address">
 
-<!ENTITY pref_dynamic_toolbar "Hide title bar when scrolling">
+<!-- Localization note (pref_scroll_title_bar): Label for setting that controls
+     whether or not the dynamic toolbar is enabled. -->
+<!ENTITY pref_scroll_title_bar "Scroll title bar">
 
 <!ENTITY history_removed "Page removed">
 
 <!ENTITY bookmark_edit_title "Edit Bookmark">
 <!ENTITY bookmark_edit_name "Name">
 <!ENTITY bookmark_edit_location "Location">
 <!ENTITY bookmark_edit_keyword "Keyword">
 
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..a30095135d4d64de1bb2bbf867472bc484ec8c58
GIT binary patch
literal 5468
zc$}4(c{CLM*T+9IX2Zy?!C=OWv4$*JW2{+<LYC~iqAy>&C^M+Y60IVJQe-J5`!<%o
zzGMxB$jDm4WGU--`knLq^PKaX^PK0N*L~lA?m73L_dWNVn{w9b6c>jG2LOP}+|1bK
zPo?}PQEY#7laaLp02?@a+Rg<0kAQ;zcfh#*j|K<?@<*T$I5!Fj6W~F?d2y1$Xt=P3
zg|Li}sJ@h_CPv&;5zj4*Kf_5d5hb42lj0YZu@RB65tDHhk+sFhJ7E><#r|;253A@P
zrsU+HBqXYAFRJV)r0fM$E^w;_f)nS!2`3>9FAj}>3JqegrpQ}O$+w#L8f}7rwttom
zo=-QTPM5%aGN#Uez-JIzXGq{Oyz$CVKG#U9-iW|$oK$B*;4z8iGmWb@ljb!~gqUC9
zH@{wMNvyXdG+Pq+PA7Anxl?~es=<oTY)#^~eq4K&h(7nI@*J_&mcVWM5N(%-wl8RR
zARcwded{1u;e;=D!M}FJA8{+Kch~OnI4bB_Cg@2!>RoZvxAK@zc(*UH%~zu@gfRKH
z{0E98E$R43GQKI5G?I?{l!hG1k{Hdz4Q1gz+`&y|^Ka(iH}dd|QrPAj;>thlo1YOo
zKfvw^IAlRsgK~%ca;*2VyL9QjM(NEm!sZLYW-e~?5pMIL#O7W6#y$McEW%nkVI`HY
zmMZ-tg}9O|{rx6!`KHwJb(!x8vP;)w=Az{0BITw+<R^pW1cPcYL3IKbs}?Vk<^!bo
z1Kwc#Ur+f-FZxN%`$+YBD)4%g&bbk1FA%>v5oa9#8<hRiaa$>bZNXg|>oFVR*g4|I
zvr>N;wI)KW3lLVhgQum~Egue85c@5Nz2?LoGa`@K?f0jMd?u;yjER2`G){hGq|;$U
z`~$`yu~nbYs!wdzBVhF68%`4Hb^k+`j+BT_Y=<^cR4W>*5h11?)~O~XrFuzBHKbjI
zB&HIKRleM+L~1@ps#lO|lqJ2CCe)EgRZ_$fDLjoJahHIvl9akHE|!JGri)^4iim~@
zOT6XdTKr?u`{6cLwxU=Ok^jxUY)B+KCl@!LkQhcwg1|3%iM4;QN8IPx7Y|D~xI%ID
zWu37TH8_>Xb_5F5dA!Ykz+!0}i8(+Qt3KdFplC5^$`Wt|I7vC7lo%mLgdsJ;3`6N*
z!DZt9mUhl87TX$imz7v?P|hmOd-1dcT3q)0IUkD!JwU<#4F7m4F$@6SNONNYyXc9P
z+&$}iQ7v6kEVcoq&s55ZN4C|+&8l;PG(WF4<d&;+7F?W4uaMe$>{mTrS$@tj_;_2;
zaCE=dzunI(ZcQ3o_biRfOhxl^KAF6+93p(POn~QHmUCg^k;RQ2xBSJus|(Pn04uAn
zx#t#Q7Gm_)KK<U<iEAB*vT3x~(f?Tb3A}=^Xb{X-Zut3qd4%H^9BWV>mV9c@=w>2n
zYPPmj4piztj+VsZXQDydDQP}#gGX*$An8D+aD5?a`u-P2gH`8k3rp*%CZ|Wtls8xR
z<e_G#Tb!I??v%XU)ESImIee47((vZZ`od4^d)p3>5Hqbf>{o|;?TAu=@B{6?Tl!_0
zlP_$UF`>TgGc=>hOY?BetjDLpASYjlD-RMR%+1a5TQto)W5e38HtGOw+*<-AYsPYt
zb&Nr<?xcaY&`-hSR%uc`u3e)m7zMghdI}$M@w^JM_FyMdM3v3VZ}R_z0@4D$dzJ_u
zIRG>)@ZERrb$<A8SB~6%Q~p)kEeK!P-BIznzxS2PdlEcuJ#H$5*u}qD7b!>6EA!q`
zJ|?APQddetZZVz6fX_V=@%8?8(%hv&gx*PtYss#(w2VB-QI>vcbo3IJ8LySu6Bf3h
zVfXGsPSD0zngt%V-z`FDgi8u)ngeFf{ei@JNX_hq`s*A&3W@47qjRQx>F!0uD)DRB
zI8zCNnPWE<Ut`20-O|%XyW2O&0@5Xa1$D}o<??kS3?Fz(foqDp%ql;uowslB3de|Z
z3Tm{ECbsKfS-Dr+_(%_Sb$`g8eYEdPU;1cud|7MMtHQ!U_}nIM{&nuPsj@@o%MsqH
zGBZ`T^=<%NOp+@*xoLfQS|2MaY<_l>+Lj}#b7^lEapQ*gSuXepo$T&@8Hrz78hnzL
zb~0{z@ApA(`guM0*lvhVkWb~&k>TNSYQotwXR1lI3cO(wnI`=wqm|PfvQJm{oe}Gs
z`_iAEFOE{zL>Y9|DsnS9eXq{V-kkN$Ol&hCE)}8E`5F4Ur~lETBx08yjv|XuNW<*Y
z{OKd&`)zR{K0as_<$CpzsL=1=T5(f4;Fbk=I{C~=MW+AiNF(=|sjH{aiTKWphATPA
z`fv;cptOZ|j$mMLWz4<Wr=+efiSH;8>5xsIA0ypU6%U;~qLtOsWS8vtDYFt1w6wU`
zdi1{GxL69vgi;Ht>O0geOs)H-R#&eb%!NgCT-5N#h(tuFEgy6}CDiUJW~$JG@Bbzc
zq`M@%In)yJpR+qiRGkv79UdJnv3>P^yh3G@Ypb4=b7ipgp*md?mY$IoH(iWq;FDRi
z0T#+;?7$<yBl&aGbkwikcdAVX^Zxm=XJx~FLm7YseW-567tD?#Q42+n^!@$u-}Ir|
zw2V|(b6zEaIT;ld6`>WlHhJ*@Bqw)uq$WHfVn_k7mBaIz?;P5~m=$eu_0U6cL1E#8
z@b%ZmgG0;ThkrCqIA6-m$<58lSygUx)_i&SVsKRG4c1?u%!+kc-NgW?M@t7FzNAmo
z@q+wYu*5!o?#Y7y?wP!mm6c<IP_b~TPeROxWhme=TDqn@q${(lZ$e2w*E*unCfDsg
z-XIJ}(r_qwsa&cW86O@H7<idlZC{)*@!;dfniClz(<?7m?j1-=*Fq@PT=v~z9$^A-
z9`-AyE>>P^MZYR4G7t_cNktbN5<BoXa&dzxqRaR&0LrtK@kR$nU%TgS3Zd5o$RfS-
z_y>2ww*#iXhG>ecQr#0tR}ye<C83)M!zYKdNE!OtB0%!#mr?aLIajzOJB7>)VHUoR
zp`mG?bp{Xeix8)-vY7-FqCye_y+W{`Bv!4#iRHjbi*mY7t`?9YC$$}S^jc!ppDYo#
zb8zpnvo^MYd_XZE$s233ul<F`%FC$tsP30DzJfHyPexr?E;sG-)6dPZQ3mRB5x*5l
zRFz7@w+4cbvlF{=;?>l)|0(Hf<$!^doxT$R5O9|FpM0yj$0IaB+St?BbStyAD)M_W
zs<Lo=Tb8^TJSP$W8t>nhCRM_6YcD^A4NkqhCT*=>`+HGQHIta>7XGUdhuWI1xvct&
z-r3n@!=XmW>*=St==0WRdreig*v9L{RTlp>ZMkklzr)CwA97JRfq{du>J9-mebTV<
z_$Fgww&m4s>aXcNes5d^4i;`gFD8yF8MyE#O}~~$k<qwL#sW8u<uh%tth8s_`We^Q
zXMeplI1rT$PXRP^oq1cc*J-pP3|YYH;2xB)p>Wan3wLu)=1jL-Zk+nE)x1(Om4TE?
zR5EBatt3QBf%iL!Iz9HP%Uk`Q#(w;gONYFoS{1-9*U3bd!OpruD?cgG-F!dgkBN;z
zUw8QI&62pxcHOreR?13CgVZ@K-PS%BrdM$I9!0Hkz+BTjl7k*bq1VRiv6M^@0GjnD
z_>G)L_djqBe%QZkde)?hVMLB9XEQ?as5VK5AXoroQ;-Ufx4?&$!a6@P?RR0lh1nk1
z?ei3NK8>OV&C3`=SRIE0=1mHS{f&OlJ}5%o^6z+6Di?f}$}(d<smAgI8H!bMt3e}3
zIQmk)(tV`^DBTHBAXrdQaenL2ZKHNPNbo$TWv2w{!NO(15DQXfYQb{=?$Ln+pwM8b
zS^x3c@t!G5w<8SK26yp$AeG*L^V?)IiRn<HUps;e2g~%>C#~t*pO2a$ZH^rqdoM3X
zgjRFS33TQlpwK2NsC2Pl=iw~ab+EI$_nVzXR)zsA1Qjis>`I1s@p{A;pnr(k%Tw4r
z;3ci{oBCU|6GBY`=ak$s-7Ps!lqo^v6SATIuIbHlCAxzS(H`p97{(P@!p?K>zQVOs
zpRn}i!8YE*wcr`<ujp;cNaELwB}-VcFEB*@3nz;PzT5xsbz?F0%e$IiG&e@$vmK5=
z^LkU<bI?FH|CB(b*5SdJC}3oV($%o<<r$kafQ_%OhUm^L#x#ykW&|5VeB=|OeuQ7(
z`g+6b5X<B7YJD-gP;**ngpbT|*3GLrrzsWpsvD|uY-UgD?s+L5+k!F!m-xwI1E4o`
z90vjdDUT_9f#dCA%(#}bel?E0`*goTm<?}{2;GL~Q|qklw~GqUA9i{UkTL9_G-cQM
z!cgeX`tCGgW$vM;_NnJ_Jdh+21vcil43=gM#|yzadA55GJ>c|E&%X<QlnRGeqrXUI
z2o?!aUz!?nT|-a?p+UTZ+Th^*!qKBbLPFja7CmGsDN?AWyPxcgEVBYKE>w;tU#8j@
z!r#IiD;{X8xj1I0KgvV)<@j~ac%@%|hQm-|yJ!h&Gc*{W37svb%<I7iFaFend%!+i
zfVqzK0~P^8e|d6M^JAiwJ`j8hwI6P8k~l^$dix+iHWM;dL+r2k%+n2Gg@J;R)oWz%
zw2-_9ZHrsk*-r@a!|-TLsVfHS(Tbp376eoIEXUP;((Ye}4Ht3ElRV1)&P7yeFseLW
zg`R~6_zN>v3_fqR2LU{!mrabK#*Lh8;L6%pwqeQm(fx(KdJn$bu`c`SuRH8JVSKNc
z%E3Gpqf@}P4IrJ;R7Ez%+R!6QfusUMSX~(;vzY2!i|2zd_FGu^i}+47Y8m%?F@GT0
z7&f3b{s`7fZjE(kpy@ko2S1#O`k{Y|Jg|))-Rr)4sze(4u6$9LCwVyY7iCxNyMDnB
z4H$In6*lDeR)@yA;_lU=mLsimnH8E-$$A^Ty}hTc&rsE`+-8y0ZwXlE>*nSp+-8Tw
z;tcq3WY@gJleZgI8ykPaAr!Qx2YaOE_wUIO=~Hfg9rG<{nyS5Js=pO;@s~^rkVrKa
z^ksU>SA7mdA!b5;a84xceV-U?ez_o2nwIjeN^=1UN#g)v8D3U&w#)#5LO)@Oh<02n
zOA?@WZsLGYszicwEpD@-72%3xU9l8w+;Wzij1fR0whf@TJps-}Thls0G<e`8%$TSS
zEY<nzs(lXiiB}}DUi3*hhB5X{Wt5fU<Bpaqb#~$&5u6!)XDEVV<BZd0RxvOj&hk}5
zWcp5V$ZxNzN6=mvEczb^hv(DhDn)034}EYtUk@`E3xRm7GH8+$xqa6o#1YKX7ZBTq
z01Pv%AsDDiQBmA`qX)bc?>4ag?2}D$^LIBpJ0c`mHdE+{mbEM1#ihvGZ04!<!EGEk
z)a+9{ZH{M6wj|56-T=Kz4u-1(kW1^|woIUg97blZVGzxdxN8%4+9z#GbY5J|5~uL%
zikO_V;a5J!hC;oA;o-n0!4LP$F;pXWbmq@;gn~N>FJ4HC0P!}7(*QzA<vb5n=OTt4
z2Us*JBncvLG_~5Fg<=w<vtQI-e{N@Ms~(|hJCNMUfs+(aML<J{W(XO|wM{jb8DGF0
zOwm+zpwVnk^u#idvDd5)u$BPo(h}!1K<Dj!53+65qJ~)O^ZWbQBjo+C&;XC7BS+Sk
z7qeT7Nz(fI?uN>NC@KUU!V0*hMQKI=J@Hd|(Q`$`#l;DXVBMam=s_CW(i@Kj^nzC_
zK#Um7mpF;tcM5(5?Jd;riR*&~uOG8l)q*8x9b-$Pa~o#ul!RuEr2vGHOhECSPDs&2
zD$q}WYz(aZ(qua1c9~nxX)?UIC_LMnhFMzX<LrKN5I9BE(^G_UDc&hU8hkOi{hfAU
zv(wi4GA-Lyi`rGSRN)QoQH+!+hA<ST5sV$Dpr9L<ddJQ&8XkVXlqpx=Tu^}1cnGpr
zwtWVk`Mp?>wLT-%^kPGI7SwOoi^<)grOFjFlyZ0-6(Y$xw`Y-&JmRnY67PiXa3Cq}
zKUhsJa?1bqbocc1c!CGQ)*9HwhLgiW2#Ulo=hCs57zI<R$Y#$x(bovZ_b*@^e%!58
zTIYNjBKR*$>xw7BuYA$Q(oc6-mn{KPZ9!){I(Ln?!E+@iM2zgXEi&sIY!a7ci<48j
zecO#WaKphqQV8g7MDKF;-qtOGX|5~i!?JJa9uMe$#Ee+}WSX?B!t*-vKzlaVKXl!0
zo+BZoGaEH&FfKg)*TCb&A^w51uDrVj{cYn4O@^oFGTEX1<?1h0U$S%G|41D~5kR@#
zUAgJ!8G6pPLJp>n{8vx82<!erZ}~ikEaZl9M*3Cr+AuhQBi=t+WATH&!ng5j`>It+
zkBaedW{!$`PygmkdYAz@nm(e62Oc93jO0gAjU>!rzM{bK&osOEXxBHfo?l{{)E}yJ
zv%AOZ%V>Rz*SJxxD$>EFLf*bI+mIQrlY4E|B@*Y$-qYLD+uPq8$wsY}_~PUD^@YOW
z#msQUgYx4(i9;9?n~Y~RHNsyh-^iDKU6?vKl>5!*OUWmlEAw}*7#1<T^Qmpa11~$!
zN#F8s6q<sukP|D|PtVfQ&_XA4R5KOlt83<;A-0uuMBR;f<P(uC(L!lyfkp?AAnjQE
zNmrvfA{izL;H?SS9p^8Ny{*ax9_a>1k7Ne;rovX}h#+KZvA4~7GC5&57w&>2QH|?C
z=8Yjgy&8Jy$1y3Ucd7cq;Yo93o$er$z=iy4KfhiNTZ53Z2Qp;PAfw3D%(5~1IHuF$
zzFdrIRHZdrDgmFHR;ULG12wK=&*;!@e83nd+dcPv{AfS>=~WR&EZ2BZHHE_?dF<_O
zz=TFN+sUNIoLMGHQ@-C1Cb3&*CWc0H+Yf>)I%=9?)K-=^76<-Z1eGL-3<_T`U!RhT
zX1qA~GRe1q#BDZLn=iI%TNfSQZe!k_U4#zhjNKh`&$GT2tu!)S6X);v!J)K;btCx$
zj91LT!xM;n4^)q|SyF6uKOK2pd}nQb%iY*O&snc?v9N3Zu-JL*p=r6(9w}f;p4Zf^
zktFFdh5pu>BU}_ev;RhL=@7?rXfUgPh};}-PzZ@Wh@WafM04T3#Y5r_uB+V@3LE{;
PC&=8y%J`+BN5X#rV;pI{
--- a/mobile/android/base/resources/layout/toolbar_edit_layout.xml
+++ b/mobile/android/base/resources/layout/toolbar_edit_layout.xml
@@ -1,36 +1,36 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- 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/. -->
 
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:gecko="http://schemas.android.com/apk/res-auto">
 
-        <org.mozilla.gecko.toolbar.ToolbarEditText
-              android:id="@+id/url_edit_text"
-              style="@style/UrlBar.Button"
-              android:layout_width="fill_parent"
-              android:layout_height="fill_parent"
-              android:layout_weight="1.0"
-              android:hint="@string/url_bar_default_text"
-              android:textColor="@color/url_bar_title"
-              android:textColorHint="@color/url_bar_title_hint"
-              android:textColorHighlight="@color/url_bar_text_highlight"
-              android:textSelectHandle="@drawable/handle_middle"
-              android:textSelectHandleLeft="@drawable/handle_start"
-              android:textSelectHandleRight="@drawable/handle_end"
-              android:textCursorDrawable="@null"
-              android:inputType="textUri|textNoSuggestions"
-              android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen"
-              android:selectAllOnFocus="true"
-              android:singleLine="true"
-              android:gravity="center_vertical|left"
-              gecko:autoUpdateTheme="false"/>
+    <org.mozilla.gecko.toolbar.ToolbarEditText
+          android:id="@+id/url_edit_text"
+          style="@style/UrlBar.Button"
+          android:layout_width="fill_parent"
+          android:layout_height="fill_parent"
+          android:layout_weight="1.0"
+          android:hint="@string/url_bar_default_text"
+          android:textColor="@color/url_bar_title"
+          android:textColorHint="@color/url_bar_title_hint"
+          android:textColorHighlight="@color/url_bar_text_highlight"
+          android:textSelectHandle="@drawable/handle_middle"
+          android:textSelectHandleLeft="@drawable/handle_start"
+          android:textSelectHandleRight="@drawable/handle_end"
+          android:textCursorDrawable="@null"
+          android:inputType="textUri|textNoSuggestions"
+          android:imeOptions="actionGo|flagNoExtractUi|flagNoFullscreen"
+          android:selectAllOnFocus="true"
+          android:singleLine="true"
+          android:gravity="center_vertical|left"
+          gecko:autoUpdateTheme="false"/>
 
-        <ImageButton android:id="@+id/go"
-                     style="@style/UrlBar.ImageButton.Icon"
-                     android:src="@drawable/ic_url_bar_go"
-                     android:contentDescription="@string/go"
-                     android:visibility="gone"/>
+    <ImageButton android:id="@+id/go"
+                 style="@style/UrlBar.ImageButton.Icon"
+                 android:src="@drawable/ic_url_bar_go"
+                 android:contentDescription="@string/go"
+                 android:visibility="gone"/>
 
 </merge>
--- a/mobile/android/base/resources/xml/preferences_display.xml
+++ b/mobile/android/base/resources/xml/preferences_display.xml
@@ -17,17 +17,17 @@
 
     <ListPreference android:key="browser.chrome.titlebarMode"
                     android:title="@string/pref_titlebar_mode"
                     android:entries="@array/pref_titlebar_mode_entries"
                     android:entryValues="@array/pref_titlebar_mode_values"
                     android:persistent="false" />
 
     <CheckBoxPreference android:key="browser.chrome.dynamictoolbar"
-                        android:title="@string/pref_dynamic_toolbar" />
+                        android:title="@string/pref_scroll_title_bar" />
 
     <PreferenceCategory android:title="@string/pref_category_advanced">
 
         <CheckBoxPreference
                         android:key="browser.zoom.reflowOnZoom"
                         android:title="@string/pref_reflow_on_zoom"
                         android:defaultValue="false"
                         android:persistent="false" />
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -249,17 +249,17 @@
   <string name="contextmenu_top_sites_pin">&contextmenu_top_sites_pin;</string>
   <string name="contextmenu_top_sites_unpin">&contextmenu_top_sites_unpin;</string>
   <string name="contextmenu_add_search_engine">&contextmenu_add_search_engine;</string>
 
   <string name="pref_titlebar_mode">&pref_titlebar_mode;</string>
   <string name="pref_titlebar_mode_title">&pref_titlebar_mode_title;</string>
   <string name="pref_titlebar_mode_url">&pref_titlebar_mode_url;</string>
 
-  <string name="pref_dynamic_toolbar">&pref_dynamic_toolbar;</string>
+  <string name="pref_scroll_title_bar">&pref_scroll_title_bar;</string>
 
   <string name="history_removed">&history_removed;</string>
 
   <string name="bookmark_edit_title">&bookmark_edit_title;</string>
   <string name="bookmark_edit_name">&bookmark_edit_name;</string>
   <string name="bookmark_edit_location">&bookmark_edit_location;</string>
   <string name="bookmark_edit_keyword">&bookmark_edit_keyword;</string>
 
@@ -291,29 +291,34 @@
   <string name="home_reading_list_hint_accessible">&home_reading_list_hint_accessible;</string>
   <string name="pin_site_dialog_hint">&pin_site_dialog_hint;</string>
 
   <string name="filepicker_title">&filepicker_title;</string>
   <string name="filepicker_audio_title">&filepicker_audio_title;</string>
   <string name="filepicker_image_title">&filepicker_image_title;</string>
   <string name="filepicker_video_title">&filepicker_video_title;</string>
 
-  <!-- Default bookmarks. Use bookmarks titles shared with XUL from mobile's
-       profile/bookmarks.inc. Don't expose the URLs to L10N. -->
+  <!-- Default bookmarks. We used to use bookmark titles shared with XUL from mobile's
+       profile/bookmarks.inc (see bug 964946). Don't expose the URLs to L10N. -->
   <string name="bookmarkdefaults_title_aboutfirefox">@bookmarks_aboutBrowser@</string>
   <string name="bookmarkdefaults_url_aboutfirefox">about:firefox</string>
   <string name="bookmarkdefaults_favicon_aboutfirefox">chrome/chrome/content/branding/favicon64.png</string>
 
+  <!-- Icon is automatically generated from R.drawable.bookmarkdefaults_favicon_addons -->
   <string name="bookmarkdefaults_title_addons">@bookmarks_addons@</string>
   <string name="bookmarkdefaults_url_addons">https://addons.mozilla.org/@AB_CD@/android/</string>
 
   <string name="bookmarkdefaults_title_support">@bookmarks_support@</string>
   <string name="bookmarkdefaults_url_support">http://support.mozilla.org/@AB_CD@/products/mobile</string>
   <string name="bookmarkdefaults_favicon_abouthome">chrome/chrome/content/branding/favicon64.png</string>
 
+  <!-- Icon is automatically generated from R.drawable.bookmarkdefaults_favicon_marketplace -->
+  <string name="bookmarkdefaults_title_marketplace">@bookmarks_marketplace@</string>
+  <string name="bookmarkdefaults_url_marketplace">https://marketplace.firefox.com/</string>
+
   <!-- Site identity popup -->
   <string name="identity_connected_to">&identity_connected_to;</string>
   <string name="identity_run_by">&identity_run_by;</string>
   <string name="loaded_mixed_content_message">&loaded_mixed_content_message;</string>
   <string name="blocked_mixed_content_message_top">&blocked_mixed_content_message_top;</string>
   <string name="blocked_mixed_content_message_bottom">&blocked_mixed_content_message_bottom;</string>
   <string name="learn_more">&learn_more;</string>
   <string name="enable_protection">&enable_protection;</string>
--- a/mobile/locales/en-US/profile/bookmarks.inc
+++ b/mobile/locales/en-US/profile/bookmarks.inc
@@ -21,9 +21,13 @@
 # LOCALIZATION NOTE (bookmarks_addons):
 # link title for https://addons.mozilla.org/en-US/mobile
 #define bookmarks_addons Firefox: Customize with add-ons
 
 # LOCALIZATION NOTE (bookmarks_support):
 # link title for https://support.mozilla.org/mobile
 #define bookmarks_support Firefox: Support
 
+# LOCALIZATION NOTE (bookmarks_marketplace):
+# link title for https://marketplace.firefox.com
+#define bookmarks_marketplace Firefox Marketplace
+
 #unfilter emptyLines
--- a/toolkit/devtools/server/actors/script.js
+++ b/toolkit/devtools/server/actors/script.js
@@ -1694,19 +1694,19 @@ ThreadActor.prototype = {
 
     for (let node of nodes) {
       let handlers = els.getListenerInfoFor(node);
 
       for (let handler of handlers) {
         // Create a form object for serializing the listener via the protocol.
         let listenerForm = Object.create(null);
         let listener = handler.listenerObject;
-        // Native event listeners don't provide any listenerObject and are not
-        // that useful to a JS debugger.
-        if (!listener) {
+        // Native event listeners don't provide any listenerObject or type and
+        // are not that useful to a JS debugger.
+        if (!listener || !handler.type) {
           continue;
         }
 
         // There will be no tagName if the event listener is set on the window.
         let selector = node.tagName ? findCssSelector(node) : "window";
         let nodeDO = this.globalDebugObject.makeDebuggeeValue(node);
         listenerForm.node = {
           selector: selector,