Merge mozilla-inbound into birch on a CLOSED TREE
authorEhsan Akhgari <ehsan@mozilla.com>
Sun, 12 May 2013 01:05:18 -0400
changeset 138444 491005c97e8041ee268b3ca203d6e74b1496e4b4
parent 138443 187f533ec992f7e1a2c5562a78fe27764ae83c12 (current diff)
parent 138418 9ec0ad6f7e09d1dc382d67da8878cb35e38df51b (diff)
child 138445 ba7496b49c06b6a670a62a7c2d275eee52d38f1b
push idunknown
push userunknown
push dateunknown
milestone23.0a1
Merge mozilla-inbound into birch on a CLOSED TREE
security/nss/cmd/.cvsignore
security/nss/lib/certdb/.cvsignore
security/patches/bug-832272.patch
security/patches/bug-835919.patch
toolkit/components/satchel/test/unit/formhistory_expire.sqlite
toolkit/components/satchel/test/unit/formhistory_v0.sqlite
toolkit/components/satchel/test/unit/formhistory_v0v1.sqlite
toolkit/components/satchel/test/unit/formhistory_v1.sqlite
toolkit/components/satchel/test/unit/formhistory_v1v2.sqlite
toolkit/components/satchel/test/unit/formhistory_v2.sqlite
toolkit/components/satchel/test/unit/formhistory_v2v3.sqlite
toolkit/components/satchel/test/unit/test_db_update_v1.js
toolkit/components/satchel/test/unit/test_db_update_v1b.js
toolkit/components/satchel/test/unit/test_db_update_v2.js
toolkit/components/satchel/test/unit/test_db_update_v2b.js
toolkit/components/satchel/test/unit/test_db_update_v3.js
toolkit/components/satchel/test/unit/test_db_update_v3b.js
toolkit/components/satchel/test/unit/test_expire.js
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -389,23 +389,22 @@ pref("dom.mozContacts.enabled", true);
 // WebAlarms
 pref("dom.mozAlarms.enabled", true);
 
 // SimplePush
 pref("services.push.enabled", true);
 // serverURL to be assigned by services team
 pref("services.push.serverURL", "");
 pref("services.push.userAgentID", "");
-// Exponential back-off start is 5 seconds like in HTTP/1.1.
-// Maximum back-off is pingInterval.
+// exponential back-off start is 5 seconds like in HTTP/1.1
 pref("services.push.retryBaseInterval", 5000);
-// Interval at which to ping PushServer to check connection status. In
-// milliseconds. If no reply is received within requestTimeout, the connection
-// is considered closed.
-pref("services.push.pingInterval", 1800000); // 30 minutes
+// WebSocket level ping transmit interval in seconds.
+pref("services.push.websocketPingInterval", 55);
+// exponential back-off end is 20 minutes
+pref("services.push.maxRetryInterval", 1200000);
 // How long before a DOMRequest errors as timeout
 pref("services.push.requestTimeout", 10000);
 // enable udp wakeup support
 pref("services.push.udp.wakeupEnabled", true);
 // port on which UDP server socket is bound
 pref("services.push.udp.port", 2442);
 
 // NetworkStats
new file mode 100644
--- /dev/null
+++ b/b2g/config/gaia.json
@@ -0,0 +1,4 @@
+{
+    "repo": "http://hg.mozilla.org/integration/gaia-central/",
+    "revision": "8c7dcb64cafd"
+}
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -443,16 +443,17 @@
 @BINPATH@/components/nsContentDispatchChooser.js
 @BINPATH@/components/nsHandlerService.manifest
 @BINPATH@/components/nsHandlerService.js
 @BINPATH@/components/nsWebHandlerApp.manifest
 @BINPATH@/components/nsWebHandlerApp.js
 @BINPATH@/components/satchel.manifest
 @BINPATH@/components/nsFormAutoComplete.js
 @BINPATH@/components/nsFormHistory.js
+@BINPATH@/components/FormHistoryStartup.js
 @BINPATH@/components/nsInputListAutoComplete.js
 @BINPATH@/components/contentSecurityPolicy.manifest
 @BINPATH@/components/contentSecurityPolicy.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/SettingsManager.js
--- a/browser/base/content/browser-places.js
+++ b/browser/base/content/browser-places.js
@@ -307,18 +307,18 @@ var PlacesCommandHook = {
     // If it was not requested to open directly in "edit" mode, we are done.
     if (!aShowEditUI)
       return;
 
     // Try to dock the panel to:
     // 1. the bookmarks menu button
     // 2. the page-proxy-favicon
     // 3. the content area
-    if (BookmarksMenuButton.anchor) {
-      StarUI.showEditBookmarkPopup(itemId, BookmarksMenuButton.anchor,
+    if (BookmarkingUI.anchor) {
+      StarUI.showEditBookmarkPopup(itemId, BookmarkingUI.anchor,
                                    "bottomcenter topright");
       return;
     }
 
     let pageProxyFavicon = document.getElementById("page-proxy-favicon");
     if (isElementVisible(pageProxyFavicon)) {
       StarUI.showEditBookmarkPopup(itemId, pageProxyFavicon,
                                    "bottomcenter topright");
@@ -986,57 +986,55 @@ let PlacesToolbarHelper = {
 
   customizeDone: function PTH_customizeDone() {
     this._isCustomizing = false;
     this.init();
   }
 };
 
 ////////////////////////////////////////////////////////////////////////////////
-//// BookmarksMenuButton
+//// BookmarkingUI
 
 /**
- * Handles the bookmarks menu-button in the toolbar.
+ * Handles the bookmarks star button in the URL bar, as well as the bookmark
+ * menu button.
  */
-let BookmarksMenuButton = {
+
+let BookmarkingUI = {
   get button() {
     if (!this._button) {
       this._button = document.getElementById("bookmarks-menu-button");
     }
     return this._button;
   },
 
   get star() {
-    if (!this._star && this.button) {
-      this._star = document.getAnonymousElementByAttribute(this.button,
-                                                           "anonid",
-                                                           "button");
+    if (!this._star) {
+      this._star = document.getElementById("star-button");
     }
     return this._star;
   },
 
   get anchor() {
-    if (!this._anchor && this.star && isElementVisible(this.star)) {
+    if (this.star && isElementVisible(this.star)) {
       // Anchor to the icon, so the panel looks more natural.
-      this._anchor = document.getAnonymousElementByAttribute(this.star,
-                                                             "class",
-                                                             "toolbarbutton-icon");
+      return this.star;
     }
-    return this._anchor;
+    return null;
   },
 
   STATUS_UPDATING: -1,
   STATUS_UNSTARRED: 0,
   STATUS_STARRED: 1,
   get status() {
     if (this._pendingStmt)
       return this.STATUS_UPDATING;
-    return this.button &&
-           this.button.hasAttribute("starred") ? this.STATUS_STARRED
-                                               : this.STATUS_UNSTARRED;
+    return this.star &&
+           this.star.hasAttribute("starred") ? this.STATUS_STARRED
+                                             : this.STATUS_UNSTARRED;
   },
 
   get _starredTooltip()
   {
     delete this._starredTooltip;
     return this._starredTooltip =
       gNavigatorBundle.getString("starButtonOn.tooltip");
   },
@@ -1050,21 +1048,21 @@ let BookmarksMenuButton = {
 
   /**
    * 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 BMB_onToolbarVisibilityChange() {
+  onToolbarVisibilityChange: function BUI_onToolbarVisibilityChange() {
     this._popupNeedsUpdate = true;
   },
 
-  onPopupShowing: function BMB_onPopupShowing(event) {
+  onPopupShowing: function BUI_onPopupShowing(event) {
     // Don't handle events for submenus.
     if (event.target != event.currentTarget)
       return;
 
     if (!this._popupNeedsUpdate)
       return;
     this._popupNeedsUpdate = false;
 
@@ -1088,91 +1086,84 @@ let BookmarksMenuButton = {
       toolbarMenuitem.collapsed = toolbarMenuitem.nextSibling.collapsed =
         isElementVisible(document.getElementById("personal-bookmarks"));
     }
   },
 
   /**
    * Handles star styling based on page proxy state changes.
    */
-  onPageProxyStateChanged: function BMB_onPageProxyStateChanged(aState) {
+  onPageProxyStateChanged: function BUI_onPageProxyStateChanged(aState) {
     if (!this.star) {
       return;
     }
 
     if (aState == "invalid") {
       this.star.setAttribute("disabled", "true");
-      this.button.removeAttribute("starred");
+      this.star.removeAttribute("starred");
     }
     else {
       this.star.removeAttribute("disabled");
     }
-    this._updateStyle();
   },
 
-  _updateStyle: function BMB__updateStyle() {
-    if (!this.star) {
-      return;
-    }
-
+  _updateToolbarStyle: function BUI__updateToolbarStyle() {
     let personalToolbar = document.getElementById("PersonalToolbar");
     let onPersonalToolbar = this.button.parentNode == personalToolbar ||
                             this.button.parentNode.parentNode == personalToolbar;
 
     if (onPersonalToolbar) {
       this.button.classList.add("bookmark-item");
       this.button.classList.remove("toolbarbutton-1");
     }
     else {
       this.button.classList.remove("bookmark-item");
       this.button.classList.add("toolbarbutton-1");
     }
   },
 
-  _uninitView: function BMB__uninitView() {
+  _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.
     if (this.button && this.button._placesView) {
       this.button._placesView.uninit();
     }
   },
 
-  customizeStart: function BMB_customizeStart() {
+  customizeStart: function BUI_customizeStart() {
     this._uninitView();
   },
 
-  customizeChange: function BMB_customizeChange() {
-    this._updateStyle();
+  customizeChange: function BUI_customizeChange() {
+    this._updateToolbarStyle();
   },
 
-  customizeDone: function BMB_customizeDone() {
+  customizeDone: function BUI_customizeDone() {
     delete this._button;
-    delete this._star;
-    delete this._anchor;
     this.onToolbarVisibilityChange();
-    this._updateStyle();
+    this._updateToolbarStyle();
   },
 
   _hasBookmarksObserver: false,
-  uninit: function BMB_uninit() {
+  uninit: function BUI_uninit() {
     this._uninitView();
 
     if (this._hasBookmarksObserver) {
       PlacesUtils.removeLazyBookmarkObserver(this);
     }
 
     if (this._pendingStmt) {
       this._pendingStmt.cancel();
       delete this._pendingStmt;
     }
   },
 
-  updateStarState: function BMB_updateStarState() {
-    if (!this.button || (this._uri && gBrowser.currentURI.equals(this._uri))) {
+  updateStarState: function BUI_updateStarState() {
+    if (!this.star || (this._uri && gBrowser.currentURI.equals(this._uri))) {
       return;
     }
 
     // Reset tracked values.
     this._uri = gBrowser.currentURI;
     this._itemIds = [];
 
     if (this._pendingStmt) {
@@ -1210,76 +1201,64 @@ let BookmarksMenuButton = {
           Components.utils.reportError("BookmarksMenuButton failed adding a bookmarks observer: " + ex);
         }
       }
 
       delete this._pendingStmt;
     }, this);
   },
 
-  _updateStar: function BMB__updateStar() {
-    if (!this.button) {
+  _updateStar: function BUI__updateStar() {
+    if (!this.star) {
       return;
     }
 
     if (this._itemIds.length > 0) {
-      this.button.setAttribute("starred", "true");
-      this.button.setAttribute("tooltiptext", this._starredTooltip);
+      this.star.setAttribute("starred", "true");
+      this.star.setAttribute("tooltiptext", this._starredTooltip);
     }
     else {
-      this.button.removeAttribute("starred");
-      this.button.setAttribute("tooltiptext", this._unstarredTooltip);
+      this.star.removeAttribute("starred");
+      this.star.setAttribute("tooltiptext", this._unstarredTooltip);
     }
   },
 
-  onCommand: function BMB_onCommand(aEvent) {
+  onCommand: function BUI_onCommand(aEvent) {
     if (aEvent.target != aEvent.currentTarget) {
       return;
     }
     // Ignore clicks on the star if we are updating its state.
     if (!this._pendingStmt) {
       PlacesCommandHook.bookmarkCurrentPage(this._itemIds.length > 0);
     }
   },
 
   // nsINavBookmarkObserver
-  onItemAdded: function BMB_onItemAdded(aItemId, aParentId, aIndex, aItemType,
+  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();
       }
     }
   },
 
-  onItemRemoved: function BMB_onItemRemoved(aItemId) {
-    if (!this.button) {
-      return;
-    }
-
+  onItemRemoved: function BUI_onItemRemoved(aItemId) {
     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();
     }
   },
 
-  onItemChanged: function BMB_onItemChanged(aItemId, aProperty,
+  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();
       }
@@ -1294,10 +1273,10 @@ let BookmarksMenuButton = {
   onBeginUpdateBatch: function () {},
   onEndUpdateBatch: function () {},
   onBeforeItemRemoved: function () {},
   onItemVisited: function () {},
   onItemMoved: function () {},
 
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsINavBookmarkObserver
-  ]),
+  ])
 };
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -303,30 +303,28 @@ panel[noactions] > richlistbox > richlis
 .unified-nav-current {
   font-weight: bold;
 }
 
 toolbarbutton.bookmark-item {
   max-width: 13em;
 }
 
+%ifdef MENUBAR_CAN_AUTOHIDE
+#toolbar-menubar:not([autohide="true"]) ~ toolbar > #bookmarks-menu-button,
+#toolbar-menubar:not([autohide="true"]) > #bookmarks-menu-button {
+  display: none;
+}
+%endif
+
 #editBMPanel_tagsSelector {
   /* override default listbox width from xul.css */
   width: auto;
 }
 
-/* The star doesn't make sense as text */
-toolbar[mode="text"] #bookmarks-menu-button > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
-  display: -moz-box !important;
-}
-toolbar[mode="text"] #bookmarks-menu-button > .toolbarbutton-menubutton-button > .toolbarbutton-text,
-toolbar[mode="full"] #bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button > .toolbarbutton-text {
-  display: none;
-}
-
 menupopup[emptyplacesresult="true"] > .hide-if-empty-places-result {
   display: none;
 }
 
 menuitem.spell-suggestion {
   font-weight: bold;
 }
 
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -3,16 +3,17 @@
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/RecentWindow.jsm");
+Cu.import("resource://gre/modules/PushService.jsm");
 
 const nsIWebNavigation = Ci.nsIWebNavigation;
 
 var gCharsetMenu = null;
 var gLastBrowserCharset = null;
 var gPrevCharset = null;
 var gProxyFavIcon = null;
 var gLastValidURLStr = "";
@@ -1308,17 +1309,17 @@ var gBrowserInit = {
     Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
 
     try {
       gBrowser.removeProgressListener(window.XULBrowserWindow);
       gBrowser.removeTabsProgressListener(window.TabsProgressListener);
     } catch (ex) {
     }
 
-    BookmarksMenuButton.uninit();
+    BookmarkingUI.uninit();
 
     TabsOnTop.uninit();
 
     TabsInTitlebar.uninit();
 
     var enumerator = Services.wm.getEnumerator(null);
     enumerator.getNext();
     if (!enumerator.hasMoreElements()) {
@@ -2226,17 +2227,17 @@ function setUrlAndSearchBarWidthForCondi
 function UpdatePageProxyState()
 {
   if (gURLBar && gURLBar.value != gLastValidURLStr)
     SetPageProxyState("invalid");
 }
 
 function SetPageProxyState(aState)
 {
-  BookmarksMenuButton.onPageProxyStateChanged(aState);
+  BookmarkingUI.onPageProxyStateChanged(aState);
 
   if (!gURLBar)
     return;
 
   if (!gProxyFavIcon)
     gProxyFavIcon = document.getElementById("page-proxy-favicon");
 
   gURLBar.setAttribute("pageproxystate", aState);
@@ -3322,17 +3323,17 @@ function BrowserCustomizeToolbar() {
 
   var splitter = document.getElementById("urlbar-search-splitter");
   if (splitter)
     splitter.parentNode.removeChild(splitter);
 
   CombinedStopReload.uninit();
 
   PlacesToolbarHelper.customizeStart();
-  BookmarksMenuButton.customizeStart();
+  BookmarkingUI.customizeStart();
   DownloadsButton.customizeStart();
 
   TabsInTitlebar.allowedBy("customizing-toolbars", false);
 
   var customizeURL = "chrome://global/content/customizeToolbar.xul";
   gCustomizeSheet = getBoolPref("toolbar.customization.usesheet", false);
 
   if (gCustomizeSheet) {
@@ -3386,30 +3387,30 @@ function BrowserToolboxCustomizeDone(aTo
     // Hacky: update the PopupNotifications' object's reference to the iconBox,
     // if it already exists, since it may have changed if the URL bar was
     // added/removed.
     if (!window.__lookupGetter__("PopupNotifications"))
       PopupNotifications.iconBox = document.getElementById("notification-popup-box");
   }
 
   PlacesToolbarHelper.customizeDone();
-  BookmarksMenuButton.customizeDone();
+  BookmarkingUI.customizeDone();
   DownloadsButton.customizeDone();
 
   // The url bar splitter state is dependent on whether stop/reload
   // and the location bar are combined, so we need this ordering
   CombinedStopReload.init();
   UpdateUrlbarSearchSplitterState();
   setUrlAndSearchBarWidthForConditionalForwardButton();
 
   // Update the urlbar
   if (gURLBar) {
     URLBarSetURI();
     XULBrowserWindow.asyncUpdateUI();
-    BookmarksMenuButton.updateStarState();
+    BookmarkingUI.updateStarState();
     SocialMark.updateMarkState();
     SocialShare.update();
   }
 
   TabsInTitlebar.allowedBy("customizing-toolbars", true);
 
   // Re-enable parts of the UI we disabled during the dialog
   var menubar = document.getElementById("main-menubar");
@@ -3428,17 +3429,17 @@ function BrowserToolboxCustomizeDone(aTo
 function BrowserToolboxCustomizeChange(aType) {
   switch (aType) {
     case "iconsize":
     case "mode":
       retrieveToolbarIconsizesFromTheme();
       break;
     default:
       gHomeButton.updatePersonalToolbarStyle();
-      BookmarksMenuButton.customizeChange();
+      BookmarkingUI.customizeChange();
   }
 }
 
 /**
  * Allows themes to override the "iconsize" attribute on toolbars.
  */
 function retrieveToolbarIconsizesFromTheme() {
   function retrieveToolbarIconsize(aToolbar) {
@@ -3874,17 +3875,17 @@ var XULBrowserWindow = {
       } else {
         this.reloadCommand.removeAttribute("disabled");
       }
 
       if (gURLBar) {
         URLBarSetURI(aLocationURI);
 
         // Update starring UI
-        BookmarksMenuButton.updateStarState();
+        BookmarkingUI.updateStarState();
         SocialMark.updateMarkState();
         SocialShare.update();
       }
 
       // Show or hide browser chrome based on the whitelist
       if (this.hideChromeForLocation(location)) {
         document.documentElement.setAttribute("disablechrome", "true");
       } else {
@@ -4502,17 +4503,17 @@ function onViewToolbarCommand(aEvent) {
 function setToolbarVisibility(toolbar, isVisible) {
   var hidingAttribute = toolbar.getAttribute("type") == "menubar" ?
                         "autohide" : "collapsed";
 
   toolbar.setAttribute(hidingAttribute, !isVisible);
   document.persist(toolbar.id, hidingAttribute);
 
   PlacesToolbarHelper.init();
-  BookmarksMenuButton.onToolbarVisibilityChange();
+  BookmarkingUI.onToolbarVisibilityChange();
   gBrowser.updateWindowResizers();
 
 #ifdef MENUBAR_CAN_AUTOHIDE
   updateAppButtonDisplay();
 #endif
 }
 
 var TabsOnTop = {
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -576,16 +576,19 @@
             <label id="urlbar-display" value="&urlbar.switchToTab.label;"/>
           </box>
           <hbox id="urlbar-icons">
             <image id="page-report-button"
                    class="urlbar-icon"
                    hidden="true"
                    tooltiptext="&pageReportIcon.tooltip;"
                    onclick="gPopupBlockerObserver.onReportButtonClick(event);"/>
+            <image id="star-button"
+                   class="urlbar-icon"
+                   onclick="BookmarkingUI.onCommand(event);"/>
             <image id="go-button"
                    class="urlbar-icon"
                    tooltiptext="&goEndCap.tooltip;"
                    onclick="gURLBar.handleCommand(event);"/>
           </hbox>
           <toolbarbutton id="urlbar-go-button"
                          class="chromeclass-toolbar-additional"
                          onclick="gURLBar.handleCommand(event);"
@@ -630,46 +633,52 @@
                    onpopuphiding="WebrtcIndicator.clearPopup(this);"
                    oncommand="WebrtcIndicator.menuCommand(event.target);"/>
       </toolbarbutton>
 
       <toolbarbutton id="bookmarks-menu-button"
                      class="toolbarbutton-1 chromeclass-toolbar-additional"
                      persist="class"
                      removable="true"
-                     type="menu-button"
+                     type="menu"
                      label="&bookmarksMenuButton.label;"
                      tooltiptext="&bookmarksMenuButton.tooltip;"
                      ondragenter="PlacesMenuDNDHandler.onDragEnter(event);"
                      ondragover="PlacesMenuDNDHandler.onDragOver(event);"
                      ondragleave="PlacesMenuDNDHandler.onDragLeave(event);"
-                     ondrop="PlacesMenuDNDHandler.onDrop(event);"
-                     oncommand="BookmarksMenuButton.onCommand(event);">
+                     ondrop="PlacesMenuDNDHandler.onDrop(event);">
         <menupopup id="BMB_bookmarksPopup"
                    placespopup="true"
                    context="placesContext"
                    openInTabs="children"
                    oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
                    onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
-                   onpopupshowing="BookmarksMenuButton.onPopupShowing(event);
+                   onpopupshowing="BookmarkingUI.onPopupShowing(event);
                                    if (!this.parentNode._placesView)
                                      new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
                    tooltip="bhTooltip" popupsinherittooltip="true">
           <menuitem id="BMB_viewBookmarksToolbar"
                     placesanonid="view-toolbar"
                     toolbarId="PersonalToolbar"
                     type="checkbox"
                     oncommand="onViewToolbarCommand(event)"
                     label="&viewBookmarksToolbar.label;"/>
           <menuseparator/>
           <menuitem id="BMB_bookmarksShowAll"
                     label="&showAllBookmarks2.label;"
                     command="Browser:ShowAllBookmarks"
                     key="manBookmarkKb"/>
           <menuseparator/>
+          <menuitem id="BMB_bookmarkThisPage"
+#ifndef XP_MACOSX
+                    class="menuitem-iconic"
+#endif
+                    label="&bookmarkThisPageCmd.label;"
+                    command="Browser:AddBookmarkAs"
+                    key="addBookmarkAsKb"/>
           <menuitem id="BMB_subscribeToPageMenuitem"
 #ifndef XP_MACOSX
                     class="menuitem-iconic"
 #endif
                     label="&subscribeToPageMenuitem.label;"
                     oncommand="return FeedHandler.subscribeToFeed(null, event);"
                     onclick="checkForMiddleClick(this, event);"
                     observes="singleFeedMenuitemState"/>
--- a/browser/base/content/sanitize.js
+++ b/browser/base/content/sanitize.js
@@ -1,77 +1,102 @@
 # -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 # 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/.
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
                                   "resource://gre/modules/PlacesUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
+                                  "resource://gre/modules/FormHistory.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Promise",
+                                  "resource://gre/modules/commonjs/sdk/core/promise.js");
 
 function Sanitizer() {}
 Sanitizer.prototype = {
   // warning to the caller: this one may raise an exception (e.g. bug #265028)
   clearItem: function (aItemName)
   {
     if (this.items[aItemName].canClear)
       this.items[aItemName].clear();
   },
 
-  canClearItem: function (aItemName)
+  canClearItem: function (aItemName, aCallback, aArg)
   {
-    return this.items[aItemName].canClear;
+    let canClear = this.items[aItemName].canClear;
+    if (typeof canClear == "function") {
+      canClear(aCallback, aArg);
+      return false;
+    }
+
+    aCallback(aItemName, canClear, aArg);
+    return canClear;
   },
   
   prefDomain: "",
   
   getNameFromPreference: function (aPreferenceName)
   {
     return aPreferenceName.substr(this.prefDomain.length);
   },
   
   /**
-   * Deletes privacy sensitive data in a batch, according to user preferences
-   *
-   * @returns  null if everything's fine;  an object in the form
-   *           { itemName: error, ... } on (partial) failure
+   * Deletes privacy sensitive data in a batch, according to user preferences.
+   * Returns a promise which is resolved if no errors occurred.  If an error
+   * occurs, a message is reported to the console and all other items are still
+   * cleared before the promise is finally rejected.
    */
   sanitize: function ()
   {
+    var deferred = Promise.defer();
     var psvc = Components.classes["@mozilla.org/preferences-service;1"]
                          .getService(Components.interfaces.nsIPrefService);
     var branch = psvc.getBranch(this.prefDomain);
-    var errors = null;
+    var seenError = false;
 
     // Cache the range of times to clear
     if (this.ignoreTimespan)
       var range = null;  // If we ignore timespan, clear everything
     else
       range = this.range || Sanitizer.getClearRange();
-      
+
+    let itemCount = Object.keys(this.items).length;
+    let onItemComplete = function() {
+      if (!--itemCount) {
+        seenError ? deferred.reject() : deferred.resolve();
+      }
+    };
     for (var itemName in this.items) {
-      var item = this.items[itemName];
+      let item = this.items[itemName];
       item.range = range;
-      if ("clear" in item && item.canClear && branch.getBoolPref(itemName)) {
-        // Some of these clear() may raise exceptions (see bug #265028)
-        // to sanitize as much as possible, we catch and store them, 
-        // rather than fail fast.
-        // Callers should check returned errors and give user feedback
-        // about items that could not be sanitized
-        try {
-          item.clear();
-        } catch(er) {
-          if (!errors) 
-            errors = {};
-          errors[itemName] = er;
-          dump("Error sanitizing " + itemName + ": " + er + "\n");
-        }
+      if ("clear" in item && branch.getBoolPref(itemName)) {
+        let clearCallback = (itemName, aCanClear) => {
+          // Some of these clear() may raise exceptions (see bug #265028)
+          // to sanitize as much as possible, we catch and store them,
+          // rather than fail fast.
+          // Callers should check returned errors and give user feedback
+          // about items that could not be sanitized
+          let item = this.items[itemName];
+          try {
+            if (aCanClear)
+              item.clear();
+          } catch(er) {
+            seenError = true;
+            Cu.reportError("Error sanitizing " + itemName + ": " + er + "\n");
+          }
+          onItemComplete();
+        };
+        this.canClearItem(itemName, clearCallback);
+      } else {
+        onItemComplete();
       }
     }
-    return errors;
+
+    return deferred.promise;
   },
   
   // Time span only makes sense in certain cases.  Consumers who want
   // to only clear some private data can opt in by setting this to false,
   // and can optionally specify a specific range.  If timespan is not ignored,
   // and range is not set, sanitize() will use the value of the timespan
   // pref to determine a range
   ignoreTimespan : true,
@@ -226,47 +251,56 @@ Sanitizer.prototype = {
           let searchBar = currentDocument.getElementById("searchbar");
           if (searchBar)
             searchBar.textbox.reset();
           let findBar = currentDocument.getElementById("FindToolbar");
           if (findBar)
             findBar.clear();
         }
 
-        let formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
-                                    .getService(Components.interfaces.nsIFormHistory2);
-        if (this.range)
-          formHistory.removeEntriesByTimeframe(this.range[0], this.range[1]);
-        else
-          formHistory.removeAllEntries();
+        let change = { op: "remove" };
+        if (this.range) {
+          [ change.firstUsedStart, change.firstUsedEnd ] = this.range;
+        }
+        FormHistory.update(change);
       },
 
-      get canClear()
+      canClear : function(aCallback, aArg)
       {
         var windowManager = Components.classes['@mozilla.org/appshell/window-mediator;1']
                                       .getService(Components.interfaces.nsIWindowMediator);
         var windows = windowManager.getEnumerator("navigator:browser");
         while (windows.hasMoreElements()) {
           let currentDocument = windows.getNext().document;
           let searchBar = currentDocument.getElementById("searchbar");
           if (searchBar) {
             let transactionMgr = searchBar.textbox.editor.transactionManager;
             if (searchBar.value ||
                 transactionMgr.numberOfUndoItems ||
-                transactionMgr.numberOfRedoItems)
-              return true;
+                transactionMgr.numberOfRedoItems) {
+              aCallback("formdata", true, aArg);
+              return false;
+            }
           }
           let findBar = currentDocument.getElementById("FindToolbar");
-          if (findBar && findBar.canClear)
-            return true;
+          if (findBar && findBar.canClear) {
+            aCallback("formdata", true, aArg);
+            return false;
+          }
         }
 
-        let formHistory = Components.classes["@mozilla.org/satchel/form-history;1"]
-                                    .getService(Components.interfaces.nsIFormHistory2);
-        return formHistory.hasEntries;
+        let count = 0;
+        let countDone = {
+          handleResult : function(aResult) count = aResult,
+          handleError : function(aError) Components.utils.reportError(aError),
+          handleCompletion :
+            function(aReason) { aCallback("formdata", aReason == 0 && count > 0, aArg); }
+        };
+        FormHistory.count({}, countDone);
+        return false;
       }
     },
     
     downloads: {
       clear: function ()
       {
         var dlMgr = Components.classes["@mozilla.org/download-manager;1"]
                               .getService(Components.interfaces.nsIDownloadManager);
@@ -481,14 +515,13 @@ Sanitizer.onShutdown = function()
 Sanitizer._checkAndSanitize = function() 
 {
   const prefs = Sanitizer.prefs;
   if (prefs.getBoolPref(Sanitizer.prefShutdown) && 
       !prefs.prefHasUserValue(Sanitizer.prefDidShutdown)) {
     // this is a shutdown or a startup after an unclean exit
     var s = new Sanitizer();
     s.prefDomain = "privacy.clearOnShutdown.";
-    s.sanitize() || // sanitize() returns null on full success
+    s.sanitize().then(function() {
       prefs.setBoolPref(Sanitizer.prefDidShutdown, true);
+    });
   }
 };
-
-
--- a/browser/base/content/sanitize.xul
+++ b/browser/base/content/sanitize.xul
@@ -22,17 +22,17 @@
 ]>
 
 <prefwindow id="SanitizeDialog" type="child"
             xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
             dlgbuttons="accept,cancel"
             title="&sanitizeDialog2.title;"
             noneverythingtitle="&sanitizeDialog2.title;"
             style="width: &dialog.width2;;"
-            ondialogaccept="gSanitizePromptDialog.sanitize();">
+            ondialogaccept="return gSanitizePromptDialog.sanitize();">
 
   <prefpane id="SanitizeDialogPane" onpaneload="gSanitizePromptDialog.init();">
     <stringbundle id="bundleBrowser"
                   src="chrome://browser/locale/browser.properties"/>
 
     <script type="application/javascript"
             src="chrome://browser/content/sanitize.js"/>
 
--- a/browser/base/content/sanitizeDialog.js
+++ b/browser/base/content/sanitizeDialog.js
@@ -42,21 +42,23 @@ var gSanitizePromptDialog = {
 
     var s = new Sanitizer();
     s.prefDomain = "privacy.cpd.";
 
     let sanitizeItemList = document.querySelectorAll("#itemList > [preference]");
     for (let i = 0; i < sanitizeItemList.length; i++) {
       let prefItem = sanitizeItemList[i];
       let name = s.getNameFromPreference(prefItem.getAttribute("preference"));
-      if (!s.canClearItem(name)) {
-        prefItem.preference = null;
-        prefItem.checked = false;
-        prefItem.disabled = true;
-      }
+      s.canClearItem(name, function canClearCallback(aItem, aCanClear, aPrefItem) {
+        if (!aCanClear) {
+          aPrefItem.preference = null;
+          aPrefItem.checked = false;
+          aPrefItem.disabled = true;
+        }
+      }, prefItem);
     }
 
     document.documentElement.getButton("accept").label =
       this.bundleBrowser.getString("sanitizeButtonOK");
 
     if (this.selectedTimespan === Sanitizer.TIMESPAN_EVERYTHING) {
       this.prepareWarning();
       this.warningBox.hidden = false;
@@ -102,22 +104,33 @@ var gSanitizePromptDialog = {
     // Update pref values before handing off to the sanitizer (bug 453440)
     this.updatePrefs();
     var s = new Sanitizer();
     s.prefDomain = "privacy.cpd.";
 
     s.range = Sanitizer.getClearRange(this.selectedTimespan);
     s.ignoreTimespan = !s.range;
 
+    // As the sanitize is async, we disable the buttons, update the label on
+    // the 'accept' button to indicate things are happening and return false -
+    // once the async operation completes (either with or without errors)
+    // we close the window.
+    let docElt = document.documentElement;
+    let acceptButton = docElt.getButton("accept");
+    acceptButton.disabled = true;
+    acceptButton.setAttribute("label",
+                              this.bundleBrowser.getString("sanitizeButtonClearing"));
+    docElt.getButton("cancel").disabled = true;
     try {
-      s.sanitize();
+      s.sanitize().then(window.close, window.close);
     } catch (er) {
       Components.utils.reportError("Exception during sanitize: " + er);
+      return true; // We *do* want to close immediately on error.
     }
-    return true;
+    return false;
   },
 
   /**
    * If the panel that displays a warning when the duration is "Everything" is
    * not set up, sets it up.  Otherwise does nothing.
    *
    * @param aDontShowItemList Whether only the warning message should be updated.
    *                          True means the item list visibility status should not
@@ -276,21 +289,23 @@ var gSanitizePromptDialog = {
 
     var s = new Sanitizer();
     s.prefDomain = "privacy.cpd.";
 
     let sanitizeItemList = document.querySelectorAll("#itemList > [preference]");
     for (let i = 0; i < sanitizeItemList.length; i++) {
       let prefItem = sanitizeItemList[i];
       let name = s.getNameFromPreference(prefItem.getAttribute("preference"));
-      if (!s.canClearItem(name)) {
-        prefItem.preference = null;
-        prefItem.checked = false;
-        prefItem.disabled = true;
-      }
+      s.canClearItem(name, function canClearCallback(aCanClear) {
+        if (!aCanClear) {
+          prefItem.preference = null;
+          prefItem.checked = false;
+          prefItem.disabled = true;
+        }
+      });
     }
 
     document.documentElement.getButton("accept").label =
       this.bundleBrowser.getString("sanitizeButtonOK");
 
     this.selectByTimespan();
   },
 
--- a/browser/base/content/test/browser_bug409624.js
+++ b/browser/base/content/test/browser_bug409624.js
@@ -1,15 +1,30 @@
 /* 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/. */
 
+XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
+                                  "resource://gre/modules/FormHistory.jsm");
+
 function test() {
   waitForExplicitFinish();
 
+  // This test relies on the form history being empty to start with delete
+  // all the items first.
+  FormHistory.update({ op: "remove" },
+                     { handleError: function (error) {
+                         do_throw("Error occurred updating form history: " + error);
+                       },
+                       handleCompletion: function (reason) { if (!reason) test2(); },
+                     });
+}
+
+function test2()
+{
   let prefService = Cc["@mozilla.org/preferences-service;1"]
                     .getService(Components.interfaces.nsIPrefBranch2);
 
   let findBar = gFindBar;
   let textbox = gFindBar.getElement("findbar-textbox");
 
   let tempScope = {};
   Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
@@ -24,18 +39,35 @@ function test() {
   prefBranch.setBoolPref("downloads", false);
   prefBranch.setBoolPref("formdata", true);
   prefBranch.setBoolPref("history", false);
   prefBranch.setBoolPref("offlineApps", false);
   prefBranch.setBoolPref("passwords", false);
   prefBranch.setBoolPref("sessions", false);
   prefBranch.setBoolPref("siteSettings", false);
 
-  // Sanitize now so we can test that canClear is correct
-  s.sanitize();
-  ok(!s.canClearItem("formdata"), "pre-test baseline for sanitizer");
-  textbox.value = "m";
-  ok(s.canClearItem("formdata"), "formdata can be cleared after input");
-  s.sanitize();
-  is(textbox.value, "", "findBar textbox should be empty after sanitize");
-  ok(!s.canClearItem("formdata"), "canClear now false after sanitize");
+  // Sanitize now so we can test that canClear is correct. Formdata is cleared asynchronously.
+  s.sanitize().then(function() {
+    s.canClearItem("formdata", clearDone1, s);
+  });
+}
+
+function clearDone1(aItemName, aResult, aSanitizer)
+{
+  ok(!aResult, "pre-test baseline for sanitizer");
+  gFindBar.getElement("findbar-textbox").value = "m";
+  aSanitizer.canClearItem("formdata", inputEntered, aSanitizer);
+}
+
+function inputEntered(aItemName, aResult, aSanitizer)
+{
+  ok(aResult, "formdata can be cleared after input");
+  aSanitizer.sanitize().then(function() {
+    aSanitizer.canClearItem("formdata", clearDone2);
+  });
+}
+
+function clearDone2(aItemName, aResult)
+{
+  is(gFindBar.getElement("findbar-textbox").value, "", "findBar textbox should be empty after sanitize");
+  ok(!aResult, "canClear now false after sanitize");
   finish();
 }
--- a/browser/base/content/test/browser_bug432599.js
+++ b/browser/base/content/test/browser_bug432599.js
@@ -12,24 +12,24 @@ function invokeUsingCtrlD(phase) {
     EventUtils.synthesizeKey("d", { accelKey: true });
     break;
   }
 }
 
 function invokeUsingStarButton(phase) {
   switch (phase) {
   case 1:
-     EventUtils.synthesizeMouseAtCenter(BookmarksMenuButton.star, {});
+     EventUtils.synthesizeMouseAtCenter(BookmarkingUI.star, {});
     break;
   case 2:
   case 4:
     EventUtils.synthesizeKey("VK_ESCAPE", {});
     break;
   case 3:
-     EventUtils.synthesizeMouseAtCenter(BookmarksMenuButton.star,
+     EventUtils.synthesizeMouseAtCenter(BookmarkingUI.star,
                                         { clickCount: 2 });
     break;
   }
 }
 
 var testURL = "data:text/plain,Content";
 var bookmarkId;
 
@@ -55,20 +55,20 @@ function test() {
 function initTest() {
   // First, bookmark the page.
   bookmarkId = add_bookmark(makeURI(testURL), "Bug 432599 Test");
 
   checkBookmarksPanel(invokers[currentInvoker], 1);
 }
 
 function waitForStarChange(aValue, aCallback) {
-  let expectedStatus = aValue ? BookmarksMenuButton.STATUS_STARRED
-                              : BookmarksMenuButton.STATUS_UNSTARRED;
-  if (BookmarksMenuButton.status == BookmarksMenuButton.STATUS_UPDATING ||
-      BookmarksMenuButton.status != expectedStatus) {
+  let expectedStatus = aValue ? BookmarkingUI.STATUS_STARRED
+                              : BookmarkingUI.STATUS_UNSTARRED;
+  if (BookmarkingUI.status == BookmarkingUI.STATUS_UPDATING ||
+      BookmarkingUI.status != expectedStatus) {
     info("Waiting for star button change.");
     setTimeout(waitForStarChange, 50, aValue, aCallback);
     return;
   }
   aCallback();
 }
 
 let invokers = [invokeUsingStarButton, invokeUsingCtrlD];
--- a/browser/base/content/test/browser_bug581253.js
+++ b/browser/base/content/test/browser_bug581253.js
@@ -29,37 +29,37 @@ function test() {
     ok(PlacesUtils.bookmarks.isBookmarked(uri), "the test url is bookmarked");
     waitForStarChange(true, onStarred);
   }), true);
 
   content.location = testURL;
 }
 
 function waitForStarChange(aValue, aCallback) {
-  let expectedStatus = aValue ? BookmarksMenuButton.STATUS_STARRED
-                              : BookmarksMenuButton.STATUS_UNSTARRED;
-  if (BookmarksMenuButton.status == BookmarksMenuButton.STATUS_UPDATING ||
-      BookmarksMenuButton.status != expectedStatus) {
+  let expectedStatus = aValue ? BookmarkingUI.STATUS_STARRED
+                              : BookmarkingUI.STATUS_UNSTARRED;
+  if (BookmarkingUI.status == BookmarkingUI.STATUS_UPDATING ||
+      BookmarkingUI.status != expectedStatus) {
     info("Waiting for star button change.");
     setTimeout(waitForStarChange, 50, aValue, aCallback);
     return;
   }
   aCallback();
 }
 
 function onStarred() {
-  is(BookmarksMenuButton.status, BookmarksMenuButton.STATUS_STARRED,
+  is(BookmarkingUI.status, BookmarkingUI.STATUS_STARRED,
      "star button indicates that the page is bookmarked");
 
   let uri = makeURI(testURL);
   let tagTxn = new PlacesTagURITransaction(uri, [testTag]);
   PlacesUtils.transactionManager.doTransaction(tagTxn);
 
   StarUI.panel.addEventListener("popupshown", onPanelShown, false);
-  BookmarksMenuButton.star.click();
+  BookmarkingUI.star.click();
 }
 
 function onPanelShown(aEvent) {
   if (aEvent.target == StarUI.panel) {
     StarUI.panel.removeEventListener("popupshown", arguments.callee, false);
     let tagsField = document.getElementById("editBMPanel_tagsField");
     ok(tagsField.value == testTag, "tags field value was set");
     tagsField.focus();
@@ -88,15 +88,15 @@ function waitForClearHistory(aCallback)
 
 function onPanelHidden(aEvent) {
   if (aEvent.target == StarUI.panel) {
     StarUI.panel.removeEventListener("popuphidden", arguments.callee, false);
 
     executeSoon(function() {
       ok(!PlacesUtils.bookmarks.isBookmarked(makeURI(testURL)),
          "the bookmark for the test url has been removed");
-      is(BookmarksMenuButton.status, BookmarksMenuButton.STATUS_UNSTARRED,
+      is(BookmarkingUI.status, BookmarkingUI.STATUS_UNSTARRED,
          "star button indicates that the bookmark has been removed");
       gBrowser.removeCurrentTab();
       waitForClearHistory(finish);
     });
   }
 }
--- a/browser/base/content/test/browser_bug624734.js
+++ b/browser/base/content/test/browser_bug624734.js
@@ -6,18 +6,18 @@
 
 function test() {
   waitForExplicitFinish();
 
   let tab = gBrowser.selectedTab = gBrowser.addTab();
   tab.linkedBrowser.addEventListener("load", (function(event) {
     tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
 
-    is(BookmarksMenuButton.button.getAttribute("tooltiptext"),
-       BookmarksMenuButton._unstarredTooltip,
+    is(BookmarkingUI.star.getAttribute("tooltiptext"),
+       BookmarkingUI._unstarredTooltip,
        "Star icon should have the unstarred tooltip text");
   
     gBrowser.removeCurrentTab();
     finish();
   }), true);
 
   tab.linkedBrowser.loadURI("http://example.com/browser/browser/base/content/test/dummy_page.html");
 }
--- a/browser/base/content/test/browser_sanitize-timespans.js
+++ b/browser/base/content/test/browser_sanitize-timespans.js
@@ -1,29 +1,67 @@
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+
 // Bug 453440 - Test the timespan-based logic of the sanitizer code
 var now_uSec = Date.now() * 1000;
 
 const dm = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
-const formhist = Cc["@mozilla.org/satchel/form-history;1"].getService(Ci.nsIFormHistory2);
 
 const kUsecPerMin = 60 * 1000000;
 
 let tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
                                            .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
 let Sanitizer = tempScope.Sanitizer;
 
+let FormHistory = (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
+
+function promiseFormHistoryRemoved() {
+  let deferred = Promise.defer();
+  Services.obs.addObserver(function onfh() {
+    Services.obs.removeObserver(onfh, "satchel-storage-changed", false);
+    deferred.resolve();
+  }, "satchel-storage-changed", false);
+  return deferred.promise;
+}
+
 function test() {
   waitForExplicitFinish();
 
-  setupDownloads();
-  setupFormHistory();
-  setupHistory(function() {
-    Task.spawn(onHistoryReady).then(finish);
-  });
+  Task.spawn(function() {
+    setupDownloads();
+    yield setupFormHistory();
+    yield setupHistory();
+    yield onHistoryReady();
+  }).then(finish);
+}
+
+function countEntries(name, message, check) {
+  let deferred = Promise.defer();
+
+  var obj = {};
+  if (name !== null)
+    obj.fieldname = name;
+
+  let count;
+  FormHistory.count(obj, { handleResult: function (result) count = result,
+                           handleError: function (error) {
+                             do_throw("Error occurred searching form history: " + error);
+                             deferred.reject(error)
+                           },
+                           handleCompletion: function (reason) {
+                             if (!reason) {
+                               check(count, message);
+                               deferred.resolve();
+                             }
+                           },
+                         });
+
+  return deferred.promise;
 }
 
 function onHistoryReady() {
   var hoursSinceMidnight = new Date().getHours();
   var minutesSinceMidnight = hoursSinceMidnight * 60 + new Date().getMinutes();
 
   // Should test cookies here, but nsICookieManager/nsICookieService
   // doesn't let us fake creation times.  bug 463127
@@ -42,16 +80,18 @@ function onHistoryReady() {
   itemPrefs.setBoolPref("sessions", false);
   itemPrefs.setBoolPref("siteSettings", false);
 
   // Clear 10 minutes ago
   s.range = [now_uSec - 10*60*1000000, now_uSec];
   s.sanitize();
   s.range = null;
 
+  yield promiseFormHistoryRemoved();
+
   ok(!(yield promiseIsURIVisited(makeURI("http://10minutes.com"))),
      "Pretend visit to 10minutes.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://1hour.com"))),
      "Pretend visit to 1hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
      "Pretend visit to 1hour10minutes.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://2hour.com"))),
      "Pretend visit to 2hour.com should should still exist");
@@ -63,26 +103,29 @@ function onHistoryReady() {
      "Pretend visit to 4hour10minutes.com should should still exist");
   if (minutesSinceMidnight > 10) {
     ok((yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should still exist");
   }
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
     "Pretend visit to before-today.com should still exist");
 
-  ok(!formhist.nameExists("10minutes"), "10minutes form entry should be deleted");
-  ok(formhist.nameExists("1hour"), "1hour form entry should still exist");
-  ok(formhist.nameExists("1hour10minutes"), "1hour10minutes form entry should still exist");
-  ok(formhist.nameExists("2hour"), "2hour form entry should still exist");
-  ok(formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should still exist");
-  ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
-  ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
+  let checkZero = function(num, message) { is(num, 0, message); }
+  let checkOne = function(num, message) { is(num, 1, message); }
+
+  yield countEntries("10minutes", "10minutes form entry should be deleted", checkZero);
+  yield countEntries("1hour", "1hour form entry should still exist", checkOne);
+  yield countEntries("1hour10minutes", "1hour10minutes form entry should still exist", checkOne);
+  yield countEntries("2hour", "2hour form entry should still exist", checkOne);
+  yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
+  yield countEntries("4hour", "4hour form entry should still exist", checkOne);
+  yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (minutesSinceMidnight > 10)
-    ok(formhist.nameExists("today"), "today form entry should still exist");
-  ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
+    yield countEntries("today", "today form entry should still exist", checkOne);
+  yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
   ok(!downloadExists(5555555), "10 minute download should now be deleted");
   ok(downloadExists(5555551), "<1 hour download should still be present");
   ok(downloadExists(5555556), "1 hour 10 minute download should still be present");
   ok(downloadExists(5555550), "Year old download should still be present");
   ok(downloadExists(5555552), "<2 hour old download should still be present");
   ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
   ok(downloadExists(5555553), "<4 hour old download should still be present");
@@ -90,16 +133,18 @@ function onHistoryReady() {
 
   if (minutesSinceMidnight > 10)
     ok(downloadExists(5555554), "'Today' download should still be present");
 
   // Clear 1 hour
   Sanitizer.prefs.setIntPref("timeSpan", 1);
   s.sanitize();
 
+  yield promiseFormHistoryRemoved();
+
   ok(!(yield promiseIsURIVisited(makeURI("http://1hour.com"))),
      "Pretend visit to 1hour.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
      "Pretend visit to 1hour10minutes.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://2hour.com"))),
      "Pretend visit to 2hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
      "Pretend visit to 2hour10minutes.com should should still exist");
@@ -109,25 +154,25 @@ function onHistoryReady() {
      "Pretend visit to 4hour10minutes.com should should still exist");
   if (hoursSinceMidnight > 1) {
     ok((yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should still exist");
   }
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
     "Pretend visit to before-today.com should still exist");
 
-  ok(!formhist.nameExists("1hour"), "1hour form entry should be deleted");
-  ok(formhist.nameExists("1hour10minutes"), "1hour10minutes form entry should still exist");
-  ok(formhist.nameExists("2hour"), "2hour form entry should still exist");
-  ok(formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should still exist");
-  ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
-  ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
+  yield countEntries("1hour", "1hour form entry should be deleted", checkZero);
+  yield countEntries("1hour10minutes", "1hour10minutes form entry should still exist", checkOne);
+  yield countEntries("2hour", "2hour form entry should still exist", checkOne);
+  yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
+  yield countEntries("4hour", "4hour form entry should still exist", checkOne);
+  yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (hoursSinceMidnight > 1)
-    ok(formhist.nameExists("today"), "today form entry should still exist");
-  ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
+    yield countEntries("today", "today form entry should still exist", checkOne);
+  yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
   ok(!downloadExists(5555551), "<1 hour download should now be deleted");
   ok(downloadExists(5555556), "1 hour 10 minute download should still be present");
   ok(downloadExists(5555550), "Year old download should still be present");
   ok(downloadExists(5555552), "<2 hour old download should still be present");
   ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
   ok(downloadExists(5555553), "<4 hour old download should still be present");
   ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
@@ -135,16 +180,18 @@ function onHistoryReady() {
   if (hoursSinceMidnight > 1)
     ok(downloadExists(5555554), "'Today' download should still be present");
   
   // Clear 1 hour 10 minutes
   s.range = [now_uSec - 70*60*1000000, now_uSec];
   s.sanitize();
   s.range = null;
 
+  yield promiseFormHistoryRemoved();
+
   ok(!(yield promiseIsURIVisited(makeURI("http://1hour10minutes.com"))),
      "Pretend visit to 1hour10minutes.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://2hour.com"))),
      "Pretend visit to 2hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
      "Pretend visit to 2hour10minutes.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://4hour.com"))),
      "Pretend visit to 4hour.com should should still exist");
@@ -152,188 +199,202 @@ function onHistoryReady() {
      "Pretend visit to 4hour10minutes.com should should still exist");
   if (minutesSinceMidnight > 70) {
     ok((yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should still exist");
   }
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
     "Pretend visit to before-today.com should still exist");
 
-  ok(!formhist.nameExists("1hour10minutes"), "1hour10minutes form entry should be deleted");
-  ok(formhist.nameExists("2hour"), "2hour form entry should still exist");
-  ok(formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should still exist");
-  ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
-  ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
+  yield countEntries("1hour10minutes", "1hour10minutes form entry should be deleted", checkZero);
+  yield countEntries("2hour", "2hour form entry should still exist", checkOne);
+  yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
+  yield countEntries("4hour", "4hour form entry should still exist", checkOne);
+  yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (minutesSinceMidnight > 70)
-    ok(formhist.nameExists("today"), "today form entry should still exist");
-  ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
+    yield countEntries("today", "today form entry should still exist", checkOne);
+  yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
   ok(!downloadExists(5555556), "1 hour 10 minute old download should now be deleted");
   ok(downloadExists(5555550), "Year old download should still be present");
   ok(downloadExists(5555552), "<2 hour old download should still be present");
   ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
   ok(downloadExists(5555553), "<4 hour old download should still be present");
   ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
   if (minutesSinceMidnight > 70)
     ok(downloadExists(5555554), "'Today' download should still be present");
 
   // Clear 2 hours
   Sanitizer.prefs.setIntPref("timeSpan", 2);
   s.sanitize();
 
+  yield promiseFormHistoryRemoved();
+
   ok(!(yield promiseIsURIVisited(makeURI("http://2hour.com"))),
      "Pretend visit to 2hour.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
      "Pretend visit to 2hour10minutes.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://4hour.com"))),
      "Pretend visit to 4hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
      "Pretend visit to 4hour10minutes.com should should still exist");
   if (hoursSinceMidnight > 2) {
     ok((yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should still exist");
   }
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
     "Pretend visit to before-today.com should still exist");
 
-  ok(!formhist.nameExists("2hour"), "2hour form entry should be deleted");
-  ok(formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should still exist");
-  ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
-  ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
+  yield countEntries("2hour", "2hour form entry should be deleted", checkZero);
+  yield countEntries("2hour10minutes", "2hour10minutes form entry should still exist", checkOne);
+  yield countEntries("4hour", "4hour form entry should still exist", checkOne);
+  yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (hoursSinceMidnight > 2)
-    ok(formhist.nameExists("today"), "today form entry should still exist");
-  ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
+    yield countEntries("today", "today form entry should still exist", checkOne);
+  yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
-  ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
   ok(!downloadExists(5555552), "<2 hour old download should now be deleted");
   ok(downloadExists(5555550), "Year old download should still be present");
   ok(downloadExists(5555557), "2 hour 10 minute download should still be present");
   ok(downloadExists(5555553), "<4 hour old download should still be present");
   ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
   if (hoursSinceMidnight > 2)
     ok(downloadExists(5555554), "'Today' download should still be present");
   
   // Clear 2 hours 10 minutes
   s.range = [now_uSec - 130*60*1000000, now_uSec];
   s.sanitize();
   s.range = null;
 
+  yield promiseFormHistoryRemoved();
+
   ok(!(yield promiseIsURIVisited(makeURI("http://2hour10minutes.com"))),
      "Pretend visit to 2hour10minutes.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://4hour.com"))),
      "Pretend visit to 4hour.com should should still exist");
   ok((yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
      "Pretend visit to 4hour10minutes.com should should still exist");
   if (minutesSinceMidnight > 130) {
     ok((yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should still exist");
   }
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
     "Pretend visit to before-today.com should still exist");
 
-  ok(!formhist.nameExists("2hour10minutes"), "2hour10minutes form entry should be deleted");
-  ok(formhist.nameExists("4hour"), "4hour form entry should still exist");
-  ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
+  yield countEntries("2hour10minutes", "2hour10minutes form entry should be deleted", checkZero);
+  yield countEntries("4hour", "4hour form entry should still exist", checkOne);
+  yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (minutesSinceMidnight > 130)
-    ok(formhist.nameExists("today"), "today form entry should still exist");
-  ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
+    yield countEntries("today", "today form entry should still exist", checkOne);
+  yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
   ok(!downloadExists(5555557), "2 hour 10 minute old download should now be deleted");
   ok(downloadExists(5555553), "<4 hour old download should still be present");
   ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
   ok(downloadExists(5555550), "Year old download should still be present");
   if (minutesSinceMidnight > 130)
     ok(downloadExists(5555554), "'Today' download should still be present");
 
   // Clear 4 hours
   Sanitizer.prefs.setIntPref("timeSpan", 3);
   s.sanitize();
 
+  yield promiseFormHistoryRemoved();
+
   ok(!(yield promiseIsURIVisited(makeURI("http://4hour.com"))),
      "Pretend visit to 4hour.com should now be deleted");
   ok((yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
      "Pretend visit to 4hour10minutes.com should should still exist");
   if (hoursSinceMidnight > 4) {
     ok((yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should still exist");
   }
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
     "Pretend visit to before-today.com should still exist");
 
-  ok(!formhist.nameExists("4hour"), "4hour form entry should be deleted");
-  ok(formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should still exist");
+  yield countEntries("4hour", "4hour form entry should be deleted", checkZero);
+  yield countEntries("4hour10minutes", "4hour10minutes form entry should still exist", checkOne);
   if (hoursSinceMidnight > 4)
-    ok(formhist.nameExists("today"), "today form entry should still exist");
-  ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
+    yield countEntries("today", "today form entry should still exist", checkOne);
+  yield countEntries("b4today", "b4today form entry should still exist", checkOne);
 
   ok(!downloadExists(5555553), "<4 hour old download should now be deleted");
   ok(downloadExists(5555558), "4 hour 10 minute download should still be present");
   ok(downloadExists(5555550), "Year old download should still be present");
   if (hoursSinceMidnight > 4)
     ok(downloadExists(5555554), "'Today' download should still be present");
 
   // Clear 4 hours 10 minutes
   s.range = [now_uSec - 250*60*1000000, now_uSec];
   s.sanitize();
   s.range = null;
 
+  yield promiseFormHistoryRemoved();
+
   ok(!(yield promiseIsURIVisited(makeURI("http://4hour10minutes.com"))),
      "Pretend visit to 4hour10minutes.com should now be deleted");
   if (minutesSinceMidnight > 250) {
     ok((yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should still exist");
   }
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
     "Pretend visit to before-today.com should still exist");
-  
-  ok(!formhist.nameExists("4hour10minutes"), "4hour10minutes form entry should be deleted");
+
+  yield countEntries("4hour10minutes", "4hour10minutes form entry should be deleted", checkZero);
   if (minutesSinceMidnight > 250)
-    ok(formhist.nameExists("today"), "today form entry should still exist");
-  ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
-
+    yield countEntries("today", "today form entry should still exist", checkOne);
+  yield countEntries("b4today", "b4today form entry should still exist", checkOne);
+  
   ok(!downloadExists(5555558), "4 hour 10 minute download should now be deleted");
   ok(downloadExists(5555550), "Year old download should still be present");
   if (minutesSinceMidnight > 250)
     ok(downloadExists(5555554), "'Today' download should still be present");
 
   // Clear Today
   Sanitizer.prefs.setIntPref("timeSpan", 4);
   s.sanitize();
 
+  yield promiseFormHistoryRemoved();
+
   // Be careful.  If we add our objectss just before midnight, and sanitize
   // runs immediately after, they won't be expired.  This is expected, but
   // we should not test in that case.  We cannot just test for opposite
   // condition because we could cross midnight just one moment after we
   // cache our time, then we would have an even worse random failure.
   var today = isToday(new Date(now_uSec/1000));
   if (today) {
     ok(!(yield promiseIsURIVisited(makeURI("http://today.com"))),
        "Pretend visit to today.com should now be deleted");
-    ok(!formhist.nameExists("today"), "today form entry should be deleted");
+
+    yield countEntries("today", "today form entry should be deleted", checkZero);
     ok(!downloadExists(5555554), "'Today' download should now be deleted");
   }
 
   ok((yield promiseIsURIVisited(makeURI("http://before-today.com"))),
      "Pretend visit to before-today.com should still exist");
-  ok(formhist.nameExists("b4today"), "b4today form entry should still exist");
+  yield countEntries("b4today", "b4today form entry should still exist", checkOne);
   ok(downloadExists(5555550), "Year old download should still be present");
 
   // Choose everything
   Sanitizer.prefs.setIntPref("timeSpan", 0);
   s.sanitize();
 
+  yield promiseFormHistoryRemoved();
+
   ok(!(yield promiseIsURIVisited(makeURI("http://before-today.com"))),
      "Pretend visit to before-today.com should now be deleted");
 
-  ok(!formhist.nameExists("b4today"), "b4today form entry should be deleted");
+  yield countEntries("b4today", "b4today form entry should be deleted", checkZero);
 
   ok(!downloadExists(5555550), "Year old download should now be deleted");
 }
 
-function setupHistory(aCallback) {
+function setupHistory() {
+  let deferred = Promise.defer();
+
   let places = [];
 
   function addPlace(aURI, aTitle, aVisitDate) {
     places.push({
       uri: aURI,
       title: aTitle,
       visits: [{
         visitDate: aVisitDate,
@@ -358,83 +419,150 @@ function setupHistory(aCallback) {
 
   let lastYear = new Date();
   lastYear.setFullYear(lastYear.getFullYear() - 1);
   addPlace(makeURI("http://before-today.com/"), "Before Today", lastYear.getTime() * 1000);
 
   PlacesUtils.asyncHistory.updatePlaces(places, {
     handleError: function () ok(false, "Unexpected error in adding visit."),
     handleResult: function () { },
-    handleCompletion: function () aCallback()
+    handleCompletion: function () deferred.resolve()
   });
+
+  return deferred.promise;
 }
 
 function setupFormHistory() {
-  // Make sure we've got a clean DB to start with.
-  formhist.removeAllEntries();
+
+  function searchEntries(terms, params) {
+    let deferred = Promise.defer();
+
+    let results = [];
+    FormHistory.search(terms, params, { handleResult: function (result) results.push(result),
+                                        handleError: function (error) {
+                                          do_throw("Error occurred searching form history: " + error);
+                                          deferred.reject(error);
+                                        },
+                                        handleCompletion: function (reason) { deferred.resolve(results); }
+                                      });
+    return deferred.promise;
+  }
+
+  function update(changes)
+  {
+    let deferred = Promise.defer();
+    FormHistory.update(changes, { handleError: function (error) {
+                                    do_throw("Error occurred searching form history: " + error);
+                                    deferred.reject(error);
+                                  },
+                                  handleCompletion: function (reason) { deferred.resolve(); }
+                                });
+    return deferred.promise;
+  }
 
-  // Add the entries we'll be testing.
-  formhist.addEntry("10minutes", "10m");
-  formhist.addEntry("1hour", "1h");
-  formhist.addEntry("1hour10minutes", "1h10m");
-  formhist.addEntry("2hour", "2h");
-  formhist.addEntry("2hour10minutes", "2h10m");
-  formhist.addEntry("4hour", "4h");
-  formhist.addEntry("4hour10minutes", "4h10m");
-  formhist.addEntry("today", "1d");
-  formhist.addEntry("b4today", "1y");
+  // Make sure we've got a clean DB to start with, then add the entries we'll be testing.
+  yield update(
+    [{
+        op: "remove"
+     },
+     {
+        op : "add",
+        fieldname : "10minutes",
+        value : "10m"
+      }, {
+        op : "add",
+        fieldname : "1hour",
+        value : "1h"
+      }, {
+        op : "add",
+        fieldname : "1hour10minutes",
+        value : "1h10m"
+      }, {
+        op : "add",
+        fieldname : "2hour",
+        value : "2h"
+      }, {
+        op : "add",
+        fieldname : "2hour10minutes",
+        value : "2h10m"
+      }, {
+        op : "add",
+        fieldname : "4hour",
+        value : "4h"
+      }, {
+        op : "add",
+        fieldname : "4hour10minutes",
+        value : "4h10m"
+      }, {
+        op : "add",
+        fieldname : "today",
+        value : "1d"
+      }, {
+        op : "add",
+        fieldname : "b4today",
+        value : "1y"
+      }]);
 
   // Artifically age the entries to the proper vintage.
-  let db = formhist.DBConnection;
   let timestamp = now_uSec - 10 * kUsecPerMin;
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = '10minutes'");
+  let results = yield searchEntries(["guid"], { fieldname: "10minutes" });
+  yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
+
   timestamp = now_uSec - 45 * kUsecPerMin;
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = '1hour'");
+  results = yield searchEntries(["guid"], { fieldname: "1hour" });
+  yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
+
   timestamp = now_uSec - 70 * kUsecPerMin;
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = '1hour10minutes'");
+  results = yield searchEntries(["guid"], { fieldname: "1hour10minutes" });
+  yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
+
   timestamp = now_uSec - 90 * kUsecPerMin;
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = '2hour'");
+  results = yield searchEntries(["guid"], { fieldname: "2hour" });
+  yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
+
   timestamp = now_uSec - 130 * kUsecPerMin;
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = '2hour10minutes'");
+  results = yield searchEntries(["guid"], { fieldname: "2hour10minutes" });
+  yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
+
   timestamp = now_uSec - 180 * kUsecPerMin;
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = '4hour'");
+  results = yield searchEntries(["guid"], { fieldname: "4hour" });
+  yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
+
   timestamp = now_uSec - 250 * kUsecPerMin;
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = '4hour10minutes'");
+  results = yield searchEntries(["guid"], { fieldname: "4hour10minutes" });
+  yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
 
   let today = new Date();
   today.setHours(0);
   today.setMinutes(0);
   today.setSeconds(1);
   timestamp = today.getTime() * 1000;
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = 'today'");
+  results = yield searchEntries(["guid"], { fieldname: "today" });
+  yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
 
   let lastYear = new Date();
   lastYear.setFullYear(lastYear.getFullYear() - 1);
   timestamp = lastYear.getTime() * 1000;
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = 'b4today'");
+  results = yield searchEntries(["guid"], { fieldname: "b4today" });
+  yield update({ op: "update", firstUsed: timestamp, guid: results[0].guid });
+
+  var checks = 0;
+  let checkOne = function(num, message) { is(num, 1, message); checks++; }
 
   // Sanity check.
-  ok(formhist.nameExists("10minutes"), "Checking for 10minutes form history entry creation");
-  ok(formhist.nameExists("1hour"), "Checking for 1hour form history entry creation");
-  ok(formhist.nameExists("1hour10minutes"), "Checking for 1hour10minutes form history entry creation");
-  ok(formhist.nameExists("2hour"), "Checking for 2hour form history entry creation");
-  ok(formhist.nameExists("2hour10minutes"), "Checking for 2hour10minutes form history entry creation");
-  ok(formhist.nameExists("4hour"), "Checking for 4hour form history entry creation");
-  ok(formhist.nameExists("4hour10minutes"), "Checking for 4hour10minutes form history entry creation");
-  ok(formhist.nameExists("today"), "Checking for today form history entry creation");
-  ok(formhist.nameExists("b4today"), "Checking for b4today form history entry creation");
+  yield countEntries("10minutes", "Checking for 10minutes form history entry creation", checkOne);
+  yield countEntries("1hour", "Checking for 1hour form history entry creation", checkOne);
+  yield countEntries("1hour10minutes", "Checking for 1hour10minutes form history entry creation", checkOne);
+  yield countEntries("2hour", "Checking for 2hour form history entry creation", checkOne);
+  yield countEntries("2hour10minutes", "Checking for 2hour10minutes form history entry creation", checkOne);
+  yield countEntries("4hour", "Checking for 4hour form history entry creation", checkOne);
+  yield countEntries("4hour10minutes", "Checking for 4hour10minutes form history entry creation", checkOne);
+  yield countEntries("today", "Checking for today form history entry creation", checkOne);
+  yield countEntries("b4today", "Checking for b4today form history entry creation", checkOne);
+  is(checks, 9, "9 checks made");
 }
 
 function setupDownloads() {
 
   // Add 10-minutes download to DB
   let data = {
     id:   "5555555",
     name: "fakefile-10-minutes",
--- a/browser/base/content/test/browser_sanitizeDialog.js
+++ b/browser/base/content/test/browser_sanitizeDialog.js
@@ -12,28 +12,33 @@
  * browser/base/content/test/browser_sanitize-timespans.js does that.  This
  * test checks the UI of the dialog and makes sure it's correctly connected to
  * the sanitize timespan code.
  *
  * Some of this code, especially the history creation parts, was taken from
  * browser/base/content/test/browser_sanitize-timespans.js.
  */
 
+Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
+                                  "resource://gre/modules/FormHistory.jsm");
+
 let tempScope = {};
 Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci.mozIJSSubScriptLoader)
                                            .loadSubScript("chrome://browser/content/sanitize.js", tempScope);
 let Sanitizer = tempScope.Sanitizer;
 
 const dm = Cc["@mozilla.org/download-manager;1"].
            getService(Ci.nsIDownloadManager);
-const formhist = Cc["@mozilla.org/satchel/form-history;1"].
-                 getService(Ci.nsIFormHistory2);
 
 const kUsecPerMin = 60 * 1000000;
 
+let formEntries;
+
 // Add tests here.  Each is a function that's called by doNextTest().
 var gAllTests = [
 
   /**
    * Initializes the dialog to its default state.
    */
   function () {
     let wh = new WindowHelper();
@@ -75,17 +80,17 @@ var gAllTests = [
 
         // Hide details
         this.toggleDetails();
         this.checkDetails(false);
         this.cancelDialog();
       };
       wh.onunload = function () {
         yield promiseHistoryClearedState(uris, false);
-        blankSlate();
+        yield blankSlate();
         yield promiseHistoryClearedState(uris, true);
       };
       wh.open();
     });
   },
 
   /**
    * Ensures that the combined history-downloads checkbox clears both history
@@ -143,25 +148,42 @@ var gAllTests = [
         yield promiseHistoryClearedState(uris, true);
         ensureDownloadsClearedState(downloadIDs, true);
 
         // Visits and downloads > 1 hour should still exist.
         yield promiseHistoryClearedState(olderURIs, false);
         ensureDownloadsClearedState(olderDownloadIDs, false);
 
         // OK, done, cleanup after ourselves.
-        blankSlate();
+        yield blankSlate();
         yield promiseHistoryClearedState(olderURIs, true);
         ensureDownloadsClearedState(olderDownloadIDs, true);
       };
       wh.open();
     });
   },
 
   /**
+   * Add form history entries for the next test.
+   */
+  function () {
+    formEntries = [];
+
+    let iter = function() {
+      for (let i = 0; i < 5; i++) {
+        formEntries.push(addFormEntryWithMinutesAgo(iter, i));
+        yield;
+      }
+      doNextTest();
+    }();
+
+    iter.next();
+  },
+
+  /**
    * Ensures that the combined history-downloads checkbox removes neither
    * history visits nor downloads when not checked.
    */
   function () {
     // Add history, downloads, form entries (within the past hour).
     let uris = [];
     let places = [];
     let pURI;
@@ -171,20 +193,16 @@ var gAllTests = [
       uris.push(pURI);
     }
 
     addVisits(places, function() {
       let downloadIDs = [];
       for (let i = 0; i < 5; i++) {
         downloadIDs.push(addDownloadWithMinutesAgo(i));
       }
-      let formEntries = [];
-      for (let i = 0; i < 5; i++) {
-        formEntries.push(addFormEntryWithMinutesAgo(i));
-      }
 
       let wh = new WindowHelper();
       wh.onload = function () {
         is(this.isWarningPanelVisible(), false,
            "Warning panel should be hidden after previously accepting dialog " +
            "with a predefined timespan");
         this.selectDuration(Sanitizer.TIMESPAN_HOUR);
 
@@ -202,20 +220,24 @@ var gAllTests = [
         boolPrefIs("cpd.downloads", false,
                    "downloads pref should be false after accepting dialog with " +
                    "history checkbox unchecked");
       };
       wh.onunload = function () {
         // Of the three only form entries should be cleared.
         yield promiseHistoryClearedState(uris, false);
         ensureDownloadsClearedState(downloadIDs, false);
-        ensureFormEntriesClearedState(formEntries, true);
+
+        formEntries.forEach(function (entry) {
+          let exists = yield formNameExists(entry);
+          is(exists, false, "form entry " + entry + " should no longer exist");
+        });
 
         // OK, done, cleanup after ourselves.
-        blankSlate();
+        yield blankSlate();
         yield promiseHistoryClearedState(uris, true);
         ensureDownloadsClearedState(downloadIDs, true);
       };
       wh.open();
     });
   },
 
   /**
@@ -297,26 +319,38 @@ var gAllTests = [
       wh.onunload = function () {
         yield promiseHistoryClearedState(uris, true);
       };
       wh.open();
     });
   },
 
   /**
+   * Add form history entry for the next test.
+   */
+  function () {
+    let iter = function() {
+      formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ];
+      yield;
+      doNextTest();
+    }();
+
+    iter.next();
+  },
+
+  /**
    * The next three tests checks that when a certain history item cannot be
    * cleared then the checkbox should be both disabled and unchecked.
    * In addition, we ensure that this behavior does not modify the preferences.
    */
   function () {
     // Add history.
     let pURI = makeURI("http://" + 10 + "-minutes-ago.com/");
     addVisits({uri: pURI, visitDate: visitTimeForMinutesAgo(10)}, function() {
       let uris = [ pURI ];
-      let formEntries = [ addFormEntryWithMinutesAgo(10) ];
 
       let wh = new WindowHelper();
       wh.onload = function() {
         // Check that the relevant checkboxes are enabled
         var cb = this.win.document.querySelectorAll(
                    "#itemList > [preference='privacy.cpd.formdata']");
         ok(cb.length == 1 && !cb[0].disabled, "There is formdata, checkbox to " +
            "clear formdata should be enabled.");
@@ -326,17 +360,19 @@ var gAllTests = [
         ok(cb.length == 1 && !cb[0].disabled, "There is history, checkbox to " +
            "clear history should be enabled.");
 
         this.checkAllCheckboxes();
         this.acceptDialog();
       };
       wh.onunload = function () {
         yield promiseHistoryClearedState(uris, true);
-        ensureFormEntriesClearedState(formEntries, true);
+
+        let exists = yield formNameExists(formEntries[0]);
+        is(exists, false, "form entry " + formEntries[0] + " should no longer exist");
       };
       wh.open();
     });
   },
   function () {
     let wh = new WindowHelper();
     wh.onload = function() {
       boolPrefIs("cpd.history", true,
@@ -360,35 +396,48 @@ var gAllTests = [
       ok(cb.length == 1 && !cb[0].disabled && cb[0].checked,
          "There is no history, but history checkbox should always be enabled " +
          "and will be checked from previous preference.");
 
       this.acceptDialog();
     }
     wh.open();
   },
+
+  /**
+   * Add form history entry for the next test.
+   */
   function () {
-    let formEntries = [ addFormEntryWithMinutesAgo(10) ];
+    let iter = function() {
+      formEntries = [ addFormEntryWithMinutesAgo(iter, 10) ];
+      yield;
+      doNextTest();
+    }();
 
+    iter.next();
+  },
+
+  function () {
     let wh = new WindowHelper();
     wh.onload = function() {
       boolPrefIs("cpd.formdata", true,
                  "formdata pref should persist previous value after accepting " +
                  "dialog where you could not clear formdata.");
 
       var cb = this.win.document.querySelectorAll(
                  "#itemList > [preference='privacy.cpd.formdata']");
       ok(cb.length == 1 && !cb[0].disabled && cb[0].checked,
          "There exists formEntries so the checkbox should be in sync with " +
          "the pref.");
 
       this.acceptDialog();
     };
     wh.onunload = function () {
-      ensureFormEntriesClearedState(formEntries, true);
+      let exists = yield formNameExists(formEntries[0]);
+      is(exists, false, "form entry " + formEntries[0] + " should no longer exist");
     };
     wh.open();
   },
 
 
   /**
    * These next six tests together ensure that toggling details persists
    * across dialog openings.
@@ -734,17 +783,19 @@ WindowHelper.prototype = {
   isWarningPanelVisible: function () {
     return !this.getWarningPanel().hidden;
   },
 
   /**
    * Opens the clear recent history dialog.  Before calling this, set
    * this.onload to a function to execute onload.  It should close the dialog
    * when done so that the tests may continue.  Set this.onunload to a function
-   * to execute onunload.  this.onunload is optional.
+   * to execute onunload.  this.onunload is optional. If it returns true, the
+   * caller is expected to call waitForAsyncUpdates at some point; if false is
+   * returned, waitForAsyncUpdates is called automatically.
    */
   open: function () {
     let wh = this;
 
     function windowObserver(aSubject, aTopic, aData) {
       if (aTopic != "domwindowopened")
         return;
 
@@ -886,38 +937,69 @@ function addDownloadWithMinutesAgo(aMinu
 }
 
 /**
  * Adds a form entry to history.
  *
  * @param aMinutesAgo
  *        The entry will be added this many minutes ago
  */
-function addFormEntryWithMinutesAgo(aMinutesAgo) {
+function addFormEntryWithMinutesAgo(then, aMinutesAgo) {
   let name = aMinutesAgo + "-minutes-ago";
-  formhist.addEntry(name, "dummy");
 
   // Artifically age the entry to the proper vintage.
-  let db = formhist.DBConnection;
   let timestamp = now_uSec - (aMinutesAgo * kUsecPerMin);
-  db.executeSimpleSQL("UPDATE moz_formhistory SET firstUsed = " +
-                      timestamp +  " WHERE fieldname = '" + name + "'");
+
+  FormHistory.update({ op: "add", fieldname: name, value: "dummy", firstUsed: timestamp },
+                     { handleError: function (error) {
+                         do_throw("Error occurred updating form history: " + error);
+                       },
+                       handleCompletion: function (reason) { then.next(); }
+                     });
+  return name;
+}
 
-  is(formhist.nameExists(name), true,
-     "Sanity check: form entry " + name + " should exist after creating it");
-  return name;
+/**
+ * Checks if a form entry exists.
+ */
+function formNameExists(name)
+{
+  let deferred = Promise.defer();
+
+  let count = 0;
+  FormHistory.count({ fieldname: name },
+                    { handleResult: function (result) count = result,
+                      handleError: function (error) {
+                        do_throw("Error occurred searching form history: " + error);
+                        deferred.reject(error);
+                      },
+                      handleCompletion: function (reason) {
+                          if (!reason) deferred.resolve(count);
+                      }
+                    });
+
+  return deferred.promise;
 }
 
 /**
  * Removes all history visits, downloads, and form entries.
  */
 function blankSlate() {
   PlacesUtils.bhistory.removeAllPages();
   dm.cleanUp();
-  formhist.removeAllEntries();
+
+  let deferred = Promise.defer();
+  FormHistory.update({ op: "remove" },
+                     { handleError: function (error) {
+                         do_throw("Error occurred updating form history: " + error);
+                         deferred.reject(error);
+                       },
+                       handleCompletion: function (reason) { if (!reason) deferred.resolve(); }
+                     });
+  return deferred.promise;
 }
 
 /**
  * Ensures that the given pref is the expected value.
  *
  * @param aPrefName
  *        The pref's sub-branch under the privacy branch
  * @param aExpectedVal
@@ -978,32 +1060,16 @@ function ensureDownloadsClearedState(aDo
   let niceStr = aShouldBeCleared ? "no longer" : "still";
   aDownloadIDs.forEach(function (id) {
     is(downloadExists(id), !aShouldBeCleared,
        "download " + id + " should " + niceStr + " exist");
   });
 }
 
 /**
- * Ensures that the specified form entries are either cleared or not.
- *
- * @param aFormEntries
- *        Array of form entry names
- * @param aShouldBeCleared
- *        True if each form entry should be cleared, false otherwise
- */
-function ensureFormEntriesClearedState(aFormEntries, aShouldBeCleared) {
-  let niceStr = aShouldBeCleared ? "no longer" : "still";
-  aFormEntries.forEach(function (entry) {
-    is(formhist.nameExists(entry), !aShouldBeCleared,
-       "form entry " + entry + " should " + niceStr + " exist");
-  });
-}
-
-/**
  * Ensures that the given pref is the expected value.
  *
  * @param aPrefName
  *        The pref's sub-branch under the privacy branch
  * @param aExpectedVal
  *        The pref's expected value
  * @param aMsg
  *        Passed to is()
@@ -1021,13 +1087,13 @@ function intPrefIs(aPrefName, aExpectedV
 function visitTimeForMinutesAgo(aMinutesAgo) {
   return now_uSec - aMinutesAgo * kUsecPerMin;
 }
 
 ///////////////////////////////////////////////////////////////////////////////
 
 function test() {
   requestLongerTimeout(2);
+  waitForExplicitFinish();
   blankSlate();
-  waitForExplicitFinish();
   // Kick off all the tests in the gAllTests array.
   waitForAsyncUpdates(doNextTest);
 }
--- a/browser/base/content/test/browser_save_link-perwindowpb.js
+++ b/browser/base/content/test/browser_save_link-perwindowpb.js
@@ -5,21 +5,22 @@ var MockFilePicker = SpecialPowers.MockF
 MockFilePicker.init(window);
 
 let tempScope = {};
 Cu.import("resource://gre/modules/NetUtil.jsm", tempScope);
 let NetUtil = tempScope.NetUtil;
 
 // Trigger a save of a link in public mode, then trigger an identical save
 // in private mode and ensure that the second request is differentiated from
-// the first by checking the cookies that are sent.
-
+// the first by checking that cookies set by the first response are not sent
+// during the second request.
 function triggerSave(aWindow, aCallback) {
   var fileName;
   let testBrowser = aWindow.gBrowser.selectedBrowser;
+  // This page sets a cookie if and only if a cookie does not exist yet
   testBrowser.loadURI("http://mochi.test:8888/browser/browser/base/content/test/bug792517-2.html");
   testBrowser.addEventListener("pageshow", function pageShown(event) {
     if (event.target.location == "about:blank")
       return;
     testBrowser.removeEventListener("pageshow", pageShown, false);
 
     executeSoon(function () {
       aWindow.document.addEventListener("popupshown", function(e) contextMenuOpened(aWindow, e), false);
@@ -41,55 +42,44 @@ function triggerSave(aWindow, aCallback)
     MockFilePicker.displayDirectory = destDir;
     MockFilePicker.showCallback = function(fp) {
       fileName = fp.defaultString;
       destFile.append (fileName);
       MockFilePicker.returnFiles = [destFile];
       MockFilePicker.filterIndex = 1; // kSaveAsType_URL
     };
 
-    mockTransferCallback = function(a) {
-      onTransferComplete(aWindow, a, destFile, destDir);
+    mockTransferCallback = function(downloadSuccess) {
+      onTransferComplete(aWindow, downloadSuccess, destDir);
+      destDir.remove(true);
+      ok(!destDir.exists(), "Destination dir should be removed");
+      ok(!destFile.exists(), "Destination file should be removed");
       mockTransferCallback = function(){};
     }
 
     // Select "Save Link As" option from context menu
     var saveLinkCommand = aWindow.document.getElementById("context-savelink");
     saveLinkCommand.doCommand();
 
     event.target.hidePopup();
   }
 
-  function onTransferComplete(aWindow, downloadSuccess, destFile, destDir) {
+  function onTransferComplete(aWindow, downloadSuccess, destDir) {
     ok(downloadSuccess, "Link should have been downloaded successfully");
     aWindow.gBrowser.removeCurrentTab();
 
-    // Give the request a chance to finish
-    executeSoon(function() aCallback(destFile, destDir));
+    executeSoon(function() aCallback());
   }
 }
 
-function readFile(file, callback) {
-  let channel = NetUtil.newChannel(file);
-  channel.contentType = "application/javascript";
-
-  NetUtil.asyncFetch(channel, function(inputStream, status) {
-    ok(Components.isSuccessCode(status),
-       "file was read successfully");
-
-    let content = NetUtil.readInputStreamToString(inputStream,
-                                                  inputStream.available());
-    executeSoon(function() callback(content));
-  });
-}
-
 function test() {
   waitForExplicitFinish();
 
   var windowsToClose = [];
+  var gNumSet = 0;
   function testOnWindow(options, callback) {
     var win = OpenBrowserWindow(options);
     win.addEventListener("load", function onLoad() {
       win.removeEventListener("load", onLoad, false);
       windowsToClose.push(win);
       executeSoon(function() callback(win));
     }, false);
   }
@@ -97,32 +87,63 @@ function test() {
   mockTransferRegisterer.register();
 
   registerCleanupFunction(function () {
     mockTransferRegisterer.unregister();
     MockFilePicker.cleanup();
     windowsToClose.forEach(function(win) {
       win.close();
     });
+    Services.obs.removeObserver(observer, "http-on-modify-request");
+    Services.obs.removeObserver(observer, "http-on-examine-response");
   });
+ 
+  function observer(subject, topic, state) {
+    if (topic == "http-on-modify-request") {
+      onModifyRequest(subject);
+    } else if (topic == "http-on-examine-response") {
+      onExamineResponse(subject);
+    }
+  }
+
+  function onExamineResponse(subject) {
+    let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+    try {
+      let cookies = channel.getResponseHeader("set-cookie");
+      // From browser/base/content/test/bug792715.sjs, we receive a Set-Cookie
+      // header with foopy=1 when there are no cookies for that domain.
+      is(cookies, "foopy=1", "Cookie should be foopy=1");
+      gNumSet += 1;
+    } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { }
+  }
+
+  function onModifyRequest(subject) {
+    let channel = subject.QueryInterface(Ci.nsIHttpChannel);
+    try {
+      let cookies = channel.getRequestHeader("cookie");
+      // From browser/base/content/test/bug792715.sjs, we should never send a
+      // cookie because we are making only 2 requests: one in public mode, and
+      // one in private mode.
+      throw "We should never send a cookie in this test";
+    } catch (ex if ex.result == Cr.NS_ERROR_NOT_AVAILABLE) { }
+  }
+
+  Services.obs.addObserver(observer, "http-on-modify-request", false);
+  Services.obs.addObserver(observer, "http-on-examine-response", false);
 
   testOnWindow(undefined, function(win) {
-    triggerSave(win, function(destFile, destDir) {
-      readFile(destFile, function(content) {
-        is(content, "cookie-not-present", "no cookie should be sent");
-        destDir.remove(true);
+    // The first save from a regular window sets a cookie.
+    triggerSave(win, function() {
+      is(gNumSet, 1, "1 cookie should be set");
 
-        testOnWindow({private: true}, function(win) {
-          triggerSave(win, function(destFile, destDir) {
-            readFile(destFile, function(content) {
-              is(content, "cookie-not-present", "no cookie should be sent");
-              destDir.remove(true);
-              finish();
-            });
-          });
+      // The second save from a private window also sets a cookie.
+      testOnWindow({private: true}, function(win) {
+        triggerSave(win, function() {
+          is(gNumSet, 2, "2 cookies should be set");
+          finish();
         });
       });
     });
   });
 }
 
 Cc["@mozilla.org/moz/jssubscript-loader;1"]
   .getService(Ci.mozIJSSubScriptLoader)
--- a/browser/base/content/test/browser_typeAheadFind.js
+++ b/browser/base/content/test/browser_typeAheadFind.js
@@ -3,32 +3,26 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 let testWindow = null;
 
 function test() {
   waitForExplicitFinish();
 
   testWindow = OpenBrowserWindow();
-  testWindow.addEventListener("load", function() {
-    testWindow.removeEventListener("load", arguments.callee, false);
-    ok(true, "Load listener called");
-
-    executeSoon(function() {
-      let selectedBrowser = testWindow.gBrowser.selectedBrowser;
-      selectedBrowser.addEventListener("pageshow", function() {
-        selectedBrowser.removeEventListener("pageshow", arguments.callee,
-                                            false);
-        ok(true, "pageshow listener called");
-        waitForFocus(onFocus, testWindow.content);
-      }, true);
-      testWindow.content.location = "data:text/html,<h1>A Page</h1>";
-    });
-  }, false);
+  whenDelayedStartupFinished(testWindow, function () {
+    let selectedBrowser = testWindow.gBrowser.selectedBrowser;
+    selectedBrowser.addEventListener("pageshow", function() {
+      selectedBrowser.removeEventListener("pageshow", arguments.callee, true);
+      ok(true, "pageshow listener called");
+      waitForFocus(onFocus, testWindow.content);
+    }, true);
+    testWindow.gBrowser.loadURI("data:text/html,<h1>A Page</h1>");
+  });
 }
 
 function onFocus() {
-  EventUtils.synthesizeKey("/", {});
-  ok(gFindBarInitialized, "find bar is now initialized");
-  testWindow.gFindBar.close();
+  ok(!testWindow.gFindBarInitialized, "find bar is not initialized");
+  EventUtils.synthesizeKey("/", {}, testWindow);
+  ok(testWindow.gFindBarInitialized, "find bar is now initialized");
   testWindow.close();
   finish();
 }
--- a/browser/base/content/test/social/browser_social_activation.js
+++ b/browser/base/content/test/social/browser_social_activation.js
@@ -37,17 +37,19 @@ function postTestCleanup(callback) {
     } catch(ex) {
       // this test didn't add this provider - that's ok.
       cbRemoved();
     }
   }
 }
 
 function addBuiltinManifest(manifest) {
-  setManifestPref("social.manifest."+manifest.origin, manifest);
+  let prefname = getManifestPrefname(manifest);
+  setBuiltinManifestPref(prefname, manifest);
+  return prefname;
 }
 
 function addTab(url, callback) {
   let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
   tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
     tab.linkedBrowser.removeEventListener("load", tabLoad, true);
     tabsToRemove.push(tab);
     executeSoon(function() {callback(tab)});
@@ -75,16 +77,89 @@ function activateProvider(domain, callba
 
 function activateIFrameProvider(domain, callback) {
   let activationURL = domain+"/browser/browser/base/content/test/social/social_activate_iframe.html"
   addTab(activationURL, function(tab) {
     sendActivationEvent(tab, callback, false);
   });
 }
 
+function waitForProviderLoad(cb) {
+  Services.obs.addObserver(function providerSet(subject, topic, data) {
+    Services.obs.removeObserver(providerSet, "social:provider-set");
+    info("social:provider-set observer was notified");
+    waitForCondition(function() {
+      let sbrowser = document.getElementById("social-sidebar-browser");
+      return Social.provider &&
+             Social.provider.profile &&
+             Social.provider.profile.displayName &&
+             sbrowser.docShellIsActive;
+    }, function() {
+      // executeSoon to let the browser UI observers run first
+      executeSoon(cb);
+    },
+    "waitForProviderLoad: provider profile was not set");
+  }, "social:provider-set", false);
+}
+
+
+function getAddonItemInList(aId, aList) {
+  var item = aList.firstChild;
+  while (item) {
+    if ("mAddon" in item && item.mAddon.id == aId) {
+      aList.ensureElementIsVisible(item);
+      return item;
+    }
+    item = item.nextSibling;
+  }
+  return null;
+}
+
+function clickAddonRemoveButton(tab, aCallback) {
+  AddonManager.getAddonsByTypes(["service"], function(aAddons) {
+    let addon = aAddons[0];
+
+    let doc = tab.linkedBrowser.contentDocument;
+    let list = doc.getElementById("addon-list");
+
+    let item = getAddonItemInList(addon.id, list);
+    isnot(item, null, "Should have found the add-on in the list");
+
+    var button = doc.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
+    isnot(button, null, "Should have a remove button");
+    ok(!button.disabled, "Button should not be disabled");
+
+    EventUtils.synthesizeMouseAtCenter(button, { }, doc.defaultView);
+
+    // Force XBL to apply
+    item.clientTop;
+
+    is(item.getAttribute("pending"), "uninstall", "Add-on should be uninstalling");
+
+    executeSoon(function() { aCallback(addon); });
+  });
+}
+
+function activateOneProvider(manifest, finishActivation, aCallback) {
+  activateProvider(manifest.origin, function() {
+    waitForProviderLoad(function() {
+      ok(!SocialUI.activationPanel.hidden, "activation panel is showing");
+      is(Social.provider.origin, manifest.origin, "new provider is active");
+      checkSocialUI();
+
+      if (finishActivation)
+        document.getElementById("social-activation-button").click();
+      else
+        document.getElementById("social-undoactivation-button").click();
+
+      executeSoon(aCallback);
+    });
+  });
+}
+
 let gTestDomains = ["https://example.com", "https://test1.example.com", "https://test2.example.com"];
 let gProviders = [
   {
     name: "provider 1",
     origin: "https://example.com",
     sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html?provider1",
     workerURL: "https://example.com/browser/browser/base/content/test/social/social_worker.js#no-profile,no-recommend",
     iconURL: "chrome://branding/content/icon48.png"
@@ -134,105 +209,136 @@ var tests = {
       Services.prefs.clearUserPref("social.whitelist");
       next();
     });
   },
 
   testActivationFirstProvider: function(next) {
     Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     // first up we add a manifest entry for a single provider.
-    activateProvider(gTestDomains[0], function() {
-      ok(!SocialUI.activationPanel.hidden, "activation panel should be showing");
-      is(Social.provider.origin, gTestDomains[0], "new provider is active");
+    activateOneProvider(gProviders[0], false, function() {
+      // we deactivated leaving no providers left, so Social is disabled.
+      ok(!Social.provider, "should be no provider left after disabling");
       checkSocialUI();
-      // hit "undo"
-      document.getElementById("social-undoactivation-button").click();
-      executeSoon(function() {
-        // we deactivated leaving no providers left, so Social is disabled.
-        ok(!Social.provider, "should be no provider left after disabling");
-        checkSocialUI();
-        Services.prefs.clearUserPref("social.whitelist");
-        next();
-      })
+      Services.prefs.clearUserPref("social.whitelist");
+      next();
     });
   },
 
   testActivationBuiltin: function(next) {
-    let prefname = getManifestPrefname(gProviders[0]);
-    setBuiltinManifestPref(prefname, gProviders[0]);
+    let prefname = addBuiltinManifest(gProviders[0]);
     is(SocialService.getOriginActivationType(gTestDomains[0]), "builtin", "manifest is builtin");
     // first up we add a manifest entry for a single provider.
-    activateProvider(gTestDomains[0], function() {
-      ok(!SocialUI.activationPanel.hidden, "activation panel should be showing");
-      is(Social.provider.origin, gTestDomains[0], "new provider is active");
+    activateOneProvider(gProviders[0], false, function() {
+      // we deactivated leaving no providers left, so Social is disabled.
+      ok(!Social.provider, "should be no provider left after disabling");
       checkSocialUI();
-      // hit "undo"
-      document.getElementById("social-undoactivation-button").click();
-      executeSoon(function() {
-        // we deactivated leaving no providers left, so Social is disabled.
-        ok(!Social.provider, "should be no provider left after disabling");
-        checkSocialUI();
-        resetBuiltinManifestPref(prefname);
-        next();
-      })
-    }, true);
+      resetBuiltinManifestPref(prefname);
+      next();
+    });
   },
 
   testActivationMultipleProvider: function(next) {
     // The trick with this test is to make sure that Social.providers[1] is
     // the current provider when doing the undo - this makes sure that the
     // Social code doesn't fallback to Social.providers[0], which it will
     // do in some cases (but those cases do not include what this test does)
     // first enable the 2 providers
     Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     SocialService.addProvider(gProviders[0], function() {
       SocialService.addProvider(gProviders[1], function() {
         Social.provider = Social.providers[1];
         checkSocialUI();
         // activate the last provider.
-        addBuiltinManifest(gProviders[2]);
-        activateProvider(gTestDomains[2], function() {
-          ok(!SocialUI.activationPanel.hidden, "activation panel should be showing");
-          is(Social.provider.origin, gTestDomains[2], "new provider is active");
+        let prefname = addBuiltinManifest(gProviders[2]);
+        activateOneProvider(gProviders[2], false, function() {
+          // we deactivated - the first provider should be enabled.
+          is(Social.provider.origin, Social.providers[1].origin, "original provider should have been reactivated");
           checkSocialUI();
-          // hit "undo"
-          document.getElementById("social-undoactivation-button").click();
-          executeSoon(function() {
-            // we deactivated - the first provider should be enabled.
-            is(Social.provider.origin, Social.providers[1].origin, "original provider should have been reactivated");
-            checkSocialUI();
-            Services.prefs.clearUserPref("social.whitelist");
-            next();
-          });
+          Services.prefs.clearUserPref("social.whitelist");
+          resetBuiltinManifestPref(prefname);
+          next();
         });
       });
     });
   },
 
   testRemoveNonCurrentProvider: function(next) {
     Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
     SocialService.addProvider(gProviders[0], function() {
       SocialService.addProvider(gProviders[1], function() {
         Social.provider = Social.providers[1];
         checkSocialUI();
         // activate the last provider.
         addBuiltinManifest(gProviders[2]);
         activateProvider(gTestDomains[2], function() {
-          ok(!SocialUI.activationPanel.hidden, "activation panel should be showing");
-          is(Social.provider.origin, gTestDomains[2], "new provider is active");
-          checkSocialUI();
-          // A bit contrived, but set a new provider current while the
-          // activation ui is up.
-          Social.provider = Social.providers[1];
-          // hit "undo"
-          document.getElementById("social-undoactivation-button").click();
-          executeSoon(function() {
-            // we deactivated - the same provider should be enabled.
-            is(Social.provider.origin, Social.providers[1].origin, "original provider still be active");
+          waitForProviderLoad(function() {
+            ok(!SocialUI.activationPanel.hidden, "activation panel is showing");
+            is(Social.provider.origin, gTestDomains[2], "new provider is active");
             checkSocialUI();
-            Services.prefs.clearUserPref("social.whitelist");
-            next();
+            // A bit contrived, but set a new provider current while the
+            // activation ui is up.
+            Social.provider = Social.providers[1];
+            // hit "undo"
+            document.getElementById("social-undoactivation-button").click();
+            executeSoon(function() {
+              // we deactivated - the same provider should be enabled.
+              is(Social.provider.origin, Social.providers[1].origin, "original provider still be active");
+              checkSocialUI();
+              Services.prefs.clearUserPref("social.whitelist");
+              next();
+            });
           });
         });
       });
     });
   },
+
+  testAddonManagerDoubleInstall: function(next) {
+    Services.prefs.setCharPref("social.whitelist", gTestDomains.join(","));
+    // Create a new tab and load about:addons
+    let blanktab = gBrowser.addTab();
+    gBrowser.selectedTab = blanktab;
+    BrowserOpenAddonsMgr('addons://list/service');
+
+    is(blanktab, gBrowser.selectedTab, "Current tab should be blank tab");
+
+    gBrowser.selectedBrowser.addEventListener("load", function tabLoad() {
+      gBrowser.selectedBrowser.removeEventListener("load", tabLoad, true);
+      let browser = blanktab.linkedBrowser;
+      is(browser.currentURI.spec, "about:addons", "about:addons should load into blank tab.");
+
+      addBuiltinManifest(gProviders[0]);
+      activateOneProvider(gProviders[0], true, function() {
+        gBrowser.removeTab(gBrowser.selectedTab);
+        tabsToRemove.pop();
+        // uninstall the provider
+        clickAddonRemoveButton(blanktab, function(addon) {
+          checkSocialUI();
+          activateOneProvider(gProviders[0], true, function() {
+
+            // after closing the addons tab, verify provider is still installed
+            gBrowser.tabContainer.addEventListener("TabClose", function onTabClose() {
+              gBrowser.tabContainer.removeEventListener("TabClose", onTabClose);
+              AddonManager.getAddonsByTypes(["service"], function(aAddons) {
+                is(aAddons.length, 1, "there can be only one");
+                Services.prefs.clearUserPref("social.whitelist");
+                next();
+              });
+            });
+
+            // verify only one provider in list
+            AddonManager.getAddonsByTypes(["service"], function(aAddons) {
+              is(aAddons.length, 1, "there can be only one");
+
+              let doc = blanktab.linkedBrowser.contentDocument;
+              let list = doc.getElementById("addon-list");
+              is(list.childNodes.length, 1, "only one addon is displayed");
+
+              gBrowser.removeTab(blanktab);
+            });
+
+          });
+        });
+      });
+    }, true);
+  }
 }
--- a/browser/base/content/test/social/browser_social_markButton.js
+++ b/browser/base/content/test/social/browser_social_markButton.js
@@ -64,152 +64,124 @@ function testInitial(finishcb) {
       is(markButton.style.listStyleImage, 'url("https://example.com/browser/browser/base/content/test/social/social_mark_image.png")', "check image url is correct");
       SocialMark.togglePageMark(function() {
         is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
         executeSoon(function() {
           testStillMarkedIn2Tabs();
         });
       });
     });
-    markButton.click();
   }, "provider didn't provide page-mark-config");
 }
 
 function testStillMarkedIn2Tabs() {
   let toMark = "http://test2.example.com";
   let markUri = Services.io.newURI(toMark, null, null);
   let markButton = SocialMark.button;
   let initialTab = gBrowser.selectedTab;
   info("initialTab has loaded " + gBrowser.currentURI.spec);
   is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' for the initial tab");
-  let tab1 = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tab1b = gBrowser.getBrowserForTab(tab1);
 
-  tab1b.addEventListener("load", function tabLoad(event) {
-    tab1b.removeEventListener("load", tabLoad, true);
-    let tab2 = gBrowser.selectedTab = gBrowser.addTab(toMark);
-    let tab2b = gBrowser.getBrowserForTab(tab2);
-    tab2b.addEventListener("load", function tabLoad(event) {
-      tab2b.removeEventListener("load", tabLoad, true);
+  addTab(toMark, function(tab1) {
+    addTab(toMark, function(tab2) {
       // should start without either page being marked.
       is(markButton.hasAttribute("marked"), false, "SocialMark button should not have 'marked' before we've done anything");
       Social.isURIMarked(markUri, function(marked) {
         ok(!marked, "page is unmarked in annotations");
         markButton.click();
         waitForCondition(function() markButton.hasAttribute("marked"), function() {
           Social.isURIMarked(markUri, function(marked) {
             ok(marked, "page is marked in annotations");
             // and switching to the first tab (with the same URL) should still reflect marked.
-            gBrowser.selectedTab = tab1;
-            is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
-            // wait for tabselect
-            gBrowser.tabContainer.addEventListener("TabSelect", function onTabSelect() {
-              gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect, false);
-              waitForCondition(function() !markButton.hasAttribute("marked"), function() {
-                gBrowser.selectedTab = tab1;
-      
-                SocialMark.togglePageMark(function() {
-                  Social.isURIMarked(gBrowser.currentURI, function(marked) {
-                    ok(!marked, "page is unmarked in annotations");
-                    is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
-                    gBrowser.removeTab(tab1);
-                    gBrowser.removeTab(tab2);
-                    executeSoon(testStillMarkedAfterReopen);
+            selectBrowserTab(tab1, function() {
+              is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
+              // wait for tabselect
+              selectBrowserTab(initialTab, function() {
+                waitForCondition(function() !markButton.hasAttribute("marked"), function() {
+                  gBrowser.selectedTab = tab1;
+        
+                  SocialMark.togglePageMark(function() {
+                    Social.isURIMarked(gBrowser.currentURI, function(marked) {
+                      ok(!marked, "page is unmarked in annotations");
+                      is(markButton.hasAttribute("marked"), false, "mark button should not be marked");
+                      gBrowser.removeTab(tab1);
+                      gBrowser.removeTab(tab2);
+                      executeSoon(testStillMarkedAfterReopen);
+                    });
                   });
-                });
-              }, "button has been unmarked");
-            }, false);
-            // but switching back the initial one should reflect not marked.
-            gBrowser.selectedTab = initialTab;
+                }, "button has been unmarked");
+              });
+            });
           });
         }, "button has been marked");
       });
-
-    }, true);
-  }, true);
+    });
+  });
 }
 
 function testStillMarkedAfterReopen() {
   let toMark = "http://test2.example.com";
   let markButton = SocialMark.button;
 
   is(markButton.hasAttribute("marked"), false, "Reopen: SocialMark button should not have 'marked' for the initial tab");
-  let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tabb = gBrowser.getBrowserForTab(tab);
-  tabb.addEventListener("load", function tabLoad(event) {
-    tabb.removeEventListener("load", tabLoad, true);
+  addTab(toMark, function(tab) {
     SocialMark.togglePageMark(function() {
       is(markButton.hasAttribute("marked"), true, "SocialMark button should reflect the marked state");
       gBrowser.removeTab(tab);
       // should be on the initial unmarked tab now.
       waitForCondition(function() !markButton.hasAttribute("marked"), function() {
         // now open the same URL - should be back to Marked.
-        tab = gBrowser.selectedTab = gBrowser.addTab(toMark, {skipAnimation: true});
-        tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
-          tab.linkedBrowser.removeEventListener("load", tabLoad, true);
-          executeSoon(function() {
-            is(markButton.hasAttribute("marked"), true, "New tab to previously marked URL should reflect marked state");
-            SocialMark.togglePageMark(function() {
-              gBrowser.removeTab(tab);
-              executeSoon(testOnlyMarkCertainUrlsTabSwitch);
-            });
+        addTab(toMark, function(tab) {
+          is(markButton.hasAttribute("marked"), true, "New tab to previously marked URL should reflect marked state");
+          SocialMark.togglePageMark(function() {
+            gBrowser.removeTab(tab);
+            executeSoon(testOnlyMarkCertainUrlsTabSwitch);
           });
-        }, true);
+        });
       }, "button is now unmarked");
     });
-  }, true);
+  });
 }
 
 function testOnlyMarkCertainUrlsTabSwitch() {
   let toMark = "http://test2.example.com";
   let notSharable = "about:blank";
   let markButton = SocialMark.button;
-  let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tabb = gBrowser.getBrowserForTab(tab);
-  tabb.addEventListener("load", function tabLoad(event) {
-    tabb.removeEventListener("load", tabLoad, true);
+  addTab(toMark, function(tab) {
     ok(!markButton.hidden, "SocialMark button not hidden for http url");
-    let tab2 = gBrowser.selectedTab = gBrowser.addTab(notSharable);
-    let tabb2 = gBrowser.getBrowserForTab(tab2);
-    tabb2.addEventListener("load", function tabLoad(event) {
-      tabb2.removeEventListener("load", tabLoad, true);
+    addTab(notSharable, function(tab2) {
       ok(markButton.disabled, "SocialMark button disabled for about:blank");
-      gBrowser.selectedTab = tab;
-      ok(!markButton.disabled, "SocialMark button re-shown when switching back to http: url");
-      gBrowser.selectedTab = tab2;
-      ok(markButton.disabled, "SocialMark button re-hidden when switching back to about:blank");
-      gBrowser.removeTab(tab);
-      gBrowser.removeTab(tab2);
-      executeSoon(testOnlyMarkCertainUrlsSameTab);
-    }, true);
-  }, true);
+      selectBrowserTab(tab, function() {
+        ok(!markButton.disabled, "SocialMark button re-shown when switching back to http: url");
+        selectBrowserTab(tab2, function() {
+          ok(markButton.disabled, "SocialMark button re-hidden when switching back to about:blank");
+          gBrowser.removeTab(tab);
+          gBrowser.removeTab(tab2);
+          executeSoon(testOnlyMarkCertainUrlsSameTab);
+        });
+      });
+    });
+  });
 }
 
 function testOnlyMarkCertainUrlsSameTab() {
   let toMark = "http://test2.example.com";
   let notSharable = "about:blank";
   let markButton = SocialMark.button;
-  let tab = gBrowser.selectedTab = gBrowser.addTab(toMark);
-  let tabb = gBrowser.getBrowserForTab(tab);
-  tabb.addEventListener("load", function tabLoad(event) {
-    tabb.removeEventListener("load", tabLoad, true);
+  addTab(toMark, function(tab) {
     ok(!markButton.disabled, "SocialMark button not disabled for http url");
-    tabb.addEventListener("load", function tabLoad(event) {
-      tabb.removeEventListener("load", tabLoad, true);
+    loadIntoTab(tab, notSharable, function() {
       ok(markButton.disabled, "SocialMark button disabled for about:blank");
-      tabb.addEventListener("load", function tabLoad(event) {
-        tabb.removeEventListener("load", tabLoad, true);
+      loadIntoTab(tab, toMark, function() {
         ok(!markButton.disabled, "SocialMark button re-enabled http url");
         gBrowser.removeTab(tab);
         executeSoon(testDisable);
-      }, true);
-      tabb.loadURI(toMark);
-    }, true);
-    tabb.loadURI(notSharable);
-  }, true);
+      });
+    });
+  });
 }
 
 function testDisable() {
   let markButton = SocialMark.button;
   Services.prefs.setBoolPref(prefName, false);
   is(markButton.hidden, true, "SocialMark button should be hidden when pref is disabled");
   gFinishCB();
 }
--- a/browser/base/content/test/social/browser_social_toolbar.js
+++ b/browser/base/content/test/social/browser_social_toolbar.js
@@ -90,23 +90,37 @@ var tests = {
     let ambience3 = {
       name: "testIcon3",
       iconURL: "https://example.com/browser/browser/base/content/test/moz.png",
       contentPanel: "about:blank",
       counter: 0,
       label: "Test Ambient 3",
       menuURL: "https://example.com/testAmbient3"
     };
+    let ambience4 = {
+      name: "testIcon4",
+      iconURL: "https://example.com/browser/browser/base/content/test/moz.png",
+      contentPanel: "about:blank",
+      counter: 0,
+      label: "Test Ambient 4",
+      menuURL: "https://example.com/testAmbient4"
+    };
     Social.provider.setAmbientNotification(ambience);
 
     // for Bug 813834.  Check preference whether stored data is correct.
     is(JSON.parse(Services.prefs.getComplexValue("social.cached.ambientNotificationIcons", Ci.nsISupportsString).data).data.testIcon.label, "Test Ambient 1 \u2046", "label is stored into preference correctly");
 
     Social.provider.setAmbientNotification(ambience2);
     Social.provider.setAmbientNotification(ambience3);
+    
+    try {
+      Social.provider.setAmbientNotification(ambience4);
+    } catch(e) {}
+    let numIcons = Object.keys(Social.provider.ambientNotificationIcons).length;
+    ok(numIcons == 3, "prevent adding more than 3 ambient notification icons");
 
     let statusIcon = document.getElementById("social-provider-button").nextSibling;
     waitForCondition(function() {
       statusIcon = document.getElementById("social-provider-button").nextSibling;
       return !!statusIcon;
     }, function () {
       let badge = statusIcon.getAttribute("badge");
       is(badge, "42", "status value is correct");
--- a/browser/base/content/test/social/head.js
+++ b/browser/base/content/test/social/head.js
@@ -294,8 +294,30 @@ function addWindowListener(aURL, aCallba
 
 function addTab(url, callback) {
   let tab = gBrowser.selectedTab = gBrowser.addTab(url, {skipAnimation: true});
   tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
     tab.linkedBrowser.removeEventListener("load", tabLoad, true);
     executeSoon(function() {callback(tab)});
   }, true);
 }
+
+function selectBrowserTab(tab, callback) {
+  if (gBrowser.selectedTab == tab) {
+    executeSoon(function() {callback(tab)});
+    return;
+  }
+  gBrowser.tabContainer.addEventListener("TabSelect", function onTabSelect() {
+    gBrowser.tabContainer.removeEventListener("TabSelect", onTabSelect, false);
+    is(gBrowser.selectedTab, tab, "browser tab is selected");
+    executeSoon(function() {callback(tab)});
+  });
+  gBrowser.selectedTab = tab;
+}
+
+function loadIntoTab(tab, url, callback) {
+  tab.linkedBrowser.addEventListener("load", function tabLoad(event) {
+    tab.linkedBrowser.removeEventListener("load", tabLoad, true);
+    executeSoon(function() {callback(tab)});
+  }, true);
+  tab.linkedBrowser.loadURI(url);
+}
+
--- a/browser/base/content/test/social/social_activate.html
+++ b/browser/base/content/test/social/social_activate.html
@@ -7,18 +7,18 @@
 var data = {
   // currently required
   "name": "Demo Social Service",
   "iconURL": "chrome://branding/content/icon16.png",
   "icon32URL": "chrome://branding/content/favicon32.png",
   "icon64URL": "chrome://branding/content/icon64.png",
 
   // at least one of these must be defined
-  "workerURL": "/browser/browser/base/content/test/social/social_sidebar.html",
-  "sidebarURL": "/browser/browser/base/content/test/social/social_worker.js",
+  "sidebarURL": "/browser/browser/base/content/test/social/social_sidebar.html",
+  "workerURL": "/browser/browser/base/content/test/social/social_worker.js",
 
   // should be available for display purposes
   "description": "A short paragraph about this provider",
   "author": "Shane Caraveo, Mozilla",
 
   // optional
   "version": "1.0"
 }
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -1339,32 +1339,21 @@ BrowserGlue.prototype = {
     if (currentUIVersion < 12) {
       // Remove bookmarks-menu-button-container, then place
       // bookmarks-menu-button into its position.
       let currentsetResource = this._rdf.GetResource("currentset");
       let toolbarResource = this._rdf.GetResource(BROWSER_DOCURL + "nav-bar");
       let currentset = this._getPersist(toolbarResource, currentsetResource);
       // Need to migrate only if toolbar is customized.
       if (currentset) {
-        if (currentset.contains("bookmarks-menu-button-container"))
-          currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/,"$2");
-
-        // Now insert the new button.
-        if (currentset.contains("downloads-button")) {
-          currentset = currentset.replace(/(^|,)downloads-button($|,)/,
-                                          "$1bookmarks-menu-button,downloads-button$2");
-        } else if (currentset.contains("home-button")) {
-          currentset = currentset.replace(/(^|,)home-button($|,)/,
-                                          "$1bookmarks-menu-button,home-button$2");
-        } else {
-          // Just append.
-          currentset = currentset.replace(/(^|,)window-controls($|,)/,
-                                          "$1bookmarks-menu-button,window-controls$2")
+        if (currentset.contains("bookmarks-menu-button-container")) {
+          currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/,
+                                          "$1bookmarks-menu-button$2");
+          this._setPersist(toolbarResource, currentsetResource, currentset);
         }
-        this._setPersist(toolbarResource, currentsetResource, currentset);
       }
     }
 
     if (currentUIVersion < 13) {
       try {
         if (Services.prefs.getBoolPref("plugins.hide_infobar_for_missing_plugin"))
           Services.prefs.setBoolPref("plugins.notifyMissingFlash", false);
       }
--- a/browser/components/places/tests/unit/test_clearHistory_shutdown.js
+++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -25,16 +25,22 @@ let EXPECTED_NOTIFICATIONS = [
 ];
 
 const UNEXPECTED_NOTIFICATIONS = [
   "xpcom-shutdown"
 ];
 
 const URL = "ftp://localhost/clearHistoryOnShutdown/";
 
+// Send the profile-after-change notification to the form history component to ensure
+// that it has been initialized.
+var formHistoryStartup = Cc["@mozilla.org/satchel/form-history-startup;1"].
+                         getService(Ci.nsIObserver);
+formHistoryStartup.observe(null, "profile-after-change", null);
+
 let notificationIndex = 0;
 
 let notificationsObserver = {
   observe: function observe(aSubject, aTopic, aData) {
     print("Received notification: " + aTopic);
 
     // Note that some of these notifications could arrive multiple times, for
     // example in case of sync, we allow that.
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -104,16 +104,19 @@
       <field name="_stringBundle">document.getAnonymousElementByAttribute(this,
           "anonid", "searchbar-stringbundle");</field>
       <field name="_textbox">document.getAnonymousElementByAttribute(this,
           "anonid", "searchbar-textbox");</field>
       <field name="_popup">document.getAnonymousElementByAttribute(this,
           "anonid", "searchbar-popup");</field>
       <field name="_ss">null</field>
       <field name="_engines">null</field>
+      <field name="FormHistory" readonly="true">
+        (Components.utils.import("resource://gre/modules/FormHistory.jsm", {})).FormHistory;
+      </field>
 
       <property name="engines" readonly="true">
         <getter><![CDATA[
           if (!this._engines)
             this._engines = this.searchService.getVisibleEngines();
           return this._engines;
         ]]></getter>
       </property>
@@ -450,22 +453,23 @@
           else {
             var newTabPref = textBox._prefBranch.getBoolPref("browser.search.openintab");
             if ((aEvent && aEvent.altKey) ^ newTabPref)
               where = "tab";
           }
 
           // Save the current value in the form history
           if (textValue && !PrivateBrowsingUtils.isWindowPrivate(window)) {
-            try {
-              textBox._formHistSvc.addEntry(textBox.getAttribute("autocompletesearchparam"),
-                                            textValue);
-            } catch (ex) {
-              Components.utils.reportError("Saving search to form history failed: " + ex);
-            }
+            this.FormHistory.update(
+              { op : "bump",
+                fieldname : textBox.getAttribute("autocompletesearchparam"),
+                value : textValue },
+              { handleError : function(aError) {
+                  Components.utils.reportError("Saving search to form history failed: " + aError.message);
+              }});
           }
 
           this.doSearch(textValue, where);
         ]]></body>
       </method>
 
       <method name="doSearch">
         <parameter name="aData"/>
@@ -549,30 +553,26 @@
         // Because XBL and the customize toolbar code interacts poorly,
         // there may not be anything to remove here
         try {
           this.controllers.removeController(this.searchbarController);
         } catch (ex) { }
       ]]></destructor>
 
       <field name="_stringBundle"/>
-      <field name="_formHistSvc"/>
       <field name="_prefBranch"/>
       <field name="_suggestMenuItem"/>
       <field name="_suggestEnabled"/>
 
       <method name="initialize">
         <body><![CDATA[
           const kXULNS =
             "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
           // Initialize fields
           this._stringBundle = document.getBindingParent(this)._stringBundle;
-          this._formHistSvc =
-                   Components.classes["@mozilla.org/satchel/form-history;1"]
-                             .getService(Components.interfaces.nsIFormHistory2);
           this._prefBranch =
                     Components.classes["@mozilla.org/preferences-service;1"]
                               .getService(Components.interfaces.nsIPrefBranch);
           this._suggestEnabled =
               this._prefBranch.getBoolPref("browser.search.suggest.enabled");
 
           if (this._prefBranch.getBoolPref("browser.urlbar.clickSelectsAll"))
             this.setAttribute("clickSelectsAll", true);
@@ -743,28 +743,27 @@
       <field name="searchbarController" readonly="true"><![CDATA[({
         _self: this,
         supportsCommand: function(aCommand) {
           return aCommand == "cmd_clearhistory" ||
                  aCommand == "cmd_togglesuggest";
         },
 
         isCommandEnabled: function(aCommand) {
-          if (aCommand == "cmd_clearhistory") {
-            var param = this._self.getAttribute("autocompletesearchparam");
-            return this._self._formHistSvc.nameExists(param);
-          }
           return true;
         },
 
         doCommand: function (aCommand) {
           switch (aCommand) {
             case "cmd_clearhistory":
               var param = this._self.getAttribute("autocompletesearchparam");
-              this._self._formHistSvc.removeEntriesForName(param);
+
+              let searchBar = this._self.parentNode;
+
+              BrowserSearch.searchBar.FormHistory.update({ op : "remove", fieldname : param }, null);
               this._self.value = "";
               break;
             case "cmd_togglesuggest":
               // The pref observer will update _suggestEnabled and the menu
               // checkmark.
               this._self._prefBranch.setBoolPref("browser.search.suggest.enabled",
                                                  !this._self._suggestEnabled);
               break;
--- a/browser/components/search/test/browser_426329.js
+++ b/browser/components/search/test/browser_426329.js
@@ -1,30 +1,35 @@
 // Instead of loading ChromeUtils.js into the test scope in browser-test.js for all tests,
 // we only need ChromeUtils.js for a few files which is why we are using loadSubScript.
 var ChromeUtils = {};
 this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                      getService(Ci.mozIJSSubScriptLoader);
 this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
 
+XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
+  "resource://gre/modules/FormHistory.jsm");
+
 function test() {
   waitForExplicitFinish();
 
   const ENGINE_HTML_BASE = "http://mochi.test:8888/browser/browser/components/search/test/test.html";
 
   var searchEntries = ["test", "More Text", "Some Text"];
   var searchBar = BrowserSearch.searchBar;
   var searchButton = document.getAnonymousElementByAttribute(searchBar,
                      "anonid", "search-go-button");
   ok(searchButton, "got search-go-button");
 
   searchBar.value = "test";
 
   var ss = Services.search;
 
+  let testIterator;
+
   function observer(aSub, aTopic, aData) {
     switch (aData) {
       case "engine-added":
         var engine = ss.getEngineByName("Bug 426329");
         ok(engine, "Engine was added.");
         //XXX Bug 493051
         //ss.currentEngine = engine;
         break;
@@ -177,43 +182,74 @@ function test() {
   }
 
   function testRightClick() {
     init();
     searchBar.removeEventListener("popupshowing", stopPopup, true);
     content.location.href = "about:blank";
     simulateClick({ button: 2 }, searchButton);
     setTimeout(function() {
-
       is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab");
       is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing");
 
-      testSearchHistory();
+      testIterator = testSearchHistory();
+      testIterator.next();
     }, 5000);
   }
 
+  function countEntries(name, value, message) {
+    let count = 0;
+    FormHistory.count({ fieldname: name, value: value },
+                      { handleResult: function(result) { count = result; },
+                        handleError: function(error) { throw error; },
+                        handleCompletion: function(reason) {
+                          if (!reason) {
+                            ok(count > 0, message);
+                            testIterator.next();
+                          }
+                        }
+                      });
+  }
+
   function testSearchHistory() {
     var textbox = searchBar._textbox;
     for (var i = 0; i < searchEntries.length; i++) {
-      let exists = textbox._formHistSvc.entryExists(textbox.getAttribute("autocompletesearchparam"), searchEntries[i]);
-      ok(exists, "form history entry '" + searchEntries[i] + "' should exist");
+      yield countEntries(textbox.getAttribute("autocompletesearchparam"), searchEntries[i],
+                         "form history entry '" + searchEntries[i] + "' should exist");
     }
     testAutocomplete();
   }
 
   function testAutocomplete() {
     var popup = searchBar.textbox.popup;
-    popup.addEventListener("popupshowing", function testACPopupShowing() {
-      popup.removeEventListener("popupshowing", testACPopupShowing);
+    popup.addEventListener("popupshown", function testACPopupShowing() {
+      popup.removeEventListener("popupshown", testACPopupShowing);
       checkMenuEntries(searchEntries);
-      SimpleTest.executeSoon(finalize);
+      testClearHistory();
     });
     searchBar.textbox.showHistoryPopup();
   }
 
+  function testClearHistory() {
+    let controller = searchBar.textbox.controllers.getControllerForCommand("cmd_clearhistory")
+    ok(controller.isCommandEnabled("cmd_clearhistory"), "Clear history command enabled");
+    controller.doCommand("cmd_clearhistory");
+    let count = 0;
+    FormHistory.count({ },
+                      { handleResult: function(result) { count = result; },
+                        handleError: function(error) { throw error; },
+                        handleCompletion: function(reason) {
+                          if (!reason) {
+                            ok(count == 0, "History cleared");
+                            finalize();
+                          }
+                        }
+                      });
+  }
+
   function finalize() {
     searchBar.value = "";
     while (gBrowser.tabs.length != 1) {
       gBrowser.removeTab(gBrowser.tabs[0]);
     }
     content.location.href = "about:blank";
     var engine = ss.getEngineByName("Bug 426329");
     ss.removeEngine(engine);
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -179,16 +179,17 @@
 @BINPATH@/components/dom_battery.xpt
 #ifdef MOZ_B2G_BT
 @BINPATH@/components/dom_bluetooth.xpt
 #endif
 @BINPATH@/components/dom_camera.xpt
 @BINPATH@/components/dom_canvas.xpt
 @BINPATH@/components/dom_contacts.xpt
 @BINPATH@/components/dom_alarm.xpt
+@BINPATH@/components/dom_push.xpt
 @BINPATH@/components/dom_core.xpt
 @BINPATH@/components/dom_css.xpt
 @BINPATH@/components/dom_devicestorage.xpt
 @BINPATH@/components/dom_events.xpt
 @BINPATH@/components/dom_file.xpt
 @BINPATH@/components/dom_geolocation.xpt
 @BINPATH@/components/dom_media.xpt
 @BINPATH@/components/dom_network.xpt
@@ -427,16 +428,17 @@
 @BINPATH@/components/nsContentDispatchChooser.js
 @BINPATH@/components/nsHandlerService.manifest
 @BINPATH@/components/nsHandlerService.js
 @BINPATH@/components/nsWebHandlerApp.manifest
 @BINPATH@/components/nsWebHandlerApp.js
 @BINPATH@/components/satchel.manifest
 @BINPATH@/components/nsFormAutoComplete.js
 @BINPATH@/components/nsFormHistory.js
+@BINPATH@/components/FormHistoryStartup.js
 @BINPATH@/components/nsInputListAutoComplete.js
 @BINPATH@/components/contentSecurityPolicy.manifest
 @BINPATH@/components/contentSecurityPolicy.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 #ifdef MOZ_B2G_RIL
 @BINPATH@/components/RadioInterfaceLayer.manifest
 @BINPATH@/components/RadioInterfaceLayer.js
@@ -513,16 +515,18 @@
 @BINPATH@/components/PermissionSettings.js
 @BINPATH@/components/PermissionSettings.manifest
 @BINPATH@/components/ContactManager.js
 @BINPATH@/components/ContactManager.manifest
 @BINPATH@/components/NavigatorPropertyHelper.js
 @BINPATH@/components/NavigatorPropertyHelper.manifest
 @BINPATH@/components/AlarmsManager.js
 @BINPATH@/components/AlarmsManager.manifest
+@BINPATH@/components/Push.js
+@BINPATH@/components/Push.manifest
 @BINPATH@/components/TCPSocket.js
 @BINPATH@/components/TCPSocketParentIntermediary.js
 @BINPATH@/components/TCPSocket.manifest
 
 @BINPATH@/components/AppProtocolHandler.js
 @BINPATH@/components/AppProtocolHandler.manifest
 
 #ifdef MOZ_WEBRTC
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -137,16 +137,21 @@ vulnerablePluginsMessage=Some plugins ha
 pluginInfo.unknownPlugin=Unknown
 
 # Sanitize
 # LOCALIZATION NOTE (sanitizeDialog2.everything.title): When "Time range to
 # clear" is set to "Everything", the Clear Recent History dialog's title is
 # changed to this.  See UI mockup and comment 11 at bug 480169 -->
 sanitizeDialog2.everything.title=Clear All History
 sanitizeButtonOK=Clear Now
+# LOCALIZATION NOTE (sanitizeButtonClearing): The label for the default
+# button between the user clicking it and the window closing.  Indicates the
+# items are being cleared.
+sanitizeButtonClearing=Clearing
+
 # LOCALIZATION NOTE (sanitizeEverythingWarning2): Warning that appears when
 # "Time range to clear" is set to "Everything" in Clear Recent History dialog,
 # provided that the user has not modified the default set of history items to clear.
 sanitizeEverythingWarning2=All history will be cleared.
 # LOCALIZATION NOTE (sanitizeSelectedWarning): Warning that appears when
 # "Time range to clear" is set to "Everything" in Clear Recent History dialog,
 # provided that the user has modified the default set of history items to clear.
 sanitizeSelectedWarning=All selected items will be cleared.
--- a/browser/themes/linux/browser.css
+++ b/browser/themes/linux/browser.css
@@ -470,16 +470,20 @@ menuitem:not([type]):not(.menuitem-toolt
   list-style-image: url("chrome://browser/skin/page-livemarks.png");
 }
 
 #bookmarksToolbarFolderMenu,
 #BMB_bookmarksToolbar {
   list-style-image: url("chrome://browser/skin/places/bookmarksToolbar.png");
 }
 
+#BMB_bookmarkThisPage {
+  list-style-image: url("chrome://browser/skin/places/starPage.png");
+}
+
 #BMB_unsortedBookmarks {
   list-style-image: url("chrome://browser/skin/places/unsortedBookmarks.png");
 }
 
 #appmenu_downloads,
 #menu_openDownloads {
   list-style-image: url("chrome://browser/skin/Toolbar-small.png");
   -moz-image-region: rect(0px 16px 16px 0px);
@@ -644,20 +648,29 @@ toolbar[mode="full"] .toolbarbutton-1 > 
 #downloads-button {
   -moz-image-region: rect(0px 24px 24px 0px);
 }
 
 #history-button {
   -moz-image-region: rect(0px 48px 24px 24px);
 }
 
-#bookmarks-button {
+#bookmarks-button,
+#bookmarks-menu-button {
   -moz-image-region: rect(0px 72px 24px 48px);
 }
 
+#bookmarks-menu-button.bookmark-item {
+  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
+}
+
+#bookmarks-menu-button.toolbarbutton-1 {
+  -moz-box-orient: horizontal;
+}
+
 #print-button {
   list-style-image: url("moz-icon://stock/gtk-print?size=toolbar");
 }
 #print-button[disabled="true"] {
   list-style-image: url("moz-icon://stock/gtk-print?size=toolbar&state=disabled");
 }
 
 #new-tab-button {
@@ -801,17 +814,19 @@ toolbar[iconsize="small"] #downloads-but
   -moz-image-region: rect(0px 16px 16px 0px);
 }
 
 toolbar[iconsize="small"] #webrtc-status-button /* temporary placeholder (bug 824825) */,
 toolbar[iconsize="small"] #history-button {
   -moz-image-region: rect(0px 32px 16px 16px);
 }
 
-toolbar[iconsize="small"] #bookmarks-button {
+toolbar[iconsize="small"] #bookmarks-button,
+toolbar[iconsize="small"] #bookmarks-menu-button,
+#bookmarks-menu-button.bookmark-item {
   -moz-image-region: rect(0px 48px 16px 32px);
 }
 
 toolbar[iconsize="small"] #print-button {
   list-style-image: url("moz-icon://stock/gtk-print?size=menu");
 }
 toolbar[iconsize="small"] #print-button[disabled="true"] {
   list-style-image: url("moz-icon://stock/gtk-print?size=menu&state=disabled");
@@ -1500,38 +1515,27 @@ richlistitem[type~="action"][actiontype=
 }
 
 /* social recommending panel */
 
 #social-mark-button {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
+/* Star button */
+#star-button {
+  list-style-image: url("chrome://browser/skin/places/starPage.png");
+}
+
+#star-button[starred="true"] {
+  list-style-image: url("chrome://browser/skin/places/pageStarred.png");
+}
+
 /* bookmarks menu-button */
 
-#bookmarks-menu-button {
-  list-style-image: url("chrome://browser/skin/Toolbar.png");
-  -moz-image-region: rect(0px 216px 24px 192px);
-}
-
-#bookmarks-menu-button[starred] {
-  -moz-image-region: rect(24px 216px 48px 192px);
-}
-
-toolbar[iconsize="small"] #bookmarks-menu-button,
-#bookmarks-menu-button.bookmark-item {
-  list-style-image: url("chrome://browser/skin/Toolbar-small.png");
-  -moz-image-region: rect(0px 144px 16px 128px);
-}
-
-toolbar[iconsize="small"] #bookmarks-menu-button[starred],
-#bookmarks-menu-button.bookmark-item[starred] {
-  -moz-image-region: rect(16px 144px 32px 128px);
-}
-
 #bookmarks-menu-button[disabled] > .toolbarbutton-icon,
 #bookmarks-menu-button[disabled] > .toolbarbutton-menu-dropmarker,
 #bookmarks-menu-button[disabled] > .toolbarbutton-menubutton-dropmarker,
 #bookmarks-menu-button[disabled] > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
 #bookmarks-menu-button > .toolbarbutton-menubutton-button[disabled] > .toolbarbutton-icon {
   opacity: .4;
 }
 
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -77,23 +77,25 @@ browser.jar:
   skin/classic/browser/newtab/newTab.css              (newtab/newTab.css)
   skin/classic/browser/newtab/controls.png            (newtab/controls.png)
   skin/classic/browser/newtab/noise.png               (newtab/noise.png)
   skin/classic/browser/places/bookmarksMenu.png       (places/bookmarksMenu.png)
   skin/classic/browser/places/bookmarksToolbar.png    (places/bookmarksToolbar.png)
   skin/classic/browser/places/calendar.png            (places/calendar.png)
 * skin/classic/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
   skin/classic/browser/places/livemark-item.png       (places/livemark-item.png)
+  skin/classic/browser/places/pageStarred.png         (places/pageStarred.png)
   skin/classic/browser/places/star-icons.png          (places/star-icons.png)
   skin/classic/browser/places/starred48.png           (places/starred48.png)
   skin/classic/browser/places/unstarred48.png         (places/unstarred48.png)
   skin/classic/browser/places/places.css              (places/places.css)
   skin/classic/browser/places/organizer.css           (places/organizer.css)
   skin/classic/browser/places/organizer.xml           (places/organizer.xml)
   skin/classic/browser/places/query.png               (places/query.png)
+  skin/classic/browser/places/starPage.png            (places/starPage.png)
   skin/classic/browser/places/tag.png                 (places/tag.png)
   skin/classic/browser/places/toolbarDropMarker.png   (places/toolbarDropMarker.png)
   skin/classic/browser/places/unsortedBookmarks.png   (places/unsortedBookmarks.png)
   skin/classic/browser/places/downloads.png           (places/downloads.png)
   skin/classic/browser/preferences/alwaysAsk.png      (preferences/alwaysAsk.png)
   skin/classic/browser/preferences/mail.png           (preferences/mail.png)
   skin/classic/browser/preferences/Options.png        (preferences/Options.png)
 #ifdef MOZ_SERVICES_SYNC
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..61a9f90e05b15a6206727be766294d7baa593995
GIT binary patch
literal 767
zc$@+M0s#GqP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00004b3#c}2nYxW
zd<bNS00009a7bBm000fw000fw0YWI7cmMzZ8FWQhbW?9;ba!ELWdL_~cP?peYja~^
zaAhuUa%Y?FJQ@H10(?nCK~y-6ol{*)R8bUN``mM9#+f_W7=EQA%|vEjIzdV?;!{2J
zP|-t0B}|d{6hTCf5ft?U0wMaK87+_s@}ak&9-3=HVIY*D2BVn{B9tF9qjT>)yNA#m
z9ADaSV6$0kueA>65Hq7#nF*>FLc9Y}PC3r8XpD!8;Y3k303vZS*m${eTjY932ytg4
z9GH3CNybsp-{ZC%YnGKg#~+><b~29YV%UuwbXM(XXHq`_`zyBYy2JoFi#kol;R0|4
zKu8F!hKQ;e)Yx(=<q<p#s9$hIGd{lDxlC||n0Z<$XA;0TfO}+iix=hL(<f^8Uel$!
z5}GtXnh!*-e==$TN?EXT6UgUM$Y-Z`YWSfwKRfhBuu`cKe^`{19)Ru%LdZZN832SA
z#M6*+3O>IZf!dSg^3@1tPEj%rKMB!S5kB9x{a}|Q0RTDx1*iqElLqVwussGqK<RB5
zNnW*nr{52-QpbszVJ1it(S5fOy0GtXw<a+Fw#ETo0yy*cvSb}>?I^yy?QnAQW4+9L
zF&bk_0zfpzHh>N@L7w#3^E0&#M<i6XFovrUGo!EV?A&NyG{)^KV_6VY(<_WE5eZ>{
z^ael}00>YEfbhd8Ym!29RTXen8Z$w}OozOI{UEIt<Y`4Nw+Y!r56IIB(t;@2+yqvt
z*-X%?@LC6Zq`S;;dFx@@i}*2k%Sw+vUM7%T9zN@<i`>?AZxFh-%9YPdMgV*&Z~&lG
zSs9fcdBB6O8l3dVqh5BFLdwa7X1@14`P{c#O@HfRWn~qB($$x^_BZzTxStY<fO3}Z
xMq_NQ&1xp7MQENI%qqhwe=ZthwQj-x)E{H~B#C0KngRd-002ovPDHLkV1oMDLjeE)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..3193a3535f1e390211aa403ad757b10cc9d5a884
GIT binary patch
literal 723
zc$@*#0xbQBP)<h;3K|Lk000e1NJLTq000mG000mO1^@s6AM^iV00001b5ch_0Itp)
z=>Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01FcU01FcV0GgZ_00007bV*G`2iXG{
z4;CqYo`_)p00L7<L_t(I%cYagYZ^fm$G<zfYK*!h27jz^)pc1*3W62Grch5MAR#xQ
zr(SYN+Dnll^y(i_JmjpG^bqqmB(wz&rKLGEmn;$DkJYA>7+PwL*wyJ}3&ut$^udE+
z-giFl&Ad0jY_nRe);n&udu4NTbAFbH%nGQg8aO>YO*I;gN0CTGp_DTJ4A|b@wsblj
zXEYjp;BvWCNs?kmM@Rf^kiP(wQch76rBbOR0N4O5ZES2PwOTEDo1Oq*0W1OVM<S8%
z&dyGv-|w%iuCCq(AOT=ySzfQ#>&pzo1Q!<<Lje5d8I}RCyt}(AX0zD<A;iM-yqjg&
zWt+`*4}i~1WH$_>W*El#<>lpTLI~O4-@nM`^A{X|kt9jZKp^nUEcrVCCxE$$O0PMN
z3v(P73I>A@0kFwrG6$f|0_YzfACDIn7S7`FxXLgLOb|PD-7x?I0QUCwUTke`oec(q
z&ny7i?RLLatJR*}Zf~YisU#tU0bm6%`Rc}aJRX!vrI*EG@sqCWIskejg~#KOPfkvr
zP)grYN<UCaKTi02P1Bwx5{bJr+o-ClMHEE`fT6j?nmz_F5Cmb~VzC%A0bZ}y%k%s?
z01f~djYdC)!{N25`{{H#GqG6AH#tz;pG7zvo|9$SK?vah*fmXiRjE{(nx=JgxtwQh
zZS6TBWROm$J)$Vi0l2;aXf~T{tJUiEdcAjrLgCHT)zz0!D0F^saPaH!@X(*hWb);5
z`LV9+U)$|AciR_W00;nrKA+Dq9TWs%-kid~RCoT*egieo&tASG=hy%M002ovPDHLk
FV1lgCI7$Ej
--- a/browser/themes/osx/browser.css
+++ b/browser/themes/osx/browser.css
@@ -896,32 +896,52 @@ toolbar[mode="icons"] #forward-button:-m
 
   #history-button[checked="true"] {
     -moz-image-region: rect(40px, 320px, 80px, 280px);
   }
 }
 
 /* bookmark sidebar & menu buttons */
 
-#bookmarks-button {
+#bookmarks-button,
+#bookmarks-menu-button {
   -moz-image-region: rect(0, 180px, 20px, 160px);
 }
 
 #bookmarks-button[checked="true"] {
   -moz-image-region: rect(20px, 180px, 40px, 160px);
 }
 
+#bookmarks-menu-button.bookmark-item {
+  -moz-image-region: rect(2px, 178px, 18px, 162px);
+  list-style-image: url("chrome://browser/skin/Toolbar.png");
+}
+
 @media (min-resolution: 2dppx) {
-  #bookmarks-button {
+  #bookmarks-button,
+  #bookmarks-menu-button {
     -moz-image-region: rect(0, 360px, 40px, 320px);
   }
 
   #bookmarks-button[checked="true"] {
     -moz-image-region: rect(40px, 360px, 80px, 320px);
   }
+
+  #bookmarks-menu-button.bookmark-item {
+    -moz-image-region: rect(4px, 356px, 36px, 324px);
+    list-style-image: url("chrome://browser/skin/Toolbar@2x.png");
+  }
+
+  #bookmarks-menu-button.bookmark-item > .toolbarbutton-icon {
+    width: 16px;
+  }
+}
+
+#bookmarks-menu-button.toolbarbutton-1 {
+  -moz-box-orient: horizontal;
 }
 
 /* print button */
 
 #print-button {
   -moz-image-region: rect(0, 200px, 20px, 180px);
 }
 
@@ -1712,59 +1732,45 @@ window[tabsontop="false"] richlistitem[t
 }
 
 /* social recommending panel */
 
 #social-mark-button {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-/* bookmarks menu-button */
-
-#bookmarks-menu-button {
-  -moz-image-region: rect(0px 500px 20px 480px);
-}
-
-#bookmarks-menu-button[starred] {
-  -moz-image-region: rect(20px 500px 40px 480px);
-}
-
-#bookmarks-menu-button.bookmark-item {
+/* Star button */
+#star-button {
   list-style-image: url("chrome://browser/skin/places/star-icons.png");
-  -moz-image-region: rect(0px 16px 16px 0px);
-}
-
-#bookmarks-menu-button.bookmark-item[starred] {
-  -moz-image-region: rect(0px 32px 16px 16px);
-}
-
-#bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button {
-  padding: 0;
+  -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+#star-button:hover:active,
+#star-button[starred="true"] {
+  -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+#star-button:hover:active[starred="true"] {
+  -moz-image-region: rect(0, 48px, 16px, 32px);
 }
 
 @media (min-resolution: 2dppx) {
-  #bookmarks-menu-button {
-    -moz-image-region: rect(0px, 1000px, 40px, 960px);
-  }
-
-  #bookmarks-menu-button[starred] {
-    -moz-image-region: rect(40px, 1000px, 80px, 960px);
+  #star-button {
+    list-style-image: url("chrome://browser/skin/places/star-icons.png");
+    -moz-image-region: rect(0, 32px, 32px, 0);
+    width: 22px;
   }
 
-  #bookmarks-menu-button.bookmark-item {
-    list-style-image: url("chrome://browser/skin/places/star-icons@2x.png");
-    -moz-image-region: rect(0px 32px 32px 0px);
+  #star-button:hover:active,
+  #star-button[starred="true"] {
+    -moz-image-region: rect(0, 64px, 32px, 32px);
   }
 
-  #bookmarks-menu-button.bookmark-item[starred] {
-    -moz-image-region: rect(0px 64px 32px 32px);
-  }
-
-  #bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button > .toolbarbutton-icon {
-    width: 16px;
+  #star-button:hover:active[starred="true"] {
+    -moz-image-region: rect(0, 96px, 32px, 64px);
   }
 }
 
 /* BOOKMARKING PANEL */
 #editBookmarkPanelStarIcon {
   list-style-image: url("chrome://browser/skin/places/starred48.png");
   width: 48px;
   height: 48px;
--- a/browser/themes/windows/browser.css
+++ b/browser/themes/windows/browser.css
@@ -602,17 +602,17 @@ toolbarbutton.bookmark-item {
 toolbarbutton.bookmark-item:hover:active:not([disabled="true"]),
 toolbarbutton.bookmark-item[open="true"] {
   padding-top: 3px;
   padding-bottom: 1px;
   -moz-padding-start: 4px;
   -moz-padding-end: 2px;
 }
 
-.bookmark-item > .toolbarbutton-icon {
+.bookmark-item:not(#bookmarks-menu-button) > .toolbarbutton-icon {
   width: 16px;
   height: 16px;
 }
 
 /* Prevent [mode="icons"] from hiding the label */
 .bookmark-item > .toolbarbutton-text {
   display: -moz-box !important;
 }
@@ -1080,20 +1080,29 @@ toolbar[mode=full] .toolbarbutton-1 > .t
 #downloads-button {
   -moz-image-region: rect(0, 108px, 18px, 90px);
 }
 
 #history-button {
   -moz-image-region: rect(0, 126px, 18px, 108px);
 }
 
-#bookmarks-button {
+#bookmarks-button,
+#bookmarks-menu-button {
   -moz-image-region: rect(0, 144px, 18px, 126px);
 }
 
+#bookmarks-menu-button.bookmark-item {
+  list-style-image: url("chrome://browser/skin/Toolbar.png");
+}
+
+#bookmarks-menu-button.bookmark-item:-moz-lwtheme-brighttext {
+  list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
+}
+
 #print-button {
   -moz-image-region: rect(0, 162px, 18px, 144px);
 }
 
 #new-tab-button {
   -moz-image-region: rect(0, 180px, 18px, 162px);
 }
 
@@ -1801,37 +1810,35 @@ richlistitem[type~="action"][actiontype=
 }
 
 /* social recommending panel */
 
 #social-mark-button {
   -moz-image-region: rect(0, 16px, 16px, 0);
 }
 
-/* bookmarks menu-button */
-
-#bookmarks-menu-button {
-  -moz-image-region: rect(0px 378px 18px 360px);
-}
-
-#bookmarks-menu-button[starred] {
-  -moz-image-region: rect(18px 378px 36px 360px);
-}
-
-#bookmarks-menu-button.bookmark-item {
+/* star button */
+
+#star-button {
   list-style-image: url("chrome://browser/skin/places/bookmark.png");
   -moz-image-region: rect(0px 16px 16px 0px);
 }
 
-#bookmarks-menu-button.bookmark-item[starred] {
+#star-button:hover {
+  background-image: radial-gradient(circle closest-side, hsla(45,100%,73%,.3), hsla(45,100%,73%,0));
+  -moz-image-region: rect(0px 32px 16px 16px);
+}
+
+#star-button:hover:active {
+  background-image: radial-gradient(circle closest-side, hsla(45,100%,73%,.1), hsla(45,100%,73%,0));
   -moz-image-region: rect(0px 48px 16px 32px);
 }
 
-#bookmarks-menu-button.bookmark-item > .toolbarbutton-menubutton-button> .toolbarbutton-icon {
-  -moz-margin-start: 5px;
+#star-button[starred] {
+  list-style-image: url("chrome://browser/skin/places/editBookmark.png");
 }
 
 /* bookmarking panel */
 
 #editBookmarkPanelStarIcon {
   list-style-image: url("chrome://browser/skin/places/starred48.png");
   width: 48px;
   height: 48px;
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -90,16 +90,17 @@ browser.jar:
         skin/classic/browser/feeds/videoFeedIcon16.png               (feeds/feedIcon16.png)
         skin/classic/browser/feeds/subscribe.css                     (feeds/subscribe.css)
         skin/classic/browser/feeds/subscribe-ui.css                  (feeds/subscribe-ui.css)
         skin/classic/browser/newtab/newTab.css                       (newtab/newTab.css)
         skin/classic/browser/newtab/controls.png                     (newtab/controls.png)
         skin/classic/browser/newtab/noise.png                        (newtab/noise.png)
         skin/classic/browser/places/places.css                       (places/places.css)
 *       skin/classic/browser/places/organizer.css                    (places/organizer.css)
+        skin/classic/browser/places/editBookmark.png                 (places/editBookmark.png)
         skin/classic/browser/places/bookmark.png                     (places/bookmark.png)
         skin/classic/browser/places/query.png                        (places/query.png)
         skin/classic/browser/places/bookmarksMenu.png                (places/bookmarksMenu.png)
         skin/classic/browser/places/bookmarksToolbar.png             (places/bookmarksToolbar.png)
         skin/classic/browser/places/calendar.png                     (places/calendar.png)
         skin/classic/browser/places/toolbarDropMarker.png            (places/toolbarDropMarker.png)
         skin/classic/browser/places/editBookmarkOverlay.css          (places/editBookmarkOverlay.css)
         skin/classic/browser/places/libraryToolbar.png               (places/libraryToolbar.png)
@@ -341,16 +342,17 @@ browser.jar:
         skin/classic/aero/browser/feeds/subscribe.css                (feeds/subscribe.css)
         skin/classic/aero/browser/feeds/subscribe-ui.css             (feeds/subscribe-ui.css)
         skin/classic/aero/browser/newtab/newTab.css                  (newtab/newTab.css)
         skin/classic/aero/browser/newtab/controls.png                (newtab/controls.png)
         skin/classic/aero/browser/newtab/noise.png                   (newtab/noise.png)
 *       skin/classic/aero/browser/places/places.css                  (places/places-aero.css)
 *       skin/classic/aero/browser/places/organizer.css               (places/organizer-aero.css)
         skin/classic/aero/browser/places/bookmark.png                (places/bookmark.png)
+        skin/classic/aero/browser/places/editBookmark.png            (places/editBookmark.png)
         skin/classic/aero/browser/places/query.png                   (places/query-aero.png)
         skin/classic/aero/browser/places/bookmarksMenu.png           (places/bookmarksMenu-aero.png)
         skin/classic/aero/browser/places/bookmarksToolbar.png        (places/bookmarksToolbar-aero.png)
         skin/classic/aero/browser/places/calendar.png                (places/calendar-aero.png)
         skin/classic/aero/browser/places/toolbarDropMarker.png       (places/toolbarDropMarker-aero.png)
         skin/classic/aero/browser/places/editBookmarkOverlay.css     (places/editBookmarkOverlay.css)
         skin/classic/aero/browser/places/libraryToolbar.png          (places/libraryToolbar-aero.png)
         skin/classic/aero/browser/places/starred48.png               (places/starred48-aero.png)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fbca0523df169b6892d2b5050035152fb6b92606
GIT binary patch
literal 1642
zc$@)h29^1VP)<h;3K|Lk000e1NJLTq001xm000mO1^@s6P_F#3000ItNkl<Zc-obg
zc~DbV6vi)kF0DAzacPUCj;MfEi$zf!rS7-@tu-R+pjA-<f;u8rKqRqjL1c4S)UuRC
zkYUjv${L_na0fwx7y`o7ju=Q-D~rX`bCEcb29-a4Gv}T29XQ{8=RP1#-;&W#<5@Ef
zJS*mB8Y5(ksFgYrhf)stU_St;#NTAvC4ZBtF*F{9(SNA7IisD*UoJ@US#lX;t+5&?
z+DGQrM6422hZ<fkv?sI1Y9PRLn&H)Bi$HKx4=~mrs{t0*f^loO#}kWhd(~llnC8ZE
z`Xvs;RbCw?mQ@DV74Ij9Xl^V9F4)b=ZHzYoN!%8QwwakVCIjAVMmNRJFb8_E4@o?=
zIQ!%_X3obND6l8m)n|=zV4xf*4?_&I3-%JNk2SzIn{HTo$`Jm^wT7N+Rv_dp1I+1v
z&;V}-!{Tmd=5Wm!56N9`iNe5>@)Kgr9j7&_8Ep_eX%?=_Y~g-#Tvin-Q5a~e2ohuN
z7_CvwXa;;XO<{274K8wY3YrtmV6b@S5Kj|KA=yDE*Kf;I1F9KefX8Mw^0qT>Na(K?
z6H=16OLE0!CnV650U-AQX!ivg(;Tf)4QhjF5B9DrvL_pH-`#}!NJuefTuNo|^4zwH
zGZN@819*7}Ndp?w9Ia6eYR}Lf>|KXLIfZT45X9}CU7zMW9}2t-;K_weL#Gd=yC_3(
z9p-555^7Le7m7XDYflYC*OeZVbT>zLLuV?yMao6I-q6Z}cWYfuQC*Kq-0HD698Wd6
z^=^Qq@5SYA=(_=L=}AHohDnBZYke)5UD7*#_1GI8Za(ALtw1AaN-zUyIvYB#T0(z;
zExaz-sW3p_0?pAH)u7fC$^%wH2wS6`cy4Ab<ZWYqe^Pw35oCz~&)nY+YU+eX8n7qM
z-I%49WlMB%=i^)N`ZvPM5;Pd|eo%wMH(*a}rY+G9VSl+4_g&V0Wd{yQ2Yx?PBS)=}
zyHpxxK0}Z0;HMtOti`;oOtPfpNHa+Bmo&Pm25V0W4?RU~lE(&?KK803<1=i?g1o&X
zx$Ry+GxQV!$fH$*HR)}ir>IR#w<hzkcO1fswEo(ljK8v@E5{06(?S1G4Xx3)aMOQ<
z414qymzY5&c^b0}c|S5~ubRVOK?AN$-Qkfm@?fATYydT>)G#$Z+^aThVo~lMk~Sz4
zyaHJ)u3gwj<O%R^+m!*-q~h=kXb<gGT;eF(nJXSftncm<;_BSoqmtetJ9rSjt|QWB
zrh)QTh(Xl7O_?L2D|?!tkq=OHU?fk2peo(2!P|s!p@xTh2t{EcXBRp6ywtr3<mmv<
zPme^N1%DU1HlT*17+(1+l;Nru)t+YyaA%)NWY?`B?eN0j(LST&xu#6OUqTlth&%!A
zIRI3<;qb0+;#x0gE<DhJ8lT|-h!V`TUBtJ&B2Wr1V(`BB0<=Wn@Mqt|(m0Uc_isTB
zPhno!K+3@dxorh@02PiZX+?8M2j&Y#HNZ1rl6xMw_rhZyfY=vgWk<R?Zo743j0R2u
zjZqUrcURS4Z#><kzQ&kdkY2>KNr#@tXS-gCqq=DfFA{;qsQHa*RT&U?88r9Y!qcvC
zfkAxh{SP>ZBMeqM59K<`_ClunVv$M%$5*jF<FnO;bol2*j`C|h%d5GYnHVHKpKnyt
zTI$j<DDocUZDFpvE?1kRY#@P6Cg*b|2|=0yot6IbN9Q(HU)@R0BeAuyW;NZ9f;wKe
zBn_g0>k%eoqOyTNw#MX=(6!M2$QiotbD&;e0e?msSC&U@6iKozhjP7DPEd4abw8i2
zF+tgY+Q|*fH*rq0@8ky<iMX4X1h4h1t(c!m@!G&xU@oaJ@cY{H_L}8bo2zUf(}w7l
zap&C;#hQw;?MQrvHQ7urIt4HIR?H>T#{E!x!$YeaYg)<%PVe|iKR<X4)aO`3UeIdM
zx&1m;iXqQC=<F^CUQ<_}V*`1oR|9J2{m+2sT9zgb$*05O^AtoKnMk3r^LJz#nzW)C
z)PAA-&snMk(Gb`XKOFWaeEtNPfG`vq|FVpzqe(W^B%71z$_9cgH8mscwX@D~ba&Hd
oWg_;@Ww2&XHK#2#KUd`c1L=DksDR{$rvLx|07*qoM6N<$f`AMCM*si-
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -912,17 +912,17 @@ user_pref("camino.use_system_proxy_setti
     encoded = base64.b64encode(image)
     self.log.info("SCREENSHOT: data:image/png;base64,%s", encoded)
 
   def killAndGetStack(self, processPID, utilityPath, debuggerInfo):
     """Kill the process, preferrably in a way that gets us a stack trace.
        Also attempts to obtain a screenshot before killing the process."""
     if not debuggerInfo:
       self.dumpScreen(utilityPath)
-    self.killAndGetStackNoScreenshot(self, processPID, utilityPath, debuggerInfo)
+    self.killAndGetStackNoScreenshot(processPID, utilityPath, debuggerInfo)
 
   def killAndGetStackNoScreenshot(self, processPID, utilityPath, debuggerInfo):
     """Kill the process, preferrably in a way that gets us a stack trace."""
     if self.CRASHREPORTER and not debuggerInfo:
       if self.UNIXISH:
         # ABRT will get picked up by Breakpad's signal handler
         os.kill(processPID, signal.SIGABRT)
         return
--- a/config/tests/unit-writemozinfo.py
+++ b/config/tests/unit-writemozinfo.py
@@ -170,22 +170,26 @@ class TestWriteJson(unittest.TestCase):
         os.unlink(self.f)
 
     def testBasic(self):
         """
         Test that writing to a file produces correct output.
         """
         write_json(self.f, env={'OS_TARGET':'WINNT',
                                 'TARGET_CPU':'i386',
+                                'TOPSRCDIR':'/tmp',
+                                'MOZCONFIG':'foo',
                                 'MOZ_WIDGET_TOOLKIT':'windows'})
         with open(self.f) as f:
             d = json.load(f)
             self.assertEqual('win', d['os'])
             self.assertEqual('x86', d['processor'])
             self.assertEqual('windows', d['toolkit'])
+            self.assertEqual('/tmp', d['topsrcdir'])
+            self.assertEqual(os.path.normpath('/tmp/foo'), d['mozconfig'])
             self.assertEqual(32, d['bits'])
 
     def testFileObj(self):
         """
         Test that writing to a file-like object produces correct output.
         """
         s = StringIO()
         write_json(s, env={'OS_TARGET':'WINNT',
--- a/config/writemozinfo.py
+++ b/config/writemozinfo.py
@@ -25,17 +25,20 @@ def build_dict(env=os.environ):
     # Check that all required variables are present first.
     required = ["TARGET_CPU", "OS_TARGET", "MOZ_WIDGET_TOOLKIT"]
     missing = [r for r in required if r not in env]
     if missing:
         raise Exception("Missing required environment variables: %s" %
                         ', '.join(missing))
 
     if 'MOZCONFIG' in env:
-        d["mozconfig"] = env["MOZCONFIG"]
+        mozconfig = env["MOZCONFIG"]
+        if 'TOPSRCDIR' in env:
+            mozconfig = os.path.join(env["TOPSRCDIR"], mozconfig)
+        d['mozconfig'] = os.path.normpath(mozconfig)
 
     if 'TOPSRCDIR' in env:
         d["topsrcdir"] = env["TOPSRCDIR"]
 
     # os
     o = env["OS_TARGET"]
     known_os = {"Linux": "linux",
                 "WINNT": "win",
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -498,41 +498,55 @@ public:
 private:
   static bool
   FindAttributeDependence(const nsIAtom* aAttribute,
                           const MappedAttributeEntry* const aMaps[],
                           uint32_t aMapCount);
 
 protected:
   inline bool GetAttr(int32_t aNameSpaceID, nsIAtom* aName,
-                      mozilla::dom::DOMString& aResult) const
+                      DOMString& aResult) const
   {
     NS_ASSERTION(nullptr != aName, "must have attribute name");
     NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown,
                  "must have a real namespace ID!");
     MOZ_ASSERT(aResult.HasStringBuffer() && aResult.StringBufferLength() == 0,
                "Should have empty string coming in");
     const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID);
     if (val) {
       val->ToString(aResult);
+      return true;
     }
     // else DOMString comes pre-emptied.
-    return val != nullptr;
+    return false;
   }
 
 public:
+  inline bool GetAttr(const nsAString& aName, DOMString& aResult) const
+  {
+    MOZ_ASSERT(aResult.HasStringBuffer() && aResult.StringBufferLength() == 0,
+               "Should have empty string coming in");
+    const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName);
+    if (val) {
+      val->ToString(aResult);
+      return true;
+    }
+    // else DOMString comes pre-emptied.
+    return false;
+  }
+
   void GetTagName(nsAString& aTagName) const
   {
     aTagName = NodeName();
   }
   void GetId(nsAString& aId) const
   {
     GetAttr(kNameSpaceID_None, nsGkAtoms::id, aId);
   }
-  void GetId(mozilla::dom::DOMString& aId) const
+  void GetId(DOMString& aId) const
   {
     GetAttr(kNameSpaceID_None, nsGkAtoms::id, aId);
   }
   void SetId(const nsAString& aId)
   {
     SetAttr(kNameSpaceID_None, nsGkAtoms::id, aId, true);
   }
 
@@ -543,22 +557,22 @@ public:
     if (!slots->mAttributeMap) {
       slots->mAttributeMap = new nsDOMAttributeMap(this);
     }
 
     return slots->mAttributeMap;
   }
   void GetAttribute(const nsAString& aName, nsString& aReturn)
   {
-    mozilla::dom::DOMString str;
+    DOMString str;
     GetAttribute(aName, str);
     str.ToString(aReturn);
   }
 
-  void GetAttribute(const nsAString& aName, mozilla::dom::DOMString& aReturn);
+  void GetAttribute(const nsAString& aName, DOMString& aReturn);
   void GetAttributeNS(const nsAString& aNamespaceURI,
                       const nsAString& aLocalName,
                       nsAString& aReturn);
   void SetAttribute(const nsAString& aName, const nsAString& aValue,
                     ErrorResult& aError);
   void SetAttributeNS(const nsAString& aNamespaceURI,
                       const nsAString& aLocalName,
                       const nsAString& aValue,
@@ -702,38 +716,36 @@ public:
   int32_t ScrollLeftMax()
   {
     nsIScrollableFrame* sf = GetScrollFrame();
     return sf ?
            nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().XMost()) :
            0;
   }
 
-  virtual already_AddRefed<mozilla::dom::UndoManager> GetUndoManager()
+  virtual already_AddRefed<UndoManager> GetUndoManager()
   {
     return nullptr;
   }
 
   virtual bool UndoScope()
   {
     return false;
   }
 
-  virtual void SetUndoScope(bool aUndoScope, mozilla::ErrorResult& aError)
+  virtual void SetUndoScope(bool aUndoScope, ErrorResult& aError)
   {
   }
 
-  virtual void GetInnerHTML(nsAString& aInnerHTML,
-                            mozilla::ErrorResult& aError);
-  virtual void SetInnerHTML(const nsAString& aInnerHTML,
-                            mozilla::ErrorResult& aError);
-  void GetOuterHTML(nsAString& aOuterHTML, mozilla::ErrorResult& aError);
-  void SetOuterHTML(const nsAString& aOuterHTML, mozilla::ErrorResult& aError);
+  virtual void GetInnerHTML(nsAString& aInnerHTML, ErrorResult& aError);
+  virtual void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError);
+  void GetOuterHTML(nsAString& aOuterHTML, ErrorResult& aError);
+  void SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError);
   void InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText,
-                          mozilla::ErrorResult& aError);
+                          ErrorResult& aError);
 
   //----------------------------------------
 
   /**
    * Add a script event listener with the given event handler name
    * (like onclick) and with the value as JS
    * @param aEventName the event listener name
    * @param aValue the JS to attach
@@ -759,17 +771,17 @@ public:
    *
    * @param aFlags      Extra flags for the dispatching event.  The true flags
    *                    will be respected.
    */
   static nsresult DispatchClickEvent(nsPresContext* aPresContext,
                                      nsInputEvent* aSourceEvent,
                                      nsIContent* aTarget,
                                      bool aFullDispatch,
-                                     const mozilla::widget::EventFlags* aFlags,
+                                     const widget::EventFlags* aFlags,
                                      nsEventStatus* aStatus);
 
   /**
    * Method to dispatch aEvent to aTarget. If aFullDispatch is true, the event
    * will be dispatched through the full dispatching of the presshell of the
    * aPresContext; if it's false the event will be dispatched only as a DOM
    * event.
    * If aPresContext is nullptr, this does nothing.
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -1859,16 +1859,24 @@ public:
    * fullscreen by setting the "fullScreen" attribute on its XUL window.
    */
   static bool IsFullscreenApiContentOnly();
 
   /**
    * Returns true if the idle observers API is enabled.
    */
   static bool IsIdleObserverAPIEnabled() { return sIsIdleObserverAPIEnabled; }
+
+  /*
+   * Returns true if the performance timing APIs are enabled.
+   */
+  static bool IsPerformanceTimingEnabled()
+  {
+    return sIsPerformanceTimingEnabled;
+  }
   
   /**
    * Returns true if the doc tree branch which contains aDoc contains any
    * plugins which we don't control event dispatch for, i.e. do any plugins
    * in the same tab as this document receive key events outside of our
    * control? This always returns false on MacOSX.
    */
   static bool HasPluginWithUncontrolledEventDispatch(nsIDocument* aDoc);
@@ -2223,16 +2231,17 @@ private:
 
   static bool sIsHandlingKeyBoardEvent;
   static bool sAllowXULXBL_for_file;
   static bool sIsFullScreenApiEnabled;
   static bool sTrustedFullScreenOnly;
   static bool sFullscreenApiIsContentOnly;
   static uint32_t sHandlingInputTimeout;
   static bool sIsIdleObserverAPIEnabled;
+  static bool sIsPerformanceTimingEnabled;
 
   static nsHtml5StringParser* sHTMLFragmentParser;
   static nsIParser* sXMLFragmentParser;
   static nsIFragmentContentSink* sXMLFragmentSink;
 
   /**
    * True if there's a fragment parser activation on the stack.
    */
--- a/content/base/src/nsAttrAndChildArray.cpp
+++ b/content/base/src/nsAttrAndChildArray.cpp
@@ -314,16 +314,33 @@ nsAttrAndChildArray::GetAttr(nsIAtom* aL
       }
     }
   }
 
   return nullptr;
 }
 
 const nsAttrValue*
+nsAttrAndChildArray::GetAttr(const nsAString& aLocalName) const
+{
+  uint32_t i, slotCount = AttrSlotCount();
+  for (i = 0; i < slotCount && AttrSlotIsTaken(i); ++i) {
+    if (ATTRS(mImpl)[i].mName.Equals(aLocalName)) {
+      return &ATTRS(mImpl)[i].mValue;
+    }
+  }
+
+  if (mImpl && mImpl->mMappedAttrs) {
+    return mImpl->mMappedAttrs->GetAttr(aLocalName);
+  }
+
+  return nullptr;
+}
+
+const nsAttrValue*
 nsAttrAndChildArray::GetAttr(const nsAString& aName,
                              nsCaseTreatment aCaseSensitive) const
 {
   // Check whether someone is being silly and passing non-lowercase
   // attr names.
   if (aCaseSensitive == eIgnoreCase &&
       nsContentUtils::StringContainsASCIIUpper(aName)) {
     // Try again with a lowercased name, but make sure we can't reenter this
--- a/content/base/src/nsAttrAndChildArray.h
+++ b/content/base/src/nsAttrAndChildArray.h
@@ -66,17 +66,21 @@ public:
   nsresult InsertChildAt(nsIContent* aChild, uint32_t aPos);
   void RemoveChildAt(uint32_t aPos);
   // Like RemoveChildAt but hands the reference to the child being
   // removed back to the caller instead of just releasing it.
   already_AddRefed<nsIContent> TakeChildAt(uint32_t aPos);
   int32_t IndexOfChild(const nsINode* aPossibleChild) const;
 
   uint32_t AttrCount() const;
-  const nsAttrValue* GetAttr(nsIAtom* aLocalName, int32_t aNamespaceID = kNameSpaceID_None) const;
+  const nsAttrValue* GetAttr(nsIAtom* aLocalName,
+                             int32_t aNamespaceID = kNameSpaceID_None) const;
+  // As above but using a string attr name and always using
+  // kNameSpaceID_None.  This is always case-sensitive.
+  const nsAttrValue* GetAttr(const nsAString& aName) const;
   // Get an nsAttrValue by qualified name.  Can optionally do
   // ASCII-case-insensitive name matching.
   const nsAttrValue* GetAttr(const nsAString& aName,
                              nsCaseTreatment aCaseSensitive) const;
   const nsAttrValue* AttrAt(uint32_t aPos) const;
   nsresult SetAndTakeAttr(nsIAtom* aLocalName, nsAttrValue& aValue);
   nsresult SetAndTakeAttr(nsINodeInfo* aName, nsAttrValue& aValue);
 
--- a/content/base/src/nsAttrName.h
+++ b/content/base/src/nsAttrName.h
@@ -100,16 +100,22 @@ public:
   }
 
   // Faster comparison in the case we know the namespace is null
   bool Equals(nsIAtom* aAtom) const
   {
     return reinterpret_cast<uintptr_t>(aAtom) == mBits;
   }
 
+  // And the same but without forcing callers to atomize
+  bool Equals(const nsAString& aLocalName) const
+  {
+    return IsAtom() && Atom()->Equals(aLocalName);
+  }
+
   bool Equals(nsIAtom* aLocalName, int32_t aNamespaceID) const
   {
     if (aNamespaceID == kNameSpaceID_None) {
       return Equals(aLocalName);
     }
     return !IsAtom() && NodeInfo()->Equals(aLocalName, aNamespaceID);
   }
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -226,16 +226,17 @@ nsString* nsContentUtils::sOSText = null
 nsString* nsContentUtils::sAltText = nullptr;
 nsString* nsContentUtils::sModifierSeparator = nullptr;
 
 bool nsContentUtils::sInitialized = false;
 bool nsContentUtils::sIsFullScreenApiEnabled = false;
 bool nsContentUtils::sTrustedFullScreenOnly = true;
 bool nsContentUtils::sFullscreenApiIsContentOnly = false;
 bool nsContentUtils::sIsIdleObserverAPIEnabled = false;
+bool nsContentUtils::sIsPerformanceTimingEnabled = false;
 
 uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
 
 nsHtml5StringParser* nsContentUtils::sHTMLFragmentParser = nullptr;
 nsIParser* nsContentUtils::sXMLFragmentParser = nullptr;
 nsIFragmentContentSink* nsContentUtils::sXMLFragmentSink = nullptr;
 bool nsContentUtils::sFragmentParsingActive = false;
 
@@ -422,16 +423,19 @@ nsContentUtils::Init()
   // with this pref, as it affects the security model of the fullscreen API.
   sFullscreenApiIsContentOnly = Preferences::GetBool("full-screen-api.content-only", false);
 
   Preferences::AddBoolVarCache(&sTrustedFullScreenOnly,
                                "full-screen-api.allow-trusted-requests-only");
 
   sIsIdleObserverAPIEnabled = Preferences::GetBool("dom.idle-observers-api.enabled", true);
 
+  Preferences::AddBoolVarCache(&sIsPerformanceTimingEnabled,
+                               "dom.enable_performance", true);
+
   Preferences::AddUintVarCache(&sHandlingInputTimeout,
                                "dom.event.handling-user-input-time-limit",
                                1000);
 
   Element::InitCCCallbacks();
 
   sInitialized = true;
 
--- a/content/base/src/nsScriptLoader.cpp
+++ b/content/base/src/nsScriptLoader.cpp
@@ -843,17 +843,18 @@ nsScriptLoader::EvaluateScript(nsScriptL
   JSVersion version = JSVersion(aRequest->mJSVersion);
   if (version != JSVERSION_UNKNOWN) {
     JS::CompileOptions options(cx);
     options.setFileAndLine(url.get(), aRequest->mLineNo)
            .setVersion(JSVersion(aRequest->mJSVersion));
     if (aRequest->mOriginPrincipal) {
       options.setOriginPrincipals(nsJSPrincipals::get(aRequest->mOriginPrincipal));
     }
-    rv = context->EvaluateString(aScript, *globalObject->GetGlobalJSObject(),
+    JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
+    rv = context->EvaluateString(aScript, global,
                                  options, /* aCoerceToString = */ false, nullptr);
   }
 
   // Put the old script back in case it wants to do anything else.
   mCurrentScript = oldCurrent;
 
   JSAutoRequest ar(cx);
   context->SetProcessingScriptTag(oldProcessingScriptTag);
--- a/content/html/content/src/nsDOMStringMap.cpp
+++ b/content/html/content/src/nsDOMStringMap.cpp
@@ -60,29 +60,26 @@ nsDOMStringMap::~nsDOMStringMap()
 JSObject*
 nsDOMStringMap::WrapObject(JSContext *cx, JS::Handle<JSObject*> scope)
 {
   return DOMStringMapBinding::Wrap(cx, scope, this);
 }
 
 void
 nsDOMStringMap::NamedGetter(const nsAString& aProp, bool& found,
-                            nsString& aResult) const
+                            DOMString& aResult) const
 {
   nsAutoString attr;
 
   if (!DataPropToAttr(aProp, attr)) {
     found = false;
     return;
   }
 
-  nsCOMPtr<nsIAtom> attrAtom = do_GetAtom(attr);
-  MOZ_ASSERT(attrAtom, "Should be infallible");
-
-  found = mElement->GetAttr(kNameSpaceID_None, attrAtom, aResult);
+  found = mElement->GetAttr(attr, aResult);
 }
 
 void
 nsDOMStringMap::NamedSetter(const nsAString& aProp,
                             const nsAString& aValue,
                             ErrorResult& rv)
 {
   nsAutoString attr;
@@ -134,110 +131,107 @@ nsDOMStringMap::NamedDeleter(const nsASt
 void
 nsDOMStringMap::GetSupportedNames(nsTArray<nsString>& aNames)
 {
   uint32_t attrCount = mElement->GetAttrCount();
 
   // Iterate through all the attributes and add property
   // names corresponding to data attributes to return array.
   for (uint32_t i = 0; i < attrCount; ++i) {
-    nsAutoString attrString;
     const nsAttrName* attrName = mElement->GetAttrNameAt(i);
     // Skip the ones that are not in the null namespace
     if (attrName->NamespaceID() != kNameSpaceID_None) {
       continue;
     }
-    attrName->LocalName()->ToString(attrString);
 
     nsAutoString prop;
-    if (!AttrToDataProp(attrString, prop)) {
+    if (!AttrToDataProp(nsDependentAtomString(attrName->LocalName()),
+                        prop)) {
       continue;
     }
 
     aNames.AppendElement(prop);
   }
 }
 
 /**
  * Converts a dataset property name to the corresponding data attribute name.
  * (ex. aBigFish to data-a-big-fish).
  */
 bool nsDOMStringMap::DataPropToAttr(const nsAString& aProp,
-                                      nsAString& aResult)
+                                    nsAutoString& aResult)
 {
-  const PRUnichar* cur = aProp.BeginReading();
-  const PRUnichar* end = aProp.EndReading();
-
-  // String corresponding to the data attribute on the element.
-  nsAutoString attr;
-  // Length of attr will be at least the length of the property + 5 for "data-".
-  attr.SetCapacity(aProp.Length() + 5);
-
-  attr.Append(NS_LITERAL_STRING("data-"));
+  // aResult is an autostring, so don't worry about setting its capacity:
+  // SetCapacity is slow even when it's a no-op and we already have enough
+  // storage there for most cases, probably.
+  aResult.AppendLiteral("data-");
 
   // Iterate property by character to form attribute name.
   // Return syntax error if there is a sequence of "-" followed by a character
   // in the range "a" to "z".
   // Replace capital characters with "-" followed by lower case character.
   // Otherwise, simply append character to attribute name.
+  const PRUnichar* start = aProp.BeginReading();
+  const PRUnichar* end = aProp.EndReading();
+  const PRUnichar* cur = start;
   for (; cur < end; ++cur) {
     const PRUnichar* next = cur + 1;
     if (PRUnichar('-') == *cur && next < end &&
         PRUnichar('a') <= *next && *next <= PRUnichar('z')) {
       // Syntax error if character following "-" is in range "a" to "z".
       return false;
     }
 
     if (PRUnichar('A') <= *cur && *cur <= PRUnichar('Z')) {
+      // Append the characters in the range [start, cur)
+      aResult.Append(start, cur - start);
       // Uncamel-case characters in the range of "A" to "Z".
-      attr.Append(PRUnichar('-'));
-      attr.Append(*cur - 'A' + 'a');
-    } else {
-      attr.Append(*cur);
+      aResult.Append(PRUnichar('-'));
+      aResult.Append(*cur - 'A' + 'a');
+      start = next; // We've already appended the thing at *cur
     }
   }
 
-  aResult.Assign(attr);
+  aResult.Append(start, cur - start);
+
   return true;
 }
 
 /**
  * Converts a data attribute name to the corresponding dataset property name.
  * (ex. data-a-big-fish to aBigFish).
  */
 bool nsDOMStringMap::AttrToDataProp(const nsAString& aAttr,
-                                      nsAString& aResult)
+                                    nsAutoString& aResult)
 {
   // If the attribute name does not begin with "data-" then it can not be
   // a data attribute.
   if (!StringBeginsWith(aAttr, NS_LITERAL_STRING("data-"))) {
     return false;
   }
 
   // Start reading attribute from first character after "data-".
   const PRUnichar* cur = aAttr.BeginReading() + 5;
   const PRUnichar* end = aAttr.EndReading();
 
-  // Dataset property name. Ensure that the string is large enough to store
-  // all the characters in the property name.
-  nsAutoString prop;
+  // Don't try to mess with aResult's capacity: the probably-no-op SetCapacity()
+  // call is not that fast.
 
   // Iterate through attrName by character to form property name.
   // If there is a sequence of "-" followed by a character in the range "a" to
   // "z" then replace with upper case letter.
   // Otherwise append character to property name.
   for (; cur < end; ++cur) {
     const PRUnichar* next = cur + 1;
     if (PRUnichar('-') == *cur && next < end && 
         PRUnichar('a') <= *next && *next <= PRUnichar('z')) {
       // Upper case the lower case letters that follow a "-".
-      prop.Append(*next - 'a' + 'A');
+      aResult.Append(*next - 'a' + 'A');
       // Consume character to account for "-" character.
       ++cur;
     } else {
       // Simply append character if camel case is not necessary.
-      prop.Append(*cur);
+      aResult.Append(*cur);
     }
   }
 
-  aResult.Assign(prop);
   return true;
 }
--- a/content/html/content/src/nsDOMStringMap.h
+++ b/content/html/content/src/nsDOMStringMap.h
@@ -30,26 +30,27 @@ public:
     return mElement;
   }
 
   nsDOMStringMap(nsGenericHTMLElement* aElement);
 
   // WebIDL API
   virtual JSObject* WrapObject(JSContext *cx,
                                JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
-  void NamedGetter(const nsAString& aProp, bool& found, nsString& aResult) const;
+  void NamedGetter(const nsAString& aProp, bool& found,
+                   mozilla::dom::DOMString& aResult) const;
   void NamedSetter(const nsAString& aProp, const nsAString& aValue,
                    mozilla::ErrorResult& rv);
   void NamedDeleter(const nsAString& aProp, bool &found);
   void GetSupportedNames(nsTArray<nsString>& aNames);
 
 private:
   virtual ~nsDOMStringMap();
 
 protected:
   nsRefPtr<nsGenericHTMLElement> mElement;
   // Flag to guard against infinite recursion.
   bool mRemovingProp;
-  static bool DataPropToAttr(const nsAString& aProp, nsAString& aResult);
-  static bool AttrToDataProp(const nsAString& aAttr, nsAString& aResult);
+  static bool DataPropToAttr(const nsAString& aProp, nsAutoString& aResult);
+  static bool AttrToDataProp(const nsAString& aAttr, nsAutoString& aResult);
 };
 
 #endif
--- a/content/media/wmf/WMFReader.cpp
+++ b/content/media/wmf/WMFReader.cpp
@@ -46,18 +46,18 @@ WMFReader::WMFReader(AbstractMediaDecode
     mAudioBytesPerSample(0),
     mAudioRate(0),
     mVideoWidth(0),
     mVideoHeight(0),
     mVideoStride(0),
     mHasAudio(false),
     mHasVideo(false),
     mCanSeek(false),
-    mIsMP3Enabled(WMFDecoder::IsMP3Supported()),
-    mUseHwAccel(false)
+    mUseHwAccel(false),
+    mIsMP3Enabled(WMFDecoder::IsMP3Supported())
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
   MOZ_COUNT_CTOR(WMFReader);
 }
 
 WMFReader::~WMFReader()
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be on main thread.");
@@ -784,17 +784,16 @@ WMFReader::CreateD3DVideoFrame(IMFSample
   NS_ENSURE_TRUE(aSample, E_POINTER);
   NS_ENSURE_TRUE(aOutVideoData, E_POINTER);
   NS_ENSURE_TRUE(mDXVA2Manager, E_ABORT);
   NS_ENSURE_TRUE(mUseHwAccel, E_ABORT);
 
   *aOutVideoData = nullptr;
   HRESULT hr;
 
-  ImageFormat format = D3D9_RGB32_TEXTURE;
   nsRefPtr<Image> image;
   hr = mDXVA2Manager->CopyToImage(aSample,
                                   nsIntSize(mPictureRegion.width, mPictureRegion.height),
                                   mDecoder->GetImageContainer(),
                                   getter_AddRefs(image));
   NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
   NS_ENSURE_TRUE(image, E_FAIL);
 
--- a/content/media/wmf/WMFSourceReaderCallback.cpp
+++ b/content/media/wmf/WMFSourceReaderCallback.cpp
@@ -32,22 +32,22 @@ WMFSourceReaderCallback::QueryInterface(
   *aInterface = NULL;
   return E_NOINTERFACE;
 }
 
 NS_IMPL_THREADSAFE_ADDREF(WMFSourceReaderCallback)
 NS_IMPL_THREADSAFE_RELEASE(WMFSourceReaderCallback)
 
 WMFSourceReaderCallback::WMFSourceReaderCallback()
-  : mResultStatus(S_OK)
+  : mMonitor("WMFSourceReaderCallback")
+  , mResultStatus(S_OK)
   , mStreamFlags(0)
   , mTimestamp(0)
   , mSample(nullptr)
   , mReadFinished(false)
-  , mMonitor("WMFSourceReaderCallback")
 {
 #ifdef PR_LOGGING
   if (!gWMFSourceReaderCallbackLog) {
     gWMFSourceReaderCallbackLog = PR_NewLogModule("WMFSourceReaderCallback");
   }
 #endif
 }
 
--- a/content/media/wmf/WMFSourceReaderCallback.h
+++ b/content/media/wmf/WMFSourceReaderCallback.h
@@ -14,17 +14,17 @@
 namespace mozilla {
 
 // A listener which we pass into the IMFSourceReader upon creation which is
 // notified when an asynchronous call to IMFSourceReader::ReadSample()
 // completes. This allows us to abort ReadSample() operations when the
 // WMFByteStream's underlying MediaResource is closed. This ensures that
 // the decode threads don't get stuck in a synchronous ReadSample() call
 // when the MediaResource is unexpectedly shutdown.
-class WMFSourceReaderCallback : public IMFSourceReaderCallback
+class WMFSourceReaderCallback MOZ_FINAL : public IMFSourceReaderCallback
 {
 public:
   WMFSourceReaderCallback();
 
   // IUnknown Methods.
   STDMETHODIMP QueryInterface(REFIID aIId, LPVOID *aInterface);
   STDMETHODIMP_(ULONG) AddRef();
   STDMETHODIMP_(ULONG) Release();
--- a/content/xbl/src/nsXBLProtoImplField.cpp
+++ b/content/xbl/src/nsXBLProtoImplField.cpp
@@ -424,17 +424,17 @@ nsXBLProtoImplField::InstallField(nsIScr
 
   JS::Rooted<JS::Value> result(cx);
   JS::CompileOptions options(cx);
   options.setFileAndLine(uriSpec.get(), mLineNumber)
          .setVersion(JSVERSION_LATEST)
          .setUserBit(true); // Flag us as XBL
   rv = context->EvaluateString(nsDependentString(mFieldText,
                                                  mFieldTextLength),
-                               *wrappedNode, options,
+                               wrappedNode, options,
                                /* aCoerceToString = */ false,
                                result.address());
   if (NS_FAILED(rv)) {
     return rv;
   }
 
 
   // Now, enter the node's compartment, wrap the eval result, and define it on
--- a/dom/base/SiteSpecificUserAgent.js
+++ b/dom/base/SiteSpecificUserAgent.js
@@ -4,24 +4,23 @@
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/UserAgentOverrides.jsm");
 
-const DEFAULT_UA = Cc["@mozilla.org/network/protocol;1?name=http"]
-                     .getService(Ci.nsIHttpProtocolHandler)
-                     .userAgent;
+const HTTP_PROTO_HANDLER = Cc["@mozilla.org/network/protocol;1?name=http"]
+                             .getService(Ci.nsIHttpProtocolHandler);
 
 function SiteSpecificUserAgent() {}
 
 SiteSpecificUserAgent.prototype = {
   getUserAgentForURIAndWindow: function ssua_getUserAgentForURIAndWindow(aURI, aWindow) {
-    return UserAgentOverrides.getOverrideForURI(aURI) || DEFAULT_UA;
+    return UserAgentOverrides.getOverrideForURI(aURI) || HTTP_PROTO_HANDLER.userAgent;
   },
 
   classID: Components.ID("{506c680f-3d1c-4954-b351-2c80afbc37d3}"),
   QueryInterface: XPCOMUtils.generateQI([Ci.nsISiteSpecificUserAgent])
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([SiteSpecificUserAgent]);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -3639,33 +3639,33 @@ IDBConstantGetter(JSContext *cx, JSHandl
       if (consoleServ) {
         consoleServ->LogMessage(errorObject);
       }
     }
   }
 
   // Redefine property to remove getter
   NS_ConvertASCIItoUTF16 valStr(c.value);
-  jsval value;
-  if (!xpc::StringToJsval(cx, valStr, &value)) {
+  JS::Rooted<JS::Value> value(cx);
+  if (!xpc::StringToJsval(cx, valStr, value.address())) {
     return JS_FALSE;
   }
   if (!::JS_DefineProperty(cx, obj, c.name, value,
                            JS_PropertyStub, JS_StrictPropertyStub,
                            JSPROP_ENUMERATE)) {
     return JS_FALSE;
   }
 
   // Return value
   vp.set(value);
   return JS_TRUE;
 }
 
 static nsresult
-DefineIDBInterfaceConstants(JSContext *cx, JSObject *obj, const nsIID *aIID)
+DefineIDBInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj, const nsIID *aIID)
 {
   const char* interface;
   if (aIID->Equals(NS_GET_IID(nsIIDBCursor))) {
     interface = IDBConstant::IDBCursor;
   }
   else if (aIID->Equals(NS_GET_IID(nsIIDBRequest))) {
     interface = IDBConstant::IDBRequest;
   }
@@ -5540,20 +5540,21 @@ nsNodeSH::AddProperty(nsIXPConnectWrappe
                       JSObject *obj, jsid id, jsval *vp, bool *_retval)
 {
   nsNodeSH::PreserveWrapper(GetNative(wrapper, obj));
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNodeSH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                     JSObject *aObj, jsid id, uint32_t flags,
+                     JSObject *aObj, jsid aId, uint32_t flags,
                      JSObject **objp, bool *_retval)
 {
   JS::Rooted<JSObject*> obj(cx, aObj);
+  JS::Rooted<jsid> id(cx, aId);
   if (id == sOnload_id || id == sOnerror_id) {
     // Make sure that this node can't go away while waiting for a
     // network load that could fire an event handler.
     // XXXbz won't this fail if the listener is added using
     // addEventListener?  On the other hand, even if I comment this
     // code out I can't seem to reproduce the bug it was trying to
     // fix....
     nsNodeSH::PreserveWrapper(GetNative(wrapper, obj));
@@ -6765,20 +6766,21 @@ nsHTMLDocumentSH::NewResolve(nsIXPConnec
     }
   }
 
   return nsDocumentSH::NewResolve(wrapper, cx, obj, id, flags, objp, _retval);
 }
 
 NS_IMETHODIMP
 nsHTMLDocumentSH::GetProperty(nsIXPConnectWrappedNative *wrapper,
-                              JSContext *cx, JSObject *aObj, jsid id,
+                              JSContext *cx, JSObject *aObj, jsid aId,
                               jsval *vp, bool *_retval)
 {
   JS::Rooted<JSObject*> obj(cx, aObj);
+  JS::Rooted<jsid> id(cx, aId);
   nsCOMPtr<nsISupports> result;
 
   JSAutoRequest ar(cx);
 
   nsWrapperCache *cache;
   nsresult rv = ResolveImpl(cx, wrapper, id, getter_AddRefs(result), &cache);
   NS_ENSURE_SUCCESS(rv, rv);
 
@@ -7161,19 +7163,20 @@ nsCSSRuleListSH::GetItemAt(nsISupports *
 
 // One reason we need a newResolve hook is that in order for
 // enumeration of storage object keys to work the keys we're
 // enumerating need to exist on the storage object for the JS engine
 // to find them.
 
 NS_IMETHODIMP
 nsStorage2SH::NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                         JSObject *obj, jsid id, uint32_t flags,
+                         JSObject *obj, jsid aId, uint32_t flags,
                          JSObject **objp, bool *_retval)
 {
+  JS::Rooted<jsid> id(cx, aId);
   if (ObjectIsNativeWrapper(cx, obj)) {
     return NS_OK;
   }
 
   JS::Rooted<JSObject*> realObj(cx);
   wrapper->GetJSObject(realObj.address());
 
   JSAutoCompartment ac(cx, realObj);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -2024,28 +2024,28 @@ WindowStateHolder::~WindowStateHolder()
 NS_IMPL_ISUPPORTS1(WindowStateHolder, WindowStateHolder)
 
 nsresult
 nsGlobalWindow::CreateOuterObject(nsGlobalWindow* aNewInner)
 {
   AutoPushJSContext cx(mContext->GetNativeContext());
 
   JS::Rooted<JSObject*> global(cx, aNewInner->FastGetGlobalJSObject());
-  JSObject* outer = NewOuterWindowProxy(cx, global, IsChromeWindow());
+  JS::Rooted<JSObject*> outer(cx, NewOuterWindowProxy(cx, global, IsChromeWindow()));
   if (!outer) {
     return NS_ERROR_FAILURE;
   }
 
   js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this)));
 
   return SetOuterObject(cx, outer);
 }
 
 nsresult
-nsGlobalWindow::SetOuterObject(JSContext* aCx, JSObject* aOuterObject)
+nsGlobalWindow::SetOuterObject(JSContext* aCx, JS::Handle<JSObject*> aOuterObject)
 {
   // Force our context's global object to be the outer.
   // NB: JS_SetGlobalObject sets aCx->compartment.
   JS_SetGlobalObject(aCx, aOuterObject);
 
   // Set up the prototype for the outer object.
   JSObject* inner = JS_GetParent(aOuterObject);
   JS::Rooted<JSObject*> proto(aCx);
@@ -2388,17 +2388,18 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       mJSObject = outerObject;
       SetWrapper(mJSObject);
 
       {
         JSAutoCompartment ac(cx, mJSObject);
 
         JS_SetParent(cx, mJSObject, newInnerWindow->mJSObject);
 
-        rv = SetOuterObject(cx, mJSObject);
+        JS::Rooted<JSObject*> obj(cx, mJSObject);
+        rv = SetOuterObject(cx, obj);
         NS_ENSURE_SUCCESS(rv, rv);
 
         NS_ASSERTION(!JS_IsExceptionPending(cx),
                      "We might overwrite a pending exception!");
         XPCWrappedNativeScope* scope = xpc::GetObjectScope(mJSObject);
         if (scope->mWaiverWrapperMap) {
           scope->mWaiverWrapperMap->Reparent(cx, newInnerWindow->mJSObject);
         }
@@ -10228,17 +10229,18 @@ nsGlobalWindow::RunTimeoutHandler(nsTime
     const char* filename = nullptr;
     uint32_t lineNo = 0;
     handler->GetLocation(&filename, &lineNo);
 
     AutoPushJSContext cx(aScx->GetNativeContext());
     JS::CompileOptions options(cx);
     options.setFileAndLine(filename, lineNo)
            .setVersion(JSVERSION_DEFAULT);
-    aScx->EvaluateString(nsDependentString(script), *FastGetGlobalJSObject(),
+    JS::Rooted<JSObject*> global(cx, FastGetGlobalJSObject());
+    aScx->EvaluateString(nsDependentString(script), global,
                          options, /*aCoerceToString = */ false, nullptr);
   } else {
     // Hold strong ref to ourselves while we call the callback.
     nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
     ErrorResult ignored;
     callback->Call(me, handler->GetArgs(), ignored);
   }
 
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -1005,17 +1005,17 @@ protected:
 
   virtual void UpdateParentTarget();
 
   bool GetIsTabModalPromptAllowed();
 
   inline int32_t DOMMinTimeoutValue() const;
 
   nsresult CreateOuterObject(nsGlobalWindow* aNewInner);
-  nsresult SetOuterObject(JSContext* aCx, JSObject* aOuterObject);
+  nsresult SetOuterObject(JSContext* aCx, JS::Handle<JSObject*> aOuterObject);
   nsresult CloneStorageEvent(const nsAString& aType,
                              nsCOMPtr<nsIDOMStorageEvent>& aEvent);
 
   // Implements Get{Real,Scriptable}Top.
   nsresult GetTopImpl(nsIDOMWindow **aWindow, bool aScriptable);
 
   // Outer windows only.
   nsDOMWindowList* GetWindowList();
--- a/dom/base/nsIScriptContext.h
+++ b/dom/base/nsIScriptContext.h
@@ -56,17 +56,17 @@ public:
    *                 internally, though 'originPrincipals' may be passed.
    * @param aCoerceToString if the return value is not JSVAL_VOID, convert it
    *                        to a string before returning.
    * @param aRetValue the result of executing the script.  Pass null if you
    *                  don't care about the result.  Note that asking for a
    *                  result will deoptimize your script somewhat in many cases.
    */
   virtual nsresult EvaluateString(const nsAString& aScript,
-                                  JSObject& aScopeObject,
+                                  JS::Handle<JSObject*> aScopeObject,
                                   JS::CompileOptions& aOptions,
                                   bool aCoerceToString,
                                   JS::Value* aRetValue) = 0;
 
   /**
    * Compile a script.
    *
    * @param aText a PRUnichar buffer containing script source
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -1249,17 +1249,17 @@ nsJSContext::GetCCRefcnt()
     refcnt++;
   }
 
   return refcnt;
 }
 
 nsresult
 nsJSContext::EvaluateString(const nsAString& aScript,
-                            JSObject& aScopeObject,
+                            JS::Handle<JSObject*> aScopeObject,
                             JS::CompileOptions& aOptions,
                             bool aCoerceToString,
                             JS::Value* aRetValue)
 {
   PROFILER_LABEL("JS", "EvaluateString");
   MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN);
   MOZ_ASSERT_IF(aCoerceToString, aRetValue);
   NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
@@ -1273,38 +1273,38 @@ nsJSContext::EvaluateString(const nsAStr
 
   if (!mScriptsEnabled) {
     return NS_OK;
   }
 
   nsCxPusher pusher;
   pusher.Push(mContext);
 
-  xpc_UnmarkGrayObject(&aScopeObject);
+  xpc_UnmarkGrayObject(aScopeObject);
   nsAutoMicroTask mt;
 
-  JSPrincipals* p = JS_GetCompartmentPrincipals(js::GetObjectCompartment(&aScopeObject));
+  JSPrincipals* p = JS_GetCompartmentPrincipals(js::GetObjectCompartment(aScopeObject));
   aOptions.setPrincipals(p);
 
   bool ok = false;
   nsresult rv = sSecurityManager->CanExecuteScripts(mContext, nsJSPrincipals::get(p), &ok);
   NS_ENSURE_SUCCESS(rv, rv);
   NS_ENSURE_TRUE(ok, NS_OK);
 
   nsJSContext::TerminationFuncHolder holder(this);
 
   // Scope the JSAutoCompartment so that it gets destroyed before we pop the
   // cx and potentially call JS_RestoreFrameChain.
   XPCAutoRequest ar(mContext);
   {
-    JSAutoCompartment ac(mContext, &aScopeObject);
+    JSAutoCompartment ac(mContext, aScopeObject);
 
     ++mExecuteDepth;
 
-    JS::RootedObject rootedScope(mContext, &aScopeObject);
+    JS::RootedObject rootedScope(mContext, aScopeObject);
     ok = JS::Evaluate(mContext, rootedScope, aOptions,
                       PromiseFlatString(aScript).get(),
                       aScript.Length(), aRetValue);
     if (ok && aCoerceToString && !aRetValue->isUndefined()) {
       JSString* str = JS_ValueToString(mContext, *aRetValue);
       ok = !!str;
       *aRetValue = ok ? JS::StringValue(str) : JS::UndefinedValue();
     }
@@ -1385,18 +1385,19 @@ nsJSContext::CompileScript(const PRUnich
     return NS_ERROR_OUT_OF_MEMORY;
   }
   aScriptObject.set(script);
   return NS_OK;
 }
 
 nsresult
 nsJSContext::ExecuteScript(JSScript* aScriptObject,
-                           JSObject* aScopeObject)
+                           JSObject* aScopeObject_)
 {
+  JS::Rooted<JSObject*> aScopeObject(mContext, aScopeObject_);
   NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
 
   if (!mScriptsEnabled) {
     return NS_OK;
   }
 
   nsAutoMicroTask mt;
 
--- a/dom/base/nsJSEnvironment.h
+++ b/dom/base/nsJSEnvironment.h
@@ -40,17 +40,17 @@ public:
               nsIScriptGlobalObject* aGlobalObject);
   virtual ~nsJSContext();
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSContext,
                                                          nsIScriptContext)
 
   virtual nsresult EvaluateString(const nsAString& aScript,
-                                  JSObject& aScopeObject,
+                                  JS::Handle<JSObject*> aScopeObject,
                                   JS::CompileOptions &aOptions,
                                   bool aCoerceToString,
                                   JS::Value* aRetValue);
 
   virtual nsresult CompileScript(const PRUnichar* aText,
                                  int32_t aTextLength,
                                  nsIPrincipal *principal,
                                  const char *aURL,
--- a/dom/base/nsPerformance.cpp
+++ b/dom/base/nsPerformance.cpp
@@ -34,87 +34,108 @@ nsPerformanceTiming::nsPerformanceTiming
 
 nsPerformanceTiming::~nsPerformanceTiming()
 {
 }
 
 DOMTimeMilliSec
 nsPerformanceTiming::DomainLookupStart() const
 {
+  if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+    return 0;
+  }
   if (!mChannel) {
     return FetchStart();
   }
   mozilla::TimeStamp stamp;
   mChannel->GetDomainLookupStart(&stamp);
   return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
 }
 
 DOMTimeMilliSec
 nsPerformanceTiming::DomainLookupEnd() const
 {
+  if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+    return 0;
+  }
   if (!mChannel) {
     return FetchStart();
   }
   mozilla::TimeStamp stamp;
   mChannel->GetDomainLookupEnd(&stamp);
   return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
 }
 
 DOMTimeMilliSec
 nsPerformanceTiming::ConnectStart() const
 {
+  if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+    return 0;
+  }
   if (!mChannel) {
     return FetchStart();
   }
   mozilla::TimeStamp stamp;
   mChannel->GetConnectStart(&stamp);
   return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
 }
 
 DOMTimeMilliSec
 nsPerformanceTiming::ConnectEnd() const
 {
+  if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+    return 0;
+  }
   if (!mChannel) {
     return FetchStart();
   }
   mozilla::TimeStamp stamp;
   mChannel->GetConnectEnd(&stamp);
   return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
 }
 
 DOMTimeMilliSec
 nsPerformanceTiming::RequestStart() const
 {
+  if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+    return 0;
+  }
   if (!mChannel) {
     return FetchStart();
   }
   mozilla::TimeStamp stamp;
   mChannel->GetRequestStart(&stamp);
   return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
 }
 
 DOMTimeMilliSec
 nsPerformanceTiming::ResponseStart() const
 {
+  if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+    return 0;
+  }
   if (!mChannel) {
     return FetchStart();
   }
   mozilla::TimeStamp stamp;
   mChannel->GetResponseStart(&stamp);
   mozilla::TimeStamp cacheStamp;
   mChannel->GetCacheReadStart(&cacheStamp);
   if (stamp.IsNull() || (!cacheStamp.IsNull() && cacheStamp < stamp)) {
     stamp = cacheStamp;
   }
   return GetDOMTiming()->TimeStampToDOMOrFetchStart(stamp);
 }
 
 DOMTimeMilliSec
 nsPerformanceTiming::ResponseEnd() const
 {
+  if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+    return 0;
+  }
   if (!mChannel) {
     return FetchStart();
   }
   mozilla::TimeStamp stamp;
   mChannel->GetResponseEnd(&stamp);
   mozilla::TimeStamp cacheStamp;
   mChannel->GetCacheReadEnd(&cacheStamp);
   if (stamp.IsNull() || (!cacheStamp.IsNull() && cacheStamp < stamp)) {
--- a/dom/base/nsPerformance.h
+++ b/dom/base/nsPerformance.h
@@ -6,16 +6,17 @@
 #define nsPerformance_h___
 
 #include "nscore.h"
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "mozilla/Attributes.h"
 #include "nsWrapperCache.h"
 #include "nsDOMNavigationTiming.h"
+#include "nsContentUtils.h"
 
 class nsIURI;
 class nsITimedChannel;
 class nsIDOMWindow;
 class nsPerformance;
 class JSObject;
 struct JSContext;
 
@@ -35,59 +36,98 @@ public:
     return mPerformance;
   }
 
   virtual JSObject* WrapObject(JSContext *cx,
                                JS::Handle<JSObject*> scope) MOZ_OVERRIDE;
 
   // PerformanceNavigation WebIDL methods
   DOMTimeMilliSec NavigationStart() const {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetNavigationStart();
   }
   DOMTimeMilliSec UnloadEventStart() {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetUnloadEventStart();
   }
   DOMTimeMilliSec UnloadEventEnd() {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetUnloadEventEnd();
   }
   DOMTimeMilliSec RedirectStart() {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetRedirectStart();
   }
   DOMTimeMilliSec RedirectEnd() {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetRedirectEnd();
   }
   DOMTimeMilliSec FetchStart() const {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetFetchStart();
   }
   DOMTimeMilliSec DomainLookupStart() const;
   DOMTimeMilliSec DomainLookupEnd() const;
   DOMTimeMilliSec ConnectStart() const;
   DOMTimeMilliSec ConnectEnd() const;
   DOMTimeMilliSec RequestStart() const;
   DOMTimeMilliSec ResponseStart() const;
   DOMTimeMilliSec ResponseEnd() const;
   DOMTimeMilliSec DomLoading() const {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetDomLoading();
   }
   DOMTimeMilliSec DomInteractive() const {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetDomInteractive();
   }
   DOMTimeMilliSec DomContentLoadedEventStart() const {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetDomContentLoadedEventStart();
   }
   DOMTimeMilliSec DomContentLoadedEventEnd() const {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetDomContentLoadedEventEnd();
   }
   DOMTimeMilliSec DomComplete() const {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetDomComplete();
   }
   DOMTimeMilliSec LoadEventStart() const {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetLoadEventStart();
   }
   DOMTimeMilliSec LoadEventEnd() const {
+    if (!nsContentUtils::IsPerformanceTimingEnabled()) {
+      return 0;
+    }
     return GetDOMTiming()->GetLoadEventEnd();
   }
 
 private:
   ~nsPerformanceTiming();
   nsRefPtr<nsPerformance> mPerformance;
   nsCOMPtr<nsITimedChannel> mChannel;
 };
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -7858,16 +7858,21 @@ class CGBindingRoot(CGThing):
     """
     Root codegen class for binding generation. Instantiate the class, and call
     declare or define to generate header or cpp code (respectively).
     """
     def __init__(self, config, prefix, webIDLFile):
         descriptors = config.getDescriptors(webIDLFile=webIDLFile,
                                             hasInterfaceOrInterfacePrototypeObject=True,
                                             skipGen=False)
+        def descriptorRequiresPreferences(desc):
+            iface = desc.interface
+            return any(m.getExtendedAttribute("Pref") for m in iface.members + [iface]);
+        requiresPreferences = any(descriptorRequiresPreferences(d) for d in descriptors)
+        hasOwnedDescriptors = any(d.nativeOwnership == 'owned' for d in descriptors)
         hasWorkerStuff = len(config.getDescriptors(webIDLFile=webIDLFile,
                                                    workers=True)) != 0
         mainDictionaries = config.getDictionaries(webIDLFile=webIDLFile,
                                                   workers=False)
         workerDictionaries = config.getDictionaries(webIDLFile=webIDLFile,
                                                     workers=True)
         mainCallbacks = config.getCallbacks(webIDLFile=webIDLFile,
                                             workers=False)
@@ -7957,30 +7962,30 @@ class CGBindingRoot(CGThing):
                          mainDictionaries + workerDictionaries,
                          mainCallbacks + workerCallbacks,
                          callbackDescriptors,
                          ['mozilla/dom/BindingDeclarations.h',
                           'mozilla/ErrorResult.h',
                           'mozilla/dom/DOMJSClass.h',
                           'mozilla/dom/DOMJSProxyHandler.h'],
                          ['mozilla/dom/BindingUtils.h',
-                          'mozilla/dom/NonRefcountedDOMObject.h',
                           'mozilla/dom/Nullable.h',
                           'PrimitiveConversions.h',
                           'XPCQuickStubs.h',
                           'XPCWrapper.h',
                           'nsDOMQS.h',
                           'AccessCheck.h',
                           'nsContentUtils.h',
-                          'mozilla/Preferences.h',
                           # Have to include nsDOMQS.h to get fast arg unwrapping
                           # for old-binding things with castability.
                           'nsDOMQS.h'
                           ] + (['WorkerPrivate.h',
-                                'nsThreadUtils.h'] if hasWorkerStuff else []),
+                                'nsThreadUtils.h'] if hasWorkerStuff else [])
+                            + (['mozilla/Preferences.h'] if requiresPreferences else [])
+                            + (['mozilla/dom/NonRefcountedDOMObject.h'] if hasOwnedDescriptors else []),
                          curr,
                          config,
                          jsImplemented)
 
         # Add include guards.
         curr = CGIncludeGuard(prefix, curr)
 
         # Add the auto-generated comment.
--- a/dom/camera/CameraControlImpl.cpp
+++ b/dom/camera/CameraControlImpl.cpp
@@ -149,17 +149,17 @@ CameraControlImpl::Get(JSContext* aCx, u
 
   uint32_t length = regionArray.Length();
   DOM_CAMERA_LOGI("%s:%d : got %d regions\n", __func__, __LINE__, length);
 
   for (uint32_t i = 0; i < length; ++i) {
     CameraRegion* r = &regionArray[i];
     JS::Rooted<JS::Value> v(aCx);
 
-    JSObject* o = JS_NewObject(aCx, nullptr, nullptr, nullptr);
+    JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, nullptr, nullptr));
     if (!o) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     DOM_CAMERA_LOGI("top=%d\n", r->top);
     v = INT_TO_JSVAL(r->top);
     if (!JS_SetProperty(aCx, o, "top", v.address())) {
       return NS_ERROR_FAILURE;
--- a/dom/camera/DOMCameraCapabilities.cpp
+++ b/dom/camera/DOMCameraCapabilities.cpp
@@ -22,17 +22,18 @@ NS_INTERFACE_MAP_BEGIN(DOMCameraCapabili
   NS_INTERFACE_MAP_ENTRY(nsICameraCapabilities)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(CameraCapabilities)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(DOMCameraCapabilities)
 NS_IMPL_RELEASE(DOMCameraCapabilities)
 
 static nsresult
-ParseZoomRatioItemAndAdd(JSContext* aCx, JSObject* aArray, uint32_t aIndex, const char* aStart, char** aEnd)
+ParseZoomRatioItemAndAdd(JSContext* aCx, JS::Handle<JSObject*> aArray,
+                         uint32_t aIndex, const char* aStart, char** aEnd)
 {
   if (!*aEnd) {
     // make 'aEnd' follow the same semantics as strchr().
     aEnd = nullptr;
   }
 
   /**
    * The by-100 divisor is Gonk-specific.  For now, assume other platforms
@@ -48,17 +49,18 @@ ParseZoomRatioItemAndAdd(JSContext* aCx,
   if (!JS_SetElement(aCx, aArray, aIndex, &v)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 static nsresult
-ParseStringItemAndAdd(JSContext* aCx, JSObject* aArray, uint32_t aIndex, const char* aStart, char** aEnd)
+ParseStringItemAndAdd(JSContext* aCx, JS::Handle<JSObject*> aArray,
+                      uint32_t aIndex, const char* aStart, char** aEnd)
 {
   JSString* s;
 
   if (*aEnd) {
     s = JS_NewStringCopyN(aCx, aStart, *aEnd - aStart);
   } else {
     s = JS_NewStringCopyZ(aCx, aStart);
   }
@@ -70,62 +72,66 @@ ParseStringItemAndAdd(JSContext* aCx, JS
   if (!JS_SetElement(aCx, aArray, aIndex, &v)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 static nsresult
-ParseDimensionItemAndAdd(JSContext* aCx, JSObject* aArray, uint32_t aIndex, const char* aStart, char** aEnd)
+ParseDimensionItemAndAdd(JSContext* aCx, JS::Handle<JSObject*> aArray,
+                         uint32_t aIndex, const char* aStart, char** aEnd)
 {
   char* x;
 
   if (!*aEnd) {
     // make 'aEnd' follow the same semantics as strchr().
     aEnd = nullptr;
   }
 
-  JS::Value w = INT_TO_JSVAL(strtol(aStart, &x, 10));
-  JS::Value h = INT_TO_JSVAL(strtol(x + 1, aEnd, 10));
+  JS::Rooted<JS::Value> w(aCx, INT_TO_JSVAL(strtol(aStart, &x, 10)));
+  JS::Rooted<JS::Value> h(aCx, INT_TO_JSVAL(strtol(x + 1, aEnd, 10)));
 
-  JSObject* o = JS_NewObject(aCx, nullptr, nullptr, nullptr);
+  JS::Rooted<JSObject*> o(aCx, JS_NewObject(aCx, nullptr, nullptr, nullptr));
   if (!o) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
-  if (!JS_SetProperty(aCx, o, "width", &w)) {
+  if (!JS_SetProperty(aCx, o, "width", w.address())) {
     return NS_ERROR_FAILURE;
   }
-  if (!JS_SetProperty(aCx, o, "height", &h)) {
+  if (!JS_SetProperty(aCx, o, "height", h.address())) {
     return NS_ERROR_FAILURE;
   }
 
   JS::Value v = OBJECT_TO_JSVAL(o);
   if (!JS_SetElement(aCx, aArray, aIndex, &v)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 nsresult
-DOMCameraCapabilities::ParameterListToNewArray(JSContext* aCx, JSObject** aArray, uint32_t aKey, ParseItemAndAddFunc aParseItemAndAdd)
+DOMCameraCapabilities::ParameterListToNewArray(JSContext* aCx,
+                                               JS::MutableHandle<JSObject*> aArray,
+                                               uint32_t aKey,
+                                               ParseItemAndAddFunc aParseItemAndAdd)
 {
   NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
   const char* value = mCamera->GetParameterConstChar(aKey);
   if (!value) {
     // in case we get nonsense data back
-    *aArray = nullptr;
+    aArray.set(nullptr);
     return NS_OK;
   }
 
-  *aArray = JS_NewArrayObject(aCx, 0, nullptr);
-  if (!*aArray) {
+  aArray.set(JS_NewArrayObject(aCx, 0, nullptr));
+  if (!aArray) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   const char* p = value;
   uint32_t index = 0;
   nsresult rv;
   char* q;
 
@@ -136,45 +142,45 @@ DOMCameraCapabilities::ParameterListToNe
      * where the C version is declared to return const like the C++ version.
      *
      * Unfortunately, for both cases, strtod() and strtol() take a 'char**' as
      * the end-of-conversion pointer, so we need to cast away strchr()'s
      * const-ness here to make the MSVC build everything happy.
      */
     q = const_cast<char*>(strchr(p, ','));
     if (q != p) { // skip consecutive delimiters, just in case
-      rv = aParseItemAndAdd(aCx, *aArray, index, p, &q);
+      rv = aParseItemAndAdd(aCx, aArray, index, p, &q);
       NS_ENSURE_SUCCESS(rv, rv);
       ++index;
     }
     p = q;
     if (p) {
       ++p;
     }
   }
 
-  return JS_FreezeObject(aCx, *aArray) ? NS_OK : NS_ERROR_FAILURE;
+  return JS_FreezeObject(aCx, aArray) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 DOMCameraCapabilities::StringListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey)
 {
-  JSObject* array;
+  JS::Rooted<JSObject*> array(aCx);
 
   nsresult rv = ParameterListToNewArray(aCx, &array, aKey, ParseStringItemAndAdd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aArray = OBJECT_TO_JSVAL(array);
   return NS_OK;
 }
 
 nsresult
 DOMCameraCapabilities::DimensionListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey)
 {
-  JSObject* array;
+  JS::Rooted<JSObject*> array(aCx);
   nsresult rv;
 
   rv = ParameterListToNewArray(aCx, &array, aKey, ParseDimensionItemAndAdd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aArray = OBJECT_TO_JSVAL(array);
   return NS_OK;
 }
@@ -328,17 +334,17 @@ DOMCameraCapabilities::GetZoomRatios(JSC
 
   const char* value = mCamera->GetParameterConstChar(CAMERA_PARAM_SUPPORTED_ZOOM);
   if (!value || strcmp(value, "true") != 0) {
     // if zoom is not supported, return a null object
     *aZoomRatios = JSVAL_NULL;
     return NS_OK;
   }
 
-  JSObject* array;
+  JS::Rooted<JSObject*> array(cx);
 
   nsresult rv = ParameterListToNewArray(cx, &array, CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, ParseZoomRatioItemAndAdd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aZoomRatios = OBJECT_TO_JSVAL(array);
   return NS_OK;
 }
 
@@ -352,34 +358,34 @@ DOMCameraCapabilities::GetVideoSizes(JSC
   nsresult rv = mCamera->GetVideoSizes(sizes);
   NS_ENSURE_SUCCESS(rv, rv);
   if (sizes.Length() == 0) {
     // video recording not supported, return a null object
     *aVideoSizes = JSVAL_NULL;
     return NS_OK;
   }
 
-  JSObject* array = JS_NewArrayObject(cx, 0, nullptr);
+  JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, 0, nullptr));
   if (!array) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   for (uint32_t i = 0; i < sizes.Length(); ++i) {
-    JSObject* o = JS_NewObject(cx, nullptr, nullptr, nullptr);
-    JS::Value v = INT_TO_JSVAL(sizes[i].width);
-    if (!JS_SetProperty(cx, o, "width", &v)) {
+    JS::Rooted<JSObject*> o(cx, JS_NewObject(cx, nullptr, nullptr, nullptr));
+    JS::Rooted<JS::Value> v(cx, INT_TO_JSVAL(sizes[i].width));
+    if (!JS_SetProperty(cx, o, "width", v.address())) {
       return NS_ERROR_FAILURE;
     }
     v = INT_TO_JSVAL(sizes[i].height);
-    if (!JS_SetProperty(cx, o, "height", &v)) {
+    if (!JS_SetProperty(cx, o, "height", v.address())) {
       return NS_ERROR_FAILURE;
     }
 
     v = OBJECT_TO_JSVAL(o);
-    if (!JS_SetElement(cx, array, i, &v)) {
+    if (!JS_SetElement(cx, array, i, v.address())) {
       return NS_ERROR_FAILURE;
     }
   }
 
   *aVideoSizes = OBJECT_TO_JSVAL(array);
   return NS_OK;
 }
 
@@ -390,15 +396,15 @@ DOMCameraCapabilities::GetRecorderProfil
   NS_ENSURE_TRUE(mCamera, NS_ERROR_NOT_AVAILABLE);
 
   nsRefPtr<RecorderProfileManager> profileMgr = mCamera->GetRecorderProfileManager();
   if (!profileMgr) {
     *aRecorderProfiles = JSVAL_NULL;
     return NS_OK;
   }
 
-  JSObject* o = nullptr;  // keeps the compiler from emitting a warning
-  nsresult rv = profileMgr->GetJsObject(cx, &o);
+  JS::Rooted<JSObject*> o(cx);
+  nsresult rv = profileMgr->GetJsObject(cx, o.address());
   NS_ENSURE_SUCCESS(rv, rv);
 
   *aRecorderProfiles = OBJECT_TO_JSVAL(o);
   return NS_OK;
 }
--- a/dom/camera/DOMCameraCapabilities.h
+++ b/dom/camera/DOMCameraCapabilities.h
@@ -6,33 +6,34 @@
 #define DOM_CAMERA_DOMCAMERACAPABILITIES_H
 
 #include "ICameraControl.h"
 #include "nsAutoPtr.h"
 #include "CameraCommon.h"
 
 namespace mozilla {
 
-typedef nsresult (*ParseItemAndAddFunc)(JSContext* aCx, JSObject* aArray, uint32_t aIndex, const char* aStart, char** aEnd);
+typedef nsresult (*ParseItemAndAddFunc)(JSContext* aCx, JS::Handle<JSObject*> aArray,
+                                        uint32_t aIndex, const char* aStart, char** aEnd);
 
 class DOMCameraCapabilities MOZ_FINAL : public nsICameraCapabilities
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSICAMERACAPABILITIES
 
   DOMCameraCapabilities(ICameraControl* aCamera)
     : mCamera(aCamera)
   {
     DOM_CAMERA_LOGT("%s:%d : this=%p\n", __func__, __LINE__, this);
   }
 
   nsresult ParameterListToNewArray(
     JSContext* cx,
-    JSObject** aArray,
+    JS::MutableHandle<JSObject*> aArray,
     uint32_t aKey,
     ParseItemAndAddFunc aParseItemAndAdd
   );
   nsresult StringListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey);
   nsresult DimensionListToNewObject(JSContext* aCx, JS::Value* aArray, uint32_t aKey);
 
 private:
   DOMCameraCapabilities(const DOMCameraCapabilities&) MOZ_DELETE;
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -2490,17 +2490,17 @@ nsDOMDeviceStorage::EnumerateEditable(co
 static PRTime
 ExtractDateFromOptions(JSContext* aCx, const JS::Value& aOptions)
 {
   PRTime result = 0;
   mozilla::idl::DeviceStorageEnumerationParameters params;
   if (!JSVAL_IS_VOID(aOptions) && !aOptions.isNull()) {
     nsresult rv = params.Init(aCx, &aOptions);
     if (NS_SUCCEEDED(rv) && !JSVAL_IS_VOID(params.since) && !params.since.isNull() && params.since.isObject()) {
-      JSObject* obj = JSVAL_TO_OBJECT(params.since);
+      JS::Rooted<JSObject*> obj(aCx, JSVAL_TO_OBJECT(params.since));
       if (JS_ObjectIsDate(aCx, obj) && js_DateIsValid(obj)) {
         result = js_DateGetMsecSinceEpoch(obj);
       }
     }
   }
   return result;
 }
 
--- a/dom/file/ArchiveRequest.cpp
+++ b/dom/file/ArchiveRequest.cpp
@@ -125,42 +125,42 @@ nsresult
 ArchiveRequest::ReaderReady(nsTArray<nsCOMPtr<nsIDOMFile> >& aFileList,
                             nsresult aStatus)
 {
   if (NS_FAILED(aStatus)) {
     FireError(aStatus);
     return NS_OK;
   }
 
-  JS::Value result;
   nsresult rv;
 
   nsIScriptContext* sc = GetContextForEventHandlers(&rv);
   NS_ENSURE_STATE(sc);
 
   AutoPushJSContext cx(sc->GetNativeContext());
   NS_ASSERTION(cx, "Failed to get a context!");
 
   JS::Rooted<JSObject*> global(cx, sc->GetNativeGlobal());
   NS_ASSERTION(global, "Failed to get global object!");
 
   JSAutoRequest ar(cx);
   JSAutoCompartment ac(cx, global);
 
+  JS::Rooted<JS::Value> result(cx);
   switch (mOperation) {
     case GetFilenames:
-      rv = GetFilenamesResult(cx, &result, aFileList);
+      rv = GetFilenamesResult(cx, result.address(), aFileList);
       break;
 
     case GetFile:
-      rv = GetFileResult(cx, &result, aFileList);
+      rv = GetFileResult(cx, result.address(), aFileList);
       break;
 
       case GetFiles:
-        rv = GetFilesResult(cx, &result, aFileList);
+        rv = GetFilesResult(cx, result.address(), aFileList);
         break;
   }
 
   if (NS_FAILED(rv)) {
     NS_WARNING("Get*Result failed!");
   }
 
   if (NS_SUCCEEDED(rv)) {
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -152,17 +152,17 @@ IDBFactory::Create(nsPIDOMWindow* aWindo
 
   factory.forget(aFactory);
   return NS_OK;
 }
 
 // static
 nsresult
 IDBFactory::Create(JSContext* aCx,
-                   JSObject* aOwningObject,
+                   JS::Handle<JSObject*> aOwningObject,
                    ContentParent* aContentParent,
                    IDBFactory** aFactory)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aCx, "Null context!");
   NS_ASSERTION(aOwningObject, "Null object!");
   NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject,
                "Not a global object!");
@@ -529,17 +529,17 @@ IDBFactory::OpenInternal(const nsAString
                          bool aDeleting,
                          JSContext* aCallingCx,
                          IDBOpenDBRequest** _retval)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!");
 
   nsCOMPtr<nsPIDOMWindow> window;
-  JSObject* scriptOwner = nullptr;
+  JS::Rooted<JSObject*> scriptOwner(aCallingCx);
   StoragePrivilege privilege;
 
   if (mWindow) {
     window = mWindow;
     scriptOwner =
       static_cast<nsGlobalWindow*>(window.get())->FastGetGlobalJSObject();
     privilege = Content;
   }
@@ -617,18 +617,18 @@ IDBFactory::OpenInternal(const nsAString
 
 JSObject*
 IDBFactory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
   return IDBFactoryBinding::Wrap(aCx, aScope, this);
 }
 
 int16_t
-IDBFactory::Cmp(JSContext* aCx, JS::Value aFirst, JS::Value aSecond,
-                ErrorResult& aRv)
+IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
+                JS::Handle<JS::Value> aSecond, ErrorResult& aRv)
 {
   Key first, second;
   nsresult rv = first.SetFromJSVal(aCx, aFirst);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return 0;
   }
 
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -59,17 +59,17 @@ public:
                          IDBFactory** aFactory)
   {
     return Create(aWindow, EmptyCString(), aContentParent, aFactory);
   }
 
   // Called when using IndexedDB from a JS component or a JSM in the current
   // process.
   static nsresult Create(JSContext* aCx,
-                         JSObject* aOwningObject,
+                         JS::Handle<JSObject*> aOwningObject,
                          ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
   // Called when using IndexedDB from a JS component or a JSM in a different
   // process.
   static nsresult Create(ContentParent* aContentParent,
                          IDBFactory** aFactory);
 
@@ -153,17 +153,18 @@ public:
   already_AddRefed<nsIIDBOpenDBRequest>
   DeleteDatabase(JSContext* aCx, const NonNull<nsAString>& aName,
                  ErrorResult& aRv)
   {
     return Open(aCx, nullptr, aName, Optional<uint64_t>(), true, aRv);
   }
 
   int16_t
-  Cmp(JSContext* aCx, JS::Value aFirst, JS::Value aSecond, ErrorResult& aRv);
+  Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
+      JS::Handle<JS::Value> aSecond, ErrorResult& aRv);
 
   already_AddRefed<nsIIDBOpenDBRequest>
   OpenForPrincipal(JSContext* aCx, nsIPrincipal* aPrincipal,
                    const NonNull<nsAString>& aName,
                    const Optional<uint64_t>& aVersion, ErrorResult& aRv);
 
   already_AddRefed<nsIIDBOpenDBRequest>
   DeleteForPrincipal(JSContext* aCx, nsIPrincipal* aPrincipal,
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -579,33 +579,32 @@ already_AddRefed<IDBRequest>
 GenerateRequest(IDBObjectStore* aObjectStore, JSContext* aCx)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   IDBDatabase* database = aObjectStore->Transaction()->Database();
   return IDBRequest::Create(aObjectStore, database,
                             aObjectStore->Transaction(), aCx);
 }
 
-struct GetAddInfoClosure
+struct MOZ_STACK_CLASS GetAddInfoClosure
 {
   IDBObjectStore* mThis;
   StructuredCloneWriteInfo& mCloneWriteInfo;
-  jsval mValue;
+  JS::Handle<JS::Value> mValue;
 };
 
 nsresult
 GetAddInfoCallback(JSContext* aCx, void* aClosure)
 {
   GetAddInfoClosure* data = static_cast<GetAddInfoClosure*>(aClosure);
 
   data->mCloneWriteInfo.mOffsetToKeyProp = 0;
   data->mCloneWriteInfo.mTransaction = data->mThis->Transaction();
 
-  if (!IDBObjectStore::SerializeValue(aCx, data->mCloneWriteInfo,
-                                      data->mValue)) {
+  if (!IDBObjectStore::SerializeValue(aCx, data->mCloneWriteInfo, data->mValue)) {
     return NS_ERROR_DOM_DATA_CLONE_ERR;
   }
 
   return NS_OK;
 }
 
 inline
 BlobChild*
@@ -1236,17 +1235,17 @@ IDBObjectStore::DeserializeValue(JSConte
 
   return buffer.read(aCx, aValue, &callbacks, &aCloneReadInfo);
 }
 
 // static
 bool
 IDBObjectStore::SerializeValue(JSContext* aCx,
                                StructuredCloneWriteInfo& aCloneWriteInfo,
-                               jsval aValue)
+                               JS::Handle<JS::Value> aValue)
 {
   NS_ASSERTION(NS_IsMainThread(),
                "Should only be serializing on the main thread!");
   NS_ASSERTION(aCx, "A JSContext is required!");
 
   JSAutoRequest ar(aCx);
 
   JSStructuredCloneCallbacks callbacks = {
@@ -1734,18 +1733,18 @@ IDBObjectStore::~IDBObjectStore()
   if (mRooted) {
     mCachedKeyPath = JSVAL_VOID;
     NS_DROP_JS_OBJECTS(this, IDBObjectStore);
   }
 }
 
 nsresult
 IDBObjectStore::GetAddInfo(JSContext* aCx,
-                           jsval aValue,
-                           jsval aKeyVal,
+                           JS::Handle<JS::Value> aValue,
+                           JS::Handle<JS::Value> aKeyVal,
                            StructuredCloneWriteInfo& aCloneWriteInfo,
                            Key& aKey,
                            nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
 {
   nsresult rv;
 
   // Return DATA_ERR if a key was passed in and this objectStore uses inline
   // keys.
@@ -1822,17 +1821,18 @@ IDBObjectStore::AddOrPut(const jsval& aV
 
   JS::Rooted<JS::Value> keyval(aCx,
     (aOptionalArgCount >= 1) ? aKey : JSVAL_VOID);
 
   StructuredCloneWriteInfo cloneWriteInfo;
   Key key;
   nsTArray<IndexUpdateInfo> updateInfo;
 
-  nsresult rv = GetAddInfo(aCx, aValue, keyval, cloneWriteInfo, key,
+  JS::Rooted<JS::Value> value(aCx, aValue);
+  nsresult rv = GetAddInfo(aCx, value, keyval, cloneWriteInfo, key,
                            updateInfo);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsRefPtr<IDBRequest> request = GenerateRequest(this, aCx);
   NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
 
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -94,17 +94,17 @@ public:
   static bool
   DeserializeValue(JSContext* aCx,
                    StructuredCloneReadInfo& aCloneReadInfo,
                    jsval* aValue);
 
   static bool
   SerializeValue(JSContext* aCx,
                  StructuredCloneWriteInfo& aCloneWriteInfo,
-                 jsval aValue);
+                 JS::Handle<JS::Value> aValue);
 
   template <class DeserializationTraits>
   static JSObject*
   StructuredCloneReadCallback(JSContext* aCx,
                               JSStructuredCloneReader* aReader,
                               uint32_t aTag,
                               uint32_t aData,
                               void* aClosure);
@@ -252,18 +252,18 @@ public:
 
   static JSClass sDummyPropJSClass;
 
 protected:
   IDBObjectStore();
   ~IDBObjectStore();
 
   nsresult GetAddInfo(JSContext* aCx,
-                      jsval aValue,
-                      jsval aKeyVal,
+                      JS::Handle<JS::Value> aValue,
+                      JS::Handle<JS::Value> aKeyVal,
                       StructuredCloneWriteInfo& aCloneWriteInfo,
                       Key& aKey,
                       nsTArray<IndexUpdateInfo>& aUpdateInfoArray);
 
   nsresult AddOrPut(const jsval& aValue,
                     const jsval& aKey,
                     JSContext* aCx,
                     uint8_t aOptionalArgCount,
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -337,17 +337,17 @@ IDBOpenDBRequest::~IDBOpenDBRequest()
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 }
 
 // static
 already_AddRefed<IDBOpenDBRequest>
 IDBOpenDBRequest::Create(IDBFactory* aFactory,
                          nsPIDOMWindow* aOwner,
-                         JSObject* aScriptOwner,
+                         JS::Handle<JSObject*> aScriptOwner,
                          JSContext* aCallingCx)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
   NS_ASSERTION(aFactory, "Null pointer!");
 
   nsRefPtr<IDBOpenDBRequest> request = new IDBOpenDBRequest();
 
   request->BindToOwner(aOwner);
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -125,17 +125,17 @@ public:
   NS_FORWARD_NSIIDBREQUEST(IDBRequest::)
   NS_DECL_NSIIDBOPENDBREQUEST
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, IDBRequest)
 
   static
   already_AddRefed<IDBOpenDBRequest>
   Create(IDBFactory* aFactory,
          nsPIDOMWindow* aOwner,
-         JSObject* aScriptOwner,
+         JS::Handle<JSObject*> aScriptOwner,
          JSContext* aCallingCx);
 
   void SetTransaction(IDBTransaction* aTransaction);
 
   // nsIDOMEventTarget
   virtual nsresult PostHandleEvent(nsEventChainPostVisitor& aVisitor);
 
   IDBFactory*
--- a/dom/indexedDB/KeyPath.cpp
+++ b/dom/indexedDB/KeyPath.cpp
@@ -153,35 +153,35 @@ GetJSValFromKeyPathString(JSContext* aCx
       // We have started inserting new objects or are about to just insert
       // the first one.
 
       *aKeyJSVal = JSVAL_VOID;
 
       if (tokenizer.hasMoreTokens()) {
         // If we're not at the end, we need to add a dummy object to the
         // chain.
-        JSObject* dummy = JS_NewObject(aCx, nullptr, nullptr, nullptr);
+        JS::Rooted<JSObject*> dummy(aCx, JS_NewObject(aCx, nullptr, nullptr, nullptr));
         if (!dummy) {
           rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
           break;
         }
 
         if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(),
                                  token.Length(),
                                  OBJECT_TO_JSVAL(dummy), nullptr, nullptr,
                                  JSPROP_ENUMERATE)) {
           rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
           break;
         }
 
         obj = dummy;
       }
       else {
-        JSObject* dummy = JS_NewObject(aCx, &IDBObjectStore::sDummyPropJSClass,
-                                       nullptr, nullptr);
+        JS::Rooted<JSObject*> dummy(aCx, JS_NewObject(aCx, &IDBObjectStore::sDummyPropJSClass,
+                                                      nullptr, nullptr));
         if (!dummy) {
           rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
           break;
         }
 
         if (!JS_DefineUCProperty(aCx, obj, token.BeginReading(),
                                  token.Length(), OBJECT_TO_JSVAL(dummy),
                                  nullptr, nullptr, JSPROP_ENUMERATE)) {
--- a/dom/mobilemessage/src/MmsMessage.cpp
+++ b/dom/mobilemessage/src/MmsMessage.cpp
@@ -99,17 +99,17 @@ MmsMessage::MmsMessage(const mobilemessa
  * @params aReturn
  *         The integer value to return.
  * @return NS_OK if the convertion succeeds.
  */
 static nsresult
 convertTimeToInt(JSContext* aCx, const JS::Value& aTime, uint64_t& aReturn)
 {
   if (aTime.isObject()) {
-    JSObject* timestampObj = &aTime.toObject();
+    JS::Rooted<JSObject*> timestampObj(aCx, &aTime.toObject());
     if (!JS_ObjectIsDate(aCx, timestampObj)) {
       return NS_ERROR_INVALID_ARG;
     }
     aReturn = js_DateGetMsecSinceEpoch(timestampObj);
   } else {
     if (!aTime.isNumber()) {
       return NS_ERROR_INVALID_ARG;
     }
@@ -155,28 +155,28 @@ MmsMessage::Create(int32_t              
   } else {
     return NS_ERROR_INVALID_ARG;
   }
 
   // Set |deliveryStatus|.
   if (!aDeliveryStatus.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
-  JSObject* deliveryStatusObj = &aDeliveryStatus.toObject();
+  JS::Rooted<JSObject*> deliveryStatusObj(aCx, &aDeliveryStatus.toObject());
   if (!JS_IsArrayObject(aCx, deliveryStatusObj)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   uint32_t length;
   JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, deliveryStatusObj, &length));
 
   nsTArray<DeliveryStatus> deliveryStatus;
+  JS::Rooted<JS::Value> statusJsVal(aCx);
   for (uint32_t i = 0; i < length; ++i) {
-    JS::Value statusJsVal;
-    if (!JS_GetElement(aCx, deliveryStatusObj, i, &statusJsVal) ||
+    if (!JS_GetElement(aCx, deliveryStatusObj, i, statusJsVal.address()) ||
         !statusJsVal.isString()) {
       return NS_ERROR_INVALID_ARG;
     }
 
     nsDependentJSString statusStr;
     statusStr.init(aCx, statusJsVal.toString());
 
     DeliveryStatus status;
@@ -194,27 +194,27 @@ MmsMessage::Create(int32_t              
 
     deliveryStatus.AppendElement(status);
   }
 
   // Set |receivers|.
   if (!aReceivers.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
-  JSObject* receiversObj = &aReceivers.toObject();
+  JS::Rooted<JSObject*> receiversObj(aCx, &aReceivers.toObject());
   if (!JS_IsArrayObject(aCx, receiversObj)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, receiversObj, &length));
 
   nsTArray<nsString> receivers;
+  JS::Rooted<JS::Value> receiverJsVal(aCx);
   for (uint32_t i = 0; i < length; ++i) {
-    JS::Value receiverJsVal;
-    if (!JS_GetElement(aCx, receiversObj, i, &receiverJsVal) ||
+    if (!JS_GetElement(aCx, receiversObj, i, receiverJsVal.address()) ||
         !receiverJsVal.isString()) {
       return NS_ERROR_INVALID_ARG;
     }
 
     nsDependentJSString receiverStr;
     receiverStr.init(aCx, receiverJsVal.toString());
     receivers.AppendElement(receiverStr);
   }
@@ -223,32 +223,32 @@ MmsMessage::Create(int32_t              
   uint64_t timestamp;
   nsresult rv = convertTimeToInt(aCx, aTimestamp, timestamp);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Set |attachments|.
   if (!aAttachments.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
-  JSObject* attachmentsObj = &aAttachments.toObject();
+  JS::Rooted<JSObject*> attachmentsObj(aCx, &aAttachments.toObject());
   if (!JS_IsArrayObject(aCx, attachmentsObj)) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsTArray<MmsAttachment> attachments;
   JS_ALWAYS_TRUE(JS_GetArrayLength(aCx, attachmentsObj, &length));
 
+  JS::Rooted<JS::Value> attachmentJsVal(aCx);
   for (uint32_t i = 0; i < length; ++i) {
-    JS::Value attachmentJsVal;
-    if (!JS_GetElement(aCx, attachmentsObj, i, &attachmentJsVal)) {
+    if (!JS_GetElement(aCx, attachmentsObj, i, attachmentJsVal.address())) {
       return NS_ERROR_INVALID_ARG;
     }
 
     MmsAttachment attachment;
-    rv = attachment.Init(aCx, &attachmentJsVal);
+    rv = attachment.Init(aCx, attachmentJsVal.address());
     NS_ENSURE_SUCCESS(rv, rv);
 
     attachments.AppendElement(attachment);
   }
 
   // Set |expiryDate|.
   uint64_t expiryDate;
   rv = convertTimeToInt(aCx, aExpiryDate, expiryDate);
@@ -384,36 +384,36 @@ MmsMessage::GetDeliveryStatus(JSContext*
       case eDeliveryStatus_EndGuard:
       default:
         MOZ_NOT_REACHED("We shouldn't get any other delivery status!");
         return NS_ERROR_UNEXPECTED;
     }
     tempStrArray.AppendElement(statusStr);
   }
 
-  JSObject* deliveryStatusObj = nullptr;
-  nsresult rv = nsTArrayToJSArray(aCx, tempStrArray, &deliveryStatusObj);
+  JS::Rooted<JSObject*> deliveryStatusObj(aCx);
+  nsresult rv = nsTArrayToJSArray(aCx, tempStrArray, deliveryStatusObj.address());
   NS_ENSURE_SUCCESS(rv, rv);
 
   aDeliveryStatus->setObject(*deliveryStatusObj);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MmsMessage::GetSender(nsAString& aSender)
 {
   aSender = mSender;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MmsMessage::GetReceivers(JSContext* aCx, JS::Value* aReceivers)
 {
-  JSObject* reveiversObj = nullptr;
-  nsresult rv = nsTArrayToJSArray(aCx, mReceivers, &reveiversObj);
+  JS::Rooted<JSObject*> reveiversObj(aCx);
+  nsresult rv = nsTArrayToJSArray(aCx, mReceivers, reveiversObj.address());
   NS_ENSURE_SUCCESS(rv, rv);
 
   aReceivers->setObject(*reveiversObj);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MmsMessage::GetTimestamp(JSContext* cx, JS::Value* aDate)
@@ -452,26 +452,26 @@ MmsMessage::GetAttachments(JSContext* aC
   // TODO Bug 850529 We should return an empty array (or null)
   // when it has no attachments? Need to further check this.
   uint32_t length = mAttachments.Length();
   if (length == 0) {
     *aAttachments = JSVAL_NULL;
     return NS_OK;
   }
 
-  JSObject* attachments = JS_NewArrayObject(aCx, length, nullptr);
+  JS::Rooted<JSObject*> attachments(aCx, JS_NewArrayObject(aCx, length, nullptr));
   NS_ENSURE_TRUE(attachments, NS_ERROR_OUT_OF_MEMORY);
 
   for (uint32_t i = 0; i < length; ++i) {
     const MmsAttachment &attachment = mAttachments[i];
 
-    JSObject* attachmentObj = JS_NewObject(aCx, nullptr, nullptr, nullptr);
+    JS::Rooted<JSObject*> attachmentObj(aCx, JS_NewObject(aCx, nullptr, nullptr, nullptr));
     NS_ENSURE_TRUE(attachmentObj, NS_ERROR_OUT_OF_MEMORY);
 
-    JS::Value tmpJsVal;
+    JS::Rooted<JS::Value> tmpJsVal(aCx);
     JSString* tmpJsStr;
 
     // Get |attachment.mId|.
     tmpJsStr = JS_NewUCStringCopyN(aCx,
                                    attachment.id.get(),
                                    attachment.id.Length());
     NS_ENSURE_TRUE(tmpJsStr, NS_ERROR_OUT_OF_MEMORY);
 
@@ -494,26 +494,26 @@ MmsMessage::GetAttachments(JSContext* aC
     }
 
     // Get |attachment.mContent|.
     JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForScopeChain(aCx));
     nsresult rv = nsContentUtils::WrapNative(aCx,
                                              global,
                                              attachment.content,
                                              &NS_GET_IID(nsIDOMBlob),
-                                             &tmpJsVal);
+                                             tmpJsVal.address());
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!JS_DefineProperty(aCx, attachmentObj, "content", tmpJsVal,
                            NULL, NULL, JSPROP_ENUMERATE)) {
       return NS_ERROR_FAILURE;
     }
 
     tmpJsVal = OBJECT_TO_JSVAL(attachmentObj);
-    if (!JS_SetElement(aCx, attachments, i, &tmpJsVal)) {
+    if (!JS_SetElement(aCx, attachments, i, tmpJsVal.address())) {
       return NS_ERROR_FAILURE;
     }
   }
 
   aAttachments->setObject(*attachments);
   return NS_OK;
 }
 
--- a/dom/mobilemessage/src/SmsManager.cpp
+++ b/dom/mobilemessage/src/SmsManager.cpp
@@ -136,17 +136,17 @@ SmsManager::GetSegmentInfoForText(const 
 {
   nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE);
 
   return smsService->GetSegmentInfoForText(aText, aResult);
 }
 
 nsresult
-SmsManager::Send(JSContext* aCx, JSObject* aGlobal, JS::Handle<JSString*> aNumber,
+SmsManager::Send(JSContext* aCx, JS::Handle<JSObject*> aGlobal, JS::Handle<JSString*> aNumber,
                  const nsAString& aMessage, JS::Value* aRequest)
 {
   nsCOMPtr<nsISmsService> smsService = do_GetService(SMS_SERVICE_CONTRACTID);
   NS_ENSURE_TRUE(smsService, NS_ERROR_FAILURE);
 
   nsDependentJSString number;
   number.init(aCx, aNumber);
 
--- a/dom/mobilemessage/src/SmsManager.h
+++ b/dom/mobilemessage/src/SmsManager.h
@@ -31,17 +31,17 @@ public:
 
   void Init(nsPIDOMWindow *aWindow);
   void Shutdown();
 
 private:
   /**
    * Internal Send() method used to send one message.
    */
-  nsresult Send(JSContext* aCx, JSObject* aGlobal, JS::Handle<JSString*> aNumber,
+  nsresult Send(JSContext* aCx, JS::Handle<JSObject*> aGlobal, JS::Handle<JSString*> aNumber,
                 const nsAString& aMessage, JS::Value* aRequest);
 
   nsresult DispatchTrustedSmsEventToSelf(const nsAString& aEventName,
                                          nsIDOMMozSmsMessage* aMessage);
 
   /**
    * Helper to get message ID from SMS Message object
    */
--- a/dom/mobilemessage/src/ipc/SmsParent.cpp
+++ b/dom/mobilemessage/src/ipc/SmsParent.cpp
@@ -50,23 +50,23 @@ MmsAttachmentDataToJSObject(JSContext* a
                                          aAttachment.location().Length());
   NS_ENSURE_TRUE(locStr, nullptr);
   if (!JS_DefineProperty(aContext, obj, "location", JS::StringValue(locStr),
                          nullptr, nullptr, 0)) {
     return nullptr;
   }
 
   nsCOMPtr<nsIDOMBlob> blob = static_cast<BlobParent*>(aAttachment.contentParent())->GetBlob();
-  JS::Value content;
-  JS::Rooted<JSObject*> global (aContext, JS_GetGlobalForScopeChain(aContext));
+  JS::Rooted<JS::Value> content(aContext);
+  JS::Rooted<JSObject*> global(aContext, JS_GetGlobalForScopeChain(aContext));
   nsresult rv = nsContentUtils::WrapNative(aContext,
                                            global,
                                            blob,
                                            &NS_GET_IID(nsIDOMBlob),
-                                           &content);
+                                           content.address());
   NS_ENSURE_SUCCESS(rv, nullptr);
   if (!JS_DefineProperty(aContext, obj, "content", content,
                          nullptr, nullptr, 0)) {
     return nullptr;
   }
 
   return obj;
 }
--- a/dom/network/src/TCPSocketChild.cpp
+++ b/dom/network/src/TCPSocketChild.cpp
@@ -13,32 +13,32 @@
 #include "jsfriendapi.h"
 #include "jswrapper.h"
 
 using mozilla::net::gNeckoChild;
 
 namespace IPC {
 
 bool
-DeserializeArrayBuffer(JSObject* aObj,
+DeserializeArrayBuffer(JS::Handle<JSObject*> aObj,
                        const InfallibleTArray<uint8_t>& aBuffer,
-                       JS::Value* aVal)
+                       JS::MutableHandle<JS::Value> aVal)
 {
   JSContext* cx = nsContentUtils::GetSafeJSContext();
   JSAutoRequest ar(cx);
   JSAutoCompartment ac(cx, aObj);
 
   JS::Rooted<JSObject*> obj(cx, JS_NewArrayBuffer(cx, aBuffer.Length()));
   if (!obj)
     return false;
   uint8_t* data = JS_GetArrayBufferData(obj);
   if (!data)
     return false;
   memcpy(data, aBuffer.Elements(), aBuffer.Length());
-  *aVal = OBJECT_TO_JSVAL(obj);
+  aVal.set(OBJECT_TO_JSVAL(obj));
   return true;
 }
 
 } // namespace IPC
 
 namespace mozilla {
 namespace dom {
 
@@ -130,18 +130,20 @@ TCPSocketChild::RecvCallback(const nsStr
   } else if (aData.type() == CallbackData::TTCPError) {
     const TCPError& err(aData.get_TCPError());
     rv = mSocket->CallListenerError(aType, err.name());
 
   } else if (aData.type() == CallbackData::TSendableData) {
     const SendableData& data = aData.get_SendableData();
 
     if (data.type() == SendableData::TArrayOfuint8_t) {
-      JS::Value val;
-      bool ok = IPC::DeserializeArrayBuffer(mSocketObj, data.get_ArrayOfuint8_t(), &val);
+      JSContext* cx = nsContentUtils::GetSafeJSContext();
+      JS::Rooted<JS::Value> val(cx);
+      JS::Rooted<JSObject*> socket(cx, mSocketObj);
+      bool ok = IPC::DeserializeArrayBuffer(socket, data.get_ArrayOfuint8_t(), &val);
       NS_ENSURE_TRUE(ok, true);
       rv = mSocket->CallListenerArrayBuffer(aType, val);
 
     } else if (data.type() == SendableData::TnsString) {
       rv = mSocket->CallListenerData(aType, data.get_nsString());
 
     } else {
       MOZ_NOT_REACHED("Invalid callback data type!");
--- a/dom/network/src/TCPSocketParent.cpp
+++ b/dom/network/src/TCPSocketParent.cpp
@@ -2,26 +2,27 @@
  * 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/. */
 
 #include "TCPSocketParent.h"
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "nsJSUtils.h"
 #include "nsIDOMTCPSocket.h"
+#include "nsContentUtils.h"
 #include "mozilla/unused.h"
 #include "mozilla/AppProcessChecker.h"
 
 namespace IPC {
 
 //Defined in TCPSocketChild.cpp
 extern bool
-DeserializeArrayBuffer(JSObject* aObj,
+DeserializeArrayBuffer(JS::Handle<JSObject*> aObj,
                        const InfallibleTArray<uint8_t>& aBuffer,
-                       JS::Value* aVal);
+                       JS::MutableHandle<JS::Value> aVal);
 
 }
 
 namespace mozilla {
 namespace dom {
 
 static void
 FireInteralError(mozilla::net::PTCPSocketParent* aActor, uint32_t aLineNo)
@@ -90,18 +91,20 @@ TCPSocketParent::RecvResume()
 bool
 TCPSocketParent::RecvData(const SendableData& aData)
 {
   NS_ENSURE_TRUE(mIntermediary, true);
 
   nsresult rv;
   switch (aData.type()) {
     case SendableData::TArrayOfuint8_t: {
-      JS::Value val;
-      IPC::DeserializeArrayBuffer(mIntermediaryObj, aData.get_ArrayOfuint8_t(), &val);
+      AutoSafeJSContext cx;
+      JS::Rooted<JS::Value> val(cx);
+      JS::Rooted<JSObject*> obj(cx, mIntermediaryObj);
+      IPC::DeserializeArrayBuffer(obj, aData.get_ArrayOfuint8_t(), &val);
       rv = mIntermediary->SendArrayBuffer(val);
       NS_ENSURE_SUCCESS(rv, true);
       break;
     }
 
     case SendableData::TnsString:
       rv = mIntermediary->SendString(aData.get_nsString());
       NS_ENSURE_SUCCESS(rv, true);
@@ -162,18 +165,18 @@ TCPSocketParent::SendCallback(const nsAS
       }
       InfallibleTArray<uint8_t> arr;
       arr.SwapElements(fallibleArr);
       data = SendableData(arr);
 
     } else {
       nsDependentJSString name;
 
-      JS::Value val;
-      if (!JS_GetProperty(aCx, obj, "name", &val)) {
+      JS::Rooted<JS::Value> val(aCx);
+      if (!JS_GetProperty(aCx, obj, "name", val.address())) {
         NS_ERROR("No name property on supposed error object");
       } else if (JSVAL_IS_STRING(val)) {
         if (!name.init(aCx, JSVAL_TO_STRING(val))) {
           NS_WARNING("couldn't initialize string");
         }
       }
 
       data = TCPError(name);
--- a/dom/network/tests/unit/test_tcpsocket.js
+++ b/dom/network/tests/unit/test_tcpsocket.js
@@ -508,14 +508,9 @@ add_test(cleanup);
 
 function run_test() {
   if (!gInChild)
     Services.prefs.setBoolPref('dom.mozTCPSocket.enabled', true);
 
   server = new TestServer();
 
   run_next_test();
-
-  do_timeout(10000, function() {
-    do_throw(
-      "The test should never take this long unless the system is hosed.");
-  });
 }
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1556,17 +1556,17 @@ bool NP_CALLBACK
 
   NPN_PLUGIN_LOG(PLUGIN_LOG_NOISY,
                  ("NPN_Evaluate(npp %p, npobj %p, script <<<%s>>>) called\n",
                   npp, npobj, script->UTF8Characters));
 
   JS::CompileOptions options(cx);
   options.setFileAndLine(spec, 0)
          .setVersion(JSVERSION_DEFAULT);
-  nsresult rv = scx->EvaluateString(utf16script, *obj, options,
+  nsresult rv = scx->EvaluateString(utf16script, obj, options,
                                     /* aCoerceToString = */ false,
                                     rval);
 
   return NS_SUCCEEDED(rv) &&
          (!result || JSValToNPVariant(npp, cx, *rval, result));
 }
 
 bool NP_CALLBACK
--- a/dom/push/src/PushService.jsm
+++ b/dom/push/src/PushService.jsm
@@ -14,17 +14,16 @@ const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 Cu.import("resource://gre/modules/Preferences.jsm");
 Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
-Cu.import("resource://gre/modules/AlarmService.jsm");
 
 this.EXPORTED_SYMBOLS = ["PushService"];
 
 const prefs = new Preferences("services.push.");
 
 const kPUSHDB_DB_NAME = "push";
 const kPUSHDB_DB_VERSION = 1; // Change this if the IndexedDB format changes
 const kPUSHDB_STORE_NAME = "push";
@@ -83,18 +82,17 @@ this.PushDB.prototype = {
     debug("put()");
 
     this.newTxn(
       "readwrite",
       kPUSHDB_STORE_NAME,
       function txnCb(aTxn, aStore) {
         debug("Going to put " + aChannelRecord.channelID);
         aStore.put(aChannelRecord).onsuccess = function setTxnResult(aEvent) {
-          debug("Request successful. Updated record ID: " +
-                aEvent.target.result);
+          debug("Request successful. Updated record ID: " + aEvent.target.result);
         };
       },
       aSuccessCb,
       aErrorCb
     );
   },
 
   /*
@@ -264,22 +262,22 @@ this.PushWebSocketListener.prototype = {
         return;
     this._pushService._wsOnServerClose(context, aStatusCode, aReason);
   }
 }
 
 // websocket states
 // websocket is off
 const STATE_SHUT_DOWN = 0;
-// Websocket has been opened on client side, waiting for successful open.
+// websocket has been opened on client side, waiting for successful open
 // (_wsOnStart)
 const STATE_WAITING_FOR_WS_START = 1;
-// Websocket opened, hello sent, waiting for server reply (_handleHelloReply).
+// websocket opened, hello sent, waiting for server reply (_handleHelloReply)
 const STATE_WAITING_FOR_HELLO = 2;
-// Websocket operational, handshake completed, begin protocol messaging.
+// websocket operational, handshake completed, begin protocol messaging
 const STATE_READY = 3;
 
 /**
  * The implementation of the SimplePush system. This runs in the B2G parent
  * process and is started on boot. It uses WebSockets to communicate with the
  * server and PushDB (IndexedDB) for persistence.
  */
 this.PushService = {
@@ -330,33 +328,35 @@ this.PushService = {
 
               delete this._pendingRequests[channelID];
               for (var i = this._requestQueue.length - 1; i >= 0; --i)
                 if (this._requestQueue[i].channelID == channelID)
                   this._requestQueue.splice(i, 1);
             }
           }
         }
+        else if (aSubject == this._retryTimeoutTimer) {
+          this._beginWSSetup();
+        }
         break;
       case "webapps-uninstall":
         debug("webapps-uninstall");
         let appsService = Cc["@mozilla.org/AppsService;1"]
                             .getService(Ci.nsIAppsService);
         var app = appsService.getAppFromObserverMessage(aData);
         if (!app) {
           debug("webapps-uninstall: No app found " + aData.origin);
           return;
         }
 
         this._db.getAllByManifestURL(app.manifestURL, function(records) {
           debug("Got " + records.length);
           for (var i = 0; i < records.length; i++) {
             this._db.delete(records[i].channelID, null, function() {
-              debug("app uninstall: " + app.manifestURL +
-                    " Could not delete entry " + records[i].channelID);
+              debug("app uninstall: " + app.manifestURL + " Could not delete entry " + records[i].channelID);
             });
             // courtesy, but don't establish a connection
             // just for it
             if (this._ws) {
               debug("Had a connection, so telling the server");
               this._request("unregister", {channelID: records[i].channelID});
             }
           }
@@ -384,16 +384,35 @@ this.PushService = {
 
   // keeps requests buffered if the websocket disconnects or is not connected
   _requestQueue: [],
   _ws: null,
   _pendingRequests: {},
   _currentState: STATE_SHUT_DOWN,
   _requestTimeout: 0,
   _requestTimeoutTimer: null,
+
+  /**
+   * How retries work:  The goal is to ensure websocket is always up on
+   * networks not supporting UDP. So the websocket should only be shutdown if
+   * onServerClose indicates UDP wakeup.  If WS is closed due to socket error,
+   * _socketError() is called.  The retry timer is started and when it times
+   * out, beginWSSetup() is called again.
+   *
+   * On a successful connection, the timer is cancelled if it is running and
+   * the values are reset to defaults.
+   *
+   * If we are in the middle of a timeout (i.e. waiting), but
+   * a register/unregister is called, we don't want to wait around anymore.
+   * _sendRequest will automatically call beginWSSetup(), which will cancel the
+   * timer. In addition since the state will have changed, even if a pending
+   * timer event comes in (because the timer fired the event before it was
+   * cancelled), so the connection won't be reset.
+   */
+  _retryTimeoutTimer: null,
   _retryFailCount: 0,
 
   /**
    * According to the WS spec, servers should immediately close the underlying
    * TCP connection after they close a WebSocket. This causes wsOnStop to be
    * called with error NS_BASE_STREAM_CLOSED. Since the client has to keep the
    * WebSocket up, it should try to reconnect. But if the server closes the
    * WebSocket because it will wake up the client via UDP, then the client
@@ -416,18 +435,16 @@ this.PushService = {
 
     let ppmm = Cc["@mozilla.org/parentprocessmessagemanager;1"]
                  .getService(Ci.nsIMessageBroadcaster);
 
     kCHILD_PROCESS_MESSAGES.forEach(function addMessage(msgName) {
         ppmm.addMessageListener(msgName, this);
     }.bind(this));
 
-    this._alarmID = null;
-
     this._requestTimeout = prefs.get("requestTimeout");
 
     this._udpPort = prefs.get("udp.port");
 
     this._db.getAllChannelIDs(
       function(channelIDs) {
         if (channelIDs.length > 0) {
           debug("Found registered channelIDs. Starting WebSocket");
@@ -444,26 +461,22 @@ this.PushService = {
     // slightly different URLs.
     prefs.observe("serverURL", this);
   },
 
   _shutdownWS: function() {
     debug("shutdownWS()");
     this._currentState = STATE_SHUT_DOWN;
     this._willBeWokenUpByUDP = false;
-
     if (this._wsListener)
       this._wsListener._pushService = null;
     try {
         this._ws.close(0, null);
     } catch (e) {}
     this._ws = null;
-
-    this._waitingForPong = false;
-    this._stopAlarm();
   },
 
   _shutdown: function() {
     debug("_shutdown()");
 
     Services.obs.removeObserver(this, "network-interface-state-changed",
                                 false);
     Services.obs.removeObserver(this, "webapps-uninstall", false);
@@ -480,66 +493,60 @@ this.PushService = {
     // All pending requests (ideally none) are dropped at this point. We
     // shouldn't have any applications performing registration/unregistration
     // or receiving notifications.
     this._shutdownWS();
 
     // At this point, profile-change-net-teardown has already fired, so the
     // WebSocket has been closed with NS_ERROR_ABORT (if it was up) and will
     // try to reconnect. Stop the timer.
-    this._stopAlarm();
+    if (this._retryTimeoutTimer)
+      this._retryTimeoutTimer.cancel();
 
     if (this._requestTimeoutTimer)
       this._requestTimeoutTimer.cancel();
 
     debug("shutdown complete!");
   },
 
-  /**
-   * How retries work:  The goal is to ensure websocket is always up on
-   * networks not supporting UDP. So the websocket should only be shutdown if
-   * onServerClose indicates UDP wakeup.  If WS is closed due to socket error,
-   * _reconnectAfterBackoff() is called.  The retry alarm is started and when
-   * it times out, beginWSSetup() is called again.
-   *
-   * On a successful connection, the alarm is cancelled in
-   * wsOnMessageAvailable() when the ping alarm is started.
-   *
-   * If we are in the middle of a timeout (i.e. waiting), but
-   * a register/unregister is called, we don't want to wait around anymore.
-   * _sendRequest will automatically call beginWSSetup(), which will cancel the
-   * timer. In addition since the state will have changed, even if a pending
-   * timer event comes in (because the timer fired the event before it was
-   * cancelled), so the connection won't be reset.
-   */
-  _reconnectAfterBackoff: function() {
-    debug("reconnectAfterBackoff()");
+  // aStatusCode is an NS error from Components.results
+  _socketError: function(aStatusCode) {
+    debug("socketError()");
 
-    // Calculate new timeout, but cap it to pingInterval.
+    // Calculate new timeout, but cap it to
     var retryTimeout = prefs.get("retryBaseInterval") *
                        Math.pow(2, this._retryFailCount);
-    retryTimeout = Math.min(retryTimeout, prefs.get("pingInterval"));
+
+    // It is easier to express the max interval as a pref in milliseconds,
+    // rather than have it as a number and make people do the calculation of
+    // retryBaseInterval * 2^maxRetryFailCount.
+    retryTimeout = Math.min(retryTimeout, prefs.get("maxRetryInterval"));
 
     this._retryFailCount++;
 
     debug("Retry in " + retryTimeout + " Try number " + this._retryFailCount);
-    this._setAlarm(retryTimeout);
+
+    if (!this._retryTimeoutTimer) {
+      this._retryTimeoutTimer = Cc["@mozilla.org/timer;1"]
+                                  .createInstance(Ci.nsITimer);
+    }
+
+    this._retryTimeoutTimer.init(this,
+                                 retryTimeout,
+                                 Ci.nsITimer.TYPE_ONE_SHOT);
   },
 
   _beginWSSetup: function() {
     debug("beginWSSetup()");
     if (this._currentState != STATE_SHUT_DOWN) {
       debug("_beginWSSetup: Not in shutdown state! Current state " +
             this._currentState);
       return;
     }
 
-    // Stop any pending reconnects scheduled for the near future.
-    this._stopAlarm();
-
     var serverURL = prefs.get("serverURL");
     if (!serverURL) {
       debug("No services.push.serverURL found!");
       return;
     }
 
     var uri;
     try {
@@ -558,111 +565,24 @@ this.PushService = {
       debug("Push over an insecure connection (ws://) is not allowed!");
       return;
     }
     else {
       debug("Unsupported websocket scheme " + uri.scheme);
       return;
     }
 
-
     debug("serverURL: " + uri.spec);
     this._wsListener = new PushWebSocketListener(this);
     this._ws.protocol = "push-notification";
+    this._ws.pingInterval = prefs.get("websocketPingInterval");
     this._ws.asyncOpen(uri, serverURL, this._wsListener, null);
     this._currentState = STATE_WAITING_FOR_WS_START;
   },
 
-  /** |delay| should be in milliseconds. */
-  _setAlarm: function(delay) {
-    // Stop any existing alarm.
-    this._stopAlarm();
-
-    AlarmService.add(
-      {
-        date: new Date(Date.now() + delay),
-        ignoreTimezone: true
-      },
-      this._onAlarmFired.bind(this),
-      function onSuccess(alarmID) {
-        this._alarmID = alarmID;
-        debug("Set alarm " + delay + " in the future " + this._alarmID);
-      }.bind(this)
-    )
-  },
-
-  _stopAlarm: function() {
-    if (this._alarmID !== null) {
-      debug("Stopped existing alarm " + this._alarmID);
-      AlarmService.remove(this._alarmID);
-      this._alarmID = null;
-    }
-  },
-
-  /**
-   * There is only one alarm active at any time. This alarm has 3 intervals
-   * corresponding to 3 tasks.
-   *
-   * 1) Reconnect on ping timeout.
-   *    If we haven't received any messages from the server by the time this
-   *    alarm fires, the connection is closed and PushService tries to
-   *    reconnect, repurposing the alarm for (3).
-   *
-   * 2) Send a ping.
-   *    The protocol sends a ping ({}) on the wire every pingInterval ms. Once
-   *    it sends the ping, the alarm goes to task (1) which is waiting for
-   *    a pong. If data is received after the ping is sent,
-   *    _wsOnMessageAvailable() will reset the ping alarm (which cancels
-   *    waiting for the pong). So as long as the connection is fine, pong alarm
-   *    never fires.
-   *
-   * 3) Reconnect after backoff.
-   *    The alarm is set by _reconnectAfterBackoff() and increases in duration
-   *    every time we try and fail to connect.  When it triggers, websocket
-   *    setup begins again. On successful socket setup, the socket starts
-   *    receiving messages. The alarm now goes to (2) where it monitors the
-   *    WebSocket by sending a ping.  Since incoming data is a sign of the
-   *    connection being up, the ping alarm is reset every time data is
-   *    received.
-   */
-  _onAlarmFired: function() {
-    // Conditions are arranged in decreasing specificity.
-    // i.e. when _waitingForPong is true, other conditions are also true.
-    if (this._waitingForPong) {
-      debug("Did not receive pong in time. Reconnecting WebSocket.");
-      this._shutdownWS();
-      this._reconnectAfterBackoff();
-    }
-    else if (this._currentState == STATE_READY) {
-      // Send a ping.
-      // Bypass the queue; we don't want this to be kept pending.
-      this._ws.sendMsg('{}');
-      debug("Sent ping.");
-      this._waitingForPong = true;
-      this._setAlarm(prefs.get("requestTimeout"));
-    }
-    else if (this._alarmID !== null) {
-      debug("reconnect alarm fired.");
-      // Reconnect after back-off.
-      // The check for a non-null _alarmID prevents a situation where the alarm
-      // fires, but _shutdownWS() is called from another code-path (e.g.
-      // network state change) and we don't want to reconnect.
-      //
-      // It also handles the case where _beginWSSetup() is called from another
-      // code-path.
-      //
-      // alarmID will be non-null only when no shutdown/connect is
-      // called between _reconnectAfterBackoff() setting the alarm and the
-      // alarm firing.
-
-      // Websocket is shut down. Backoff interval expired, try to connect.
-      this._beginWSSetup();
-    }
-  },
-
   /**
    * Protocol handler invoked by server message.
    */
   _handleHelloReply: function(reply) {
     debug("handleHelloReply()");
     if (this._currentState != STATE_WAITING_FOR_HELLO) {
       debug("Unexpected state " + this._currentState +
             "(expected STATE_WAITING_FOR_HELLO)");
@@ -1203,16 +1123,19 @@ this.PushService = {
   _wsOnStart: function(context) {
     debug("wsOnStart()");
     if (this._currentState != STATE_WAITING_FOR_WS_START) {
       debug("NOT in STATE_WAITING_FOR_WS_START. Current state " +
             this._currentState + ". Skipping");
       return;
     }
 
+    if (this._retryTimeoutTimer)
+      this._retryTimeoutTimer.cancel();
+
     // Since we've had a successful connection reset the retry fail count.
     this._retryFailCount = 0;
 
     var data = {
       messageType: "hello",
     }
 
     if (this._UAID)
@@ -1249,35 +1172,27 @@ this.PushService = {
    * connection close status code.
    *
    * If we do not explicitly call ws.close() then statusCode is always
    * NS_BASE_STREAM_CLOSED, even on a successful close.
    */
   _wsOnStop: function(context, statusCode) {
     debug("wsOnStop()");
 
-    this._shutdownWS();
-
     if (statusCode != Cr.NS_OK &&
         !(statusCode == Cr.NS_BASE_STREAM_CLOSED && this._willBeWokenUpByUDP)) {
       debug("Socket error " + statusCode);
-      this._reconnectAfterBackoff();
+      this._socketError(statusCode);
     }
 
+    this._shutdownWS();
   },
 
   _wsOnMessageAvailable: function(context, message) {
     debug("wsOnMessageAvailable() " + message);
-
-    this._waitingForPong = false;
-
-    // Reset the ping timer.  Note: This path is executed at every step of the
-    // handshake, so this alarm does not need to be set explicitly at startup.
-    this._setAlarm(prefs.get("pingInterval"));
-
     var reply = undefined;
     try {
       reply = JSON.parse(message);
     } catch(e) {
       debug("Parsing JSON failed. text : " + message);
       return;
     }
 
@@ -1309,17 +1224,17 @@ this.PushService = {
     }
 
     this[handler](reply);
   },
 
   /**
    * The websocket should never be closed. Since we don't call ws.close(),
    * _wsOnStop() receives error code NS_BASE_STREAM_CLOSED (see comment in that
-   * function), which calls reconnect and re-establishes the WebSocket
+   * function), which calls socketError and re-establishes the WebSocket
    * connection.
    *
    * If the server said it'll use UDP for wakeup, we set _willBeWokenUpByUDP
    * and stop reconnecting in _wsOnStop().
    */
   _wsOnServerClose: function(context, aStatusCode, aReason) {
     debug("wsOnServerClose() " + aStatusCode + " " + aReason);
 
@@ -1373,19 +1288,19 @@ this.PushService = {
    * notifications.
    */
   onStopListening: function(aServ, aStatus) {
     debug("UDP Server socket was shutdown. Status: " + aStatus);
     this._beginWSSetup();
   },
 
   /**
-   * Get mobile network information to decide if the client is capable of being
-   * woken up by UDP (which currently just means having an mcc and mnc along
-   * with an IP).
+   * Get mobile network information to decide if the client is capable of being woken
+   * up by UDP (which currently just means having an mcc and mnc along with an
+   * IP).
    */
   _getNetworkState: function() {
     debug("getNetworkState()");
     try {
       var nm = Cc["@mozilla.org/network/manager;1"].getService(Ci.nsINetworkManager);
       if (nm.active && nm.active.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
         var mcp = Cc["@mozilla.org/ril/content-helper;1"].getService(Ci.nsIMobileConnectionProvider);
         if (mcp.iccInfo) {
--- a/dom/src/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/src/jsurl/nsJSProtocolHandler.cpp
@@ -311,17 +311,17 @@ nsresult nsJSThunk::EvaluateScript(nsICh
         }
     } else {
         // No need to use the sandbox, evaluate the script directly in
         // the given scope.
         JS::CompileOptions options(cx);
         options.setFileAndLine(mURL.get(), 1)
                .setVersion(JSVERSION_DEFAULT);
         rv = scriptContext->EvaluateString(NS_ConvertUTF8toUTF16(script),
-                                           *globalJSObject, options,
+                                           globalJSObject, options,
                                            /* aCoerceToString = */ true,
                                            v.address());
 
         // If there's an error on cx as a result of that call, report
         // it now -- either we're just running under the event loop,
         // so we shouldn't propagate JS exceptions out of here, or we
         // can't be sure that our caller is JS (and if it's not we'll
         // lose the error), or it might be JS that then proceeds to
--- a/dom/system/OSFileConstants.cpp
+++ b/dom/system/OSFileConstants.cpp
@@ -596,17 +596,17 @@ static dom::ConstantSpec gWinProperties[
 
 
 /**
  * Get a field of an object as an object.
  *
  * If the field does not exist, create it. If it exists but is not an
  * object, throw a JS error.
  */
-JSObject *GetOrCreateObjectProperty(JSContext *cx, JSObject *aObject,
+JSObject *GetOrCreateObjectProperty(JSContext *cx, JS::Handle<JSObject*> aObject,
                                     const char *aProperty)
 {
   JS::Rooted<JS::Value> val(cx);
   if (!JS_GetProperty(cx, aObject, aProperty, val.address())) {
     return NULL;
   }
   if (!val.isUndefined()) {
     if (val.isObject()) {
@@ -620,17 +620,17 @@ JSObject *GetOrCreateObjectProperty(JSCo
   return JS_DefineObject(cx, aObject, aProperty, NULL, NULL, JSPROP_ENUMERATE);
 }
 
 /**
  * Set a property of an object from a nsString.
  *
  * If the nsString is void (i.e. IsVoid is true), do nothing.
  */
-bool SetStringProperty(JSContext *cx, JSObject *aObject, const char *aProperty,
+bool SetStringProperty(JSContext *cx, JS::Handle<JSObject*> aObject, const char *aProperty,
                        const nsString aValue)
 {
   if (aValue.IsVoid()) {
     return true;
   }
   JSString* strValue = JS_NewUCStringCopyZ(cx, aValue.get());
   NS_ENSURE_TRUE(strValue, false);
   JS::Value valValue = STRING_TO_JSVAL(strValue);
@@ -638,17 +638,17 @@ bool SetStringProperty(JSContext *cx, JS
 }
 
 /**
  * Define OS-specific constants.
  *
  * This function creates or uses JS object |OS.Constants| to store
  * all its constants.
  */
-bool DefineOSFileConstants(JSContext *cx, JSObject *global)
+bool DefineOSFileConstants(JSContext *cx, JS::Handle<JSObject*> global)
 {
   MOZ_ASSERT(gInitialized);
 
   if (gPaths == NULL) {
     // If an initialization error was ignored, we may end up with
     // |gInitialized == true| but |gPaths == NULL|. We cannot
     // |MOZ_ASSERT| this, as this would kill precompile_cache.js,
     // so we simply return an error.
--- a/dom/system/OSFileConstants.h
+++ b/dom/system/OSFileConstants.h
@@ -34,17 +34,17 @@ nsresult InitOSFileConstants();
 void CleanupOSFileConstants();
 
 /**
  * Define OS-specific constants.
  *
  * This function creates or uses JS object |OS.Constants| to store
  * all its constants.
  */
-bool DefineOSFileConstants(JSContext *cx, JSObject *global);
+bool DefineOSFileConstants(JSContext *cx, JS::Handle<JSObject*> global);
 
 /**
  * XPConnect initializer, for use in the main thread.
  */
 class OSFileConstantsService MOZ_FINAL : public nsIOSFileConstantsService
 {
  public:
   NS_DECL_ISUPPORTS
--- a/dom/workers/ChromeWorkerScope.cpp
+++ b/dom/workers/ChromeWorkerScope.cpp
@@ -48,19 +48,19 @@ UnicodeToNative(JSContext* aCx, const js
 BEGIN_WORKERS_NAMESPACE
 
 bool
 DefineChromeWorkerFunctions(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
 {
   // Currently ctypes is the only special property given to ChromeWorkers.
 #ifdef BUILD_CTYPES
   {
-    jsval ctypes;
+    JS::Rooted<JS::Value> ctypes(aCx);
     if (!JS_InitCTypesClass(aCx, aGlobal) ||
-        !JS_GetProperty(aCx, aGlobal, "ctypes", &ctypes)) {
+        !JS_GetProperty(aCx, aGlobal, "ctypes", ctypes.address())) {
       return false;
     }
 
     static JSCTypesCallbacks callbacks = {
       UnicodeToNative
     };
 
     JS_SetCTypesCallbacks(JSVAL_TO_OBJECT(ctypes), &callbacks);
--- a/dom/workers/EventTarget.cpp
+++ b/dom/workers/EventTarget.cpp
@@ -34,65 +34,68 @@ EventTarget::GetEventListener(const nsAS
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return NULL;
   }
 
   return mListenerManager.GetEventListener(INTERNED_STRING_TO_JSID(cx, type));
 }
 
 void
-EventTarget::SetEventListener(const nsAString& aType, JSObject* aListener,
+EventTarget::SetEventListener(const nsAString& aType, JSObject* aListener_,
                               ErrorResult& aRv)
 {
   JSContext* cx = GetJSContext();
+  JS::Rooted<JSObject*> aListener(cx, aListener_);
 
   JSString* type =
     JS_NewUCStringCopyN(cx, aType.BeginReading(), aType.Length());
   if (!type || !(type = JS_InternJSString(cx, type))) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   mListenerManager.SetEventListener(cx, INTERNED_STRING_TO_JSID(cx, type),
                                     aListener, aRv);
 }
 
 void
-EventTarget::AddEventListener(const nsAString& aType, JSObject* aListener,
+EventTarget::AddEventListener(const nsAString& aType, JSObject* aListener_,
                               bool aCapturing, Nullable<bool> aWantsUntrusted,
                               ErrorResult& aRv)
 {
-  if (!aListener) {
+  if (!aListener_) {
     return;
   }
 
   JSContext* cx = GetJSContext();
+  JS::Rooted<JSObject*> aListener(cx, aListener_);
 
   JSString* type =
     JS_NewUCStringCopyN(cx, aType.BeginReading(), aType.Length());
   if (!type || !(type = JS_InternJSString(cx, type))) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
   bool wantsUntrusted = !aWantsUntrusted.IsNull() && aWantsUntrusted.Value();
   mListenerManager.AddEventListener(cx, INTERNED_STRING_TO_JSID(cx, type),
                                     aListener, aCapturing, wantsUntrusted,
                                     aRv);
 }
 
 void
-EventTarget::RemoveEventListener(const nsAString& aType, JSObject* aListener,
+EventTarget::RemoveEventListener(const nsAString& aType, JSObject* aListener_,
                                  bool aCapturing, ErrorResult& aRv)
 {
-  if (!aListener) {
+  if (!aListener_) {
     return;
   }
 
   JSContext* cx = GetJSContext();
+  JS::Rooted<JSObject*> aListener(cx, aListener_);
 
   JSString* type =
     JS_NewUCStringCopyN(cx, aType.BeginReading(), aType.Length());
   if (!type || !(type = JS_InternJSString(cx, type))) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return;
   }
 
--- a/dom/workers/Exceptions.cpp
+++ b/dom/workers/Exceptions.cpp
@@ -225,22 +225,22 @@ DOMException::Create(JSContext* aCx, nsr
   const char* message;
   uint16_t code;
   if (NS_FAILED(NS_GetNameAndMessageForDOMNSResult(aNSResult, &name, &message,
                                                    &code))) {
     JS_ReportError(aCx, "Exception thrown (nsresult = 0x%x).", aNSResult);
     return NULL;
   }
 
-  JSString* jsname = JS_NewStringCopyZ(aCx, name);
+  JS::Rooted<JSString*> jsname(aCx, JS_NewStringCopyZ(aCx, name));
   if (!jsname) {
     return NULL;
   }
 
-  JSString* jsmessage = JS_NewStringCopyZ(aCx, message);
+  JS::Rooted<JSString*> jsmessage(aCx, JS_NewStringCopyZ(aCx, message));
   if (!jsmessage) {
     return NULL;
   }
 
   JS_SetReservedSlot(obj, SLOT_code, INT_TO_JSVAL(code));
   JS_SetReservedSlot(obj, SLOT_name, STRING_TO_JSVAL(jsname));
   JS_SetReservedSlot(obj, SLOT_message, STRING_TO_JSVAL(jsmessage));
 
--- a/dom/workers/File.cpp
+++ b/dom/workers/File.cpp
@@ -60,17 +60,17 @@ public:
     return obj;
   }
 
   static nsIDOMBlob*
   GetPrivate(JSObject* aObj);
 
 private:
   static nsIDOMBlob*
-  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
+  GetInstancePrivate(JSContext* aCx, JS::Handle<JSObject*> aObj, const char* aFunctionName)
   {
     nsIDOMBlob* blob = GetPrivate(aObj);
     if (blob) {
       return blob;
     }
 
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
                          JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
@@ -153,17 +153,17 @@ private:
     aVp.set(STRING_TO_JSVAL(jsType));
 
     return true;
   }
 
   static JSBool
   Slice(JSContext* aCx, unsigned aArgc, jsval* aVp)
   {
-    JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
+    JS::Rooted<JSObject*> obj(aCx, JS_THIS_OBJECT(aCx, aVp));
     if (!obj) {
       return false;
     }
 
     nsIDOMBlob* blob = GetInstancePrivate(aCx, obj, "slice");
     if (!blob) {
       return false;
     }
@@ -266,17 +266,17 @@ public:
   static JSClass*
   Class()
   {
     return &sClass;
   }
 
 private:
   static nsIDOMFile*
-  GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName)
+  GetInstancePrivate(JSContext* aCx, JS::Handle<JSObject*> aObj, const char* aFunctionName)
   {
     nsIDOMFile* file = GetPrivate(aObj);
     if (file) {
       return file;
     }
 
     JS_ReportErrorNumber(aCx, js_GetErrorMessage, NULL,
                          JSMSG_INCOMPATIBLE_PROTO, sClass.name, aFunctionName,
@@ -407,17 +407,17 @@ namespace file {
 
 JSObject*
 CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob)
 {
   return Blob::Create(aCx, aBlob);
 }
 
 bool
-InitClasses(JSContext* aCx, JSObject* aGlobal)
+InitClasses(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
 {
   JSObject* blobProto = Blob::InitClass(aCx, aGlobal);
   return blobProto && File::InitClass(aCx, aGlobal, blobProto);
 }
 
 nsIDOMBlob*
 GetDOMBlobFromJSObject(JSObject* aObj)
 {
--- a/dom/workers/File.h
+++ b/dom/workers/File.h
@@ -14,17 +14,17 @@
 class nsIDOMFile;
 class nsIDOMBlob;
 
 BEGIN_WORKERS_NAMESPACE
 
 namespace file {
 
 bool
-InitClasses(JSContext* aCx, JSObject* aGlobal);
+InitClasses(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
 
 JSObject*
 CreateBlob(JSContext* aCx, nsIDOMBlob* aBlob);
 
 nsIDOMBlob*
 GetDOMBlobFromJSObject(JSObject* aObj);
 
 JSObject*
--- a/dom/workers/ImageData.cpp
+++ b/dom/workers/ImageData.cpp
@@ -32,17 +32,18 @@ public:
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj)
   {
     return JS_InitClass(aCx, aObj, NULL, &sClass, Construct, 0, sProperties,
                         NULL, NULL, NULL);
   }
 
   static JSObject*
-  Create(JSContext* aCx, uint32_t aWidth, uint32_t aHeight, JSObject *aData)
+  Create(JSContext* aCx, uint32_t aWidth,
+         uint32_t aHeight, JS::Handle<JSObject*> aData)
   {
     MOZ_ASSERT(aData);
     MOZ_ASSERT(JS_IsTypedArrayObject(aData));
     MOZ_ASSERT(JS_IsUint8ClampedArray(aData));
 
     JSObject* obj = JS_NewObject(aCx, &sClass, NULL, NULL);
     if (!obj) {
       return NULL;
@@ -162,17 +163,18 @@ namespace imagedata {
 
 bool
 InitClass(JSContext* aCx, JSObject* aGlobal)
 {
   return !!ImageData::InitClass(aCx, aGlobal);
 }
 
 JSObject*
-Create(JSContext* aCx, uint32_t aWidth, uint32_t aHeight, JSObject* aData)
+Create(JSContext* aCx, uint32_t aWidth,
+       uint32_t aHeight, JS::Handle<JSObject*> aData)
 {
   return ImageData::Create(aCx, aWidth, aHeight, aData);
 }
 
 bool
 IsImageData(JSObject* aObj)
 {
   return ImageData::IsInstance(aObj);
--- a/dom/workers/ImageData.h
+++ b/dom/workers/ImageData.h
@@ -10,17 +10,18 @@
 BEGIN_WORKERS_NAMESPACE
 
 namespace imagedata {
 
 bool
 InitClass(JSContext* aCx, JSObject* aGlobal);
 
 JSObject*
-Create(JSContext* aCx, uint32_t aWidth, uint32_t aHeight, JSObject* aData);
+Create(JSContext* aCx, uint32_t aWidth,
+       uint32_t aHeight, JS::Handle<JSObject*> aData);
 
 /*
  * All data members live in private slots on the JS Object. Callers must
  * first check IsImageData, after which they may call the data accessors.
  */
 
 bool
 IsImageData(JSObject* aObj);
--- a/dom/workers/Worker.cpp
+++ b/dom/workers/Worker.cpp
@@ -69,20 +69,20 @@ public:
   {
     return &sClass.mClass;
   }
 
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
             bool aMainRuntime)
   {
-    JSObject* proto =
+    JS::Rooted<JSObject*> proto(aCx,
       js::InitClassWithReserved(aCx, aObj, aParentProto, ProtoClass(),
                                 Construct, 0, sProperties, sFunctions,
-                                NULL, NULL);
+                                NULL, NULL));
     if (!proto) {
       return NULL;
     }
 
     js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
                         JS::PrivateValue(DOMClassStruct()));
 
     if (!aMainRuntime) {
@@ -183,17 +183,17 @@ private:
     const char* name = sEventStrings[JSID_TO_INT(aIdval)];
     WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
     if (!worker) {
       return !JS_IsExceptionPending(aCx);
     }
 
     NS_ConvertASCIItoUTF16 nameStr(name + 2);
     ErrorResult rv;
-    JSObject* listener = worker->GetEventListener(nameStr, rv);
+    JS::Rooted<JSObject*> listener(aCx, worker->GetEventListener(nameStr, rv));
 
     if (rv.Failed()) {
       JS_ReportError(aCx, "Failed to get listener!");
     }
 
     aVp.set(listener ? OBJECT_TO_JSVAL(listener) : JSVAL_NULL);
     return true;
   }
@@ -206,18 +206,18 @@ private:
     JS_ASSERT(JSID_TO_INT(aIdval) >= 0 && JSID_TO_INT(aIdval) < STRING_COUNT);
 
     const char* name = sEventStrings[JSID_TO_INT(aIdval)];
     WorkerPrivate* worker = GetInstancePrivate(aCx, aObj, name);
     if (!worker) {
       return !JS_IsExceptionPending(aCx);
     }
 
-    JSObject* listener;
-    if (!JS_ValueToObject(aCx, aVp, &listener)) {
+    JS::Rooted<JSObject*> listener(aCx);
+    if (!JS_ValueToObject(aCx, aVp, listener.address())) {
       return false;
     }
 
     NS_ConvertASCIItoUTF16 nameStr(name + 2);
     ErrorResult rv;
     worker->SetEventListener(nameStr, listener, rv);
 
     if (rv.Failed()) {
@@ -384,19 +384,19 @@ public:
   {
     return &sClass.mClass;
   }
 
   static JSObject*
   InitClass(JSContext* aCx, JSObject* aObj, JSObject* aParentProto,
             bool aMainRuntime)
   {
-    JSObject* proto =
+    JS::Rooted<JSObject*> proto(aCx,
       js::InitClassWithReserved(aCx, aObj, aParentProto, ProtoClass(),
-                                Construct, 0, NULL, NULL, NULL, NULL);
+                                Construct, 0, NULL, NULL, NULL, NULL));
     if (!proto) {
       return NULL;
     }
 
     js::SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT,
                         JS::PrivateValue(DOMClassStruct()));
 
     if (!aMainRuntime) {
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -199,18 +199,18 @@ struct WorkerStructuredCloneCallbacks
       if (!JS_ReadUint32Pair(aReader, &width, &height) ||
           !JS_ReadTypedArray(aReader, dataArray.address()))
       {
         return nullptr;
       }
       MOZ_ASSERT(dataArray.isObject());
 
       // Construct the ImageData.
-      JSObject* obj = imagedata::Create(aCx, width, height,
-                                        &dataArray.toObject());
+      JS::Rooted<JSObject*> dataObj(aCx, &dataArray.toObject());
+      JSObject* obj = imagedata::Create(aCx, width, height, dataObj);
       return obj;
     }
 
     Error(aCx, 0);
     return nullptr;
   }
 
   static JSBool
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -287,25 +287,25 @@ private:
 
     jsval argv[3] = { JSVAL_VOID, JSVAL_VOID, JSVAL_VOID };
     if (!JS_GetProperty(aCx, event, "message", &argv[0]) ||
         !JS_GetProperty(aCx, event, "filename", &argv[1]) ||
         !JS_GetProperty(aCx, event, "lineno", &argv[2])) {
       return false;
     }
 
-    jsval rval = JSVAL_VOID;
+    JS::Rooted<JS::Value> rval(aCx, JS::UndefinedValue());
     if (!JS_CallFunctionValue(aCx, JSVAL_TO_OBJECT(scope), listener,
-                              ArrayLength(argv), argv, &rval)) {
+                              ArrayLength(argv), argv, rval.address())) {
       JS_ReportPendingException(aCx);
       return false;
     }
 
     if (JSVAL_IS_BOOLEAN(rval) && JSVAL_TO_BOOLEAN(rval) &&
-        !JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL, &rval)) {
+        !JS_CallFunctionName(aCx, event, "preventDefault", 0, NULL, rval.address())) {
       return false;
     }
 
     return true;
   }
 
   static JSBool
   GetOnErrorListener(JSContext* aCx, JSHandleObject aObj, JSHandleId aIdval, JSMutableHandleValue aVp)
--- a/editor/libeditor/text/tests/test_bug527935.html
+++ b/editor/libeditor/text/tests/test_bug527935.html
@@ -32,29 +32,29 @@ SimpleTest.waitForFocus(function() {
     newInput.setAttribute("name", "test");
     document.body.appendChild(newInput);
 
     setTimeout(function() {
       var popupShown = false;
       function listener() {
         popupShown = true;
       }
-      SpecialPowers.addAutoCompletePopupEventListener(window, listener);
+      SpecialPowers.addAutoCompletePopupEventListener(window, "popupshowing", listener);
 
       var event = document.createEvent("KeyboardEvent");
 
       event.initKeyEvent("keypress", true, true, null, false, false,
                          false, false, 0, "f".charCodeAt(0));
       newInput.value = "";
       newInput.focus();
       newInput.dispatchEvent(event);
 
       hitEventLoop(function() {
         ok(!popupShown, "Popup must not be opened");
-        SpecialPowers.removeAutoCompletePopupEventListener(window, listener);
+        SpecialPowers.removeAutoCompletePopupEventListener(window, "popupshowing", listener);
         SimpleTest.finish();
       }, 100);
     }, 0);
   }, false);
 
   initValue.focus();
   initValue.value = "foo";
   synthesizeKey("VK_ENTER", {});
--- a/embedding/tests/unit/xpcshell.ini
+++ b/embedding/tests/unit/xpcshell.ini
@@ -1,10 +1,6 @@
 [DEFAULT]
 head = 
 tail = 
 
 [test_wwauthpromptfactory.js]
-# Bug 676955: test fails consistently on Android
-fail-if = os == "android"
 [test_wwpromptfactory.js]
-# Bug 676955: test fails consistently on Android
-fail-if = os == "android"
--- a/gfx/layers/d3d10/LayerManagerD3D10.cpp
+++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp
@@ -93,17 +93,17 @@ LayerManagerD3D10::~LayerManagerD3D10()
 
       delete attachments;
     }
   }
 
   Destroy();
 }
 
-_inline void
+static inline void
 SetHRESULT(HRESULT* aHresultPtr, HRESULT aHresult)
 {
   if (aHresultPtr) {
     *aHresultPtr = aHresult;
   }
 }
  
 bool
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -1198,16 +1198,17 @@ gfxFT2FontList::FindFontsInDir(const nsC
         "DroidSerif-Italic.ttf",
         "DroidSerif-BoldItalic.ttf",
         "DroidSansMono.ttf",
         "DroidSansArabic.ttf",
         "DroidSansHebrew.ttf",
         "DroidSansThai.ttf",
         "MTLmr3m.ttf",
         "MTLc3m.ttf",
+        "NanumGothic.ttf",
         "DroidSansJapanese.ttf",
         "DroidSansFallback.ttf"
     };
 
     DIR *d = opendir(aDir.get());
     if (!d) {
         return;
     }
--- a/js/src/builtin/ParallelArray.cpp
+++ b/js/src/builtin/ParallelArray.cpp
@@ -108,34 +108,34 @@ ParallelArrayObject::getConstructor(JSCo
     RootedValue ctorValue(cx);
     if (!cx->global()->getIntrinsicValue(cx, ctorName, &ctorValue))
         return NULL;
     JS_ASSERT(ctorValue.isObject() && ctorValue.toObject().isFunction());
     return ctorValue.toObject().toFunction();
 }
 
 /*static*/ JSObject *
-ParallelArrayObject::newInstance(JSContext *cx)
+ParallelArrayObject::newInstance(JSContext *cx, NewObjectKind newKind /* = GenericObject */)
 {
     gc::AllocKind kind = gc::GetGCObjectKind(NumFixedSlots);
-    RootedObject result(cx, NewBuiltinClassInstance(cx, &class_, kind));
+    RootedObject result(cx, NewBuiltinClassInstance(cx, &class_, kind, newKind));
     if (!result)
         return NULL;
 
     // Add in the basic PA properties now with default values:
     if (!initProps(cx, result))
         return NULL;
 
     return result;
 }
 
 /*static*/ JSBool
 ParallelArrayObject::constructHelper(JSContext *cx, MutableHandleFunction ctor, CallArgs &args0)
 {
-    RootedObject result(cx, newInstance(cx));
+    RootedObject result(cx, newInstance(cx, TenuredObject));
     if (!result)
         return false;
 
     if (cx->typeInferenceEnabled()) {
         jsbytecode *pc;
         RootedScript script(cx, cx->stack.currentScript(&pc));
         if (script) {
             if (ctor->nonLazyScript()->shouldCloneAtCallsite) {
--- a/js/src/builtin/ParallelArray.h
+++ b/js/src/builtin/ParallelArray.h
@@ -38,17 +38,17 @@ class ParallelArrayObject : public JSObj
     //
     // NOTE: This object will NOT have the correct type object! It is
     // up to the caller to adjust the type object appropriately
     // before releasing the object into the wild.  You probably want
     // to be calling construct() above, which will adjust the type
     // object for you, since ParallelArray type objects must be setup
     // in a rather particular way to interact well with the
     // self-hosted code.  See constructHelper() for details.
-    static JSObject *newInstance(JSContext *cx);
+    static JSObject *newInstance(JSContext *cx, NewObjectKind newKind = GenericObject);
 
     // Get the constructor function for argc number of arguments.
     static JSFunction *getConstructor(JSContext *cx, unsigned argc);
 
     static JSObject *initClass(JSContext *cx, HandleObject obj);
     static bool is(const Value &v);
 };
 
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -2180,17 +2180,17 @@ bool CanConvertTypedArrayItemTo(JSObject
 //    into it.
 // 2) We are converting an argument for an ffi call, in which case 'isArgument'
 //    will be true. This allows us to handle a special case: if necessary,
 //    we can autoconvert a JS string primitive to a pointer-to-character type.
 //    In this case, ownership of the allocated string is handed off to the
 //    caller; 'freePointer' will be set to indicate this.
 JSBool
 ImplicitConvert(JSContext* cx,
-                jsval val,
+                HandleValue val,
                 JSObject* targetType_,
                 void* buffer,
                 bool isArgument,
                 bool* freePointer)
 {
   RootedObject targetType(cx, targetType_);
   JS_ASSERT(CType::IsSizeDefined(targetType));
 
@@ -4787,26 +4787,22 @@ StructType::DefineInternal(JSContext* cx
       JSFlatString* flat = ExtractStructField(cx, item, fieldType.address());
       if (!flat)
         return JS_FALSE;
       Rooted<JSStableString*> name(cx, flat->ensureStable(cx));
       if (!name)
         return JS_FALSE;
       fieldRootsArray[i] = OBJECT_TO_JSVAL(fieldType);
 
-      // Make sure each field name is unique, and add it to the hash.
+      // Make sure each field name is unique
       FieldInfoHash::AddPtr entryPtr = fields->lookupForAdd(name);
       if (entryPtr) {
         JS_ReportError(cx, "struct fields must have unique names");
         return JS_FALSE;
       }
-      ASSERT_OK(fields->add(entryPtr, name, FieldInfo()));
-      FieldInfo& info = entryPtr->value;
-      info.mType = fieldType;
-      info.mIndex = i;
 
       // Add the field to the StructType's 'prototype' property.
       if (!JS_DefineUCProperty(cx, prototype,
              name->chars().get(), name->length(), JSVAL_VOID,
              StructType::FieldGetter, StructType::FieldSetter,
              JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT))
         return JS_FALSE;
 
@@ -4815,17 +4811,24 @@ StructType::DefineInternal(JSContext* cx
       size_t fieldOffset = Align(structSize, fieldAlign);
       // Check for overflow. Since we hold invariant that fieldSize % fieldAlign
       // be zero, we can safely check fieldOffset + fieldSize without first
       // checking fieldOffset for overflow.
       if (fieldOffset + fieldSize < structSize) {
         JS_ReportError(cx, "size overflow");
         return JS_FALSE;
       }
+
+      // Add field name to the hash
+      FieldInfo info;
+      info.mType = fieldType;
+      info.mIndex = i;
       info.mOffset = fieldOffset;
+      ASSERT_OK(fields->add(entryPtr, name, info));
+
       structSize = fieldOffset + fieldSize;
 
       if (fieldAlign > structAlign)
         structAlign = fieldAlign;
     }
 
     // Pad the struct tail according to struct alignment.
     size_t structTail = Align(structSize, structAlign);
@@ -5027,17 +5030,17 @@ StructType::ConstructData(JSContext* cx,
   }
 
   // We have a type constructor of the form 'ctypes.StructType(a, b, c, ...)'.
   // ImplicitConvert each field.
   if (args.length() == fields->count()) {
     for (FieldInfoHash::Range r = fields->all(); !r.empty(); r.popFront()) {
       const FieldInfo& field = r.front().value;
       STATIC_ASSUME(field.mIndex < fields->count());  /* Quantified invariant */
-      if (!ImplicitConvert(cx, args[field.mIndex], field.mType,
+      if (!ImplicitConvert(cx, args.handleAt(field.mIndex), field.mType,
              buffer + field.mOffset,
              false, NULL))
         return JS_FALSE;
     }
 
     return JS_TRUE;
   }
 
@@ -5665,17 +5668,17 @@ FunctionType::ConstructData(JSContext* c
   // could be called on a frozen object.
   return JS_FreezeObject(cx, dataObj);
 }
 
 typedef Array<AutoValue, 16> AutoValueAutoArray;
 
 static JSBool
 ConvertArgument(JSContext* cx,
-                jsval arg,
+                HandleValue arg,
                 JSObject* type,
                 AutoValue* value,
                 AutoValueAutoArray* strings)
 {
   if (!value->SizeToType(cx, type)) {
     JS_ReportAllocationOverflow(cx);
     return false;
   }
@@ -5745,17 +5748,17 @@ FunctionType::Call(JSContext* cx,
   AutoValueAutoArray values;
   AutoValueAutoArray strings;
   if (!values.resize(args.length())) {
     JS_ReportOutOfMemory(cx);
     return false;
   }
 
   for (unsigned i = 0; i < argcFixed; ++i)
-    if (!ConvertArgument(cx, args[i], fninfo->mArgTypes[i], &values[i], &strings))
+    if (!ConvertArgument(cx, args.handleAt(i), fninfo->mArgTypes[i], &values[i], &strings))
       return false;
 
   if (fninfo->mIsVariadic) {
     if (!fninfo->mFFITypes.resize(args.length())) {
       JS_ReportOutOfMemory(cx);
       return false;
     }
 
@@ -5770,17 +5773,17 @@ FunctionType::Call(JSContext* cx,
         JS_ReportError(cx, "argument %d of type %s is not a CData object",
                        i, JS_GetTypeName(cx, JS_TypeOfValue(cx, args[i])));
         return false;
       }
       if (!(type = CData::GetCType(obj)) ||
           !(type = PrepareType(cx, OBJECT_TO_JSVAL(type))) ||
           // Relying on ImplicitConvert only for the limited purpose of
           // converting one CType to another (e.g., T[] to T*).
-          !ConvertArgument(cx, args[i], type, &values[i], &strings) ||
+          !ConvertArgument(cx, args.handleAt(i), type, &values[i], &strings) ||
           !(fninfo->mFFITypes[i] = CType::GetFFIType(cx, type))) {
         // These functions report their own errors.
         return false;
       }
     }
     if (!PrepareCIF(cx, fninfo))
       return false;
   }
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -311,17 +311,17 @@ bool IsCTypesGlobal(JSObject* obj);
 
 JSCTypesCallbacks* GetCallbacks(JSObject* obj);
 
 JSBool InitTypeClasses(JSContext* cx, JSHandleObject parent);
 
 JSBool ConvertToJS(JSContext* cx, JSHandleObject typeObj, JSHandleObject dataObj,
   void* data, bool wantPrimitive, bool ownResult, jsval* result);
 
-JSBool ImplicitConvert(JSContext* cx, jsval val, JSObject* targetType,
+JSBool ImplicitConvert(JSContext* cx, JSHandleValue val, JSObject* targetType,
   void* buffer, bool isArgument, bool* freePointer);
 
 JSBool ExplicitConvert(JSContext* cx, JSHandleValue val, JSHandleObject targetType,
   void* buffer);
 
 /*******************************************************************************
 ** JSClass reserved slot definitions
 *******************************************************************************/
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -176,45 +176,49 @@ class MinorCollectionTracer : public JST
      * This list is threaded through the Nursery using the space from already
      * moved things. The list is used to fix up the moved things and to find
      * things held live by intra-Nursery pointers.
      */
     RelocationOverlay *head;
     RelocationOverlay **tail;
 
     /* Save and restore all of the runtime state we use during MinorGC. */
-    bool priorNeedsBarrier;
+    bool savedNeedsBarrier;
+    AutoDisableProxyCheck disableStrictProxyChecking;
 
     /* Insert the given relocation entry into the list of things to visit. */
     JS_ALWAYS_INLINE void insertIntoFixupList(RelocationOverlay *entry) {
         *tail = entry;
         tail = &entry->next_;
         *tail = NULL;
     }
 
     MinorCollectionTracer(JSRuntime *rt, Nursery *nursery)
       : JSTracer(),
         nursery(nursery),
         runtime(rt),
         session(runtime, MinorCollecting),
         head(NULL),
         tail(&head),
-        priorNeedsBarrier(runtime->needsBarrier())
+        savedNeedsBarrier(runtime->needsBarrier()),
+        disableStrictProxyChecking(runtime)
     {
         JS_TracerInit(this, runtime, Nursery::MinorGCCallback);
         eagerlyTraceWeakMaps = TraceWeakMapKeysValues;
 
         runtime->gcNumber++;
         runtime->setNeedsBarrier(false);
-        ++runtime->gcDisableStrictProxyCheckingCount;
+        for (ZonesIter zone(rt); !zone.done(); zone.next())
+            zone->saveNeedsBarrier(false);
     }
 
     ~MinorCollectionTracer() {
-        --runtime->gcDisableStrictProxyCheckingCount;
-        runtime->setNeedsBarrier(priorNeedsBarrier);
+        runtime->setNeedsBarrier(savedNeedsBarrier);
+        for (ZonesIter zone(runtime); !zone.done(); zone.next())
+            zone->restoreNeedsBarrier();
     }
 };
 
 } /* namespace gc */
 } /* namespace js */
 
 static AllocKind
 GetObjectAllocKindForCopy(JSObject *obj)
@@ -237,16 +241,24 @@ GetObjectAllocKindForCopy(JSObject *obj)
 void *
 js::Nursery::allocateFromTenured(Zone *zone, AllocKind thingKind)
 {
     void *t = zone->allocator.arenas.allocateFromFreeList(thingKind, Arena::thingSize(thingKind));
     if (!t) {
         zone->allocator.arenas.checkEmptyFreeList(thingKind);
         t = zone->allocator.arenas.allocateFromArena(zone, thingKind);
     }
+
+    /*
+     * Pre barriers are disabled during minor collection, however, we still
+     * want objects to be allocated black if an incremental GC is in progress.
+     */
+    if (zone->savedNeedsBarrier())
+        static_cast<Cell *>(t)->markIfUnmarked();
+
     return t;
 }
 
 void *
 js::Nursery::moveToTenured(MinorCollectionTracer *trc, JSObject *src)
 {
     Zone *zone = src->zone();
     AllocKind dstKind = GetObjectAllocKindForCopy(src);
--- a/js/src/gc/Zone.h
+++ b/js/src/gc/Zone.h
@@ -108,19 +108,40 @@ struct Zone : private JS::shadow::Zone, 
     js::Allocator                allocator;
 
     js::CompartmentVector        compartments;
 
     bool                         hold;
 
   private:
     bool                         ionUsingBarriers_;
+
+    /*
+     * This flag saves the value of needsBarrier_ during minor collection,
+     * since needsBarrier_ is always set to false during minor collection.
+     * Outside of minor collection, the value of savedNeedsBarrier_ is
+     * undefined.
+     */
+    bool                         savedNeedsBarrier_;
+
   public:
+    bool                         active;  // GC flag, whether there are active frames
 
-    bool                         active;  // GC flag, whether there are active frames
+    void saveNeedsBarrier(bool newNeeds) {
+        savedNeedsBarrier_ = needsBarrier_;
+        needsBarrier_ = newNeeds;
+    }
+
+    void restoreNeedsBarrier() {
+        needsBarrier_ = savedNeedsBarrier_;
+    }
+
+    bool savedNeedsBarrier() const {
+        return savedNeedsBarrier_;
+    }
 
     bool needsBarrier() const {
         return needsBarrier_;
     }
 
     bool compileBarriers(bool needsBarrier) const {
         return needsBarrier || rt->gcZeal() == js::gc::ZealVerifierPreValue;
     }
--- a/js/src/ion/AsmJS.cpp
+++ b/js/src/ion/AsmJS.cpp
@@ -4879,16 +4879,20 @@ AssertStackAlignment(MacroAssembler &mas
     Label ok;
     JS_ASSERT(IsPowerOfTwo(StackAlignment));
     masm.branchTestPtr(Assembler::Zero, StackPointer, Imm32(StackAlignment - 1), &ok);
     masm.breakpoint();
     masm.bind(&ok);
 #endif
 }
 
+static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * STACK_SLOT_SIZE +
+                                             NonVolatileRegs.fpus().size() * sizeof(double);
+
+#ifndef JS_CPU_ARM
 static unsigned
 StackArgBytes(const MIRTypeVector &argTypes)
 {
     ABIArgIter iter(argTypes);
     while (!iter.done())
         iter++;
     return iter.stackBytesConsumedSoFar();
 }
@@ -4898,19 +4902,16 @@ StackDecrementForCall(MacroAssembler &ma
 {
     // Include extra padding so that, after pushing the arguments and
     // extraBytes, the stack is aligned for a call instruction.
     unsigned argBytes = StackArgBytes(argTypes);
     unsigned alreadyPushed = AlignmentAtPrologue + masm.framePushed();
     return AlignBytes(alreadyPushed + extraBytes + argBytes, StackAlignment) - alreadyPushed;
 }
 
-static const unsigned FramePushedAfterSave = NonVolatileRegs.gprs().size() * STACK_SLOT_SIZE +
-                                             NonVolatileRegs.fpus().size() * sizeof(double);
-#ifndef JS_CPU_ARM
 static bool
 GenerateEntry(ModuleCompiler &m, const AsmJSModule::ExportedFunction &exportedFunc)
 {
     MacroAssembler &masm = m.masm();
 
     // In constrast to the system ABI, the Ion convention is that all registers
     // are clobbered by calls. Thus, we must save the caller's non-volatile
     // registers.
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -5500,17 +5500,18 @@ CodeGenerator::visitSetPropertyCacheV(LS
 }
 
 bool
 CodeGenerator::visitSetPropertyCacheT(LSetPropertyCacheT *ins)
 {
     RegisterSet liveRegs = ins->safepoint()->liveRegs();
     Register objReg = ToRegister(ins->getOperand(0));
     ConstantOrRegister value;
-    bool isSetName = JSOp(*ins->mir()->resumePoint()->pc()) == JSOP_SETNAME;
+    jsbytecode *pc = ins->mir()->resumePoint()->pc();
+    bool isSetName = JSOp(*pc) == JSOP_SETNAME || JSOp(*pc) == JSOP_SETGNAME;
 
     if (ins->getOperand(1)->isConstant())
         value = ConstantOrRegister(*ins->getOperand(1)->toConstant());
     else
         value = TypedOrValueRegister(ins->valueType(), ToAnyRegister(ins->getOperand(1)));
 
     SetPropertyIC cache(liveRegs, objReg, ins->mir()->name(), value,
                         isSetName, ins->mir()->strict());
--- a/js/src/ion/IonBuilder.cpp
+++ b/js/src/ion/IonBuilder.cpp
@@ -232,45 +232,49 @@ IonBuilder::canInlineTarget(JSFunction *
     if (target->getParent() != &script()->global()) {
         IonSpew(IonSpew_Inlining, "Cannot inline due to scope mismatch");
         return false;
     }
 
     RootedScript inlineScript(cx, target->nonLazyScript());
     ExecutionMode executionMode = info().executionMode();
     if (!CanIonCompile(inlineScript, executionMode)) {
-        IonSpew(IonSpew_Inlining, "Cannot inline due to disable Ion compilation");
+        IonSpew(IonSpew_Inlining, "%s:%d Cannot inline due to disable Ion compilation",
+                                  inlineScript->filename(), inlineScript->lineno);
         return false;
     }
 
     // Don't inline functions which don't have baseline scripts compiled for them.
     if (executionMode == SequentialExecution &&
         ion::IsBaselineEnabled(cx) &&
         !inlineScript->hasBaselineScript())
     {
-        IonSpew(IonSpew_Inlining, "Cannot inline target with no baseline jitcode");
+        IonSpew(IonSpew_Inlining, "%s:%d Cannot inline target with no baseline jitcode",
+                                  inlineScript->filename(), inlineScript->lineno);
         return false;
     }
 
     // Allow inlining of recursive calls, but only one level deep.
     IonBuilder *builder = callerBuilder_;
     while (builder) {
         if (builder->script() == inlineScript) {
-            IonSpew(IonSpew_Inlining, "Not inlining recursive call");
+            IonSpew(IonSpew_Inlining, "%s:%d Not inlining recursive call",
+                                       inlineScript->filename(), inlineScript->lineno);
             return false;
         }
         builder = builder->callerBuilder_;
     }
 
     if (!canEnterInlinedFunction(target)) {
-        IonSpew(IonSpew_Inlining, "Cannot inline due to analysis data %d", script()->lineno);
+        IonSpew(IonSpew_Inlining, "%s:%d Cannot inline due to oracle veto %d",
+                                  inlineScript->filename(), inlineScript->lineno,
+                                  script()->lineno);
         return false;
     }
 
-    IonSpew(IonSpew_Inlining, "Inlining good to go!");
     return true;
 }
 
 void
 IonBuilder::popCfgStack()
 {
     if (cfgStack_.back().isLoop())
         loops_.popBack();
@@ -3424,16 +3428,22 @@ IonBuilder::inlineScriptedCall(CallInfo 
         returnBlock->add(MFunctionBoundary::New(NULL, MFunctionBoundary::Inline_Exit));
 
     // Inherit the slots from current and pop |fun|.
     returnBlock->inheritSlots(current);
     returnBlock->pop();
 
     // Accumulate return values.
     MIRGraphExits &exits = *inlineBuilder.graph().exitAccumulator();
+    if (exits.length() == 0) {
+        // Inlining of functions that have no exit is not supported.
+        calleeScript->analysis()->setIonUninlineable();
+        abortReason_ = AbortReason_Inlining;
+        return false;
+    }
     MDefinition *retvalDefn = patchInlinedReturns(callInfo, exits, returnBlock);
     if (!retvalDefn)
         return false;
     returnBlock->push(retvalDefn);
 
     // Initialize entry slots now that the stack has been fixed up.
     if (!returnBlock->initEntrySlots())
         return false;
@@ -3532,22 +3542,24 @@ IonBuilder::makeInliningDecision(JSFunct
             return false;
         }
     } else {
         if (inliningDepth_ >= js_IonOptions.maxInlineDepth) {
             IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: exceeding allowed inline depth",
                                       targetScript->filename(), targetScript->lineno);
             return false;
         }
+
+        if (targetScript->analysis()->hasLoops()) {
+            IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: big function that contains a loop",
+                                      targetScript->filename(), targetScript->lineno);
+            return false;
+        }
      }
 
-    // Always inline the empty script up to the inlining depth.
-    if (targetScript->length == 1)
-        return true;
-
     // Callee must not be excessively large.
     // This heuristic also applies to the callsite as a whole.
     if (targetScript->length > js_IonOptions.inlineMaxTotalBytecodeLength) {
         IonSpew(IonSpew_Inlining, "%s:%d - Vetoed: callee excessively large.",
                                   targetScript->filename(), targetScript->lineno);
         return false;
     }
 
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -365,17 +365,17 @@ DispatchIonCache::updateBaseAddress(IonC
     Assembler::patchDataWithValueCheck(CodeLocationLabel(code, dispatchLabel_),
                                        ImmWord(uintptr_t(&firstStub_)),
                                        ImmWord(uintptr_t(-1)));
     firstStub_ = fallbackLabel_.raw();
     rejoinLabel_.repoint(code, &masm);
 }
 
 void
-IonCache::attachStub(MacroAssembler &masm, StubAttacher &attacher, IonCode *code)
+IonCache::attachStub(MacroAssembler &masm, StubAttacher &attacher, Handle<IonCode *> code)
 {
     JS_ASSERT(canAttachStub());
     incrementStubCount();
 
     // Update the success path to continue after the IC initial jump.
     attacher.patchRejoinJump(masm, code);
 
     // Update the failure path.
@@ -386,18 +386,18 @@ IonCache::attachStub(MacroAssembler &mas
     // MarkIonExitFrame).
     attacher.patchStubCodePointer(masm, code);
 }
 
 bool
 IonCache::linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &attacher,
                             IonScript *ion, const char *attachKind)
 {
-    IonCode *code = NULL;
-    LinkStatus status = linkCode(cx, masm, ion, &code);
+    Rooted<IonCode *> code(cx);
+    LinkStatus status = linkCode(cx, masm, ion, code.address());
     if (status != LINK_GOOD)
         return status != LINK_ERROR;
 
     attachStub(masm, attacher, code);
 
     if (pc) {
         IonSpew(IonSpew_InlineCaches, "Cache %p(%s:%d/%d) generated %s %s stub at %p",
                 this, script->filename(), script->lineno, pc - script->code,
--- a/js/src/ion/IonCaches.h
+++ b/js/src/ion/IonCaches.h
@@ -223,17 +223,17 @@ class IonCache
 
     // Use the Linker to link the generated code and check if any
     // monitoring/allocation caused an invalidation of the running ion script,
     // this function returns CACHE_FLUSHED. In case of allocation issue this
     // function returns LINK_ERROR.
     LinkStatus linkCode(JSContext *cx, MacroAssembler &masm, IonScript *ion, IonCode **code);
     // Fixup variables and update jumps in the list of stubs.  Increment the
     // number of attached stubs accordingly.
-    void attachStub(MacroAssembler &masm, StubAttacher &attacher, IonCode *code);
+    void attachStub(MacroAssembler &masm, StubAttacher &attacher, Handle<IonCode *> code);
 
     // Combine both linkStub and attachStub into one function. In addition, it
     // produces a spew augmented with the attachKind string.
     bool linkAndAttachStub(JSContext *cx, MacroAssembler &masm, StubAttacher &attacher,
                            IonScript *ion, const char *attachKind);
 
     bool isAllocated() {
         return fallbackLabel_.isSet();
--- a/js/src/ion/MCallOptimize.cpp
+++ b/js/src/ion/MCallOptimize.cpp
@@ -1175,17 +1175,17 @@ IonBuilder::inlineParallelArrayTail(Call
     // Place an MPrepareCall before the first passed argument, before we
     // potentially perform rearrangement.
     MPrepareCall *start = new MPrepareCall;
     oldThis->block()->insertBefore(oldThis, start);
     call->initPrepareCall(start);
 
     // Create the MIR to allocate the new parallel array.  Take the type
     // object is taken from the prediction set.
-    RootedObject templateObject(cx, ParallelArrayObject::newInstance(cx));
+    RootedObject templateObject(cx, ParallelArrayObject::newInstance(cx, TenuredObject));
     if (!templateObject)
         return InliningStatus_Error;
     templateObject->setType(typeObject);
     MNewParallelArray *newObject = MNewParallelArray::New(templateObject);
     current->add(newObject);
     MPassArg *newThis = MPassArg::New(newObject);
     current->add(newThis);
     call->addArg(0, newThis);
@@ -1236,17 +1236,17 @@ IonBuilder::inlineNewDenseArrayForParall
     // already have one of these type objects.
     types::StackTypeSet *returnTypes = getInlineReturnTypeSet();
     if (returnTypes->getKnownTypeTag() != JSVAL_TYPE_OBJECT)
         return InliningStatus_NotInlined;
     if (returnTypes->unknownObject() || returnTypes->getObjectCount() != 1)
         return InliningStatus_NotInlined;
     types::TypeObject *typeObject = returnTypes->getTypeObject(0);
 
-    RootedObject templateObject(cx, NewDenseAllocatedArray(cx, 0));
+    RootedObject templateObject(cx, NewDenseAllocatedArray(cx, 0, NULL, TenuredObject));
     if (!templateObject)
         return InliningStatus_Error;
     templateObject->setType(typeObject);
 
     callInfo.unwrapArgs();
 
     MParNewDenseArray *newObject = new MParNewDenseArray(graph().parSlice(),
                                                          callInfo.getArg(0),
--- a/js/src/ion/MIRGraph.cpp
+++ b/js/src/ion/MIRGraph.cpp
@@ -71,16 +71,26 @@ MIRGraph::removeBlocksAfter(MBasicBlock 
         MBasicBlock *block = *iter;
         iter++;
 
         if (block->id() <= start->id())
             continue;
 
         if (block == osrBlock_)
             osrBlock_ = NULL;
+
+        if (exitAccumulator_) {
+            size_t i = 0;
+            while (i < exitAccumulator_->length()) {
+                if ((*exitAccumulator_)[i] == block)
+                    exitAccumulator_->erase(exitAccumulator_->begin() + i);
+                else
+                    i++;
+            }
+        }
         block->discardAllInstructions();
         block->discardAllPhis();
         block->discardAllResumePoints();
         block->markAsDead();
         removeBlock(block);
     }
 }
 
--- a/js/src/ion/ParallelFunctions.cpp
+++ b/js/src/ion/ParallelFunctions.cpp
@@ -39,18 +39,18 @@ ion::ParNewGCThing(gc::AllocKind allocKi
 }
 
 // Check that the object was created by the current thread
 // (and hence is writable).
 bool
 ion::ParWriteGuard(ForkJoinSlice *slice, JSObject *object)
 {
     JS_ASSERT(ForkJoinSlice::Current() == slice);
-    return slice->allocator->arenas.containsArena(slice->runtime(),
-                                                  object->arenaHeader());
+    return !IsInsideNursery(object->runtime(), object) &&
+           slice->allocator->arenas.containsArena(slice->runtime(), object->arenaHeader());
 }
 
 #ifdef DEBUG
 static void
 printTrace(const char *prefix, struct IonLIRTraceData *cached)
 {
     fprintf(stderr, "%s / Block %3u / LIR %3u / Mode %u / LIR %s\n",
             prefix,
--- a/js/src/ion/VMFunctions.cpp
+++ b/js/src/ion/VMFunctions.cpp
@@ -260,17 +260,17 @@ IteratorMore(JSContext *cx, HandleObject
 }
 
 JSObject *
 NewInitParallelArray(JSContext *cx, HandleObject templateObject)
 {
     JS_ASSERT(templateObject->getClass() == &ParallelArrayObject::class_);
     JS_ASSERT(!templateObject->hasSingletonType());
 
-    RootedObject obj(cx, ParallelArrayObject::newInstance(cx));
+    RootedObject obj(cx, ParallelArrayObject::newInstance(cx, TenuredObject));
     if (!obj)
         return NULL;
 
     obj->setType(templateObject->type());
 
     return obj;
 }
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug870328.js
@@ -0,0 +1,7 @@
+var g = newGlobal("same-compartment");
+try {
+    evalcx("'use strict'; (function() { x = 33; })()", g);
+    assertEq(0, 1);
+} catch(e) {
+    assertEq(e.toString().contains("variable x"), true);
+}
--- a/js/src/jsanalyze.cpp
+++ b/js/src/jsanalyze.cpp
@@ -60,17 +60,17 @@ ScriptAnalysis::addJump(JSContext *cx, u
         code->stackDepth = stackDepth;
     }
     JS_ASSERT(code->stackDepth == stackDepth);
 
     code->jumpTarget = true;
 
     if (offset < *currentOffset) {
         /* Scripts containing loops are never inlined. */
-        isJaegerInlineable = isIonInlineable = false;
+        isJaegerInlineable = false;
         hasLoops_ = true;
 
         if (code->analyzed) {
             /*
              * Backedge in a do-while loop, the body has been analyzed. Rewalk
              * the body to set inLoop bits.
              */
             for (unsigned i = offset; i <= *currentOffset; i++) {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -5084,14 +5084,16 @@ AutoSuppressGC::AutoSuppressGC(JSContext
 
 AutoSuppressGC::AutoSuppressGC(JSCompartment *comp)
   : suppressGC_(comp->rt->mainThread.suppressGC)
 {
     suppressGC_++;
 }
 
 #ifdef DEBUG
-AutoDisableProxyCheck::AutoDisableProxyCheck(JSRuntime *rt)
+AutoDisableProxyCheck::AutoDisableProxyCheck(JSRuntime *rt
+                                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM_IN_IMPL)
   : count(rt->gcDisableStrictProxyCheckingCount)
 {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     count++;
 }
 #endif
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1282,21 +1282,24 @@ class AutoSuppressGC
         suppressGC_--;
     }
 };
 
 } /* namespace gc */
 
 #ifdef DEBUG
 /* Use this to avoid assertions when manipulating the wrapper map. */
-struct AutoDisableProxyCheck
+class AutoDisableProxyCheck
 {
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
     uintptr_t &count;
 
-    AutoDisableProxyCheck(JSRuntime *rt);
+  public:
+    AutoDisableProxyCheck(JSRuntime *rt
+                          MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     ~AutoDisableProxyCheck() {
         count--;
     }
 };
 #else
 struct AutoDisableProxyCheck
 {
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -4500,17 +4500,17 @@ DebuggerObject_defineProperties(JSContex
     {
         AutoIdVector rewrappedIds(cx);
         AutoPropDescArrayRooter rewrappedDescs(cx);
 
         Maybe<AutoCompartment> ac;
         ac.construct(cx, obj);
         RootedId id(cx);
         for (size_t i = 0; i < n; i++) {
-            if (!rewrappedIds.append(jsid()) || !rewrappedDescs.append())
+            if (!rewrappedIds.append(JSID_VOID) || !rewrappedDescs.append())
                 return false;
             id = ids[i];
             if (!unwrappedDescs[i].wrapInto(cx, obj, id, &rewrappedIds[i], &rewrappedDescs[i]))
                 return false;
         }
 
         ErrorCopier ec(ac, dbg->toJSObject());
         for (size_t i = 0; i < n; i++) {
--- a/js/src/vm/ForkJoin.cpp
+++ b/js/src/vm/ForkJoin.cpp
@@ -180,17 +180,17 @@ class ParallelDo
     RootedScript bailoutScript;
     jsbytecode *bailoutBytecode;
 
     ParallelDo(JSContext *cx, HandleObject fun);
     ExecutionStatus apply();
 
   private:
     JSContext *cx_;
-    HeapPtrObject fun_;
+    HandleObject fun_;
     Vector<ParallelBailoutRecord, 16> bailoutRecords;
 
     inline bool executeSequentially();
 
     MethodStatus compileForParallelExecution();
     ExecutionStatus disqualifyFromParallelExecution();
     void determineBailoutCause();
     bool invalidateBailedOutScripts();
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -1718,16 +1718,17 @@ DebugScopes::addDebugScope(JSContext *cx
         return false;
     }
 
     JS_ASSERT(!scopes->liveScopes.has(&debugScope.scope()));
     if (!scopes->liveScopes.put(&debugScope.scope(), si.frame())) {
         js_ReportOutOfMemory(cx);
         return false;
     }
+    HashTableWriteBarrierPost(cx->runtime, &scopes->liveScopes, &debugScope.scope());
 
     return true;
 }
 
 void
 DebugScopes::onPopCall(AbstractFramePtr frame, JSContext *cx)
 {
     JS_ASSERT(!frame.isYielding());
@@ -1867,20 +1868,22 @@ DebugScopes::onGeneratorFrameChange(Abst
              * Not only must we correctly replace mappings [scope -> from] with
              * mappings [scope -> to], but we must add [scope -> to] if it
              * doesn't already exist so that if we need to proxy a generator's
              * scope while it is suspended, we can find its frame (which would
              * otherwise not be found by AllFramesIter).
              */
             JS_ASSERT(toIter.scope().compartment() == cx->compartment);
             LiveScopeMap::AddPtr livePtr = scopes->liveScopes.lookupForAdd(&toIter.scope());
-            if (livePtr)
+            if (livePtr) {
                 livePtr->value = to;
-            else
+            } else {
                 scopes->liveScopes.add(livePtr, &toIter.scope(), to);  // OOM here?
+                HashTableWriteBarrierPost(cx->runtime, &scopes->liveScopes, &toIter.scope());
+            }
         } else {
             ScopeIter si(toIter, from, cx);
             JS_ASSERT(si.frame().scopeChain()->compartment() == cx->compartment);
             if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
                 DebugScopeObject &debugScope = *p->value;
                 scopes->liveScopes.lookup(&debugScope.scope())->value = to;
                 scopes->missingScopes.remove(p);
                 scopes->missingScopes.put(toIter, &debugScope);  // OOM here?
@@ -1931,16 +1934,17 @@ DebugScopes::updateLiveScopes(JSContext 
         for (ScopeIter si(frame, cx); !si.done(); ++si) {
             if (si.hasScopeObject()) {
                 JS_ASSERT(si.scope().compartment() == cx->compartment);
                 DebugScopes *scopes = ensureCompartmentData(cx);
                 if (!scopes)
                     return false;
                 if (!scopes->liveScopes.put(&si.scope(), frame))
                     return false;
+                HashTableWriteBarrierPost(cx->runtime, &scopes->liveScopes, &si.scope());
             }
         }
 
         if (frame.prevUpToDate())
             return true;
         JS_ASSERT(frame.scopeChain()->compartment()->debugMode());
         frame.setPrevUpToDate();
     }
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -4573,16 +4573,18 @@ nsCSSFrameConstructor::FindMathMLData(El
                   FCDATA_IS_LINE_PARTICIPANT |
                   FCDATA_WRAP_KIDS_IN_BLOCKS,
                   NS_NewMathMLmathInlineFrame);
     return &sInlineMathData;
   }
       
 
   static const FrameConstructionDataByTag sMathMLData[] = {
+    SIMPLE_MATHML_CREATE(annotation_, NS_NewMathMLTokenFrame),
+    SIMPLE_MATHML_CREATE(annotation_xml_, NS_NewMathMLmrowFrame),
     SIMPLE_MATHML_CREATE(mi_, NS_NewMathMLTokenFrame),
     SIMPLE_MATHML_CREATE(mn_, NS_NewMathMLTokenFrame),
     SIMPLE_MATHML_CREATE(ms_, NS_NewMathMLTokenFrame),
     SIMPLE_MATHML_CREATE(mtext_, NS_NewMathMLTokenFrame),
     SIMPLE_MATHML_CREATE(mo_, NS_NewMathMLmoFrame),
     SIMPLE_MATHML_CREATE(mfrac_, NS_NewMathMLmfracFrame),
     SIMPLE_MATHML_CREATE(msup_, NS_NewMathMLmsupFrame),
     SIMPLE_MATHML_CREATE(msub_, NS_NewMathMLmsubFrame),
--- a/layout/base/nsFrameManager.cpp
+++ b/layout/base/nsFrameManager.cpp
@@ -1187,19 +1187,16 @@ nsFrameManager::ReResolveStyleContext(ns
         ->GetPseudo() == oldContext->GetPseudo() &&
        prevContinuationContext->GetParent() == parentContext;
     if (copyFromContinuation) {
       // Just use the style context from the frame's previous
       // continuation (see assertion about aFrame->GetNextContinuation()
       // above, which we would have previously hit for aFrame's previous
       // continuation).
       newContext = prevContinuationContext;
-      // We don't know what changes the previous continuation had, so
-      // assume the worst.
-      nonInheritedHints = nsChangeHint_Hints_NotHandledForDescendants;
     }
     else if (pseudoTag == nsCSSAnonBoxes::mozNonElement) {
       NS_ASSERTION(localContent,
                    "non pseudo-element frame without content node");
       newContext = styleSet->ResolveStyleForNonElement(parentContext);
     }
     else if (!aRestyleHint && !prevContinuation) {
       // Unfortunately, if prevContinuation is non-null then we may have
@@ -1643,18 +1640,16 @@ nsFrameManager::ComputeStyleChangeFor(ns
 {
   PROFILER_LABEL("CSS", "ComputeStyleChangeFor");
 
   nsIContent *content = aFrame->GetContent();
   if (aMinChange) {
     aChangeList->AppendChange(aFrame, content, aMinChange);
   }
 
-  nsChangeHint topLevelChange = aMinChange;
-
   nsIFrame* frame = aFrame;
   nsIFrame* frame2 = aFrame;
 
   NS_ASSERTION(!frame->GetPrevContinuation(), "must start with the first in flow");
 
   // We want to start with this frame and walk all its next-in-flows,
   // as well as all its special siblings and their next-in-flows,
   // reresolving style on all the frames we encounter in this walk.
@@ -1670,26 +1665,25 @@ nsFrameManager::ComputeStyleChangeFor(ns
   treeMatchContext.InitAncestors(parentElement);
   nsTArray<nsIContent*> visibleKidsOfHiddenElement;
   do {
     // Outer loop over special siblings
     do {
       // Inner loop over next-in-flows of the current frame
       nsChangeHint frameChange =
         ReResolveStyleContext(GetPresContext(), frame, nullptr,
-                              aChangeList, topLevelChange, nsChangeHint(0),
+                              aChangeList, aMinChange, nsChangeHint(0),
                               aRestyleDescendants ?
                                 eRestyle_Subtree : eRestyle_Self,
                               aRestyleTracker,
                               eSendAllNotifications,
                               visibleKidsOfHiddenElement,
                               treeMatchContext);
-      NS_UpdateHint(topLevelChange, frameChange);
 
-      if (topLevelChange & nsChangeHint_ReconstructFrame) {
+      if (frameChange & nsChangeHint_ReconstructFrame) {
         // If it's going to cause a framechange, then don't bother
         // with the continuations or special siblings since they'll be
         // clobbered by the frame reconstruct anyway.
         NS_ASSERTION(!frame->GetPrevContinuation(),
                      "continuing frame had more severe impact than first-in-flow");
         return;
       }
 
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -116,20 +116,20 @@ typedef struct CapturingContentInfo {
   // capture should only be allowed during a mousedown event
   bool mAllowed;
   bool mPointerLock;
   bool mRetargetToElement;
   bool mPreventDrag;
   nsIContent* mContent;
 } CapturingContentInfo;
 
-// bf539e0a-c314-4ea7-ba7c-ebd34e8a4065
+// fac033dd-938d-45bc-aaa5-dc2fa7ef5a40
 #define NS_IPRESSHELL_IID \
-{ 0xbf539e0a, 0xc314, 0x4ea7, \
-  { 0xba, 0x7c, 0xeb, 0xd3, 0x4e, 0x8a, 0x40, 0x65 } }
+{ 0xfac033dd, 0x938d, 0x45bc, \
+  { 0xaa, 0xa5, 0xdc, 0x2f, 0xa7, 0xef, 0x5a, 0x40 } }
 
 // debug VerifyReflow flags
 #define VERIFY_REFLOW_ON                    0x01
 #define VERIFY_REFLOW_NOISY                 0x02
 #define VERIFY_REFLOW_ALL                   0x04
 #define VERIFY_REFLOW_DUMP_COMMANDS         0x08
 #define VERIFY_REFLOW_NOISY_RC              0x10
 #define VERIFY_REFLOW_REALLY_NOISY_RC       0x20
@@ -407,21 +407,16 @@ public:
   virtual bool GetIsViewportOverridden() = 0;
 
   /**
    * Return true if the presshell expects layout flush.
    */
   virtual bool IsLayoutFlushObserver() = 0;
 
   /**
-   * Reflow the frame model with a reflow reason of eReflowReason_StyleChange
-   */
-  virtual NS_HIDDEN_(void) StyleChangeReflow() = 0;
-
-  /**
    * This calls through to the frame manager to get the root frame.
    */
   virtual NS_HIDDEN_(nsIFrame*) GetRootFrameExternal() const;
   nsIFrame* GetRootFrame() const {
 #ifdef _IMPL_NS_LAYOUT
     return mFrameManager->GetRootFrame();
 #else
     return GetRootFrameExternal();
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -2309,29 +2309,16 @@ PresShell::CheckVisibilityContent(nsICon
 
   *aRetval = false;
   DoCheckVisibility(mPresContext, aNode, aStartOffset, aEndOffset, aRetval);
   return NS_OK;
 }
 
 //end implementations nsISelectionController
 
-
-void
-PresShell::StyleChangeReflow()
-{
-  nsIFrame* rootFrame = mFrameConstructor->GetRootFrame();
-  // At the moment at least, we don't have a root frame before the initial
-  // reflow; it's safe to just ignore the request in that case
-  if (!rootFrame)
-    return;
-
-  FrameNeedsReflow(rootFrame, eStyleChange, NS_FRAME_IS_DIRTY);
-}
-
 nsIFrame*
 nsIPresShell::GetRootFrameExternal() const
 {
   return mFrameConstructor->GetRootFrame();
 }
 
 nsIFrame*
 nsIPresShell::GetRootScrollFrame() const
--- a/layout/base/nsPresShell.h
+++ b/layout/base/nsPresShell.h
@@ -84,17 +84,16 @@ public:
                                      int16_t aFlags);
   NS_IMETHOD RepaintSelection(SelectionType aType);
 
   virtual NS_HIDDEN_(void) BeginObservingDocument();
   virtual NS_HIDDEN_(void) EndObservingDocument();
   virtual NS_HIDDEN_(nsresult) Initialize(nscoord aWidth, nscoord aHeight);
   virtual NS_HIDDEN_(nsresult) ResizeReflow(nscoord aWidth, nscoord aHeight);
   virtual NS_HIDDEN_(nsresult) ResizeReflowOverride(nscoord aWidth, nscoord aHeight);
-  virtual NS_HIDDEN_(void) StyleChangeReflow();
   virtual NS_HIDDEN_(nsIPageSequenceFrame*) GetPageSequenceFrame() const;
   virtual NS_HIDDEN_(nsIFrame*) GetRealPrimaryFrameFor(nsIContent* aContent) const;
 
   virtual NS_HIDDEN_(nsIFrame*) GetPlaceholderFrameFor(nsIFrame* aFrame) const;
   virtual NS_HIDDEN_(void) FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
                                             nsFrameState aBitToAdd);
   virtual NS_HIDDEN_(void) FrameNeedsToContinueReflow(nsIFrame *aFrame);
   virtual NS_HIDDEN_(void) CancelAllPendingReflows();
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -432,17 +432,17 @@ load first-letter-638937-2.html
 load 734777.html
 test-pref(layout.css.flexbox.enabled,true) load 737313-1.html
 test-pref(layout.css.flexbox.enabled,true) load 737313-2.html
 test-pref(layout.css.flexbox.enabled,true) load 737313-3.html
 test-pref(font.size.inflation.emPerLine,15) asserts(1-100) load font-inflation-762332.html # bug 762332
 load 762902.html
 load 762764-1.html
 load 786740-1.html
-asserts(12) test-pref(layout.css.flexbox.enabled,true) load 798020-1.html
+asserts(8) test-pref(layout.css.flexbox.enabled,true) load 798020-1.html
 test-pref(layout.css.flexbox.enabled,true) load 798235-1.html
 test-pref(layout.css.flexbox.enabled,true) load 799207-1.html
 asserts(12) test-pref(layout.css.flexbox.enabled,true) load 799207-2.html
 test-pref(layout.css.flexbox.enabled,true) load 801268-1.html
 test-pref(layout.css.flexbox.enabled,true) load 804089-1.xhtml
 load 810726.html
 load 840818.html
 test-pref(layout.css.flexbox.enabled,true) load 812822-1.html
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -984,18 +984,35 @@ nsBlockFrame::Reflow(nsPresContext*     
   nsOverflowAreas fcBounds;
   nsReflowStatus fcStatus = NS_FRAME_COMPLETE;
   rv = ReflowPushedFloats(state, fcBounds, fcStatus);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // If we're not dirty (which means we'll mark everything dirty later)
   // and our width has changed, mark the lines dirty that we need to
   // mark dirty for a resize reflow.
-  if (reflowState->mFlags.mHResize)
+  if (!(GetStateBits() & NS_FRAME_IS_DIRTY) && reflowState->mFlags.mHResize) {
     PrepareResizeReflow(state);
+  }
+
+  if (GetStateBits() & NS_BLOCK_LOOK_FOR_DIRTY_FRAMES) {
+    for (line_iterator line = begin_lines(), line_end = end_lines();
+         line != line_end; ++line) {
+      int32_t n = line->GetChildCount();
+      for (nsIFrame* lineFrame = line->mFirstChild;
+           n > 0; lineFrame = lineFrame->GetNextSibling(), --n) {
+        if (NS_SUBTREE_DIRTY(lineFrame)) {
+          // NOTE:  MarkLineDirty does more than just marking the line dirty.
+          MarkLineDirty(line, &mLines);
+          break;
+        }
+      }
+    }
+    RemoveStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
+  }
 
   mState &= ~NS_FRAME_FIRST_REFLOW;
 
   // Now reflow...
   rv = ReflowDirtyLines(state);
 
   // If we have a next-in-flow, and that next-in-flow has pushed floats from
   // this frame from a previous iteration of reflow, then we should not return
@@ -6373,23 +6390,52 @@ nsBlockFrame::ChildIsDirty(nsIFrame* aCh
     }
     
     if (bulletLine != end_lines()) {
       MarkLineDirty(bulletLine, &mLines);
     }
     // otherwise we have an empty line list, and ReflowDirtyLines
     // will handle reflowing the bullet.
   } else {
-    // Mark the line containing the child frame dirty. We would rather do this
-    // in MarkIntrinsicWidthsDirty but that currently won't tell us which
-    // child is being dirtied.
-    bool isValid;
-    nsBlockInFlowLineIterator iter(this, aChild, &isValid);
-    if (isValid) {
-      iter.GetContainer()->MarkLineDirty(iter.GetLine(), iter.GetLineList());
+    // Note that we should go through our children to mark lines dirty
+    // before the next reflow.  Doing it now could make things O(N^2)
+    // since finding the right line is O(N).
+    // We don't need to worry about marking lines on the overflow list
+    // as dirty; we're guaranteed to reflow them if we take them off the
+    // overflow list.
+    // However, we might have gotten a float, in which case we need to
+    // reflow the line containing its placeholder.  So find the
+    // ancestor-or-self of the placeholder that's a child of the block,
+    // and mark it as NS_FRAME_HAS_DIRTY_CHILDREN too, so that we mark
+    // its line dirty when we handle NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
+    // We need to take some care to handle the case where a float is in
+    // a different continuation than its placeholder, including marking
+    // an extra block with NS_BLOCK_LOOK_FOR_DIRTY_FRAMES.
+    if (!(aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
+      AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
+    } else {
+      NS_ASSERTION(aChild->IsFloating(), "should be a float");
+      nsIFrame *thisFC = GetFirstContinuation();
+      nsIFrame *placeholderPath =
+        PresContext()->FrameManager()->GetPlaceholderFrameFor(aChild);
+      // SVG code sometimes sends FrameNeedsReflow notifications during
+      // frame destruction, leading to null placeholders, but we're safe
+      // ignoring those.
+      if (placeholderPath) {
+        for (;;) {
+          nsIFrame *parent = placeholderPath->GetParent();
+          if (parent->GetContent() == mContent &&
+              parent->GetFirstContinuation() == thisFC) {
+            parent->AddStateBits(NS_BLOCK_LOOK_FOR_DIRTY_FRAMES);
+            break;
+          }
+          placeholderPath = parent;
+        }
+        placeholderPath->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+      }
     }
   }
 
   nsBlockFrameSuper::ChildIsDirty(aChild);
 }
 
 void
 nsBlockFrame::Init(nsIContent*      aContent,
@@ -6655,16 +6701,25 @@ nsBlockFrame::RenumberListsInBlock(nsPre
       if (kidRenumberedABullet) {
         line->MarkDirty();
         renumberedABullet = true;
       }
       kid = kid->GetNextSibling();
     }
   } while (bifLineIter.Next());
 
+  // We need to set NS_FRAME_HAS_DIRTY_CHILDREN bits up the tree between
+  // the bullet and the caller of RenumberLists.  But the caller itself
+  // has to be responsible for setting the bit itself, since that caller
+  // might be making a FrameNeedsReflow call, which requires that the
+  // bit not be set yet.
+  if (renumberedABullet && aDepth != 0) {
+    aBlockFrame->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
+  }
+
   return renumberedABullet;
 }
 
 bool
 nsBlockFrame::RenumberListsFor(nsPresContext* aPresContext,
                                nsIFrame* aKid,
                                int32_t* aOrdinal,
                                int32_t aDepth,
@@ -6699,18 +6754,28 @@ nsBlockFrame::RenumberListsFor(nsPresCon
     if (listItem) {
       nsBulletFrame* bullet = listItem->GetBullet();
       if (bullet) {
         bool changed;
         *aOrdinal = bullet->SetListItemOrdinal(*aOrdinal, &changed, aIncrement);
         if (changed) {
           kidRenumberedABullet = true;
 
-          // The ordinal changed - mark the bullet frame dirty.
-          listItem->ChildIsDirty(bullet);
+          // The ordinal changed - mark the bullet frame, and any
+          // intermediate frames between it and the block (are there
+          // ever any?), dirty.
+          // The calling code will make the necessary FrameNeedsReflow
+          // call for the list ancestor.
+          bullet->AddStateBits(NS_FRAME_IS_DIRTY);
+          nsIFrame *f = bullet;
+          do {
+            nsIFrame *parent = f->GetParent();
+            parent->ChildIsDirty(f);
+            f = parent;
+          } while (f != listItem);
         }
       }
 
       // XXX temporary? if the list-item has child list-items they
       // should be numbered too; especially since the list-item is
       // itself (ASSUMED!) not to be a counter-resetter.
       bool meToo = RenumberListsInBlock(aPresContext, listItem, aOrdinal,
                                         aDepth + 1, aIncrement);
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -85,16 +85,21 @@ class nsIntervalSet;
 #define NS_BLOCK_HAS_OVERFLOW_LINES         NS_FRAME_STATE_BIT(25)
 #define NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS  NS_FRAME_STATE_BIT(26)
 
 // Set on any block that has descendant frames in the normal
 // flow with 'clear' set to something other than 'none'
 // (including <BR CLEAR="..."> frames)
 #define NS_BLOCK_HAS_CLEAR_CHILDREN         NS_FRAME_STATE_BIT(27)
 
+// This block has had a child marked dirty, so before we reflow we need
+// to look through the lines to find any such children and mark
+// appropriate lines dirty.
+#define NS_BLOCK_LOOK_FOR_DIRTY_FRAMES      NS_FRAME_STATE_BIT(61)
+
 // Are our cached intrinsic widths intrinsic widths for font size
 // inflation?  i.e., what was the current state of
 // GetPresContext()->mInflationDisabledForShrinkWrap at the time they
 // were computed?
 // nsBlockFrame is the only thing that caches intrinsic widths that
 // needs to track this because it's the only thing that caches intrinsic
 // widths that lives inside of things (form controls) that do intrinsic
 // sizing with font inflation enabled.
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -355,16 +355,18 @@ nsContainerFrame::BuildDisplayListForNon
     BuildDisplayListForChild(aBuilder, kid, aDirtyRect, set, aFlags);
     kid = kid->GetNextSibling();
   }
 }
 
 /* virtual */ void
 nsContainerFrame::ChildIsDirty(nsIFrame* aChild)
 {
+  NS_ASSERTION(NS_SUBTREE_DIRTY(aChild), "child isn't actually dirty");
+
   AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
 }
 
 bool
 nsContainerFrame::IsLeaf() const
 {
   return false;
 }
--- a/layout/mathml/Makefile.in
+++ b/layout/mathml/Makefile.in
@@ -42,16 +42,17 @@ CPPSRCS =         nsMathMLChar.cpp		    
                   nsMathMLmmultiscriptsFrame.cpp	\
                   nsMathMLmtableFrame.cpp		\
                   nsMathMLmunderoverFrame.cpp		\
                   nsMathMLmpaddedFrame.cpp		\
                   nsMathMLmspaceFrame.cpp		\
                   nsMathMLmstyleFrame.cpp		\
                   nsMathMLmsqrtFrame.cpp		\
                   nsMathMLmrootFrame.cpp		\
+                  nsMathMLSelectedFrame.cpp		\
                   nsMathMLmactionFrame.cpp		\
                   nsMathMLmencloseFrame.cpp		\
                   nsMathMLsemanticsFrame.cpp		\
                   $(NULL)
 
 include $(topsrcdir)/config/config.mk
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
--- a/layout/mathml/mathml.css
+++ b/layout/mathml/mathml.css
@@ -405,33 +405,16 @@ mtd[_moz-math-columnline="dashed"] {
 }
 /* Don't actually style -moz-math-anonymous by default */
 /*
 ::-moz-math-anonymous {
 }
 */
 
 /**********************************************************************/
-/* Hide embedded semantic MathML content (as opposed to presentational
-   content, which we render). Ideally, here is the behavior that we want:
-
-   if there is an annotation-xml[encoding="MathML-Presentation"]
-     render that annotation, and ignore the first child of the
-     <semantics> element and all other annotations, 
-   else
-     render the first child of <semantics> and ignore all annotations
-
-   But this cannot be expressed with CSS. As a stop-gap, just render
-   the first child to cater for most of the common cases - bug 154931.
-*/
-semantics > :not(:first-child) {
-  display: none;
-}
-
-/**********************************************************************/
 /* This is used when wrapping non-MathML inline elements inside math. */
 *|*::-moz-mathml-anonymous-block {
   display: inline-block !important;
   position: static !important;
   text-indent: 0;
 }
 
 /*****************************************/
copy from layout/mathml/nsMathMLmactionFrame.cpp
copy to layout/mathml/nsMathMLSelectedFrame.cpp
--- a/layout/mathml/nsMathMLmactionFrame.cpp
+++ b/layout/mathml/nsMathMLSelectedFrame.cpp
@@ -3,115 +3,43 @@
  * 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/. */
 
 #include "nsCOMPtr.h"
 #include "nsFrame.h"
 #include "nsPresContext.h"
 #include "nsStyleContext.h"
 #include "nsStyleConsts.h"
-#include "nsINameSpaceManager.h"
-
-#include "nsCSSRendering.h"
-#include "prprf.h"         // For PR_snprintf()
-
-#include "nsIDocShellTreeItem.h"
-#include "nsIDocShellTreeOwner.h"
-#include "nsIWebBrowserChrome.h"
-#include "nsIInterfaceRequestor.h"
-#include "nsIInterfaceRequestorUtils.h"
-#include "nsIDOMElement.h"
-#include "nsTextFragment.h"
-
-#include "nsMathMLmactionFrame.h"
-#include "nsAutoPtr.h"
-#include "nsStyleSet.h"
+#include "nsMathMLSelectedFrame.h"
 #include "nsDisplayList.h"
 
-//
-// <maction> -- bind actions to a subexpression - implementation
-//
-
-enum nsMactionActionTypes {
-  NS_MATHML_ACTION_TYPE_CLASS_ERROR            = 0x10,
-  NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION    = 0x20,
-  NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION = 0x40,
-  NS_MATHML_ACTION_TYPE_CLASS_BITMASK          = 0xF0,
-
-  NS_MATHML_ACTION_TYPE_NONE       = NS_MATHML_ACTION_TYPE_CLASS_ERROR|0x01,
-
-  NS_MATHML_ACTION_TYPE_TOGGLE     = NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION|0x01,
-  NS_MATHML_ACTION_TYPE_UNKNOWN    = NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION|0x02,
-
-  NS_MATHML_ACTION_TYPE_STATUSLINE = NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION|0x01,
-  NS_MATHML_ACTION_TYPE_TOOLTIP    = NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION|0x02
-};
-
-
-// helper function to parse actiontype attribute
-static int32_t
-GetActionType(nsIContent* aContent)
+nsMathMLSelectedFrame::~nsMathMLSelectedFrame()
 {
-  nsAutoString value;
-
-  if (aContent) {
-    if (!aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::actiontype_, value))
-      return NS_MATHML_ACTION_TYPE_NONE; 
-  }
-
-  if (value.EqualsLiteral("toggle"))
-    return NS_MATHML_ACTION_TYPE_TOGGLE;
-  if (value.EqualsLiteral("statusline"))
-    return NS_MATHML_ACTION_TYPE_STATUSLINE;
-  if (value.EqualsLiteral("tooltip"))
-    return NS_MATHML_ACTION_TYPE_TOOLTIP;
-
-  return NS_MATHML_ACTION_TYPE_UNKNOWN;
-}
-
-nsIFrame*
-NS_NewMathMLmactionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
-{
-  return new (aPresShell) nsMathMLmactionFrame(aContext);
-}
-
-NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmactionFrame)
-
-nsMathMLmactionFrame::~nsMathMLmactionFrame()
-{
-  // unregister us as a mouse event listener ...
-  //  printf("maction:%p unregistering as mouse event listener ...\n", this);
-  if (mListener) {
-    mContent->RemoveSystemEventListener(NS_LITERAL_STRING("click"), mListener,
-                                        false);
-    mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseover"), mListener,
-                                        false);
-    mContent->RemoveSystemEventListener(NS_LITERAL_STRING("mouseout"), mListener,
-                                        false);
-  }
 }
 
 void
-nsMathMLmactionFrame::Init(nsIContent*      aContent,
-                           nsIFrame*        aParent,
-                           nsIFrame*        aPrevInFlow)
+nsMathMLSelectedFrame::Init(nsIContent*      aContent,
+                            nsIFrame*        aParent,
+                            nsIFrame*        aPrevInFlow)
 {
   // Init our local attributes
-
-  mChildCount = -1; // these will be updated in GetSelectedFrame()
-  mSelection = 0;
+  mInvalidMarkup = false;
   mSelectedFrame = nullptr;
-  mActionType = GetActionType(aContent);
 
   // Let the base class do the rest
   nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
 }
 
 NS_IMETHODIMP
-nsMathMLmactionFrame::TransmitAutomaticData() {
+nsMathMLSelectedFrame::TransmitAutomaticData()
+{
+  // Note that to determine space-like and embellished op properties:
+  //   - <semantics> behaves the same as <maction>
+  //   - <annotation-xml> behaves the same as <mrow>
+
   // The REC defines the following element to be space-like:
   // * an maction element whose selected sub-expression exists and is
   //   space-like;
   nsIMathMLFrame* mathMLFrame = do_QueryFrame(mSelectedFrame);
   if (mathMLFrame && mathMLFrame->IsSpaceLike()) {
     mPresentationData.flags |= NS_MATHML_SPACE_LIKE;
   } else {
     mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE;
@@ -122,162 +50,39 @@ nsMathMLmactionFrame::TransmitAutomaticD
   //   embellished operator;
   mPresentationData.baseFrame = mSelectedFrame;
   GetEmbellishDataFrom(mSelectedFrame, mEmbellishData);
 
   return NS_OK;
 }
 
 nsresult
-nsMathMLmactionFrame::ChildListChanged(int32_t aModType)
+nsMathMLSelectedFrame::ChildListChanged(int32_t aModType)
 {
-  // update cached values
-  mChildCount = -1;
-  mSelection = 0;
-  mSelectedFrame = nullptr;
   GetSelectedFrame();
-
   return nsMathMLContainerFrame::ChildListChanged(aModType);
 }
 
-// return the frame whose number is given by the attribute selection="number"
-nsIFrame* 
-nsMathMLmactionFrame::GetSelectedFrame()
+NS_IMETHODIMP
+nsMathMLSelectedFrame::SetInitialChildList(ChildListID     aListID,
+                                           nsFrameList&    aChildList)
 {
-  nsAutoString value;
-  int32_t selection; 
-
-  if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == 
-       NS_MATHML_ACTION_TYPE_CLASS_ERROR) {
-    // Mark mSelection as an error.
-    mSelection = -1;
-    mSelectedFrame = nullptr;
-    return mSelectedFrame;
-  }
-
-  // Selection is not applied to tooltip and statusline.
-  // Thereby return the first child.
-  if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == 
-       NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION) {
-    // We don't touch mChildCount here. It's incorrect to assign it 1,
-    // and it's inefficient to count the children. It's fine to leave
-    // it be equal -1 because it's not used with other actiontypes.
-    mSelection = 1;
-    mSelectedFrame = mFrames.FirstChild();
-    return mSelectedFrame;
-  }
-
-  GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::selection_,
-               value);
-  if (!value.IsEmpty()) {
-    nsresult errorCode;
-    selection = value.ToInteger(&errorCode);
-    if (NS_FAILED(errorCode)) 
-      selection = 1;
-  }
-  else selection = 1; // default is first frame
-
-  if (-1 != mChildCount) { // we have been in this function before...
-    // cater for invalid user-supplied selection
-    if (selection > mChildCount || selection < 1)
-      selection = -1;
-    // quick return if it is identical with our cache
-    if (selection == mSelection) 
-      return mSelectedFrame;
-  }
-
-  // get the selected child and cache new values...
-  int32_t count = 0;
-  nsIFrame* childFrame = mFrames.FirstChild();
-  while (childFrame) {
-    if (!mSelectedFrame) 
-      mSelectedFrame = childFrame; // default is first child
-    if (++count == selection) 
-      mSelectedFrame = childFrame;
-
-    childFrame = childFrame->GetNextSibling();
-  }
-  // cater for invalid user-supplied selection
-  if (selection > count || selection < 1)
-    selection = -1;
-
-  mChildCount = count;
-  mSelection = selection;
-  TransmitAutomaticData();
-
-  return mSelectedFrame;
-}
-
-NS_IMETHODIMP
-nsMathMLmactionFrame::SetInitialChildList(ChildListID     aListID,
-                                          nsFrameList&    aChildList)
-{
-  nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList);
-
+  nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListID,
+                                                            aChildList);
   // This very first call to GetSelectedFrame() will cause us to be marked as an
   // embellished operator if the selected child is an embellished operator
-  if (!GetSelectedFrame()) {
-    mActionType = NS_MATHML_ACTION_TYPE_NONE;
-  }
-  else {
-    // create mouse event listener and register it
-    mListener = new nsMathMLmactionFrame::MouseListener(this);
-    // printf("maction:%p registering as mouse event listener ...\n", this);
-    mContent->AddSystemEventListener(NS_LITERAL_STRING("click"), mListener,
-                                     false, false);
-    mContent->AddSystemEventListener(NS_LITERAL_STRING("mouseover"), mListener,
-                                     false, false);
-    mContent->AddSystemEventListener(NS_LITERAL_STRING("mouseout"), mListener,
-                                     false, false);
-  }
+  GetSelectedFrame();
   return rv;
 }
 
-NS_IMETHODIMP
-nsMathMLmactionFrame::AttributeChanged(int32_t  aNameSpaceID,
-                                       nsIAtom* aAttribute,
-                                       int32_t  aModType)
-{
-  bool needsReflow = false;
-
-  if (aAttribute == nsGkAtoms::actiontype_) {
-    // updating mActionType ...
-    int32_t oldActionType = mActionType;
-    mActionType = GetActionType(mContent);
-
-    // Initiate a reflow when actiontype classes are different.
-    if ((oldActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) !=
-          (mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK)) {
-      needsReflow = true;
-    }
-  } else if (aAttribute == nsGkAtoms::selection_) {
-    if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == 
-         NS_MATHML_ACTION_TYPE_CLASS_USE_SELECTION) {
-      needsReflow = true;
-    }
-  } else {
-    // let the base class handle other attribute changes
-    return 
-      nsMathMLContainerFrame::AttributeChanged(aNameSpaceID, 
-                                               aAttribute, aModType);
-  }
-
-  if (needsReflow) {
-    PresContext()->PresShell()->
-      FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
-  }
-
-  return NS_OK;
-}
-
 //  Only paint the selected child...
 void
-nsMathMLmactionFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                                       const nsRect&           aDirtyRect,
-                                       const nsDisplayListSet& aLists)
+nsMathMLSelectedFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
+                                        const nsRect&           aDirtyRect,
+                                        const nsDisplayListSet& aLists)
 {
   // Report an error if something wrong was found in this frame.
   // We can't call nsDisplayMathMLError from here,
   // so ask nsMathMLContainerFrame to do the work for us.
   if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
     nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
     return;
   }
@@ -295,20 +100,20 @@ nsMathMLmactionFrame::BuildDisplayList(n
 #if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
   // visual debug
   DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
 #endif
 }
 
 // Only reflow the selected child ...
 NS_IMETHODIMP
-nsMathMLmactionFrame::Reflow(nsPresContext*          aPresContext,
-                             nsHTMLReflowMetrics&     aDesiredSize,
-                             const nsHTMLReflowState& aReflowState,
-                             nsReflowStatus&          aStatus)
+nsMathMLSelectedFrame::Reflow(nsPresContext*          aPresContext,
+                              nsHTMLReflowMetrics&     aDesiredSize,
+                              const nsHTMLReflowState& aReflowState,
+                              nsReflowStatus&          aStatus)
 {
   nsresult rv = NS_OK;
   aStatus = NS_FRAME_COMPLETE;
   aDesiredSize.width = aDesiredSize.height = 0;
   aDesiredSize.ascent = 0;
   mBoundingMetrics = nsBoundingMetrics();
   nsIFrame* childFrame = GetSelectedFrame();
   if (childFrame) {
@@ -323,23 +128,23 @@ nsMathMLmactionFrame::Reflow(nsPresConte
   }
   FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
   NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
   return rv;
 }
 
 // Only place the selected child ...
 /* virtual */ nsresult
-nsMathMLmactionFrame::Place(nsRenderingContext& aRenderingContext,
-                            bool                 aPlaceOrigin,
-                            nsHTMLReflowMetrics& aDesiredSize)
+nsMathMLSelectedFrame::Place(nsRenderingContext& aRenderingContext,
+                             bool                 aPlaceOrigin,
+                             nsHTMLReflowMetrics& aDesiredSize)
 {
   nsIFrame* childFrame = GetSelectedFrame();
 
-  if (mSelection == -1) {
+  if (mInvalidMarkup) {
     return ReflowError(aRenderingContext, aDesiredSize);
   }
 
   aDesiredSize.width = aDesiredSize.height = 0;
   aDesiredSize.ascent = 0;
   mBoundingMetrics = nsBoundingMetrics();
   if (childFrame) {
     GetReflowAndBoundingMetricsFor(childFrame, aDesiredSize, mBoundingMetrics);
@@ -347,121 +152,8 @@ nsMathMLmactionFrame::Place(nsRenderingC
       FinishReflowChild(childFrame, PresContext(), nullptr, aDesiredSize, 0, 0, 0);
     }
     mReference.x = 0;
     mReference.y = aDesiredSize.ascent;
   }
   aDesiredSize.mBoundingMetrics = mBoundingMetrics;
   return NS_OK;
 }
-
-// ################################################################
-// Event handlers 
-// ################################################################
-
-NS_IMPL_ISUPPORTS1(nsMathMLmactionFrame::MouseListener,
-                   nsIDOMEventListener)
-
-
-// helper to show a msg on the status bar
-// curled from nsObjectFrame.cpp ...
-void
-ShowStatus(nsPresContext* aPresContext, nsString& aStatusMsg)
-{
-  nsCOMPtr<nsISupports> cont = aPresContext->GetContainer();
-  if (cont) {
-    nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(cont));
-    if (docShellItem) {
-      nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
-      docShellItem->GetTreeOwner(getter_AddRefs(treeOwner));
-      if (treeOwner) {
-        nsCOMPtr<nsIWebBrowserChrome> browserChrome(do_GetInterface(treeOwner));
-        if (browserChrome) {
-          browserChrome->SetStatus(nsIWebBrowserChrome::STATUS_LINK, aStatusMsg.get());
-        }
-      }
-    }
-  }
-}
-
-NS_IMETHODIMP
-nsMathMLmactionFrame::MouseListener::HandleEvent(nsIDOMEvent* aEvent)
-{
-  nsAutoString eventType;
-  aEvent->GetType(eventType);
-  if (eventType.EqualsLiteral("mouseover")) {
-    mOwner->MouseOver();
-  }
-  else if (eventType.EqualsLiteral("click")) {
-    mOwner->MouseClick();
-  }
-  else if (eventType.EqualsLiteral("mouseout")) {
-    mOwner->MouseOut();
-  }
-  else {
-    NS_ABORT();
-  }
-
-  return NS_OK;
-}
-
-void
-nsMathMLmactionFrame::MouseOver()
-{
-  // see if we should display a status message
-  if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) {
-    // retrieve content from a second child if it exists
-    nsIFrame* childFrame = mFrames.FrameAt(1);
-    if (!childFrame) return;
-
-    nsIContent* content = childFrame->GetContent();
-    if (!content) return;
-
-    // check whether the content is mtext or not
-    if (content->GetNameSpaceID() == kNameSpaceID_MathML &&
-        content->Tag() == nsGkAtoms::mtext_) {
-      // get the text to be displayed
-      content = content->GetFirstChild();
-      if (!content) return;
-
-      const nsTextFragment* textFrg = content->GetText();
-      if (!textFrg) return;
-
-      nsAutoString text;
-      textFrg->AppendTo(text);
-      // collapse whitespaces as listed in REC, section 3.2.6.1
-      text.CompressWhitespace();
-      ShowStatus(PresContext(), text);
-    }
-  }
-}
-
-void
-nsMathMLmactionFrame::MouseOut()
-{
-  // see if we should remove the status message
-  if (NS_MATHML_ACTION_TYPE_STATUSLINE == mActionType) {
-    nsAutoString value;
-    value.SetLength(0);
-    ShowStatus(PresContext(), value);
-  }
-}
-
-void
-nsMathMLmactionFrame::MouseClick()
-{
-  if (NS_MATHML_ACTION_TYPE_TOGGLE == mActionType) {
-    if (mChildCount > 1) {
-      int32_t selection = (mSelection == mChildCount)? 1 : mSelection + 1;
-      nsAutoString value;
-      char cbuf[10];
-      PR_snprintf(cbuf, sizeof(cbuf), "%d", selection);
-      value.AssignASCII(cbuf);
-      bool notify = false; // don't yet notify the document
-      mContent->SetAttr(kNameSpaceID_None, nsGkAtoms::selection_, value, notify);
-
-      // Now trigger a content-changed reflow...
-      PresContext()->PresShell()->
-        FrameNeedsReflow(mSelectedFrame, nsIPresShell::eTreeChange,
-                         NS_FRAME_IS_DIRTY);
-    }
-  }
-}
copy from layout/mathml/nsMathMLmactionFrame.h
copy to layout/mathml/nsMathMLSelectedFrame.h
--- a/layout/mathml/nsMathMLmactionFrame.h
+++ b/layout/mathml/nsMathMLSelectedFrame.h
@@ -1,40 +1,29 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
-#ifndef nsMathMLmactionFrame_h___
-#define nsMathMLmactionFrame_h___
-
-#include "nsCOMPtr.h"
-#include "nsMathMLContainerFrame.h"
-#include "nsIDOMEventListener.h"
-#include "mozilla/Attributes.h"
+#ifndef nsMathMLSelectedFrame_h___
+#define nsMathMLSelectedFrame_h___
 
-//
-// <maction> -- bind actions to a subexpression
-//
+#include "nsMathMLContainerFrame.h"
 
-class nsMathMLmactionFrame : public nsMathMLContainerFrame {
+class nsMathMLSelectedFrame : public nsMathMLContainerFrame {
 public:
-  NS_DECL_FRAMEARENA_HELPERS
-
-  friend nsIFrame* NS_NewMathMLmactionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
-
-  NS_IMETHOD
-  TransmitAutomaticData();
-
   virtual void
   Init(nsIContent*      aContent,
        nsIFrame*        aParent,
        nsIFrame*        aPrevInFlow) MOZ_OVERRIDE;
 
   NS_IMETHOD
+  TransmitAutomaticData() MOZ_OVERRIDE;
+
+  NS_IMETHOD
   SetInitialChildList(ChildListID     aListID,
                       nsFrameList&    aChildList) MOZ_OVERRIDE;
 
   virtual nsresult
   ChildListChanged(int32_t aModType) MOZ_OVERRIDE;
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
@@ -46,45 +35,20 @@ public:
         nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
 
   NS_IMETHOD
   Reflow(nsPresContext*          aPresContext,
          nsHTMLReflowMetrics&     aDesiredSize,
          const nsHTMLReflowState& aReflowState,
          nsReflowStatus&          aStatus) MOZ_OVERRIDE;
 
-  NS_IMETHOD
-  AttributeChanged(int32_t  aNameSpaceID,
-                   nsIAtom* aAttribute,
-                   int32_t  aModType) MOZ_OVERRIDE;
-
-private:
-  void MouseClick();
-  void MouseOver();
-  void MouseOut();
-
-  class MouseListener MOZ_FINAL : public nsIDOMEventListener
-  {
-    NS_DECL_ISUPPORTS
-    NS_DECL_NSIDOMEVENTLISTENER
-
-    MouseListener(nsMathMLmactionFrame* aOwner) : mOwner(aOwner) { }
+protected:
+  nsMathMLSelectedFrame(nsStyleContext* aContext) :
+    nsMathMLContainerFrame(aContext) {}
+  virtual ~nsMathMLSelectedFrame();
+  
+  virtual nsIFrame* GetSelectedFrame() = 0;
+  nsIFrame*       mSelectedFrame;
 
-    nsMathMLmactionFrame* mOwner;
-  };
-
-protected:
-  nsMathMLmactionFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {}
-  virtual ~nsMathMLmactionFrame();
-  
-private:
-  int32_t         mActionType;
-  int32_t         mChildCount;
-  int32_t         mSelection;
-  nsIFrame*       mSelectedFrame;
-  nsCOMPtr<MouseListener> mListener;
-
-  // helper to return the frame for the attribute selection="number"
-  nsIFrame* 
-  GetSelectedFrame();
+  bool            mInvalidMarkup;
 };
 
-#endif /* nsMathMLmactionFrame_h___ */
+#endif /* nsMathMLSelectedFrame_h___ */
--- a/layout/mathml/nsMathMLTokenFrame.cpp
+++ b/layout/mathml/nsMathMLTokenFrame.cpp
@@ -27,17 +27,18 @@ nsMathMLTokenFrame::~nsMathMLTokenFrame(
 }
 
 NS_IMETHODIMP
 nsMathMLTokenFrame::InheritAutomaticData(nsIFrame* aParent)
 {
   // let the base class get the default from our parent
   nsMathMLContainerFrame::InheritAutomaticData(aParent);
 
-  if (mContent->Tag() != nsGkAtoms::mspace_) {
+  if (mContent->Tag() != nsGkAtoms::mspace_ &&
+      mContent->Tag() != nsGkAtoms::annotation_) {
     // see if the directionality attribute is there
     nsMathMLFrame::FindAttrDirectionality(mContent, mPresentationData);
   }
 
   ProcessTextData();
 
   return NS_OK;
 }
--- a/layout/mathml/nsMathMLmactionFrame.cpp
+++ b/layout/mathml/nsMathMLmactionFrame.cpp
@@ -92,80 +92,56 @@ nsMathMLmactionFrame::~nsMathMLmactionFr
 void
 nsMathMLmactionFrame::Init(nsIContent*      aContent,
                            nsIFrame*        aParent,
                            nsIFrame*        aPrevInFlow)
 {
   // Init our local attributes
 
   mChildCount = -1; // these will be updated in GetSelectedFrame()
-  mSelection = 0;
-  mSelectedFrame = nullptr;
   mActionType = GetActionType(aContent);
 
   // Let the base class do the rest
-  nsMathMLContainerFrame::Init(aContent, aParent, aPrevInFlow);
-}
-
-NS_IMETHODIMP
-nsMathMLmactionFrame::TransmitAutomaticData() {
-  // The REC defines the following element to be space-like:
-  // * an maction element whose selected sub-expression exists and is
-  //   space-like;
-  nsIMathMLFrame* mathMLFrame = do_QueryFrame(mSelectedFrame);
-  if (mathMLFrame && mathMLFrame->IsSpaceLike()) {
-    mPresentationData.flags |= NS_MATHML_SPACE_LIKE;
-  } else {
-    mPresentationData.flags &= ~NS_MATHML_SPACE_LIKE;
-  }
-
-  // The REC defines the following element to be an embellished operator:
-  // * an maction element whose selected sub-expression exists and is an
-  //   embellished operator;
-  mPresentationData.baseFrame = mSelectedFrame;
-  GetEmbellishDataFrom(mSelectedFrame, mEmbellishData);
-
-  return NS_OK;
+  return nsMathMLSelectedFrame::Init(aContent, aParent, aPrevInFlow);
 }
 
 nsresult
 nsMathMLmactionFrame::ChildListChanged(int32_t aModType)
 {
   // update cached values
   mChildCount = -1;
-  mSelection = 0;
   mSelectedFrame = nullptr;
-  GetSelectedFrame();
 
-  return nsMathMLContainerFrame::ChildListChanged(aModType);
+  return nsMathMLSelectedFrame::ChildListChanged(aModType);
 }
 
 // return the frame whose number is given by the attribute selection="number"
 nsIFrame* 
 nsMathMLmactionFrame::GetSelectedFrame()
 {
   nsAutoString value;
   int32_t selection; 
 
   if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == 
        NS_MATHML_ACTION_TYPE_CLASS_ERROR) {
-    // Mark mSelection as an error.
     mSelection = -1;
+    mInvalidMarkup = true;
     mSelectedFrame = nullptr;
     return mSelectedFrame;
   }
 
   // Selection is not applied to tooltip and statusline.
   // Thereby return the first child.
   if ((mActionType & NS_MATHML_ACTION_TYPE_CLASS_BITMASK) == 
        NS_MATHML_ACTION_TYPE_CLASS_IGNORE_SELECTION) {
     // We don't touch mChildCount here. It's incorrect to assign it 1,
     // and it's inefficient to count the children. It's fine to leave
     // it be equal -1 because it's not used with other actiontypes.
     mSelection = 1;
+    mInvalidMarkup = false;
     mSelectedFrame = mFrames.FirstChild();
     return mSelectedFrame;
   }
 
   GetAttribute(mContent, mPresentationData.mstyle, nsGkAtoms::selection_,
                value);
   if (!value.IsEmpty()) {
     nsresult errorCode;
@@ -196,30 +172,29 @@ nsMathMLmactionFrame::GetSelectedFrame()
     childFrame = childFrame->GetNextSibling();
   }
   // cater for invalid user-supplied selection
   if (selection > count || selection < 1)
     selection = -1;
 
   mChildCount = count;
   mSelection = selection;
+  mInvalidMarkup = (mSelection == -1);
   TransmitAutomaticData();
 
   return mSelectedFrame;
 }
 
 NS_IMETHODIMP
 nsMathMLmactionFrame::SetInitialChildList(ChildListID     aListID,
                                           nsFrameList&    aChildList)
 {
-  nsresult rv = nsMathMLContainerFrame::SetInitialChildList(aListID, aChildList);
+  nsresult rv = nsMathMLSelectedFrame::SetInitialChildList(aListID, aChildList);
 
-  // This very first call to GetSelectedFrame() will cause us to be marked as an
-  // embellished operator if the selected child is an embellished operator
-  if (!GetSelectedFrame()) {
+  if (!mSelectedFrame) {
     mActionType = NS_MATHML_ACTION_TYPE_NONE;
   }
   else {
     // create mouse event listener and register it
     mListener = new nsMathMLmactionFrame::MouseListener(this);
     // printf("maction:%p registering as mouse event listener ...\n", this);
     mContent->AddSystemEventListener(NS_LITERAL_STRING("click"), mListener,
                                      false, false);
@@ -263,101 +238,16 @@ nsMathMLmactionFrame::AttributeChanged(i
   if (needsReflow) {
     PresContext()->PresShell()->
       FrameNeedsReflow(this, nsIPresShell::eTreeChange, NS_FRAME_IS_DIRTY);
   }
 
   return NS_OK;
 }
 
-//  Only paint the selected child...
-void
-nsMathMLmactionFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                                       const nsRect&           aDirtyRect,
-                                       const nsDisplayListSet& aLists)
-{
-  // Report an error if something wrong was found in this frame.
-  // We can't call nsDisplayMathMLError from here,
-  // so ask nsMathMLContainerFrame to do the work for us.
-  if (NS_MATHML_HAS_ERROR(mPresentationData.flags)) {
-    nsMathMLContainerFrame::BuildDisplayList(aBuilder, aDirtyRect, aLists);
-    return;
-  }
-
-  DisplayBorderBackgroundOutline(aBuilder, aLists);
-
-  nsIFrame* childFrame = GetSelectedFrame();
-  if (childFrame) {
-    // Put the child's background directly onto the content list
-    nsDisplayListSet set(aLists, aLists.Content());
-    // The children should be in content order
-    BuildDisplayListForChild(aBuilder, childFrame, aDirtyRect, set);
-  }
-
-#if defined(DEBUG) && defined(SHOW_BOUNDING_BOX)
-  // visual debug
-  DisplayBoundingMetrics(aBuilder, this, mReference, mBoundingMetrics, aLists);
-#endif
-}
-
-// Only reflow the selected child ...
-NS_IMETHODIMP
-nsMathMLmactionFrame::Reflow(nsPresContext*          aPresContext,
-                             nsHTMLReflowMetrics&     aDesiredSize,
-                             const nsHTMLReflowState& aReflowState,
-                             nsReflowStatus&          aStatus)
-{
-  nsresult rv = NS_OK;
-  aStatus = NS_FRAME_COMPLETE;
-  aDesiredSize.width = aDesiredSize.height = 0;
-  aDesiredSize.ascent = 0;
-  mBoundingMetrics = nsBoundingMetrics();
-  nsIFrame* childFrame = GetSelectedFrame();
-  if (childFrame) {
-    nsSize availSize(aReflowState.ComputedWidth(), NS_UNCONSTRAINEDSIZE);
-    nsHTMLReflowState childReflowState(aPresContext, aReflowState,
-                                       childFrame, availSize);
-    rv = ReflowChild(childFrame, aPresContext, aDesiredSize,
-                     childReflowState, aStatus);
-    SaveReflowAndBoundingMetricsFor(childFrame, aDesiredSize,
-                                    aDesiredSize.mBoundingMetrics);
-    mBoundingMetrics = aDesiredSize.mBoundingMetrics;
-  }
-  FinalizeReflow(*aReflowState.rendContext, aDesiredSize);
-  NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize);
-  return rv;
-}
-
-// Only place the selected child ...
-/* virtual */ nsresult
-nsMathMLmactionFrame::Place(nsRenderingContext& aRenderingContext,
-                            bool                 aPlaceOrigin,
-                            nsHTMLReflowMetrics& aDesiredSize)
-{
-  nsIFrame* childFrame = GetSelectedFrame();
-
-  if (mSelection == -1) {
-    return ReflowError(aRenderingContext, aDesiredSize);
-  }
-
-  aDesiredSize.width = aDesiredSize.height = 0;
-  aDesiredSize.ascent = 0;
-  mBoundingMetrics = nsBoundingMetrics();
-  if (childFrame) {
-    GetReflowAndBoundingMetricsFor(childFrame, aDesiredSize, mBoundingMetrics);
-    if (aPlaceOrigin) {
-      FinishReflowChild(childFrame, PresContext(), nullptr, aDesiredSize, 0, 0, 0);
-    }
-    mReference.x = 0;
-    mReference.y = aDesiredSize.ascent;
-  }
-  aDesiredSize.mBoundingMetrics = mBoundingMetrics;
-  return NS_OK;
-}
-
 // ################################################################
 // Event handlers 
 // ################################################################
 
 NS_IMPL_ISUPPORTS1(nsMathMLmactionFrame::MouseListener,
                    nsIDOMEventListener)
 
 
--- a/layout/mathml/nsMathMLmactionFrame.h
+++ b/layout/mathml/nsMathMLmactionFrame.h
@@ -2,64 +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/. */
 
 #ifndef nsMathMLmactionFrame_h___
 #define nsMathMLmactionFrame_h___
 
 #include "nsCOMPtr.h"
-#include "nsMathMLContainerFrame.h"
+#include "nsMathMLSelectedFrame.h"
 #include "nsIDOMEventListener.h"
 #include "mozilla/Attributes.h"
 
 //
 // <maction> -- bind actions to a subexpression
 //
 
-class nsMathMLmactionFrame : public nsMathMLContainerFrame {
+class nsMathMLmactionFrame : public nsMathMLSelectedFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   friend nsIFrame* NS_NewMathMLmactionFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
-  NS_IMETHOD
-  TransmitAutomaticData();
-
   virtual void
   Init(nsIContent*      aContent,
        nsIFrame*        aParent,
        nsIFrame*        aPrevInFlow) MOZ_OVERRIDE;
 
   NS_IMETHOD
   SetInitialChildList(ChildListID     aListID,
-                      nsFrameList&    aChildList) MOZ_OVERRIDE;
-
-  virtual nsresult
-  ChildListChanged(int32_t aModType) MOZ_OVERRIDE;
-
-  virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
-                                const nsRect&           aDirtyRect,
-                                const nsDisplayListSet& aLists) MOZ_OVERRIDE;
+                      nsFrameList&    aChildList);
 
   virtual nsresult
-  Place(nsRenderingContext& aRenderingContext,
-        bool                 aPlaceOrigin,
-        nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
-
-  NS_IMETHOD
-  Reflow(nsPresContext*          aPresContext,
-         nsHTMLReflowMetrics&     aDesiredSize,
-         const nsHTMLReflowState& aReflowState,
-         nsReflowStatus&          aStatus) MOZ_OVERRIDE;
+  ChildListChanged(int32_t aModType);
 
   NS_IMETHOD
   AttributeChanged(int32_t  aNameSpaceID,
                    nsIAtom* aAttribute,
-                   int32_t  aModType) MOZ_OVERRIDE;
+                   int32_t  aModType);
 
 private:
   void MouseClick();
   void MouseOver();
   void MouseOut();
 
   class MouseListener MOZ_FINAL : public nsIDOMEventListener
   {
@@ -67,24 +49,24 @@ private:
     NS_DECL_NSIDOMEVENTLISTENER
 
     MouseListener(nsMathMLmactionFrame* aOwner) : mOwner(aOwner) { }
 
     nsMathMLmactionFrame* mOwner;
   };
 
 protected:
-  nsMathMLmactionFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {}
+  nsMathMLmactionFrame(nsStyleContext* aContext) :
+    nsMathMLSelectedFrame(aContext) {}
   virtual ~nsMathMLmactionFrame();
   
 private:
   int32_t         mActionType;
   int32_t         mChildCount;
   int32_t         mSelection;
-  nsIFrame*       mSelectedFrame;
   nsCOMPtr<MouseListener> mListener;
 
   // helper to return the frame for the attribute selection="number"
   nsIFrame* 
   GetSelectedFrame();
 };
 
 #endif /* nsMathMLmactionFrame_h___ */
--- a/layout/mathml/nsMathMLsemanticsFrame.cpp
+++ b/layout/mathml/nsMathMLsemanticsFrame.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 
 #include "nsMathMLsemanticsFrame.h"
+#include "nsMimeTypes.h"
 
 //
 // <semantics> -- associate annotations with a MathML expression
 //
 
 nsIFrame*
 NS_NewMathMLsemanticsFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
@@ -17,22 +18,102 @@ NS_NewMathMLsemanticsFrame(nsIPresShell*
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLsemanticsFrame)
 
 nsMathMLsemanticsFrame::~nsMathMLsemanticsFrame()
 {
 }
 
-NS_IMETHODIMP
-nsMathMLsemanticsFrame::TransmitAutomaticData()
+nsIFrame* 
+nsMathMLsemanticsFrame::GetSelectedFrame()
 {
-  // The REC defines the following elements to be embellished operators:
-  // * one of the elements msub, msup, msubsup, munder, mover, munderover,
-  //   mmultiscripts, mfrac, or semantics (Section 5.1 Annotation Framework),
-  //   whose first argument exists and is an embellished operator; 
-  //
-  // If our first child is an embellished operator, its flags bubble to us
-  mPresentationData.baseFrame = mFrames.FirstChild();
-  GetEmbellishDataFrom(mPresentationData.baseFrame, mEmbellishData);
+  // By default, we will display the first child of the <semantics> element.
+  nsIFrame* childFrame = mFrames.FirstChild(); 
+  mSelectedFrame = childFrame;
+
+  // An empty <semantics> is invalid
+  if (!childFrame) {
+    mInvalidMarkup = true;
+    return mSelectedFrame;
+  }
+  mInvalidMarkup = false;
+
+  // Using <annotation> or <annotation-xml> as a first child is invalid.
+  // However some people use this syntax so we take care of this case too.
+  bool firstChildIsAnnotation = false;
+  nsIContent* childContent = childFrame->GetContent();
+  if (childContent->GetNameSpaceID() == kNameSpaceID_MathML &&
+      (childContent->Tag() == nsGkAtoms::annotation_ ||
+       childContent->Tag() == nsGkAtoms::annotation_xml_)) {
+    firstChildIsAnnotation = true;
+  }
+
+  // If the first child is a presentation MathML element other than
+  // <annotation> or <annotation-xml>, we are done.
+  if (!firstChildIsAnnotation &&
+      childFrame->IsFrameOfType(nsIFrame::eMathML)) {
+    nsIMathMLFrame* mathMLFrame = do_QueryFrame(childFrame);
+    if (mathMLFrame) {
+      TransmitAutomaticData();
+      return mSelectedFrame;
+    }
+    // The first child is not an annotation, so skip it.
+    childFrame = childFrame->GetNextSibling();
+  }
+
+  // Otherwise, we read the list of annotations and select the first one that
+  // could be displayed in place of the first child of <semantics>. If none is
+  // found, we fallback to this first child.
+  for ( ; childFrame; childFrame = childFrame->GetNextSibling()) {
+    nsIContent* childContent = childFrame->GetContent();
+
+    if (childContent->GetNameSpaceID() != kNameSpaceID_MathML) continue;
+
+    if (childContent->Tag() == nsGkAtoms::annotation_) {
 
-  return NS_OK;
+      // If the <annotation> element has an src attribute we ignore it.
+      // XXXfredw Should annotation images be supported? See the related
+      // bug 297465 for mglyph.
+      if (childContent->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) continue;
+
+      // Otherwise, we assume it is a text annotation that can always be
+      // displayed and stop here.
+      mSelectedFrame = childFrame;
+      break;
+    }
+
+    if (childContent->Tag() == nsGkAtoms::annotation_xml_) {
+
+      // If the <annotation-xml> element has an src attribute we ignore it.
+      if (childContent->HasAttr(kNameSpaceID_None, nsGkAtoms::src)) continue;
+
+      // If the <annotation-xml> element has an encoding attribute
+      // describing presentation MathML, SVG or HTML we assume the content
+      // can be displayed and stop here.
+      //
+      // We recognize the following encoding values:
+      //
+      // - "MathML-Presentation", which is mentioned in the MathML3 REC
+      // - "SVG1.1" which is mentioned in the W3C note
+      //                   http://www.w3.org/Math/Documents/Notes/graphics.xml
+      // - Other mime Content-Types for SVG and HTML
+      //
+      // We exclude APPLICATION_MATHML_XML = "application/mathml+xml" which
+      // is ambiguous about whether it is Presentation or Content MathML.
+      // Authors must use a more explicit encoding value.
+      nsAutoString value;
+      childContent->GetAttr(kNameSpaceID_None, nsGkAtoms::encoding, value);
+      if (value.EqualsLiteral("application/mathml-presentation+xml") ||
+          value.EqualsLiteral("MathML-Presentation") ||
+          value.EqualsLiteral(IMAGE_SVG_XML) ||
+          value.EqualsLiteral("SVG1.1") ||
+          value.EqualsLiteral(APPLICATION_XHTML_XML) ||
+          value.EqualsLiteral(TEXT_HTML)) {
+        mSelectedFrame = childFrame;
+        break;
+      }
+    }
+  }
+
+  TransmitAutomaticData();
+  return mSelectedFrame;
 }
--- a/layout/mathml/nsMathMLsemanticsFrame.h
+++ b/layout/mathml/nsMathMLsemanticsFrame.h
@@ -1,31 +1,30 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* 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/. */
 
 #ifndef nsMathMLsemanticsFrame_h___
 #define nsMathMLsemanticsFrame_h___
 
-#include "nsMathMLContainerFrame.h"
+#include "nsMathMLSelectedFrame.h"
 
 //
 // <semantics> -- associate annotations with a MathML expression
 //
 
-class nsMathMLsemanticsFrame : public nsMathMLContainerFrame {
+class nsMathMLsemanticsFrame : public nsMathMLSelectedFrame {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
   friend nsIFrame* NS_NewMathMLsemanticsFrame(nsIPresShell* aPresShell,
                                               nsStyleContext* aContext);
 
-  NS_IMETHOD
-  TransmitAutomaticData();
-
 protected:
   nsMathMLsemanticsFrame(nsStyleContext* aContext) :
-    nsMathMLContainerFrame(aContext) {}
+    nsMathMLSelectedFrame(aContext) {}
   virtual ~nsMathMLsemanticsFrame();
+
+  nsIFrame* GetSelectedFrame();
 };
 
 #endif /* nsMathMLsemanticsFrame_h___ */
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1707,17 +1707,17 @@ needs-focus == 731726-1.html 731726-1-re
 == 745934-1.html 745934-1-ref.html
 skip-if(B2G) == 748803-1.html 748803-1-ref.html
 == 750551-1.html 750551-1-ref.html
 skip-if(B2G) == 751012-1a.html 751012-1-ref.html
 skip-if(B2G) == 751012-1b.html 751012-1-ref.html
 == 690643-1.html 690643-1-ref.html
 == 748692-1a.html 748692-1-ref.html
 == 748692-1b.html 748692-1-ref.html
-fails-if(Android) == 753329-1.html about:blank
+random-if(Android) == 753329-1.html about:blank
 == 758561-1.html 758561-1-ref.html
 fuzzy-if(true,1,19) fails-if(d2d) == 759036-1.html 759036-1-ref.html
 fuzzy-if(true,17,5859) == 759036-2.html 759036-2-ref.html
 == 776443-1.html 776443-1-ref.html
 == 776443-2.html 776443-2-ref.html
 == 776265-1a.html 776265-1-ref.html
 == 776265-1b.html 776265-1-ref.html
 == 776265-1c.html 776265-1-ref.html
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -56,16 +56,18 @@ skip-if(B2G) == quotes-1.xhtml quotes-1-
 != embellished-op-3-4.html embellished-op-3-4-ref.html
 != embellished-op-3-5.html embellished-op-3-5-ref.html
 == embellished-op-4-1.html embellished-op-4-1-ref.html
 == embellished-op-4-2.html embellished-op-4-2-ref.html
 == embellished-op-4-3.html embellished-op-4-3-ref.html
 == embellished-op-5-1.html embellished-op-5-ref.html
 == embellished-op-5-2.html embellished-op-5-ref.html
 == semantics-1.xhtml semantics-1-ref.xhtml
+== semantics-2.html semantics-2-ref.html
+== semantics-3.html semantics-3-ref.html
 != mathcolor-1.xml mathcolor-1-ref.xml
 != mathcolor-2.xml mathcolor-2-ref.xml
 != mathcolor-3.xml mathcolor-3-ref.xml
 == mathcolor-4.xml mathcolor-4-ref.xml
 != mathbackground-1.xml mathbackground-1-ref.xml
 != mathbackground-2.xml mathbackground-2-ref.xml
 != mathbackground-3.xml mathbackground-3-ref.xml
 == mathbackground-4.xml mathbackground-4-ref.xml
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/semantics-2-ref.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+<head>
+  <title>Various tests for semantics</title>
+  <meta charset="utf-8"/>
+</head>
+
+<body>
+  <p>Empty semantics (invalid): <math><maction></maction></math></p>
+  <p>annotation (invalid): <math><mrow><mtext>annotation</mtext></mrow></math></p>
+  <p>annotation-xml (invalid): <math><mrow><mrow><mtext>annotation-xml</mtext></mrow></mrow></math></p>
+  <p>presentation MathML (no annotations): <math><mrow><mtext>presentation MathML</mtext></mrow></math></p>
+  <p>content MathML (no annotations): <math><maction actiontype="toggle"><csymbol>content MathML</csymbol></maction></math></p>
+  <p>presentation MathML ; annotation: <math><mrow><mtext>presentation MathML</mtext></mrow></math></p>
+  <p>presentation MathML ; annotation-xml: <math><mrow><mtext>presentation MathML</mtext></mrow></math></p>
+  <p>content MathML ; annotation: <math><mrow><mtext>annotation</mtext></mrow></math></p>
+  <p>content MathML ; annotation-xml: <math><mrow><mrow><mtext>annotation-xml</mtext></mrow></mrow></math></p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/semantics-2.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<html>
+<head>
+  <title>Various tests for semantics</title>
+  <meta charset="utf-8"/>
+</head>
+
+<body>
+  <p>Empty semantics (invalid): <math><semantics></semantics></math></p>
+  <p>annotation (invalid): <math><semantics><annotation>annotation</annotation></semantics></math></p>
+  <p>annotation-xml (invalid): <math><semantics><annotation-xml encoding="MathML-Presentation"><mtext>annotation-xml</mtext></annotation-xml></semantics></math></p>
+  <p>presentation MathML (no annotations): <math><semantics><mtext>presentation MathML</mtext></semantics></math></p>
+  <p>content MathML (no annotations): <math><semantics><csymbol>content MathML</csymbol></semantics></math></p>
+  <p>presentation MathML ; annotation: <math><semantics><mtext>presentation MathML</mtext><annotation>annotation</annotation></semantics></math></p>
+  <p>presentation MathML ; annotation-xml: <math><semantics><mtext>presentation MathML</mtext><annotation-xml encoding="application/mathml-presentation+xml"><mtext>annotation-xml</mtext></annotation-xml></semantics></math></p>
+  <p>content MathML ; annotation: <math><semantics><csymbol>content MathML</csymbol><annotation>annotation</annotation></semantics></math></p>
+  <p>content MathML ; annotation-xml: <math><semantics><csymbol>content MathML</csymbol><annotation-xml encoding="application/mathml-presentation+xml"><mtext>annotation-xml</mtext></annotation-xml></semantics></math></p>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/semantics-3-ref.html
@@ -0,0 +1,20 @@
+<!doctype html>
+<html>
+<head>
+  <title>Various tests for mrow</title>
+  <meta charset="utf-8"/>
+</head>
+
+<body>
+  <p>annotation 1: <math><mrow><mtext>annotation</mtext></mrow></math></p>
+  <p>annotation 2: <math><mrow><mtext>\sin x + 5</mtext></mrow></math></p>
+  <p>annotation 3: <math><mrow><mtext>annotation</mtext></mrow></math></p>
+
+  <p>annotation-xml 1: <math><mrow><mrow><mtext>application/mathml-presentation+xml</mtext></mrow></mrow></math></p>
+  <p>annotation-xml 2: <math><mrow><mrow><mtext>MathML-Presentation</mtext></mrow></mrow></math></p>
+  <p>annotation-xml 3: <math><mrow><mtext><svg xmlns="http://www.w3.org/2000/svg" height="2em"><text y="1em">image/svg+xml</text></svg></mtext></mrow></math></p>
+  <p>annotation-xml 4: <math><mrow><mtext><svg xmlns="http://www.w3.org/2000/svg" height="2em"><text y="1em">SVG1.1</text></svg></mtext></mrow></math></p>
+  <p>annotation-xml 5: <math><mrow><mtext><html xmlns="http://www.w3.org/1999/xhtml"><head><title>application/xhtml+xml</title></head><body><p>application/xhtml+xml</p></body></html></mtext></mrow></math></p>
+  <p>annotation-xml 6: <math><mrow><mtext><html><head><title>text/html</title></head><body><p>text/html</p></body></html></mtext></mrow></math></p>
+  <p>annotation-xml 7: <math><mrow><mrow><mtext>annotation-xml</mtext></mrow></mrow></math></p></body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/semantics-3.html
@@ -0,0 +1,21 @@
+<!doctype html>
+<html>
+<head>
+  <title>Various tests for semantics</title>
+   <meta charset="utf-8"/>
+</head>
+
+<body>
+  <p>annotation 1: <math><semantics><csymbol>Content MathML</csymbol><annotation>annotation</annotation><annotation>error</annotation><annotation-xml encoding="application/mathml-presentation+xml"><mtext>error</mtext></annotation-xml></semantics></math></p>
+  <p>annotation 2: <math><semantics><csymbol>Content MathML</csymbol><annotation encoding="application/x-tex">\sin x + 5</annotation><annotation>error</annotation><annotation-xml encoding="application/mathml-presentation+xml"><mtext>error</mtext></annotation-xml></semantics></math></p>
+  <p>annotation 3: <math><semantics><csymbol>Content MathML</csymbol><annotation src="external-resource">error</annotation><annotation>annotation</annotation><annotation-xml encoding="application/mathml-presentation+xml"><mtext>error</mtext></annotation-xml></semantics></math></p>
+
+  <p>annotation-xml 1: <math><semantics><csymbol>Content MathML</csymbol><annotation-xml encoding="application/mathml-presentation+xml"><mtext>application/mathml-presentation+xml</mtext></annotation-xml><annotation-xml encoding="application/mathml-presentation+xml"><mtext>error</mtext></annotation-xml><annotation>error</annotation></semantics></math></p>
+  <p>annotation-xml 2: <math><semantics><csymbol>Content MathML</csymbol><annotation-xml encoding="MathML-Presentation"><mtext>MathML-Presentation</mtext></annotation-xml><annotation-xml encoding="application/mathml-presentation+xml"><mtext>error</mtext></annotation-xml><annotation>error</annotation></semantics></math></p>
+  <p>annotation-xml 3: <math><semantics><csymbol>Content MathML</csymbol><annotation-xml encoding="image/svg+xml"><svg xmlns="http://www.w3.org/2000/svg" height="2em"><text y="1em">image/svg+xml</text></svg></annotation-xml><annotation-xml encoding="application/mathml-presentation+xml"><mtext>error</mtext></annotation-xml><annotation>error</annotation></semantics></math></p>
+  <p>annotation-xml 4: <math><semantics><csymbol>Content MathML</csymbol><annotation-xml encoding="SVG1.1"><svg xmlns="http://www.w3.org/2000/svg" height="2em"><text y="1em">SVG1.1</text></svg></annotation-xml><annotation-xml encoding="application/mathml-presentation+xml"><mtext>error</mtext></annotation-xml><annotation>error</annotation></semantics></math></p>
+  <p>annotation-xml 5: <math><semantics><csymbol>Content MathML</csymbol><annotation-xml encoding="application/xhtml+xml"><html xmlns="http://www.w3.org/1999/xhtml"><head><title>application/xhtml+xml</title></head><body><p>application/xhtml+xml</p></body></html></annotation-xml><annotation-xml encoding="application/mathml-presentation+xml"><mtext>error</mtext></annotation-xml><annotation>error</annotation></math></p>
+  <p>annotation-xml 6: <math><semantics><csymbol>Content MathML</csymbol><annotation-xml encoding="text/html"><html><head><title>text/html</title></head><body><p>text/html</p></body></html></annotation-xml><annotation-xml encoding="application/mathml-presentation+xml"><mtext>error</mtext></annotation-xml><annotation>error</annotation></semantics></math></p>
+  <p>annotation-xml 7: <math><semantics><csymbol>Content MathML</csymbol><annotation-xml encoding="unknown"><mtext>error</mtext></annotation-xml><annotation-xml encoding="application/mathml-presentation+xml"><mtext>annotation-xml</mtext></annotation-xml><annotation>error</annotation></semantics></math></p>
+</body>
+</html>
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3050,35 +3050,54 @@ Tab.prototype = {
                   || gViewportMargins.bottom > 0 || gViewportMargins.left > 0)) {
       // If the page size has changed so that it might or might not fit on the
       // screen with the margins included, run updateViewportSize to resize the
       // browser accordingly.
       // A page will receive the smaller viewport when its page size fits
       // within the screen size, so remeasure when the page size remains within
       // the threshold of screen + margins, in case it's sizing itself relative
       // to the viewport.
-      if (((Math.round(viewport.pageBottom - viewport.pageTop)
-              <= gScreenHeight + gViewportMargins.top + gViewportMargins.bottom)
-             != this.viewportExcludesVerticalMargins) ||
-          ((Math.round(viewport.pageRight - viewport.pageLeft)
-              <= gScreenWidth + gViewportMargins.left + gViewportMargins.right)
-             != this.viewportExcludesHorizontalMargins)) {
+      let hasHorizontalMargins = gViewportMargins.left > 0 || gViewportMargins.right > 0;
+      let hasVerticalMargins = gViewportMargins.top > 0 || gViewportMargins.bottom > 0;
+      let pageHeightGreaterThanScreenHeight, pageWidthGreaterThanScreenWidth;
+
+      if (hasHorizontalMargins) {
+        pageWidthGreaterThanScreenWidth =
+            Math.round(viewport.pageRight - viewport.pageLeft)
+            > gScreenWidth + gViewportMargins.left + gViewportMargins.right;
+      }
+      if (hasVerticalMargins) {
+        pageHeightGreaterThanScreenHeight =
+            Math.round(viewport.pageBottom - viewport.pageTop)
+            > gScreenHeight + gViewportMargins.top + gViewportMargins.bottom;
+      }
+
+      if ((hasHorizontalMargins
+           && (pageWidthGreaterThanScreenWidth != this.viewportExcludesHorizontalMargins)) ||
+          (hasVerticalMargins
+           && (pageHeightGreaterThanScreenHeight != this.viewportExcludesVerticalMargins))) {
         if (!this.viewportMeasureCallback) {
           this.viewportMeasureCallback = setTimeout(function() {
             this.viewportMeasureCallback = null;
 
             let viewport = this.getViewport();
             if (Math.abs((viewport.pageRight - viewport.pageLeft)
                            - this.lastPageSizeAfterViewportChange.width) >= 0.5 ||
                 Math.abs((viewport.pageBottom - viewport.pageTop)
                            - this.lastPageSizeAfterViewportChange.height) >= 0.5) {
               this.updateViewportSize(gScreenWidth);
             }
           }.bind(this), kViewportRemeasureThrottle);
         }
+      } else if (this.viewportMeasureCallback) {
+        // If the page changed size twice since we last measured the viewport and
+        // the latest size change reveals we don't need to remeasure, cancel any
+        // pending remeasure.
+        clearTimeout(this.viewportMeasureCallback);
+        this.viewportMeasureCallback = null;
       }
     }
   },
 
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "DOMContentLoaded": {
         let target = aEvent.originalTarget;
@@ -4662,17 +4681,17 @@ var FormAssistant = {
         if (aData == "NOTHING") {
           // only look for input elements, not contentEditable or multiline text areas
           let focused = BrowserApp.getFocusedInput(BrowserApp.selectedBrowser, true);
           if (!focused)
             break;
 
           if (this._showValidationMessage(focused))
             break;
-          this._showAutoCompleteSuggestions(focused);
+          this._showAutoCompleteSuggestions(focused, function () {});
         } else {
           // temporarily hide the form assist popup while we're panning or zooming the page
           this._hideFormAssistPopup();
         }
         break;
       case "FormAssist:AutoComplete":
         if (!this._currentInputElement)
           break;
@@ -4732,32 +4751,43 @@ var FormAssistant = {
       case "click":
         currentElement = aEvent.target;
 
         // Prioritize a form validation message over autocomplete suggestions
         // when the element is first focused (a form validation message will
         // only be available if an invalid form was submitted)
         if (this._showValidationMessage(currentElement))
           break;
-        if (!this._showAutoCompleteSuggestions(currentElement))
-          this._hideFormAssistPopup();
+
+        let checkResultsClick = hasResults => {
+          if (!hasResults) {
+            this._hideFormAssistPopup();
+          }
+        };
+
+        this._showAutoCompleteSuggestions(currentElement, checkResultsClick);
         break;
 
       case "input":
         currentElement = aEvent.target;
 
         // Since we can only show one popup at a time, prioritze autocomplete
         // suggestions over a form validation message
-        if (this._showAutoCompleteSuggestions(currentElement))
-          break;
-        if (this._showValidationMessage(currentElement))
-          break;
-
-        // If we're not showing autocomplete suggestions, hide the form assist popup
-        this._hideFormAssistPopup();
+        let checkResultsInput = hasResults => {
+          if (hasResults)
+            return;
+
+          if (!this._showValidationMessage(currentElement))
+            return;
+
+          // If we're not showing autocomplete suggestions, hide the form assist popup
+          this._hideFormAssistPopup();
+        };
+
+        this._showAutoCompleteSuggestions(currentElement, checkResultsInput);
         break;
 
       // Reset invalid submit state on each pageshow
       case "pageshow":
         let target = aEvent.originalTarget;
         let selectedDocument = BrowserApp.selectedBrowser.contentDocument;
         if (target == selectedDocument || target.ownerDocument == selectedDocument)
           this._invalidSubmit = false;
@@ -4771,37 +4801,41 @@ var FormAssistant = {
         (aElement.hasAttribute("autocomplete") &&
          aElement.getAttribute("autocomplete").toLowerCase() == "off"))
       return false;
 
     return true;
   },
 
   // Retrieves autocomplete suggestions for an element from the form autocomplete service.
-  _getAutoCompleteSuggestions: function _getAutoCompleteSuggestions(aSearchString, aElement) {
+  // aCallback(array_of_suggestions) is called when results are available.
+  _getAutoCompleteSuggestions: function _getAutoCompleteSuggestions(aSearchString, aElement, aCallback) {
     // Cache the form autocomplete service for future use
     if (!this._formAutoCompleteService)
       this._formAutoCompleteService = Cc["@mozilla.org/satchel/form-autocomplete;1"].
                                       getService(Ci.nsIFormAutoComplete);
 
-    let results = this._formAutoCompleteService.autoCompleteSearch(aElement.name || aElement.id,
-                                                                   aSearchString, aElement, null);
-    let suggestions = [];
-    for (let i = 0; i < results.matchCount; i++) {
-      let value = results.getValueAt(i);
-
-      // Do not show the value if it is the current one in the input field
-      if (value == aSearchString)
-        continue;
-
-      // Supply a label and value, since they can differ for datalist suggestions
-      suggestions.push({ label: value, value: value });
-    }
-
-    return suggestions;
+    let resultsAvailable = function (results) {
+      let suggestions = [];
+      for (let i = 0; i < results.matchCount; i++) {
+        let value = results.getValueAt(i);
+
+        // Do not show the value if it is the current one in the input field
+        if (value == aSearchString)
+          continue;
+
+        // Supply a label and value, since they can differ for datalist suggestions
+        suggestions.push({ label: value, value: value });
+        aCallback(suggestions);
+      }
+    };
+
+    this._formAutoCompleteService.autoCompleteSearchAsync(aElement.name || aElement.id,
+                                                          aSearchString, aElement, null,
+                                                          resultsAvailable);
   },
 
   /**
    * (Copied from mobile/xul/chrome/content/forms.js)
    * This function is similar to getListSuggestions from
    * components/satchel/src/nsInputListAutoComplete.js but sadly this one is
    * used by the autocomplete.xml binding which is not in used in fennec
    */
@@ -4828,50 +4862,57 @@ var FormAssistant = {
         continue;
       suggestions.push({ label: label, value: item.value });
     }
 
     return suggestions;
   },
 
   // Retrieves autocomplete suggestions for an element from the form autocomplete service
-  // and sends the suggestions to the Java UI, along with element position data.
-  // Returns true if there are suggestions to show, false otherwise.
-  _showAutoCompleteSuggestions: function _showAutoCompleteSuggestions(aElement) {
-    if (!this._isAutoComplete(aElement))
-      return false;
+  // and sends the suggestions to the Java UI, along with element position data. As
+  // autocomplete queries are asynchronous, calls aCallback when done with a true
+  // argument if results were found and false if no results were found.
+  _showAutoCompleteSuggestions: function _showAutoCompleteSuggestions(aElement, aCallback) {
+    if (!this._isAutoComplete(aElement)) {
+      aCallback(false);
+      return;
+    }
 
     // Don't display the form auto-complete popup after the user starts typing
     // to avoid confusing somes IME. See bug 758820 and bug 632744.
     if (this._isBlocklisted && aElement.value.length > 0) {
-      return false;
-    }
-
-    let autoCompleteSuggestions = this._getAutoCompleteSuggestions(aElement.value, aElement);
-    let listSuggestions = this._getListSuggestions(aElement);
-
-    // On desktop, we show datalist suggestions below autocomplete suggestions,
-    // without duplicates removed.
-    let suggestions = autoCompleteSuggestions.concat(listSuggestions);
-
-    // Return false if there are no suggestions to show
-    if (!suggestions.length)
-      return false;
-
-    sendMessageToJava({
-      type:  "FormAssist:AutoComplete",
-      suggestions: suggestions,
-      rect: ElementTouchHelper.getBoundingContentRect(aElement)
-    });
-
-    // Keep track of input element so we can fill it in if the user
-    // selects an autocomplete suggestion
-    this._currentInputElement = aElement;
-
-    return true;
+      aCallback(false);
+      return;
+    }
+
+    let resultsAvailable = autoCompleteSuggestions => {
+      // On desktop, we show datalist suggestions below autocomplete suggestions,
+      // without duplicates removed.
+      let listSuggestions = this._getListSuggestions(aElement);
+      let suggestions = autoCompleteSuggestions.concat(listSuggestions);
+
+      // Return false if there are no suggestions to show
+      if (!suggestions.length) {
+        aCallback(false);
+        return;
+      }
+
+      sendMessageToJava({
+        type:  "FormAssist:AutoComplete",
+        suggestions: suggestions,
+        rect: ElementTouchHelper.getBoundingContentRect(aElement)
+      });
+
+      // Keep track of input element so we can fill it in if the user
+      // selects an autocomplete suggestion
+      this._currentInputElement = aElement;
+      aCallback(true);
+    };
+
+    this._getAutoCompleteSuggestions(aElement.value, aElement, resultsAvailable);
   },
 
   // Only show a validation message if the user submitted an invalid form,
   // there's a non-empty message string, and the element is the correct type
   _isValidateable: function _isValidateable(aElement) {
     if (!this._invalidSubmit ||
         !aElement.validationMessage ||
         !(aElement instanceof HTMLInputElement ||
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -340,16 +340,17 @@
 @BINPATH@/components/nsContentDispatchChooser.js
 @BINPATH@/components/nsHandlerService.manifest
 @BINPATH@/components/nsHandlerService.js
 @BINPATH@/components/nsWebHandlerApp.manifest
 @BINPATH@/components/nsWebHandlerApp.js
 @BINPATH@/components/satchel.manifest
 @BINPATH@/components/nsFormAutoComplete.js
 @BINPATH@/components/nsFormHistory.js
+@BINPATH@/components/FormHistoryStartup.js
 @BINPATH@/components/nsInputListAutoComplete.js
 @BINPATH@/components/contentSecurityPolicy.manifest
 @BINPATH@/components/contentSecurityPolicy.js
 @BINPATH@/components/contentAreaDropListener.manifest
 @BINPATH@/components/contentAreaDropListener.js
 @BINPATH@/components/messageWakeupService.js
 @BINPATH@/components/messageWakeupService.manifest
 @BINPATH@/components/nsFilePicker.js
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -81,16 +81,19 @@ pref("dom.indexedDB.enabled", true);
 // Space to allow indexedDB databases before prompting (in MB).
 pref("dom.indexedDB.warningQuota", 50);
 
 // Whether or not Web Workers are enabled.
 pref("dom.workers.enabled", true);
 // The number of workers per domain allowed to run concurrently.
 pref("dom.workers.maxPerDomain", 20);
 
+// Whether nonzero values can be returned from performance.timing.*
+pref("dom.enable_performance", true);
+
 // Fastback caching - if this pref is negative, then we calculate the number
 // of content viewers to cache based on the amount of available memory.
 pref("browser.sessionhistory.max_total_viewers", -1);
 
 pref("ui.use_native_colors", true);
 pref("ui.click_hold_context_menus", false);
 pref("browser.display.use_document_fonts",  1);  // 0 = never, 1 = quick, 2 = always
 pref("browser.display.use_document_colors", true);
@@ -3267,17 +3270,17 @@ pref("font.name.sans-serif.ja", "Open Sa
 pref("font.name.monospace.ja", "MotoyaLMaru");
 pref("font.name-list.sans-serif.ja", "Open Sans, Roboto, Droid Sans, MotoyaLMaru, MotoyaLCedar, Droid Sans Japanese");
 pref("font.name-list.monospace.ja", "MotoyaLMaru, MotoyaLCedar, Droid Sans Mono");
 
 pref("font.name.serif.ko", "Charis SIL Compact");
 pref("font.name.sans-serif.ko", "Open Sans");
 pref("font.name.monospace.ko", "Droid Sans Mono");
 pref("font.name-list.serif.ko", "HYSerif");
-pref("font.name-list.sans-serif.ko", "SmartGothic, DroidSansFallback, Droid Sans Fallback");
+pref("font.name-list.sans-serif.ko", "SmartGothic, NanumGothic, DroidSansFallback, Droid Sans Fallback");
 
 pref("font.name.serif.th", "Charis SIL Compact");
 pref("font.name.sans-serif.th", "Open Sans");
 pref("font.name.monospace.th", "Droid Sans Mono");
 pref("font.name-list.sans-serif.th", "Droid Sans Thai, Open Sans, Droid Sans");
 
 pref("font.name.serif.tr", "Charis SIL Compact");
 pref("font.name.sans-serif.tr", "Open Sans");
--- a/netwerk/base/src/nsAutodialWin.cpp
+++ b/netwerk/base/src/nsAutodialWin.cpp
@@ -38,18 +38,18 @@ static PRLogModuleInfo* gLog = nullptr;
 
 // Don't try to dial again within a few seconds of when user pressed cancel.
 #define NO_RETRY_PERIOD_SEC 5
 PRIntervalTime nsAutodial::mDontRetryUntil = 0;
 
 // ctor. 
 nsAutodial::nsAutodial()
 :   mAutodialBehavior(AUTODIAL_DEFAULT),
-    mNumRASConnectionEntries(0),
-    mAutodialServiceDialingLocation(-1)
+    mAutodialServiceDialingLocation(-1),
+    mNumRASConnectionEntries(0)
 {
     // Initializations that can be made again since RAS OS settings can 
     // change.
     Init();
 }
 
 // dtor
 nsAutodial::~nsAutodial()
--- a/netwerk/base/src/nsSocketTransportService2.cpp
+++ b/netwerk/base/src/nsSocketTransportService2.cpp
@@ -942,18 +942,16 @@ void
 nsSocketTransportService::ProbeMaxCount()