Merge inbound to m-c.
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 08 Apr 2014 18:17:58 -0400
changeset 177591 5811efc11011e9569fc75a314ae86b04dc287563
parent 177535 0701321143880d66f8176ddac61ab42c4a4909f4 (current diff)
parent 177590 9f6c65c78caded7bb957fdf204d41a617f03043b (diff)
child 177592 43cd629879c28598037145b5924603eaeb4b9cbd
child 177620 2eec85fb423ad7be1c37ea039cf4828ee2dc3dc5
child 177660 27fd61f01ff72ad55f40748be2c7a26c668e7bef
child 177673 4853301561bf6833ae1dcfa7cd53ebe203ef3122
push id26556
push userryanvm@gmail.com
push dateTue, 08 Apr 2014 22:16:57 +0000
treeherdermozilla-central@5811efc11011 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone31.0a1
first release with
nightly linux32
5811efc11011 / 31.0a1 / 20140409030203 / files
nightly linux64
5811efc11011 / 31.0a1 / 20140409030203 / files
nightly mac
5811efc11011 / 31.0a1 / 20140409030203 / files
nightly win32
5811efc11011 / 31.0a1 / 20140409030203 / files
nightly win64
5811efc11011 / 31.0a1 / 20140409030203 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge inbound to m-c.
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -917,21 +917,57 @@ chatbox:-moz-full-screen-ancestor > .cha
 }
 
 /* Give this menupopup an arrow panel styling */
 #BMB_bookmarksPopup {
   -moz-appearance: none;
   -moz-binding: url("chrome://browser/content/places/menu.xml#places-popup-arrow");
   background: transparent;
   border: none;
-  transition: opacity 300ms;
+  transform: scale(.7);
+  opacity: 0;
+  transition-property: transform, opacity;
+  transition-duration: 0.15s;
+  transition-timing-function: ease;
   /* The popup inherits -moz-image-region from the button, must reset it */
   -moz-image-region: auto;
 }
 
+#BMB_bookmarksPopup[animate="open"] {
+  transform: none;
+  opacity: 1.0;
+}
+
+#BMB_bookmarksPopup[arrowposition="after_start"] {
+  transform-origin: 20px top;
+}
+
+#BMB_bookmarksPopup[arrowposition="after_end"] {
+  transform-origin: calc(100% - 20px) top;
+}
+
+#BMB_bookmarksPopup[arrowposition="before_start"] {
+  transform-origin: 20px bottom;
+}
+
+#BMB_bookmarksPopup[arrowposition="before_end"] {
+  transform-origin: calc(100% - 20px) bottom;
+}
+
+#BMB_bookmarksPopup[arrowposition="after_start"][animate="cancel"],
+#BMB_bookmarksPopup[arrowposition="before_end"][animate="cancel"] {
+  transform: scale(.7) skew(30deg, 20deg);
+}
+
+#BMB_bookmarksPopup[arrowposition="after_end"][animate="cancel"],
+#BMB_bookmarksPopup[arrowposition="before_start"][animate="cancel"] {
+  transform: scale(.7) skew(-30deg, -20deg);
+}
+
+
 /* Customize mode */
 #navigator-toolbox,
 #browser-bottombox,
 #content-deck {
   transition-property: margin-left, margin-right;
   transition-duration: 200ms;
   transition-timing-function: linear;
 }
--- a/browser/base/content/test/general/browser_bug553455.js
+++ b/browser/base/content/test/general/browser_bug553455.js
@@ -874,20 +874,23 @@ function test() {
   Services.prefs.setBoolPref("extensions.logging.enabled", true);
   Services.prefs.setBoolPref("extensions.strictCompatibility", true);
 
   Services.obs.addObserver(XPInstallObserver, "addon-install-started", false);
   Services.obs.addObserver(XPInstallObserver, "addon-install-blocked", false);
   Services.obs.addObserver(XPInstallObserver, "addon-install-failed", false);
   Services.obs.addObserver(XPInstallObserver, "addon-install-complete", false);
 
+  PopupNotifications.transitionsEnabled = false;
+
   registerCleanupFunction(function() {
     // Make sure no more test parts run in case we were timed out
     TESTS = [];
     PopupNotifications.panel.removeEventListener("popupshown", check_notification, false);
+    PopupNotifications.transitionsEnabled = true;
 
     AddonManager.getAllInstalls(function(aInstalls) {
       aInstalls.forEach(function(aInstall) {
         aInstall.cancel();
       });
     });
 
     Services.prefs.clearUserPref("extensions.logging.enabled");
--- a/browser/base/content/test/general/browser_customize_popupNotification.js
+++ b/browser/base/content/test/general/browser_customize_popupNotification.js
@@ -7,21 +7,24 @@ function test() {
   waitForExplicitFinish();
   let newWin = OpenBrowserWindow();
   whenDelayedStartupFinished(newWin, function () {
     // Remove the URL bar
     newWin.gURLBar.parentNode.removeChild(newWin.gURLBar);
 
     waitForFocus(function () {
       let PN = newWin.PopupNotifications;
+      PN.transitionsEnabled = false;
       try {
         let notification = PN.show(newWin.gBrowser.selectedBrowser, "some-notification", "Some message");
         ok(notification, "showed the notification");
         ok(PN.isPanelOpen, "panel is open");
         is(PN.panel.anchorNode, newWin.gBrowser.selectedTab, "notification is correctly anchored to the tab");
+        PN.panel.hidePopup();
       } catch (ex) {
         ok(false, "threw exception: " + ex);
       }
+      PN.transitionsEnabled = true;
       newWin.close();
       finish();
     }, newWin);
   });
 }
--- a/browser/base/content/test/general/browser_popupNotification.js
+++ b/browser/base/content/test/general/browser_popupNotification.js
@@ -3,27 +3,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 function test() {
   waitForExplicitFinish();
 
   ok(PopupNotifications, "PopupNotifications object exists");
   ok(PopupNotifications.panel, "PopupNotifications panel exists");
 
+  // Disable transitions as they slow the test down and we want to click the
+  // mouse buttons in a predictable location.
+  PopupNotifications.transitionsEnabled = false;
+
   registerCleanupFunction(cleanUp);
 
   runNextTest();
 }
 
 function cleanUp() {
   for (var topic in gActiveObservers)
     Services.obs.removeObserver(gActiveObservers[topic], topic);
   for (var eventName in gActiveListeners)
     PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false);
   PopupNotifications.buttonDelay = PREF_SECURITY_DELAY_INITIAL;
+  PopupNotifications.transitionsEnabled = true;
 }
 
 const PREF_SECURITY_DELAY_INITIAL = Services.prefs.getIntPref("security.notification_enable_delay");
 
 var gActiveListeners = {};
 var gActiveObservers = {};
 var gShownState = {};
 
--- a/browser/components/customizableui/content/panelUI.inc.xul
+++ b/browser/components/customizableui/content/panelUI.inc.xul
@@ -2,16 +2,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/. -->
 
 <panel id="PanelUI-popup"
        role="group"
        type="arrow"
        hidden="true"
        flip="slide"
+       animate="false"
        position="bottomcenter topright"
        noautofocus="true">
   <panelmultiview id="PanelUI-multiView" mainViewId="PanelUI-mainView">
     <panelview id="PanelUI-mainView" context="customizationPanelContextMenu">
       <vbox id="PanelUI-contents-scroller">
         <vbox id="PanelUI-contents" class="panelUI-grid"/>
       </vbox>
 
--- a/browser/components/customizableui/test/browser_884402_customize_from_overflow.js
+++ b/browser/components/customizableui/test/browser_884402_customize_from_overflow.js
@@ -1,22 +1,26 @@
 "use strict";
 
 let overflowPanel = document.getElementById("widget-overflow");
 
 const isOSX = (Services.appinfo.OS === "Darwin");
 
 let originalWindowWidth;
 registerCleanupFunction(function() {
+  overflowPanel.removeAttribute("animate");
   window.resizeTo(originalWindowWidth, window.outerHeight);
 });
 
 // Right-click on an item within the overflow panel should
 // show a context menu with options to move it.
 add_task(function() {
+
+  overflowPanel.setAttribute("animate", "false");
+
   originalWindowWidth = window.outerWidth;
   let navbar = document.getElementById(CustomizableUI.AREA_NAVBAR);
   ok(!navbar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
   let oldChildCount = navbar.customizationTarget.childElementCount;
   window.resizeTo(400, window.outerHeight);
 
   yield waitForCondition(() => navbar.hasAttribute("overflowing"));
   ok(navbar.hasAttribute("overflowing"), "Should have an overflowing toolbar.");
--- a/browser/components/places/content/menu.xml
+++ b/browser/components/places/content/menu.xml
@@ -521,16 +521,19 @@
             return;
           }
 
           var container = document.getAnonymousElementByAttribute(this, "anonid", "container");
           var arrowbox = document.getAnonymousElementByAttribute(this, "anonid", "arrowbox");
 
           var position = this.alignmentPosition;
           var offset = this.alignmentOffset;
+
+          this.setAttribute("arrowposition", position);
+
           // if this panel has a "sliding" arrow, we may have previously set margins...
           arrowbox.style.removeProperty("transform");
           if (position.indexOf("start_") == 0 || position.indexOf("end_") == 0) {
             container.orient = "horizontal";
             arrowbox.orient = "vertical";
             if (position.indexOf("_after") > 0) {
               arrowbox.pack = "end";
             } else {
@@ -573,16 +576,17 @@
           arrow.hidden = false;
         ]]></body>
       </method>
     </implementation>
 
     <handlers>
       <handler event="popupshowing" phase="target"><![CDATA[
         this.adjustArrowPosition();
+        this.setAttribute("animate", "open");
       ]]></handler>
       <handler event="popupshown" phase="target"><![CDATA[
         this.setAttribute("panelopen", "true");
         let disablePointerEvents;
         if (!this.hasAttribute("disablepointereventsfortransition")) {
           let container = document.getAnonymousElementByAttribute(this, "anonid", "container");
           let cs = getComputedStyle(container);
           let transitionProp = cs.transitionProperty;
@@ -599,17 +603,21 @@
         }
       ]]></handler>
       <handler event="transitionend"><![CDATA[
         if (event.originalTarget.getAttribute("anonid") == "container" &&
             event.propertyName == "transform") {
           this.style.removeProperty("pointer-events");
         }
       ]]></handler>
+      <handler event="popuphiding" phase="target"><![CDATA[
+        this.setAttribute("animate", "cancel");
+      ]]></handler>
       <handler event="popuphidden" phase="target"><![CDATA[
         this.removeAttribute("panelopen");
         if (this.getAttribute("disablepointereventsfortransition") == "true") {
           this.style.pointerEvents = 'none';
         }
+        this.removeAttribute("animate");
       ]]></handler>
     </handlers>
   </binding>
 </bindings>
--- a/browser/components/places/tests/browser/browser_toolbarbutton_menu_context.js
+++ b/browser/components/places/tests/browser/browser_toolbarbutton_menu_context.js
@@ -12,27 +12,29 @@ add_task(function testPopup() {
   CustomizableUI.addWidgetToArea("bookmarks-menu-button", CustomizableUI.AREA_PANEL);
   CustomizableUI.addWidgetToArea("bookmarks-menu-button", CustomizableUI.AREA_NAVBAR, pos);
   info("Checking popup context menu after moving the bookmarks button");
   yield checkPopupContextMenu();
 });
 
 function* checkPopupContextMenu() {
   let dropmarker = document.getAnonymousElementByAttribute(bookmarksMenuButton, "anonid", "dropmarker");
+  BMB_menuPopup.setAttribute("style", "transition: none;");
   let popupShownPromise = onPopupEvent(BMB_menuPopup, "shown");
   EventUtils.synthesizeMouseAtCenter(dropmarker, {});
   info("Waiting for bookmarks menu to be shown.");
   yield popupShownPromise;
   let contextMenuShownPromise = onPopupEvent(contextMenu, "shown");
   EventUtils.synthesizeMouseAtCenter(BMB_showAllBookmarks, {type: "contextmenu", button: 2 });
   info("Waiting for context menu on bookmarks menu to be shown.");
   yield contextMenuShownPromise;
   ok(!newBookmarkItem.hasAttribute("disabled"), "New bookmark item shouldn't be disabled");
   let contextMenuHiddenPromise = onPopupEvent(contextMenu, "hidden");
   contextMenu.hidePopup();
+  BMB_menuPopup.removeAttribute("style");
   info("Waiting for context menu on bookmarks menu to be hidden.");
   yield contextMenuHiddenPromise;
   let popupHiddenPromise = onPopupEvent(BMB_menuPopup, "hidden");
   // Can't use synthesizeMouseAtCenter because the dropdown panel is in the way
   EventUtils.synthesizeKey("VK_ESCAPE", {});
   info("Waiting for bookmarks menu to be hidden.");
   yield popupHiddenPromise;
 }
--- a/browser/devtools/shared/widgets/Tooltip.js
+++ b/browser/devtools/shared/widgets/Tooltip.js
@@ -98,16 +98,17 @@ let PanelFactory = {
    * @param {OptionsStore} options
    *        An options store to get some configuration from
    */
   get: function(doc, options) {
     // Create the tooltip
     let panel = doc.createElement("panel");
     panel.setAttribute("hidden", true);
     panel.setAttribute("ignorekeys", true);
+    panel.setAttribute("animate", false);
 
     panel.setAttribute("consumeoutsideclicks", options.get("consumeOutsideClick"));
     panel.setAttribute("noautofocus", options.get("noAutoFocus"));
     panel.setAttribute("type", "arrow");
     panel.setAttribute("level", "top");
 
     panel.setAttribute("class", "devtools-tooltip theme-tooltip-panel");
     doc.querySelector("window").appendChild(panel);
--- a/browser/modules/test/browser_SignInToWebsite.js
+++ b/browser/modules/test/browser_SignInToWebsite.js
@@ -271,16 +271,18 @@ function test() {
   try {
     Components.utils.import("resource:///modules/SignInToWebsite.jsm", sitw);
   } catch (ex) {
     ok(true, "Skip the test since SignInToWebsite.jsm isn't packaged outside outside mozilla-central");
     finish();
     return;
   }
 
+  PopupNotifications.transitionsEnabled = false;
+
   registerCleanupFunction(cleanUp);
 
   ok(sitw.SignInToWebsiteUX, "SignInToWebsiteUX object exists");
   if (!Services.prefs.getBoolPref("dom.identity.enabled")) {
     // If the pref isn't enabled then init wasn't called so do that for the test.
     sitw.SignInToWebsiteUX.init();
   }
 
@@ -308,16 +310,18 @@ function resetState() {
   IdentityService.reset();
 }
 
 // Cleanup after all tests
 function cleanUp() {
   info("cleanup");
   resetState();
 
+  PopupNotifications.transitionsEnabled = true;
+
   for (let topic in gActiveObservers)
     Services.obs.removeObserver(gActiveObservers[topic], topic);
   for (let eventName in gActiveListeners)
     PopupNotifications.panel.removeEventListener(eventName, gActiveListeners[eventName], false);
   delete IdentityService.RP._rpFlows[outerWinId];
 
   // Put the JSM functions back to how they were
   IdentityService.IDP.setAuthenticationFlow = window.setAuthenticationFlow;
--- a/browser/modules/test/browser_UITour3.js
+++ b/browser/modules/test/browser_UITour3.js
@@ -18,16 +18,20 @@ function test() {
 let tests = [
   function test_info_icon(done) {
     let popup = document.getElementById("UITourTooltip");
     let title = document.getElementById("UITourTooltipTitle");
     let desc = document.getElementById("UITourTooltipDescription");
     let icon = document.getElementById("UITourTooltipIcon");
     let buttons = document.getElementById("UITourTooltipButtons");
 
+    // Disable the animation to prevent the mouse clicks from hitting the main
+    // window during the transition instead of the buttons in the popup.
+    popup.setAttribute("animate", "false");
+
     popup.addEventListener("popupshown", function onPopupShown() {
       popup.removeEventListener("popupshown", onPopupShown);
 
       is(title.textContent, "a title", "Popup should have correct title");
       is(desc.textContent, "some text", "Popup should have correct description text");
 
       let imageURL = getRootDirectory(gTestPath) + "image.png";
       imageURL = imageURL.replace("chrome://mochitests/content/", "https://example.com/");
@@ -145,16 +149,17 @@ let tests = [
   function test_info_target_callback(done) {
     let popup = document.getElementById("UITourTooltip");
     popup.addEventListener("popupshown", function onPopupShown() {
       popup.removeEventListener("popupshown", onPopupShown);
       PanelUI.show().then(() => {
         is(gContentWindow.callbackResult, "target", "target callback called");
         is(gContentWindow.callbackData.target, "appMenu", "target callback was from the appMenu");
         is(gContentWindow.callbackData.type, "popupshown", "target callback was from the mousedown");
+        popup.removeAttribute("animate");
         done();
       });
     });
 
     let infoOptions = gContentWindow.makeInfoOptions();
     gContentAPI.showInfo("appMenu", "I want to know when the target is clicked", "*click*", null, null, infoOptions);
   },
 ];
--- a/configure.in
+++ b/configure.in
@@ -4890,17 +4890,17 @@ fi
 AC_SUBST(MOZ_ENABLE_LIBPROXY)
 AC_SUBST(MOZ_LIBPROXY_CFLAGS)
 AC_SUBST(MOZ_LIBPROXY_LIBS)
 
 dnl ========================================================
 dnl = GNOME component (mozgnome)
 dnl ========================================================
 
-if test "$MOZ_ENABLE_GTK2"
+if test "$MOZ_ENABLE_GTK"
 then
     MOZ_ENABLE_GNOME_COMPONENT=1
 fi
 AC_SUBST(MOZ_ENABLE_GNOME_COMPONENT)
 
 dnl ========================================================
 dnl = libgnomeui support module
 dnl ========================================================
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -3659,27 +3659,17 @@ nsXMLHttpRequest::GetInterface(const nsI
   }
 
   return QueryInterface(aIID, aResult);
 }
 
 JS::Value
 nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aRv)
 {
-  const nsID* iid = aIID->GetID();
-  nsCOMPtr<nsISupports> result;
-  JS::Rooted<JS::Value> v(aCx, JSVAL_NULL);
-  aRv = GetInterface(*iid, getter_AddRefs(result));
-  NS_ENSURE_FALSE(aRv.Failed(), JSVAL_NULL);
-
-  JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
-  JSAutoCompartment ac(aCx, wrapper);
-  JS::Rooted<JSObject*> global(aCx, JS_GetGlobalForObject(aCx, wrapper));
-  aRv = nsContentUtils::WrapNative(aCx, global, result, iid, &v);
-  return aRv.Failed() ? JSVAL_NULL : v;
+  return dom::GetInterface(aCx, this, aIID, aRv);
 }
 
 nsXMLHttpRequestUpload*
 nsXMLHttpRequest::Upload()
 {
   if (!mUpload) {
     mUpload = new nsXMLHttpRequestUpload(this);
   }
--- a/content/html/document/test/test_bug512367.html
+++ b/content/html/document/test/test_bug512367.html
@@ -20,18 +20,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script type="application/javascript">
 
 var frame = document.getElementById("i");
 
 SimpleTest.waitForExplicitFinish();
 addLoadEvent(function() {
   var viewer =
-    SpecialPowers.wrap(frame.contentWindow
-                 .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor))
+    SpecialPowers.wrap(frame.contentWindow)
+                 .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
                  .getInterface(SpecialPowers.Ci.nsIWebNavigation)
                  .QueryInterface(SpecialPowers.Ci.nsIDocShell)
                  .contentViewer
                  .QueryInterface(SpecialPowers.Ci.nsIMarkupDocumentViewer);
 
   viewer.fullZoom = 1.5;
 
   setTimeout(function() {
--- a/content/media/webspeech/synth/test/common.js
+++ b/content/media/webspeech/synth/test/common.js
@@ -1,10 +1,8 @@
-SpecialPowers.setBoolPref("media.webspeech.synth.enabled", true);
-
 var gSpeechRegistry = SpecialPowers.Cc["@mozilla.org/synth-voice-registry;1"]
   .getService(SpecialPowers.Ci.nsISynthVoiceRegistry);
 
 var gAddedVoices = [];
 
 function SpeechTaskCallback(onpause, onresume, oncancel) {
   this.onpause = onpause;
   this.onresume = onresume;
@@ -165,17 +163,16 @@ function synthCleanup() {
     is(voicesAfter, voicesBefore - toRemove, "Successfully removed test voices");
   } else {
     // XXX: It would be nice to check here that the child gets the voice
     // removed update, but alas, it is aynchronous.
     var mm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
       .getService(SpecialPowers.Ci.nsISyncMessageSender);
     mm.sendSyncMessage('test:SpeechSynthesis:ipcSynthCleanup');
   }
-  SpecialPowers.clearUserPref("media.webspeech.synth.enabled");
 }
 
 function synthTestQueue(aTestArgs, aEndFunc) {
   var utterances = [];
   for (var i in aTestArgs) {
     var uargs = aTestArgs[i][0];
     var u = new SpeechSynthesisUtterance(uargs.text);
 
copy from content/media/webspeech/synth/test/test_setup.html
copy to content/media/webspeech/synth/test/file_setup.html
--- a/content/media/webspeech/synth/test/test_setup.html
+++ b/content/media/webspeech/synth/test/file_setup.html
@@ -1,19 +1,23 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=525444
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 525444: Web Speech API check all classes are present</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript">
+    window.SimpleTest = parent.SimpleTest;
+    window.is = parent.is;
+    window.isnot = parent.isnot;
+    window.ok = parent.ok;
+  </script>
   <script type="application/javascript" src="common.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
@@ -67,12 +71,14 @@ var voices2 = speechSynthesis.getVoices(
 
 ok(voices1.length == voices2.length, "Voice count matches");
 
 for (var i in voices1) {
   ok(voices1[i] == voices2[i], "Voice instance matches");
 }
 
 synthCleanup();
+
+SimpleTest.finish();
 </script>
 </pre>
 </body>
 </html>
copy from content/media/webspeech/synth/test/test_speech_queue.html
copy to content/media/webspeech/synth/test/file_speech_queue.html
--- a/content/media/webspeech/synth/test/test_speech_queue.html
+++ b/content/media/webspeech/synth/test/file_speech_queue.html
@@ -1,33 +1,35 @@
 <!DOCTYPE HTML>
 <html lang="en-US">
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=525444
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 525444: Web Speech API, check speech synth queue</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript">
+    window.SimpleTest = parent.SimpleTest;
+    window.is = parent.is;
+    window.isnot = parent.isnot;
+    window.ok = parent.ok;
+  </script>
   <script type="application/javascript" src="common.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525444">Mozilla Bug 525444</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
-SimpleTest.waitForExplicitFinish();
-
 var englishJamaican = synthAddVoice('TestSpeechServiceNoAudio',
                                     'Bob Marley', 'en-JM', true);
 var englishBritish = synthAddVoice('TestSpeechServiceNoAudio',
                                    'Amy Winehouse', 'en-GB', true);
 var englishCanadian = synthAddVoice('TestSpeechServiceNoAudio',
                                     'Leonard Cohen', 'en-CA', true);
 var frenchCanadian = synthAddVoice('TestSpeechServiceNoAudio',
                                    'Celine Dion', 'fr-CA', true);
copy from content/media/webspeech/synth/test/test_speech_simple.html
copy to content/media/webspeech/synth/test/file_speech_simple.html
--- a/content/media/webspeech/synth/test/test_speech_simple.html
+++ b/content/media/webspeech/synth/test/file_speech_simple.html
@@ -1,33 +1,36 @@
 <!DOCTYPE HTML>
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=650295
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 650295: Web Speech API check all classes are present</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript">
+    window.SimpleTest = parent.SimpleTest;
+    window.info = parent.info;
+    window.is = parent.is;
+    window.isnot = parent.isnot;
+    window.ok = parent.ok;
+  </script>
   <script type="application/javascript" src="common.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a>
 <p id="display"></p>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
-SimpleTest.waitForExplicitFinish();
-
 synthAddVoice('TestSpeechServiceWithAudio', 'Male 1', 'en-GB', true);
 
 var gotStartEvent = false;
 var gotBoundaryEvent = false;
 var utterance = new SpeechSynthesisUtterance("Hello, world!");
 utterance.addEventListener('start', function(e) {
   ok(speechSynthesis.speaking, "speechSynthesis is speaking.");
   ok(!speechSynthesis.pending, "speechSynthesis has no other utterances queued.");
--- a/content/media/webspeech/synth/test/mochitest.ini
+++ b/content/media/webspeech/synth/test/mochitest.ini
@@ -1,9 +1,13 @@
 [DEFAULT]
 skip-if = e10s
-support-files = common.js
+support-files =
+  common.js
+  file_setup.html
+  file_speech_queue.html
+  file_speech_simple.html
 
 [test_setup.html]
 [test_speech_queue.html]
 skip-if = buildapp == 'b2g' # b2g(Test timed out) b2g-debug(Test timed out) b2g-desktop(Test timed out)
 [test_speech_simple.html]
 skip-if = buildapp == 'b2g' # b2g(Test timed out) b2g-debug(Test timed out) b2g-desktop(Test timed out)
--- a/content/media/webspeech/synth/test/test_setup.html
+++ b/content/media/webspeech/synth/test/test_setup.html
@@ -2,77 +2,31 @@
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=525444
 -->
 <head>
   <meta charset="utf-8">
   <title>Test for Bug 525444: Web Speech API check all classes are present</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a>
 <p id="display"></p>
+<iframe id="testFrame"></iframe>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
-synthAddVoice('TestSpeechServiceNoAudio', 'Bob Marley', 'en-JM', true);
-synthAddVoice('TestSpeechServiceNoAudio', 'Amy Winehouse', 'en-GB', true);
-synthAddVoice('TestSpeechServiceNoAudio', 'Leonard Cohen', 'en-CA', true);
-synthAddVoice('TestSpeechServiceNoAudio', 'Celine Dion', 'fr-CA', true);
-synthAddVoice('TestSpeechServiceNoAudio', 'Julieta Venegas', 'es-MX', true);
-
-ok(SpeechSynthesis, "SpeechSynthesis exists in global scope");
-ok(SpeechSynthesisVoice, "SpeechSynthesisVoice exists in global scope");
-ok(SpeechSynthesisEvent, "SpeechSynthesisEvent exists in global scope");
+SimpleTest.waitForExplicitFinish();
 
-// SpeechSynthesisUtterance is the only type that has a constructor
-//  and writable properties
-ok(SpeechSynthesisUtterance, "SpeechSynthesisUtterance exists in global scope");
-var ssu = new SpeechSynthesisUtterance("hello world");
-is(typeof ssu, "object", "SpeechSynthesisUtterance instance is an object");
-is(ssu.text, "hello world", "SpeechSynthesisUtterance.text is correct");
-is(ssu.volume, 1, "SpeechSynthesisUtterance.volume default is correct");
-is(ssu.rate, 1, "SpeechSynthesisUtterance.rate default is correct");
-is(ssu.pitch, 1, "SpeechSynthesisUtterance.pitch default is correct");
-ssu.lang = "he-IL";
-ssu.volume = 0.5;
-ssu.rate = 2.0;
-ssu.pitch = 1.5;
-is(ssu.lang, "he-IL", "SpeechSynthesisUtterance.lang is correct");
-is(ssu.volume, 0.5,  "SpeechSynthesisUtterance.volume is correct");
-is(ssu.rate, 2.0,  "SpeechSynthesisUtterance.rate is correct");
-is(ssu.pitch, 1.5,  "SpeechSynthesisUtterance.pitch is correct");
+SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
+                          function() { document.getElementById("testFrame").src = "file_setup.html"; });
 
-// Test for singleton instance hanging off of window.
-ok(speechSynthesis, "speechSynthesis exists in global scope");
-is(typeof speechSynthesis, "object", "speechSynthesis instance is an object");
-is(typeof speechSynthesis.speak, "function", "speechSynthesis.speak is a function");
-is(typeof speechSynthesis.cancel, "function", "speechSynthesis.cancel is a function");
-is(typeof speechSynthesis.pause, "function", "speechSynthesis.pause is a function");
-is(typeof speechSynthesis.resume, "function", "speechSynthesis.resume is a function");
-is(typeof speechSynthesis.getVoices, "function", "speechSynthesis.getVoices is a function");
-
-is(typeof speechSynthesis.pending, "boolean", "speechSynthesis.pending is a boolean");
-is(typeof speechSynthesis.speaking, "boolean", "speechSynthesis.speaking is a boolean");
-is(typeof speechSynthesis.paused, "boolean", "speechSynthesis.paused is a boolean");
-
-var voices1 = speechSynthesis.getVoices();
-var voices2 = speechSynthesis.getVoices();
-
-ok(voices1.length == voices2.length, "Voice count matches");
-
-for (var i in voices1) {
-  ok(voices1[i] == voices2[i], "Voice instance matches");
-}
-
-synthCleanup();
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webspeech/synth/test/test_speech_queue.html
+++ b/content/media/webspeech/synth/test/test_speech_queue.html
@@ -8,63 +8,26 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 525444: Web Speech API, check speech synth queue</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=525444">Mozilla Bug 525444</a>
 <p id="display"></p>
+<iframe id="testFrame"></iframe>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-var englishJamaican = synthAddVoice('TestSpeechServiceNoAudio',
-                                    'Bob Marley', 'en-JM', true);
-var englishBritish = synthAddVoice('TestSpeechServiceNoAudio',
-                                   'Amy Winehouse', 'en-GB', true);
-var englishCanadian = synthAddVoice('TestSpeechServiceNoAudio',
-                                    'Leonard Cohen', 'en-CA', true);
-var frenchCanadian = synthAddVoice('TestSpeechServiceNoAudio',
-                                   'Celine Dion', 'fr-CA', true);
-var spanishMexican = synthAddVoice('TestSpeechServiceNoAudio',
-                                   'Julieta Venegas', 'es-MX', true);
-
-synthSetDefault(englishBritish, true);
-
-synthTestQueue(
-  [[{text: "Hello, world."},
-    { uri: englishBritish }],
-   [{text: "Bonjour tout le monde .", lang: "fr", rate: 0.5, pitch: 0.75},
-    { uri: frenchCanadian, rate: 0.5, pitch: 0.75}],
-   [{text: "How are you doing?", lang: "en-GB"},
-    { rate: 1, pitch: 1, uri: englishBritish}],
-   [{text: "¡hasta mañana", lang: "es-ES"},
-    { uri: spanishMexican }]],
-  function () {
-    synthSetDefault(englishJamaican, true);
-    var test_data = [[{text: "I shot the  sheriff."},
-                      { uri: englishJamaican }]];
-    var voices = speechSynthesis.getVoices();
-    for (var i in voices) {
-      test_data.push([{text: "Hello world", voice: voices[i]},
-                      {uri: voices[i].voiceURI}]);
-    }
-
-    synthTestQueue(test_data,
-                   function () {
-                     synthCleanup();
-                     SimpleTest.finish();
-                   });
-  });
-
-
+SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
+                          function() { document.getElementById("testFrame").src = "file_speech_queue.html"; });
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/media/webspeech/synth/test/test_speech_simple.html
+++ b/content/media/webspeech/synth/test/test_speech_simple.html
@@ -8,46 +8,26 @@ https://bugzilla.mozilla.org/show_bug.cg
   <title>Test for Bug 650295: Web Speech API check all classes are present</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="application/javascript" src="common.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=650295">Mozilla Bug 650295</a>
 <p id="display"></p>
+<iframe id="testFrame"></iframe>
 <div id="content" style="display: none">
   
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 525444 **/
 
 SimpleTest.waitForExplicitFinish();
 
-synthAddVoice('TestSpeechServiceWithAudio', 'Male 1', 'en-GB', true);
-
-var gotStartEvent = false;
-var gotBoundaryEvent = false;
-var utterance = new SpeechSynthesisUtterance("Hello, world!");
-utterance.addEventListener('start', function(e) {
-  ok(speechSynthesis.speaking, "speechSynthesis is speaking.");
-  ok(!speechSynthesis.pending, "speechSynthesis has no other utterances queued.");
-  gotStartEvent = true;
-});
-
-utterance.addEventListener('end', function(e) {
-  ok(!speechSynthesis.speaking, "speechSynthesis is not speaking.");
-  ok(!speechSynthesis.pending, "speechSynthesis has no other utterances queued.");
-  ok(gotStartEvent, "Got 'start' event.");
-  info('end ' + e.elapsedTime);
-  synthCleanup();
-  SimpleTest.finish();
-});
-
-speechSynthesis.speak(utterance);
-ok(!speechSynthesis.speaking, "speechSynthesis is not speaking yet.");
-ok(speechSynthesis.pending, "speechSynthesis has an utterance queued.");
+SpecialPowers.pushPrefEnv({ set: [['media.webspeech.synth.enabled', true]] },
+                          function() { document.getElementById("testFrame").src = "file_speech_simple.html"; });
 
 </script>
 </pre>
 </body>
 </html>
--- a/content/xul/content/src/nsXULPopupListener.cpp
+++ b/content/xul/content/src/nsXULPopupListener.cpp
@@ -292,17 +292,17 @@ void
 nsXULPopupListener::ClosePopup()
 {
   if (mPopupContent) {
     // this is called when the listener is going away, so make sure that the
     // popup is hidden. Use asynchronous hiding just to be safe so we don't
     // fire events during destruction.  
     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     if (pm)
-      pm->HidePopup(mPopupContent, false, true, true);
+      pm->HidePopup(mPopupContent, false, true, true, false);
     mPopupContent = nullptr;  // release the popup
   }
 } // ClosePopup
 
 static already_AddRefed<nsIContent>
 GetImmediateChild(nsIContent* aContent, nsIAtom *aTag) 
 {
   for (nsIContent* child = aContent->GetFirstChild();
--- a/dom/apps/src/OperatorApps.jsm
+++ b/dom/apps/src/OperatorApps.jsm
@@ -137,17 +137,18 @@ this.OperatorAppsRegistry = {
 
   _copyDirectory: function(aOrg, aDst) {
     debug("copying " + aOrg + " to " + aDst);
     return aDst && Task.spawn(function() {
       try {
         let orgDir = Cc["@mozilla.org/file/local;1"]
                        .createInstance(Ci.nsIFile);
         orgDir.initWithPath(aOrg);
-        if (!orgDir.isDirectory()) {
+        if (!orgDir.exists() || !orgDir.isDirectory()) {
+          debug(aOrg + " does not exist or is not a directory");
           return;
         }
 
         let dstDir = Cc["@mozilla.org/file/local;1"]
                        .createInstance(Ci.nsIFile);
         dstDir.initWithPath(aDst);
         if (!dstDir.exists()) {
           dstDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
@@ -163,17 +164,17 @@ this.OperatorAppsRegistry = {
             dstFile.append(entry.leafName);
             if(dstFile.exists()) {
               dstFile.remove(false);
             }
 
             entry.copyTo(dstDir, entry.leafName);
           } else {
             yield this._copyDirectory(entry.path,
-                                      Path.join(aDst, entry.name));
+                                      Path.join(aDst, entry.leafName));
           }
         }
       } catch (e) {
         debug("Error copying " + aOrg + " to " + aDst + ". " + e);
       }
     }.bind(this));
   },
 
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -177,17 +177,16 @@ using namespace mozilla::dom;
 static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
 
 // NOTE: DEFAULT_SCRIPTABLE_FLAGS and DOM_DEFAULT_SCRIPTABLE_FLAGS
 //       are defined in nsIDOMClassInfo.h.
 
 #define WINDOW_SCRIPTABLE_FLAGS                                               \
  (nsIXPCScriptable::WANT_PRECREATE |                                          \
   nsIXPCScriptable::WANT_POSTCREATE |                                         \
-  nsIXPCScriptable::WANT_FINALIZE |                                           \
   nsIXPCScriptable::WANT_ENUMERATE |                                          \
   nsIXPCScriptable::DONT_ENUM_QUERY_INTERFACE |                               \
   nsIXPCScriptable::IS_GLOBAL_OBJECT |                                        \
   nsIXPCScriptable::WANT_OUTER_OBJECT)
 
 #define ARRAY_SCRIPTABLE_FLAGS                                                \
   (DOM_DEFAULT_SCRIPTABLE_FLAGS       |                                       \
    nsIXPCScriptable::WANT_GETPROPERTY |                                       \
@@ -1850,17 +1849,17 @@ NS_IMETHODIMP
 nsWindowSH::PostCreate(nsIXPConnectWrappedNative *wrapper,
                        JSContext *cx, JSObject *obj)
 {
   JS::Rooted<JSObject*> window(cx, obj);
 
 #ifdef DEBUG
   nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper));
 
-  NS_ASSERTION(sgo && sgo->GetGlobalJSObject() == nullptr,
+  NS_ASSERTION(sgo && sgo->GetGlobalJSObject() == obj,
                "Multiple wrappers created for global object!");
 #endif
 
   const NativeProperties* windowProperties =
     WindowBinding::sNativePropertyHooks->mNativeProperties.regular;
   const NativeProperties* eventTargetProperties =
     EventTargetBinding::sNativePropertyHooks->mNativeProperties.regular;
 
@@ -3395,36 +3394,16 @@ nsWindowSH::NewResolve(nsIXPConnectWrapp
     return NS_OK;
   }
 
   return nsDOMGenericSH::NewResolve(wrapper, cx, obj, id, flags, objp,
                                     _retval);
 }
 
 NS_IMETHODIMP
-nsWindowSH::Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop,
-                     JSObject *obj)
-{
-  // Since this call is virtual, the exact rooting hazard static analysis is
-  // not able to determine that it happens during finalization and should be
-  // ignored. Moreover, the analysis cannot discover and validate the
-  // potential targets of the virtual call to OnFinalize below because of the
-  // indirection through nsCOMMPtr. Thus, we annotate the analysis here so
-  // that it does not report OnFinalize as GCing with |obj| on stack.
-  JS::AutoAssertNoGC nogc;
-
-  nsCOMPtr<nsIScriptGlobalObject> sgo(do_QueryWrappedNative(wrapper));
-  NS_ENSURE_TRUE(sgo, NS_ERROR_UNEXPECTED);
-
-  sgo->OnFinalize(obj);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsWindowSH::OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
                         JSObject * obj, JSObject * *_retval)
 {
   nsGlobalWindow *origWin = nsGlobalWindow::FromWrapper(wrapper);
   nsGlobalWindow *win = origWin->GetOuterWindowInternal();
 
   if (!win) {
     // If we no longer have an outer window. No code should ever be
--- a/dom/base/nsDOMClassInfo.h
+++ b/dom/base/nsDOMClassInfo.h
@@ -265,18 +265,16 @@ public:
   NS_IMETHOD PostCreatePrototype(JSContext * cx, JSObject * proto) MOZ_OVERRIDE;
   NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj) MOZ_OVERRIDE;
   NS_IMETHOD Enumerate(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                        JSObject *obj, bool *_retval) MOZ_OVERRIDE;
   NS_IMETHOD NewResolve(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                         JSObject *obj, jsid id, uint32_t flags,
                         JSObject **objp, bool *_retval) MOZ_OVERRIDE;
-  NS_IMETHOD Finalize(nsIXPConnectWrappedNative *wrapper, JSFreeOp *fop,
-                      JSObject *obj) MOZ_OVERRIDE;
   NS_IMETHOD OuterObject(nsIXPConnectWrappedNative *wrapper, JSContext * cx,
                          JSObject * obj, JSObject * *_retval) MOZ_OVERRIDE;
 
   static bool GlobalScopePolluterNewResolve(JSContext *cx, JS::Handle<JSObject*> obj,
                                             JS::Handle<jsid> id, unsigned flags,
                                             JS::MutableHandle<JSObject*> objp);
   static bool GlobalScopePolluterGetProperty(JSContext *cx, JS::Handle<JSObject*> obj,
                                              JS::Handle<jsid> id, JS::MutableHandle<JS::Value> vp);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1366,17 +1366,16 @@ nsGlobalWindow::ShutDown()
 
 // static
 void
 nsGlobalWindow::CleanupCachedXBLHandlers(nsGlobalWindow* aWindow)
 {
   if (aWindow->mCachedXBLPrototypeHandlers &&
       aWindow->mCachedXBLPrototypeHandlers->Count() > 0) {
     aWindow->mCachedXBLPrototypeHandlers->Clear();
-    mozilla::DropJSObjects(aWindow);
   }
 }
 
 void
 nsGlobalWindow::MaybeForgiveSpamCount()
 {
   if (IsOuterWindow() &&
       IsPopupSpamWindow())
@@ -1612,27 +1611,21 @@ nsGlobalWindow::FreeInnerObjects()
   mGamepads.Clear();
 #endif
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsISupports
 //*****************************************************************************
 
-#define OUTER_WINDOW_ONLY                                                     \
-  if (IsOuterWindow()) {
-
-#define END_OUTER_WINDOW_ONLY                                                 \
-    foundInterface = 0;                                                       \
-  } else
-
 DOMCI_DATA(Window, nsGlobalWindow)
 
 // QueryInterface implementation for nsGlobalWindow
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGlobalWindow)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   // Make sure this matches the cast in nsGlobalWindow::FromWrapper()
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventTarget)
   NS_INTERFACE_MAP_ENTRY(nsIDOMWindow)
 #ifdef MOZ_B2G
   NS_INTERFACE_MAP_ENTRY(nsIDOMWindowB2G)
 #endif // MOZ_B2G
 #ifdef MOZ_WEBSPEECH
   NS_INTERFACE_MAP_ENTRY(nsISpeechSynthesisGetter)
@@ -1655,19 +1648,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(mozilla::dom::EventTarget)
   NS_INTERFACE_MAP_ENTRY(nsPIDOMWindow)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsIDOMWindowPerformance)
   NS_INTERFACE_MAP_ENTRY(nsITouchEventReceiver)
   NS_INTERFACE_MAP_ENTRY(nsIInlineEventHandlers)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(Window)
-  OUTER_WINDOW_ONLY
-    NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
-  END_OUTER_WINDOW_ONLY
 NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow)
 
 static PLDHashOperator
 MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
@@ -1717,16 +1707,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
     NS_IMPL_CYCLE_COLLECTION_DESCRIBE(nsGlobalWindow, tmp->mRefCnt.get())
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mControllers)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mArguments)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDialogArguments)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mReturnValue)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
 
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
 #endif
 
@@ -1764,26 +1755,28 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExternal)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsGlobalWindow)
   nsGlobalWindow::CleanupCachedXBLHandlers(tmp);
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mControllers)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mArguments)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDialogArguments)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mReturnValue)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
 
 #ifdef MOZ_WEBSPEECH
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mSpeechSynthesis)
 #endif
 
@@ -1823,16 +1816,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPersonalbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mScrollbars)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mExternal)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 #ifdef DEBUG
 void
 nsGlobalWindow::RiskyUnlink()
 {
   NS_CYCLE_COLLECTION_INNERNAME.Unlink(this);
 }
@@ -1852,16 +1846,17 @@ TraceXBLHandlers(nsXBLPrototypeHandler* 
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
   if (tmp->mCachedXBLPrototypeHandlers) {
     TraceData data = { aCallbacks, aClosure };
     tmp->mCachedXBLPrototypeHandlers->Enumerate(TraceXBLHandlers, &data);
   }
+  NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 bool
 nsGlobalWindow::IsBlackForCC(bool aTracingNeeded)
 {
   if (!nsCCUncollectableMarker::sGeneration) {
     return false;
   }
@@ -1897,22 +1892,22 @@ nsresult
 nsGlobalWindow::EnsureScriptEnvironment()
 {
   nsGlobalWindow* outer = GetOuterWindowInternal();
   if (!outer) {
     NS_WARNING("No outer window available!");
     return NS_ERROR_FAILURE;
   }
 
-  if (outer->mJSObject) {
+  if (outer->GetWrapperPreserveColor()) {
     return NS_OK;
   }
 
   NS_ASSERTION(!outer->GetCurrentInnerWindowInternal(),
-               "mJSObject is null, but we have an inner window?");
+               "No cached wrapper, but we have an inner window?");
 
   // If this window is a [i]frame, don't bother GC'ing when the frame's context
   // is destroyed since a GC will happen when the frameset or host document is
   // destroyed anyway.
   nsCOMPtr<nsIScriptContext> context = new nsJSContext(!IsFrame(), outer);
 
   NS_ASSERTION(!outer->mContext, "Will overwrite mContext!");
 
@@ -1937,19 +1932,50 @@ JSObject *
 nsGlobalWindow::GetGlobalJSObject()
 {
   return FastGetGlobalJSObject();
 }
 
 void
 nsGlobalWindow::TraceGlobalJSObject(JSTracer* aTrc)
 {
-  if (mJSObject) {
-    JS_CallTenuredObjectTracer(aTrc, &mJSObject, "active window global");
-  }
+  TraceWrapper(aTrc, "active window global");
+}
+
+/* static */
+JSObject*
+nsGlobalWindow::OuterObject(JSContext* aCx, JS::HandleObject aObj)
+{
+  nsGlobalWindow *origWin;
+  UNWRAP_OBJECT(Window, aObj, origWin);
+  nsGlobalWindow *win = origWin->GetOuterWindowInternal();
+
+  if (!win) {
+    // If we no longer have an outer window. No code should ever be
+    // running on a window w/o an outer, which means this hook should
+    // never be called when we have no outer. But just in case, return
+    // null to prevent leaking an inner window to code in a different
+    // window.
+    NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!");
+    return nullptr;
+  }
+
+  JS::Rooted<JSObject*> winObj(aCx, win->FastGetGlobalJSObject());
+  MOZ_ASSERT(winObj);
+
+  // Note that while |wrapper| is same-compartment with cx, the outer window
+  // might not be. If we're running script in an inactive scope and evalute
+  // |this|, the outer window is actually a cross-compartment wrapper. So we
+  // need to wrap here.
+  if (!JS_WrapObject(aCx, &winObj)) {
+    NS_WARNING("nsGlobalWindow::OuterObject shouldn't fail!");
+    return nullptr;
+  }
+
+  return winObj;
 }
 
 bool
 nsGlobalWindow::WouldReuseInnerWindow(nsIDocument *aNewDocument)
 {
   // We reuse the inner window when:
   // a. We are currently at our original document.
   // b. At least one of the following conditions are true:
@@ -2122,17 +2148,17 @@ WindowStateHolder::WindowStateHolder(nsG
   NS_PRECONDITION(aWindow, "null window");
   NS_PRECONDITION(aWindow->IsInnerWindow(), "Saving an outer window");
 
   mInnerWindowHolder = aHolder;
 
   aWindow->SuspendTimeouts();
 
   // When a global goes into the bfcache, we disable script.
-  xpc::Scriptability::Get(aWindow->mJSObject).SetDocShellAllowsScript(false);
+  xpc::Scriptability::Get(aWindow->GetWrapperPreserveColor()).SetDocShellAllowsScript(false);
 }
 
 WindowStateHolder::~WindowStateHolder()
 {
   if (mInnerWindow) {
     // This window was left in the bfcache and is now going away. We need to
     // free it up.
     // Note that FreeInnerObjects may already have been called on the
@@ -2160,17 +2186,16 @@ TreatAsRemoteXUL(nsIPrincipal* aPrincipa
  * Return the native global and an nsISupports 'holder' that can be used
  * to manage the lifetime of it.
  */
 static nsresult
 CreateNativeGlobalForInner(JSContext* aCx,
                            nsGlobalWindow* aNewInner,
                            nsIURI* aURI,
                            nsIPrincipal* aPrincipal,
-                           JS::TenuredHeap<JSObject*>& aNativeGlobal,
                            nsIXPConnectJSObjectHolder** aHolder)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aNewInner);
   MOZ_ASSERT(aNewInner->IsInnerWindow());
   MOZ_ASSERT(aPrincipal);
   MOZ_ASSERT(aHolder);
 
@@ -2188,30 +2213,25 @@ CreateNativeGlobalForInner(JSContext* aC
   nsIXPConnect* xpc = nsContentUtils::XPConnect();
 
   // Determine if we need the Components object.
   bool needComponents = nsContentUtils::IsSystemPrincipal(aPrincipal) ||
                         TreatAsRemoteXUL(aPrincipal);
   uint32_t flags = needComponents ? 0 : nsIXPConnect::OMIT_COMPONENTS_OBJECT;
   flags |= nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK;
 
-  nsRefPtr<nsIXPConnectJSObjectHolder> jsholder;
   nsresult rv = xpc->InitClassesWithNewWrappedGlobal(
     aCx, ToSupports(aNewInner),
-    aPrincipal, flags, options, getter_AddRefs(jsholder));
+    aPrincipal, flags, options, aHolder);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  MOZ_ASSERT(jsholder);
-  aNativeGlobal = jsholder->GetJSObject();
-  jsholder.forget(aHolder);
-
   // Set the location information for the new global, so that tools like
   // about:memory may use that information
-  MOZ_ASSERT(aNativeGlobal.getPtr());
-  xpc::SetLocationForGlobal(aNativeGlobal, aURI);
+  MOZ_ASSERT(aNewInner->GetWrapperPreserveColor());
+  xpc::SetLocationForGlobal(aNewInner->GetWrapperPreserveColor(), aURI);
 
   return NS_OK;
 }
 
 nsresult
 nsGlobalWindow::SetNewDocument(nsIDocument* aDocument,
                                nsISupports* aState,
                                bool aForceReuseInnerWindow)
@@ -2331,66 +2351,66 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
   // Check if we're near the stack limit before we get anywhere near the
   // transplanting code.
   JS_CHECK_RECURSION(cx, return NS_ERROR_FAILURE);
 
   nsCOMPtr<WindowStateHolder> wsh = do_QueryInterface(aState);
   NS_ASSERTION(!aState || wsh, "What kind of weird state are you giving me here?");
 
+  JS::Rooted<JSObject*> newInnerGlobal(cx);
   if (reUseInnerWindow) {
     // We're reusing the current inner window.
     NS_ASSERTION(!currentInner->IsFrozen(),
                  "We should never be reusing a shared inner window");
     newInnerWindow = currentInner;
+    newInnerGlobal = currentInner->GetWrapperPreserveColor();
 
     if (aDocument != oldDoc) {
-      JS::Rooted<JSObject*> obj(cx, currentInner->mJSObject);
-      JS::ExposeObjectToActiveJS(obj);
+      JS::ExposeObjectToActiveJS(newInnerGlobal);
     }
 
     // We're reusing the inner window, but this still counts as a navigation,
     // so all expandos and such defined on the outer window should go away. Force
     // all Xray wrappers to be recomputed.
-    JS::ExposeObjectToActiveJS(mJSObject);
-    JS::Rooted<JSObject*> rootedObject(cx, mJSObject);
+    JS::Rooted<JSObject*> rootedObject(cx, GetWrapperPreserveColor());
+    JS::ExposeObjectToActiveJS(rootedObject);
     if (!JS_RefreshCrossCompartmentWrappers(cx, rootedObject)) {
       return NS_ERROR_FAILURE;
     }
 
     // Inner windows are only reused for same-origin principals, but the principals
     // don't necessarily match exactly. Update the principal on the compartment to
     // match the new document.
     // NB: We don't just call currentInner->RefreshCompartmentPrincipals() here
     // because we haven't yet set its mDoc to aDocument.
-    JSCompartment *compartment = js::GetObjectCompartment(currentInner->mJSObject);
+    JSCompartment *compartment = js::GetObjectCompartment(newInnerGlobal);
 #ifdef DEBUG
     bool sameOrigin = false;
     nsIPrincipal *existing =
       nsJSPrincipals::get(JS_GetCompartmentPrincipals(compartment));
     aDocument->NodePrincipal()->Equals(existing, &sameOrigin);
     MOZ_ASSERT(sameOrigin);
 #endif
     JS_SetCompartmentPrincipals(compartment,
                                 nsJSPrincipals::get(aDocument->NodePrincipal()));
   } else {
     if (aState) {
       newInnerWindow = wsh->GetInnerWindow();
+      newInnerGlobal = newInnerWindow->GetWrapperPreserveColor();
       mInnerWindowHolder = wsh->GetInnerWindowHolder();
-
-      NS_ASSERTION(newInnerWindow, "Got a state without inner window");
-    } else if (thisChrome) {
-      newInnerWindow = new nsGlobalChromeWindow(this);
-    } else if (mIsModalContentWindow) {
-      newInnerWindow = new nsGlobalModalWindow(this);
     } else {
-      newInnerWindow = new nsGlobalWindow(this);
-    }
-
-    if (!aState) {
+      if (thisChrome) {
+        newInnerWindow = new nsGlobalChromeWindow(this);
+      } else if (mIsModalContentWindow) {
+        newInnerWindow = new nsGlobalModalWindow(this);
+      } else {
+        newInnerWindow = new nsGlobalWindow(this);
+      }
+
       // Freeze the outer window and null out the inner window so
       // that initializing classes on the new inner doesn't end up
       // reaching into the old inner window for classes etc.
       //
       // [This happens with Object.prototype when XPConnect creates
       // a temporary global while initializing classes; the reason
       // being that xpconnect creates the temp global w/o a parent
       // and proto, which makes the JS engine look up classes in
@@ -2400,29 +2420,30 @@ nsGlobalWindow::SetNewDocument(nsIDocume
 
       Freeze();
       mCreatingInnerWindow = true;
       // Every script context we are initialized with must create a
       // new global.
       rv = CreateNativeGlobalForInner(cx, newInnerWindow,
                                       aDocument->GetDocumentURI(),
                                       aDocument->NodePrincipal(),
-                                      newInnerWindow->mJSObject,
                                       getter_AddRefs(mInnerWindowHolder));
-      NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerWindow->mJSObject && mInnerWindowHolder,
-                   "Failed to get script global and holder");
+      newInnerGlobal = mInnerWindowHolder->GetJSObject();
+      NS_ASSERTION(NS_SUCCEEDED(rv) && newInnerGlobal &&
+                   newInnerWindow->GetWrapperPreserveColor() == newInnerGlobal,
+                   "Failed to get script global");
 
       mCreatingInnerWindow = false;
       createdInnerWindow = true;
       Thaw();
 
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
-    if (currentInner && currentInner->mJSObject) {
+    if (currentInner && currentInner->GetWrapperPreserveColor()) {
       if (oldDoc == aDocument) {
         // Move the navigator from the old inner window to the new one since
         // this is a document.write. This is safe from a same-origin point of
         // view because document.write can only be used by the same origin.
         newInnerWindow->mNavigator = currentInner->mNavigator;
         currentInner->mNavigator = nullptr;
         if (newInnerWindow->mNavigator) {
           newInnerWindow->mNavigator->SetWindow(newInnerWindow);
@@ -2444,122 +2465,114 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       // held in the bfcache.
       if (!currentInner->IsFrozen()) {
         currentInner->FreeInnerObjects();
       }
     }
 
     mInnerWindow = newInnerWindow;
 
-    if (!mJSObject) {
-      JS::Rooted<JSObject*> global(cx, newInnerWindow->FastGetGlobalJSObject());
-      JS::Rooted<JSObject*> outer(cx, NewOuterWindowProxy(cx, global, thisChrome));
+    if (!GetWrapperPreserveColor()) {
+      JS::Rooted<JSObject*> outer(cx,
+        NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
       NS_ENSURE_TRUE(outer, NS_ERROR_FAILURE);
 
       js::SetProxyExtra(outer, 0, js::PrivateValue(ToSupports(this)));
+      js::SetProxyExtra(outer, 1, JS::ObjectValue(*newInnerGlobal));
 
       // Inform the nsJSContext, which is the canonical holder of the outer.
       mContext->SetWindowProxy(outer);
       mContext->DidInitializeContext();
 
-      mJSObject = mContext->GetWindowProxy();
-      SetWrapper(mJSObject);
+      SetWrapper(mContext->GetWindowProxy());
     } else {
-      JS::ExposeObjectToActiveJS(newInnerWindow->mJSObject);
-      JS::Rooted<JSObject*> global(cx, newInnerWindow->mJSObject);
+      JS::ExposeObjectToActiveJS(newInnerGlobal);
       JS::Rooted<JSObject*> outerObject(cx,
-        NewOuterWindowProxy(cx, global, thisChrome));
+        NewOuterWindowProxy(cx, newInnerGlobal, thisChrome));
       if (!outerObject) {
         NS_ERROR("out of memory");
         return NS_ERROR_FAILURE;
       }
 
-      js::SetProxyExtra(mJSObject, 0, js::PrivateValue(nullptr));
-
-      JS::Rooted<JSObject*> obj(cx, mJSObject);
+      JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
+
+      js::SetProxyExtra(obj, 0, js::PrivateValue(nullptr));
+
       outerObject = xpc::TransplantObject(cx, obj, outerObject);
       if (!outerObject) {
         NS_ERROR("unable to transplant wrappers, probably OOM");
         return NS_ERROR_FAILURE;
       }
 
       js::SetProxyExtra(outerObject, 0, js::PrivateValue(ToSupports(this)));
 
-      mJSObject = outerObject;
-      SetWrapper(mJSObject);
+      SetWrapper(outerObject);
 
       {
-        JSAutoCompartment ac(cx, mJSObject);
-
-        JS::Rooted<JSObject*> obj(cx, mJSObject);
-        JS::Rooted<JSObject*> newParent(cx, newInnerWindow->mJSObject);
-        JS_SetParent(cx, obj, newParent);
+        JSAutoCompartment ac(cx, outerObject);
+
+        JS_SetParent(cx, outerObject, newInnerGlobal);
 
         // Inform the nsJSContext, which is the canonical holder of the outer.
-        mContext->SetWindowProxy(obj);
+        mContext->SetWindowProxy(outerObject);
 
         NS_ASSERTION(!JS_IsExceptionPending(cx),
                      "We might overwrite a pending exception!");
-        XPCWrappedNativeScope* scope = xpc::GetObjectScope(obj);
+        XPCWrappedNativeScope* scope = xpc::GetObjectScope(outerObject);
         if (scope->mWaiverWrapperMap) {
-          scope->mWaiverWrapperMap->Reparent(cx, newParent);
+          scope->mWaiverWrapperMap->Reparent(cx, newInnerGlobal);
         }
       }
     }
 
     // Enter the new global's compartment.
-    JSAutoCompartment ac(cx, mJSObject);
+    JSAutoCompartment ac(cx, GetWrapperPreserveColor());
 
     // Set scriptability based on the state of the docshell.
     bool allow = GetDocShell()->GetCanExecuteScripts();
-    xpc::Scriptability::Get(mJSObject).SetDocShellAllowsScript(allow);
+    xpc::Scriptability::Get(GetWrapperPreserveColor()).SetDocShellAllowsScript(allow);
 
     // If we created a new inner window above, we need to do the last little bit
     // of initialization now that the dust has settled.
     if (createdInnerWindow) {
       nsIXPConnect *xpc = nsContentUtils::XPConnect();
       nsCOMPtr<nsIXPConnectWrappedNative> wrapper;
-      nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerWindow->mJSObject,
+      nsresult rv = xpc->GetWrappedNativeOfJSObject(cx, newInnerGlobal,
                                                     getter_AddRefs(wrapper));
       NS_ENSURE_SUCCESS(rv, rv);
       NS_ABORT_IF_FALSE(wrapper, "bad wrapper");
       rv = wrapper->FinishInitForWrappedGlobal();
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     if (!aState) {
-      if (!JS_DefineProperty(cx, newInnerWindow->mJSObject, "window",
-                             OBJECT_TO_JSVAL(mJSObject),
+      if (!JS_DefineProperty(cx, newInnerGlobal, "window",
+                             OBJECT_TO_JSVAL(GetWrapperPreserveColor()),
                              JS_PropertyStub, JS_StrictPropertyStub,
                              JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT)) {
         NS_ERROR("can't create the 'window' property");
         return NS_ERROR_FAILURE;
       }
     }
   }
 
-  JSAutoCompartment ac(cx, mJSObject);
+  JSAutoCompartment ac(cx, GetWrapperPreserveColor());
 
   if (!aState && !reUseInnerWindow) {
     // Loading a new page and creating a new inner window, *not*
     // restoring from session history.
 
     // Now that both the the inner and outer windows are initialized
     // let the script context do its magic to hook them together.
+    MOZ_ASSERT(mContext->GetWindowProxy() == GetWrapperPreserveColor());
 #ifdef DEBUG
-    JS::Rooted<JSObject*> newInnerJSObject(cx,
-        newInnerWindow->FastGetGlobalJSObject());
-#endif
-
-    MOZ_ASSERT(mContext->GetWindowProxy() == mJSObject);
-#ifdef DEBUG
-    JS::Rooted<JSObject*> rootedJSObject(cx, mJSObject);
+    JS::Rooted<JSObject*> rootedJSObject(cx, GetWrapperPreserveColor());
     JS::Rooted<JSObject*> proto1(cx), proto2(cx);
     JS_GetPrototype(cx, rootedJSObject, &proto1);
-    JS_GetPrototype(cx, newInnerJSObject, &proto2);
+    JS_GetPrototype(cx, newInnerGlobal, &proto2);
     NS_ASSERTION(proto1 == proto2,
                  "outer and inner globals should have the same prototype");
 #endif
 
     nsCOMPtr<Element> frame = GetFrameElementInternal();
     if (frame) {
       nsPIDOMWindow* parentWindow = frame->OwnerDoc()->GetWindow();
       if (parentWindow && parentWindow->TimeoutSuspendCount()) {
@@ -2578,34 +2591,34 @@ nsGlobalWindow::SetNewDocument(nsIDocume
       if (newInnerWindow->mDoc != aDocument) {
         newInnerWindow->mDoc = aDocument;
 
         // We're reusing the inner window for a new document. In this
         // case we don't clear the inner window's scope, but we must
         // make sure the cached document property gets updated.
 
         // XXXmarkh - tell other languages about this?
-        JS::Rooted<JSObject*> obj(cx, currentInner->mJSObject);
+        JS::Rooted<JSObject*> obj(cx, currentInner->GetWrapperPreserveColor());
         ::JS_DeleteProperty(cx, obj, "document");
       }
     } else {
       newInnerWindow->InnerSetNewDocument(aDocument);
 
       // Initialize DOM classes etc on the inner window.
-      JS::Rooted<JSObject*> obj(cx, newInnerWindow->mJSObject);
+      JS::Rooted<JSObject*> obj(cx, newInnerGlobal);
       rv = mContext->InitClasses(obj);
       NS_ENSURE_SUCCESS(rv, rv);
     }
 
     // If the document comes from a JAR, check if the channel was determined
     // to be unsafe. If so, permanently disable script on the compartment by
     // calling Block() and throwing away the key.
     nsCOMPtr<nsIJARChannel> jarChannel = do_QueryInterface(aDocument->GetChannel());
     if (jarChannel && jarChannel->GetIsUnsafe()) {
-      xpc::Scriptability::Get(newInnerWindow->mJSObject).Block();
+      xpc::Scriptability::Get(newInnerGlobal).Block();
     }
 
     if (mArguments) {
       newInnerWindow->DefineArgumentsProperty(mArguments);
       mArguments = nullptr;
     }
 
     // Give the new inner window our chrome event handler (since it
@@ -2614,17 +2627,17 @@ nsGlobalWindow::SetNewDocument(nsIDocume
   }
 
   mContext->GC(JS::gcreason::SET_NEW_DOCUMENT);
   mContext->DidInitializeContext();
 
   // We wait to fire the debugger hook until the window is all set up and hooked
   // up with the outer. See bug 969156.
   if (createdInnerWindow) {
-    JS::Rooted<JSObject*> global(cx, newInnerWindow->mJSObject);
+    JS::Rooted<JSObject*> global(cx, newInnerWindow->GetWrapper());
     JS_FireOnNewGlobalObject(cx, global);
   }
 
   if (newInnerWindow && !newInnerWindow->mHasNotifiedGlobalCreated && mDoc) {
     // We should probably notify. However if this is the, arguably bad,
     // situation when we're creating a temporary non-chrome-about-blank
     // document in a chrome docshell, don't notify just yet. Instead wait
     // until we have a real chrome doc.
@@ -2783,18 +2796,17 @@ nsGlobalWindow::SetDocShell(nsIDocShell*
 void
 nsGlobalWindow::DetachFromDocShell()
 {
   NS_ASSERTION(IsOuterWindow(), "Uh, DetachFromDocShell() called on inner window!");
 
   // DetachFromDocShell means the window is being torn down. Drop our
   // reference to the script context, allowing it to be deleted
   // later. Meanwhile, keep our weak reference to the script object
-  // (mJSObject) so that it can be retrieved later (until it is
-  // finalized by the JS GC).
+  // so that it can be retrieved later (until it is finalized by the JS GC).
 
   NS_ASSERTION(mTimeouts.isEmpty(), "Uh, outer window holds timeouts!");
 
   // Call FreeInnerObjects on all inner windows, not just the current
   // one, since some could be held by WindowStateHolder objects that
   // are GC-owned.
   for (nsRefPtr<nsGlobalWindow> inner = (nsGlobalWindow *)PR_LIST_HEAD(this);
        inner != this;
@@ -3208,29 +3220,21 @@ nsGlobalWindow::DispatchDOMEvent(WidgetE
                                  nsEventStatus* aEventStatus)
 {
   return EventDispatcher::DispatchDOMEvent(static_cast<nsPIDOMWindow*>(this),
                                            aEvent, aDOMEvent, aPresContext,
                                            aEventStatus);
 }
 
 void
-nsGlobalWindow::OnFinalize(JSObject* aObject)
-{
-  if (aObject == mJSObject) {
-    mJSObject = nullptr;
-  }
-}
-
-void
 nsGlobalWindow::PoisonOuterWindowProxy(JSObject *aObject)
 {
   MOZ_ASSERT(IsOuterWindow());
-  if (aObject == mJSObject) {
-    mJSObject.setToCrashOnTouch();
+  if (aObject == GetWrapperPreserveColor()) {
+    PoisonWrapper();
   }
 }
 
 nsresult
 nsGlobalWindow::SetArguments(nsIArray *aArguments)
 {
   MOZ_ASSERT(IsOuterWindow());
   nsresult rv;
@@ -3268,17 +3272,17 @@ nsresult
 nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments)
 {
   MOZ_ASSERT(!mIsModalContentWindow); // Handled separately.
   nsIScriptContext *ctx = GetOuterWindowInternal()->mContext;
   NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
   AutoPushJSContext cx(ctx->GetNativeContext());
   NS_ENSURE_TRUE(cx, NS_ERROR_NOT_INITIALIZED);
 
-  JS::Rooted<JSObject*> obj(cx, mJSObject);
+  JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
   return GetContextInternal()->SetProperty(obj, "arguments", aArguments);
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIScriptObjectPrincipal
 //*****************************************************************************
 
 nsIPrincipal*
@@ -4384,20 +4388,20 @@ nsGlobalWindow::GetOpener(nsIDOMWindow**
   return rv.ErrorCode();
 }
 
 void
 nsGlobalWindow::SetOpener(nsIDOMWindow* aOpener, ErrorResult& aError)
 {
   // Check if we were called from a privileged chrome script.  If not, and if
   // aOpener is not null, just define aOpener on our inner window's JS object,
-  // wapped into the current compartment so that for Xrays we define on the Xray
-  // expando object, but don't set it on the outer window, so that it'll get
-  // reset on navigation.  This is just like replaceable properties, but we're
-  // not quite readonly.
+  // wrapped into the current compartment so that for Xrays we define on the
+  // Xray expando object, but don't set it on the outer window, so that it'll
+  // get reset on navigation.  This is just like replaceable properties, but
+  // we're not quite readonly.
   if (aOpener && !nsContentUtils::IsCallerChrome()) {
     // JS_WrapObject will outerize, so we don't care if aOpener is an inner.
     nsCOMPtr<nsIGlobalObject> glob = do_QueryInterface(aOpener);
     if (!glob) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
@@ -4408,18 +4412,18 @@ nsGlobalWindow::SetOpener(nsIDOMWindow* 
     // expandos on Xrays as needed.
 
     JS::Rooted<JSObject*> otherObj(cx, glob->GetGlobalJSObject());
     if (!otherObj) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
-    JS::Rooted<JSObject*> thisObj(cx, mJSObject);
-    if (!mJSObject) {
+    JS::Rooted<JSObject*> thisObj(cx, GetWrapperPreserveColor());
+    if (!thisObj) {
       aError.Throw(NS_ERROR_UNEXPECTED);
       return;
     }
 
     if (!JS_WrapObject(cx, &otherObj) ||
         !JS_WrapObject(cx, &thisObj) ||
         !JS_DefineProperty(cx, thisObj, "opener", JS::ObjectValue(*otherObj),
                            JS_PropertyStub, JS_StrictPropertyStub,
@@ -5093,18 +5097,18 @@ nsGlobalWindow::RequestAnimationFrame(co
 {
   FORWARD_TO_INNER_OR_THROW(RequestAnimationFrame, (aCallback, aError), aError,
                             0);
 
   if (!mDoc) {
     return 0;
   }
 
-  if (mJSObject) {
-    js::NotifyAnimationActivity(mJSObject);
+  if (GetWrapperPreserveColor()) {
+    js::NotifyAnimationActivity(GetWrapperPreserveColor());
   }
 
   int32_t handle;
   aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
   return handle;
 }
 
 NS_IMETHODIMP
@@ -5655,17 +5659,17 @@ nsGlobalWindow::DispatchResizeEvent(cons
   ErrorResult res;
   nsRefPtr<Event> domEvent =
     mDoc->CreateEvent(NS_LITERAL_STRING("CustomEvent"), res);
   if (res.Failed()) {
     return false;
   }
 
   AutoSafeJSContext cx;
-  JSAutoCompartment ac(cx, mJSObject);
+  JSAutoCompartment ac(cx, GetWrapperPreserveColor());
   DOMWindowResizeEventDetail detail;
   detail.mWidth = aSize.width;
   detail.mHeight = aSize.height;
   JS::Rooted<JS::Value> detailValue(cx);
   if (!detail.ToObject(cx, &detailValue)) {
     return false;
   }
 
@@ -5692,17 +5696,17 @@ nsGlobalWindow::DispatchResizeEvent(cons
   return defaultActionEnabled;
 }
 
 void
 nsGlobalWindow::RefreshCompartmentPrincipal()
 {
   FORWARD_TO_INNER(RefreshCompartmentPrincipal, (), /* void */ );
 
-  JS_SetCompartmentPrincipals(js::GetObjectCompartment(mJSObject),
+  JS_SetCompartmentPrincipals(js::GetObjectCompartment(GetWrapperPreserveColor()),
                               nsJSPrincipals::get(mDoc->NodePrincipal()));
 }
 
 static already_AddRefed<nsIDocShellTreeItem>
 GetCallerDocShellTreeItem()
 {
   JSContext *cx = nsContentUtils::GetCurrentJSContext();
   nsCOMPtr<nsIDocShellTreeItem> callerItem;
@@ -8650,20 +8654,17 @@ nsGlobalWindow::GetCachedXBLPrototypeHan
 }
 
 void
 nsGlobalWindow::CacheXBLPrototypeHandler(nsXBLPrototypeHandler* aKey,
                                          JS::Handle<JSObject*> aHandler)
 {
   if (!mCachedXBLPrototypeHandlers) {
     mCachedXBLPrototypeHandlers = new nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*>();
-  }
-
-  if (!mCachedXBLPrototypeHandlers->Count()) {
-    mozilla::HoldJSObjects(this);
+    PreserveWrapper(ToSupports(this));
   }
 
   mCachedXBLPrototypeHandlers->Put(aKey, aHandler);
 }
 
 /**
  * GetScriptableFrameElement is called when script reads
  * nsIGlobalWindow::frameElement.
@@ -10402,16 +10403,22 @@ nsGlobalWindow::GetInterface(const nsIID
   }
   else {
     return QueryInterface(aIID, aSink);
   }
 
   return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
 }
 
+JS::Value
+nsGlobalWindow::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aError)
+{
+  return dom::GetInterface(aCx, this, aIID, aError);
+}
+
 void
 nsGlobalWindow::FireOfflineStatusEvent()
 {
   if (!IsCurrentInnerWindow())
     return;
   nsAutoString name;
   if (NS_IsOffline()) {
     name.AssignLiteral("offline");
@@ -12535,17 +12542,17 @@ nsGlobalWindow::EnsureSizeUpToDate()
   }
 }
 
 already_AddRefed<nsISupports>
 nsGlobalWindow::SaveWindowState()
 {
   NS_PRECONDITION(IsOuterWindow(), "Can't save the inner window's state");
 
-  if (!mContext || !mJSObject) {
+  if (!mContext || !GetWrapperPreserveColor()) {
     // The window may be getting torn down; don't bother saving state.
     return nullptr;
   }
 
   nsGlobalWindow *inner = GetCurrentInnerWindowInternal();
   NS_ASSERTION(inner, "No inner window to save");
 
   // Don't do anything else to this inner window! After this point, all
@@ -12565,17 +12572,17 @@ nsGlobalWindow::SaveWindowState()
   return state.forget();
 }
 
 nsresult
 nsGlobalWindow::RestoreWindowState(nsISupports *aState)
 {
   NS_ASSERTION(IsOuterWindow(), "Cannot restore an inner window");
 
-  if (!mContext || !mJSObject) {
+  if (!mContext || !GetWrapperPreserveColor()) {
     // The window may be getting torn down; don't bother restoring state.
     return NS_OK;
   }
 
   nsCOMPtr<WindowStateHolder> holder = do_QueryInterface(aState);
   NS_ENSURE_TRUE(holder, NS_ERROR_FAILURE);
 
 #ifdef DEBUG_PAGE_CACHE
@@ -13416,94 +13423,163 @@ nsGlobalWindow::GetMessageManager(ErrorR
                                 MM_CHROME | MM_BROADCASTER);
   }
   return myself->mMessageManager;
 }
 
 // nsGlobalModalWindow implementation
 
 // QueryInterface implementation for nsGlobalModalWindow
-NS_IMPL_CYCLE_COLLECTION_INHERITED_1(nsGlobalModalWindow,
-                                     nsGlobalWindow,
-                                     mReturnValue)
-
 DOMCI_DATA(ModalContentWindow, nsGlobalModalWindow)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsGlobalModalWindow)
+NS_INTERFACE_MAP_BEGIN(nsGlobalModalWindow)
   NS_INTERFACE_MAP_ENTRY(nsIDOMModalContentWindow)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow)
 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
 
 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 
 
+JS::Value
+nsGlobalWindow::GetDialogArguments(JSContext* aCx, ErrorResult& aError)
+{
+  FORWARD_TO_OUTER_OR_THROW(GetDialogArguments, (aCx, aError), aError,
+                            JS::UndefinedValue());
+
+  MOZ_ASSERT(IsModalContentWindow(),
+             "This should only be called on modal windows!");
+
+  // This does an internal origin check, and returns undefined if the subject
+  // does not subsumes the origin of the arguments.
+  JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
+  JSAutoCompartment ac(aCx, wrapper);
+  JS::Rooted<JS::Value> args(aCx);
+  mDialogArguments->Get(aCx, wrapper, nsContentUtils::GetSubjectPrincipal(),
+                        &args, aError);
+  return args;
+}
+
 NS_IMETHODIMP
 nsGlobalModalWindow::GetDialogArguments(nsIVariant **aArguments)
 {
   FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
                                         NS_ERROR_NOT_INITIALIZED);
 
   // This does an internal origin check, and returns undefined if the subject
   // does not subsumes the origin of the arguments.
   return mDialogArguments->Get(nsContentUtils::GetSubjectPrincipal(), aArguments);
 }
 
+JS::Value
+nsGlobalWindow::GetReturnValue(JSContext* aCx, ErrorResult& aError)
+{
+  FORWARD_TO_OUTER_OR_THROW(GetReturnValue, (aCx, aError), aError,
+                            JS::UndefinedValue());
+
+  MOZ_ASSERT(IsModalContentWindow(),
+             "This should only be called on modal windows!");
+
+  JS::Rooted<JS::Value> returnValue(aCx);
+  if (mReturnValue) {
+    JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
+    JSAutoCompartment ac(aCx, wrapper);
+    mReturnValue->Get(aCx, wrapper, nsContentUtils::GetSubjectPrincipal(),
+                      &returnValue, aError);
+  }
+  return returnValue;
+}
+
 NS_IMETHODIMP
 nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal)
 {
   FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK);
 
   nsCOMPtr<nsIVariant> result;
   if (!mReturnValue) {
     nsCOMPtr<nsIVariant> variant = CreateVoidVariant();
     variant.forget(aRetVal);
     return NS_OK;
   }
   return mReturnValue->Get(nsContentUtils::GetSubjectPrincipal(), aRetVal);
 }
 
+void
+nsGlobalWindow::SetReturnValue(JSContext* aCx,
+                               JS::Handle<JS::Value> aReturnValue,
+                               ErrorResult& aError)
+{
+  FORWARD_TO_OUTER_OR_THROW(SetReturnValue, (aCx, aReturnValue, aError),
+                            aError, );
+
+  MOZ_ASSERT(IsModalContentWindow(),
+             "This should only be called on modal windows!");
+
+  nsCOMPtr<nsIVariant> returnValue;
+  aError =
+    nsContentUtils::XPConnect()->JSToVariant(aCx, aReturnValue,
+                                             getter_AddRefs(returnValue));
+  if (!aError.Failed()) {
+    mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(),
+                                         returnValue);
+  }
+}
+
 NS_IMETHODIMP
 nsGlobalModalWindow::SetReturnValue(nsIVariant *aRetVal)
 {
   FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(SetReturnValue, (aRetVal), NS_OK);
 
   mReturnValue = new DialogValueHolder(nsContentUtils::GetSubjectPrincipal(),
                                        aRetVal);
   return NS_OK;
 }
 
+/* static */
+bool
+nsGlobalWindow::IsModalContentWindow(JSContext* aCx, JSObject* aGlobal)
+{
+  // For now, have to deal with XPConnect objects here.
+  nsGlobalWindow* win;
+  nsresult rv = UNWRAP_OBJECT(Window, aGlobal, win);
+  if (NS_FAILED(rv)) {
+    nsCOMPtr<nsPIDOMWindow> piWin = do_QueryWrapper(aCx, aGlobal);
+    win = static_cast<nsGlobalWindow*>(piWin.get());
+  }
+  return win->IsModalContentWindow();
+}
+
 NS_IMETHODIMP
 nsGlobalWindow::GetConsole(JSContext* aCx,
                            JS::MutableHandle<JS::Value> aConsole)
 {
   ErrorResult rv;
   nsRefPtr<Console> console = GetConsole(rv);
   if (rv.Failed()) {
     return rv.ErrorCode();
   }
 
-  JS::Rooted<JSObject*> thisObj(aCx, mJSObject);
-  if (!mJSObject) {
+  JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
+  if (!thisObj) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!JS_WrapObject(aCx, &thisObj) ||
       !WrapNewBindingObject(aCx, thisObj, console, aConsole)) {
     return NS_ERROR_FAILURE;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::SetConsole(JSContext* aCx, JS::Handle<JS::Value> aValue)
 {
-  JS::Rooted<JSObject*> thisObj(aCx, mJSObject);
-  if (!mJSObject) {
+  JS::Rooted<JSObject*> thisObj(aCx, GetWrapper());
+  if (!thisObj) {
     return NS_ERROR_UNEXPECTED;
   }
 
   if (!JS_WrapObject(aCx, &thisObj) ||
       !JS_DefineProperty(aCx, thisObj, "console", aValue,
                          JS_PropertyStub, JS_StrictPropertyStub,
                          JSPROP_ENUMERATE)) {
     return NS_ERROR_FAILURE;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -79,16 +79,17 @@ class nsIArray;
 class nsIBaseWindow;
 class nsIContent;
 class nsICSSDeclaration;
 class nsIDocShellTreeOwner;
 class nsIDOMCrypto;
 class nsIDOMOfflineResourceList;
 class nsIScrollableFrame;
 class nsIControllers;
+class nsIJSID;
 class nsIScriptContext;
 class nsIScriptTimeoutHandler;
 class nsIWebBrowserChrome;
 
 class nsDOMWindowList;
 class nsLocation;
 class nsScreen;
 class nsHistory;
@@ -272,16 +273,26 @@ public:
     if (aSubject->SubsumesConsideringDomain(mOrigin)) {
       result = mValue;
     } else {
       result = CreateVoidVariant();
     }
     result.forget(aResult);
     return NS_OK;
   }
+  void Get(JSContext* aCx, JS::Handle<JSObject*> aScope, nsIPrincipal* aSubject,
+           JS::MutableHandle<JS::Value> aResult, mozilla::ErrorResult& aError)
+  {
+    if (aSubject->Subsumes(mOrigin)) {
+      aError = nsContentUtils::XPConnect()->VariantToJS(aCx, aScope,
+                                                        mValue, aResult);
+    } else {
+      aResult.setUndefined();
+    }
+  }
   virtual ~DialogValueHolder() {}
 private:
   nsCOMPtr<nsIPrincipal> mOrigin;
   nsCOMPtr<nsIVariant> mValue;
 };
 
 //*****************************************************************************
 // nsGlobalWindow: Global Object for Scripting
@@ -343,29 +354,31 @@ public:
   }
 
   // nsIGlobalJSObjectHolder
   virtual JSObject *GetGlobalJSObject();
 
   // nsIScriptGlobalObject
   JSObject *FastGetGlobalJSObject() const
   {
-    return mJSObject;
+    return GetWrapperPreserveColor();
   }
+
   void TraceGlobalJSObject(JSTracer* aTrc);
 
   virtual nsresult EnsureScriptEnvironment();
 
   virtual nsIScriptContext *GetScriptContext();
 
   void PoisonOuterWindowProxy(JSObject *aObject);
-  virtual void OnFinalize(JSObject* aObject);
 
   virtual bool IsBlackForCC(bool aTracingNeeded = true);
 
+  static JSObject* OuterObject(JSContext* aCx, JS::HandleObject aObj);
+
   // nsIScriptObjectPrincipal
   virtual nsIPrincipal* GetPrincipal();
 
   // nsIDOMWindow
   NS_DECL_NSIDOMWINDOW
 
 #ifdef MOZ_B2G
   // nsIDOMWindowB2G
@@ -576,16 +589,19 @@ public:
     return  mCreatingInnerWindow;
   }
 
   bool IsChromeWindow() const
   {
     return mIsChrome;
   }
 
+  using nsPIDOMWindow::IsModalContentWindow;
+  static bool IsModalContentWindow(JSContext* aCx, JSObject* aGlobal);
+
   // GetScrollFrame does not flush.  Callers should do it themselves as needed,
   // depending on which info they actually want off the scrollable frame.
   nsIScrollableFrame *GetScrollFrame();
 
   nsresult Observe(nsISupports* aSubject, const char* aTopic,
                    const char16_t* aData);
 
   // Outer windows only.
@@ -977,16 +993,24 @@ public:
   void Restore(mozilla::ErrorResult& aError);
   void NotifyDefaultButtonLoaded(mozilla::dom::Element& aDefaultButton,
                                  mozilla::ErrorResult& aError);
   nsIMessageBroadcaster* GetMessageManager(mozilla::ErrorResult& aError);
   void BeginWindowMove(mozilla::dom::Event& aMouseDownEvent,
                        mozilla::dom::Element* aPanel,
                        mozilla::ErrorResult& aError);
 
+  JS::Value GetDialogArguments(JSContext* aCx, mozilla::ErrorResult& aError);
+  JS::Value GetReturnValue(JSContext* aCx, mozilla::ErrorResult& aError);
+  void SetReturnValue(JSContext* aCx, JS::Handle<JS::Value> aReturnValue,
+                      mozilla::ErrorResult& aError);
+
+  JS::Value GetInterface(JSContext* aCx, nsIJSID* aIID,
+                         mozilla::ErrorResult& aError);
+
 protected:
   // Array of idle observers that are notified of idle events.
   nsTObserverArray<IdleObserverHolder> mIdleObservers;
 
   // Idle timer used for function callbacks to notify idle observers.
   nsCOMPtr<nsITimer> mIdleTimer;
 
   // Idle fuzz time added to idle timer callbacks.
@@ -1435,16 +1459,19 @@ protected:
   nsCOMPtr<nsIControllers>      mControllers;
 
   // For |window.arguments|, via |openDialog|.
   nsCOMPtr<nsIArray>            mArguments;
 
   // For |window.dialogArguments|, via |showModalDialog|.
   nsRefPtr<DialogValueHolder> mDialogArguments;
 
+  // Only used in the outer.
+  nsRefPtr<DialogValueHolder> mReturnValue;
+
   nsRefPtr<mozilla::dom::Navigator> mNavigator;
   nsRefPtr<nsScreen>            mScreen;
   nsRefPtr<nsDOMWindowList>     mFrames;
   nsRefPtr<mozilla::dom::BarProp> mMenubar;
   nsRefPtr<mozilla::dom::BarProp> mToolbar;
   nsRefPtr<mozilla::dom::BarProp> mLocationbar;
   nsRefPtr<mozilla::dom::BarProp> mPersonalbar;
   nsRefPtr<mozilla::dom::BarProp> mStatusbar;
@@ -1480,19 +1507,16 @@ protected:
   uint32_t                      mTimeoutPublicIdCounter;
   uint32_t                      mTimeoutFiringDepth;
   nsRefPtr<nsLocation>          mLocation;
   nsRefPtr<nsHistory>           mHistory;
 
   // These member variables are used on both inner and the outer windows.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
 
-  // The JS global object.  Global objects are always allocated tenured.
-  JS::TenuredHeap<JSObject*> mJSObject;
-
   typedef nsCOMArray<nsIDOMStorageEvent> nsDOMStorageEventArray;
   nsDOMStorageEventArray mPendingStorageEvents;
 
   uint32_t mTimeoutsSuspendDepth;
 
   // the method that was used to focus mFocusedNode
   uint32_t mFocusMethod;
 
@@ -1634,22 +1658,16 @@ public:
   nsGlobalModalWindow(nsGlobalWindow *aOuterWindow)
     : nsGlobalWindow(aOuterWindow)
   {
     mIsModalContentWindow = true;
   }
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMMODALCONTENTWINDOW
-
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
-
-protected:
-  // For use by outer windows only.
-  nsRefPtr<DialogValueHolder> mReturnValue;
 };
 
 /* factory function */
 inline already_AddRefed<nsGlobalWindow>
 NS_NewScriptGlobalObject(bool aIsChrome, bool aIsModalContentWindow)
 {
   nsRefPtr<nsGlobalWindow> global;
 
--- a/dom/base/nsIScriptGlobalObject.h
+++ b/dom/base/nsIScriptGlobalObject.h
@@ -28,18 +28,18 @@ struct ErrorEventInit;
 // aStatus will be filled in with the status.
 bool
 NS_HandleScriptError(nsIScriptGlobalObject *aScriptGlobal,
                      const mozilla::dom::ErrorEventInit &aErrorEvent,
                      nsEventStatus *aStatus);
 
 
 #define NS_ISCRIPTGLOBALOBJECT_IID \
-{ 0x6995e1ff, 0x9fc5, 0x44a7, \
- { 0xbd, 0x7c, 0xe7, 0xcd, 0x44, 0x47, 0x22, 0x87 } }
+{ 0x876f83bd, 0x6314, 0x460a, \
+  { 0xa0, 0x45, 0x1c, 0x8f, 0x46, 0x2f, 0xb8, 0xe1 } }
 
 /**
  * The global object which keeps a script context for each supported script
  * language. This often used to store per-window global state.
  * This is a heavyweight interface implemented only by DOM globals, and
  * it might go away some time in the future.
  */
 
@@ -63,25 +63,16 @@ public:
    */
   virtual nsIScriptContext *GetScriptContext() = 0;
 
   nsIScriptContext* GetContext() {
     return GetScriptContext();
   }
 
   /**
-   * Called when the global script for a language is finalized, typically as
-   * part of its GC process.  By the time this call is made, the
-   * nsIScriptContext for the language has probably already been removed.
-   * After this call, the passed object is dead - which should generally be the
-   * same object the global is using for a global for that language.
-   */
-  virtual void OnFinalize(JSObject* aObject) = 0;
-
-  /**
    * Handle a script error.  Generally called by a script context.
    */
   virtual nsresult HandleScriptError(
                      const mozilla::dom::ErrorEventInit &aErrorEventInit,
                      nsEventStatus *aEventStatus) {
     NS_ENSURE_STATE(NS_HandleScriptError(this, aErrorEventInit, aEventStatus));
     return NS_OK;
   }
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -6,18 +6,18 @@
 #ifndef nsWrapperCache_h___
 #define nsWrapperCache_h___
 
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Assertions.h"
 #include "js/Id.h"          // must come before js/RootingAPI.h
 #include "js/Value.h"       // must come before js/RootingAPI.h
 #include "js/RootingAPI.h"
+#include "js/Tracer.h"
 
-struct JSTracer;
 class XPCWrappedNativeScope;
 
 #define NS_WRAPPERCACHE_IID \
 { 0x6f3179a1, 0x36f7, 0x4a5c, \
   { 0x8c, 0xf1, 0xad, 0xc8, 0x7c, 0xde, 0x3e, 0x87 } }
 
 /**
  * Class to store the wrapper for an object. This can only be used with objects
@@ -217,16 +217,31 @@ public:
 #ifdef DEBUG
     // Make sure the cycle collector will be able to traverse to the wrapper.
     CheckCCWrapperTraversal(aScriptObjectHolder, aTracer);
 #endif
   }
 
   void ReleaseWrapper(void* aScriptObjectHolder);
 
+protected:
+  void TraceWrapper(JSTracer* aTrc, const char* name)
+  {
+    if (mWrapper) {
+      JS_CallHeapObjectTracer(aTrc, &mWrapper, name);
+    }
+  }
+
+  void PoisonWrapper()
+  {
+    if (mWrapper) {
+      mWrapper.setToCrashOnTouch();
+    }
+  }
+
 private:
   JSObject *GetWrapperJSObject() const
   {
     return mWrapper;
   }
 
   void SetWrapperJSObject(JSObject* aWrapper)
   {
--- a/dom/bindings/BindingDeclarations.h
+++ b/dom/bindings/BindingDeclarations.h
@@ -20,20 +20,16 @@
 #include "nsCOMPtr.h"
 #include "nsTArray.h"
 #include "nsAutoPtr.h" // for nsRefPtr member variables
 #include "mozilla/dom/DOMString.h"
 #include "mozilla/dom/OwningNonNull.h"
 
 class nsWrapperCache;
 
-// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't
-// try to use it without fixing that first.
-class nsGlobalWindow;
-
 namespace mozilla {
 namespace dom {
 
 // Struct that serves as a base class for all dictionaries.  Particularly useful
 // so we can use IsBaseOf to detect dictionary template arguments.
 struct DictionaryBase
 {
 protected:
@@ -438,22 +434,16 @@ public:
 
 inline nsWrapperCache*
 GetWrapperCache(nsWrapperCache* cache)
 {
   return cache;
 }
 
 inline nsWrapperCache*
-GetWrapperCache(nsGlobalWindow*)
-{
-  return nullptr;
-}
-
-inline nsWrapperCache*
 GetWrapperCache(void* p)
 {
   return nullptr;
 }
 
 // Helper template for smart pointers to resolve ambiguity between
 // GetWrappeCache(void*) and GetWrapperCache(const ParentObject&).
 template <template <typename> class SmartPtr, typename T>
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -810,23 +810,30 @@ QueryInterface(JSContext* cx, unsigned a
   JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
   JS::Rooted<JS::Value> thisv(cx, JS_THIS(cx, vp));
   if (thisv.isNull())
     return false;
 
   // Get the object. It might be a security wrapper, in which case we do a checked
   // unwrap.
   JS::Rooted<JSObject*> origObj(cx, &thisv.toObject());
-  JSObject* obj = js::CheckedUnwrap(origObj);
+  JSObject* obj = js::CheckedUnwrap(origObj, /* stopAtOuter = */ false);
   if (!obj) {
       JS_ReportError(cx, "Permission denied to access object");
       return false;
   }
 
-  nsISupports* native = UnwrapDOMObjectToISupports(obj);
+  // Switch this to UnwrapDOMObjectToISupports once our global objects are
+  // using new bindings.
+  JS::Rooted<JS::Value> val(cx, JS::ObjectValue(*obj));
+  nsISupports* native = nullptr;
+  nsCOMPtr<nsISupports> nativeRef;
+  xpc_qsUnwrapArg<nsISupports>(cx, val, &native,
+                               static_cast<nsISupports**>(getter_AddRefs(nativeRef)),
+                               &val);
   if (!native) {
     return Throw(cx, NS_ERROR_FAILURE);
   }
 
   if (argc < 1) {
     return Throw(cx, NS_ERROR_XPC_NOT_ENOUGH_ARGS);
   }
 
@@ -857,16 +864,39 @@ QueryInterface(JSContext* cx, unsigned a
   if (NS_FAILED(rv)) {
     return Throw(cx, rv);
   }
 
   *vp = thisv;
   return true;
 }
 
+JS::Value
+GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
+                 nsWrapperCache* aCache, nsIJSID* aIID, ErrorResult& aError)
+{
+  const nsID* iid = aIID->GetID();
+
+  nsRefPtr<nsISupports> result;
+  aError = aRequestor->GetInterface(*iid, getter_AddRefs(result));
+  if (aError.Failed()) {
+    return JS::NullValue();
+  }
+
+  JS::Rooted<JSObject*> wrapper(aCx, aCache->GetWrapper());
+  JS::Rooted<JSObject*> global(aCx, js::GetGlobalForObjectCrossCompartment(wrapper));
+  JS::Rooted<JS::Value> v(aCx, JSVAL_NULL);
+  if (!WrapObject(aCx, global, result, iid, &v)) {
+    aError.Throw(NS_ERROR_FAILURE);
+    return JS::NullValue();
+  }
+
+  return v;
+}
+
 bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
 }
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name)
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -29,16 +29,17 @@
 #include "MainThreadUtils.h"
 #include "nsISupportsImpl.h"
 #include "qsObjectHelper.h"
 #include "xpcpublic.h"
 #include "nsIVariant.h"
 
 #include "nsWrapperCacheInlines.h"
 
+class nsIJSID;
 class nsPIDOMWindow;
 
 extern nsresult
 xpc_qsUnwrapArgImpl(JSContext* cx, JS::Handle<JS::Value> v, const nsIID& iid, void** ppArg,
                     nsISupports** ppArgRef, JS::MutableHandle<JS::Value> vp);
 
 namespace mozilla {
 namespace dom {
@@ -1601,16 +1602,27 @@ WantsQueryInterface
   static_assert(IsBaseOf<nsISupports, T>::value,
                 "QueryInterface can't work without an nsISupports.");
   static bool Enabled(JSContext* aCx, JSObject* aGlobal)
   {
     return NS_IsMainThread() && IsChromeOrXBL(aCx, aGlobal);
   }
 };
 
+JS::Value
+GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
+                 nsWrapperCache* aCache, nsIJSID* aIID, ErrorResult& aError);
+
+template<class T>
+JS::Value
+GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID, ErrorResult& aError)
+{
+  return GetInterfaceImpl(aCx, aThis, aThis, aIID, aError);
+}
+
 bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name);
 
 // vp is allowed to be null; in that case no get will be attempted,
 // and *found will simply indicate whether the property exists.
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -48,17 +48,18 @@ def isTypeCopyConstructible(type):
              CGUnionStruct.isUnionCopyConstructible(type)) or
             (type.isDictionary() and
              CGDictionary.isDictionaryCopyConstructible(type.inner)))
 
 
 def wantsAddProperty(desc):
     return (desc.concrete and
             desc.wrapperCache and
-            not desc.interface.getExtendedAttribute("Global"))
+            not (desc.workers and
+                 desc.interface.getExtendedAttribute("Global")))
 
 
 # We'll want to insert the indent at the beginnings of lines, but we
 # don't want to indent empty lines.  So only indent lines that have a
 # non-newline character on them.
 lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE)
 
 
@@ -325,19 +326,56 @@ class CGDOMJSClass(CGThing):
     def declare(self):
         return ""
 
     def define(self):
         traceHook = 'nullptr'
         callHook = LEGACYCALLER_HOOK_NAME if self.descriptor.operations["LegacyCaller"] else 'nullptr'
         slotCount = INSTANCE_RESERVED_SLOTS + self.descriptor.interface.totalMembersInSlots
         classFlags = "JSCLASS_IS_DOMJSCLASS | "
+        classExtensionAndObjectOps = """\
+JS_NULL_CLASS_EXT,
+JS_NULL_OBJECT_OPS
+"""
         if self.descriptor.interface.getExtendedAttribute("Global"):
             classFlags += "JSCLASS_DOM_GLOBAL | JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(DOM_GLOBAL_SLOTS) | JSCLASS_IMPLEMENTS_BARRIERS"
             traceHook = "JS_GlobalObjectTraceHook"
+            if not self.descriptor.workers:
+                classExtensionAndObjectOps = """\
+{
+  nsGlobalWindow::OuterObject, /* outerObject */
+  nullptr, /* innerObject */
+  nullptr, /* iteratorObject */
+  false, /* isWrappedNative */
+  nullptr /* weakmapKeyDelegateOp */
+},
+{
+  nullptr, /* lookupGeneric */
+  nullptr, /* lookupProperty */
+  nullptr, /* lookupElement */
+  nullptr, /* defineGeneric */
+  nullptr, /* defineProperty */
+  nullptr, /* defineElement */
+  nullptr, /* getGeneric  */
+  nullptr, /* getProperty */
+  nullptr, /* getElement */
+  nullptr, /* setGeneric */
+  nullptr, /* setProperty */
+  nullptr, /* setElement */
+  nullptr, /* getGenericAttributes */
+  nullptr, /* setGenericAttributes */
+  nullptr, /* deleteProperty */
+  nullptr, /* deleteElement */
+  nullptr, /* watch */
+  nullptr, /* unwatch */
+  nullptr, /* slice */
+  nullptr, /* enumerate */
+  JS_ObjectToOuterObject /* thisObject */
+}
+"""
         else:
             classFlags += "JSCLASS_HAS_RESERVED_SLOTS(%d)" % slotCount
         if self.descriptor.interface.getExtendedAttribute("NeedNewResolve"):
             newResolveHook = "(JSResolveOp)" + NEWRESOLVE_HOOK_NAME
             classFlags += " | JSCLASS_NEW_RESOLVE"
             enumerateHook = ENUMERATE_HOOK_NAME
         elif self.descriptor.interface.getExtendedAttribute("Global"):
             newResolveHook = "(JSResolveOp) mozilla::dom::ResolveGlobal"
@@ -361,30 +399,30 @@ class CGDOMJSClass(CGThing):
                 ${resolve}, /* resolve */
                 JS_ConvertStub,
                 ${finalize}, /* finalize */
                 ${call}, /* call */
                 nullptr,               /* hasInstance */
                 nullptr,               /* construct */
                 ${trace}, /* trace */
                 JS_NULL_CLASS_SPEC,
-                JS_NULL_CLASS_EXT,
-                JS_NULL_OBJECT_OPS
+                $*{classExtensionAndObjectOps}
               },
               $*{descriptor}
             };
             """,
             name=self.descriptor.interface.identifier.name,
             flags=classFlags,
             addProperty=ADDPROPERTY_HOOK_NAME if wantsAddProperty(self.descriptor) else 'JS_PropertyStub',
             enumerate=enumerateHook,
             resolve=newResolveHook,
             finalize=FINALIZE_HOOK_NAME,
             call=callHook,
             trace=traceHook,
+            classExtensionAndObjectOps=classExtensionAndObjectOps,
             descriptor=DOMClass(self.descriptor))
 
 
 class CGDOMProxyJSClass(CGThing):
     """
     Generate a DOMJSClass for a given proxy descriptor
     """
     def __init__(self, descriptor):
--- a/dom/contacts/ContactManager.js
+++ b/dom/contacts/ContactManager.js
@@ -24,16 +24,17 @@ XPCOMUtils.defineLazyServiceGetter(this,
                                    "nsIMessageSender");
 
 const CONTACTS_SENDMORE_MINIMUM = 5;
 
 // We need this to create a copy of the mozContact object in ContactManager.save
 // Keep in sync with the interfaces.
 const PROPERTIES = [
   "name", "honorificPrefix", "givenName", "additionalName", "familyName",
+  "phoneticGivenName", "phoneticFamilyName",
   "honorificSuffix", "nickname", "photo", "category", "org", "jobTitle",
   "bday", "note", "anniversary", "sex", "genderIdentity", "key", "adr", "email",
   "url", "impp", "tel"
 ];
 
 let mozContactInitWarned = false;
 
 function Contact() { }
--- a/dom/contacts/fallback/ContactDB.jsm
+++ b/dom/contacts/fallback/ContactDB.jsm
@@ -17,17 +17,17 @@ const Ci = Components.interfaces;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/PhoneNumberUtils.jsm");
 Cu.importGlobalProperties(["indexedDB"]);
 
 /* all exported symbols need to be bound to this on B2G - Bug 961777 */
 this.DB_NAME = "contacts";
-this.DB_VERSION = 19;
+this.DB_VERSION = 20;
 this.STORE_NAME = "contacts";
 this.SAVED_GETALL_STORE_NAME = "getallcache";
 const CHUNK_SIZE = 20;
 this.REVISION_STORE = "revision";
 const REVISION_KEY = "revision";
 
 function exportContact(aRecord) {
   if (aRecord) {
@@ -164,16 +164,20 @@ ContactDB.prototype = {
       objectStore.createIndex("givenNameLowerCase",  "search.givenName",  { multiEntry: true });
       objectStore.createIndex("nameLowerCase",       "search.name",       { multiEntry: true });
       objectStore.createIndex("telLowerCase",        "search.tel",        { multiEntry: true });
       objectStore.createIndex("emailLowerCase",      "search.email",      { multiEntry: true });
       objectStore.createIndex("tel", "search.exactTel", { multiEntry: true });
       objectStore.createIndex("category", "properties.category", { multiEntry: true });
       objectStore.createIndex("email", "search.email", { multiEntry: true });
       objectStore.createIndex("telMatch", "search.parsedTel", {multiEntry: true});
+      objectStore.createIndex("phoneticFamilyName", "properties.phoneticFamilyName", { multiEntry: true });
+      objectStore.createIndex("phoneticGivenName", "properties.phoneticGivenName", { multiEntry: true });
+      objectStore.createIndex("phoneticFamilyNameLowerCase", "search.phoneticFamilyName", { multiEntry: true });
+      objectStore.createIndex("phoneticGivenNameLowerCase",  "search.phoneticGivenName",  { multiEntry: true });
       aDb.createObjectStore(SAVED_GETALL_STORE_NAME);
       aDb.createObjectStore(REVISION_STORE).put(0, REVISION_KEY);
     }
 
     let valueUpgradeSteps = [];
 
     function scheduleValueUpgrade(upgradeFunc) {
       var length = valueUpgradeSteps.push(upgradeFunc);
@@ -700,16 +704,27 @@ ContactDB.prototype = {
               }
             });
           }
           return true;
         });
 
         next();
       },
+      function upgrade19to20() {
+        if (DEBUG) debug("upgrade19to20 create schema(phonetic)");
+        if (!objectStore) {
+          objectStore = aTransaction.objectStore(STORE_NAME);
+        }
+        objectStore.createIndex("phoneticFamilyName", "properties.phoneticFamilyName", { multiEntry: true });
+        objectStore.createIndex("phoneticGivenName", "properties.phoneticGivenName", { multiEntry: true });
+        objectStore.createIndex("phoneticFamilyNameLowerCase", "search.phoneticFamilyName", { multiEntry: true });
+        objectStore.createIndex("phoneticGivenNameLowerCase",  "search.phoneticGivenName",  { multiEntry: true });
+        next();
+      },
     ];
 
     let index = aOldVersion;
     let outer = this;
 
     /* This function runs all upgrade functions that are in the
      * valueUpgradeSteps array. These functions have the following properties:
      * - they must be synchronous
@@ -819,16 +834,18 @@ ContactDB.prototype = {
       name:            [],
       givenName:       [],
       familyName:      [],
       email:           [],
       category:        [],
       tel:             [],
       exactTel:        [],
       parsedTel:       [],
+      phoneticFamilyName:   [],
+      phoneticGivenName:    [],
     };
 
     for (let field in aContact.properties) {
       contact.properties[field] = aContact.properties[field];
       // Add search fields
       if (aContact.properties[field] && contact.search[field]) {
         for (let i = 0; i <= aContact.properties[field].length; i++) {
           if (aContact.properties[field][i]) {
@@ -1110,27 +1127,42 @@ ContactDB.prototype = {
     if (DEBUG) debug("getCount");
     this.newTxn("readonly", STORE_NAME, function (txn, store) {
       store.count().onsuccess = function (e) {
         aSuccessCb(e.target.result);
       };
     }, null, aErrorCb);
   },
 
+  getSortByParam: function CDB_getSortByParam(aFindOptions) {
+    switch (aFindOptions.sortBy) {
+      case "familyName":
+        return [ "familyName", "givenName" ];
+      case "givenName":
+        return [ "givenName" , "familyName" ];
+      case "phoneticFamilyName":
+        return [ "phoneticFamilyName" , "phoneticGivenName" ];
+      case "phoneticGivenName":
+        return [ "phoneticGivenName" , "phoneticFamilyName" ];
+      default:
+        return [ "givenName" , "familyName" ];
+    }
+  },
+
   /*
    * Sorting the contacts by sortBy field. aSortBy can either be familyName or givenName.
    * If 2 entries have the same sortyBy field or no sortBy field is present, we continue
    * sorting with the other sortyBy field.
    */
   sortResults: function CDB_sortResults(aResults, aFindOptions) {
     if (!aFindOptions)
       return;
     if (aFindOptions.sortBy != "undefined") {
       const sortOrder = aFindOptions.sortOrder;
-      const sortBy = aFindOptions.sortBy == "familyName" ? [ "familyName", "givenName" ] : [ "givenName" , "familyName" ];
+      const sortBy = this.getSortByParam(aFindOptions);
 
       aResults.sort(function (a, b) {
         let x, y;
         let result = 0;
         let xIndex = 0;
         let yIndex = 0;
 
         do {
--- a/dom/contacts/tests/shared.js
+++ b/dom/contacts/tests/shared.js
@@ -85,26 +85,30 @@ var adr2 = {
   countryName: "country2"
 };
 
 var properties1 = {
   // please keep capital letters at the start of these names
   name: ["Test1 TestFamilyName", "Test2 Wagner"],
   familyName: ["TestFamilyName","Wagner"],
   givenName: ["Test1","Test2"],
+  phoneticFamilyName: ["TestphoneticFamilyName1","TestphoneticFamilyName2"],
+  phoneticGivenName: ["TestphoneticGivenName1","TestphoneticGivenName2"],
   nickname: ["nicktest"],
   tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}, {type: ["home"], value: "+49 451 491934"}],
   adr: [adr1],
   email: [{type: ["work"], value: "x@y.com"}],
 };
 
 var properties2 = {
   name: ["dummyHonorificPrefix dummyGivenName dummyFamilyName dummyHonorificSuffix", "dummyHonorificPrefix2"],
   familyName: ["dummyFamilyName"],
   givenName: ["dummyGivenName"],
+  phoneticFamilyName: ["dummyphoneticFamilyName"],
+  phoneticGivenName: ["dummyphoneticGivenName"],
   honorificPrefix: ["dummyHonorificPrefix","dummyHonorificPrefix2"],
   honorificSuffix: ["dummyHonorificSuffix"],
   additionalName: ["dummyadditionalName"],
   nickname: ["dummyNickname"],
   tel: [{type: ["test"], value: "7932012345", carrier: "myCarrier", pref: 1},{type: ["home", "custom"], value: "7932012346", pref: 0}],
   email: [{type: ["test"], value: "a@b.c"}, {value: "b@c.d", pref: 1}],
   adr: [adr1, adr2],
   impp: [{type: ["aim"], value:"im1", pref: 1}, {value: "im2"}],
@@ -115,16 +119,96 @@ var properties2 = {
   url: [{type: ["work", "work2"], value: "www.1.com", pref: 1}, {value:"www2.com"}],
   bday: new Date("1980, 12, 01"),
   anniversary: new Date("2000, 12, 01"),
   sex: "male",
   genderIdentity: "test",
   key: ["ERPJ394GJJWEVJ0349GJ09W3H4FG0WFW80VHW3408GH30WGH348G3H"]
 };
 
+// To test sorting(CJK)
+var c9 = {
+  phoneticFamilyName: ["a"],
+  phoneticGivenName: ["a"],
+};
+
+var c10 = {
+  phoneticFamilyName: ["b"],
+  phoneticGivenName: ["b"],
+};
+
+var c11 = {
+  phoneticFamilyName: ["c","a","b"],
+  phoneticGivenName: ["c","a","b"],
+};
+
+var c12 = {
+  phoneticFamilyName: ["c","a","c"],
+  phoneticGivenName: ["c","a","c"],
+};
+
+var c13 = {
+  phoneticFamilyName: [],
+  phoneticGivenName: [],
+};
+
+var c14 = {
+  phoneticFamilyName: ["e","e","e"],
+  phoneticGivenName: ["e","e","e"],
+};
+
+var c15 = {
+  phoneticFamilyName: ["e","e","e"],
+  phoneticGivenName: ["e","e","e"],
+};
+
+var c16 = {
+  phoneticFamilyName: ["e","e","e"],
+  phoneticGivenName: ["e","e","e"],
+};
+
+var properties3 = {
+  // please keep capital letters at the start of these names
+  name: ["Taro Yamada", "Ichiro Suzuki"],
+  familyName: ["Yamada","Suzuki"],
+  givenName: ["Taro","Ichiro"],
+  phoneticFamilyName: ["TestPhoneticFamilyYamada","TestPhoneticFamilySuzuki"],
+  phoneticGivenName: ["TestPhoneticGivenTaro","TestPhoneticGivenIchiro"],
+  nickname: ["phoneticNicktest"],
+  tel: [{type: ["work"], value: "123456", carrier: "testCarrier"} , {type: ["home", "fax"], value: "+55 (31) 9876-3456"}, {type: ["home"], value: "+49 451 491934"}],
+  adr: [adr1],
+  email: [{type: ["work"], value: "x@y.com"}],
+};
+
+var properties4 = {
+  name: ["dummyHonorificPrefix dummyTaro dummyYamada dummyHonorificSuffix", "dummyHonorificPrefix2"],
+  familyName: ["dummyYamada"],
+  givenName: ["dummyTaro"],
+  phoneticFamilyName: ["dummyTestPhoneticFamilyYamada"],
+  phoneticGivenName: ["dummyTestPhoneticGivenTaro"],
+  honorificPrefix: ["dummyPhoneticHonorificPrefix","dummyPhoneticHonorificPrefix2"],
+  honorificSuffix: ["dummyPhoneticHonorificSuffix"],
+  additionalName: ["dummyPhoneticAdditionalName"],
+  nickname: ["dummyPhoneticNickname"],
+  tel: [{type: ["test"], value: "7932012345", carrier: "myCarrier", pref: 1},{type: ["home", "custom"], value: "7932012346", pref: 0}],
+  email: [{type: ["test"], value: "a@b.c"}, {value: "b@c.d", pref: 1}],
+  adr: [adr1, adr2],
+  impp: [{type: ["aim"], value:"im1", pref: 1}, {value: "im2"}],
+  org: ["org1", "org2"],
+  jobTitle: ["boss", "superboss"],
+  note: ["test note"],
+  category: ["cat1", "cat2"],
+  url: [{type: ["work", "work2"], value: "www.1.com", pref: 1}, {value:"www2.com"}],
+  bday: new Date("1980, 12, 01"),
+  anniversary: new Date("2000, 12, 01"),
+  sex: "male",
+  genderIdentity: "test",
+  key: ["ERPJ394GJJWEVJ0349GJ09W3H4FG0WFW80VHW3408GH30WGH348G3H"]
+};
+
 var sample_id1;
 var sample_id2;
 
 var createResult1;
 var createResult2;
 
 var findResult1;
 var findResult2;
@@ -260,16 +344,18 @@ function checkContacts(contact1, contact
     ok(false, "Expected both contacts to be either present or absent");
     return;
   }
   checkStrArray(contact1.name, contact2.name, "Same name");
   checkStrArray(contact1.honorificPrefix, contact2.honorificPrefix, "Same honorificPrefix");
   checkStrArray(contact1.givenName, contact2.givenName, "Same givenName");
   checkStrArray(contact1.additionalName, contact2.additionalName, "Same additionalName");
   checkStrArray(contact1.familyName, contact2.familyName, "Same familyName");
+  checkStrArray(contact1.phoneticFamilyName, contact2.phoneticFamilyName, "Same phoneticFamilyName");
+  checkStrArray(contact1.phoneticGivenName, contact2.phoneticGivenName, "Same phoneticGivenName");
   checkStrArray(contact1.honorificSuffix, contact2.honorificSuffix, "Same honorificSuffix");
   checkStrArray(contact1.nickname, contact2.nickname, "Same nickname");
   checkCategory(contact1.category, contact2.category);
   checkStrArray(contact1.org, contact2.org, "Same org");
   checkStrArray(contact1.jobTitle, contact2.jobTitle, "Same jobTitle");
   is(contact1.bday ? contact1.bday.valueOf() : null, contact2.bday ? contact2.bday.valueOf() : null, "Same birthday");
   checkStrArray(contact1.note, contact2.note, "Same note");
   is(contact1.anniversary ? contact1.anniversary.valueOf() : null , contact2.anniversary ? contact2.anniversary.valueOf() : null, "Same anniversary");
--- a/dom/contacts/tests/test_contacts_basics2.html
+++ b/dom/contacts/tests/test_contacts_basics2.html
@@ -740,16 +740,18 @@ var steps = [
     next();
   },
   function() {
     ok(true, "Inline changes to array properties should be seen by save");
     var c = new mozContact({
       name: [],
       familyName: [],
       givenName: [],
+      phoneticFamilyName: [],
+      phoneticGivenName: [],
       nickname: [],
       tel: [],
       adr: [],
       email: []
     });
     for (var prop of Object.getOwnPropertyNames(properties1)) {
       if (!Array.isArray(properties1[prop])) {
         continue;
@@ -821,16 +823,311 @@ var steps = [
         };
         req.onerror = onFailure;
       };
       req.onerror = onFailure;
     };
     req.onerror = onFailure;
   },
   function () {
+    ok(true, "Adding a new contact");
+    createResult1 = new mozContact(properties3);
+    req = mozContacts.save(createResult1)
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      sample_id1 = createResult1.id;
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Adding a new contact2");
+    createResult2 = new mozContact(properties4);
+    req = mozContacts.save(createResult2);
+    req.onsuccess = function () {
+      ok(createResult2.id, "The contact now has an ID.");
+      sample_id2 = createResult2.id;
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find({sortBy: "phoneticFamilyName"});
+    req.onsuccess = function () {
+      is(req.result.length, 2, "Found exactly 2 contact.");
+      checkContacts(req.result[1], properties3);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching contacts by query1");
+    var options = {filterBy: ["phoneticGivenName", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties3.phoneticGivenName[0].substring(0, 3)}
+    req = mozContacts.find(options)
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      ok(findResult1.id == sample_id1, "Same ID");
+      checkContacts(findResult1, createResult1);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Searching contacts by query2");
+    var options = {filterBy: ["phoneticGivenName", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties4.phoneticGivenName[0].substring(0, 3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 1, "Found exactly 1 contact.");
+      findResult1 = req.result[0];
+      is(findResult1.adr.length, 2, "Adr length 2");
+      checkContacts(findResult1, createResult2);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
+    ok(true, "Adding 20 contacts");
+    for (var i=0; i<19; i++) {
+      createResult1 = new mozContact(properties3);
+      req = mozContacts.save(createResult1);
+      req.onsuccess = function () {
+        ok(createResult1.id, "The contact now has an ID.");
+      };
+      req.onerror = onFailure;
+    };
+    createResult1 = new mozContact(properties3);
+    req = mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkStrArray(createResult1.name, properties3.name, "Same Name");
+      checkCount(20, "20 contacts in DB", next);
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find(defaultOptions);
+    req.onsuccess = function () {
+      is(req.result.length, 20, "20 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts2");
+    var options = {filterBy: ["phoneticGivenName"],
+                   filterOp: "startsWith",
+                   filterValue: properties3.phoneticGivenName[0].substring(0, 3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 20, "20 Entries.");
+      checkContacts(createResult1, req.result[19]);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts3");
+    var options = {filterBy: ["phoneticGivenName", "tel", "email"],
+                   filterOp: "startsWith",
+                   filterValue: properties3.phoneticGivenName[0].substring(0, 3)};
+    req = mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 20, "20 Entries.");
+      checkContacts(createResult1, req.result[10]);
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
+    ok(true, "Testing clone contact");
+    createResult1 = new mozContact(properties3);
+    req = mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkStrArray(createResult1.phoneticFamilyName, properties3.phoneticFamilyName, "Same phoneticFamilyName");
+      checkStrArray(createResult1.phoneticGivenName, properties3.phoneticGivenName, "Same phoneticGivenName");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Retrieving all contacts");
+    req = mozContacts.find({sortBy: "phoneticGivenName"});
+    req.onsuccess = function () {
+      is(req.result.length, 1, "1 Entries.");
+      next();
+    }
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c11);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c11, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c10);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c10, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c12);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c12, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c9);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c9, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    var options = {sortBy: "phoneticFamilyName",
+                   sortOrder: "ascending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 4, "4 results");
+      checkContacts(req.result[0], c9);
+      checkContacts(req.result[1], c10);
+      checkContacts(req.result[2], c11);
+      checkContacts(req.result[3], c12);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    var options = {sortBy: "phoneticFamilyName",
+                   sortOrder: "descending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 4, "4 results");
+      checkContacts(req.result[0], c12);
+      checkContacts(req.result[1], c11);
+      checkContacts(req.result[2], c10);
+      checkContacts(req.result[3], c9);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c13);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c13, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting with empty string");
+    var options = {sortBy: "phoneticFamilyName",
+                   sortOrder: "ascending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 5, "5 results");
+      checkContacts(req.result[0], c13);
+      checkContacts(req.result[1], c9);
+      checkContacts(req.result[2], c10);
+      checkContacts(req.result[3], c11);
+      checkContacts(req.result[4], c12);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c15);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c15, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c14);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c14, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    ok(true, "Test sorting");
+    createResult1 = new mozContact(c16);
+    req = navigator.mozContacts.save(createResult1);
+    req.onsuccess = function () {
+      ok(createResult1.id, "The contact now has an ID.");
+      checkContacts(c16, createResult1);
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  function () {
+    // Android does not support published/updated fields. Skip this.
+    if (isAndroid) {
+      next();
+      return;
+    }
+
+    ok(true, "Test sorting with published");
+    var options = {sortBy: "phoneticFamilyName",
+                   sortOrder: "descending"};
+    req = navigator.mozContacts.find(options);
+    req.onsuccess = function () {
+      is(req.result.length, 3, "3 results");
+      ok(req.result[0].published < req.result[1].published, "Right sorting order");
+      ok(req.result[1].published < req.result[2].published, "Right sorting order");
+      next();
+    };
+    req.onerror = onFailure;
+  },
+  clearDatabase,
+  function () {
     ok(true, "all done!\n");
     SimpleTest.finish();
   }
 ];
 
 function next() {
   ok(true, "Begin!");
   if (index >= steps.length) {
--- a/dom/events/test/test_bug508479.html
+++ b/dom/events/test/test_bug508479.html
@@ -8,20 +8,17 @@
 <script>
   
 var gGotHandlingDrop = false;
 var gGotNotHandlingDrop = false;
 
 SimpleTest.waitForExplicitFinish();
 
 function fireEvent(target, event) {
-  var utils =
-    window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
-           getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
-  SpecialPowers.wrap(utils).dispatchDOMEventViaPresShell(target, event, true);
+  SpecialPowers.DOMWindowUtils.dispatchDOMEventViaPresShell(target, event, true);
 }
 
 function fireDrop(element, shouldAllowDrop, shouldAllowOnlyChromeDrop) {
   var ds = SpecialPowers.Cc["@mozilla.org/widget/dragservice;1"].
     getService(SpecialPowers.Ci.nsIDragService);
 
   var dataTransfer;
   var trapDrag = function(event) {
--- a/dom/indexedDB/test/browser_permissionsPromptAllow.js
+++ b/dom/indexedDB/test/browser_permissionsPromptAllow.js
@@ -5,16 +5,19 @@
 
 const testPageURL = "http://mochi.test:8888/browser/" +
   "dom/indexedDB/test/browser_permissionsPrompt.html";
 const notificationID = "indexedDB-permissions-prompt";
 
 function test()
 {
   waitForExplicitFinish();
+
+  PopupNotifications.transitionsEnabled = false;
+
   // We want a prompt.
   setPermission(testPageURL, "indexedDB", "allow");
   executeSoon(test1);
 }
 
 function test1()
 {
   info("creating tab");
@@ -64,16 +67,17 @@ function test2()
          "First database creation was successful");
       ok(!exception, "No exception");
       is(getPermission(testPageURL, "indexedDB"),
          Components.interfaces.nsIPermissionManager.UNKNOWN_ACTION,
          "Correct permission set");
       gBrowser.removeCurrentTab();
       unregisterAllPopupEventHandlers();
       removePermission(testPageURL, "indexedDB");
+      PopupNotifications.transitionsEnabled = true;
       executeSoon(finish);
     });
 
     registerPopupEventHandler("popupshowing", function () {
       ok(false, "Shouldn't show a popup this time");
     });
     registerPopupEventHandler("popupshown", function () {
       ok(false, "Shouldn't show a popup this time");
--- a/dom/indexedDB/test/browser_permissionsPromptDeny.js
+++ b/dom/indexedDB/test/browser_permissionsPromptDeny.js
@@ -6,16 +6,17 @@
 const testPageURL = "http://mochi.test:8888/browser/" +
   "dom/indexedDB/test/browser_permissionsPrompt.html";
 const notificationID = "indexedDB-permissions-prompt";
 
 function test()
 {
   waitForExplicitFinish();
   // We want the prompt.
+  PopupNotifications.transitionsEnabled = false;
   setPermission(testPageURL, "indexedDB", "allow");
   executeSoon(test1);
 }
 
 function test1()
 {
   info("creating tab");
   gBrowser.selectedTab = gBrowser.addTab();
@@ -63,16 +64,17 @@ function test2()
       ok(!result, "No database created");
       is(exception, "InvalidStateError", "Correct exception");
       is(getPermission(testPageURL, "indexedDB"),
          Components.interfaces.nsIPermissionManager.DENY_ACTION,
          "Correct permission set");
       gBrowser.selectedBrowser.docShell.QueryInterface(Ci.nsILoadContext).usePrivateBrowsing = false;
       unregisterAllPopupEventHandlers();
       gBrowser.removeCurrentTab();
+      PopupNotifications.transitionsEnabled = true;
       executeSoon(test3);
     });
 
     registerPopupEventHandler("popupshowing", function () {
       ok(false, "prompt showing");
     });
     registerPopupEventHandler("popupshown", function () {
       ok(false, "prompt shown");
--- a/dom/indexedDB/test/browser_quotaPromptAllow.js
+++ b/dom/indexedDB/test/browser_quotaPromptAllow.js
@@ -7,16 +7,17 @@
 const testPageURL = "http://bug704464-1.example.com/browser/" +
   "dom/indexedDB/test/browser_quotaPrompt.html";
 const notificationID = "indexedDB-quota-prompt";
 
 function test()
 {
   waitForExplicitFinish();
   requestLongerTimeout(10);
+  PopupNotifications.transitionsEnabled = false;
   removePermission(testPageURL, "indexedDB-unlimited");
   Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
   executeSoon(test1);
 }
 
 let addMoreTest1Count = 0;
 
 function test1()
@@ -103,16 +104,17 @@ function test2()
             is(getPermission(testPageURL, "indexedDB-unlimited"),
                Components.interfaces.nsIPermissionManager.ALLOW_ACTION,
                "Correct permission set");
 
             gBrowser.removeCurrentTab();
             unregisterAllPopupEventHandlers();
             removePermission(testPageURL, "indexedDB");
             Services.prefs.clearUserPref("dom.indexedDB.warningQuota");
+            PopupNotifications.transitionsEnabled = true;
             executeSoon(finish);
           });
           executeSoon(function() { dispatchEvent("indexedDB-done"); });
         }
         else {
           ++addMoreCount;
           executeSoon(function() { dispatchEvent("indexedDB-addMore"); });
         }
--- a/dom/indexedDB/test/browser_quotaPromptDatabases.js
+++ b/dom/indexedDB/test/browser_quotaPromptDatabases.js
@@ -7,16 +7,17 @@
 const testPageURL = "http://bug704464-3.example.com/browser/" +
   "dom/indexedDB/test/browser_quotaPromptDatabases.html";
 const notificationID = "indexedDB-quota-prompt";
 
 function test()
 {
   waitForExplicitFinish();
   requestLongerTimeout(10);
+  PopupNotifications.transitionsEnabled = false;
   removePermission(testPageURL, "indexedDB-unlimited");
   Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
   executeSoon(test1);
 }
 
 let addMoreTest1Count = 0;
 
 function test1()
@@ -38,16 +39,17 @@ function test1()
           setFinishedCallback(function(result) {
             is(result, "finished", "Got 'finished' result");
             is(getPermission(testPageURL, "indexedDB-unlimited"),
                Components.interfaces.nsIPermissionManager.ALLOW_ACTION,
                "Correct permission set");
             gBrowser.removeCurrentTab();
             unregisterAllPopupEventHandlers();
             addMoreTest1Count = seenPopupCount;
+            PopupNotifications.transitionsEnabled = true;
             executeSoon(finish);
           });
           executeSoon(function() { dispatchEvent("indexedDB-done"); });
         }
         else {
           ++addMoreTest1Count;
           executeSoon(function() { dispatchEvent("indexedDB-addMore"); });
         }
--- a/dom/indexedDB/test/browser_quotaPromptDelete.js
+++ b/dom/indexedDB/test/browser_quotaPromptDelete.js
@@ -7,16 +7,17 @@
 const testPageURL = "http://bug702292.example.com/browser/" +
   "dom/indexedDB/test/browser_quotaPromptDelete.html";
 const notificationID = "indexedDB-quota-prompt";
 
 function test()
 {
   waitForExplicitFinish();
   requestLongerTimeout(10);
+  PopupNotifications.transitionsEnabled = false;
   removePermission(testPageURL, "indexedDB-unlimited");
   Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
   executeSoon(test1);
 }
 
 let addMoreTest1Count = 0;
 let haveReset = false;
 let secondTimeCount = 0;
@@ -49,16 +50,17 @@ function test1()
               } else {
                 setFinishedCallback(function(result) {
                   is(result, "finished", "Got 'finished' result");
                   is(getPermission(testPageURL, "indexedDB-unlimited"),
                      Components.interfaces.nsIPermissionManager.DENY_ACTION,
                      "Correct permission set");
                   gBrowser.removeCurrentTab();
                   unregisterAllPopupEventHandlers();
+                  PopupNotifications.transitionsEnabled = true;
                   executeSoon(finish);
                 });
                 executeSoon(function() { dispatchEvent("indexedDB-done"); });
               }
             }
 
             function secondTimeThroughAddMore() {
               setFinishedCallback(secondTimeThroughCallback);
--- a/dom/indexedDB/test/browser_quotaPromptDeny.js
+++ b/dom/indexedDB/test/browser_quotaPromptDeny.js
@@ -7,16 +7,17 @@
 const testPageURL = "http://bug704464-2.example.com/browser/" +
   "dom/indexedDB/test/browser_quotaPrompt.html";
 const notificationID = "indexedDB-quota-prompt";
 
 function test()
 {
   waitForExplicitFinish();
   requestLongerTimeout(10);
+  PopupNotifications.transitionsEnabled = false;
   removePermission(testPageURL, "indexedDB-unlimited");
   Services.prefs.setIntPref("dom.indexedDB.warningQuota", 2);
   executeSoon(test1);
 }
 
 let addMoreTest1Count = 0;
 
 function test1()
@@ -43,16 +44,17 @@ function test1()
           setFinishedCallback(function(result) {
             is(result, "finished", "Got 'finished' result");
             is(getPermission(testPageURL, "indexedDB-unlimited"),
                Components.interfaces.nsIPermissionManager.DENY_ACTION,
                "Correct permission set");
             gBrowser.removeCurrentTab();
             unregisterAllPopupEventHandlers();
             addMoreTest1Count = seenPopupCount;
+            PopupNotifications.transitionsEnabled = true;
             executeSoon(test2);
           });
           executeSoon(function() { dispatchEvent("indexedDB-done"); });
         }
         else {
           ++addMoreTest1Count;
           executeSoon(function() { dispatchEvent("indexedDB-addMore"); });
         }
--- a/dom/tests/browser/browser_ConsoleStorageAPITests.js
+++ b/dom/tests/browser/browser_ConsoleStorageAPITests.js
@@ -38,18 +38,17 @@ var ConsoleObserver = {
         is(messages.length, 0, "Cleared Storage");
 
         // make sure a closed window's events are in fact removed from the
         // storage cache
         win.console.log("adding a new event");
         // Close the window.
         gBrowser.removeTab(tab, {animate: false});
         // Ensure actual window destruction is not delayed (too long).
-        window.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIDOMWindowUtils).garbageCollect();
+        SpecialPowers.DOMWindowUtils.garbageCollect();
         // Ensure "inner-window-destroyed" event is processed,
         // so the storage cache is cleared.
         executeSoon(function () {
           // use the old windowID again to see if we have any stray cached messages
           messages = ConsoleAPIStorage.getEvents(windowID);
           is(messages.length, 0, "tab close is clearing the cache");
           finish();
         });
--- a/dom/tests/mochitest/bugs/test_bug346659.html
+++ b/dom/tests/mochitest/bugs/test_bug346659.html
@@ -130,20 +130,17 @@ function messageReceiver(evt) {
       ok(0, "unexpected test number (" + testNumber + ") when data is " + evt.data);
   }
 
   handleTestEnd();
 }
 
 function handleTestEnd() {
   function gc() {
-    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-    window.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
-          .getInterface(Components.interfaces.nsIDOMWindowUtils)
-          .garbageCollect();
+    SpecialPowers.DOMWindowUtils.garbageCollect();
   }
   if (numTestsSet1) {
     if (!--numTestsSet1) {
       // gc to get rid of all the old windows
       gc(); gc(); gc();
       setTimeout(startSecondBatch, 0);
     }
   } else if (numTestsSet2) {
--- a/dom/tests/mochitest/bugs/test_bug397571.html
+++ b/dom/tests/mochitest/bugs/test_bug397571.html
@@ -17,18 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   
 </div>
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 /** Test for Bug 397571 **/
 
 // Get the interface
-var utils = window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor).
-               getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
+var utils = SpecialPowers.DOMWindowUtils.SpecialPowers_wrappedObject;
 
 // Try to call functions without privileges
 var success = false;
 try {
   isForced = utils.docCharsetIsForced;
 }
 catch(e) {
   success = true;
--- a/dom/tests/mochitest/general/file_showModalDialog.html
+++ b/dom/tests/mochitest/general/file_showModalDialog.html
@@ -1,15 +1,15 @@
 <!DOCTYPE html>
 <html>
 <head>
 <script>
   function go() {
     is(SpecialPowers.wrap(window).location.toString(), location.toString(), "sanity");
-    is(SpecialPowers.Cu.getClassName(window, /* aUnwrap = */ true), "ModalContentWindow", "We are modal");
+    ok("returnValue" in window && "dialogArguments" in window, "We are modal");
     var iwin = document.getElementById('ifr').contentWindow;
     is(SpecialPowers.Cu.getClassName(iwin, /* aUnwrap = */ true), "Window", "Descendant frames should not be modal");
 
     if (location.origin != "http://mochi.test:8888") {
       is(window.dialogArguments, undefined,
         "dialogArguments should be undefined cross-origin: " + location.origin);
     }
 
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -1219,37 +1219,43 @@ function createInterfaceMap(isXBLScope) 
   var isDesktop = !/Mobile|Tablet/.test(navigator.userAgent);
   var isB2G = !isDesktop && !navigator.userAgent.contains("Android");
   var hasPermission = function (aPermission) {
     return SpecialPowers.hasPermission(aPermission, window.document);
   };
 
   var interfaceMap = {};
 
-  function addInterfaces(interfaces, shouldExpect)
+  function addInterfaces(interfaces)
   {
     for (var entry of interfaces) {
       if (typeof(entry) === "string") {
-        interfaceMap[entry] = shouldExpect;
+        interfaceMap[entry] = true;
       } else if ((entry.nightly === !isNightly) ||
                  (entry.xbl === !isXBLScope) ||
                  (entry.desktop === !isDesktop) ||
                  (entry.b2g === !isB2G) ||
                  (entry.release === !isRelease) ||
                  (entry.pref && !prefs.getBoolPref(entry.pref))  ||
                  (entry.permission && !hasPermission(entry.permission))) {
         interfaceMap[entry.name] = false;
       } else {
-        interfaceMap[entry.name] = shouldExpect;
+        interfaceMap[entry.name] = true;
       }
     }
   }
 
-  addInterfaces(ecmaGlobals, true);
-  addInterfaces(interfaceNamesInGlobalScope, true);
+  addInterfaces(ecmaGlobals);
+  addInterfaces(interfaceNamesInGlobalScope);
+  if (isXBLScope) {
+    // We expose QueryInterface to XBL scopes. It's not an interface but we
+    // need to handle it because it's an own property of the global and the
+    // property name starts with an uppercase letter.
+    interfaceMap["QueryInterface"] = true;
+  }
 
   return interfaceMap;
 }
 
 function runTest(isXBLScope) {
   var interfaceMap = createInterfaceMap(isXBLScope);
   for (var name of Object.getOwnPropertyNames(window)) {
     // An interface name should start with an upper case character.
--- a/dom/webidl/Contacts.webidl
+++ b/dom/webidl/Contacts.webidl
@@ -39,18 +39,20 @@ dictionary ContactProperties {
   sequence<ContactField>?    url;
   sequence<ContactField>?    impp;
 
   sequence<ContactTelField>? tel;
 
   sequence<DOMString>?           name;
   sequence<DOMString>?           honorificPrefix;
   sequence<DOMString>?           givenName;
+  sequence<DOMString>?           phoneticGivenName;
   sequence<DOMString>?           additionalName;
   sequence<DOMString>?           familyName;
+  sequence<DOMString>?           phoneticFamilyName;
   sequence<DOMString>?           honorificSuffix;
   sequence<DOMString>?           nickname;
   sequence<DOMString>?           category;
   sequence<DOMString>?           org;
   sequence<DOMString>?           jobTitle;
   sequence<DOMString>?           note;
   sequence<DOMString>?           key;
 };
@@ -76,18 +78,20 @@ interface mozContact {
   [Cached, Pure] attribute sequence<ContactField>?    url;
   [Cached, Pure] attribute sequence<ContactField>?    impp;
 
   [Cached, Pure] attribute sequence<ContactTelField>? tel;
 
   [Cached, Pure] attribute sequence<DOMString>?       name;
   [Cached, Pure] attribute sequence<DOMString>?       honorificPrefix;
   [Cached, Pure] attribute sequence<DOMString>?       givenName;
+  [Cached, Pure] attribute sequence<DOMString>?       phoneticGivenName;
   [Cached, Pure] attribute sequence<DOMString>?       additionalName;
   [Cached, Pure] attribute sequence<DOMString>?       familyName;
+  [Cached, Pure] attribute sequence<DOMString>?       phoneticFamilyName;
   [Cached, Pure] attribute sequence<DOMString>?       honorificSuffix;
   [Cached, Pure] attribute sequence<DOMString>?       nickname;
   [Cached, Pure] attribute sequence<DOMString>?       category;
   [Cached, Pure] attribute sequence<DOMString>?       org;
   [Cached, Pure] attribute sequence<DOMString>?       jobTitle;
   [Cached, Pure] attribute sequence<DOMString>?       note;
   [Cached, Pure] attribute sequence<DOMString>?       key;
 
--- a/dom/webidl/LegacyQueryInterface.webidl
+++ b/dom/webidl/LegacyQueryInterface.webidl
@@ -81,12 +81,13 @@ StyleSheet implements LegacyQueryInterfa
 Text implements LegacyQueryInterface;
 Touch implements LegacyQueryInterface;
 TouchList implements LegacyQueryInterface;
 TreeColumns implements LegacyQueryInterface;
 TreeWalker implements LegacyQueryInterface;
 UndoManager implements LegacyQueryInterface;
 ValidityState implements LegacyQueryInterface;
 WebSocket implements LegacyQueryInterface;
+Window implements LegacyQueryInterface;
 XMLHttpRequest implements LegacyQueryInterface;
 XMLHttpRequestUpload implements LegacyQueryInterface;
 XMLSerializer implements LegacyQueryInterface;
 XPathEvaluator implements LegacyQueryInterface;
--- a/dom/webidl/Window.webidl
+++ b/dom/webidl/Window.webidl
@@ -11,16 +11,17 @@
  * http://dev.w3.org/csswg/cssom-view/
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/RequestAnimationFrame/Overview.html
  * https://dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview.html
  * https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
  * http://dvcs.w3.org/hg/speech-api/raw-file/tip/speechapi.html
  */
 
 interface ApplicationCache;
+interface IID;
 interface MozFrameRequestCallback;
 interface nsIBrowserDOMWindow;
 interface nsIMessageBroadcaster;
 interface nsIDOMCrypto;
 typedef any Transferable;
 
 // http://www.whatwg.org/specs/web-apps/current-work/
 [Global, NeedNewResolve]
@@ -220,16 +221,24 @@ partial interface Window {
 [NoInterfaceObject]
 interface SpeechSynthesisGetter {
   [Throws, Pref="media.webspeech.synth.enabled"] readonly attribute SpeechSynthesis speechSynthesis;
 };
 
 Window implements SpeechSynthesisGetter;
 #endif
 
+// http://www.whatwg.org/specs/web-apps/current-work/
+[NoInterfaceObject]
+interface WindowModal {
+  [Throws, Func="nsGlobalWindow::IsModalContentWindow"] readonly attribute any dialogArguments;
+  [Throws, Func="nsGlobalWindow::IsModalContentWindow"] attribute any returnValue;
+};
+Window implements WindowModal;
+
 // Mozilla-specific stuff
 partial interface Window {
   //[NewObject, Throws] CSSStyleDeclaration getDefaultComputedStyle(Element elt, optional DOMString pseudoElt = "");
   [NewObject, Throws] CSSStyleDeclaration? getDefaultComputedStyle(Element elt, optional DOMString pseudoElt = "");
 
   [Throws] long mozRequestAnimationFrame(MozFrameRequestCallback aCallback);
 
   /**
@@ -336,16 +345,18 @@ partial interface Window {
   [Throws, ChromeOnly] WindowProxy? openDialog(optional DOMString url = "",
                                                optional DOMString name = "",
                                                optional DOMString options = "",
                                                any... extraArguments);
 
   [Replaceable, Throws] readonly attribute object? content;
 
   [ChromeOnly, Throws] readonly attribute object? __content;
+
+  [Throws, ChromeOnly] any getInterface(IID iid);
 };
 
 Window implements TouchEventHandlers;
 
 Window implements OnErrorEventHandlerForWindow;
 
 // ConsoleAPI
 partial interface Window {
--- a/editor/composer/test/test_bug519928.html
+++ b/editor/composer/test/test_bug519928.html
@@ -17,17 +17,18 @@ https://bugzilla.mozilla.org/show_bug.cg
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
 var iframe = document.getElementById("load-frame");
 
 function enableJS() allowJS(true, iframe);
 function disableJS() allowJS(false, iframe);
 function allowJS(allow, frame) {
-  SpecialPowers.wrap(frame.contentWindow.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor))
+  SpecialPowers.wrap(frame.contentWindow)
+               .QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
                .getInterface(SpecialPowers.Ci.nsIWebNavigation)
                .QueryInterface(SpecialPowers.Ci.nsIDocShell)
                .allowJavascript = allow;
 }
 function expectJSAllowed(allowed, testCondition, callback) {
   window.ICanRunMyJS = false;
   var self_ = window;
   testCondition();
--- a/editor/libeditor/html/tests/test_bug432225.html
+++ b/editor/libeditor/html/tests/test_bug432225.html
@@ -31,17 +31,18 @@ function getEdit() {
 
 function editDoc() {
   return getEdit().contentDocument;
 }
 
 function getSpellCheckSelection() {
   var Ci = SpecialPowers.Ci;
   var win = editDoc().defaultView;
-  var editingSession = SpecialPowers.wrap(win.QueryInterface(Ci.nsIInterfaceRequestor))
+  var editingSession = SpecialPowers.wrap(win)
+                                    .QueryInterface(Ci.nsIInterfaceRequestor)
                                     .getInterface(Ci.nsIWebNavigation)
                                     .QueryInterface(Ci.nsIInterfaceRequestor)
                                     .getInterface(Ci.nsIEditingSession);
   var editor = editingSession.getEditorForWindow(win);
   var selcon = editor.selectionController;
   return selcon.getSelection(selcon.SELECTION_SPELLCHECK);  
 }
 
--- a/editor/libeditor/html/tests/test_bug468353.html
+++ b/editor/libeditor/html/tests/test_bug468353.html
@@ -48,17 +48,18 @@ function runTest() {
   var editdoc = editframe.document;
   var editor = null;
   editdoc.write('');
   editdoc.close();
 
   editdoc.designMode='on';
 
   // Hold the reference to the editor
-  editor = SpecialPowers.wrap(editframe.QueryInterface(Ci.nsIInterfaceRequestor))
+  editor = SpecialPowers.wrap(editframe)
+                        .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIEditingSession)
                         .getEditorForWindow(editframe);
 
   styleSheets = editor.QueryInterface(Ci.nsIEditorStyleSheets);
 
   editdoc.designMode='off';
@@ -67,34 +68,36 @@ function runTest() {
   
   // Let go
   editor = null;
   styleSheets = null;
   
   editdoc.body.contentEditable = true;
   
   // Hold the reference to the editor
-  editor = SpecialPowers.wrap(editframe.QueryInterface(Ci.nsIInterfaceRequestor))
+  editor = SpecialPowers.wrap(editframe)
+                        .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIEditingSession)
                         .getEditorForWindow(editframe);
 
   styleSheets = editor.QueryInterface(Ci.nsIEditorStyleSheets);
   
   editdoc.body.contentEditable = false;
   
   checkStylesheets();
 
   editdoc.designMode = "on";
   editdoc.body.contentEditable = true;
   editdoc.designMode = "off";
 
   // Hold the reference to the editor
-  editor = SpecialPowers.wrap(editframe.QueryInterface(Ci.nsIInterfaceRequestor))
+  editor = SpecialPowers.wrap(editframe)
+                        .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIWebNavigation)
                         .QueryInterface(Ci.nsIInterfaceRequestor)
                         .getInterface(Ci.nsIEditingSession)
                         .getEditorForWindow(editframe);
 
   styleSheets = editor.QueryInterface(Ci.nsIEditorStyleSheets);
 
   editdoc.body.contentEditable = false;
--- a/gfx/2d/DataSurfaceHelpers.cpp
+++ b/gfx/2d/DataSurfaceHelpers.cpp
@@ -43,16 +43,39 @@ CopySurfaceDataToPackedArray(uint8_t* aS
     for (int row = 0; row < aSrcSize.height; ++row) {
       memcpy(aDst, aSrc, packedStride);
       aSrc += aSrcStride;
       aDst += packedStride;
     }
   }
 }
 
+void
+CopyBGRXSurfaceDataToPackedBGRArray(uint8_t* aSrc, uint8_t* aDst,
+                                    IntSize aSrcSize, int32_t aSrcStride)
+{
+  int packedStride = aSrcSize.width * 3;
+
+  uint8_t* srcPx = aSrc;
+  uint8_t* dstPx = aDst;
+
+  for (int row = 0; row < aSrcSize.height; ++row) {
+    for (int col = 0; col < aSrcSize.height; ++col) {
+      dstPx[0] = srcPx[0];
+      dstPx[1] = srcPx[1];
+      dstPx[2] = srcPx[2];
+      // srcPx[3] (unused or alpha component) dropped on floor
+      srcPx += 4;
+      dstPx += 3;
+    }
+    srcPx = aSrc += aSrcStride;
+    dstPx = aDst += packedStride;
+  }
+}
+
 uint8_t*
 SurfaceToPackedBGRA(DataSourceSurface *aSurface)
 {
   SurfaceFormat format = aSurface->GetFormat();
   if (format != SurfaceFormat::B8G8R8A8 && format != SurfaceFormat::B8G8R8X8) {
     return nullptr;
   }
 
@@ -77,10 +100,42 @@ SurfaceToPackedBGRA(DataSourceSurface *a
   if (format == SurfaceFormat::B8G8R8X8) {
     // Convert BGRX to BGRA by setting a to 255.
     ConvertBGRXToBGRA(reinterpret_cast<uint8_t *>(imageBuffer), size, size.width * sizeof(uint32_t));
   }
 
   return imageBuffer;
 }
 
+uint8_t*
+SurfaceToPackedBGR(DataSourceSurface *aSurface)
+{
+  SurfaceFormat format = aSurface->GetFormat();
+  MOZ_ASSERT(format == SurfaceFormat::B8G8R8X8, "Format not supported");
+
+  if (format != SurfaceFormat::B8G8R8X8) {
+    // To support B8G8R8A8 we'd need to un-pre-multiply alpha
+    return nullptr;
+  }
+
+  IntSize size = aSurface->GetSize();
+
+  uint8_t* imageBuffer = new (std::nothrow) uint8_t[size.width * size.height * 3 * sizeof(uint8_t)];
+  if (!imageBuffer) {
+    return nullptr;
+  }
+
+  DataSourceSurface::MappedSurface map;
+  if (!aSurface->Map(DataSourceSurface::MapType::READ, &map)) {
+    delete [] imageBuffer;
+    return nullptr;
+  }
+
+  CopyBGRXSurfaceDataToPackedBGRArray(map.mData, imageBuffer, size,
+                                      map.mStride);
+
+  aSurface->Unmap();
+
+  return imageBuffer;
+}
+
 }
 }
--- a/gfx/2d/DataSurfaceHelpers.h
+++ b/gfx/2d/DataSurfaceHelpers.h
@@ -25,10 +25,22 @@ CopySurfaceDataToPackedArray(uint8_t* aS
 
 /**
  * Convert aSurface to a packed buffer in BGRA format. The pixel data is
  * returned in a buffer allocated with new uint8_t[].
  */
 uint8_t*
 SurfaceToPackedBGRA(DataSourceSurface *aSurface);
 
+/**
+ * Convert aSurface to a packed buffer in BGR format. The pixel data is
+ * returned in a buffer allocated with new uint8_t[].
+ *
+ * This function is currently only intended for use with surfaces of format
+ * SurfaceFormat::B8G8R8X8 since the X components of the pixel data are simply
+ * dropped (no attempt is made to un-pre-multiply alpha from the color
+ * components).
+ */
+uint8_t*
+SurfaceToPackedBGR(DataSourceSurface *aSurface);
+
 }
 }
--- a/gfx/gl/GLContextProviderGLX.cpp
+++ b/gfx/gl/GLContextProviderGLX.cpp
@@ -10,16 +10,17 @@
 #elif defined(MOZ_WIDGET_QT)
 #define GET_NATIVE_WINDOW(aWidget) (Window)(aWidget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW))
 #endif
 
 #include <X11/Xlib.h>
 #include <X11/Xutil.h>
 
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/StaticPtr.h"
 #include "mozilla/X11Util.h"
 
 #include "prenv.h"
 #include "GLContextProvider.h"
 #include "GLLibraryLoader.h"
 #include "nsDebug.h"
 #include "nsIWidget.h"
 #include "GLXLibrary.h"
@@ -1183,34 +1184,44 @@ GLContextProviderGLX::CreateOffscreen(co
         return nullptr;
 
     if (!glContext->InitOffscreen(ToIntSize(size), caps))
         return nullptr;
 
     return glContext.forget();
 }
 
-static nsRefPtr<GLContext> gGlobalContext;
-// TODO move that out of static initializaion
-static bool gUseContextSharing = getenv("MOZ_DISABLE_CONTEXT_SHARING_GLX") == 0;
+static StaticRefPtr<GLContext> gGlobalContext;
 
 GLContext*
 GLContextProviderGLX::GetGlobalContext()
 {
+    static bool checkedContextSharing = false;
+    static bool useContextSharing = false;
+
+    if (!checkedContextSharing) {
+        useContextSharing = getenv("MOZ_DISABLE_CONTEXT_SHARING_GLX") == 0;
+        checkedContextSharing = true;
+    }
+
     // TODO: get GLX context sharing to work well with multiple threads
-    if (!gUseContextSharing) {
+    if (!useContextSharing) {
         return nullptr;
     }
 
     static bool triedToCreateContext = false;
     if (!triedToCreateContext && !gGlobalContext) {
         triedToCreateContext = true;
 
         gfxIntSize dummySize = gfxIntSize(16, 16);
-        gGlobalContext = CreateOffscreenPixmapContext(dummySize);
+        // StaticPtr doesn't support assignments from already_AddRefed,
+        // so use a temporary nsRefPtr to make the reference counting
+        // fall out correctly.
+        nsRefPtr<GLContext> holder = CreateOffscreenPixmapContext(dummySize);
+        gGlobalContext = holder;
     }
 
     return gGlobalContext;
 }
 
 void
 GLContextProviderGLX::Shutdown()
 {
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -279,29 +279,36 @@ TextureClient::CreateTextureClientForDra
                                              const gfx::IntSize& aSizeHint)
 {
   if (aMoz2DBackend == gfx::BackendType::NONE) {
     aMoz2DBackend = gfxPlatform::GetPlatform()->GetContentBackend();
   }
 
   RefPtr<TextureClient> result;
 
+#if defined(MOZ_WIDGET_GONK) || defined(XP_WIN)
+  int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
+#endif
+
 #ifdef XP_WIN
   LayersBackend parentBackend = aAllocator->GetCompositorBackendType();
   if (parentBackend == LayersBackend::LAYERS_D3D11 &&
       (aMoz2DBackend == gfx::BackendType::DIRECT2D ||
         aMoz2DBackend == gfx::BackendType::DIRECT2D1_1) &&
       gfxWindowsPlatform::GetPlatform()->GetD2DDevice() &&
-
+      aSizeHint.width <= maxTextureSize &&
+      aSizeHint.height <= maxTextureSize &&
       !(aTextureFlags & TEXTURE_ALLOC_FALLBACK)) {
     result = new TextureClientD3D11(aFormat, aTextureFlags);
   }
   if (parentBackend == LayersBackend::LAYERS_D3D9 &&
       aMoz2DBackend == gfx::BackendType::CAIRO &&
       aAllocator->IsSameProcess() &&
+      aSizeHint.width <= maxTextureSize &&
+      aSizeHint.height <= maxTextureSize &&
       !(aTextureFlags & TEXTURE_ALLOC_FALLBACK)) {
     if (!gfxWindowsPlatform::GetPlatform()->GetD3D9Device()) {
       result = new DIBTextureClientD3D9(aFormat, aTextureFlags);
     } else {
       result = new CairoTextureClientD3D9(aFormat, aTextureFlags);
     }
   }
 #endif
@@ -332,17 +339,16 @@ TextureClient::CreateTextureClientForDra
 #endif
 #endif
 #endif
 
 #ifdef MOZ_WIDGET_GONK
   if (!DisableGralloc(aFormat, aSizeHint)) {
     // Don't allow Gralloc texture clients to exceed the maximum texture size.
     // BufferTextureClients have code to handle tiling the surface client-side.
-    int32_t maxTextureSize = aAllocator->GetMaxTextureSize();
     if (aSizeHint.width <= maxTextureSize && aSizeHint.height <= maxTextureSize) {
       result = new GrallocTextureClientOGL(aAllocator, aFormat, aMoz2DBackend,
                                            aTextureFlags);
     }
   }
 #endif
 
   // Can't do any better than a buffer texture client.
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -168,16 +168,19 @@ TextureClientD3D11::~TextureClientD3D11(
     UnlockD3DTexture(mTexture.get());
   }
 #endif
 }
 
 bool
 TextureClientD3D11::Lock(OpenMode aMode)
 {
+  if (!mTexture) {
+    return false;
+  }
   MOZ_ASSERT(!mIsLocked, "The Texture is already locked!");
   LockD3DTexture(mTexture.get());
   mIsLocked = true;
 
   if (mNeedsClear) {
     mDrawTarget = GetAsDrawTarget();
     mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
     mNeedsClear = false;
@@ -185,16 +188,17 @@ TextureClientD3D11::Lock(OpenMode aMode)
 
   return true;
 }
 
 void
 TextureClientD3D11::Unlock()
 {
   MOZ_ASSERT(mIsLocked, "Unlocked called while the texture is not locked!");
+
   if (mDrawTarget) {
     // see the comment on TextureClientDrawTarget::GetAsDrawTarget.
     // This DrawTarget is internal to the TextureClient and is only exposed to the
     // outside world between Lock() and Unlock(). This assertion checks that no outside
     // reference remains by the time Unlock() is called.
     MOZ_ASSERT(mDrawTarget->refCount() == 1);
     mDrawTarget->Flush();
   }
@@ -205,31 +209,35 @@ TextureClientD3D11::Unlock()
   mIsLocked = false;
 }
 
 TemporaryRef<DrawTarget>
 TextureClientD3D11::GetAsDrawTarget()
 {
   MOZ_ASSERT(mIsLocked, "Calling TextureClient::GetAsDrawTarget without locking :(");
 
+  if (!mTexture) {
+    return nullptr;
+  }
+
   if (mDrawTarget) {
     return mDrawTarget;
   }
 
   mDrawTarget = Factory::CreateDrawTargetForD3D10Texture(mTexture, mFormat);
   return mDrawTarget;
 }
 
 bool
 TextureClientD3D11::AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags aFlags)
 {
   mSize = aSize;
   ID3D10Device* device = gfxWindowsPlatform::GetPlatform()->GetD3D10Device();
 
-  CD3D10_TEXTURE2D_DESC newDesc(SurfaceFormatToDXGIFormat(mFormat),
+  CD3D10_TEXTURE2D_DESC newDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
                                 aSize.width, aSize.height, 1, 1,
                                 D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE);
 
   newDesc.MiscFlags = D3D10_RESOURCE_MISC_SHARED_KEYEDMUTEX;
 
   HRESULT hr = device->CreateTexture2D(&newDesc, nullptr, byRef(mTexture));
 
   if (FAILED(hr)) {
@@ -327,20 +335,20 @@ DXGITextureHostD3D11::GetTextureSources(
   return mTextureSource.get();
 }
 
 bool
 DataTextureSourceD3D11::Update(DataSourceSurface* aSurface,
                                nsIntRegion* aDestRegion,
                                IntPoint* aSrcOffset)
 {
-  // Right now we only support null aDestRegion and aSrcOffset (which means)
-  // full surface update. Incremental update is only used on Mac so it is
-  // not clear that we ever will need to support it for D3D.
-  MOZ_ASSERT(!aDestRegion && !aSrcOffset);
+  // Right now we only support full surface update. If aDestRegion is provided,
+  // It will be ignored. Incremental update with a source offset is only used
+  // on Mac so it is not clear that we ever will need to support it for D3D.
+  MOZ_ASSERT(!aSrcOffset);
   MOZ_ASSERT(aSurface);
 
   if (!mCompositor || !mCompositor->GetDevice()) {
     return false;
   }
 
   uint32_t bpp = BytesPerPixel(aSurface->GetFormat());
   DXGI_FORMAT dxgiFormat = SurfaceFormatToDXGIFormat(aSurface->GetFormat());
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -371,20 +371,20 @@ DataTextureSourceD3D9::GetD3D9Texture()
                     : mTexture;
 }
 
 bool
 DataTextureSourceD3D9::Update(gfx::DataSourceSurface* aSurface,
                               nsIntRegion* aDestRegion,
                               gfx::IntPoint* aSrcOffset)
 {
-  // Right now we only support null aDestRegion and aSrcOffset (which means
-  // full surface update). Incremental update is only used on Mac so it is
-  // not clear that we ever will need to support it for D3D.
-  MOZ_ASSERT(!aDestRegion && !aSrcOffset);
+  // Right now we only support full surface update. If aDestRegion is provided,
+  // It will be ignored. Incremental update with a source offset is only used
+  // on Mac so it is not clear that we ever will need to support it for D3D.
+  MOZ_ASSERT(!aSrcOffset);
 
   if (!mCompositor || !mCompositor->device()) {
     NS_WARNING("No D3D device to update the texture.");
     return false;
   }
   mSize = aSurface->GetSize();
 
   uint32_t bpp = 0;
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -389,17 +389,16 @@ GrallocTextureHostOGL::GetRenderState()
 TemporaryRef<gfx::DataSourceSurface>
 GrallocTextureHostOGL::GetAsSurface() {
   return mTextureSource ? mTextureSource->GetAsSurface()
                         : nullptr;
 }
 
 TemporaryRef<gfx::DataSourceSurface>
 GrallocTextureSourceOGL::GetAsSurface() {
-  MOZ_ASSERT(gl());
   if (!IsValid()) {
     return nullptr;
   }
   gl()->MakeCurrent();
 
   GLuint tex = GetGLTexture();
   gl()->fActiveTexture(LOCAL_GL_TEXTURE0);
   gl()->fBindTexture(GetTextureTarget(), tex);
--- a/gfx/tests/mochitest/test_bug513439.html
+++ b/gfx/tests/mochitest/test_bug513439.html
@@ -20,18 +20,17 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 513439 **/
 
 var prefService = SpecialPowers.Cc["@mozilla.org/preferences-service;1"]
                                .getService(SpecialPowers.Ci.nsIPrefService);
 var layoutCSSBranch = prefService.getBranch("layout.css.");
 var oldVal = layoutCSSBranch.getCharPref("devPixelsPerPx");
 
 try {
-  var domWindowUtils = window.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-                             .getInterface(SpecialPowers.Ci.nsIDOMWindowUtils);
+  var domWindowUtils = SpecialPowers.DOMWindowUtils;
   var devPxPerCSSPx = domWindowUtils.screenPixelsPerCSSPixel;
 
   layoutCSSBranch.setCharPref("devPixelsPerPx", "2");
   is(domWindowUtils.screenPixelsPerCSSPixel, 2, "devPixelsPerPx wasn't set correctly");
 
   layoutCSSBranch.setCharPref("devPixelsPerPx", "1.5");
   is(domWindowUtils.screenPixelsPerCSSPixel, 1.5, "devPixelsPerPx wasn't set correctly");
 
--- a/gfx/thebes/gfxAndroidPlatform.cpp
+++ b/gfx/thebes/gfxAndroidPlatform.cpp
@@ -346,16 +346,24 @@ gfxFontEntry*
 gfxAndroidPlatform::MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
                                      const uint8_t *aFontData, uint32_t aLength)
 {
     return gfxPlatformFontList::PlatformFontList()->MakePlatformFont(aProxyEntry,
                                                                      aFontData,
                                                                      aLength);
 }
 
+gfxFontEntry*
+gfxAndroidPlatform::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
+                                    const nsAString& aFontName)
+{
+    return gfxPlatformFontList::PlatformFontList()->LookupLocalFont(aProxyEntry,
+                                                                    aFontName);
+}
+
 TemporaryRef<ScaledFont>
 gfxAndroidPlatform::GetScaledFontForFont(DrawTarget* aTarget, gfxFont *aFont)
 {
     return GetScaledFontForFontWithCairoSkia(aTarget, aFont);
 }
 
 bool
 gfxAndroidPlatform::FontHintingEnabled()
--- a/gfx/thebes/gfxAndroidPlatform.h
+++ b/gfx/thebes/gfxAndroidPlatform.h
@@ -44,16 +44,18 @@ public:
     // to support IPC font list (sharing between chrome and content)
     void GetFontList(InfallibleTArray<FontListEntry>* retValue);
 
     // platform implementations of font functions
     virtual bool IsFontFormatSupported(nsIURI *aFontURI, uint32_t aFormatFlags);
     virtual gfxPlatformFontList* CreatePlatformFontList();
     virtual gfxFontEntry* MakePlatformFont(const gfxProxyFontEntry *aProxyEntry,
                                            const uint8_t *aFontData, uint32_t aLength);
+    virtual gfxFontEntry* LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
+                                          const nsAString& aFontName);
 
     virtual void GetCommonFallbackFonts(const uint32_t aCh,
                                         int32_t aRunScript,
                                         nsTArray<const char*>& aFontList);
 
     virtual nsresult GetFontList(nsIAtom *aLangGroup,
                                  const nsACString& aGenericFamily,
                                  nsTArray<nsString>& aListOfFonts);
--- a/gfx/thebes/gfxFT2FontList.cpp
+++ b/gfx/thebes/gfxFT2FontList.cpp
@@ -1381,41 +1381,46 @@ gfxFT2FontList::InitFontList()
 }
 
 struct FullFontNameSearch {
     FullFontNameSearch(const nsAString& aFullName)
         : mFullName(aFullName), mFontEntry(nullptr)
     { }
 
     nsString     mFullName;
-    gfxFontEntry *mFontEntry;
+    FT2FontEntry *mFontEntry;
 };
 
 // callback called for each family name, based on the assumption that the 
 // first part of the full name is the family name
 static PLDHashOperator
 FindFullName(nsStringHashKey::KeyType aKey,
              nsRefPtr<gfxFontFamily>& aFontFamily,
              void* userArg)
 {
     FullFontNameSearch *data = reinterpret_cast<FullFontNameSearch*>(userArg);
 
     // does the family name match up to the length of the family name?
     const nsString& family = aFontFamily->Name();
-    
+
     nsString fullNameFamily;
     data->mFullName.Left(fullNameFamily, family.Length());
 
     // if so, iterate over faces in this family to see if there is a match
-    if (family.Equals(fullNameFamily)) {
+    if (family.Equals(fullNameFamily, nsCaseInsensitiveStringComparator())) {
         nsTArray<nsRefPtr<gfxFontEntry> >& fontList = aFontFamily->GetFontList();
         int index, len = fontList.Length();
         for (index = 0; index < len; index++) {
-            if (fontList[index]->Name().Equals(data->mFullName)) {
-                data->mFontEntry = fontList[index];
+            gfxFontEntry* fe = fontList[index];
+            if (!fe) {
+                continue;
+            }
+            if (fe->Name().Equals(data->mFullName,
+                                  nsCaseInsensitiveStringComparator())) {
+                data->mFontEntry = static_cast<FT2FontEntry*>(fe);
                 return PL_DHASH_STOP;
             }
         }
     }
 
     return PL_DHASH_NEXT;
 }
 
@@ -1423,17 +1428,42 @@ gfxFontEntry*
 gfxFT2FontList::LookupLocalFont(const gfxProxyFontEntry *aProxyEntry,
                                 const nsAString& aFontName)
 {
     // walk over list of names
     FullFontNameSearch data(aFontName);
 
     mFontFamilies.Enumerate(FindFullName, &data);
 
-    return data.mFontEntry;
+    if (!data.mFontEntry) {
+        return nullptr;
+    }
+
+    // Clone the font entry so that we can then set its style descriptors
+    // from the proxy rather than the actual font.
+
+    // Ensure existence of mFTFace in the original entry
+    data.mFontEntry->CairoFontFace();
+    if (!data.mFontEntry->mFTFace) {
+        return nullptr;
+    }
+
+    FT2FontEntry* fe =
+        FT2FontEntry::CreateFontEntry(data.mFontEntry->mFTFace,
+                                      data.mFontEntry->mFilename.get(),
+                                      data.mFontEntry->mFTFontIndex,
+                                      data.mFontEntry->Name(), nullptr);
+    if (fe) {
+        fe->mItalic = aProxyEntry->mItalic;
+        fe->mWeight = aProxyEntry->mWeight;
+        fe->mStretch = aProxyEntry->mStretch;
+        fe->mIsUserFont = fe->mIsLocalUserFont = true;
+    }
+
+    return fe;
 }
 
 gfxFontFamily*
 gfxFT2FontList::GetDefaultFont(const gfxFontStyle* aStyle)
 {
 #ifdef XP_WIN
     HGDIOBJ hGDI = ::GetStockObject(SYSTEM_FONT);
     LOGFONTW logFont;
--- a/gfx/thebes/gfxUtils.cpp
+++ b/gfx/thebes/gfxUtils.cpp
@@ -2,16 +2,18 @@
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "gfxUtils.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxDrawable.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
 #include "nsRegion.h"
 #include "yuv_convert.h"
 #include "ycbcr_to_rgb565.h"
 #include "GeckoProfiler.h"
 #include "ImageContainer.h"
 #include "gfx2DGlue.h"
 
 #ifdef XP_WIN
@@ -843,16 +845,86 @@ gfxUtils::ConvertYCbCrToRGB(const Planar
                                aData.mPicSize.height,
                                aData.mYStride,
                                aData.mCbCrStride,
                                aStride,
                                yuvtype);
   }
 }
 
+/* static */ TemporaryRef<DataSourceSurface>
+gfxUtils::CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
+                                                   SurfaceFormat aFormat)
+{
+  MOZ_ASSERT(aFormat != aSurface->GetFormat(),
+             "Unnecessary - and very expersive - surface format conversion");
+
+  Rect bounds(0, 0, aSurface->GetSize().width, aSurface->GetSize().height);
+
+  if (aSurface->GetType() != SurfaceType::DATA) {
+    // If the surface is NOT of type DATA then its data is not mapped into main
+    // memory. Format conversion is probably faster on the GPU, and by doing it
+    // there we can avoid any expensive uploads/readbacks except for (possibly)
+    // a single readback due to the unavoidable GetDataSurface() call. Using
+    // CreateOffscreenContentDrawTarget ensures the conversion happens on the
+    // GPU.
+    RefPtr<DrawTarget> dt = gfxPlatform::GetPlatform()->
+      CreateOffscreenContentDrawTarget(aSurface->GetSize(), aFormat);
+    // Using DrawSurface() here rather than CopySurface() because CopySurface
+    // is optimized for memcpy and therefore isn't good for format conversion.
+    // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
+    // generally more optimized.
+    dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
+                    DrawOptions(1.0f, CompositionOp::OP_OVER));
+    RefPtr<SourceSurface> surface = dt->Snapshot();
+    return surface->GetDataSurface();
+  }
+
+  // If the surface IS of type DATA then it may or may not be in main memory
+  // depending on whether or not it has been mapped yet. We have no way of
+  // knowing, so we can't be sure if it's best to create a data wrapping
+  // DrawTarget for the conversion or an offscreen content DrawTarget. We could
+  // guess it's not mapped and create an offscreen content DrawTarget, but if
+  // it is then we'll end up uploading the surface data, and most likely the
+  // caller is going to be accessing the resulting surface data, resulting in a
+  // readback (both very expensive operations). Alternatively we could guess
+  // the data is mapped and create a data wrapping DrawTarget and, if the
+  // surface is not in main memory, then we will incure a readback. The latter
+  // of these two "wrong choices" is the least costly (a readback, vs an
+  // upload and a readback), and more than likely the DATA surface that we've
+  // been passed actually IS in main memory anyway. For these reasons it's most
+  // likely best to create a data wrapping DrawTarget here to do the format
+  // conversion.
+  RefPtr<DataSourceSurface> dataSurface =
+    Factory::CreateDataSourceSurface(aSurface->GetSize(), aFormat);
+  DataSourceSurface::MappedSurface map;
+  if (!dataSurface ||
+      !dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
+    return nullptr;
+  }
+  RefPtr<DrawTarget> dt =
+    Factory::CreateDrawTargetForData(BackendType::CAIRO,
+                                     map.mData,
+                                     dataSurface->GetSize(),
+                                     map.mStride,
+                                     aFormat);
+  if (!dt) {
+    dataSurface->Unmap();
+    return nullptr;
+  }
+  // Using DrawSurface() here rather than CopySurface() because CopySurface
+  // is optimized for memcpy and therefore isn't good for format conversion.
+  // Using OP_OVER since in our case it's equivalent to OP_SOURCE and
+  // generally more optimized.
+  dt->DrawSurface(aSurface, bounds, bounds, DrawSurfaceOptions(),
+                  DrawOptions(1.0f, CompositionOp::OP_OVER));
+  dataSurface->Unmap();
+  return dataSurface.forget();
+}
+
 #ifdef MOZ_DUMP_PAINTING
 /* static */ void
 gfxUtils::WriteAsPNG(DrawTarget* aDT, const char* aFile)
 {
   aDT->Flush();
   nsRefPtr<gfxASurface> surf = gfxPlatform::GetPlatform()->GetThebesSurfaceForDrawTarget(aDT);
   if (surf) {
     surf->WriteAsPNG(aFile);
--- a/gfx/thebes/gfxUtils.h
+++ b/gfx/thebes/gfxUtils.h
@@ -4,31 +4,37 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_UTILS_H
 #define GFX_UTILS_H
 
 #include "gfxTypes.h"
 #include "GraphicsFilter.h"
 #include "imgIContainer.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/RefPtr.h"
 
 class gfxDrawable;
 class nsIntRegion;
 struct nsIntRect;
 
 namespace mozilla {
 namespace layers {
 class PlanarYCbCrData;
 }
 }
 
 class gfxUtils {
 public:
+    typedef mozilla::gfx::DataSourceSurface DataSourceSurface;
     typedef mozilla::gfx::IntPoint IntPoint;
     typedef mozilla::gfx::Matrix Matrix;
+    typedef mozilla::gfx::SourceSurface SourceSurface;
+    typedef mozilla::gfx::SurfaceFormat SurfaceFormat;
+
     /*
      * Premultiply or Unpremultiply aSourceSurface, writing the result
      * to aDestSurface or back into aSourceSurface if aDestSurface is null.
      *
      * If aDestSurface is given, it must have identical format, dimensions, and
      * stride as the source.
      *
      * If the source is not gfxImageFormat::ARGB32, no operation is performed.  If
@@ -150,16 +156,61 @@ public:
      */
     static void
     ConvertYCbCrToRGB(const mozilla::layers::PlanarYCbCrData& aData,
                       const gfxImageFormat& aDestFormat,
                       const gfxIntSize& aDestSize,
                       unsigned char* aDestBuffer,
                       int32_t aStride);
 
+    /**
+     * Creates a copy of aSurface, but having the SurfaceFormat aFormat.
+     *
+     * This function always creates a new surface. Do not call it if aSurface's
+     * format is the same as aFormat. Such a non-conversion would just be an
+     * unnecessary and wasteful copy (this function asserts to prevent that).
+     *
+     * This function is intended to be called by code that needs to access the
+     * pixel data of the surface, but doesn't want to have lots of branches
+     * to handle different pixel data formats (code which would become out of
+     * date if and when new formats are added). Callers can use this function
+     * to copy the surface to a specified format so that they only have to
+     * handle pixel data in that one format.
+     *
+     * WARNING: There are format conversions that will not be supported by this
+     * function. It very much depends on what the Moz2D backends support. If
+     * the temporary B8G8R8A8 DrawTarget that this function creates has a
+     * backend that supports DrawSurface() calls passing a surface with
+     * aSurface's format it will work. Otherwise it will not.
+     *
+     *                      *** IMPORTANT PERF NOTE ***
+     *
+     * This function exists partly because format conversion is fraught with
+     * non-obvious performance hazards, so we don't want Moz2D consumers to be
+     * doing their own format conversion. Do not try to do so, or at least read
+     * the comments in this functions implemtation. That said, the copy that
+     * this function carries out has a cost and, although this function tries
+     * to avoid perf hazards such as expensive uploads to/readbacks from the
+     * GPU, it can't guarantee that it always successfully does so. Perf
+     * critical code that can directly handle the common formats that it
+     * encounters in a way that is cheaper than a copy-with-format-conversion
+     * should consider doing so, and only use this function as a fallback to
+     * handle other formats.
+     *
+     * XXXjwatt it would be nice if SourceSurface::GetDataSurface took a
+     * SurfaceFormat argument (with a default argument meaning "use the
+     * existing surface's format") and returned a DataSourceSurface in that
+     * format. (There would then be an issue of callers maybe failing to
+     * realize format conversion may involve expensive copying/uploading/
+     * readback.)
+     */
+    static mozilla::TemporaryRef<DataSourceSurface>
+    CopySurfaceToDataSourceSurfaceWithFormat(SourceSurface* aSurface,
+                                             SurfaceFormat aFormat);
+
     static const uint8_t sUnpremultiplyTable[256*256];
     static const uint8_t sPremultiplyTable[256*256];
 #ifdef MOZ_DUMP_PAINTING
     /**
      * Writes a binary PNG file.
      */
     static void WriteAsPNG(mozilla::gfx::DrawTarget* aDT, const char* aFile);
 
--- a/gfx/thebes/moz.build
+++ b/gfx/thebes/moz.build
@@ -62,49 +62,46 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'andr
     ]
     SOURCES += [
         'gfxAndroidPlatform.cpp',
         'gfxFT2FontBase.cpp',
         'gfxFT2FontList.cpp',
         'gfxFT2Fonts.cpp',
         'gfxFT2Utils.cpp',
         'gfxPDFSurface.cpp',
-        'nsUnicodeRange.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
     EXPORTS += [
         'gfxAndroidPlatform.h',
         'gfxFT2FontBase.h',
         'gfxFT2Fonts.h',
         'gfxPDFSurface.h',
     ]
     SOURCES += [
         'gfxAndroidPlatform.cpp',
         'gfxFT2FontBase.cpp',
         'gfxFT2FontList.cpp',
         'gfxFT2Fonts.cpp',
         'gfxFT2Utils.cpp',
         'gfxPDFSurface.cpp',
-        'nsUnicodeRange.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     EXPORTS += [
         'gfxPlatformMac.h',
         'gfxQuartzImageSurface.h',
         'gfxQuartzNativeDrawing.h',
         'gfxQuartzSurface.h',
     ]
     SOURCES += [
         'gfxCoreTextShaper.cpp',
         'gfxMacFont.cpp',
         'gfxPlatformMac.cpp',
         'gfxQuartzImageSurface.cpp',
         'gfxQuartzNativeDrawing.cpp',
         'gfxQuartzSurface.cpp',
-        'nsUnicodeRange.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_GTK']:
     EXPORTS += [
         'gfxFT2FontBase.h',
         'gfxGdkNativeRenderer.h',
         'gfxPangoFonts.h',
         'gfxPDFSurface.h',
         'gfxPlatformGtk.h',
@@ -115,17 +112,16 @@ elif CONFIG['MOZ_WIDGET_GTK']:
         'gfxFontconfigUtils.cpp',
         'gfxFT2FontBase.cpp',
         'gfxFT2Utils.cpp',
         'gfxGdkNativeRenderer.cpp',
         'gfxPangoFonts.cpp',
         'gfxPDFSurface.cpp',
         'gfxPlatformGtk.cpp',
         'gfxPSSurface.cpp',
-        'nsUnicodeRange.cpp',
     ]
 
     if CONFIG['MOZ_X11']:
         EXPORTS += [
             'gfxXlibNativeRenderer.h',
             'gfxXlibSurface.h',
         ]
         SOURCES += [
@@ -145,17 +141,16 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'qt
     SOURCES += [
         'gfxFontconfigUtils.cpp',
         'gfxFT2FontBase.cpp',
         'gfxFT2Utils.cpp',
         'gfxPangoFonts.cpp',
         'gfxPDFSurface.cpp',
         'gfxQPainterSurface.cpp',
         'gfxQtPlatform.cpp',
-        'nsUnicodeRange.cpp',
     ]
 
     if CONFIG['MOZ_X11']:
         EXPORTS += [
             'gfxXlibSurface.h',
         ]
         SOURCES += [
             'gfxQtNativeRenderer.cpp',
@@ -179,17 +174,16 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'wi
         'gfxGDIFont.cpp',
         'gfxGDIFontList.cpp',
         'gfxGDIShaper.cpp',
         'gfxPDFSurface.cpp',
         'gfxUniscribeShaper.cpp',
         'gfxWindowsNativeDrawing.cpp',
         'gfxWindowsPlatform.cpp',
         'gfxWindowsSurface.cpp',
-        'nsUnicodeRange.cpp',
     ]
     if CONFIG['MOZ_ENABLE_DWRITE_FONT']:
         # gfxDWriteFontList.cpp forces NSPR logging, so it cannot be built in unified mode.
         SOURCES += [
             'gfxD2DSurface.cpp',
             'gfxDWriteCommon.cpp',
             'gfxDWriteFontList.cpp',
             'gfxDWriteFonts.cpp',
@@ -248,16 +242,17 @@ UNIFIED_SOURCES += [
     'gfxReusableImageSurfaceWrapper.cpp',
     'gfxReusableSharedImageSurfaceWrapper.cpp',
     'gfxScriptItemizer.cpp',
     'gfxSkipChars.cpp',
     'gfxSVGGlyphs.cpp',
     'gfxTeeSurface.cpp',
     'gfxUtils.cpp',
     'nsSurfaceTexture.cpp',
+    'nsUnicodeRange.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
     # gfxMacPlatformFontList.mm forces NSPR logging so it cannot be built in unified mode.
     SOURCES += [
         'gfxMacPlatformFontList.mm',
     ]
 
--- a/intl/uconv/ucvlatin/nsUTF16ToUnicode.cpp
+++ b/intl/uconv/ucvlatin/nsUTF16ToUnicode.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 "nsUTF16ToUnicode.h"
 #include "nsCharTraits.h"
+#include "mozilla/Endian.h"
 
 enum {
   STATE_NORMAL = 0,
   STATE_HALF_CODE_POINT = 1,
   STATE_FIRST_CALL = 2,
   STATE_SECOND_BYTE = STATE_FIRST_CALL | STATE_HALF_CODE_POINT,
   STATE_ODD_SURROGATE_PAIR = 4
 };
@@ -70,17 +71,17 @@ nsUTF16ToUnicodeBase::UTF16ConvertToUnic
   char16_t u;
   if (mState == STATE_HALF_CODE_POINT) {
     if (dest == destEnd)
       goto error;
 
     // the 1st byte of a 16-bit code unit was stored in |mOddByte| in the
     // previous run while the 2nd byte has to come from |*src|.
     mState = STATE_NORMAL;
-#ifdef IS_BIG_ENDIAN
+#if MOZ_BIG_ENDIAN
     u = (mOddByte << 8) | uint8_t(*src++); // safe, we know we have at least one byte.
 #else
     u = (*src++ << 8) | mOddByte; // safe, we know we have at least one byte.
 #endif
     srcEvenEnd = src + ((srcEnd - src) & ~1); // handle even number of bytes in main loop
     goto have_codepoint;
   } else {
     srcEvenEnd = src + ((srcEnd - src) & ~1); // handle even number of bytes in main loop
@@ -204,17 +205,17 @@ nsUTF16BEToUnicode::Convert(const char *
         if (uint8_t(*aSrc) != 0xFE) {
           mState = STATE_NORMAL;
           break;
         }
         *aDestLength = 0;
         mState = STATE_SECOND_BYTE;
         return NS_OK_UDEC_MOREINPUT;
       }
-#ifdef IS_LITTLE_ENDIAN
+#if MOZ_LITTLE_ENDIAN
       // on LE machines, BE BOM is 0xFFFE
       if (0xFFFE != *((char16_t*)aSrc)) {
         mState = STATE_NORMAL;
       }
 #else
       if (0xFEFF != *((char16_t*)aSrc)) {
         mState = STATE_NORMAL;
       }
@@ -228,24 +229,18 @@ nsUTF16BEToUnicode::Convert(const char *
       }
       if (uint8_t(*aSrc) != 0xFF) {
         mOddByte = 0xFE;
         mState = STATE_HALF_CODE_POINT;
       }
       break;
   }
 
-  nsresult rv = UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength,
-#ifdef IS_LITTLE_ENDIAN
-                                      true
-#else
-                                      false
-#endif
-                                      );
-  return rv;
+  return UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength,
+                               bool(MOZ_LITTLE_ENDIAN));
 }
 
 NS_IMETHODIMP
 nsUTF16LEToUnicode::Convert(const char * aSrc, int32_t * aSrcLength,
                             char16_t * aDest, int32_t * aDestLength)
 {
   switch (mState) {
     case STATE_FIRST_CALL:
@@ -257,17 +252,17 @@ nsUTF16LEToUnicode::Convert(const char *
         if (uint8_t(*aSrc) != 0xFF) {
           mState = STATE_NORMAL;
           break;
         }
         *aDestLength = 0;
         mState = STATE_SECOND_BYTE;
         return NS_OK_UDEC_MOREINPUT;
       }
-#ifdef IS_BIG_ENDIAN
+#if MOZ_BIG_ENDIAN
       // on BE machines, LE BOM is 0xFFFE
       if (0xFFFE != *((char16_t*)aSrc)) {
         mState = STATE_NORMAL;
       }
 #else
       if (0xFEFF != *((char16_t*)aSrc)) {
         mState = STATE_NORMAL;
       }
@@ -281,24 +276,18 @@ nsUTF16LEToUnicode::Convert(const char *
       }
       if (uint8_t(*aSrc) != 0xFE) {
         mOddByte = 0xFF;
         mState = STATE_HALF_CODE_POINT;
       }
       break;
   }
 
-  nsresult rv = UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength,
-#ifdef IS_BIG_ENDIAN
-                                      true
-#else
-                                      false
-#endif
-                                      );
-  return rv;
+  return UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength,
+                               bool(MOZ_BIG_ENDIAN));
 }
 
 NS_IMETHODIMP
 nsUTF16ToUnicode::Reset()
 {
   mEndian = kUnknown;
   mFoundBOM = false;
   return nsUTF16ToUnicodeBase::Reset();
@@ -343,21 +332,19 @@ nsUTF16ToUnicode::Convert(const char * a
              // and let the garbage show up in the browser. (security concern?)
              // (bug 246194)
         mState = STATE_NORMAL;
         mEndian = kBigEndian;
       }
     }
     
     nsresult rv = UTF16ConvertToUnicode(aSrc, aSrcLength, aDest, aDestLength,
-#ifdef IS_BIG_ENDIAN
+#if MOZ_BIG_ENDIAN
                                         (mEndian == kLittleEndian)
-#elif defined(IS_LITTLE_ENDIAN)
+#else
                                         (mEndian == kBigEndian)
-#else
-    #error "Unknown endianness"
 #endif
                                         );
 
     // If BOM is not found and we're to return NS_OK, signal that BOM
     // is not found. Otherwise, return |rv| from |UTF16ConvertToUnicode|
     return (rv == NS_OK && !mFoundBOM) ? NS_OK_UDEC_NOBOMFOUND : rv;
 }
--- a/intl/uconv/ucvlatin/nsUnicodeToUTF16.cpp
+++ b/intl/uconv/ucvlatin/nsUnicodeToUTF16.cpp
@@ -1,18 +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 "nsUnicodeToUTF16.h"
 #include <string.h>
 
-inline static void SwapBytes(char *aDest, const char16_t* aSrc, int32_t aLen);
-
 NS_IMETHODIMP nsUnicodeToUTF16BE::Convert(const char16_t * aSrc, int32_t * aSrcLength, 
       char * aDest, int32_t * aDestLength)
 {
   int32_t srcInLen = *aSrcLength;
   int32_t destInLen = *aDestLength;
   int32_t srcOutLen = 0;
   int32_t destOutLen = 0;
   int32_t copyCharLen;
@@ -92,45 +90,17 @@ NS_IMETHODIMP nsUnicodeToUTF16BE::Reset(
 NS_IMETHODIMP nsUnicodeToUTF16BE::SetOutputErrorBehavior(int32_t aBehavior, 
       nsIUnicharEncoder * aEncoder, char16_t aChar)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP nsUnicodeToUTF16BE::CopyData(char* aDest, const char16_t* aSrc, int32_t aLen  )
 {
-#ifdef IS_BIG_ENDIAN
-  //UnicodeToUTF16SameEndian
-  ::memcpy(aDest, (void*) aSrc, aLen * 2);
-#elif defined(IS_LITTLE_ENDIAN)
-  //UnicodeToUTF16DiffEndian
-  SwapBytes(aDest, aSrc, aLen);
-#else
-  #error "Unknown endianness"
-#endif
+  mozilla::NativeEndian::copyAndSwapToBigEndian(aDest, aSrc, aLen);
   return NS_OK;
 }
 
 NS_IMETHODIMP nsUnicodeToUTF16LE::CopyData(char* aDest, const char16_t* aSrc, int32_t aLen  )
 {
-#ifdef IS_LITTLE_ENDIAN
-  //UnicodeToUTF16SameEndian
-  ::memcpy(aDest, (void*) aSrc, aLen * 2);
-#elif defined(IS_BIG_ENDIAN)
-  //UnicodeToUTF16DiffEndian
-  SwapBytes(aDest, aSrc, aLen);
-#else
-  #error "Unknown endianness"
-#endif
+  mozilla::NativeEndian::copyAndSwapToLittleEndian(aDest, aSrc, aLen);
   return NS_OK;
 }
-
-inline void SwapBytes(char *aDest, const char16_t* aSrc, int32_t aLen)
-{
-  char16_t *p = (char16_t*) aDest;
-  // copy the data  by swaping 
-  for(int32_t i = 0; i < aLen; i++)
-  {
-    char16_t aChar = *aSrc++;
-    *p++ = (0x00FF & (aChar >> 8)) | (0xFF00 & (aChar << 8));
-  }
-}
-
--- a/intl/uconv/ucvlatin/nsUnicodeToUTF16.h
+++ b/intl/uconv/ucvlatin/nsUnicodeToUTF16.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsUnicodeToUTF16_h_
 #define nsUnicodeToUTF16_h_
 
 #include "nsUCSupport.h"
+#include "mozilla/Endian.h"
 
 class nsUnicodeToUTF16BE: public nsBasicEncoder
 {
 public:
   nsUnicodeToUTF16BE() { mBOM = 0;}
 
   //--------------------------------------------------------------------
   // Interface nsIUnicodeEncoder [declaration]
@@ -34,23 +35,19 @@ class nsUnicodeToUTF16LE: public nsUnico
 {
 public:
   nsUnicodeToUTF16LE() { mBOM = 0;}
 
 protected:
   NS_IMETHOD CopyData(char* aDest, const char16_t* aSrc, int32_t aLen  );
 };
 
-// XXX In theory, we have to check the endianness at run-time because some
-// modern RISC processors can be run at both LE and BE. 
-#ifdef IS_LITTLE_ENDIAN
+#if MOZ_LITTLE_ENDIAN
 class nsUnicodeToUTF16: public nsUnicodeToUTF16LE
-#elif defined(IS_BIG_ENDIAN)
+#else
 class nsUnicodeToUTF16: public nsUnicodeToUTF16BE
-#else
-#error "Unknown endianness"
 #endif
 {
 public:
   nsUnicodeToUTF16() { mBOM = 0xFEFF;}
 };
 
 #endif /* nsUnicodeToUTF16_h_ */
--- a/js/public/ProfilingStack.h
+++ b/js/public/ProfilingStack.h
@@ -30,19 +30,19 @@ class ProfileEntry
     //
     //    entry[size] = ...;
     //    size++;
     //
     // If the size modification were somehow reordered before the stores, then
     // if a sample were taken it would be examining bogus information.
     //
     // A ProfileEntry represents both a C++ profile entry and a JS one. Both use
-    // the string as a description, but JS uses the sp as nullptr to indicate
-    // that it is a JS entry. The script_ is then only ever examined for a JS
-    // entry, and the idx is used by both, but with different meanings.
+    // the string as a description, but JS uses the sp as nullptr or (void*)1 to
+    // indicate that it is a JS entry. The script_ is then only ever examined for
+    // a JS entry, and the idx is used by both, but with different meanings.
     //
     const char * volatile string; // Descriptive string of this entry
     void * volatile sp;           // Relevant stack pointer for the entry
     JSScript * volatile script_;  // if js(), non-null script which is running
     int32_t volatile idx;         // if js(), idx of pc, otherwise line number
 
   public:
     // All of these methods are marked with the 'volatile' keyword because SPS's
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -240,16 +240,28 @@ class Heap : public js::HeapBase<T>
         } else if (js::GCMethods<T>::needsPostBarrier(ptr)) {
             relocate();  /* Called before overwriting ptr. */
             ptr = newPtr;
         } else {
             ptr = newPtr;
         }
     }
 
+    /*
+     * Set the pointer to a value which will cause a crash if it is
+     * dereferenced.
+     */
+    void setToCrashOnTouch() {
+        ptr = reinterpret_cast<T>(crashOnTouchPointer);
+    }
+
+    bool isSetToCrashOnTouch() {
+        return ptr == crashOnTouchPointer;
+    }
+
   private:
     void init(T newPtr) {
         MOZ_ASSERT(!js::GCMethods<T>::poisoned(newPtr));
         ptr = newPtr;
         if (js::GCMethods<T>::needsPostBarrier(ptr))
             post();
     }
 
@@ -261,16 +273,20 @@ class Heap : public js::HeapBase<T>
     }
 
     void relocate() {
 #ifdef JSGC_GENERATIONAL
         js::GCMethods<T>::relocate(&ptr);
 #endif
     }
 
+    enum {
+        crashOnTouchPointer = 1
+    };
+
     T ptr;
 };
 
 #ifdef JS_DEBUG
 /*
  * For generational GC, assert that an object is in the tenured generation as
  * opposed to being in the nursery.
  */
@@ -358,33 +374,20 @@ class TenuredHeap : public js::HeapBase<
         return *this;
     }
 
     TenuredHeap<T> &operator=(const TenuredHeap<T>& other) {
         bits = other.bits;
         return *this;
     }
 
-    /*
-     * Set the pointer to a value which will cause a crash if it is
-     * dereferenced.
-     */
-    void setToCrashOnTouch() {
-        bits = (bits & flagsMask) | crashOnTouchPointer;
-    }
-
-    bool isSetToCrashOnTouch() {
-        return (bits & ~flagsMask) == crashOnTouchPointer;
-    }
-
   private:
     enum {
         maskBits = 3,
         flagsMask = (1 << maskBits) - 1,
-        crashOnTouchPointer = 1 << maskBits
     };
 
     uintptr_t bits;
 };
 
 /*
  * Reference to a T that has been rooted elsewhere. This is most useful
  * as a parameter type, which guarantees that the T lvalue is properly
--- a/js/src/jit/BaselineBailouts.cpp
+++ b/js/src/jit/BaselineBailouts.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsprf.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/CompileInfo.h"
 #include "jit/IonSpewer.h"
+#include "jit/Recover.h"
 #include "vm/ArgumentsObject.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/IonFrames-inl.h"
 
 using namespace js;
 using namespace js::jit;
@@ -471,17 +472,17 @@ InitFromBailout(JSContext *cx, HandleScr
 {
     // If excInfo is non-nullptr, we are bailing out to a catch or finally block
     // and this is the frame where we will resume. Usually the expression stack
     // should be empty in this case but there can be iterators on the stack.
     uint32_t exprStackSlots;
     if (excInfo)
         exprStackSlots = excInfo->numExprSlots;
     else
-        exprStackSlots = iter.allocations() - (script->nfixed() + CountArgSlots(script, fun));
+        exprStackSlots = iter.numAllocations() - (script->nfixed() + CountArgSlots(script, fun));
 
     builder.resetFramePushed();
 
     // Build first baseline frame:
     // +===============+
     // | PrevFramePtr  |
     // +---------------+
     // |   Baseline    |
@@ -624,19 +625,19 @@ InitFromBailout(JSContext *cx, HandleScr
         // in the calling frame.
         Value thisv = iter.read();
         IonSpew(IonSpew_BaselineBailouts, "      Is function!");
         IonSpew(IonSpew_BaselineBailouts, "      thisv=%016llx", *((uint64_t *) &thisv));
 
         size_t thisvOffset = builder.framePushed() + IonJSFrameLayout::offsetOfThis();
         *builder.valuePointerAtStackOffset(thisvOffset) = thisv;
 
-        JS_ASSERT(iter.allocations() >= CountArgSlots(script, fun));
+        JS_ASSERT(iter.numAllocations() >= CountArgSlots(script, fun));
         IonSpew(IonSpew_BaselineBailouts, "      frame slots %u, nargs %u, nfixed %u",
-                iter.allocations(), fun->nargs(), script->nfixed());
+                iter.numAllocations(), fun->nargs(), script->nfixed());
 
         if (!callerPC) {
             // This is the first frame. Store the formals in a Vector until we
             // are done. Due to UCE and phi elimination, we could store an
             // UndefinedValue() here for formals we think are unused, but
             // locals may still reference the original argument slot
             // (MParameter/LArgument) and expect the original Value.
             JS_ASSERT(startFrameFormals.empty());
@@ -1343,16 +1344,18 @@ jit::BailoutIonToBaseline(JSContext *cx,
     RootedFunction fun(cx, callee);
     RootedScript scr(cx, iter.script());
     AutoValueVector startFrameFormals(cx);
 
     RootedScript topCaller(cx);
     jsbytecode *topCallerPC = nullptr;
 
     while (true) {
+        MOZ_ASSERT(snapIter.instruction()->isResumePoint());
+
 #if JS_TRACE_LOGGING
         if (frameNo > 0) {
             TraceLogging::defaultLogger()->log(TraceLogging::SCRIPT_START, scr);
             TraceLogging::defaultLogger()->log(TraceLogging::INFO_ENGINE_BASELINE);
         }
 #endif
         IonSpew(IonSpew_BaselineBailouts, "    FrameNo %d", frameNo);
 
@@ -1378,26 +1381,27 @@ jit::BailoutIonToBaseline(JSContext *cx,
             break;
 
         JS_ASSERT(nextCallee);
         JS_ASSERT(callPC);
         caller = scr;
         callerPC = callPC;
         fun = nextCallee;
         scr = fun->existingScript();
-        snapIter.nextFrame();
 
         // Save top caller info for adjusting SPS frames later.
         if (!topCaller) {
             JS_ASSERT(frameNo == 0);
             topCaller = caller;
             topCallerPC = callerPC;
         }
 
         frameNo++;
+
+        snapIter.nextInstruction();
     }
     IonSpew(IonSpew_BaselineBailouts, "  Done restoring frames");
 
     // If there were multiple inline frames unpacked, and inline frame profiling
     // is off, then the current top SPS frame is for the outermost caller, and
     // has an uninitialized PC.  Initialize it now.
     if (frameNo > 0 && !js_JitOptions.profileInlineFrames)
         cx->runtime()->spsProfiler.updatePC(topCaller, topCallerPC);
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -7933,25 +7933,25 @@ CodeGenerator::visitSetDOMProperty(LSetD
     return true;
 }
 
 typedef bool(*SPSFn)(JSContext *, HandleScript);
 static const VMFunction SPSEnterInfo = FunctionInfo<SPSFn>(SPSEnter);
 static const VMFunction SPSExitInfo = FunctionInfo<SPSFn>(SPSExit);
 
 bool
-CodeGenerator::visitFunctionBoundary(LFunctionBoundary *lir)
+CodeGenerator::visitProfilerStackOp(LProfilerStackOp *lir)
 {
     Register temp = ToRegister(lir->temp()->output());
     bool inlinedFunction = lir->inlineLevel() > 0;
 
     switch (lir->type()) {
-        case MFunctionBoundary::Inline_Enter:
+        case MProfilerStackOp::InlineEnter:
             // Multiple scripts can be inlined at one depth, but there is only
-            // one Inline_Exit node to signify this. To deal with this, if we
+            // one InlineExit node to signify this. To deal with this, if we
             // reach the entry of another inline script on the same level, then
             // just reset the sps metadata about the frame. We must balance
             // calls to leave()/reenter(), so perform the balance without
             // emitting any instrumentation. Technically the previous inline
             // call at this same depth has reentered, but the instrumentation
             // will be emitted at the common join point for all inlines at the
             // same depth.
             if (sps_.inliningDepth() == lir->inlineLevel()) {
@@ -7960,40 +7960,40 @@ CodeGenerator::visitFunctionBoundary(LFu
                 sps_.reenter(masm, temp);
             }
 
             sps_.leave(masm, temp, /* inlinedFunction = */ true);
             if (!sps_.enterInlineFrame())
                 return false;
             // fallthrough
 
-        case MFunctionBoundary::Enter:
+        case MProfilerStackOp::Enter:
             if (gen->options.spsSlowAssertionsEnabled()) {
                 if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
                     saveLive(lir);
                     pushArg(ImmGCPtr(lir->script()));
                     if (!callVM(SPSEnterInfo, lir))
                         return false;
                     restoreLive(lir);
                 }
                 sps_.pushManual(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
                 return true;
             }
 
             return sps_.push(lir->script(), masm, temp, /* inlinedFunction = */ inlinedFunction);
 
-        case MFunctionBoundary::Inline_Exit:
+        case MProfilerStackOp::InlineExit:
             // all inline returns were covered with ::Exit, so we just need to
             // maintain the state of inline frames currently active and then
             // reenter the caller
             sps_.leaveInlineFrame();
             sps_.reenter(masm, temp, /* inlinedFunction = */ true);
             return true;
 
-        case MFunctionBoundary::Exit:
+        case MProfilerStackOp::Exit:
             if (gen->options.spsSlowAssertionsEnabled()) {
                 if (!inlinedFunction || js_JitOptions.profileInlineFrames) {
                     saveLive(lir);
                     pushArg(ImmGCPtr(lir->script()));
                     // Once we've exited, then we shouldn't emit instrumentation for
                     // the corresponding reenter() because we no longer have a
                     // frame.
                     sps_.skipNextReenter();
@@ -8003,17 +8003,17 @@ CodeGenerator::visitFunctionBoundary(LFu
                 }
                 return true;
             }
 
             sps_.pop(masm, temp, /* inlinedFunction = */ inlinedFunction);
             return true;
 
         default:
-            MOZ_ASSUME_UNREACHABLE("invalid LFunctionBoundary type");
+            MOZ_ASSUME_UNREACHABLE("invalid LProfilerStackOp type");
     }
 }
 
 bool
 CodeGenerator::visitOutOfLineAbortPar(OutOfLineAbortPar *ool)
 {
     ParallelBailoutCause cause = ool->cause();
     jsbytecode *bytecode = ool->bytecode();
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -269,17 +269,17 @@ class CodeGenerator : public CodeGenerat
     bool visitBitNotV(LBitNotV *lir);
     bool visitBitOpV(LBitOpV *lir);
     bool emitInstanceOf(LInstruction *ins, JSObject *prototypeObject);
     bool visitIn(LIn *ins);
     bool visitInArray(LInArray *ins);
     bool visitInstanceOfO(LInstanceOfO *ins);
     bool visitInstanceOfV(LInstanceOfV *ins);
     bool visitCallInstanceOf(LCallInstanceOf *ins);
-    bool visitFunctionBoundary(LFunctionBoundary *lir);
+    bool visitProfilerStackOp(LProfilerStackOp *lir);
     bool visitGetDOMProperty(LGetDOMProperty *lir);
     bool visitGetDOMMember(LGetDOMMember *lir);
     bool visitSetDOMProperty(LSetDOMProperty *lir);
     bool visitCallDOMNative(LCallDOMNative *lir);
     bool visitCallGetIntrinsicValue(LCallGetIntrinsicValue *lir);
     bool visitIsCallable(LIsCallable *lir);
     bool visitHaveSameClass(LHaveSameClass *lir);
     bool visitHasClass(LHasClass *lir);
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -649,17 +649,17 @@ IonBuilder::build()
         MInstruction *argsObj = MConstant::New(alloc(), UndefinedValue());
         current->add(argsObj);
         current->initSlot(info().argsObjSlot(), argsObj);
     }
 
     // Emit the start instruction, so we can begin real instructions.
     current->makeStart(MStart::New(alloc(), MStart::StartType_Default));
     if (instrumentedProfiling())
-        current->add(MFunctionBoundary::New(alloc(), script(), MFunctionBoundary::Enter));
+        current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Enter));
 
     // Guard against over-recursion. Do this before we start unboxing, since
     // this will create an OSI point that will read the incoming argument
     // values, which is nice to do before their last real use, to minimize
     // register/stack pressure.
     MCheckOverRecursed *check = MCheckOverRecursed::New(alloc());
     current->add(check);
     check->setResumePoint(current->entryResumePoint());
@@ -786,21 +786,21 @@ IonBuilder::buildInline(IonBuilder *call
 
     // Connect the entrance block to the last block in the caller's graph.
     MBasicBlock *predecessor = callerBuilder->current;
     JS_ASSERT(predecessor == callerResumePoint->block());
 
     // All further instructions generated in from this scope should be
     // considered as part of the function that we're inlining. We also need to
     // keep track of the inlining depth because all scripts inlined on the same
-    // level contiguously have only one Inline_Exit node.
+    // level contiguously have only one InlineExit node.
     if (instrumentedProfiling()) {
-        predecessor->add(MFunctionBoundary::New(alloc(), script(),
-                                                MFunctionBoundary::Inline_Enter,
-                                                inliningDepth_));
+        predecessor->add(MProfilerStackOp::New(alloc(), script(),
+                                               MProfilerStackOp::InlineEnter,
+                                               inliningDepth_));
     }
 
     predecessor->end(MGoto::New(alloc(), current));
     if (!current->addPredecessorWithoutPhis(predecessor))
         return false;
 
     // Initialize scope chain slot to Undefined.  It's set later by |initScopeChain|.
     MInstruction *scope = MConstant::New(alloc(), UndefinedValue());
@@ -3623,18 +3623,18 @@ IonBuilder::processReturn(JSOp op)
         break;
 
       default:
         def = nullptr;
         MOZ_ASSUME_UNREACHABLE("unknown return op");
     }
 
     if (instrumentedProfiling()) {
-        current->add(MFunctionBoundary::New(alloc(), script(), MFunctionBoundary::Exit,
-                                            inliningDepth_));
+        current->add(MProfilerStackOp::New(alloc(), script(), MProfilerStackOp::Exit,
+                                           inliningDepth_));
     }
     MReturn *ret = MReturn::New(alloc(), def);
     current->end(ret);
 
     if (!graph().addReturn(current))
         return ControlStatus_Error;
 
     // Make sure no one tries to use this block now.
@@ -3956,19 +3956,19 @@ IonBuilder::inlineScriptedCall(CallInfo 
 
     // Create return block.
     jsbytecode *postCall = GetNextPc(pc);
     MBasicBlock *returnBlock = newBlock(nullptr, postCall);
     if (!returnBlock)
         return false;
     returnBlock->setCallerResumePoint(callerResumePoint_);
 
-    // When profiling add Inline_Exit instruction to indicate end of inlined function.
+    // When profiling add InlineExit instruction to indicate end of inlined function.
     if (instrumentedProfiling())
-        returnBlock->add(MFunctionBoundary::New(alloc(), nullptr, MFunctionBoundary::Inline_Exit));
+        returnBlock->add(MProfilerStackOp::New(alloc(), nullptr, MProfilerStackOp::InlineExit));
 
     // Inherit the slots from current and pop |fun|.
     returnBlock->inheritSlots(current);
     returnBlock->pop();
 
     // Accumulate return values.
     if (returns.empty()) {
         // Inlining of functions that have no exit is not supported.
--- a/js/src/jit/IonFrameIterator-inl.h
+++ b/js/src/jit/IonFrameIterator-inl.h
@@ -18,16 +18,17 @@ namespace js {
 namespace jit {
 
 template <AllowGC allowGC>
 inline
 InlineFrameIteratorMaybeGC<allowGC>::InlineFrameIteratorMaybeGC(
                                                 JSContext *cx, const IonBailoutIterator *iter)
   : frame_(iter),
     framesRead_(0),
+    frameCount_(UINT32_MAX),
     callee_(cx),
     script_(cx)
 {
     if (iter) {
         start_ = SnapshotIterator(*iter);
         findNextFrame();
     }
 }
--- a/js/src/jit/IonFrameIterator.h
+++ b/js/src/jit/IonFrameIterator.h
@@ -239,16 +239,18 @@ class IonFrameIterator
     void dump() const;
 
     inline BaselineFrame *baselineFrame() const;
 };
 
 class IonJSFrameLayout;
 class IonBailoutIterator;
 
+class RResumePoint;
+
 // Reads frame information in snapshot-encoding order (that is, outermost frame
 // to innermost frame).
 class SnapshotIterator
 {
     SnapshotReader snapshot_;
     RecoverReader recover_;
     IonJSFrameLayout *fp_;
     MachineState machine_;
@@ -282,55 +284,70 @@ class SnapshotIterator
 
   public:
     // Handle iterating over RValueAllocations of the snapshots.
     inline RValueAllocation readAllocation() {
         MOZ_ASSERT(moreAllocations());
         return snapshot_.readAllocation();
     }
     Value skip() {
-        readAllocation();
+        snapshot_.skipAllocation();
         return UndefinedValue();
     }
 
-    inline uint32_t allocations() const {
-        return recover_.allocations();
+    const RResumePoint *resumePoint() const;
+    const RInstruction *instruction() const {
+        return recover_.instruction();
     }
+
+    uint32_t numAllocations() const;
     inline bool moreAllocations() const {
-        return recover_.moreAllocations(snapshot_);
+        return snapshot_.numAllocationsRead() < numAllocations();
     }
 
   public:
     // Exhibits frame properties contained in the snapshot.
-    inline uint32_t pcOffset() const {
-        return recover_.pcOffset();
-    }
+    uint32_t pcOffset() const;
     inline bool resumeAfter() const {
         // Inline frames are inlined on calls, which are considered as being
         // resumed on the Call as baseline will push the pc once we return from
         // the call.
         if (moreFrames())
             return false;
         return recover_.resumeAfter();
     }
     inline BailoutKind bailoutKind() const {
         return snapshot_.bailoutKind();
     }
 
   public:
-    // Handle iterating over frames of the snapshots.
-    inline void nextFrame() {
-        // Reuse the Snapshot buffer.
-        recover_.nextFrame(snapshot_);
+    // Read the next instruction available and get ready to either skip it or
+    // evaluate it.
+    inline void nextInstruction() {
+        MOZ_ASSERT(snapshot_.numAllocationsRead() == numAllocations());
+        recover_.nextInstruction();
+        snapshot_.resetNumAllocationsRead();
     }
-    inline bool moreFrames() const {
-        return recover_.moreFrames();
+
+    // Skip an Instruction by walking to the next instruction and by skipping
+    // all the allocations corresponding to this instruction.
+    void skipInstruction();
+
+    inline bool moreInstructions() const {
+        return recover_.moreInstructions();
     }
-    inline uint32_t frameCount() const {
-        return recover_.frameCount();
+
+  public:
+    // Handle iterating over frames of the snapshots.
+    void nextFrame();
+
+    inline bool moreFrames() const {
+        // The last instruction is recovering the innermost frame, so as long as
+        // there is more instruction there is necesseray more frames.
+        return moreInstructions();
     }
 
   public:
     // Connect all informations about the current script in order to recover the
     // content of baseline frames.
 
     SnapshotIterator(IonScript *ionScript, SnapshotOffset snapshotOffset,
                      IonJSFrameLayout *fp, const MachineState &machine);
@@ -404,17 +421,24 @@ class SnapshotIterator
 // Reads frame information in callstack order (that is, innermost frame to
 // outermost frame).
 template <AllowGC allowGC=CanGC>
 class InlineFrameIteratorMaybeGC
 {
     const IonFrameIterator *frame_;
     SnapshotIterator start_;
     SnapshotIterator si_;
-    unsigned framesRead_;
+    uint32_t framesRead_;
+
+    // When the inline-frame-iterator is created, this variable is defined to
+    // UINT32_MAX. Then the first iteration of findNextFrame, which settle on
+    // the innermost frame, is used to update this counter to the number of
+    // frames contained in the recover buffer.
+    uint32_t frameCount_;
+
     typename MaybeRooted<JSFunction*, allowGC>::RootType callee_;
     typename MaybeRooted<JSScript*, allowGC>::RootType script_;
     jsbytecode *pc_;
     uint32_t numActualArgs_;
 
     struct Nop {
         void operator()(const Value &v) { }
     };
@@ -437,30 +461,31 @@ class InlineFrameIteratorMaybeGC
         resetOn(iter);
     }
 
     InlineFrameIteratorMaybeGC(JSContext *cx, const IonBailoutIterator *iter);
 
     InlineFrameIteratorMaybeGC(JSContext *cx, const InlineFrameIteratorMaybeGC *iter)
       : frame_(iter ? iter->frame_ : nullptr),
         framesRead_(0),
+        frameCount_(iter ? iter->frameCount_ : UINT32_MAX),
         callee_(cx),
         script_(cx)
     {
         if (frame_) {
             start_ = SnapshotIterator(*frame_);
             // findNextFrame will iterate to the next frame and init. everything.
             // Therefore to settle on the same frame, we report one frame less readed.
             framesRead_ = iter->framesRead_ - 1;
             findNextFrame();
         }
     }
 
     bool more() const {
-        return frame_ && framesRead_ < start_.frameCount();
+        return frame_ && framesRead_ < frameCount_;
     }
     JSFunction *callee() const {
         JS_ASSERT(callee_);
         return callee_;
     }
     JSFunction *maybeCallee() const {
         return callee_;
     }
@@ -505,18 +530,18 @@ class InlineFrameIteratorMaybeGC
                 InlineFrameIteratorMaybeGC it(cx, this);
                 ++it;
                 unsigned argsObjAdj = it.script()->argumentsHasVarBinding() ? 1 : 0;
                 SnapshotIterator parent_s(it.snapshotIterator());
 
                 // Skip over all slots until we get to the last slots
                 // (= arguments slots of callee) the +3 is for [this], [returnvalue],
                 // [scopechain], and maybe +1 for [argsObj]
-                JS_ASSERT(parent_s.allocations() >= nactual + 3 + argsObjAdj);
-                unsigned skip = parent_s.allocations() - nactual - 3 - argsObjAdj;
+                JS_ASSERT(parent_s.numAllocations() >= nactual + 3 + argsObjAdj);
+                unsigned skip = parent_s.numAllocations() - nactual - 3 - argsObjAdj;
                 for (unsigned j = 0; j < skip; j++)
                     parent_s.skip();
 
                 // Get the overflown arguments
                 parent_s.readFrameArgs(argOp, nullptr, nullptr, nformal, nactual, it.script());
             } else {
                 // There is no parent frame to this inlined frame, we can read
                 // from the frame's Value vector directly.
@@ -592,17 +617,18 @@ class InlineFrameIteratorMaybeGC
     void resetOn(const IonFrameIterator *iter);
 
     const IonFrameIterator &frame() const {
         return *frame_;
     }
 
     // Inline frame number, 0 for the outermost (non-inlined) frame.
     size_t frameNo() const {
-        return start_.frameCount() - framesRead_;
+        MOZ_ASSERT(frameCount_ != UINT32_MAX);
+        return frameCount_ - framesRead_;
     }
 
   private:
     InlineFrameIteratorMaybeGC() MOZ_DELETE;
     InlineFrameIteratorMaybeGC(const InlineFrameIteratorMaybeGC &iter) MOZ_DELETE;
 };
 typedef InlineFrameIteratorMaybeGC<CanGC> InlineFrameIterator;
 typedef InlineFrameIteratorMaybeGC<NoGC> InlineFrameIteratorNoGC;
--- a/js/src/jit/IonFrames.cpp
+++ b/js/src/jit/IonFrames.cpp
@@ -15,16 +15,17 @@
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/Ion.h"
 #include "jit/IonMacroAssembler.h"
 #include "jit/IonSpewer.h"
 #include "jit/JitCompartment.h"
 #include "jit/ParallelFunctions.h"
 #include "jit/PcScriptCache.h"
+#include "jit/Recover.h"
 #include "jit/Safepoints.h"
 #include "jit/Snapshots.h"
 #include "jit/VMFunctions.h"
 #include "vm/ForkJoin.h"
 #include "vm/Interpreter.h"
 
 #include "jit/IonFrameIterator-inl.h"
 #include "vm/Probes-inl.h"
@@ -1493,16 +1494,52 @@ SnapshotIterator::allocationValue(const 
       }
 #endif
 
       default:
         MOZ_ASSUME_UNREACHABLE("huh?");
     }
 }
 
+const RResumePoint *
+SnapshotIterator::resumePoint() const
+{
+    return instruction()->toResumePoint();
+}
+
+uint32_t
+SnapshotIterator::numAllocations() const
+{
+    return resumePoint()->numOperands();
+}
+
+uint32_t
+SnapshotIterator::pcOffset() const
+{
+    return resumePoint()->pcOffset();
+}
+
+void
+SnapshotIterator::skipInstruction()
+{
+    MOZ_ASSERT(snapshot_.numAllocationsRead() == 0);
+    size_t numOperands = instruction()->numOperands();
+    for (size_t i = 0; i < numOperands; i++)
+        skip();
+    nextInstruction();
+}
+
+void
+SnapshotIterator::nextFrame()
+{
+    nextInstruction();
+    while (!instruction()->isResumePoint())
+        skipInstruction();
+}
+
 IonScript *
 IonFrameIterator::ionScript() const
 {
     JS_ASSERT(type() == JitFrame_IonJS);
 
     IonScript *ionScript = nullptr;
     if (checkInvalidation(&ionScript))
         return ionScript;
@@ -1531,16 +1568,17 @@ IonFrameIterator::osiIndex() const
 }
 
 template <AllowGC allowGC>
 void
 InlineFrameIteratorMaybeGC<allowGC>::resetOn(const IonFrameIterator *iter)
 {
     frame_ = iter;
     framesRead_ = 0;
+    frameCount_ = UINT32_MAX;
 
     if (iter) {
         start_ = SnapshotIterator(*iter);
         findNextFrame();
     }
 }
 template void InlineFrameIteratorMaybeGC<NoGC>::resetOn(const IonFrameIterator *iter);
 template void InlineFrameIteratorMaybeGC<CanGC>::resetOn(const IonFrameIterator *iter);
@@ -1548,28 +1586,41 @@ template void InlineFrameIteratorMaybeGC
 template <AllowGC allowGC>
 void
 InlineFrameIteratorMaybeGC<allowGC>::findNextFrame()
 {
     JS_ASSERT(more());
 
     si_ = start_;
 
-    // Read the initial frame.
+    // Read the initial frame out of the C stack.
     callee_ = frame_->maybeCallee();
     script_ = frame_->script();
+
+    // Settle on the outermost frame without evaluating any instructions before
+    // looking for a pc.
+    if (!si_.instruction()->isResumePoint())
+        si_.nextFrame();
+
     pc_ = script_->offsetToPC(si_.pcOffset());
 #ifdef DEBUG
     numActualArgs_ = 0xbadbad;
 #endif
 
     // This unfortunately is O(n*m), because we must skip over outer frames
     // before reading inner ones.
-    unsigned remaining = start_.frameCount() - framesRead_ - 1;
-    for (unsigned i = 0; i < remaining; i++) {
+
+    // The first time (frameCount_ == UINT32_MAX) we do not know the number of
+    // frames that we are going to inspect.  So we are iterating until there is
+    // no more frames, to settle on the inner most frame and to count the number
+    // of frames.
+    size_t remaining = (frameCount_ != UINT32_MAX) ? frameNo() - 1 : SIZE_MAX;
+
+    size_t i = 1;
+    for (; i <= remaining && si_.moreFrames(); i++) {
         JS_ASSERT(IsIonInlinablePC(pc_));
 
         // Recover the number of actual arguments from the script.
         if (JSOp(*pc_) != JSOP_FUNAPPLY)
             numActualArgs_ = GET_ARGC(pc_);
         if (JSOp(*pc_) == JSOP_FUNCALL) {
             JS_ASSERT(GET_ARGC(pc_) > 0);
             numActualArgs_ = GET_ARGC(pc_) - 1;
@@ -1577,20 +1628,21 @@ InlineFrameIteratorMaybeGC<allowGC>::fin
             numActualArgs_ = 0;
         } else if (IsSetPropPC(pc_)) {
             numActualArgs_ = 1;
         }
 
         JS_ASSERT(numActualArgs_ != 0xbadbad);
 
         // Skip over non-argument slots, as well as |this|.
-        unsigned skipCount = (si_.allocations() - 1) - numActualArgs_ - 1;
+        unsigned skipCount = (si_.numAllocations() - 1) - numActualArgs_ - 1;
         for (unsigned j = 0; j < skipCount; j++)
             si_.skip();
 
+        // The JSFunction is a constant, otherwise we would not have inlined it.
         Value funval = si_.read();
 
         // Skip extra value allocations.
         while (si_.moreAllocations())
             si_.skip();
 
         si_.nextFrame();
 
@@ -1599,16 +1651,24 @@ InlineFrameIteratorMaybeGC<allowGC>::fin
         // Inlined functions may be clones that still point to the lazy script
         // for the executed script, if they are clones. The actual script
         // exists though, just make sure the function points to it.
         script_ = callee_->existingScript();
 
         pc_ = script_->offsetToPC(si_.pcOffset());
     }
 
+    // The first time we do not know the number of frames, we only settle on the
+    // last frame, and update the number of frames based on the number of
+    // iteration that we have done.
+    if (frameCount_ == UINT32_MAX) {
+        MOZ_ASSERT(!si_.moreFrames());
+        frameCount_ = i;
+    }
+
     framesRead_++;
 }
 template void InlineFrameIteratorMaybeGC<NoGC>::findNextFrame();
 template void InlineFrameIteratorMaybeGC<CanGC>::findNextFrame();
 
 template <AllowGC allowGC>
 bool
 InlineFrameIteratorMaybeGC<allowGC>::isFunctionFrame() const
@@ -1799,18 +1859,18 @@ InlineFrameIteratorMaybeGC<allowGC>::dum
     fprintf(stderr, "  script = %p, pc = %p\n", (void*) script(), pc());
     fprintf(stderr, "  current op: %s\n", js_CodeName[*pc()]);
 
     if (!more()) {
         numActualArgs();
     }
 
     SnapshotIterator si = snapshotIterator();
-    fprintf(stderr, "  slots: %u\n", si.allocations() - 1);
-    for (unsigned i = 0; i < si.allocations() - 1; i++) {
+    fprintf(stderr, "  slots: %u\n", si.numAllocations() - 1);
+    for (unsigned i = 0; i < si.numAllocations() - 1; i++) {
         if (isFunction) {
             if (i == 0)
                 fprintf(stderr, "  scope chain: ");
             else if (i == 1)
                 fprintf(stderr, "  this: ");
             else if (i - 2 < callee()->nargs())
                 fprintf(stderr, "  formal (arg %d): ", i - 2);
             else {
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -5690,39 +5690,39 @@ class LCallInstanceOf : public LCallInst
     const LAllocation *rhs() {
         return getOperand(RHS);
     }
 
     static const size_t LHS = 0;
     static const size_t RHS = BOX_PIECES;
 };
 
-class LFunctionBoundary : public LInstructionHelper<0, 0, 1>
-{
-  public:
-    LIR_HEADER(FunctionBoundary)
-
-    LFunctionBoundary(const LDefinition &temp) {
+class LProfilerStackOp : public LInstructionHelper<0, 0, 1>
+{
+  public:
+    LIR_HEADER(ProfilerStackOp)
+
+    LProfilerStackOp(const LDefinition &temp) {
         setTemp(0, temp);
     }
 
     const LDefinition *temp() {
         return getTemp(0);
     }
 
     JSScript *script() {
-        return mir_->toFunctionBoundary()->script();
-    }
-
-    MFunctionBoundary::Type type() {
-        return mir_->toFunctionBoundary()->type();
+        return mir_->toProfilerStackOp()->script();
+    }
+
+    MProfilerStackOp::Type type() {
+        return mir_->toProfilerStackOp()->type();
     }
 
     unsigned inlineLevel() {
-        return mir_->toFunctionBoundary()->inlineLevel();
+        return mir_->toProfilerStackOp()->inlineLevel();
     }
 };
 
 class LIsCallable : public LInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(IsCallable);
     LIsCallable(const LAllocation &object) {
--- a/js/src/jit/LIR.cpp
+++ b/js/src/jit/LIR.cpp
@@ -114,38 +114,58 @@ static size_t
 TotalOperandCount(MResumePoint *mir)
 {
     size_t accum = mir->numOperands();
     while ((mir = mir->caller()))
         accum += mir->numOperands();
     return accum;
 }
 
-LRecoverInfo::LRecoverInfo(MResumePoint *mir)
-  : mir_(mir),
+LRecoverInfo::LRecoverInfo(TempAllocator &alloc)
+  : instructions_(alloc),
     recoverOffset_(INVALID_RECOVER_OFFSET)
 { }
 
 LRecoverInfo *
 LRecoverInfo::New(MIRGenerator *gen, MResumePoint *mir)
 {
-    LRecoverInfo *recover = new(gen->alloc()) LRecoverInfo(mir);
-    if (!recover)
+    LRecoverInfo *recoverInfo = new(gen->alloc()) LRecoverInfo(gen->alloc());
+    if (!recoverInfo || !recoverInfo->init(mir))
         return nullptr;
 
-    IonSpew(IonSpew_Snapshots, "Generating LIR recover %p from MIR (%p)",
-            (void *)recover, (void *)mir);
+    IonSpew(IonSpew_Snapshots, "Generating LIR recover info %p from MIR (%p)",
+            (void *)recoverInfo, (void *)mir);
 
-    return recover;
+    return recoverInfo;
 }
 
-LSnapshot::LSnapshot(LRecoverInfo *recover, BailoutKind kind)
-  : numSlots_(TotalOperandCount(recover->mir()) * BOX_PIECES),
+bool
+LRecoverInfo::init(MResumePoint *rp)
+{
+    MResumePoint *it = rp;
+
+    // Sort operations in the order in which we need to restore the stack. This
+    // implies that outer frames, as well as operations needed to recover the
+    // current frame, are located before the current frame. The inner-most
+    // resume point should be the last element in the list.
+    do {
+        if (!instructions_.append(it))
+            return false;
+        it = it->caller();
+    } while (it);
+
+    Reverse(instructions_.begin(), instructions_.end());
+    MOZ_ASSERT(mir() == rp);
+    return true;
+}
+
+LSnapshot::LSnapshot(LRecoverInfo *recoverInfo, BailoutKind kind)
+  : numSlots_(TotalOperandCount(recoverInfo->mir()) * BOX_PIECES),
     slots_(nullptr),
-    recoverInfo_(recover),
+    recoverInfo_(recoverInfo),
     snapshotOffset_(INVALID_SNAPSHOT_OFFSET),
     bailoutId_(INVALID_BAILOUT_ID),
     bailoutKind_(kind)
 { }
 
 bool
 LSnapshot::init(MIRGenerator *gen)
 {
--- a/js/src/jit/LIR.h
+++ b/js/src/jit/LIR.h
@@ -873,36 +873,51 @@ class LCallInstructionHelper : public LI
   public:
     virtual bool isCall() const {
         return true;
     }
 };
 
 class LRecoverInfo : public TempObject
 {
-    MResumePoint *mir_;
+  public:
+    typedef Vector<MResumePoint *, 2, IonAllocPolicy> Instructions;
+
+  private:
+    // List of instructions needed to recover the stack frames.
+    // Outer frames are stored before inner frames.
+    Instructions instructions_;
 
     // Cached offset where this resume point is encoded.
     RecoverOffset recoverOffset_;
 
-    LRecoverInfo(MResumePoint *mir);
+    LRecoverInfo(TempAllocator &alloc);
+    bool init(MResumePoint *mir);
 
   public:
     static LRecoverInfo *New(MIRGenerator *gen, MResumePoint *mir);
 
+    // Resume point of the inner most function.
     MResumePoint *mir() const {
-        return mir_;
+        return instructions_.back();
     }
     RecoverOffset recoverOffset() const {
         return recoverOffset_;
     }
     void setRecoverOffset(RecoverOffset offset) {
         JS_ASSERT(recoverOffset_ == INVALID_RECOVER_OFFSET);
         recoverOffset_ = offset;
     }
+
+    MResumePoint **begin() {
+        return instructions_.begin();
+    }
+    MResumePoint **end() {
+        return instructions_.end();
+    }
 };
 
 // An LSnapshot is the reflection of an MResumePoint in LIR. Unlike MResumePoints,
 // they cannot be shared, as they are filled in by the register allocator in
 // order to capture the precise low-level stack state in between an
 // instruction's input and output. During code generation, LSnapshots are
 // compressed and saved in the compiled script.
 class LSnapshot : public TempObject
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -267,17 +267,17 @@
     _(RoundF)                       \
     _(In)                           \
     _(InArray)                      \
     _(InstanceOfO)                  \
     _(InstanceOfV)                  \
     _(CallInstanceOf)               \
     _(InterruptCheck)               \
     _(InterruptCheckImplicit)       \
-    _(FunctionBoundary)             \
+    _(ProfilerStackOp)              \
     _(GetDOMProperty)               \
     _(GetDOMMember)                 \
     _(SetDOMProperty)               \
     _(CallDOMNative)                \
     _(IsCallable)                   \
     _(HaveSameClass)                \
     _(HasClass)                      \
     _(AsmJSLoadHeap)                \
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -3353,19 +3353,19 @@ LIRGenerator::visitCallInstanceOf(MCallI
 
     LCallInstanceOf *lir = new(alloc()) LCallInstanceOf(useRegisterAtStart(rhs));
     if (!useBoxAtStart(lir, LCallInstanceOf::LHS, lhs))
         return false;
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
-LIRGenerator::visitFunctionBoundary(MFunctionBoundary *ins)
+LIRGenerator::visitProfilerStackOp(MProfilerStackOp *ins)
 {
-    LFunctionBoundary *lir = new(alloc()) LFunctionBoundary(temp());
+    LProfilerStackOp *lir = new(alloc()) LProfilerStackOp(temp());
     if (!add(lir, ins))
         return false;
     // If slow assertions are enabled, then this node will result in a callVM
     // out to a C++ function for the assertions, so we will need a safepoint.
     return !gen->options.spsSlowAssertionsEnabled() || assignSafepoint(lir, ins);
 }
 
 bool
@@ -3527,18 +3527,21 @@ LIRGenerator::visitGetDOMProperty(MGetDO
 
     return defineReturn(lir, ins) && assignSafepoint(lir, ins);
 }
 
 bool
 LIRGenerator::visitGetDOMMember(MGetDOMMember *ins)
 {
     MOZ_ASSERT(ins->isDomMovable(), "Members had better be movable");
-    MOZ_ASSERT(ins->domAliasSet() == JSJitInfo::AliasNone,
-               "Members had better not alias anything");
+    // We wish we could assert that ins->domAliasSet() == JSJitInfo::AliasNone,
+    // but some MGetDOMMembers are for [Pure], not [Constant] properties, whose
+    // value can in fact change as a result of DOM setters and method calls.
+    MOZ_ASSERT(ins->domAliasSet() != JSJitInfo::AliasEverything,
+               "Member gets had better not alias the world");
     LGetDOMMember *lir =
         new(alloc()) LGetDOMMember(useRegister(ins->object()));
     return defineBox(lir, ins);
 }
 
 bool
 LIRGenerator::visitRecompileCheck(MRecompileCheck *ins)
 {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -233,17 +233,17 @@ class LIRGenerator : public LIRGenerator
     bool visitRunOncePrologue(MRunOncePrologue *ins);
     bool visitRest(MRest *ins);
     bool visitRestPar(MRestPar *ins);
     bool visitThrow(MThrow *ins);
     bool visitIn(MIn *ins);
     bool visitInArray(MInArray *ins);
     bool visitInstanceOf(MInstanceOf *ins);
     bool visitCallInstanceOf(MCallInstanceOf *ins);
-    bool visitFunctionBoundary(MFunctionBoundary *ins);
+    bool visitProfilerStackOp(MProfilerStackOp *ins);
     bool visitIsCallable(MIsCallable *ins);
     bool visitHaveSameClass(MHaveSameClass *ins);
     bool visitHasClass(MHasClass *ins);
     bool visitAsmJSLoadGlobalVar(MAsmJSLoadGlobalVar *ins);
     bool visitAsmJSStoreGlobalVar(MAsmJSStoreGlobalVar *ins);
     bool visitAsmJSLoadFFIFunc(MAsmJSLoadFFIFunc *ins);
     bool visitAsmJSParameter(MAsmJSParameter *ins);
     bool visitAsmJSReturn(MAsmJSReturn *ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -9212,48 +9212,48 @@ class MNewStringObject :
     TypePolicy *typePolicy() {
         return this;
     }
 };
 
 // Node that represents that a script has begun executing. This comes at the
 // start of the function and is called once per function (including inline
 // ones)
-class MFunctionBoundary : public MNullaryInstruction
+class MProfilerStackOp : public MNullaryInstruction
 {
   public:
     enum Type {
         Enter,        // a function has begun executing and it is not inline
         Exit,         // any function has exited (inlined or normal)
-        Inline_Enter, // an inline function has begun executing
-
-        Inline_Exit   // all instructions of an inline function are done, a
+        InlineEnter,  // an inline function has begun executing
+
+        InlineExit    // all instructions of an inline function are done, a
                       // return from the inline function could have occurred
                       // before this boundary
     };
 
   private:
     JSScript *script_;
     Type type_;
     unsigned inlineLevel_;
 
-    MFunctionBoundary(JSScript *script, Type type, unsigned inlineLevel)
+    MProfilerStackOp(JSScript *script, Type type, unsigned inlineLevel)
       : script_(script), type_(type), inlineLevel_(inlineLevel)
     {
-        JS_ASSERT_IF(type != Inline_Exit, script != nullptr);
-        JS_ASSERT_IF(type == Inline_Enter, inlineLevel != 0);
+        JS_ASSERT_IF(type != InlineExit, script != nullptr);
+        JS_ASSERT_IF(type == InlineEnter, inlineLevel != 0);
         setGuard();
     }
 
   public:
-    INSTRUCTION_HEADER(FunctionBoundary)
-
-    static MFunctionBoundary *New(TempAllocator &alloc, JSScript *script, Type type,
+    INSTRUCTION_HEADER(ProfilerStackOp)
+
+    static MProfilerStackOp *New(TempAllocator &alloc, JSScript *script, Type type,
                                   unsigned inlineLevel = 0) {
-        return new(alloc) MFunctionBoundary(script, type, inlineLevel);
+        return new(alloc) MProfilerStackOp(script, type, inlineLevel);
     }
 
     JSScript *script() {
         return script_;
     }
 
     Type type() {
         return type_;
@@ -9420,54 +9420,18 @@ class MResumePoint MOZ_FINAL : public MN
     }
 
     void discardUses() {
         for (size_t i = 0; i < stackDepth_; i++) {
             if (operands_[i].hasProducer())
                 operands_[i].producer()->removeUse(&operands_[i]);
         }
     }
-};
-
-/*
- * Facade for a chain of MResumePoints that cross frame boundaries (due to
- * function inlining). Operands are ordered from oldest frame to newest.
- */
-class FlattenedMResumePointIter
-{
-    Vector<MResumePoint *, 8, SystemAllocPolicy> resumePoints;
-    MResumePoint *newest;
-    size_t numOperands_;
-
-  public:
-    explicit FlattenedMResumePointIter(MResumePoint *newest)
-      : newest(newest), numOperands_(0)
-    {}
-
-    bool init() {
-        MResumePoint *it = newest;
-        do {
-            if (!resumePoints.append(it))
-                return false;
-            it = it->caller();
-        } while (it);
-        Reverse(resumePoints.begin(), resumePoints.end());
-        return true;
-    }
-
-    MResumePoint **begin() {
-        return resumePoints.begin();
-    }
-    MResumePoint **end() {
-        return resumePoints.end();
-    }
-
-    size_t numOperands() const {
-        return numOperands_;
-    }
+
+    bool writeRecoverData(CompactBufferWriter &writer) const;
 };
 
 class MIsCallable
   : public MUnaryInstruction,
     public SingleObjectPolicy
 {
     MIsCallable(MDefinition *object)
       : MUnaryInstruction(object)
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -182,17 +182,17 @@ namespace jit {
     _(RunOncePrologue)                                                      \
     _(Rest)                                                                 \
     _(Floor)                                                                \
     _(Round)                                                                \
     _(In)                                                                   \
     _(InstanceOf)                                                           \
     _(CallInstanceOf)                                                       \
     _(InterruptCheck)                                                       \
-    _(FunctionBoundary)                                                     \
+    _(ProfilerStackOp)                                                      \
     _(GetDOMProperty)                                                       \
     _(GetDOMMember)                                                         \
     _(SetDOMProperty)                                                       \
     _(IsCallable)                                                           \
     _(HaveSameClass)                                                        \
     _(HasClass)                                                              \
     _(AsmJSNeg)                                                             \
     _(AsmJSUnsignedToDouble)                                                \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp
+++ b/js/src/jit/ParallelSafetyAnalysis.cpp
@@ -286,17 +286,17 @@ class ParallelSafetyVisitor : public MIn
     UNSAFE_OP(Random)
     SAFE_OP(Pow)
     SAFE_OP(PowHalf)
     UNSAFE_OP(RegExpTest)
     UNSAFE_OP(RegExpExec)
     UNSAFE_OP(RegExpReplace)
     UNSAFE_OP(StringReplace)
     UNSAFE_OP(CallInstanceOf)
-    UNSAFE_OP(FunctionBoundary)
+    UNSAFE_OP(ProfilerStackOp)
     UNSAFE_OP(GuardString)
     UNSAFE_OP(NewDeclEnvObject)
     UNSAFE_OP(In)
     UNSAFE_OP(InArray)
     SAFE_OP(GuardThreadExclusive)
     SAFE_OP(InterruptCheckPar)
     SAFE_OP(CheckOverRecursedPar)
     SAFE_OP(FunctionDispatch)
new file mode 100644
--- /dev/null
+++ b/js/src/jit/Recover.cpp
@@ -0,0 +1,110 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 "jit/Recover.h"
+
+#include "jit/IonSpewer.h"
+#include "jit/MIR.h"
+#include "jit/MIRGraph.h"
+
+using namespace js;
+using namespace js::jit;
+
+void
+RInstruction::readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw)
+{
+    uint32_t op = reader.readUnsigned();
+    switch (Opcode(op)) {
+      case Recover_ResumePoint:
+        new (raw->addr()) RResumePoint(reader);
+        break;
+      default:
+        MOZ_ASSUME_UNREACHABLE("Bad decoding of the previous instruction?");
+        break;
+    }
+}
+
+bool
+MResumePoint::writeRecoverData(CompactBufferWriter &writer) const
+{
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_ResumePoint));
+
+    MBasicBlock *bb = block();
+    JSFunction *fun = bb->info().funMaybeLazy();
+    JSScript *script = bb->info().script();
+    uint32_t exprStack = stackDepth() - bb->info().ninvoke();
+
+#ifdef DEBUG
+    // Ensure that all snapshot which are encoded can safely be used for
+    // bailouts.
+    if (GetIonContext()->cx) {
+        uint32_t stackDepth;
+        bool reachablePC;
+        jsbytecode *bailPC = pc();
+
+        if (mode() == MResumePoint::ResumeAfter)
+            bailPC = GetNextPc(pc());
+
+        if (!ReconstructStackDepth(GetIonContext()->cx, script,
+                                   bailPC, &stackDepth, &reachablePC))
+        {
+            return false;
+        }
+
+        if (reachablePC) {
+            if (JSOp(*bailPC) == JSOP_FUNCALL) {
+                // For fun.call(this, ...); the reconstructStackDepth will
+                // include the this. When inlining that is not included.  So the
+                // exprStackSlots will be one less.
+                MOZ_ASSERT(stackDepth - exprStack <= 1);
+            } else if (JSOp(*bailPC) != JSOP_FUNAPPLY &&
+                       !IsGetPropPC(bailPC) && !IsSetPropPC(bailPC))
+            {
+                // For fun.apply({}, arguments) the reconstructStackDepth will
+                // have stackdepth 4, but it could be that we inlined the
+                // funapply. In that case exprStackSlots, will have the real
+                // arguments in the slots and not be 4.
+
+                // With accessors, we have different stack depths depending on
+                // whether or not we inlined the accessor, as the inlined stack
+                // contains a callee function that should never have been there
+                // and we might just be capturing an uneventful property site,
+                // in which case there won't have been any violence.
+                MOZ_ASSERT(exprStack == stackDepth);
+            }
+        }
+    }
+#endif
+
+    // Test if we honor the maximum of arguments at all times.  This is a sanity
+    // check and not an algorithm limit. So check might be a bit too loose.  +4
+    // to account for scope chain, return value, this value and maybe
+    // arguments_object.
+    MOZ_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS + 4);
+
+    uint32_t implicit = StartArgSlot(script);
+    uint32_t formalArgs = CountArgSlots(script, fun);
+    uint32_t nallocs = formalArgs + script->nfixed() + exprStack;
+
+    IonSpew(IonSpew_Snapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u",
+            implicit, formalArgs - implicit, script->nfixed(), exprStack);
+
+    uint32_t pcoff = script->pcToOffset(pc());
+    IonSpew(IonSpew_Snapshots, "Writing pc offset %u, nslots %u", pcoff, nallocs);
+    writer.writeUnsigned(pcoff);
+    writer.writeUnsigned(nallocs);
+    return true;
+}
+
+RResumePoint::RResumePoint(CompactBufferReader &reader)
+{
+    static_assert(sizeof(*this) <= sizeof(RInstructionStorage),
+                  "Storage space is too small to decode this recover instruction.");
+    pcOffset_ = reader.readUnsigned();
+    numOperands_ = reader.readUnsigned();
+    IonSpew(IonSpew_Snapshots, "Read RResumePoint (pc offset %u, nslots %u)",
+            pcOffset_, numOperands_);
+}
new file mode 100644
--- /dev/null
+++ b/js/src/jit/Recover.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * 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 jit_Recover_h
+#define jit_Recover_h
+
+#include "mozilla/Attributes.h"
+
+#include "jit/Snapshots.h"
+
+namespace js {
+namespace jit {
+
+class RResumePoint;
+
+class RInstruction
+{
+  public:
+    enum Opcode
+    {
+        Recover_ResumePoint = 0
+    };
+
+    virtual Opcode opcode() const = 0;
+
+    bool isResumePoint() const {
+        return opcode() == Recover_ResumePoint;
+    }
+    inline const RResumePoint *toResumePoint() const;
+
+    virtual uint32_t numOperands() const = 0;
+
+    static void readRecoverData(CompactBufferReader &reader, RInstructionStorage *raw);
+};
+
+class RResumePoint MOZ_FINAL : public RInstruction
+{
+  private:
+    uint32_t pcOffset_;           // Offset from script->code.
+    uint32_t numOperands_;        // Number of slots.
+
+    friend class RInstruction;
+    RResumePoint(CompactBufferReader &reader);
+
+  public:
+    virtual Opcode opcode() const {
+        return Recover_ResumePoint;
+    }
+
+    uint32_t pcOffset() const {
+        return pcOffset_;
+    }
+    virtual uint32_t numOperands() const {
+        return numOperands_;
+    }
+};
+
+const RResumePoint *
+RInstruction::toResumePoint() const
+{
+    MOZ_ASSERT(isResumePoint());
+    return static_cast<const RResumePoint *>(this);
+}
+
+}
+}
+
+#endif /* jit_Recover_h */
--- a/js/src/jit/Snapshots.cpp
+++ b/js/src/jit/Snapshots.cpp
@@ -7,18 +7,19 @@
 #include "jit/Snapshots.h"
 
 #include "jsscript.h"
 
 #include "jit/CompileInfo.h"
 #include "jit/IonSpewer.h"
 #ifdef TRACK_SNAPSHOTS
 # include "jit/LIR.h"
-# include "jit/MIR.h"
 #endif
+#include "jit/MIR.h"
+#include "jit/Recover.h"
 
 using namespace js;
 using namespace js::jit;
 
 // Snapshot header:
 //
 //   [vwu] bits ((n+1)-31]: frame count
 //         bit n+1: resume after
@@ -476,19 +477,19 @@ static const uint32_t SNAPSHOT_ROFFSET_S
 static const uint32_t SNAPSHOT_ROFFSET_BITS = 32 - SNAPSHOT_ROFFSET_SHIFT;
 static const uint32_t SNAPSHOT_ROFFSET_MASK = COMPUTE_MASK_(SNAPSHOT_ROFFSET);
 
 // Details of recover header packing.
 static const uint32_t RECOVER_RESUMEAFTER_SHIFT = 0;
 static const uint32_t RECOVER_RESUMEAFTER_BITS = 1;
 static const uint32_t RECOVER_RESUMEAFTER_MASK = COMPUTE_MASK_(RECOVER_RESUMEAFTER);
 
-static const uint32_t RECOVER_FRAMECOUNT_SHIFT = COMPUTE_SHIFT_AFTER_(RECOVER_RESUMEAFTER);
-static const uint32_t RECOVER_FRAMECOUNT_BITS = 32 - RECOVER_FRAMECOUNT_SHIFT;
-static const uint32_t RECOVER_FRAMECOUNT_MASK = COMPUTE_MASK_(RECOVER_FRAMECOUNT);
+static const uint32_t RECOVER_RINSCOUNT_SHIFT = COMPUTE_SHIFT_AFTER_(RECOVER_RESUMEAFTER);
+static const uint32_t RECOVER_RINSCOUNT_BITS = 32 - RECOVER_RINSCOUNT_SHIFT;
+static const uint32_t RECOVER_RINSCOUNT_MASK = COMPUTE_MASK_(RECOVER_RINSCOUNT);
 
 #undef COMPUTE_MASK_
 #undef COMPUTE_SHIFT_AFTER_
 
 void
 SnapshotReader::readSnapshotHeader()
 {
     uint32_t bits = reader_.readUnsigned();
@@ -525,74 +526,72 @@ SnapshotReader::spewBailingFrom() const
         fprintf(IonSpewFile, " [%u], LIR: ", mirId_);
         LInstruction::printName(IonSpewFile, LInstruction::Opcode(lirOpcode_));
         fprintf(IonSpewFile, " [%u]", lirId_);
         fprintf(IonSpewFile, "\n");
     }
 }
 #endif
 
+uint32_t
+SnapshotReader::readAllocationIndex()
+{
+    allocRead_++;
+    return reader_.readUnsigned();
+}
+
 RValueAllocation
 SnapshotReader::readAllocation()
 {
     IonSpew(IonSpew_Snapshots, "Reading slot %u", allocRead_);
-    allocRead_++;
-
-    uint32_t offset = reader_.readUnsigned() * ALLOCATION_TABLE_ALIGNMENT;
+    uint32_t offset = readAllocationIndex() * ALLOCATION_TABLE_ALIGNMENT;
     allocReader_.seek(allocTable_, offset);
     return RValueAllocation::read(allocReader_);
 }
 
 bool
 SnapshotWriter::init()
 {
     // Based on the measurements made in Bug 962555 comment 20, this should be
     // enough to prevent the reallocation of the hash table for at least half of
     // the compilations.
     return allocMap_.init(32);
 }
 
 RecoverReader::RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size)
   : reader_(nullptr, nullptr),
-    frameCount_(0),
-    framesRead_(0),
-    allocCount_(0)
+    numInstructions_(0),
+    numInstructionsRead_(0)
 {
     if (!recovers)
         return;
     reader_ = CompactBufferReader(recovers + snapshot.recoverOffset(), recovers + size);
     readRecoverHeader();
-    readFrame(snapshot);
+    readInstruction();
 }
 
 void
 RecoverReader::readRecoverHeader()
 {
     uint32_t bits = reader_.readUnsigned();
 
-    frameCount_ = (bits & RECOVER_FRAMECOUNT_MASK) >> RECOVER_FRAMECOUNT_SHIFT;
+    numInstructions_ = (bits & RECOVER_RINSCOUNT_MASK) >> RECOVER_RINSCOUNT_SHIFT;
     resumeAfter_ = (bits & RECOVER_RESUMEAFTER_MASK) >> RECOVER_RESUMEAFTER_SHIFT;
-    JS_ASSERT(frameCount_);
+    MOZ_ASSERT(numInstructions_);
 
-    IonSpew(IonSpew_Snapshots, "Read recover header with frameCount %u (ra: %d)",
-            frameCount_, resumeAfter_);
+    IonSpew(IonSpew_Snapshots, "Read recover header with instructionCount %u (ra: %d)",
+            numInstructions_, resumeAfter_);
 }
 
 void
-RecoverReader::readFrame(SnapshotReader &snapshot)
+RecoverReader::readInstruction()
 {
-    JS_ASSERT(moreFrames());
-    JS_ASSERT(snapshot.allocRead_ == allocCount_);
-
-    pcOffset_ = reader_.readUnsigned();
-    allocCount_ = reader_.readUnsigned();
-    IonSpew(IonSpew_Snapshots, "Read pc offset %u, nslots %u", pcOffset_, allocCount_);
-
-    framesRead_++;
-    snapshot.allocRead_ = 0;
+    MOZ_ASSERT(moreInstructions());
+    RInstruction::readRecoverData(reader_, &rawData_);
+    numInstructionsRead_++;
 }
 
 SnapshotOffset
 SnapshotWriter::startSnapshot(RecoverOffset recoverOffset, BailoutKind kind)
 {
     lastStart_ = writer_.length();
     allocWritten_ = 0;
 
@@ -668,46 +667,32 @@ RecoverWriter::startRecover(uint32_t fra
     MOZ_ASSERT(frameCount);
     nframes_ = frameCount;
     framesWritten_ = 0;
 
     IonSpew(IonSpew_Snapshots, "starting recover with frameCount %u",
             frameCount);
 
     MOZ_ASSERT(!(uint32_t(resumeAfter) &~ RECOVER_RESUMEAFTER_MASK));
-    MOZ_ASSERT(frameCount < uint32_t(1 << RECOVER_FRAMECOUNT_BITS));
+    MOZ_ASSERT(frameCount < uint32_t(1 << RECOVER_RINSCOUNT_BITS));
     uint32_t bits =
         (uint32_t(resumeAfter) << RECOVER_RESUMEAFTER_SHIFT) |
-        (frameCount << RECOVER_FRAMECOUNT_SHIFT);
+        (frameCount << RECOVER_RINSCOUNT_SHIFT);
 
     RecoverOffset recoverOffset = writer_.length();
     writer_.writeUnsigned(bits);
     return recoverOffset;
 }
 
-void
-RecoverWriter::writeFrame(JSFunction *fun, JSScript *script,
-                          jsbytecode *pc, uint32_t exprStack)
+bool
+RecoverWriter::writeFrame(const MResumePoint *rp)
 {
-    // Test if we honor the maximum of arguments at all times.
-    // This is a sanity check and not an algorithm limit. So check might be a bit too loose.
-    // +4 to account for scope chain, return value, this value and maybe arguments_object.
-    JS_ASSERT(CountArgSlots(script, fun) < SNAPSHOT_MAX_NARGS + 4);
-
-    uint32_t implicit = StartArgSlot(script);
-    uint32_t formalArgs = CountArgSlots(script, fun);
-    uint32_t nallocs = formalArgs + script->nfixed() + exprStack;
-
-    IonSpew(IonSpew_Snapshots, "Starting frame; implicit %u, formals %u, fixed %u, exprs %u",
-            implicit, formalArgs - implicit, script->nfixed(), exprStack);
-
-    uint32_t pcoff = script->pcToOffset(pc);
-    IonSpew(IonSpew_Snapshots, "Writing pc offset %u, nslots %u", pcoff, nallocs);
-    writer_.writeUnsigned(pcoff);
-    writer_.writeUnsigned(nallocs);
+    if (!rp->writeRecoverData(writer_))
+        return false;
     framesWritten_++;
+    return true;
 }
 
 void
 RecoverWriter::endRecover()
 {
     JS_ASSERT(nframes_ == framesWritten_);
 }
--- a/js/src/jit/Snapshots.h
+++ b/js/src/jit/Snapshots.h
@@ -2,16 +2,18 @@
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * 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 jit_Snapshot_h
 #define jit_Snapshot_h
 
+#include "mozilla/Alignment.h"
+
 #include "jsalloc.h"
 #include "jsbytecode.h"
 
 #include "jit/CompactBuffer.h"
 #include "jit/IonTypes.h"
 #include "jit/Registers.h"
 
 #include "js/HashTable.h"
@@ -303,17 +305,16 @@ class RValueAllocation
 };
 
 class RecoverWriter;
 
 // Collects snapshots in a contiguous buffer, which is copied into IonScript
 // memory after code generation.
 class SnapshotWriter
 {
-    friend class RecoverWriter;
     CompactBufferWriter writer_;
     CompactBufferWriter allocWriter_;
 
     // Map RValueAllocations to an offset in the allocWriter_ buffer.  This is
     // useful as value allocations are repeated frequently.
     typedef RValueAllocation RVA;
     typedef HashMap<RVA, uint32_t, RVA::Hasher, SystemAllocPolicy> RValueAllocMap;
     RValueAllocMap allocMap_;
@@ -354,27 +355,29 @@ class SnapshotWriter
     size_t RVATableSize() const {
         return allocWriter_.length();
     }
     const uint8_t *RVATableBuffer() const {
         return allocWriter_.buffer();
     }
 };
 
+class MResumePoint;
+
 class RecoverWriter
 {
     CompactBufferWriter writer_;
 
     uint32_t nframes_;
     uint32_t framesWritten_;
 
   public:
     SnapshotOffset startRecover(uint32_t frameCount, bool resumeAfter);
 
-    void writeFrame(JSFunction *fun, JSScript *script, jsbytecode *pc, uint32_t exprStack);
+    bool writeFrame(const MResumePoint *rp);
 
     void endRecover();
 
     size_t size() const {
         return writer_.length();
     }
     const uint8_t *buffer() const {
         return writer_.buffer();
@@ -388,18 +391,16 @@ class RecoverWriter
 class RecoverReader;
 
 // A snapshot reader reads the entries out of the compressed snapshot buffer in
 // a script. These entries describe the equivalent interpreter frames at a given
 // position in JIT code. Each entry is an Ion's value allocations, used to
 // recover the corresponding Value from an Ion frame.
 class SnapshotReader
 {
-    friend class RecoverReader;
-
     CompactBufferReader reader_;
     CompactBufferReader allocReader_;
     const uint8_t* allocTable_;
 
     BailoutKind bailoutKind_;
     uint32_t allocRead_;          // Number of slots that have been read.
     RecoverOffset recoverOffset_; // Offset of the recover instructions.
 
@@ -413,70 +414,82 @@ class SnapshotReader
 
   public:
     void readTrackSnapshot();
     void spewBailingFrom() const;
 #endif
 
   private:
     void readSnapshotHeader();
-    void readFrameHeader();
+    uint32_t readAllocationIndex();
 
   public:
     SnapshotReader(const uint8_t *snapshots, uint32_t offset,
                    uint32_t RVATableSize, uint32_t listSize);
 
     RValueAllocation readAllocation();
+    void skipAllocation() {
+        readAllocationIndex();
+    }
 
     BailoutKind bailoutKind() const {
         return bailoutKind_;
     }
     RecoverOffset recoverOffset() const {
         return recoverOffset_;
     }
+
+    uint32_t numAllocationsRead() const {
+        return allocRead_;
+    }
+    void resetNumAllocationsRead() {
+        allocRead_ = 0;
+    }
 };
 
+typedef mozilla::AlignedStorage<4 * sizeof(uint32_t)> RInstructionStorage;
+class RInstruction;
+
 class RecoverReader
 {
     CompactBufferReader reader_;
 
-    uint32_t frameCount_;
-    uint32_t framesRead_;         // Number of frame headers that have been read.
-    uint32_t pcOffset_;           // Offset from script->code.
-    uint32_t allocCount_;         // Number of slots.
+    // Number of encoded instructions.
+    uint32_t numInstructions_;
+
+    // Number of instruction read.
+    uint32_t numInstructionsRead_;
+
+    // True if we need to resume after the Resume Point instruction of the
+    // innermost frame.
     bool resumeAfter_;
 
+    // Space is reserved as part of the RecoverReader to avoid allocations of
+    // data which is needed to decode the current instruction.
+    RInstructionStorage rawData_;
+
   private:
     void readRecoverHeader();
-    void readFrame(SnapshotReader &snapshot);
+    void readInstruction();
 
   public:
     RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size);
 
-    bool moreFrames() const {
-        return framesRead_ < frameCount_;
+    bool moreInstructions() const {
+        return numInstructionsRead_ < numInstructions_;
     }
-    void nextFrame(SnapshotReader &snapshot) {
-        readFrame(snapshot);
-    }
-    uint32_t frameCount() const {
-        return frameCount_;
+    void nextInstruction() {
+        readInstruction();
     }
 
-    uint32_t pcOffset() const {
-        return pcOffset_;
+    const RInstruction *instruction() const {
+        return reinterpret_cast<const RInstruction *>(rawData_.addr());
     }
+
     bool resumeAfter() const {
         return resumeAfter_;
     }
-
-    uint32_t allocations() const {
-        return allocCount_;
-    }
-    bool moreAllocations(const SnapshotReader &snapshot) const {
-        return snapshot.allocRead_ < allocCount_;
-    }
 };
 
 }
 }
 
 #endif /* jit_Snapshot_h */
--- a/js/src/jit/arm/Simulator-arm.cpp
+++ b/js/src/jit/arm/Simulator-arm.cpp
@@ -32,16 +32,35 @@
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/Likely.h"
 #include "mozilla/MathAlgorithms.h"
 
 #include "jit/arm/Assembler-arm.h"
 #include "jit/AsmJS.h"
 #include "vm/Runtime.h"
 
+extern "C" {
+
+int64_t
+__aeabi_idivmod(int x, int y)
+{
+    uint32_t lo = uint32_t(x / y);
+    uint32_t hi = uint32_t(x % y);
+    return (int64_t(hi) << 32) | lo;
+}
+
+int64_t
+__aeabi_uidivmod(int x, int y)
+{
+    uint32_t lo = uint32_t(x) / uint32_t(y);
+    uint32_t hi = uint32_t(x) % uint32_t(y);
+    return (int64_t(hi) << 32) | lo;
+}
+}
+
 namespace js {
 namespace jit {
 
 // Load/store multiple addressing mode.
 enum BlockAddrMode {
     // Alias modes for comparison when writeback does not matter.
     da_x         = (0|0|0) << 21,  // Decrement after.
     ia_x         = (0|4|0) << 21,  // Increment after.
@@ -4266,27 +4285,8 @@ JSRuntime::simulatorRuntime() const
 }
 
 void
 JSRuntime::setSimulatorRuntime(js::jit::SimulatorRuntime *srt)
 {
     MOZ_ASSERT(!simulatorRuntime_);
     simulatorRuntime_ = srt;
 }
-
-extern "C" {
-
-int64_t
-__aeabi_idivmod(int x, int y)
-{
-    uint32_t lo = uint32_t(x / y);
-    uint32_t hi = uint32_t(x % y);
-    return (int64_t(hi) << 32) | lo;
-}
-
-int64_t
-__aeabi_uidivmod(int x, int y)
-{
-    uint32_t lo = uint32_t(x) / uint32_t(y);
-    uint32_t hi = uint32_t(x) % uint32_t(y);
-    return (int64_t(hi) << 32) | lo;
-}
-}
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -245,94 +245,44 @@ CodeGeneratorShared::encode(LRecoverInfo
             (void *)recover, frameCount);
 
     MResumePoint::Mode mode = recover->mir()->mode();
     JS_ASSERT(mode != MResumePoint::Outer);
     bool resumeAfter = (mode == MResumePoint::ResumeAfter);
 
     RecoverOffset offset = recovers_.startRecover(frameCount, resumeAfter);
 
-    FlattenedMResumePointIter mirOperandIter(recover->mir());
-    if (!mirOperandIter.init())
-        return false;
-
-    for (MResumePoint **it = mirOperandIter.begin(), **end = mirOperandIter.end();
+    for (MResumePoint **it = recover->begin(), **end = recover->end();
          it != end;
          ++it)
     {
-        MResumePoint *mir = *it;
-        MBasicBlock *block = mir->block();
-        JSFunction *fun = block->info().funMaybeLazy();
-        JSScript *script = block->info().script();
-        jsbytecode *pc = mir->pc();
-        uint32_t exprStack = mir->stackDepth() - block->info().ninvoke();
-        recovers_.writeFrame(fun, script, pc, exprStack);
-
-#ifdef DEBUG
-        // Ensure that all snapshot which are encoded can safely be used for
-        // bailouts.
-        if (GetIonContext()->cx) {
-            uint32_t stackDepth;
-            bool reachablePC;
-            jsbytecode *bailPC = pc;
-
-            if (mir->mode() == MResumePoint::ResumeAfter)
-                bailPC = GetNextPc(pc);
-
-            if (!ReconstructStackDepth(GetIonContext()->cx, script,
-                                       bailPC, &stackDepth, &reachablePC))
-            {
-                return false;
-            }
-
-            if (reachablePC) {
-                if (JSOp(*bailPC) == JSOP_FUNCALL) {
-                    // For fun.call(this, ...); the reconstructStackDepth will
-                    // include the this. When inlining that is not included.
-                    // So the exprStackSlots will be one less.
-                    JS_ASSERT(stackDepth - exprStack <= 1);
-                } else if (JSOp(*bailPC) != JSOP_FUNAPPLY &&
-                           !IsGetPropPC(bailPC) && !IsSetPropPC(bailPC))
-                {
-                    // For fun.apply({}, arguments) the reconstructStackDepth will
-                    // have stackdepth 4, but it could be that we inlined the
-                    // funapply. In that case exprStackSlots, will have the real
-                    // arguments in the slots and not be 4.
-
-                    // With accessors, we have different stack depths depending on
-                    // whether or not we inlined the accessor, as the inlined stack
-                    // contains a callee function that should never have been there
-                    // and we might just be capturing an uneventful property site, in
-                    // which case there won't have been any violence.
-                    JS_ASSERT(exprStack == stackDepth);
-                }
-            }
-        }
-#endif
+        if (!recovers_.writeFrame(*it))
+            return false;
     }
 
     recovers_.endRecover();
     recover->setRecoverOffset(offset);
     return !recovers_.oom();
 }
 
 bool
 CodeGeneratorShared::encode(LSnapshot *snapshot)
 {
     if (snapshot->snapshotOffset() != INVALID_SNAPSHOT_OFFSET)
         return true;
 
-    if (!encode(snapshot->recoverInfo()))
+    LRecoverInfo *recoverInfo = snapshot->recoverInfo();
+    if (!encode(recoverInfo))
         return false;
 
-    RecoverOffset recoverOffset = snapshot->recoverInfo()->recoverOffset();
+    RecoverOffset recoverOffset = recoverInfo->recoverOffset();
     MOZ_ASSERT(recoverOffset != INVALID_RECOVER_OFFSET);
 
-    IonSpew(IonSpew_Snapshots, "Encoding LSnapshot %p (LRecoverInfo %p)",
-            (void *)snapshot, (void*) snapshot->recoverInfo());
+    IonSpew(IonSpew_Snapshots, "Encoding LSnapshot %p (LRecover %p)",
+            (void *)snapshot, (void*) recoverInfo);
 
     SnapshotOffset offset = snapshots_.startSnapshot(recoverOffset, snapshot->bailoutKind());
 
 #ifdef TRACK_SNAPSHOTS
     uint32_t pcOpcode = 0;
     uint32_t lirOpcode = 0;
     uint32_t lirId = 0;
     uint32_t mirOpcode = 0;
@@ -346,22 +296,18 @@ CodeGeneratorShared::encode(LSnapshot *s
             mirId = ins->mirRaw()->id();
             if (ins->mirRaw()->trackedPc())
                 pcOpcode = *ins->mirRaw()->trackedPc();
         }
     }
     snapshots_.trackSnapshot(pcOpcode, mirOpcode, mirId, lirOpcode, lirId);
 #endif
 
-    FlattenedMResumePointIter mirOperandIter(snapshot->recoverInfo()->mir());
-    if (!mirOperandIter.init())
-        return false;
-
     uint32_t startIndex = 0;
-    for (MResumePoint **it = mirOperandIter.begin(), **end = mirOperandIter.end();
+    for (MResumePoint **it = recoverInfo->begin(), **end = recoverInfo->end();
          it != end;
          ++it)
     {
         MResumePoint *mir = *it;
         if (!encodeAllocations(snapshot, mir, &startIndex))
             return false;
     }
 
--- a/js/src/jit/shared/Lowering-shared.cpp
+++ b/js/src/jit/shared/Lowering-shared.cpp
@@ -58,38 +58,37 @@ LIRGeneratorShared::lowerTypedPhiInput(M
 
 LRecoverInfo *
 LIRGeneratorShared::getRecoverInfo(MResumePoint *rp)
 {
     if (cachedRecoverInfo_ && cachedRecoverInfo_->mir() == rp)
         return cachedRecoverInfo_;
 
     LRecoverInfo *recoverInfo = LRecoverInfo::New(gen, rp);
+    if (!recoverInfo)
+        return nullptr;
+
     cachedRecoverInfo_ = recoverInfo;
     return recoverInfo;
 }
 
 #ifdef JS_NUNBOX32
 LSnapshot *
 LIRGeneratorShared::buildSnapshot(LInstruction *ins, MResumePoint *rp, BailoutKind kind)
 {
     LRecoverInfo *recover = getRecoverInfo(rp);
     if (!recover)
         return nullptr;
 
     LSnapshot *snapshot = LSnapshot::New(gen, recover, kind);
     if (!snapshot)
         return nullptr;
 
-    FlattenedMResumePointIter iter(rp);
-    if (!iter.init())
-        return nullptr;
-
     size_t i = 0;
-    for (MResumePoint **it = iter.begin(), **end = iter.end(); it != end; ++it) {
+    for (MResumePoint **it = recover->begin(), **end = recover->end(); it != end; ++it) {
         MResumePoint *mir = *it;
         for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) {
             MDefinition *ins = mir->getOperand(j);
 
             LAllocation *type = snapshot->typeOfSlot(i);
             LAllocation *payload = snapshot->payloadOfSlot(i);
 
             if (ins->isBox())
@@ -132,22 +131,18 @@ LIRGeneratorShared::buildSnapshot(LInstr
     LRecoverInfo *recover = getRecoverInfo(rp);
     if (!recover)
         return nullptr;
 
     LSnapshot *snapshot = LSnapshot::New(gen, recover, kind);
     if (!snapshot)
         return nullptr;
 
-    FlattenedMResumePointIter iter(rp);
-    if (!iter.init())
-        return nullptr;
-
     size_t i = 0;
-    for (MResumePoint **it = iter.begin(), **end = iter.end(); it != end; ++it) {
+    for (MResumePoint **it = recover->begin(), **end = recover->end(); it != end; ++it) {
         MResumePoint *mir = *it;
         for (size_t j = 0, e = mir->numOperands(); j < e; ++i, ++j) {
             MDefinition *def = mir->getOperand(j);
 
             if (def->isBox())
                 def = def->toBox()->getOperand(0);
 
             // Guards should never be eliminated.
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -276,16 +276,17 @@ if CONFIG['ENABLE_ION']:
         'jit/MCallOptimize.cpp',
         'jit/MIR.cpp',
         'jit/MIRGraph.cpp',
         'jit/MoveResolver.cpp',
         'jit/ParallelFunctions.cpp',
         'jit/ParallelSafetyAnalysis.cpp',
         'jit/PerfSpewer.cpp',
         'jit/RangeAnalysis.cpp',
+        'jit/Recover.cpp',
         'jit/RegisterAllocator.cpp',
         'jit/Safepoints.cpp',
         'jit/shared/BaselineCompiler-shared.cpp',
         'jit/shared/CodeGenerator-shared.cpp',
         'jit/shared/Lowering-shared.cpp',
         'jit/Snapshots.cpp',
         'jit/StupidAllocator.cpp',
         'jit/TypeDescrSet.cpp',
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -1371,17 +1371,17 @@ FrameIter::numFrameSlots() const
 {
     switch (data_.state_) {
       case DONE:
       case ASMJS:
         break;
       case JIT: {
 #ifdef JS_ION
         if (data_.ionFrames_.isIonJS()) {
-            return ionInlineFrames_.snapshotIterator().allocations() -
+            return ionInlineFrames_.snapshotIterator().numAllocations() -
                 ionInlineFrames_.script()->nfixed();
         }
         jit::BaselineFrame *frame = data_.ionFrames_.baselineFrame();
         return frame->numValueSlots() - data_.ionFrames_.script()->nfixed();
 #else
         break;
 #endif
       }
--- a/js/xpconnect/src/XPCQuickStubs.h
+++ b/js/xpconnect/src/XPCQuickStubs.h
@@ -501,22 +501,16 @@ castNativeArgFromWrapper(JSContext *cx,
 }
 
 inline nsWrapperCache*
 xpc_qsGetWrapperCache(nsWrapperCache *cache)
 {
     return cache;
 }
 
-// nsGlobalWindow implements nsWrapperCache, but doesn't always use it. Don't
-// try to use it without fixing that first.
-class nsGlobalWindow;
-inline nsWrapperCache*
-xpc_qsGetWrapperCache(nsGlobalWindow *not_allowed);
-
 inline nsWrapperCache*
 xpc_qsGetWrapperCache(void *p)
 {
     return nullptr;
 }
 
 /** Convert an XPCOM pointer to jsval. Return true on success.
  * aIdentity is a performance optimization. Set it to true,
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -135,17 +135,16 @@ FinishCreate(XPCWrappedNativeScope* Scop
 // and finally into XPCWrappedNative::Init. Unfortunately, this path assumes
 // very early on that we have an XPCWrappedNativeScope and corresponding global
 // JS object, which are the very things we need to create here. So we special-
 // case the logic and do some things in a different order.
 nsresult
 XPCWrappedNative::WrapNewGlobal(xpcObjectHelper &nativeHelper,
                                 nsIPrincipal *principal,
                                 bool initStandardClasses,
-                                bool fireOnNewGlobalHook,
                                 JS::CompartmentOptions& aOptions,
                                 XPCWrappedNative **wrappedGlobal)
 {
     AutoJSContext cx;
     nsISupports *identity = nativeHelper.GetCanonical();
 
     // The object should specify that it's meant to be global.
     MOZ_ASSERT(nativeHelper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
@@ -265,18 +264,16 @@ XPCWrappedNative::WrapNewGlobal(xpcObjec
 
     // Call the common creation finish routine. This does all of the bookkeeping
     // like inserting the wrapper into the wrapper map and setting up the wrapper
     // cache.
     nsresult rv = FinishCreate(scope, iface, nativeHelper.GetWrapperCache(),
                                wrapper, wrappedGlobal);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    if (fireOnNewGlobalHook)
-        JS_FireOnNewGlobalObject(cx, global);
     return NS_OK;
 }
 
 // static
 nsresult
 XPCWrappedNative::GetNewOrUsed(xpcObjectHelper& helper,
                                XPCWrappedNativeScope* Scope,
                                XPCNativeInterface* Interface,
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -286,17 +286,18 @@ xpc_TryUnmarkWrappedGrayObject(nsISuppor
         static_cast<nsXPCWrappedJS*>(wjs.get())->GetJSObject();
     }
 }
 
 /***************************************************************************/
 /***************************************************************************/
 // nsIXPConnect interface methods...
 
-static inline nsresult UnexpectedFailure(nsresult rv)
+template<typename T>
+static inline T UnexpectedFailure(T rv)
 {
     NS_ERROR("This is not supposed to fail!");
     return rv;
 }
 
 /* void initClasses (in JSContextPtr aJSContext, in JSObjectPtr aGlobalJSObj); */
 NS_IMETHODIMP
 nsXPConnect::InitClasses(JSContext * aJSContext, JSObject * aGlobalJSObj)
@@ -393,16 +394,50 @@ CreateGlobalObject(JSContext *cx, const 
                                     strcmp(className, "ChromeWindow") == 0)
                                    ? ProtoAndIfaceCache::WindowLike
                                    : ProtoAndIfaceCache::NonWindowLike);
     }
 
     return global;
 }
 
+bool
+InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal, uint32_t aFlags)
+{
+    // Immediately enter the global's compartment, so that everything else we
+    // create ends up there.
+    JSAutoCompartment ac(aJSContext, aGlobal);
+    if (!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) {
+        // XPCCallContext gives us an active request needed to save/restore.
+        if (!GetCompartmentPrivate(aGlobal)->scope->AttachComponentsObject(aJSContext) ||
+            !XPCNativeWrapper::AttachNewConstructorObject(aJSContext, aGlobal)) {
+            return UnexpectedFailure(false);
+        }
+    }
+
+    // Stuff coming through this path always ends up as a DOM global.
+    MOZ_ASSERT(js::GetObjectClass(aGlobal)->flags & JSCLASS_DOM_GLOBAL);
+
+    // Init WebIDL binding constructors wanted on all XPConnect globals.
+    //
+    // XXX Please do not add any additional classes here without the approval of
+    //     the XPConnect module owner.
+    if (!PromiseBinding::GetConstructorObject(aJSContext, aGlobal) ||
+        !TextDecoderBinding::GetConstructorObject(aJSContext, aGlobal) ||
+        !TextEncoderBinding::GetConstructorObject(aJSContext, aGlobal) ||
+        !DOMErrorBinding::GetConstructorObject(aJSContext, aGlobal)) {
+        return UnexpectedFailure(false);
+    }
+
+    if (!(aFlags & nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK))
+        JS_FireOnNewGlobalObject(aJSContext, aGlobal);
+
+    return true;
+}
+
 } // namespace xpc
 
 NS_IMETHODIMP
 nsXPConnect::InitClassesWithNewWrappedGlobal(JSContext * aJSContext,
                                              nsISupports *aCOMObj,
                                              nsIPrincipal * aPrincipal,
                                              uint32_t aFlags,
                                              JS::CompartmentOptions& aOptions,
@@ -419,49 +454,25 @@ nsXPConnect::InitClassesWithNewWrappedGl
     // Call into XPCWrappedNative to make a new global object, scope, and global
     // prototype.
     xpcObjectHelper helper(aCOMObj);
     MOZ_ASSERT(helper.GetScriptableFlags() & nsIXPCScriptable::IS_GLOBAL_OBJECT);
     nsRefPtr<XPCWrappedNative> wrappedGlobal;
     nsresult rv =
         XPCWrappedNative::WrapNewGlobal(helper, aPrincipal,
                                         aFlags & nsIXPConnect::INIT_JS_STANDARD_CLASSES,
-                                        !(aFlags & nsIXPConnect::DONT_FIRE_ONNEWGLOBALHOOK),
                                         aOptions, getter_AddRefs(wrappedGlobal));
     NS_ENSURE_SUCCESS(rv, rv);
 
     // Grab a copy of the global and enter its compartment.
     RootedObject global(aJSContext, wrappedGlobal->GetFlatJSObject());
     MOZ_ASSERT(!js::GetObjectParent(global));
-    JSAutoCompartment ac(aJSContext, global);
 
-    if (!(aFlags & nsIXPConnect::OMIT_COMPONENTS_OBJECT)) {
-        // XPCCallContext gives us an active request needed to save/restore.
-        if (!wrappedGlobal->GetScope()->AttachComponentsObject(aJSContext))
-            return UnexpectedFailure(NS_ERROR_FAILURE);
-
-        if (!XPCNativeWrapper::AttachNewConstructorObject(aJSContext, global))
-            return UnexpectedFailure(NS_ERROR_FAILURE);
-    }
-
-    // Stuff coming through this path always ends up as a DOM global.
-    // XXX Someone who knows why we can assert this should re-check
-    //     (after bug 720580).
-    MOZ_ASSERT(js::GetObjectClass(global)->flags & JSCLASS_DOM_GLOBAL);
-
-    // Init WebIDL binding constructors wanted on all XPConnect globals.
-    //
-    // XXX Please do not add any additional classes here without the approval of
-    //     the XPConnect module owner.
-    if (!PromiseBinding::GetConstructorObject(aJSContext, global) ||
-        !TextDecoderBinding::GetConstructorObject(aJSContext, global) ||
-        !TextEncoderBinding::GetConstructorObject(aJSContext, global) ||
-        !DOMErrorBinding::GetConstructorObject(aJSContext, global)) {
+    if (!InitGlobalObject(aJSContext, global, aFlags))
         return UnexpectedFailure(NS_ERROR_FAILURE);
-    }
 
     wrappedGlobal.forget(_retval);
     return NS_OK;
 }
 
 static nsresult
 NativeInterface2JSObject(HandleObject aScope,
                          nsISupports *aCOMObj,
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -2042,17 +2042,16 @@ public:
 
     XPCJSRuntime*
     GetRuntime() const {XPCWrappedNativeScope* scope = GetScope();
                         return scope ? scope->GetRuntime() : nullptr;}
 
     static nsresult
     WrapNewGlobal(xpcObjectHelper &nativeHelper,
                   nsIPrincipal *principal, bool initStandardClasses,
-                  bool fireOnNewGlobalHook,
                   JS::CompartmentOptions& aOptions,
                   XPCWrappedNative **wrappedGlobal);
 
     static nsresult
     GetNewOrUsed(xpcObjectHelper& helper,
                  XPCWrappedNativeScope* Scope,
                  XPCNativeInterface* Interface,
                  XPCWrappedNative** wrapper);
@@ -3393,16 +3392,20 @@ public:
 
     JS::RootedId defineAs;
 };
 
 JSObject *
 CreateGlobalObject(JSContext *cx, const JSClass *clasp, nsIPrincipal *principal,
                    JS::CompartmentOptions& aOptions);
 
+bool
+InitGlobalObject(JSContext* aJSContext, JS::Handle<JSObject*> aGlobal,
+                 uint32_t aFlags);
+
 // Helper for creating a sandbox object to use for evaluating
 // untrusted code completely separated from all other code in the
 // system using EvalInSandbox(). Takes the JSContext on which to
 // do setup etc on, puts the sandbox object in *vp (which must be
 // rooted by the caller), and uses the principal that's either
 // directly passed in prinOrSop or indirectly as an
 // nsIScriptObjectPrincipal holding the principal. If no principal is
 // reachable through prinOrSop, a new null principal will be created
--- a/js/xpconnect/src/xpcpublic.h
+++ b/js/xpconnect/src/xpcpublic.h
@@ -19,16 +19,17 @@
 #include "nsWrapperCache.h"
 #include "nsStringGlue.h"
 #include "nsTArray.h"
 #include "mozilla/dom/JSSlots.h"
 #include "nsMathUtils.h"
 #include "nsStringBuffer.h"
 #include "mozilla/dom/BindingDeclarations.h"
 
+class nsGlobalWindow;
 class nsIPrincipal;
 class nsScriptNameSpaceManager;
 class nsIGlobalObject;
 class nsIMemoryReporterCallback;
 
 #ifndef BAD_TLS_INDEX
 #define BAD_TLS_INDEX ((uint32_t) -1)
 #endif
--- a/js/xpconnect/tests/chrome/test_doublewrappedcompartments.xul
+++ b/js/xpconnect/tests/chrome/test_doublewrappedcompartments.xul
@@ -17,30 +17,26 @@ https://bugzilla.mozilla.org/show_bug.cg
           src="http://example.org/tests/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html"
           onload="go()"
           id="ifr">
   </iframe>
   </body>
 
   <!-- test code goes here -->
   <script type="application/javascript"><![CDATA[
-      const Ci = Components.interfaces;
-      const utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                          .getInterface(Ci.nsIDOMWindowUtils);
+      const utils = SpecialPowers.DOMWindowUtils;
 
       function go() {
         var wrappedWin = $('ifr').contentWindow;
         is(typeof wrappedWin.expando, 'undefined', "Shouldn't be able to see the expando");
 
         var unwrapped = wrappedWin.wrappedJSObject;
 
         var expando = unwrapped.expando;
-        is(utils.getClassName(expando), 'Proxy', 'properly wrapped');
+        isnot(expando, 'undefined', 'properly wrapped');
         is(typeof expando.querySelector, 'function', 'double wrapped');
 
-        ok(unwrapped.testme(expando),
-           "content didn't get a proxy, but another double wrapped object");
         SimpleTest.finish();
       }
 
       SimpleTest.waitForExplicitFinish();
   ]]></script>
 </window>
--- a/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html
+++ b/js/xpconnect/tests/mochitest/file_doublewrappedcompartments.html
@@ -1,20 +1,10 @@
 <html>
     <head>
         <script>
             // We want to put an expando on the object, but we want this object
             // to be wrapped in other compartments. This means that the expando
             // must implement precreate, which happens (in general) for nodes.
             // So we just do a cyclic reference to the document body.
             window.expando = document.documentElement;
-
-            function testme(obj) {
-                netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
-                const Ci = Components.interfaces;
-                const utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                                    .getInterface(Ci.nsIDOMWindowUtils);
-
-                return utils.getClassName(obj) != "Proxy" &&
-                       typeof obj.querySelector == 'function';
-            }
         </script>
     </head>
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1449,26 +1449,28 @@ DOMXrayTraits::resolveOwnProperty(JSCont
     return true;
 }
 
 bool
 DOMXrayTraits::defineProperty(JSContext *cx, HandleObject wrapper, HandleId id,
                               MutableHandle<JSPropertyDescriptor> desc,
                               Handle<JSPropertyDescriptor> existingDesc, bool *defined)
 {
-    if (!existingDesc.object())
-        return true;
-
     // Check for an indexed property on a Window.  If that's happening, do
     // nothing but claim we defined it so it won't get added as an expando.
-    int32_t index = GetArrayIndexFromId(cx, id);
-    if (IsArrayIndex(index) && IsWindow(cx, wrapper)) {
-        *defined = true;
+    if (IsWindow(cx, wrapper)) {
+        int32_t index = GetArrayIndexFromId(cx, id);
+        if (IsArrayIndex(index)) {
+            *defined = true;
+            return true;
+        }
+    }
+
+    if (!existingDesc.object())
         return true;
-    }
 
     JS::Rooted<JSObject*> obj(cx, getTargetObject(wrapper));
     return XrayDefineProperty(cx, wrapper, obj, id, desc, defined);
 }
 
 bool
 DOMXrayTraits::enumerateNames(JSContext *cx, HandleObject wrapper, unsigned flags,
                               AutoIdVector &props)
--- a/layout/mathml/nsIMathMLFrame.h
+++ b/layout/mathml/nsIMathMLFrame.h
@@ -195,16 +195,23 @@ public:
                                     uint32_t        aWhichFlags) = 0;
 
   // If aFrame is a child frame, returns the script increment which this frame
   // imposes on the specified frame, ignoring any artificial adjustments to
   // scriptlevel.
   // Returns 0 if the specified frame isn't a child frame.
   virtual uint8_t
   ScriptIncrement(nsIFrame* aFrame) = 0;
+
+  // Returns true if the frame is considered to be an mrow for layout purposes.
+  // This includes inferred mrows, but excludes <mrow> elements with a single
+  // child.  In the latter case, the child is to be treated as if it wasn't
+  // within an mrow, so we pretend the mrow isn't mrow-like.
+  virtual bool
+  IsMrowLike() = 0;
 };
 
 // struct used by a container frame to keep track of its embellishments.
 // By convention, the data that we keep here is bubbled from the embellished
 // hierarchy, and it remains unchanged unless we have to recover from a change
 // that occurs in the embellished hierarchy. The struct remains in its nil
 // state in those frames that are not part of the embellished hierarchy.
 struct nsEmbellishData {
--- a/layout/mathml/nsMathMLContainerFrame.cpp
+++ b/layout/mathml/nsMathMLContainerFrame.cpp
@@ -1595,15 +1595,23 @@ NS_NewMathMLmathBlockFrame(nsIPresShell*
 {
   nsMathMLmathBlockFrame* it = new (aPresShell) nsMathMLmathBlockFrame(aContext);
   it->SetFlags(aFlags);
   return it;
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathBlockFrame)
 
+NS_QUERYFRAME_HEAD(nsMathMLmathBlockFrame)
+  NS_QUERYFRAME_ENTRY(nsMathMLmathBlockFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsBlockFrame)
+
 nsIFrame*
 NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsMathMLmathInlineFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsMathMLmathInlineFrame)
+
+NS_QUERYFRAME_HEAD(nsMathMLmathInlineFrame)
+  NS_QUERYFRAME_ENTRY(nsIMathMLFrame)
+NS_QUERYFRAME_TAIL_INHERITING(nsInlineFrame)
--- a/layout/mathml/nsMathMLContainerFrame.h
+++ b/layout/mathml/nsMathMLContainerFrame.h
@@ -403,16 +403,18 @@ private:
 // simply mapping to nsBlockFrame or nsInlineFrame.
 // A separate implemention needs to provide:
 // 1) line-breaking
 // 2) proper inter-frame spacing
 // 3) firing of Stretch() (in which case FinalizeReflow() would have to be cleaned)
 // Issues: If/when mathml becomes a pluggable component, the separation will be needed.
 class nsMathMLmathBlockFrame : public nsBlockFrame {
 public:
+  NS_DECL_QUERYFRAME_TARGET(nsMathMLmathBlockFrame)
+  NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   friend nsIFrame* NS_NewMathMLmathBlockFrame(nsIPresShell* aPresShell,
           nsStyleContext* aContext, nsFrameState aFlags);
 
   // beware, mFrames is not set by nsBlockFrame
   // cannot use mFrames{.FirstChild()|.etc} since the block code doesn't set mFrames
   virtual nsresult
@@ -463,29 +465,38 @@ public:
     return rv;
   }
 
   virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE {
     return nsBlockFrame::IsFrameOfType(aFlags &
               ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace));
   }
 
+  // See nsIMathMLFrame.h
+  bool IsMrowLike() {
+    return mFrames.FirstChild() != mFrames.LastChild() ||
+           !mFrames.FirstChild();
+  }
+
 protected:
   nsMathMLmathBlockFrame(nsStyleContext* aContext) : nsBlockFrame(aContext) {
     // We should always have a float manager.  Not that things can really try
     // to float out of us anyway, but we need one for line layout.
     AddStateBits(NS_BLOCK_FLOAT_MGR);
   }
   virtual ~nsMathMLmathBlockFrame() {}
 };
 
 // --------------
 
-class nsMathMLmathInlineFrame : public nsInlineFrame {
+class nsMathMLmathInlineFrame : public nsInlineFrame,
+                                public nsMathMLFrame {
 public:
+  NS_DECL_QUERYFRAME_TARGET(nsMathMLmathInlineFrame)
+  NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
   friend nsIFrame* NS_NewMathMLmathInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext);
 
   virtual nsresult
   SetInitialChildList(ChildListID     aListID,
                       nsFrameList&    aChildList) MOZ_OVERRIDE
   {
@@ -533,14 +544,20 @@ public:
     return rv;
   }
 
   virtual bool IsFrameOfType(uint32_t aFlags) const MOZ_OVERRIDE {
       return nsInlineFrame::IsFrameOfType(aFlags &
                 ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace));
   }
 
+  bool
+  IsMrowLike() MOZ_OVERRIDE {
+    return mFrames.FirstChild() != mFrames.LastChild() ||
+           !mFrames.FirstChild();
+  }
+
 protected:
   nsMathMLmathInlineFrame(nsStyleContext* aContext) : nsInlineFrame(aContext) {}
   virtual ~nsMathMLmathInlineFrame() {}
 };
 
 #endif /* nsMathMLContainerFrame_h___ */
--- a/layout/mathml/nsMathMLFrame.h
+++ b/layout/mathml/nsMathMLFrame.h
@@ -93,16 +93,22 @@ public:
   }
 
   uint8_t
   ScriptIncrement(nsIFrame* aFrame) MOZ_OVERRIDE
   {
     return 0;
   }
 
+  bool
+  IsMrowLike() MOZ_OVERRIDE
+  {
+    return false;
+  }
+
   // helper to give a style context suitable for doing the stretching to the
   // MathMLChar. Frame classes that use this should make the extra style contexts
   // accessible to the Style System via Get/Set AdditionalStyleContext.
   static void
   ResolveMathMLCharStyle(nsPresContext*  aPresContext,
                          nsIContent*      aContent,
                          nsStyleContext*  aParenStyleContext,
                          nsMathMLChar*    aMathMLChar,
--- a/layout/mathml/nsMathMLmencloseFrame.h
+++ b/layout/mathml/nsMathMLmencloseFrame.h
@@ -76,16 +76,22 @@ public:
   InheritAutomaticData(nsIFrame* aParent) MOZ_OVERRIDE;
 
   NS_IMETHOD
   TransmitAutomaticData() MOZ_OVERRIDE;
 
   virtual nscoord
   FixInterFrameSpacing(nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
 
+  bool
+  IsMrowLike() MOZ_OVERRIDE {
+    return mFrames.FirstChild() != mFrames.LastChild() ||
+           !mFrames.FirstChild();
+  }
+
 protected:
   nsMathMLmencloseFrame(nsStyleContext* aContext);
   virtual ~nsMathMLmencloseFrame();
 
   nsresult PlaceInternal(nsRenderingContext& aRenderingContext,
                          bool                 aPlaceOrigin,
                          nsHTMLReflowMetrics& aDesiredSize,
                          bool                 aWidthOnly);
--- a/layout/mathml/nsMathMLmfencedFrame.h
+++ b/layout/mathml/nsMathMLmfencedFrame.h
@@ -75,16 +75,27 @@ public:
              bool                 aRTL);
 
   static void
   PlaceChar(nsMathMLChar*      aMathMLChar,
             nscoord            aDesiredSize,
             nsBoundingMetrics& bm,
             nscoord&           dx);
 
+  virtual bool
+  IsMrowLike() MOZ_OVERRIDE
+  {
+    // Always treated as an mrow with > 1 child as
+    // <mfenced> <mo>%</mo> </mfenced>
+    // renders equivalently to
+    // <mrow> <mo> ( </mo> <mo>%</mo> <mo> ) </mo> </mrow>
+    // This also holds with multiple children.  (MathML3 3.3.8.1)
+    return true;
+  }
+
 protected:
   nsMathMLmfencedFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {}
   virtual ~nsMathMLmfencedFrame();
   
   nsMathMLChar* mOpenChar;
   nsMathMLChar* mCloseChar;
   nsMathMLChar* mSeparatorsChar;
   int32_t       mSeparatorsCount;
--- a/layout/mathml/nsMathMLmoFrame.cpp
+++ b/layout/mathml/nsMathMLmoFrame.cpp
@@ -305,21 +305,33 @@ nsMathMLmoFrame::ProcessOperatorData()
       mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ANCESTOR;
 
     // find the position of our outermost embellished container w.r.t
     // its siblings.
 
     nsIFrame* nextSibling = embellishAncestor->GetNextSibling();
     nsIFrame* prevSibling = embellishAncestor->GetPrevSibling();
 
-    // flag to distinguish from a real infix
-    if (!prevSibling && !nextSibling)
+    // flag to distinguish from a real infix.  Set for (embellished) operators
+    // that live in (inferred) mrows.
+    nsIMathMLFrame* mathAncestor = do_QueryFrame(parentAncestor);
+    bool zeroSpacing = false;
+    if (mathAncestor) {
+      zeroSpacing =  !mathAncestor->IsMrowLike();
+    } else {
+      nsMathMLmathBlockFrame* blockFrame = do_QueryFrame(parentAncestor);
+      if (blockFrame) {
+        zeroSpacing = !blockFrame->IsMrowLike();
+      }
+    }
+    if (zeroSpacing) {
       mFlags |= NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
-    else
+    } else {
       mFlags &= ~NS_MATHML_OPERATOR_EMBELLISH_ISOLATED;
+    }
 
     // find our form
     form = NS_MATHML_OPERATOR_FORM_INFIX;
     mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::form, value);
     if (!value.IsEmpty()) {
       if (value.EqualsLiteral("prefix"))
         form = NS_MATHML_OPERATOR_FORM_PREFIX;
       else if (value.EqualsLiteral("postfix"))
@@ -339,40 +351,37 @@ nsMathMLmoFrame::ProcessOperatorData()
     // http://www.w3.org/TR/MathML/chapter3.html#presm.mo.attrs
     // thickmathspace = 5/18em
     float lspace = 5.0f/18.0f;
     float rspace = 5.0f/18.0f;
     // lookup the operator dictionary
     nsAutoString data;
     mMathMLChar.GetData(data);
     nsMathMLOperators::LookupOperator(data, form, &mFlags, &lspace, &rspace);
-    if (lspace || rspace) {
+    // Spacing is zero if our outermost embellished operator is not in an
+    // inferred mrow.
+    if (!NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags) &&
+        (lspace || rspace)) {
       // Cache the default values of lspace and rspace.
       // since these values are relative to the 'em' unit, convert to twips now
       nscoord em;
       nsRefPtr<nsFontMetrics> fm;
       nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fm));
       GetEmHeight(fm, em);
 
       mEmbellishData.leadingSpace = NSToCoordRound(lspace * em);
       mEmbellishData.trailingSpace = NSToCoordRound(rspace * em);
 
       // tuning if we don't want too much extra space when we are a script.
       // (with its fonts, TeX sets lspace=0 & rspace=0 as soon as scriptlevel>0.
       // Our fonts can be anything, so...)
-      if (StyleFont()->mScriptLevel > 0) {
-        if (NS_MATHML_OPERATOR_EMBELLISH_IS_ISOLATED(mFlags)) {
-          // could be an isolated accent or script, e.g., x^{+}, just zero out
-          mEmbellishData.leadingSpace = 0;
-          mEmbellishData.trailingSpace  = 0;
-        }
-        else if (!NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
-          mEmbellishData.leadingSpace /= 2;
-          mEmbellishData.trailingSpace  /= 2;
-        }
+      if (StyleFont()->mScriptLevel > 0 &&
+          !NS_MATHML_OPERATOR_HAS_EMBELLISH_ANCESTOR(mFlags)) {
+        mEmbellishData.leadingSpace /= 2;
+        mEmbellishData.trailingSpace /= 2;
       }
     }
   }
 
   // If we are an accent without explicit lspace="." or rspace=".",
   // we will ignore our default leading/trailing space
 
   // lspace
--- a/layout/mathml/nsMathMLmpaddedFrame.h
+++ b/layout/mathml/nsMathMLmpaddedFrame.h
@@ -33,16 +33,22 @@ public:
          const nsHTMLReflowState& aReflowState,
          nsReflowStatus&          aStatus) MOZ_OVERRIDE;
   
   virtual nsresult
   Place(nsRenderingContext& aRenderingContext,
         bool                 aPlaceOrigin,
         nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
 
+  bool
+  IsMrowLike() MOZ_OVERRIDE {
+    return mFrames.FirstChild() != mFrames.LastChild() ||
+           !mFrames.FirstChild();
+  }
+
 protected:
   nsMathMLmpaddedFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {}
   virtual ~nsMathMLmpaddedFrame();
   
   virtual nsresult
   MeasureForWidth(nsRenderingContext& aRenderingContext,
                   nsHTMLReflowMetrics& aDesiredSize) MOZ_OVERRIDE;
 
--- a/layout/mathml/nsMathMLmphantomFrame.h
+++ b/layout/mathml/nsMathMLmphantomFrame.h
@@ -26,15 +26,21 @@ public:
   TransmitAutomaticData() MOZ_OVERRIDE {
     return TransmitAutomaticDataForMrowLikeElement();
   }
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) MOZ_OVERRIDE {}
 
+  bool
+  IsMrowLike() MOZ_OVERRIDE {
+    return mFrames.FirstChild() != mFrames.LastChild() ||
+           !mFrames.FirstChild();
+  }
+
 protected:
   nsMathMLmphantomFrame(nsStyleContext* aContext)
     : nsMathMLContainerFrame(aContext) {}
   virtual ~nsMathMLmphantomFrame();
 };
 
 #endif /* nsMathMLmphantomFrame_h___ */
--- a/layout/mathml/nsMathMLmrowFrame.h
+++ b/layout/mathml/nsMathMLmrowFrame.h
@@ -27,14 +27,23 @@ public:
   NS_IMETHOD
   InheritAutomaticData(nsIFrame* aParent) MOZ_OVERRIDE;
 
   NS_IMETHOD
   TransmitAutomaticData() MOZ_OVERRIDE {
     return TransmitAutomaticDataForMrowLikeElement();
   }
 
+  bool
+  IsMrowLike() MOZ_OVERRIDE {
+    // <mrow> elements with a single child are treated identically to the case
+    // where the child wasn't within an mrow, so we pretend the mrow isn't an
+    // mrow in that situation.
+    return mFrames.FirstChild() != mFrames.LastChild() ||
+           !mFrames.FirstChild();
+  }
+
 protected:
   nsMathMLmrowFrame(nsStyleContext* aContext) : nsMathMLContainerFrame(aContext) {}
   virtual ~nsMathMLmrowFrame();
 };
 
 #endif /* nsMathMLmrowFrame_h___ */
--- a/layout/mathml/nsMathMLmsqrtFrame.h
+++ b/layout/mathml/nsMathMLmsqrtFrame.h
@@ -47,15 +47,22 @@ public:
   NS_IMETHOD
   InheritAutomaticData(nsIFrame* aParent) MOZ_OVERRIDE;
 
   virtual nsresult
   AttributeChanged(int32_t         aNameSpaceID,
                    nsIAtom*        aAttribute,
                    int32_t         aModType) MOZ_OVERRIDE;
 
+  virtual bool
+  IsMrowLike() MOZ_OVERRIDE
+  {
+    return mFrames.FirstChild() != mFrames.LastChild() ||
+           !mFrames.FirstChild();
+  }
+
 protected:
   nsMathMLmsqrtFrame(nsStyleContext* aContext);
   virtual ~nsMathMLmsqrtFrame();
 };
 
 #endif /* nsMathMLmsqrtFrame_h___ */
 
--- a/layout/mathml/nsMathMLmtableFrame.h
+++ b/layout/mathml/nsMathMLmtableFrame.h
@@ -246,16 +246,22 @@ public:
   {
     return nsBlockFrame::IsFrameOfType(aFlags &
       ~(nsIFrame::eMathML | nsIFrame::eExcludesIgnorableWhitespace));
   }
 
   virtual const nsStyleText* StyleTextForLineLayout() MOZ_OVERRIDE;
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) MOZ_OVERRIDE;
 
+  bool
+  IsMrowLike() MOZ_OVERRIDE {
+    return mFrames.FirstChild() != mFrames.LastChild() ||
+           !mFrames.FirstChild();
+  }
+
 protected:
   nsMathMLmtdInnerFrame(nsStyleContext* aContext);
   virtual ~nsMathMLmtdInnerFrame();
 
   nsStyleText* mUniqueStyleText;
 
 };  // class nsMathMLmtdInnerFrame
 
--- a/layout/reftests/font-face/local-1-ref.html
+++ b/layout/reftests/font-face/local-1-ref.html
@@ -3,17 +3,17 @@
 <head>
 	<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
 	<meta http-equiv="Content-Style-Type" content="text/css">
 	<title>test src: local() reference</title>
 	<style type="text/css">
 	  body {
 	    font-family: Nimbus Sans L, Helvetica, Bitstream Vera Sans,
                          Arial, Liberation Sans, SwissA,
-                         Droid Sans, Roboto, serif;
+                         Fira Sans OT, Fira Sans, Droid Sans, Roboto, serif;
 	  }
 	</style>
 </head>
 <body>
   <p style="font-weight: normal;">
     The quick brown fox jumped over the lazy dog
   </p>
   <p style="font-weight: bold;">
--- a/layout/reftests/font-face/local-1.html
+++ b/layout/reftests/font-face/local-1.html
@@ -30,25 +30,27 @@
 
 	  -->
 	<style type="text/css">
 	  @font-face {
 	     font-family: "Local";
 	     src: local(Nimbus Sans L), local(NimbusSansL-Regu),
 	          local(Helvetica), local(Bitstream Vera Sans),
 	          local(Arial), local(Liberation Sans), local(SwissA),
+	          local(Fira Sans OT), local(Fira Sans),
 	          local(Droid Sans), local(Roboto);
 	     font-weight: 100;
 	  }
 	  @font-face {
 	     font-family: "Local";
 	     src: local(Nimbus Sans L Bold), local(NimbusSansL-Bold),
 	          local(Helvetica Bold), local(Helvetica-Bold),
                   local(Bitstream Vera Sans  Bold),
 	          local(Arial Bold), local(Liberation Sans Bold), local(SwissA Bold),
+	          local(Fira Sans OT Bold), local(Fira Sans Bold),
 	          local(Droid Sans Bold), local(Roboto Bold);
 	     font-weight: normal;
 	  }
 	  body { font-family: Local, serif }
 	</style>
 </head>
 <body>
   <p style="font-weight: 100">
--- a/layout/reftests/font-face/local-styled-1-ref.html
+++ b/layout/reftests/font-face/local-styled-1-ref.html
@@ -2,16 +2,17 @@
 <html>
 <head>
 <style type="text/css">
 @font-face {
   font-family: test;
   src: local(Nimbus Sans L), local(NimbusSansL-Regu),
        local(Helvetica), local(Bitstream Vera Sans),
        local(Arial), local(Liberation Sans), local(SwissA),
+       local(Fira Sans OT), local(Fira Sans),
        local(Droid Sans), local(Roboto);
 }
 div {
   font-family: test, serif;
   margin: 10px;
 }
 </style>
 </head>
--- a/layout/reftests/font-face/local-styled-1.html
+++ b/layout/reftests/font-face/local-styled-1.html
@@ -2,32 +2,35 @@
 <html>
 <head>
 <style type="text/css">
 @font-face {
   font-family: test;
   src: local(Nimbus Sans L), local(NimbusSansL-Regu),
        local(Helvetica), local(Bitstream Vera Sans),
        local(Arial), local(Liberation Sans), local(SwissA),
+       local(Fira Sans OT), local(Fira Sans),
        local(Droid Sans), local(Roboto);
 }
 @font-face {
   font-family: test;
   font-style: italic;
   src: local(Nimbus Sans L), local(NimbusSansL-Regu),
        local(Helvetica), local(Bitstream Vera Sans),
        local(Arial), local(Liberation Sans), local(SwissA),
+       local(Fira Sans OT), local(Fira Sans),
        local(Droid Sans), local(Roboto);
 }
 @font-face {
   font-family: test;
   font-weight: bold;
   src: local(Nimbus Sans L), local(NimbusSansL-Regu),
        local(Helvetica), local(Bitstream Vera Sans),
        local(Arial), local(Liberation Sans), local(SwissA),
+       local(Fira Sans OT), local(Fira Sans),
        local(Droid Sans), local(Roboto);
 }
 div {
   font-family: test, serif;
   margin: 10px;
 }
 </style>
 </head>
--- a/layout/reftests/font-face/reftest.list
+++ b/layout/reftests/font-face/reftest.list
@@ -87,19 +87,18 @@ HTTP(..) != media-query-add-1-ref.html m
 
 HTTP(..) == ahem-metrics-1.html ahem-metrics-1-ref.html
 HTTP(..) == ex-unit-1.html ex-unit-1-ref.html
 HTTP(..) == ex-unit-1-dynamic.html ex-unit-1-ref.html
 
 # bug 493976 - for some reason the Arabic tests below cause Tinderbox timeouts
 # HTTP(..) == src-format-arabic.html src-format-arabic-ot-ref.html
 
-# bug 769194 - src:local() completely broken on android
-skip-if(B2G) fails-if(Android) == local-1.html local-1-ref.html # bug 773482
-skip-if(B2G) fails-if(Android) == local-styled-1.html local-styled-1-ref.html # bug 773482
+== local-1.html local-1-ref.html
+== local-styled-1.html local-styled-1-ref.html
 
 skip-if(B2G) HTTP(..) == synthetic-weight-style.html synthetic-weight-style-ref.html # bug 773482
 skip-if(B2G) HTTP(..) == synthetic-variations.html synthetic-variations-ref.html
 
 # Leak test
 HTTP(..) load 486974-1.html
 
 # compare fonts with and without bad head checksum
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mo-lspace-rspace-2-ref.html
@@ -0,0 +1,379 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>mo-lspace-rspace</title>
+    <style type="text/css">
+      mo,td {
+        background-color: red;
+      }
+      msub, mfrac, msup, msubsup, mmultiscripts,
+      mover, munder, munderover, mpadded, merror
+      {
+        background-color: blue
+      }
+      math[display]
+      {
+        background-color: blue
+      }
+    </style>
+  </head>
+  <body>
+
+    <p>
+      No lspace or rspace added:
+    </p>
+    <p>
+      <math>
+        <mrow>
+          <mo lspace="0" rspace="0">%</mo>
+        </mrow>
+      </math>
+      <math>
+        <mfrac>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </mfrac>
+      </math>
+      <math>
+        <msqrt>
+          <mo lspace="0" rspace="0">%</mo>
+        </msqrt>
+      </math>
+      <math>
+        <mroot>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </mroot>
+      </math>
+      <math>
+        <mstyle>
+          <mo lspace="0" rspace="0">%</mo>
+        </mstyle>
+      </math>
+    </p>
+
+    <p>
+    <table>
+        <tr>
+          <td>
+            <math>
+              <mphantom>
+                <mo lspace="0" rspace="0">%</mo>
+              </mphantom>
+            </math>
+          </td>
+        </tr>
+      </table>
+    </p>
+
+    <p>
+      <math>
+        <menclose notation="circle">
+          <mo lspace="0" rspace="0">%</mo>
+        </menclose>
+      </math>
+      <math>
+        <msub>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </msub>
+      </math>
+      <math>
+        <msup>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </msup>
+      </math>
+      <math>
+        <msubsup>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </msubsup>
+      </math>
+      <math>
+        <munder>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </munder>
+      </math>
+      <math>
+        <mover>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </mover>
+      </math>
+      <math>
+        <munderover>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </munderover>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <mmultiscripts>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+          <mprescripts/>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </mmultiscripts>
+      </math>
+      <math>
+        <mtable>
+          <mtr>
+            <mtd>
+              <mo lspace="0" rspace="0">%</mo>
+            </mtd>
+          </mtr>
+        </mtable>
+      </math>
+      <math>
+        <mo lspace="0" rspace="0">%</mo>
+      </math>
+      <math>
+        <msub>
+          <mrow>
+            <mo lspace="0" rspace="0">%</mo>
+          </mrow>
+          <mo lspace="0" rspace="0">%</mo>
+        </msub>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <msub>
+          <msub>
+            <mo lspace="0" rspace="0">%</mo>
+            <mo lspace="0" rspace="0">%</mo>
+          </msub>
+          <mo lspace="0" rspace="0">%</mo>
+        </msub>
+      </math>
+      <math>
+        <munder>
+          <munder>
+            <mo lspace="0" rspace="0">%</mo>
+            <mo lspace="0" rspace="0">%</mo>
+          </munder>
+          <mo lspace="0" rspace="0">%</mo>
+        </munder>
+      </math>
+      <math>
+        <mfrac>
+          <mfrac>
+            <mo lspace="0" rspace="0">%</mo>
+            <mo lspace="0" rspace="0">%</mo>
+          </mfrac>
+          <mo lspace="0" rspace="0">%</mo>
+        </mfrac>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <menclose notation="circle">
+          <mo lspace="0" rspace="0">%</mo>
+        </menclose>
+        <menclose notation="circle">
+          <mo lspace="0" rspace="0">%</mo>
+        </menclose>
+        <mroot>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </mroot>
+    </math>
+      <math>
+        <mpadded height="+1em">
+          <mo lspace="0" rspace="0">%</mo>
+        </mpadded>
+      </math>
+      <math>
+        <merror>
+          <mo lspace="0" rspace="0">%</mo>
+        </merror>
+      </math>
+    </p>
+
+    <math display="block">
+      <mo lspace="0" rspace="0">%</mo>
+    </math>
+
+    <p>
+      lspace and rspace rendered as appropriate
+    </p>
+    <p>
+      <math>
+        <mrow>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        </mrow>
+        <mfrac>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </mfrac>
+        <msqrt>
+          <mo lspace="0" rspace="0">%</mo>
+        </msqrt>
+        <mstyle>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        </mstyle>
+      </math>
+    </p>
+
+    <p>
+    <table>
+        <tr>
+          <td>
+            <math>
+              <mphantom>
+                <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+                <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+              </mphantom>
+            </math>
+          </td>
+        </tr>
+      </table>
+    </p>
+
+    <p>
+      <math>
+        <mfenced>
+          <!-- always treated as in an mrow with > 1 child -->
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        </mfenced>
+      </math>
+      <math>
+        <mfenced>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        </mfenced>
+        <menclose notation="circle">
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        </menclose>
+        <msub>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </msub>
+        <msup>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </msup>
+        <msubsup>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </msubsup>
+        <munder>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </munder>
+        <mover>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </mover>
+        <munderover>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </munderover>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <mmultiscripts>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+          <mprescripts/>
+          <mo lspace="0" rspace="0">%</mo>
+          <mo lspace="0" rspace="0">%</mo>
+        </mmultiscripts>
+        <mtable>
+          <mtr>
+            <mtd>
+              <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+              <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+            </mtd>
+          </mtr>
+        </mtable>
+        <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        <msub>
+          <mrow>
+            <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          </mrow>
+          <mo lspace="0" rspace="0">%</mo>
+        </msub>
+        <msub>
+          <mrow>
+            <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+            <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          </mrow>
+          <mo lspace="0" rspace="0">%</mo>
+        </msub>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <msub>
+          <msub>
+            <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+            <mo lspace="0" rspace="0">%</mo>
+          </msub>
+          <mo lspace="0" rspace="0">%</mo>
+        </msub>
+        <munder>
+          <munder>
+            <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+            <mo lspace="0" rspace="0">%</mo>
+          </munder>
+          <mo lspace="0" rspace="0">%</mo>
+        </munder>
+        <mfrac>
+          <mfrac>
+            <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+            <mo lspace="0" rspace="0">%</mo>
+          </mfrac>
+          <mo lspace="0" rspace="0">%</mo>
+        </mfrac>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <mrow>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mpadded height="+1em">
+            <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          </mpadded>
+        </mrow>
+      </math>
+      <math>
+        <mpadded height="+1em">
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        </mpadded>
+      </math>
+      <math>
+        <merror>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+          <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        </merror>
+      </math>
+    </p>
+
+    <math display="block">
+      <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+      <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+    </math>
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mo-lspace-rspace-2.html
@@ -0,0 +1,377 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <title>mo-lspace-rspace</title>
+    <style type="text/css">
+      mo,td {
+        background-color: red;
+      }
+      msub, mfrac, msup, msubsup, mmultiscripts,
+      mover, munder, munderover, mpadded, merror
+      {
+        background-color: blue
+      }
+      math[display]
+      {
+        background-color: blue
+      }
+    </style>
+  </head>
+  <body>
+
+    <p>
+      No lspace or rspace added:
+    </p>
+    <p>
+      <math>
+        <mrow>
+          <mo>%</mo>
+        </mrow>
+      </math>
+      <math>
+        <mfrac>
+          <mo>%</mo>
+          <mo>%</mo>
+        </mfrac>
+      </math>
+      <math>
+        <msqrt>
+          <mo>%</mo>
+        </msqrt>
+      </math>
+      <math>
+        <mroot>
+          <mo>%</mo>
+          <mo>%</mo>
+        </mroot>
+      </math>
+      <math>
+        <mstyle>
+          <mo>%</mo>
+        </mstyle>
+      </math>
+    </p>
+
+    <p>
+    <table>
+        <tr>
+          <td>
+            <math>
+              <mphantom>
+                <mo>%</mo>
+              </mphantom>
+            </math>
+          </td>
+        </tr>
+      </table>
+    </p>
+
+    <p>
+      <math>
+        <menclose notation="circle">
+          <mo>%</mo>
+        </menclose>
+      </math>
+      <math>
+        <msub>
+          <mo>%</mo>
+          <mo>%</mo>
+        </msub>
+      </math>
+      <math>
+        <msup>
+          <mo>%</mo>
+          <mo>%</mo>
+        </msup>
+      </math>
+      <math>
+        <msubsup>
+          <mo>%</mo>
+          <mo>%</mo>
+          <mo>%</mo>
+        </msubsup>
+      </math>
+      <math>
+        <munder>
+          <mo>%</mo>
+          <mo>%</mo>
+        </munder>
+      </math>
+      <math>
+        <mover>
+          <mo>%</mo>
+          <mo>%</mo>
+        </mover>
+      </math>
+      <math>
+        <munderover>
+          <mo>%</mo>
+          <mo>%</mo>
+          <mo>%</mo>
+        </munderover>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <mmultiscripts>
+          <mo>%</mo>
+          <mo>%</mo>
+          <mo>%</mo>
+          <mprescripts/>
+          <mo>%</mo>
+          <mo>%</mo>
+        </mmultiscripts>
+      </math>
+      <math>
+        <mtable>
+          <mtr>
+            <mtd>
+              <mo>%</mo>
+            </mtd>
+          </mtr>
+        </mtable>
+      </math>
+      <math>
+        <mo>%</mo>
+      </math>
+      <math>
+        <msub>
+          <mrow>
+            <mo>%</mo>
+          </mrow>
+          <mo>%</mo>
+        </msub>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <msub>
+          <msub>
+            <mo>%</mo>
+            <mo>%</mo>
+          </msub>
+          <mo>%</mo>
+        </msub>
+      </math>
+      <math>
+        <munder>
+          <munder>
+            <mo>%</mo>
+            <mo>%</mo>
+          </munder>
+          <mo>%</mo>
+        </munder>
+      </math>
+      <math>
+        <mfrac>
+          <mfrac>
+            <mo>%</mo>
+            <mo>%</mo>
+          </mfrac>
+          <mo>%</mo>
+        </mfrac>
+      </math>
+    </p>
+    <p>
+      <math>
+        <menclose notation="circle">
+          <mo>%</mo>
+        </menclose>
+        <menclose notation="circle">
+          <mo>%</mo>
+        </menclose>
+        <mroot>
+          <mo>%</mo>
+          <mo>%</mo>
+        </mroot>
+      </math>
+      <math>
+        <mpadded height="+1em">
+          <mo>%</mo>
+        </mpadded>
+      </math>
+      <math>
+        <merror>
+          <mo>%</mo>
+        </merror>
+      </math>
+    </p>
+
+    <math display="block">
+      <mo>%</mo>
+    </math>
+
+    <p>
+      lspace and rspace rendered as appropriate
+    </p>
+    <p>
+      <math>
+        <mrow>
+          <mo>%</mo>
+        </mrow>
+        <mfrac>
+          <mo>%</mo>
+          <mo>%</mo>
+        </mfrac>
+        <msqrt>
+          <mo>%</mo>
+        </msqrt>
+        <mstyle>
+          <mo>%</mo>
+        </mstyle>
+      </math>
+    </p>
+
+    <p>
+    <table>
+        <tr>
+          <td>
+            <math>
+              <mphantom>
+                <mo>%</mo>
+                <mo>%</mo>
+              </mphantom>
+            </math>
+          </td>
+        </tr>
+      </table>
+    </p>
+
+    <p>
+      <math>
+        <mfenced>
+          <mo>%</mo>
+        </mfenced>
+      </math>
+      <math>
+        <mfenced>
+          <mo>%</mo>
+          <mo>%</mo>
+        </mfenced>
+        <menclose notation="circle">
+          <mo>%</mo>
+          <mo>%</mo>
+        </menclose>
+        <msub>
+          <mo>%</mo>
+          <mo>%</mo>
+        </msub>
+        <msup>
+          <mo>%</mo>
+          <mo>%</mo>
+        </msup>
+        <msubsup>
+          <mo>%</mo>
+          <mo>%</mo>
+          <mo>%</mo>
+        </msubsup>
+        <munder>
+          <mo>%</mo>
+          <mo>%</mo>
+        </munder>
+        <mover>
+          <mo>%</mo>
+          <mo>%</mo>
+        </mover>
+        <munderover>
+          <mo>%</mo>
+          <mo>%</mo>
+          <mo>%</mo>
+        </munderover>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <mmultiscripts>
+          <mo>%</mo>
+          <mo>%</mo>
+          <mo>%</mo>
+          <mprescripts/>
+          <mo>%</mo>
+          <mo>%</mo>
+        </mmultiscripts>
+        <mtable>
+          <mtr>
+            <mtd>
+              <mo>%</mo>
+              <mo>%</mo>
+            </mtd>
+          </mtr>
+        </mtable>
+        <mo>%</mo>
+        <msub>
+          <mrow>
+            <mo>%</mo>
+          </mrow>
+          <mo>%</mo>
+        </msub>
+        <msub>
+          <mrow>
+            <mo>%</mo>
+            <mo>%</mo>
+          </mrow>
+          <mo>%</mo>
+        </msub>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <msub>
+          <msub>
+            <mo>%</mo>
+            <mo>%</mo>
+          </msub>
+          <mo>%</mo>
+        </msub>
+        <munder>
+          <munder>
+            <mo>%</mo>
+            <mo>%</mo>
+          </munder>
+          <mo>%</mo>
+        </munder>
+        <mfrac>
+          <mfrac>
+            <mo>%</mo>
+            <mo>%</mo>
+          </mfrac>
+          <mo>%</mo>
+        </mfrac>
+      </math>
+    </p>
+
+    <p>
+      <math>
+        <mrow>
+          <mo>%</mo>
+          <mo>%</mo>
+          <mpadded height="+1em">
+            <mo>%</mo>
+          </mpadded>
+        </mrow>
+      </math>
+      <math>
+        <mpadded height="+1em">
+          <mo>%</mo>
+          <mo>%</mo>
+        </mpadded>
+      </math>
+      <math>
+        <merror>
+          <mo>%</mo>
+          <mo>%</mo>
+        </merror>
+      </math>
+    </p>
+
+    <math display="block">
+      <mo>%</mo>
+      <mo>%</mo>
+    </math>
+
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mo-lspace-rspace-3-ref.html
@@ -0,0 +1,37 @@
+<html>
+  <body>
+    <p>
+      <math>
+        <mo id="mo1" LSPACE="3em" rspace="0em">%</mo>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mo id="mo2" lspace="0em" rspace="0em">%</mo>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mo id="mo3" lspace="3em" rspace="thinmathspace">%</mo>
+        <mo  lspace="thinmathspace" rspace="thinmathspace">%</mo>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mo id="mo4" lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        <mo  lspace="thinmathspace" rspace="thinmathspace">%</mo>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+        <mo lspace="thinmathspace" rspace="thinmathspace">%</mo>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mo lspace="0em" rspace="0em">%</mo>
+      </math>
+    </p>
+  </body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/mathml/mo-lspace-rspace-3.html
@@ -0,0 +1,56 @@
+<html class="reftest-wait">
+  <body>
+    <p>
+      <math>
+        <mo id="mo1">%</mo>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mo id="mo2" lspace="3em">%</mo>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mo id="mo3">%</mo>
+        <mo>%</mo>
+      </math>
+    </p>
+    <p>
+      <math>
+        <mo id="mo4" lspace="3em">%</mo>
+        <mo >%</mo>
+      </math>
+    </p>
+    <p>
+      <math id="math1">
+        <mo>%</mo>
+      </math>
+    </p>
+    <p>
+      <math id="math2">
+        <mo>%</mo>
+        <mo id="mo5">%</mo>
+      </math>
+    </p>
+    <script type="text/javascript">
+      function doTest() {
+        // Add and remove lspace
+        document.getElementById("mo1").setAttribute("lspace", "3em");
+        document.getElementById("mo2").removeAttribute("lspace");
+        // and again but with an inferred mrow
+        document.getElementById("mo3").setAttribute("lspace", "3em");
+        document.getElementById("mo4").removeAttribute("lspace");
+
+        // Change to/from inferred mrow
+        var mo1 = document.createElementNS("http://www.w3.org/1998/Math/MathML", "mo");
+        mo1.innerHTML = "%";
+        document.getElementById("math1").appendChild(mo1);
+        document.getElementById("math2").removeChild(document.getElementById("mo5"));
+
+        document.documentElement.removeAttribute("class");
+      }
+      window.addEventListener("MozReftestInvalidate", doTest, false);
+  </script>
+  </body>
+</html>
--- a/layout/reftests/mathml/op-dict-1-ref.html
+++ b/layout/reftests/mathml/op-dict-1-ref.html
@@ -2,12 +2,14 @@
 <html>
 <head>
   <title>op-dict mo form</title>
 </head>
 <body>
   <math>
     <mrow>
       <mo form="prefix">+</mo>
+      <!-- need a second child to avoid zeroing dictionary spacing -->
+      <mn>1</mn>
     </mrow>
   </math>
 </body>
 </html>
--- a/layout/reftests/mathml/op-dict-1.html
+++ b/layout/reftests/mathml/op-dict-1.html
@@ -2,12 +2,13 @@
 <html>
 <head>
   <title>op-dict mo form</title>
 </head>
 <body>
   <math>
     <mrow>
       <mo form="infix">+</mo>
+      <mn>1</mn>
     </mrow>
   </math>
 </body>
 </html>
--- a/layout/reftests/mathml/reftest.list
+++ b/layout/reftests/mathml/reftest.list
@@ -136,16 +136,18 @@ fails-if(B2G) == mpadded-9.html mpadded-
 == mtable-columnalign-multi-mtr-dynamic.html mtable-columnalign-multi-ref.html
 == mtable-columnalign-multi-mtable.html mtable-columnalign-multi-ref.html
 == mtable-columnalign-multi-mtable-dynamic.html mtable-columnalign-multi-ref.html
 == maction-selection.html maction-selection-ref.html
 == maction-dynamic-embellished-op.html maction-dynamic-embellished-op-ref.html
 skip-if(B2G) == maction-dynamic-1.html maction-dynamic-1-ref.html # bug 773482
 == maction-dynamic-2.html maction-dynamic-2-ref.html
 == mo-lspace-rspace.html mo-lspace-rspace-ref.html
+== mo-lspace-rspace-2.html mo-lspace-rspace-2-ref.html
+== mo-lspace-rspace-3.html mo-lspace-rspace-3-ref.html
 == mo-invisibleoperators.html mo-invisibleoperators-ref.html
 == mo-invisibleoperators-2.html mo-invisibleoperators-2-ref.html
 skip-if(B2G) == maction-dynamic-3.html maction-dynamic-3-ref.html # bug 773482
 == whitespace-trim-1.html whitespace-trim-1-ref.html
 == whitespace-trim-2.html whitespace-trim-2-ref.html
 == whitespace-trim-3.html whitespace-trim-3-ref.html
 fails == whitespace-trim-4.html whitespace-trim-4-ref.html # Bug 787215
 == whitespace-trim-5.html whitespace-trim-5-ref.html
--- a/layout/xul/nsMenuBarFrame.cpp
+++ b/layout/xul/nsMenuBarFrame.cpp
@@ -233,17 +233,17 @@ nsMenuBarFrame::FindMenuWithShortcut(nsI
     if (soundInterface)
       soundInterface->Beep();
   }
 
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm) {
     nsIFrame* popup = pm->GetTopPopup(ePopupTypeAny);
     if (popup)
-      pm->HidePopup(popup->GetContent(), true, true, true);
+      pm->HidePopup(popup->GetContent(), true, true, true, false);
   }
 
   SetCurrentMenuItem(nullptr);
   SetActive(false);
 
 #endif  // #ifdef XP_WIN
 
   return nullptr;
@@ -303,17 +303,17 @@ public:
     if (mOldMenu && mNewMenu) {
       menubar = do_QueryFrame(mMenuBar->GetPrimaryFrame());
       if (menubar)
         menubar->SetStayActive(true);
     }
 
     if (mOldMenu) {
       nsWeakFrame weakMenuBar(menubar);
-      pm->HidePopup(mOldMenu, false, false, false);
+      pm->HidePopup(mOldMenu, false, false, false, false);
       // clear the flag again
       if (mNewMenu && weakMenuBar.IsAlive())
         menubar->SetStayActive(false);
     }
 
     if (mNewMenu)
       pm->ShowMenu(mNewMenu, mSelectFirstItem, false);
 
--- a/layout/xul/nsMenuBarListener.cpp
+++ b/layout/xul/nsMenuBarListener.cpp
@@ -101,17 +101,17 @@ void nsMenuBarListener::InitAccessKey()
 void
 nsMenuBarListener::ToggleMenuActiveState()
 {
   nsMenuFrame* closemenu = mMenuBarFrame->ToggleMenuActiveState();
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm && closemenu) {
     nsMenuPopupFrame* popupFrame = closemenu->GetPopup();
     if (popupFrame)
-      pm->HidePopup(popupFrame->GetContent(), false, false, true);
+      pm->HidePopup(popupFrame->GetContent(), false, false, true, false);
   }
 }
 
 ////////////////////////////////////////////////////////////////////////
 nsresult
 nsMenuBarListener::KeyUp(nsIDOMEvent* aKeyEvent)
 {  
   nsCOMPtr<nsIDOMKeyEvent> keyEvent = do_QueryInterface(aKeyEvent);
--- a/layout/xul/nsMenuBoxObject.cpp
+++ b/layout/xul/nsMenuBoxObject.cpp
@@ -44,17 +44,17 @@ NS_IMETHODIMP nsMenuBoxObject::OpenMenu(
         nsCOMPtr<nsIContent> content = mContent;
         pm->ShowMenu(content, false, false);
       }
       else {
         nsMenuFrame* menu = do_QueryFrame(frame);
         if (menu) {
           nsMenuPopupFrame* popupFrame = menu->GetPopup();
           if (popupFrame)
-            pm->HidePopup(popupFrame->GetContent(), false, true, false);
+            pm->HidePopup(popupFrame->GetContent(), false, true, false, false);
         }
       }
     }
   }
 
   return NS_OK;
 }
 
--- a/layout/xul/nsMenuFrame.cpp
+++ b/layout/xul/nsMenuFrame.cpp
@@ -708,17 +708,17 @@ nsMenuFrame::OpenMenu(bool aSelectFirstI
 void
 nsMenuFrame::CloseMenu(bool aDeselectMenu)
 {
   gEatMouseMove = true;
 
   // Close the menu asynchronously
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm && HasPopup())
-    pm->HidePopup(GetPopup()->GetContent(), false, aDeselectMenu, true);
+    pm->HidePopup(GetPopup()->GetContent(), false, aDeselectMenu, true, false);
 }
 
 bool
 nsMenuFrame::IsSizedToPopup(nsIContent* aContent, bool aRequireAlways)
 {
   nsAutoString sizedToPopup;
   aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::sizetopopup, sizedToPopup);
   return sizedToPopup.EqualsLiteral("always") ||
@@ -802,17 +802,17 @@ nsMenuFrame::Enter(WidgetGUIEvent* aEven
   if (IsDisabled()) {
 #ifdef XP_WIN
     // behavior on Windows - close the popup chain
     if (mMenuParent) {
       nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
       if (pm) {
         nsIFrame* popup = pm->GetTopPopup(ePopupTypeAny);
         if (popup)
-          pm->HidePopup(popup->GetContent(), true, true, true);
+          pm->HidePopup(popup->GetContent(), true, true, true, false);
       }
     }
 #endif   // #ifdef XP_WIN
     // this menu item was disabled - exit
     return nullptr;
   }
 
   if (!IsOpen()) {
--- a/layout/xul/nsPopupBoxObject.cpp
+++ b/layout/xul/nsPopupBoxObject.cpp
@@ -45,17 +45,17 @@ nsPopupBoxObject::GetPopupSetFrame()
   return rootBox->GetPopupSetFrame();
 }
 
 NS_IMETHODIMP
 nsPopupBoxObject::HidePopup()
 {
   nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
   if (pm && mContent)
-    pm->HidePopup(mContent, false, true, false);
+    pm->HidePopup(mContent, false, true, false, false);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsPopupBoxObject::ShowPopup(nsIDOMElement* aAnchorElement,
                             nsIDOMElement* aPopupElement,
                             int32_t aXPos, int32_t aYPos,
--- a/layout/xul/nsResizerFrame.cpp
+++ b/layout/xul/nsResizerFrame.cpp
@@ -248,34 +248,37 @@ nsResizerFrame::HandleEvent(nsPresContex
         nsWeakFrame weakFrame(menuPopupFrame);
         if (menuPopupFrame) {
           nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget();
           if (widget)
             widget->GetScreenBounds(oldRect);
 
           // convert the new rectangle into outer window coordinates
           nsIntPoint clientOffset = widget->GetClientOffset();
-          rect.x -= clientOffset.x; 
-          rect.y -= clientOffset.y; 
+          rect.x -= clientOffset.x;
+          rect.y -= clientOffset.y;
         }
 
         SizeInfo sizeInfo, originalSizeInfo;
         sizeInfo.width.AppendInt(cssRect.width);
         sizeInfo.height.AppendInt(cssRect.height);
         ResizeContent(contentToResize, direction, sizeInfo, &originalSizeInfo);
         MaybePersistOriginalSize(contentToResize, originalSizeInfo);
 
         // Move the popup to the new location unless it is anchored, since
         // the position shouldn't change. nsMenuPopupFrame::SetPopupPosition
         // will instead ensure that the popup's position is anchored at the
         // right place.
         if (weakFrame.IsAlive() &&
             (oldRect.x != rect.x || oldRect.y != rect.y) &&
             (!menuPopupFrame->IsAnchored() ||
              menuPopupFrame->PopupLevel() != ePopupLevelParent)) {
+
+          rect.x = aPresContext->DevPixelsToIntCSSPixels(rect.x);
+          rect.y = aPresContext->DevPixelsToIntCSSPixels(rect.y);
           menuPopupFrame->MoveTo(rect.x, rect.y, true);
         }
       }
       else {
         window->SetPositionAndSize(rect.x, rect.y, rect.width, rect.height, true); // do the repaint.
       }
 
       doDefault = false;
--- a/layout/xul/nsTitleBarFrame.cpp
+++ b/layout/xul/nsTitleBarFrame.cpp
@@ -122,17 +122,20 @@ nsTitleBarFrame::HandleEvent(nsPresConte
 
          // if the titlebar is in a popup, move the popup frame, otherwise
          // move the widget associated with the window
          if (parent) {
            nsMenuPopupFrame* menuPopupFrame = static_cast<nsMenuPopupFrame*>(parent);
            nsCOMPtr<nsIWidget> widget = menuPopupFrame->GetWidget();
            nsIntRect bounds;
            widget->GetScreenBounds(bounds);
-           menuPopupFrame->MoveTo(bounds.x + nsMoveBy.x, bounds.y + nsMoveBy.y, false);
+
+           int32_t newx = aPresContext->DevPixelsToIntCSSPixels(bounds.x + nsMoveBy.x);
+           int32_t newy = aPresContext->DevPixelsToIntCSSPixels(bounds.y + nsMoveBy.y);
+           menuPopupFrame->MoveTo(newx, newy, false);
          }
          else {
            nsIPresShell* presShell = aPresContext->PresShell();
            nsPIDOMWindow *window = presShell->GetDocument()->GetWindow();
            if (window) {
              window->MoveBy(nsMoveBy.x, nsMoveBy.y);
            }
          }
--- a/layout/xul/nsXULPopupManager.cpp
+++ b/layout/xul/nsXULPopupManager.cpp
@@ -225,17 +225,17 @@ nsXULPopupManager::Rollup(uint32_t aCoun
       while (--aCount && last->GetParent()) {
         last = last->GetParent();
       }
       if (last) {
         lastPopup = last->Content();
       }
     }
 
-    HidePopup(item->Content(), true, true, false, lastPopup);
+    HidePopup(item->Content(), true, true, false, true, lastPopup);
   }
 
   return consume;
 }
 
 ////////////////////////////////////////////////////////////////////////
 bool nsXULPopupManager::ShouldRollupOnMouseWheelEvent()
 {
@@ -821,16 +821,17 @@ nsXULPopupManager::ShowPopupCallback(nsI
   CheckCaretDrawingState();
 }
 
 void
 nsXULPopupManager::HidePopup(nsIContent* aPopup,
                              bool aHideChain,
                              bool aDeselectMenu,
                              bool aAsynchronous,
+                             bool aIsRollup,
                              nsIContent* aLastPopup)
 {
   // if the popup is on the nohide panels list, remove it but don't close any
   // other panels
   nsMenuPopupFrame* popupFrame = nullptr;
   bool foundPanel = false;
   nsMenuChainItem* item = mNoHidePanels;
   while (item) {
@@ -913,26 +914,72 @@ nsXULPopupManager::HidePopup(nsIContent*
     // run again. In the invisible state, we just want the events to fire.
     if (state != ePopupInvisible)
       popupFrame->SetPopupState(ePopupHiding);
 
     // for menus, popupToHide is always the frontmost item in the list to hide.
     if (aAsynchronous) {
       nsCOMPtr<nsIRunnable> event =
         new nsXULPopupHidingEvent(popupToHide, nextPopup, lastPopup,
-                                  type, deselectMenu);
+                                  type, deselectMenu, aIsRollup);
         NS_DispatchToCurrentThread(event);
     }
     else {
       FirePopupHidingEvent(popupToHide, nextPopup, lastPopup,
-                           popupFrame->PresContext(), type, deselectMenu);
+                           popupFrame->PresContext(), type, deselectMenu, aIsRollup);
     }
   }
 }
 
+// This is used to hide the popup after a transition finishes.
+class TransitionEnder : public nsIDOMEventListener
+{
+public:
+
+  nsCOMPtr<nsIContent> mContent;
+  bool mDeselectMenu;
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(TransitionEnder)
+
+  TransitionEnder(nsIContent* aContent, bool aDeselectMenu)
+    : mContent(aContent), mDeselectMenu(aDeselectMenu)
+  {
+  }
+
+  virtual ~TransitionEnder() { }
+
+  NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) MOZ_OVERRIDE
+  {
+    mContent->RemoveSystemEventListener(NS_LITERAL_STRING("transitionend"), this, false);
+
+    nsMenuPopupFrame* popupFrame = do_QueryFrame(mContent->GetPrimaryFrame());
+
+    // Now hide the popup. There could be other properties transitioning, but
+    // we'll assume they all end at the same time and just hide the popup upon
+    // the first one ending.
+    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
+    if (pm && popupFrame) {
+      pm->HidePopupCallback(mContent, popupFrame, nullptr, nullptr,
+                            popupFrame->PopupType(), mDeselectMenu);
+    }
+
+    return NS_OK;
+  }
+};
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TransitionEnder)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TransitionEnder)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TransitionEnder)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_1(TransitionEnder, mContent);
+
 void
 nsXULPopupManager::HidePopupCallback(nsIContent* aPopup,
                                      nsMenuPopupFrame* aPopupFrame,
                                      nsIContent* aNextPopup,
                                      nsIContent* aLastPopup,
                                      nsPopupType aPopupType,
                                      bool aDeselectMenu)
 {
@@ -1013,27 +1060,27 @@ nsXULPopupManager::HidePopupCallback(nsI
       nsPopupState state = popupFrame->PopupState();
       if (state == ePopupHiding)
         return;
       if (state != ePopupInvisible)
         popupFrame->SetPopupState(ePopupHiding);
 
       FirePopupHidingEvent(popupToHide, nextPopup, aLastPopup,
                            popupFrame->PresContext(),
-                           foundMenu->PopupType(), aDeselectMenu);
+                           foundMenu->PopupType(), aDeselectMenu, false);
     }
   }
 }
 
 void
 nsXULPopupManager::HidePopup(nsIFrame* aFrame)
 {
   nsMenuPopupFrame* popup = do_QueryFrame(aFrame);
   if (popup)
-    HidePopup(aFrame->GetContent(), false, true, false);
+    HidePopup(aFrame->GetContent(), false, true, false, false);
 }
 
 void
 nsXULPopupManager::HidePopupAfterDelay(nsMenuPopupFrame* aPopup)
 {
   // Don't close up immediately.
   // Kick off a close timer.
   KillMenuTimer();
@@ -1282,17 +1329,18 @@ nsXULPopupManager::FirePopupShowingEvent
 }
 
 void
 nsXULPopupManager::FirePopupHidingEvent(nsIContent* aPopup,
                                         nsIContent* aNextPopup,
                                         nsIContent* aLastPopup,
                                         nsPresContext *aPresContext,
                                         nsPopupType aPopupType,
-                                        bool aDeselectMenu)
+                                        bool aDeselectMenu,
+                                        bool aIsRollup)
 {
   nsCOMPtr<nsIPresShell> presShell = aPresContext->PresShell();
 
   nsEventStatus status = nsEventStatus_eIgnore;
   WidgetMouseEvent event(true, NS_XUL_POPUP_HIDING, nullptr,
                          WidgetMouseEvent::eReal);
   EventDispatcher::Dispatch(aPopup, aPresContext, &event, nullptr, &status);
 
@@ -1321,16 +1369,40 @@ nsXULPopupManager::FirePopupHidingEvent(
     // if the event was cancelled, don't hide the popup, and reset its
     // state back to open. Only popups in chrome shells can prevent a popup
     // from hiding.
     if (status == nsEventStatus_eConsumeNoDefault &&
         !popupFrame->IsInContentShell()) {
       popupFrame->SetPopupState(ePopupOpenAndVisible);
     }
     else {
+      // If the popup has an animate attribute and it is not set to false, assume
+      // that it has a closing transition and wait for it to finish. The transition
+      // may still occur either way, but the view will be hidden and you won't be
+      // able to see it. If there is a next popup, indicating that mutliple popups
+      // are rolling up, don't wait and hide the popup right away since the effect
+      // would likely be undesirable. This also does a quick check to see if the
+      // popup has a transition defined, and skips the wait if not.
+      if (!aNextPopup && aPopup->HasAttr(kNameSpaceID_None, nsGkAtoms::animate) &&
+          popupFrame->StyleDisplay()->mTransitionPropertyCount > 0) {
+        nsAutoString animate;
+        aPopup->GetAttr(kNameSpaceID_None, nsGkAtoms::animate, animate);
+
+        // If animate="false" then don't transition at all. If animate="cancel",
+        // only show the transition if cancelling the popup or rolling up.
+        // Otherwise, always show the transition.
+        if (!animate.EqualsLiteral("false") &&
+            (!animate.EqualsLiteral("cancel") || aIsRollup)) {
+          nsCOMPtr<TransitionEnder> ender = new TransitionEnder(aPopup, aDeselectMenu);
+          aPopup->AddSystemEventListener(NS_LITERAL_STRING("transitionend"),
+                                         ender, false, false);
+          return;
+        }
+      }
+
       HidePopupCallback(aPopup, popupFrame, aNextPopup, aLastPopup,
                         aPopupType, aDeselectMenu);
     }
   }
 }
 
 bool
 nsXULPopupManager::IsPopupOpen(nsIContent* aPopup)
@@ -1575,17 +1647,17 @@ nsXULPopupManager::PopupDestroyed(nsMenu
           // it asynchronously since we are in the middle of frame destruction.
           nsMenuPopupFrame* childframe = child->Frame();
           if (nsLayoutUtils::IsProperAncestorFrame(frame, childframe)) {
             popupsToHide.AppendElement(childframe);
           }
           else {
             // HidePopup will take care of hiding any of its children, so
             // break out afterwards
-            HidePopup(child->Content(), false, false, true);
+            HidePopup(child->Content(), false, false, true, false);
             break;
           }
 
           child = child->GetChild();
         }
       }
 
       item->Detach(&mPopups);
@@ -1775,17 +1847,17 @@ nsXULPopupManager::Notify(nsITimer* aTim
 void
 nsXULPopupManager::KillMenuTimer()
 {
   if (mCloseTimer && mTimerMenu) {
     mCloseTimer->Cancel();
     mCloseTimer = nullptr;
 
     if (mTimerMenu->IsOpen())
-      HidePopup(mTimerMenu->GetContent(), false, false, true);
+      HidePopup(mTimerMenu->GetContent(), false, false, true, false);
   }
 
   mTimerMenu = nullptr;
 }
 
 void
 nsXULPopupManager::CancelMenuTimer(nsMenuParent* aMenuParent)
 {
@@ -1971,17 +2043,17 @@ nsXULPopupManager::HandleKeyboardNavigat
       return true;
     }
   }
   else if (currentMenu && isContainer && isOpen) {
     if (aDir == eNavigationDirection_Start) {
       // close a submenu when Left is pressed
       nsMenuPopupFrame* popupFrame = currentMenu->GetPopup();
       if (popupFrame)
-        HidePopup(popupFrame->GetContent(), false, false, false);
+        HidePopup(popupFrame->GetContent(), false, false, false, false);
       return true;
     }
   }
 
   return false;
 }
 
 bool
@@ -1991,17 +2063,17 @@ nsXULPopupManager::HandleKeyboardEventWi
 {
   uint32_t keyCode;
   aKeyEvent->GetKeyCode(&keyCode);
 
   // Escape should close panels, but the other keys should have no effect.
   if (aTopVisibleMenuItem &&
       aTopVisibleMenuItem->PopupType() != ePopupTypeMenu) {
     if (keyCode == nsIDOMKeyEvent::DOM_VK_ESCAPE) {
-      HidePopup(aTopVisibleMenuItem->Content(), false, false, false);
+      HidePopup(aTopVisibleMenuItem->Content(), false, false, false, true);
       aKeyEvent->StopPropagation();
       aKeyEvent->PreventDefault();
     }
     return true;
   }
 
   bool consume = (mPopups || mActiveMenuBar);
   switch (keyCode) {
@@ -2015,17 +2087,17 @@ nsXULPopupManager::HandleKeyboardEventWi
       break;
 
     case nsIDOMKeyEvent::DOM_VK_ESCAPE:
       // Pressing Escape hides one level of menus only. If no menu is open,
       // check if a menubar is active and inform it that a menu closed. Even
       // though in this latter case, a menu didn't actually close, the effect
       // ends up being the same. Similar for the tab key below.
       if (aTopVisibleMenuItem) {
-        HidePopup(aTopVisibleMenuItem->Content(), false, false, false);
+        HidePopup(aTopVisibleMenuItem->Content(), false, false, false, true);
       } else if (mActiveMenuBar) {
         mActiveMenuBar->MenuClosed();
       }
       break;
 
     case nsIDOMKeyEvent::DOM_VK_TAB:
 #ifndef XP_MACOSX
     case nsIDOMKeyEvent::DOM_VK_F10:
@@ -2322,17 +2394,17 @@ nsXULPopupHidingEvent::Run()
 
   nsIDocument *document = mPopup->GetCurrentDoc();
   if (pm && document) {
     nsIPresShell* presShell = document->GetShell();
     if (presShell) {
       nsPresContext* context = presShell->GetPresContext();
       if (context) {
         pm->FirePopupHidingEvent(mPopup, mNextPopup, mLastPopup,
-                                 context, mPopupType, mDeselectMenu);
+                                 context, mPopupType, mDeselectMenu, mIsRollup);
       }
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
@@ -2383,12 +2455,12 @@ nsXULMenuCommandEvent::Run()
 
     AutoHandlingUserInputStatePusher userInpStatePusher(mUserInput, nullptr,
                                                         shell->GetDocument());
     nsContentUtils::DispatchXULCommand(mMenu, mIsTrusted, nullptr, shell,
                                        mControl, mAlt, mShift, mMeta);
   }
 
   if (popup && mCloseMenuMode != CloseMenuMode_None)
-    pm->HidePopup(popup, mCloseMenuMode == CloseMenuMode_Auto, true, false);
+    pm->HidePopup(popup, mCloseMenuMode == CloseMenuMode_Auto, true, false, false);
 
   return NS_OK;
 }
--- a/layout/xul/nsXULPopupManager.h
+++ b/layout/xul/nsXULPopupManager.h
@@ -199,35 +199,38 @@ private:
 // this class is used for dispatching popuphiding events asynchronously.
 class nsXULPopupHidingEvent : public nsRunnable
 {
 public:
   nsXULPopupHidingEvent(nsIContent *aPopup,
                         nsIContent* aNextPopup,
                         nsIContent* aLastPopup,
                         nsPopupType aPopupType,
-                        bool aDeselectMenu)
+                        bool aDeselectMenu,
+                        bool aIsRollup)
     : mPopup(aPopup),
       mNextPopup(aNextPopup),
       mLastPopup(aLastPopup),
       mPopupType(aPopupType),
-      mDeselectMenu(aDeselectMenu)
+      mDeselectMenu(aDeselectMenu),
+      mIsRollup(aIsRollup)
   {
     NS_ASSERTION(aPopup, "null popup supplied to nsXULPopupHidingEvent constructor");
     // aNextPopup and aLastPopup may be null
   }
 
   NS_IMETHOD Run() MOZ_OVERRIDE;
 
 private:
   nsCOMPtr<nsIContent> mPopup;
   nsCOMPtr<nsIContent> mNextPopup;
   nsCOMPtr<nsIContent> mLastPopup;
   nsPopupType mPopupType;
   bool mDeselectMenu;
+  bool mIsRollup;
 };
 
 // this class is used for dispatching menu command events asynchronously.
 class nsXULMenuCommandEvent : public nsRunnable
 {
 public:
   nsXULMenuCommandEvent(nsIContent *aMenu,
                         bool aIsTrusted,
@@ -271,16 +274,17 @@ class nsXULPopupManager MOZ_FINAL : publ
                                     public nsITimerCallback,
                                     public nsIObserver
 {
 
 public:
   friend class nsXULPopupShowingEvent;
   friend class nsXULPopupHidingEvent;
   friend class nsXULMenuCommandEvent;
+  friend class TransitionEnder;
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
   NS_DECL_NSITIMERCALLBACK
   NS_DECL_NSIDOMEVENTLISTENER
 
   // nsIRollupListener
   virtual bool Rollup(uint32_t aCount, const nsIntPoint* pos, nsIContent** aLastRolledUp) MOZ_OVERRIDE;
@@ -428,23 +432,25 @@ public:
    * aHideChain - true if the entire chain of menus should be closed. If false,
    *              only this popup is closed.
    * aDeselectMenu - true if the parent <menu> of the popup should be deselected.
    *                 This will be false when the menu is closed by pressing the
    *                 Escape key.
    * aAsynchronous - true if the first popuphiding event should be sent
    *                 asynchrously. This should be true if HidePopup is called
    *                 from a frame.
+   * aIsRollup - true if this popup is hiding due to a rollup or escape keypress.
    * aLastPopup - optional popup to close last when hiding a chain of menus.
    *              If null, then all popups will be closed.
    */
   void HidePopup(nsIContent* aPopup,
                  bool aHideChain,
                  bool aDeselectMenu,
                  bool aAsynchronous,
+                 bool aIsRollup,
                  nsIContent* aLastPopup = nullptr);
 
   /**
    * Hide the popup aFrame. This method is called by the view manager when the
    * close button is pressed.
    */
   void HidePopup(nsIFrame* aFrame);
 
@@ -659,23 +665,25 @@ protected:
    * The caller must keep a strong reference to aPopup, aNextPopup and aLastPopup.
    *
    * aPopup - the popup to hide
    * aNextPopup - the next popup to hide
    * aLastPopup - the last popup in the chain to hide
    * aPresContext - nsPresContext for the popup's frame
    * aPopupType - the PopupType of the frame. 
    * aDeselectMenu - true to unhighlight the menu when hiding it
+   * aIsRollup - true if this popup is hiding due to a rollup or escape keypress
    */
   void FirePopupHidingEvent(nsIContent* aPopup,
                             nsIContent* aNextPopup,
                             nsIContent* aLastPopup,
                             nsPresContext *aPresContext,
                             nsPopupType aPopupType,
-                            bool aDeselectMenu);
+                            bool aDeselectMenu,
+                            bool aIsRollup);
 
   /**
    * Handle keyboard navigation within a menu popup specified by aItem.
    */
   bool HandleKeyboardNavigationInPopup(nsMenuChainItem* aItem,
                                          nsNavigationDirection aDir)
   {
     return HandleKeyboardNavigationInPopup(aItem, aItem->Frame(), aDir);
--- a/layout/xul/nsXULTooltipListener.cpp
+++ b/layout/xul/nsXULTooltipListener.cpp
@@ -517,17 +517,17 @@ nsXULTooltipListener::LaunchTooltip()
 nsresult
 nsXULTooltipListener::HideTooltip()
 {
 #ifdef MOZ_XUL
   nsCOMPtr<nsIContent> currentTooltip = do_QueryReferent(mCurrentTooltip);
   if (currentTooltip) {
     nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
     if (pm)
-      pm->HidePopup(currentTooltip, false, false, false);
+      pm->HidePopup(currentTooltip, false, false, false, false);
   }
 #endif
 
   DestroyTooltip();
   return NS_OK;
 }
 
 static void
--- a/media/mtransport/nricectx.cpp
+++ b/media/mtransport/nricectx.cpp
@@ -129,16 +129,18 @@ static int nr_crypto_nss_hmac(UCHAR *key
   skey = PK11_ImportSymKey(slot, mech, PK11_OriginUnwrap,
                           CKA_SIGN, &keyi, nullptr);
   if (!skey)
     goto abort;
 
 
   hmac_ctx = PK11_CreateContextBySymKey(mech, CKA_SIGN,
                                         skey, &param);
+  if (!hmac_ctx)
+    goto abort;
 
   status = PK11_DigestBegin(hmac_ctx);
   if (status != SECSuccess)
     goto abort;
 
   status = PK11_DigestOp(hmac_ctx, buf, bufl);
   if (status != SECSuccess)
     goto abort;
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -43,16 +43,17 @@ namespace test {
 class AFakePCObserver;
 #endif
 }
 
 #ifdef USE_FAKE_MEDIA_STREAMS
 class Fake_DOMMediaStream;
 #endif
 
+class nsGlobalWindow;
 class nsIDOMMediaStream;
 class nsDOMDataChannel;
 
 namespace mozilla {
 class DataChannel;
 class DtlsIdentity;
 class NrIceCtx;
 class NrIceMediaStream;
--- a/modules/libmar/moz.build
+++ b/modules/libmar/moz.build
@@ -7,12 +7,15 @@
 DIRS += ['src']
 
 if CONFIG['MOZ_ENABLE_SIGNMAR']:
     DIRS += ['sign', 'verify']
     TEST_DIRS += ['tests']
 elif CONFIG['OS_ARCH'] == 'WINNT':
     # On Windows we don't verify with NSS and updater needs to link to it
     DIRS += ['verify']
+elif CONFIG['OS_ARCH'] == 'Darwin':
+    # On OSX 10.7+ we don't verify with NSS and updater needs to link to it
+    DIRS += ['verify']
 
 # If we are building ./sign and ./verify then ./tool must come after it
 DIRS += ['tool']
 
--- a/modules/libmar/tests/unit/test_sign_verify.js
+++ b/modules/libmar/tests/unit/test_sign_verify.js
@@ -137,46 +137,48 @@ function run_test() {
 
     // Make sure the signmar binary exists and is an executable.
     do_check_true(signmarBin.exists());
     do_check_true(signmarBin.isExecutable());
 
     // Will reference the arguments to use for verification in signmar
     let args = [];
 
-    // The XPCShell test wiki indicates this is the preferred way for 
-    // Windows detection.
+    // The XPCShell test wiki indicates this is the preferred way for
+    // Windows and OSX detection.
     var isWindows = ("@mozilla.org/windows-registry-key;1" in Cc);
+    var isOSX = ("nsILocalFileMac" in Components.interfaces);
 
     // Setup the command line arguments to create the MAR.
-    // Windows vs. Linux/Mac/... have different command line for verification 
-    // since  on Windows we verify with CryptoAPI and on all other platforms 
-    // we verify with NSS. So on Windows we use an exported DER file and on 
-    // other platforms we use the NSS config db.
-    if (isWindows) {
-      if (certs.length == 1 && useShortHandCmdLine) {
-        args.push("-D", "data/" + certs[0] + ".der");
-      } else {
-        for (i = 0; i < certs.length; i++) {
-          args.push("-D" + i, "data/" + certs[i] + ".der");
-        }
-      }
-      args.push("-v", signedMAR.path);
-    } else {
+    // Windows & Mac vs. Linux/... have different command line for verification
+    // since on Windows we verify with CryptoAPI, on Mac with Security
+    // Transforms or NSS and on all other platforms we verify with NSS. So on
+    // Windows and Mac we use an exported DER file and on other platforms we use
+    // the NSS config db.
+    if (!isWindows) {
       let NSSConfigDir = do_get_file("data");
       args = ["-d", NSSConfigDir.path];
       if (certs.length == 1 && useShortHandCmdLine) {
         args.push("-n", certs[0]);
       } else {
-        for (i = 0; i < certs.length; i++) {
+        for (var i = 0; i < certs.length; i++) {
           args.push("-n" + i, certs[i]);
         }
       }
-      args.push("-v", signedMAR.path);
     }
+    if (isWindows || isOSX) {
+      if (certs.length == 1 && useShortHandCmdLine) {
+        args.push("-D", "data/" + certs[0] + ".der");
+      } else {
+        for (var i = 0; i < certs.length; i++) {
+          args.push("-D" + i, "data/" + certs[i] + ".der");
+        }
+      }
+    }
+    args.push("-v", signedMAR.path);
 
     process.init(signmarBin);
     try {
       // We put this in a try block because nsIProcess doesn't like -1 returns
       process.run(true, args, args.length);
     } catch (e) {
       // On Windows negative return value throws an exception
       process.exitValue = -1;
--- a/modules/libmar/tool/mar.c
+++ b/modules/libmar/tool/mar.c
@@ -53,20 +53,28 @@ static void print_usage() {
          "signed_input_archive.mar base_64_encoded_signature_file "
          "changed_signed_output.mar\n");
   printf("(i) is the index of the certificate to extract\n");
 #if defined(XP_WIN) && !defined(MAR_NSS)
   printf("Verify a MAR file:\n");
   printf("  mar [-C workingDir] -D DERFilePath -v signed_archive.mar\n");
   printf("At most %d signature certificate DER files are specified by "
          "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES);
-#else 
+#elif defined(XP_MACOSX)
   printf("Verify a MAR file:\n");
   printf("  mar [-C workingDir] -d NSSConfigDir -n certname "
-    "-v signed_archive.mar\n");
+         "-v signed_archive.mar -D DERFilePath\n");
+  printf("At most %d signature certificate names are specified by "
+         "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES);
+  printf("At most %d signature certificate DER files are specified by "
+         "-D0 DERFilePath1 -D1 DERFilePath2, ...\n", MAX_SIGNATURES);
+#else
+  printf("Verify a MAR file:\n");
+  printf("  mar [-C workingDir] -d NSSConfigDir -n certname "
+         "-v signed_archive.mar\n");
   printf("At most %d signature certificate names are specified by "
          "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES);
 #endif
   printf("At most %d verification certificate names are specified by "
          "-n0 certName -n1 certName2, ...\n", MAX_SIGNATURES);
 #endif
   printf("Print information on a MAR file:\n");
   printf("  mar [-H MARChannelID] [-V ProductVersion] [-C workingDir] "
@@ -100,34 +108,39 @@ static int mar_test(const char *path) {
 int main(int argc, char **argv) {
   char *NSSConfigDir = NULL;
   const char *certNames[MAX_SIGNATURES];
   char *MARChannelID = MAR_CHANNEL_ID;
   char *productVersion = MOZ_APP_VERSION;
   uint32_t i, k;
   int rv = -1;
   uint32_t certCount = 0;
+  uint32_t derCount = 0;
   int32_t sigIndex = -1;
+  char* DERFilePaths[MAX_SIGNATURES];
+
 #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
   HANDLE certFile;
-  /* We use DWORD here instead of uint64_t because it simplifies code with
-     the Win32 API ReadFile which takes a DWORD.  DER files will not be too
-     large anyway. */
-  DWORD fileSizes[MAX_SIGNATURES];
-  DWORD read;
   uint8_t *certBuffers[MAX_SIGNATURES];
-  char *DERFilePaths[MAX_SIGNATURES];
+#endif
+#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \
+                                 defined(XP_MACOSX))
+  uint32_t fileSizes[MAX_SIGNATURES];
+  uint32_t read;
 #endif
 
   memset(certNames, 0, sizeof(certNames));
 #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
+  memset(certBuffers, 0, sizeof(certBuffers));
+#endif
+#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \
+                                 defined(XP_MACOSX))
   memset(fileSizes, 0, sizeof(fileSizes));
-  memset(certBuffers, 0, sizeof(certBuffers));
+#endif
   memset(DERFilePaths, 0, sizeof(DERFilePaths));
-#endif
 
   if (argc > 1 && 0 == strcmp(argv[1], "--version")) {
     print_version();
     return 0;
   }
 
   if (argc < 3) {
     print_usage();
@@ -143,28 +156,29 @@ int main(int argc, char **argv) {
         argv[1][1] == 'I')) {
       break;
     /* -C workingdirectory */
     } else if (argv[1][0] == '-' && argv[1][1] == 'C') {
       chdir(argv[2]);
       argv += 2;
       argc -= 2;
     } 
-#if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY)
+#if !defined(NO_SIGN_VERIFY) && ((!defined(MAR_NSS) && defined(XP_WIN)) || \
+                                 defined(XP_MACOSX))
     /* -D DERFilePath, also matches -D[index] DERFilePath
        We allow an index for verifying to be symmetric
        with the import and export command line arguments. */
     else if (argv[1][0] == '-' &&
              argv[1][1] == 'D' &&
-             (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) {
-      if (certCount >= MAX_SIGNATURES) {
+             (argv[1][2] == (char)('0' + derCount) || argv[1][2] == '\0')) {
+      if (derCount >= MAX_SIGNATURES) {
         print_usage();
         return -1;
       }
-      DERFilePaths[certCount++] = argv[2];
+      DERFilePaths[derCount++] = argv[2];
       argv += 2;
       argc -= 2;
     }
 #endif
     /* -d NSSConfigdir */
     else if (argv[1][0] == '-' && argv[1][1] == 'd') {
       NSSConfigDir = argv[2];
       argv += 2;
@@ -298,22 +312,22 @@ int main(int argc, char **argv) {
       print_usage();
       return -1;
     }
     return import_signature(argv[2], sigIndex, argv[3], argv[4]);
 
   case 'v':
 
 #if defined(XP_WIN) && !defined(MAR_NSS)
-    if (certCount == 0) {
+    if (derCount == 0) {
       print_usage();
       return -1;
     }
 
-    for (k = 0; k < certCount; ++k) {
+    for (k = 0; k < derCount; ++k) {
       /* If the mar program was built using CryptoAPI, then read in the buffer
         containing the cert from disk. */
       certFile = CreateFileA(DERFilePaths[k], GENERIC_READ,
                              FILE_SHARE_READ |
                              FILE_SHARE_WRITE |
                              FILE_SHARE_DELETE,
                              NULL,
                              OPEN_EXISTING,
@@ -330,47 +344,48 @@ int main(int argc, char **argv) {
           free(certBuffers[i]);
         }
         return -1;
       }
       CloseHandle(certFile);
     }
 
     rv = mar_verify_signatures(argv[2], certBuffers, fileSizes,
-                               NULL, certCount);
-    for (k = 0; k < certCount; ++k) {
+                               NULL, derCount);
+    for (k = 0; k < derCount; ++k) {
       free(certBuffers[k]);
     }
     if (rv) {
       /* Determine if the source MAR file has the new fields for signing */
       int hasSignatureBlock;
       if (get_mar_file_info(argv[2], &hasSignatureBlock, 
                             NULL, NULL, NULL, NULL)) {
         fprintf(stderr, "ERROR: could not determine if MAR is old or new.\n");
       } else if (!hasSignatureBlock) {
         fprintf(stderr, "ERROR: The MAR file is in the old format so has"
                         " no signature to verify.\n");
       }
       return -1;
     }
 
     return 0;
+
 #else
     if (!NSSConfigDir || certCount == 0) {
       print_usage();
       return -1;
     }
 
     if (NSSInitCryptoContext(NSSConfigDir)) {
       fprintf(stderr, "ERROR: Could not initialize crypto library.\n");
       return -1;
     }
 
-    return mar_verify_signatures(argv[2], NULL, 0,
-                                 certNames, certCount);
+    return mar_verify_signatures(argv[2], (const uint8_t* const*)DERFilePaths,
+                                 0, certNames, certCount);
 
 #endif /* defined(XP_WIN) && !defined(MAR_NSS) */
   case 's':
     if (!NSSConfigDir || certCount == 0 || argc < 4) {
       print_usage();
       return -1;
     }
     return mar_repackage_and_sign(NSSConfigDir, certNames, certCount,
new file mode 100644
--- /dev/null
+++ b/modules/libmar/verify/MacVerifyCrypto.cpp
@@ -0,0 +1,367 @@
+/* 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 <CoreFoundation/CoreFoundation.h>
+#include <Security/Security.h>
+#include <dlfcn.h>
+
+#include "cryptox.h"
+
+// We declare the necessary parts of the Security Transforms API here since
+// we're building with the 10.6 SDK, which doesn't know about Security
+// Transforms.
+#ifdef __cplusplus
+extern "C" {
+#endif
+  const CFStringRef kSecTransformInputAttributeName = CFSTR("INPUT");
+  typedef CFTypeRef SecTransformRef;
+  typedef struct OpaqueSecKeyRef* SecKeyRef;
+
+  typedef SecTransformRef (*SecTransformCreateReadTransformWithReadStreamFunc)
+                            (CFReadStreamRef inputStream);
+  SecTransformCreateReadTransformWithReadStreamFunc
+    SecTransformCreateReadTransformWithReadStreamPtr = NULL;
+  typedef CFTypeRef (*SecTransformExecuteFunc)(SecTransformRef transform,
+                                               CFErrorRef* error);
+  SecTransformExecuteFunc SecTransformExecutePtr = NULL;
+  typedef SecTransformRef (*SecVerifyTransformCreateFunc)(SecKeyRef key,
+                                                          CFDataRef signature,
+                                                          CFErrorRef* error);
+  SecVerifyTransformCreateFunc SecVerifyTransformCreatePtr = NULL;
+  typedef Boolean (*SecTransformSetAttributeFunc)(SecTransformRef transform,
+                                                  CFStringRef key,
+                                                  CFTypeRef value,
+                                                  CFErrorRef* error);
+  SecTransformSetAttributeFunc SecTransformSetAttributePtr = NULL;
+  typedef SecCertificateRef (*SecCertificateCreateWithDataFunc)
+                              (CFAllocatorRef allocator,
+                               CFDataRef data);
+  SecCertificateCreateWithDataFunc SecCertificateCreateWithDataPtr = NULL;
+  typedef OSStatus (*SecCertificateCopyPublicKeyFunc)
+                     (SecCertificateRef certificate,
+                      SecKeyRef* key);
+  SecCertificateCopyPublicKeyFunc SecCertificateCopyPublicKeyPtr = NULL;
+#ifdef __cplusplus
+}
+#endif
+
+#define MAC_OS_X_VERSION_10_7_HEX 0x00001070
+
+static int sOnLionOrLater = -1;
+
+static bool OnLionOrLater()
+{
+  if (sOnLionOrLater < 0) {
+    SInt32 major = 0, minor = 0;
+
+    CFURLRef url =
+      CFURLCreateWithString(kCFAllocatorDefault,
+                            CFSTR("file:///System/Library/CoreServices/SystemVersion.plist"),
+                            NULL);
+    CFReadStreamRef stream =
+      CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+    CFReadStreamOpen(stream);
+    CFDictionaryRef sysVersionPlist = (CFDictionaryRef)
+      CFPropertyListCreateWithStream(kCFAllocatorDefault,
+                                     stream, 0, kCFPropertyListImmutable,
+                                     NULL, NULL);
+    CFReadStreamClose(stream);
+    CFRelease(stream);
+    CFRelease(url);
+
+    CFStringRef versionString = (CFStringRef)
+      CFDictionaryGetValue(sysVersionPlist, CFSTR("ProductVersion"));
+    CFArrayRef versions =
+      CFStringCreateArrayBySeparatingStrings(kCFAllocatorDefault,
+                                             versionString, CFSTR("."));
+    CFIndex count = CFArrayGetCount(versions);
+    if (count > 0) {
+      CFStringRef component = (CFStringRef) CFArrayGetValueAtIndex(versions, 0);
+      major = CFStringGetIntValue(component);
+      if (count > 1) {
+        component = (CFStringRef) CFArrayGetValueAtIndex(versions, 1);
+        minor = CFStringGetIntValue(component);
+      }
+    }
+    CFRelease(sysVersionPlist);
+    CFRelease(versions);
+
+    if (major < 10) {
+      sOnLionOrLater = 0;
+    } else {
+      int version = 0x1000 + (minor << 4);
+      sOnLionOrLater = version >= MAC_OS_X_VERSION_10_7_HEX ? 1 : 0;
+    }
+  }
+
+  return sOnLionOrLater > 0 ? true : false;
+}
+
+CryptoX_Result
+CryptoMac_InitCryptoProvider()
+{
+  if (!OnLionOrLater()) {
+    return CryptoX_Success;
+  }
+
+  if (!SecTransformCreateReadTransformWithReadStreamPtr) {
+    SecTransformCreateReadTransformWithReadStreamPtr =
+      (SecTransformCreateReadTransformWithReadStreamFunc)
+        dlsym(RTLD_DEFAULT, "SecTransformCreateReadTransformWithReadStream");
+  }
+  if (!SecTransformExecutePtr) {
+    SecTransformExecutePtr = (SecTransformExecuteFunc)
+      dlsym(RTLD_DEFAULT, "SecTransformExecute");
+  }
+  if (!SecVerifyTransformCreatePtr) {
+    SecVerifyTransformCreatePtr = (SecVerifyTransformCreateFunc)
+      dlsym(RTLD_DEFAULT, "SecVerifyTransformCreate");
+  }
+  if (!SecTransformSetAttributePtr) {
+    SecTransformSetAttributePtr = (SecTransformSetAttributeFunc)
+      dlsym(RTLD_DEFAULT, "SecTransformSetAttribute");
+  }
+  if (!SecCertificateCreateWithDataPtr) {
+    SecCertificateCreateWithDataPtr = (SecCertificateCreateWithDataFunc)
+      dlsym(RTLD_DEFAULT, "SecCertificateCreateWithData");
+  }
+  if (!SecCertificateCopyPublicKeyPtr) {
+    SecCertificateCopyPublicKeyPtr = (SecCertificateCopyPublicKeyFunc)
+      dlsym(RTLD_DEFAULT, "SecCertificateCopyPublicKey");
+  }
+  if (!SecTransformCreateReadTransformWithReadStreamPtr ||
+      !SecTransformExecutePtr ||
+      !SecVerifyTransformCreatePtr ||
+      !SecTransformSetAttributePtr ||
+      !SecCertificateCreateWithDataPtr ||
+      !SecCertificateCopyPublicKeyPtr) {
+    return CryptoX_Error;
+  }
+  return CryptoX_Success;
+}
+
+CryptoX_Result
+CryptoMac_VerifyBegin(CryptoX_SignatureHandle* aInputData,
+                      CryptoX_PublicKey* aPublicKey)
+{
+  if (!OnLionOrLater()) {
+    return NSS_VerifyBegin((VFYContext**)aInputData,
+                           (SECKEYPublicKey* const*)aPublicKey);
+  }
+
+  (void)aPublicKey;
+  if (!aInputData) {
+    return CryptoX_Error;
+  }
+
+  CryptoX_Result result = CryptoX_Error;
+  *aInputData = CFDataCreateMutable(kCFAllocatorDefault, 0);
+  if (*aInputData) {
+    result = CryptoX_Success;
+  }
+
+  return result;
+}
+
+CryptoX_Result
+CryptoMac_VerifyUpdate(CryptoX_SignatureHandle* aInputData, void* aBuf,
+                       unsigned int aLen)
+{
+  if (!OnLionOrLater()) {
+    return VFY_Update((VFYContext*)*aInputData,
+                      (const unsigned char*)aBuf, aLen);
+  }
+
+  if (aLen == 0) {
+    return CryptoX_Success;
+  }
+  if (!aInputData || !*aInputData) {
+    return CryptoX_Error;
+  }
+
+  CryptoX_Result result = CryptoX_Error;
+  CFDataAppendBytes((CFMutableDataRef)*aInputData, (const UInt8 *) aBuf, aLen);
+  if (*aInputData) {
+    result = CryptoX_Success;
+  }
+
+  return result;
+}
+
+CryptoX_Result
+CryptoMac_LoadPublicKey(const unsigned char* aCertData,
+                        CryptoX_PublicKey* aPublicKey,
+                        const char* aCertName,
+                        CryptoX_Certificate* aCert)
+{
+  if (!aPublicKey ||
+      (OnLionOrLater() && !aCertData) ||
+      (!OnLionOrLater() && !aCertName)) {
+    return CryptoX_Error;
+  }
+
+  if (!OnLionOrLater()) {
+    return NSS_LoadPublicKey(aCertName,
+                             (SECKEYPublicKey**)aPublicKey,
+                             (CERTCertificate**)aCert);
+  }
+
+  CFURLRef url =
+    CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault,
+                                            aCertData,
+                                            strlen((char*)aCertData),
+                                            false);
+  if (!url) {
+    return CryptoX_Error;
+  }
+
+  CFReadStreamRef stream = CFReadStreamCreateWithFile(kCFAllocatorDefault, url);
+  if (!stream) {
+    CFRelease(url);
+    return CryptoX_Error;
+  }
+
+  SecTransformRef readTransform =
+    SecTransformCreateReadTransformWithReadStreamPtr(stream);
+  if (!readTransform) {
+    CFRelease(url);
+    CFRelease(stream);
+    return CryptoX_Error;
+  }
+
+  CFErrorRef error;
+  CFDataRef tempCertData = (CFDataRef)SecTransformExecutePtr(readTransform,
+                                                             &error);
+  if (!tempCertData || error) {
+    CFRelease(url);
+    CFRelease(stream);
+    CFRelease(readTransform);
+    return CryptoX_Error;
+  }
+
+  SecCertificateRef cert = SecCertificateCreateWithDataPtr(kCFAllocatorDefault,
+                                                           tempCertData);
+  if (!cert) {
+    CFRelease(url);
+    CFRelease(stream);
+    CFRelease(readTransform);
+    CFRelease(tempCertData);
+    return CryptoX_Error;
+  }
+
+  CryptoX_Result result = CryptoX_Error;
+  OSStatus status = SecCertificateCopyPublicKeyPtr(cert,
+                                                   (SecKeyRef*)aPublicKey);
+  if (status == 0) {
+    result = CryptoX_Success;
+  }
+
+  CFRelease(url);
+  CFRelease(stream);
+  CFRelease(readTransform);
+  CFRelease(tempCertData);
+  CFRelease(cert);
+
+  return result;
+}
+
+CryptoX_Result
+CryptoMac_VerifySignature(CryptoX_SignatureHandle* aInputData,
+                          CryptoX_PublicKey* aPublicKey,
+                          const unsigned char* aSignature,
+                          unsigned int aSignatureLen)
+{
+  if (!OnLionOrLater()) {
+    return NSS_VerifySignature((VFYContext* const*)aInputData, aSignature,
+                               aSignatureLen);
+  }
+
+  if (!aInputData || !*aInputData || !aPublicKey || !*aPublicKey ||
+      !aSignature || aSignatureLen == 0) {
+    return CryptoX_Error;
+  }
+
+  CFDataRef signatureData = CFDataCreate(kCFAllocatorDefault,
+                                         aSignature, aSignatureLen);
+  if (!signatureData) {
+    return CryptoX_Error;
+  }
+
+  CFErrorRef error;
+  SecTransformRef verifier =
+    SecVerifyTransformCreatePtr((SecKeyRef)*aPublicKey,
+                                signatureData,
+                                &error);
+  if (!verifier || error) {
+    CFRelease(signatureData);
+    return CryptoX_Error;
+  }
+
+  SecTransformSetAttributePtr(verifier,
+                              kSecTransformInputAttributeName,
+                              (CFDataRef)*aInputData,
+                              &error);
+  if (error) {
+    CFRelease(signatureData);
+    CFRelease(verifier);
+    return CryptoX_Error;
+  }
+
+  CryptoX_Result result = CryptoX_Error;
+  CFTypeRef rv = SecTransformExecutePtr(verifier, &error);
+  if (error) {
+    CFRelease(signatureData);
+    CFRelease(verifier);
+    return CryptoX_Error;
+  }
+
+  if (CFGetTypeID(rv) == CFBooleanGetTypeID() &&
+      CFBooleanGetValue((CFBooleanRef)rv) == true) {
+    result = CryptoX_Success;
+  }
+
+  CFRelease(signatureData);
+  CFRelease(verifier);
+
+  return result;
+}
+
+void
+CryptoMac_FreeSignatureHandle(CryptoX_SignatureHandle* aInputData)
+{
+  if (!OnLionOrLater()) {
+    return VFY_DestroyContext((VFYContext*)aInputData, PR_TRUE);
+  }
+
+  if (!aInputData || !*aInputData) {
+    return;
+  }
+  CFRelease((CFMutableDataRef)*aInputData);
+}
+
+void
+CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey)
+{
+  if (!OnLionOrLater()) {
+    return SECKEY_DestroyPublicKey((SECKEYPublicKey*)*aPublicKey);
+  }
+
+  if (!aPublicKey || !*aPublicKey) {
+    return;
+  }
+  CFRelease((SecKeyRef)*aPublicKey);
+}
+
+void
+CryptoMac_FreeCertificate(CryptoX_Certificate* aCertificate)
+{
+  if (!OnLionOrLater()) {
+    return CERT_DestroyCertificate((CERTCertificate*)*aCertificate);
+  }
+
+  if (!aCertificate || !*aCertificate) {
+    return;
+  }
+  CFRelease((SecKeyRef)*aCertificate);
+}
--- a/modules/libmar/verify/cryptox.h
+++ b/modules/libmar/verify/cryptox.h
@@ -12,27 +12,85 @@
 #define CryptoX_Error (-1)
 #define CryptoX_Succeeded(X) ((X) == CryptoX_Success)
 #define CryptoX_Failed(X) ((X) != CryptoX_Success)
 
 #if defined(MAR_NSS)
 
 #include "nss_secutil.h"
 
+#define CryptoX_InvalidHandleValue NULL
+#define CryptoX_ProviderHandle void*
+
+#ifdef __cplusplus
+extern "C" {
+#endif
 CryptoX_Result NSS_LoadPublicKey(const char *certNickname, 
                                  SECKEYPublicKey **publicKey, 
                                  CERTCertificate **cert);
 CryptoX_Result NSS_VerifyBegin(VFYContext **ctx, 
                                SECKEYPublicKey * const *publicKey);
 CryptoX_Result NSS_VerifySignature(VFYContext * const *ctx , 
                                    const unsigned char *signature, 
                                    unsigned int signatureLen);
+#ifdef __cplusplus
+} // extern "C"
+#endif
 
-#define CryptoX_InvalidHandleValue NULL
-#define CryptoX_ProviderHandle void*
+#ifdef XP_MACOSX
+
+#define CryptoX_SignatureHandle void*
+#define CryptoX_PublicKey void*
+#define CryptoX_Certificate void*
+
+// Forward-declare Objective-C functions implemented in MacVerifyCrypto.mm.
+#ifdef __cplusplus
+extern "C" {
+#endif
+CryptoX_Result CryptoMac_InitCryptoProvider();
+CryptoX_Result CryptoMac_VerifyBegin(CryptoX_SignatureHandle* aInputData,
+                                     CryptoX_PublicKey* aPublicKey);
+CryptoX_Result CryptoMac_VerifyUpdate(CryptoX_SignatureHandle* aInputData,
+                                      void* aBuf, unsigned int aLen);
+CryptoX_Result CryptoMac_LoadPublicKey(const unsigned char* aCertData,
+                                       CryptoX_PublicKey* aPublicKey,
+                                       const char* aCertName,
+                                       CryptoX_Certificate* aCert);
+CryptoX_Result CryptoMac_VerifySignature(CryptoX_SignatureHandle* aInputData,
+                                         CryptoX_PublicKey* aPublicKey,
+                                         const unsigned char* aSignature,
+                                         unsigned int aSignatureLen);
+void CryptoMac_FreeSignatureHandle(CryptoX_SignatureHandle* aInputData);
+void CryptoMac_FreePublicKey(CryptoX_PublicKey* aPublicKey);
+void CryptoMac_FreeCertificate(CryptoX_Certificate* aCertificate);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#define CryptoX_InitCryptoProvider(aCryptoHandle) \
+  CryptoMac_InitCryptoProvider()
+#define CryptoX_VerifyBegin(aCryptoHandle, aInputData, aPublicKey) \
+  CryptoMac_VerifyBegin(aInputData, aPublicKey)
+#define CryptoX_VerifyUpdate(aInputData, aBuf, aLen) \
+  CryptoMac_VerifyUpdate(aInputData, aBuf, aLen)
+#define CryptoX_LoadPublicKey(aCryptoHandle, aCertData, aDataSize, \
+                              aPublicKey, aCertName, aCert) \
+  CryptoMac_LoadPublicKey(aCertData, aPublicKey, aCertName, aCert)
+#define CryptoX_VerifySignature(aInputData, aPublicKey, aSignature, \
+                                aSignatureLen) \
+  CryptoMac_VerifySignature(aInputData, aPublicKey, aSignature, aSignatureLen)
+#define CryptoX_FreeSignatureHandle(aInputData) \
+  CryptoMac_FreeSignatureHandle(aInputData)
+#define CryptoX_FreePublicKey(aPublicKey) \
+  CryptoMac_FreePublicKey(aPublicKey)
+#define CryptoX_FreeCertificate(aCertificate) \
+  CryptoMac_FreeCertificate(aCertificate)
+
+#else
+
 #define CryptoX_SignatureHandle VFYContext *
 #define CryptoX_PublicKey SECKEYPublicKey *
 #define CryptoX_Certificate CERTCertificate *
 #define CryptoX_InitCryptoProvider(CryptoHandle) \
   CryptoX_Success
 #define CryptoX_VerifyBegin(CryptoHandle, SignatureHandle, PublicKey) \
   NSS_VerifyBegin(SignatureHandle, PublicKey)
 #define CryptoX_FreeSignatureHandle(SignatureHandle) \
@@ -44,16 +102,18 @@ CryptoX_Result NSS_VerifySignature(VFYCo
   NSS_LoadPublicKey(certName, publicKey, cert)
 #define CryptoX_VerifySignature(hash, publicKey, signedData, len) \
   NSS_VerifySignature(hash, (const unsigned char *)(signedData), len)
 #define CryptoX_FreePublicKey(key) \
   SECKEY_DestroyPublicKey(*key)
 #define CryptoX_FreeCertificate(cert) \
   CERT_DestroyCertificate(*cert)
 
+#endif
+
 #elif defined(XP_WIN) 
 
 #include <windows.h>
 #include <wincrypt.h>
 
 CryptoX_Result CryptoAPI_InitCryptoContext(HCRYPTPROV *provider);
 CryptoX_Result CryptoAPI_LoadPublicKey(HCRYPTPROV hProv, 
                                        BYTE *certData,
--- a/modules/libmar/verify/moz.build
+++ b/modules/libmar/verify/moz.build
@@ -14,12 +14,17 @@ UNIFIED_SOURCES += [
 FORCE_STATIC_LIB = True
 
 if CONFIG['OS_ARCH'] == 'WINNT':
     USE_STATIC_LIBS = True
 else:
     DEFINES['MAR_NSS'] = True
     LOCAL_INCLUDES += ['../sign']
 
+if CONFIG['OS_ARCH'] == 'Darwin':
+    UNIFIED_SOURCES += [
+      'MacVerifyCrypto.cpp',
+    ]
+
 LOCAL_INCLUDES += [
     '../src',
 ]
 
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -4021,16 +4021,21 @@ pref("layers.offmainthreadcomposition.fr
 pref("layers.async-video.enabled",false);
 #endif
 
 #ifdef MOZ_X11
 // OMTC off by default on Linux, but if activated, use new textures and async-video.
 pref("layers.async-video.enabled", true);
 #endif
 
+#ifdef MOZ_WIDGET_QT
+pref("layers.offmainthreadcomposition.enabled", true);
+pref("layers.async-video.enabled",true);
+#endif
+
 #ifdef XP_MACOSX
 pref("layers.offmainthreadcomposition.enabled", true);
 pref("layers.async-video.enabled",true);
 #endif
 
 // ANDROID covers android and b2g
 #ifdef ANDROID
 pref("layers.offmainthreadcomposition.enabled", true);
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/NetStatistics.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim:set ts=4 sw=4 sts=4 et cin: */
+/* 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 NetStatistics_h__
+#define NetStatistics_h__
+
+#include "mozilla/Assertions.h"
+
+#include "nsCOMPtr.h"
+#include "nsError.h"
+#include "nsINetworkManager.h"
+#include "nsINetworkStatsServiceProxy.h"
+#include "nsThreadUtils.h"
+#include "nsProxyRelease.h"
+
+namespace mozilla {
+namespace net {
+
+// The following members are used for network per-app metering.
+const static uint64_t NETWORK_STATS_THRESHOLD = 65536;
+const static char NETWORK_STATS_NO_SERVICE_TYPE[] = "";
+
+inline nsresult
+GetActiveNetworkInterface(nsCOMPtr<nsINetworkInterface> &aNetworkInterface)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsresult rv;
+  nsCOMPtr<nsINetworkManager> networkManager =
+    do_GetService("@mozilla.org/network/manager;1", &rv);
+
+  if (NS_FAILED(rv) || !networkManager) {
+    aNetworkInterface = nullptr;
+    return rv;
+  }
+
+  networkManager->GetActive(getter_AddRefs(aNetworkInterface));
+
+  return NS_OK;
+}
+
+class SaveNetworkStatsEvent : public nsRunnable {
+public:
+  SaveNetworkStatsEvent(uint32_t aAppId,
+                        nsMainThreadPtrHandle<nsINetworkInterface> &aActiveNetwork,
+                        uint64_t aCountRecv,
+                        uint64_t aCountSent,
+                        bool aIsAccumulative)
+    : mAppId(aAppId),
+      mActiveNetwork(aActiveNetwork),
+      mCountRecv(aCountRecv),
+      mCountSent(aCountSent),
+      mIsAccumulative(aIsAccumulative)
+  {
+    MOZ_ASSERT(mAppId != NECKO_NO_APP_ID);
+    MOZ_ASSERT(mActiveNetwork);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsresult rv;
+    nsCOMPtr<nsINetworkStatsServiceProxy> mNetworkStatsServiceProxy =
+      do_GetService("@mozilla.org/networkstatsServiceProxy;1", &rv);
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+
+    // save the network stats through NetworkStatsServiceProxy
+    mNetworkStatsServiceProxy->SaveAppStats(mAppId,
+                                            mActiveNetwork,
+                                            PR_Now() / 1000,
+                                            mCountRecv,
+                                            mCountSent,
+                                            mIsAccumulative,
+                                            nullptr);
+
+    return NS_OK;
+  }
+private:
+  uint32_t mAppId;
+  nsMainThreadPtrHandle<nsINetworkInterface> mActiveNetwork;
+  uint64_t mCountRecv;
+  uint64_t mCountSent;
+  bool mIsAccumulative;
+};
+
+} // namespace mozilla:net
+} // namespace mozilla
+
+#endif // !NetStatistics_h__
--- a/netwerk/base/public/moz.build
+++ b/netwerk/base/public/moz.build
@@ -136,10 +136,15 @@ EXPORTS += [
     'nsChannelProperties.h',
     'nsNetStrings.h',
     'nsNetUtil.h',
     'nsReadLine.h',
     'nsStreamListenerWrapper.h',
     'nsURIHashKey.h',
 ]
 
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk':
+    EXPORTS += [
+        'NetStatistics.h',
+    ]
+
 FAIL_ON_WARNINGS = True
 
--- a/netwerk/base/public/nsNetUtil.h
+++ b/netwerk/base/public/nsNetUtil.h
@@ -81,21 +81,16 @@
 #include "nsIOfflineCacheUpdate.h"
 #include "nsIContentSniffer.h"
 #include "nsCategoryCache.h"
 #include "nsStringStream.h"
 #include "nsIViewSourceChannel.h"
 
 #include <limits>
 
-#ifdef MOZ_WIDGET_GONK
-#include "nsINetworkManager.h"
-#include "nsThreadUtils.h" // for NS_IsMainThread
-#endif
-
 #ifdef MOZILLA_INTERNAL_API
 
 #include "nsReadableUtils.h"
 
 inline already_AddRefed<nsIIOService>
 do_GetIOService(nsresult* error = 0)
 {
     nsCOMPtr<nsIIOService> io = mozilla::services::GetIOService();
@@ -2399,34 +2394,9 @@ NS_IsSrcdocChannel(nsIChannel *aChannel)
   nsCOMPtr<nsIViewSourceChannel> vsc = do_QueryInterface(aChannel);
   if (vsc) {
     vsc->GetIsSrcdocChannel(&isSrcdoc);
     return isSrcdoc;
   }
   return false;
 }
 
-// The following members are used for network per-app metering.
-const static uint64_t NETWORK_STATS_THRESHOLD = 65536;
-const static char NETWORK_STATS_NO_SERVICE_TYPE[] = "";
-
-#ifdef MOZ_WIDGET_GONK
-inline nsresult
-NS_GetActiveNetworkInterface(nsCOMPtr<nsINetworkInterface> &aNetworkInterface)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsresult rv;
-  nsCOMPtr<nsINetworkManager> networkManager =
-    do_GetService("@mozilla.org/network/manager;1", &rv);
-
-  if (NS_FAILED(rv) || !networkManager) {
-    aNetworkInterface = nullptr;
-    return rv;
-  }
-
-  networkManager->GetActive(getter_AddRefs(aNetworkInterface));
-
-  return NS_OK;
-}
-#endif
-
 #endif // !nsNetUtil_h__
--- a/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
+++ b/netwerk/protocol/ftp/nsFtpConnectionThread.cpp
@@ -38,17 +38,17 @@
 #include "nsIProtocolHandler.h"
 #include "nsIProxyInfo.h"
 #include "nsIRunnable.h"
 #include "nsISocketTransportService.h"
 #include "nsIURI.h"
 #include "nsICacheSession.h"
 
 #ifdef MOZ_WIDGET_GONK
-#include "nsINetworkStatsServiceProxy.h"
+#include "NetStatistics.h"
 #endif
 
 #if defined(PR_LOGGING)
 extern PRLogModuleInfo* gFTPLog;
 #endif
 #define LOG(args)         PR_LOG(gFTPLog, PR_LOG_DEBUG, args)
 #define LOG_ALWAYS(args)  PR_LOG(gFTPLog, PR_LOG_ALWAYS, args)
 
@@ -1686,17 +1686,20 @@ nsFtpState::Init(nsFtpChannel *channel)
     NS_ASSERTION(channel, "FTP: needs a channel");
 
     mChannel = channel; // a straight ref ptr to the channel
 
     // initialize counter for network metering
     mCountRecv = 0;
 
 #ifdef MOZ_WIDGET_GONK
-    NS_GetActiveNetworkInterface(mActiveNetwork);
+    nsCOMPtr<nsINetworkInterface> activeNetwork;
+    GetActiveNetworkInterface(activeNetwork);
+    mActiveNetwork =
+        new nsMainThreadPtrHolder<nsINetworkInterface>(activeNetwork);
 #endif
 
     mKeepRunning = true;
     mSuppliedEntityID = channel->EntityID();
 
     if (channel->UploadStream())
         mAction = PUT;
 
@@ -2213,30 +2216,21 @@ nsFtpState::SaveNetworkStats(bool enforc
 
     // If |enforce| is false, the traffic amount is saved
     // only when the total amount exceeds the predefined
     // threshold.
     if (!enforce && mCountRecv < NETWORK_STATS_THRESHOLD) {
         return NS_OK;
     }
 
-    nsresult rv;
-    nsCOMPtr<nsINetworkStatsServiceProxy> networkStatsServiceProxy =
-        do_GetService("@mozilla.org/networkstatsServiceProxy;1", &rv);
-    if (NS_FAILED(rv)) {
-        return rv;
-    }
-
-    networkStatsServiceProxy->SaveAppStats(appId,
-                                           mActiveNetwork,
-                                           PR_Now() / 1000,
-                                           mCountRecv,
-                                           0,
-                                           false,
-                                           nullptr);
+    // Create the event to save the network statistics.
+    // the event is then dispathed to the main thread.
+    nsRefPtr<