Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 14 Jun 2015 15:54:58 -0700
changeset 248787 3c26bef95d54
parent 248777 19ba04cd72c2 (current diff)
parent 248786 2a0e025a2251 (diff)
child 248788 3839d9084daf
child 248831 e4738a23d0c4
child 248850 e4053dff4ac3
push id28906
push userphilringnalda@gmail.com
push date2015-06-14 22:55 +0000
treeherdermozilla-central@3c26bef95d54 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone41.0a1
Merge m-i to m-c, a=merge
testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/test.html
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7801,8 +7801,21 @@ nsContentUtils::FirePageShowEvent(nsIDoc
   }
 
   nsCOMPtr<nsIDocument> doc = aItem->GetDocument();
   NS_ASSERTION(doc, "What happened here?");
   if (doc->IsShowing() == aFireIfShowing) {
     doc->OnPageShow(true, aChromeEventHandler);
   }
 }
+
+/* static */
+already_AddRefed<nsPIWindowRoot>
+nsContentUtils::GetWindowRoot(nsIDocument* aDoc)
+{
+  if (aDoc) {
+    nsPIDOMWindow* win = aDoc->GetWindow();
+    if (win) {
+      return win->GetTopWindowRoot();
+    }
+  }
+  return nullptr;
+}
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -94,16 +94,17 @@ class nsPresContext;
 class nsStringBuffer;
 class nsStringHashKey;
 class nsTextFragment;
 class nsView;
 class nsViewportInfo;
 class nsWrapperCache;
 class nsAttrValue;
 class nsITransferable;
+class nsPIWindowRoot;
 
 struct JSPropertyDescriptor;
 struct JSRuntime;
 struct nsIntMargin;
 
 template<class E> class nsCOMArray;
 template<class K, class V> class nsDataHashtable;
 template<class K, class V> class nsRefPtrHashtable;
@@ -2379,16 +2380,18 @@ public:
 
   static void FirePageShowEvent(nsIDocShellTreeItem* aItem,
                                 mozilla::dom::EventTarget* aChromeEventHandler,
                                 bool aFireIfShowing);
 
   static void FirePageHideEvent(nsIDocShellTreeItem* aItem,
                                 mozilla::dom::EventTarget* aChromeEventHandler);
 
+  static already_AddRefed<nsPIWindowRoot> GetWindowRoot(nsIDocument* aDoc);
+
 private:
   static bool InitializeEventTable();
 
   static nsresult EnsureStringBundle(PropertiesFile aFile);
 
   static bool CanCallerAccess(nsIPrincipal* aSubjectPrincipal,
                                 nsIPrincipal* aPrincipal);
 
--- a/dom/base/nsPIWindowRoot.h
+++ b/dom/base/nsPIWindowRoot.h
@@ -4,24 +4,31 @@
  * 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 nsPIWindowRoot_h__
 #define nsPIWindowRoot_h__
 
 #include "nsISupports.h"
 #include "mozilla/dom/EventTarget.h"
+#include "nsWeakReference.h"
 
 class nsPIDOMWindow;
 class nsIControllers;
 class nsIController;
 
+namespace mozilla {
+namespace dom {
+class TabParent;
+}
+}
+
 #define NS_IWINDOWROOT_IID \
-{ 0x728a2682, 0x55c0, 0x4860, \
- { 0x82, 0x6b, 0x0c, 0x30, 0x0a, 0xac, 0xaa, 0x60 } }
+{ 0x238edca0, 0xb30d, 0x46d3, \
+ { 0xb2, 0x6a, 0x17, 0xb6, 0x21, 0x28, 0x89, 0x7e } }
 
 class nsPIWindowRoot : public mozilla::dom::EventTarget
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_IWINDOWROOT_IID)
 
   virtual nsPIDOMWindow* GetWindow()=0;
 
@@ -33,13 +40,22 @@ public:
                                            nsIController** aResult) = 0;
   virtual nsresult GetControllers(nsIControllers** aResult) = 0;
 
   virtual void GetEnabledDisabledCommands(nsTArray<nsCString>& aEnabledCommands,
                                           nsTArray<nsCString>& aDisabledCommands) = 0;
 
   virtual void SetParentTarget(mozilla::dom::EventTarget* aTarget) = 0;
   virtual mozilla::dom::EventTarget* GetParentTarget() = 0;
+
+  // Stores a weak reference to the browser.
+  virtual void AddBrowser(mozilla::dom::TabParent* aBrowser) = 0;
+  virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser) = 0;
+
+  typedef void (*BrowserEnumerator)(mozilla::dom::TabParent* aTab, void* aArg);
+
+  // Enumerate all stored browsers that for which the weak reference is valid.
+  virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIWindowRoot, NS_IWINDOWROOT_IID)
 
 #endif // nsPIWindowRoot_h__
--- a/dom/base/nsWindowRoot.cpp
+++ b/dom/base/nsWindowRoot.cpp
@@ -19,16 +19,17 @@
 #include "nsFocusManager.h"
 #include "nsIContent.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIControllers.h"
 #include "nsIController.h"
 #include "xpcpublic.h"
 #include "nsCycleCollectionParticipant.h"
+#include "mozilla/dom/TabParent.h"
 
 #ifdef MOZ_XUL
 #include "nsIDOMXULElement.h"
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
@@ -380,16 +381,56 @@ nsWindowRoot::GetParentObject()
 }
 
 JSObject*
 nsWindowRoot::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::WindowRootBinding::Wrap(aCx, this, aGivenProto);
 }
 
+void
+nsWindowRoot::AddBrowser(mozilla::dom::TabParent* aBrowser)
+{
+  nsWeakPtr weakBrowser = do_GetWeakReference(static_cast<nsITabParent*>(aBrowser));
+  mWeakBrowsers.PutEntry(weakBrowser);
+}
+
+void
+nsWindowRoot::RemoveBrowser(mozilla::dom::TabParent* aBrowser)
+{
+  nsWeakPtr weakBrowser = do_GetWeakReference(static_cast<nsITabParent*>(aBrowser));
+  mWeakBrowsers.RemoveEntry(weakBrowser);
+}
+
+static PLDHashOperator
+WeakBrowserEnumFunc(nsRefPtrHashKey<nsIWeakReference>* aKey, void* aArg)
+{
+  nsTArray<nsRefPtr<TabParent>>* tabParents =
+    static_cast<nsTArray<nsRefPtr<TabParent>>*>(aArg);
+  nsCOMPtr<nsITabParent> tabParent(do_QueryReferent((*aKey).GetKey()));
+  TabParent* tab = TabParent::GetFrom(tabParent);
+  if (tab) {
+    tabParents->AppendElement(tab);
+  }
+  return PL_DHASH_NEXT;
+}
+
+void
+nsWindowRoot::EnumerateBrowsers(BrowserEnumerator aEnumFunc, void* aArg)
+{
+  // Collect strong references to all browsers in a separate array in
+  // case aEnumFunc alters mWeakBrowsers.
+  nsTArray<nsRefPtr<TabParent>> tabParents;
+  mWeakBrowsers.EnumerateEntries(WeakBrowserEnumFunc, &tabParents);
+
+  for (uint32_t i = 0; i < tabParents.Length(); ++i) {
+    aEnumFunc(tabParents[i], aArg);
+  }
+}
+
 ///////////////////////////////////////////////////////////////////////////////////
 
 already_AddRefed<EventTarget>
 NS_NewWindowRoot(nsPIDOMWindow* aWindow)
 {
   nsCOMPtr<EventTarget> result = new nsWindowRoot(aWindow);
   return result.forget();
 }
--- a/dom/base/nsWindowRoot.h
+++ b/dom/base/nsWindowRoot.h
@@ -64,29 +64,37 @@ public:
 
   nsIGlobalObject* GetParentObject();
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsWindowRoot,
                                                          nsIDOMEventTarget)
 
+  virtual void AddBrowser(mozilla::dom::TabParent* aBrowser) override;
+  virtual void RemoveBrowser(mozilla::dom::TabParent* aBrowser) override;
+  virtual void EnumerateBrowsers(BrowserEnumerator aEnumFunc, void *aArg) override;
+
 protected:
   virtual ~nsWindowRoot();
 
   void GetEnabledDisabledCommandsForControllers(nsIControllers* aControllers,
                                                 nsTHashtable<nsCharPtrHashKey>& aCommandsHandled,
                                                 nsTArray<nsCString>& aEnabledCommands,
                                                 nsTArray<nsCString>& aDisabledCommands);
 
   // Members
   nsCOMPtr<nsPIDOMWindow> mWindow;
   // We own the manager, which owns event listeners attached to us.
   nsRefPtr<mozilla::EventListenerManager> mListenerManager; // [Strong]
   nsCOMPtr<nsIDOMNode> mPopupNode; // [OWNER]
 
   nsCOMPtr<mozilla::dom::EventTarget> mParent;
+
+  // The TabParents that are currently registered with this top-level window.
+  typedef nsTHashtable<nsRefPtrHashKey<nsIWeakReference>> WeakBrowserTable;
+  WeakBrowserTable mWeakBrowsers;
 };
 
 extern already_AddRefed<mozilla::dom::EventTarget>
 NS_NewWindowRoot(nsPIDOMWindow* aWindow);
 
 #endif
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -20,16 +20,17 @@ include JavaScriptTypes;
 include URIParams;
 include BrowserConfiguration;
 
 
 using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
 using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
 using struct gfxSize from "gfxPoint.h";
 using CSSRect from "Units.h";
+using CSSSize from "Units.h";
 using LayoutDeviceIntRect from "Units.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using ScreenIntSize from "Units.h";
 using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using struct mozilla::layers::ZoomConstraints from "FrameMetrics.h";
 using FrameMetrics::ViewID from "FrameMetrics.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
@@ -543,17 +544,17 @@ child:
          uint64_t layersId,
          nullable PRenderFrame renderFrame,
          bool parentIsActive);
 
     LoadURL(nsCString uri, BrowserConfiguration config);
 
     CacheFileDescriptor(nsString path, FileDescriptor fd);
 
-    UpdateDimensions(IntRect rect, ScreenIntSize size, ScreenOrientation orientation,
+    UpdateDimensions(CSSRect rect, CSSSize size, ScreenOrientation orientation,
                      LayoutDeviceIntPoint chromeDisp) compressall;
 
     UpdateFrame(FrameMetrics frame);
 
     // The following methods correspond to functions on the GeckoContentController
     // interface in gfx/layers/apz/public/GeckoContentController.h. Refer to documentation
     // in that file for these functions.
     RequestFlingSnap(ViewID aScrollID, CSSPoint aDestination);
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -87,16 +87,17 @@
 #include "nsDOMClassInfoID.h"
 #include "nsColorPickerProxy.h"
 #include "nsPresShell.h"
 #include "nsIAppsService.h"
 #include "nsNetUtil.h"
 #include "nsIPermissionManager.h"
 #include "nsIScriptError.h"
 #include "mozilla/EventForwards.h"
+#include "nsDeviceContext.h"
 
 #define BROWSER_ELEMENT_CHILD_SCRIPT \
     NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
 
 #define TABC_LOG(...)
 // #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__)
 
 using namespace mozilla;
@@ -160,17 +161,16 @@ UsingCompositorLRU()
 }
 
 NS_IMPL_ISUPPORTS(TabChild::DelayedFireContextMenuEvent,
                   nsITimerCallback)
 
 TabChildBase::TabChildBase()
   : mContentDocumentIsDisplayed(false)
   , mTabChildGlobal(nullptr)
-  , mInnerSize(0, 0)
 {
   mozilla::HoldJSObjects(this);
 }
 
 TabChildBase::~TabChildBase()
 {
   mAnonymousGlobalScopes.Clear();
   mozilla::DropJSObjects(this);
@@ -231,19 +231,21 @@ void
 TabChildBase::InitializeRootMetrics()
 {
   // Calculate a really simple resolution that we probably won't
   // be keeping, as well as putting the scroll offset back to
   // the top-left of the page.
   mLastRootMetrics.SetViewport(CSSRect(CSSPoint(), kDefaultViewportSize));
   mLastRootMetrics.SetCompositionBounds(ParentLayerRect(
       ParentLayerPoint(),
-      ParentLayerSize(ViewAs<ParentLayerPixel>(mInnerSize, PixelCastJustification::ScreenIsParentLayerForRoot))));
+      ParentLayerSize(
+        ViewAs<ParentLayerPixel>(GetInnerSize(),
+                                 PixelCastJustification::ScreenIsParentLayerForRoot))));
   mLastRootMetrics.SetZoom(CSSToParentLayerScale2D(
-      ConvertScaleForRoot(CalculateIntrinsicScale(mInnerSize, kDefaultViewportSize))));
+      ConvertScaleForRoot(CalculateIntrinsicScale(GetInnerSize(), kDefaultViewportSize))));
   mLastRootMetrics.SetDevPixelsPerCSSPixel(WebWidget()->GetDefaultScale());
   // We use ParentLayerToLayerScale(1) below in order to turn the
   // async zoom amount into the gecko zoom amount.
   mLastRootMetrics.SetCumulativeResolution(mLastRootMetrics.GetZoom() / mLastRootMetrics.GetDevPixelsPerCSSPixel() * ParentLayerToLayerScale(1));
   // This is the root layer, so the cumulative resolution is the same
   // as the resolution.
   mLastRootMetrics.SetPresShellResolution(mLastRootMetrics.GetCumulativeResolution().ToScaleFactor().scale);
   mLastRootMetrics.SetScrollOffset(CSSPoint(0, 0));
@@ -294,42 +296,42 @@ bool
 TabChildBase::HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize)
 {
   nsIWidget* widget = WebWidget();
   if (!widget || !widget->AsyncPanZoomEnabled()) {
     return false;
   }
 
   TABC_LOG("HandlePossibleViewportChange aOldScreenSize=%s mInnerSize=%s\n",
-    Stringify(aOldScreenSize).c_str(), Stringify(mInnerSize).c_str());
+    Stringify(aOldScreenSize).c_str(), Stringify(GetInnerSize()).c_str());
 
   nsCOMPtr<nsIDocument> document(GetDocument());
   if (!document) {
     return false;
   }
 
-  nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, mInnerSize);
+  nsViewportInfo viewportInfo = nsContentUtils::GetViewportInfo(document, GetInnerSize());
   uint32_t presShellId = 0;
   mozilla::layers::FrameMetrics::ViewID viewId = FrameMetrics::NULL_SCROLL_ID;
   bool scrollIdentifiersValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
         document->GetDocumentElement(), &presShellId, &viewId);
   if (scrollIdentifiersValid) {
     ZoomConstraints constraints(
       viewportInfo.IsZoomAllowed(),
       viewportInfo.IsDoubleTapZoomAllowed(),
       ConvertScaleForRoot(viewportInfo.GetMinZoom()),
       ConvertScaleForRoot(viewportInfo.GetMaxZoom()));
     DoUpdateZoomConstraints(presShellId,
                             viewId,
                             /* isRoot = */ true,
                             constraints);
   }
 
-  float screenW = mInnerSize.width;
-  float screenH = mInnerSize.height;
+  float screenW = GetInnerSize().width;
+  float screenH = GetInnerSize().height;
   CSSSize viewport(viewportInfo.GetSize());
 
   // We're not being displayed in any way; don't bother doing anything because
   // that will just confuse future adjustments.
   if (!screenW || !screenH) {
     return false;
   }
 
@@ -353,56 +355,58 @@ TabChildBase::HandlePossibleViewportChan
   // window.innerWidth before they are painted have a correct value (bug
   // 771575).
   if (!mContentDocumentIsDisplayed) {
     return false;
   }
 
   ScreenIntSize oldScreenSize = aOldScreenSize;
   if (oldScreenSize == ScreenIntSize()) {
-    oldScreenSize = mInnerSize;
+    oldScreenSize = GetInnerSize();
   }
 
   FrameMetrics metrics(mLastRootMetrics);
   metrics.SetViewport(CSSRect(CSSPoint(), viewport));
 
-  // Calculate the composition bounds based on mInnerSize, excluding the sizes
+  // Calculate the composition bounds based on the inner size, excluding the sizes
   // of the scrollbars if they are not overlay scrollbars.
-  ScreenSize compositionSize(mInnerSize);
+  ScreenSize compositionSize(GetInnerSize());
   nsCOMPtr<nsIPresShell> shell = GetPresShell();
   if (shell) {
     nsMargin scrollbarsAppUnits =
         nsLayoutUtils::ScrollbarAreaToExcludeFromCompositionBoundsFor(shell->GetRootScrollFrame());
     // Scrollbars are not subject to scaling, so CSS pixels = screen pixels for them.
     ScreenMargin scrollbars = CSSMargin::FromAppUnits(scrollbarsAppUnits)
                             * CSSToScreenScale(1.0f);
     compositionSize.width -= scrollbars.LeftRight();
     compositionSize.height -= scrollbars.TopBottom();
   }
 
   metrics.SetCompositionBounds(ParentLayerRect(
       ParentLayerPoint(),
-      ParentLayerSize(ViewAs<ParentLayerPixel>(compositionSize, PixelCastJustification::ScreenIsParentLayerForRoot))));
+      ParentLayerSize(
+        ViewAs<ParentLayerPixel>(GetInnerSize(),
+                                 PixelCastJustification::ScreenIsParentLayerForRoot))));
   metrics.SetRootCompositionSize(
       ScreenSize(compositionSize) * ScreenToLayoutDeviceScale(1.0f) / metrics.GetDevPixelsPerCSSPixel());
 
   // This change to the zoom accounts for all types of changes I can conceive:
   // 1. screen size changes, CSS viewport does not (pages with no meta viewport
   //    or a fixed size viewport)
   // 2. screen size changes, CSS viewport also does (pages with a device-width
   //    viewport)
   // 3. screen size remains constant, but CSS viewport changes (meta viewport
   //    tag is added or removed)
   // 4. neither screen size nor CSS viewport changes
   //
   // In all of these cases, we maintain how much actual content is visible
   // within the screen width. Note that "actual content" may be different with
   // respect to CSS pixels because of the CSS viewport size changing.
   CSSToScreenScale oldIntrinsicScale = CalculateIntrinsicScale(oldScreenSize, oldBrowserSize);
-  CSSToScreenScale newIntrinsicScale = CalculateIntrinsicScale(mInnerSize, viewport);
+  CSSToScreenScale newIntrinsicScale = CalculateIntrinsicScale(GetInnerSize(), viewport);
   metrics.ZoomBy(newIntrinsicScale.scale / oldIntrinsicScale.scale);
 
   // Changing the zoom when we're not doing a first paint will get ignored
   // by AsyncPanZoomController and causes a blurry flash.
   bool isFirstPaint = true;
   if (shell) {
     isFirstPaint = shell->GetIsFirstPaint();
   }
@@ -876,17 +880,16 @@ TabChild::TabChild(nsIContentChild* aMan
                    const TabId& aTabId,
                    const TabContext& aContext,
                    uint32_t aChromeFlags)
   : TabContext(aContext)
   , mRemoteFrame(nullptr)
   , mManager(aManager)
   , mChromeFlags(aChromeFlags)
   , mLayersId(0)
-  , mOuterRect(0, 0, 0, 0)
   , mActivePointerId(-1)
   , mAppPackageFileDescriptorRecved(false)
   , mLastBackgroundColor(NS_RGB(255, 255, 255))
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mUpdateHitRegion(false)
@@ -917,19 +920,19 @@ TabChild::TabChild(nsIContentChild* aMan
 NS_IMETHODIMP
 TabChild::HandleEvent(nsIDOMEvent* aEvent)
 {
   nsAutoString eventType;
   aEvent->GetType(eventType);
   if (eventType.EqualsLiteral("DOMMetaAdded")) {
     // This meta data may or may not have been a meta viewport tag. If it was,
     // we should handle it immediately.
-    HandlePossibleViewportChange(mInnerSize);
+    HandlePossibleViewportChange(GetInnerSize());
   } else if (eventType.EqualsLiteral("FullZoomChange")) {
-    HandlePossibleViewportChange(mInnerSize);
+    HandlePossibleViewportChange(GetInnerSize());
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::Observe(nsISupports *aSubject,
                   const char *aTopic,
@@ -961,24 +964,24 @@ TabChild::Observe(nsISupports *aSubject,
         if (shell) {
           shell->SetIsFirstPaint(true);
         }
 
         mContentDocumentIsDisplayed = true;
 
         // In some cases before-first-paint gets called before
         // RecvUpdateDimensions is called and therefore before we have an
-        // mInnerSize value set. In such cases defer initializing the viewport
+        // inner size value set. In such cases defer initializing the viewport
         // until we we get an inner size.
         if (HasValidInnerSize()) {
           InitializeRootMetrics();
           if (shell) {
             nsLayoutUtils::SetResolutionAndScaleTo(shell, mLastRootMetrics.GetPresShellResolution());
           }
-          HandlePossibleViewportChange(mInnerSize);
+          HandlePossibleViewportChange(GetInnerSize());
         }
       }
     }
   }
 
   return NS_OK;
 }
 
@@ -1308,27 +1311,28 @@ TabChild::SetDimensions(uint32_t aFlags,
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::GetDimensions(uint32_t aFlags, int32_t* aX,
                              int32_t* aY, int32_t* aCx, int32_t* aCy)
 {
+  ScreenIntRect rect = GetOuterRect();
   if (aX) {
-    *aX = mOuterRect.x;
+    *aX = rect.x;
   }
   if (aY) {
-    *aY = mOuterRect.y;
+    *aY = rect.y;
   }
   if (aCx) {
-    *aCx = mOuterRect.width;
+    *aCx = rect.width;
   }
   if (aCy) {
-    *aCy = mOuterRect.height;
+    *aCy = rect.height;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TabChild::SetFocus()
 {
@@ -2045,47 +2049,51 @@ TabChild::RecvShow(const ScreenIntSize& 
     bool res = InitTabChildGlobal();
     ApplyShowInfo(aInfo);
     RecvParentActivated(aParentIsActive);
 
     return res;
 }
 
 bool
-TabChild::RecvUpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size,
-                               const ScreenOrientation& orientation, const LayoutDeviceIntPoint& chromeDisp)
+TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size,
+                               const ScreenOrientation& orientation,
+                               const LayoutDeviceIntPoint& chromeDisp)
 {
     if (!mRemoteFrame) {
         return true;
     }
 
-    mOuterRect = rect;
+    mUnscaledOuterRect = rect;
     mChromeDisp = chromeDisp;
 
     bool initialSizing = !HasValidInnerSize()
                       && (size.width != 0 && size.height != 0);
+
+    mOrientation = orientation;
+    ScreenIntSize oldScreenSize = GetInnerSize();
+    SetUnscaledInnerSize(size);
+    ScreenIntSize screenSize = GetInnerSize();
     bool sizeChanged = true;
     if (initialSizing) {
       mHasValidInnerSize = true;
-    } else if (mInnerSize == size) {
+    } else if (screenSize == oldScreenSize) {
       sizeChanged = false;
     }
 
-    mOrientation = orientation;
-    ScreenIntSize oldScreenSize = mInnerSize;
-    mInnerSize = size;
-    mWidget->Resize(rect.x + chromeDisp.x, rect.y + chromeDisp.y, size.width, size.height,
-                     true);
+    ScreenIntRect screenRect = GetOuterRect();
+    mWidget->Resize(screenRect.x + chromeDisp.x, screenRect.y + chromeDisp.y,
+                    screenSize.width, screenSize.height, true);
 
     nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
-    baseWin->SetPositionAndSize(0, 0, size.width, size.height,
+    baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
                                 true);
 
     if (initialSizing && mContentDocumentIsDisplayed) {
-      // If this is the first time we're getting a valid mInnerSize, and the
+      // If this is the first time we're getting a valid inner size, and the
       // before-first-paint event has already been handled, then we need to set
       // up our default viewport here. See the corresponding call to
       // InitializeRootMetrics in the before-first-paint handler.
       InitializeRootMetrics();
     }
 
     if (sizeChanged) {
       HandlePossibleViewportChange(oldScreenSize);
@@ -3239,27 +3247,40 @@ TabChild::RecvRequestNotifyAfterRemotePa
   // RenderFrameParent.
   compositor->RequestNotifyAfterRemotePaint(this);
   return true;
 }
 
 bool
 TabChild::RecvUIResolutionChanged()
 {
+  ScreenIntSize oldScreenSize = GetInnerSize();
   mDPI = 0;
   mDefaultScale = 0;
   static_cast<PuppetWidget*>(mWidget.get())->ClearBackingScaleCache();
   nsCOMPtr<nsIDocument> document(GetDocument());
   nsCOMPtr<nsIPresShell> presShell = document->GetShell();
   if (presShell) {
     nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
     if (presContext) {
-      presContext->UIResolutionChanged();
+      presContext->UIResolutionChangedSync();
     }
   }
+
+  ScreenIntSize screenSize = GetInnerSize();
+  if (mHasValidInnerSize && oldScreenSize != screenSize) {
+    ScreenIntRect screenRect = GetOuterRect();
+    mWidget->Resize(screenRect.x + mChromeDisp.x, screenRect.y + mChromeDisp.y,
+                    screenSize.width, screenSize.height, true);
+
+    nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
+    baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
+                                true);
+  }
+
   return true;
 }
 
 bool
 TabChild::RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache)
 {
   LookAndFeel::SetIntCache(aLookAndFeelIntCache);
   nsCOMPtr<nsIDocument> document(GetDocument());
@@ -3311,16 +3332,32 @@ TabChild::CreatePluginWidget(nsIWidget* 
                                      nsIntSize(0, 0)), &initData);
   if (NS_FAILED(rv)) {
     NS_WARNING("Creating native plugin widget on the chrome side failed.");
   }
   pluginWidget.forget(aOut);
   return rv;
 }
 
+ScreenIntSize
+TabChild::GetInnerSize()
+{
+  LayoutDeviceIntSize innerSize =
+    RoundedToInt(mUnscaledInnerSize * mWidget->GetDefaultScale());
+  return ViewAs<ScreenPixel>(innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+};
+
+ScreenIntRect
+TabChild::GetOuterRect()
+{
+  LayoutDeviceIntRect outerRect =
+    RoundedToInt(mUnscaledOuterRect * mWidget->GetDefaultScale());
+  return ViewAs<ScreenPixel>(outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+}
+
 TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild)
 : mTabChild(aTabChild)
 {
   SetIsNotDOMBinding();
 }
 
 TabChildGlobal::~TabChildGlobal()
 {
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -184,16 +184,18 @@ public:
     // change, this function doesn't do anything.  However, it should
     // not be called all the time as it is fairly expensive.
     bool HandlePossibleViewportChange(const ScreenIntSize& aOldScreenSize);
     virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId,
                                          const mozilla::layers::FrameMetrics::ViewID& aViewId,
                                          const bool& aIsRoot,
                                          const mozilla::layers::ZoomConstraints& aConstraints) = 0;
 
+    virtual ScreenIntSize GetInnerSize() = 0;
+
 protected:
     virtual ~TabChildBase();
     CSSSize GetPageSize(nsCOMPtr<nsIDocument> aDocument, const CSSSize& aViewport);
 
     // Get the DOMWindowUtils for the top-level window in this tab.
     already_AddRefed<nsIDOMWindowUtils> GetDOMWindowUtils();
     // Get the Document for the top-level window in this tab.
     already_AddRefed<nsIDocument> GetDocument() const;
@@ -217,17 +219,16 @@ protected:
     mozilla::layers::FrameMetrics ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics);
 
     bool UpdateFrameHandler(const mozilla::layers::FrameMetrics& aFrameMetrics);
 
 protected:
     CSSSize mOldViewportSize;
     bool mContentDocumentIsDisplayed;
     nsRefPtr<TabChildGlobal> mTabChildGlobal;
-    ScreenIntSize mInnerSize;
     mozilla::layers::FrameMetrics mLastRootMetrics;
     nsCOMPtr<nsIWebBrowserChrome3> mWebBrowserChrome;
 };
 
 class TabChild final : public TabChildBase,
                        public PBrowserChild,
                        public nsIWebBrowserChrome2,
                        public nsIEmbeddingSiteWindow,
@@ -314,18 +315,18 @@ public:
                                          const FileDescriptor& aFileDescriptor)
                                          override;
     virtual bool RecvShow(const ScreenIntSize& aSize,
                           const ShowInfo& aInfo,
                           const TextureFactoryIdentifier& aTextureFactoryIdentifier,
                           const uint64_t& aLayersId,
                           PRenderFrameChild* aRenderFrame,
                           const bool& aParentIsActive) override;
-    virtual bool RecvUpdateDimensions(const nsIntRect& rect,
-                                      const ScreenIntSize& size,
+    virtual bool RecvUpdateDimensions(const CSSRect& rect,
+                                      const CSSSize& size,
                                       const ScreenOrientation& orientation,
                                       const LayoutDeviceIntPoint& chromeDisp) override;
     virtual bool RecvUpdateFrame(const layers::FrameMetrics& aFrameMetrics) override;
     virtual bool RecvRequestFlingSnap(const ViewID& aScrollId,
                                       const CSSPoint& aDestination) override;
     virtual bool RecvAcknowledgeScrollUpdate(const ViewID& aScrollId,
                                              const uint32_t& aScrollGeneration) override;
     virtual bool RecvHandleDoubleTap(const CSSPoint& aPoint,
@@ -505,16 +506,18 @@ public:
     bool IPCOpen() { return mIPCOpen; }
 
     bool ParentIsActive()
     {
       return mParentIsActive;
     }
     bool AsyncPanZoomEnabled() { return mAsyncPanZoomEnabled; }
 
+    virtual ScreenIntSize GetInnerSize() override;
+
 protected:
     virtual ~TabChild();
 
     virtual PRenderFrameChild* AllocPRenderFrameChild() override;
     virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
     virtual bool RecvDestroy() override;
     virtual bool RecvSetUpdateHitRegion(const bool& aEnabled) override;
     virtual bool RecvSetIsDocShellActive(const bool& aIsActive) override;
@@ -594,29 +597,35 @@ private:
 
     bool HasValidInnerSize();
 
     // Get the pres shell resolution of the document in this tab.
     float GetPresShellResolution() const;
 
     void SetTabId(const TabId& aTabId);
 
+    ScreenIntRect GetOuterRect();
+
+    void SetUnscaledInnerSize(const CSSSize& aSize) {
+      mUnscaledInnerSize = aSize;
+    }
+
     class CachedFileDescriptorInfo;
     class CachedFileDescriptorCallbackRunnable;
     class DelayedDeleteRunnable;
 
     TextureFactoryIdentifier mTextureFactoryIdentifier;
     nsCOMPtr<nsIWebNavigation> mWebNav;
     nsCOMPtr<nsIWidget> mWidget;
     nsCOMPtr<nsIURI> mLastURI;
     RenderFrameChild* mRemoteFrame;
     nsRefPtr<nsIContentChild> mManager;
     uint32_t mChromeFlags;
     uint64_t mLayersId;
-    nsIntRect mOuterRect;
+    CSSRect mUnscaledOuterRect;
     // When we're tracking a possible tap gesture, this is the "down"
     // point of the touchstart.
     LayoutDevicePoint mGestureDownPoint;
     // The touch identifier of the active gesture.
     int32_t mActivePointerId;
     // A timer task that fires if the tap-hold timeout is exceeded by
     // the touch we're tracking.  That is, if touchend or a touchmove
     // that exceeds the gesture threshold doesn't happen.
@@ -641,16 +650,17 @@ private:
     // Position of tab, relative to parent widget (typically the window)
     LayoutDeviceIntPoint mChromeDisp;
     TabId mUniqueId;
     float mDPI;
     double mDefaultScale;
     bool mIPCOpen;
     bool mParentIsActive;
     bool mAsyncPanZoomEnabled;
+    CSSSize mUnscaledInnerSize;
 
     DISALLOW_EVIL_CONSTRUCTORS(TabChild);
 };
 
 }
 }
 
 #endif // mozilla_dom_TabChild_h
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -85,16 +85,17 @@
 #include "SourceSurfaceRawData.h"
 #include "nsAuthInformationHolder.h"
 #include "nsICancelable.h"
 #include "gfxPrefs.h"
 #include "nsILoginManagerPrompter.h"
 #include "nsPIWindowRoot.h"
 #include "gfxDrawable.h"
 #include "ImageOps.h"
+#include "UnitTransforms.h"
 #include <algorithm>
 
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 using namespace mozilla::layers;
 using namespace mozilla::layout;
 using namespace mozilla::services;
 using namespace mozilla::widget;
@@ -328,19 +329,37 @@ TabParent::CacheFrameLoader(nsFrameLoade
 }
 
 void
 TabParent::SetOwnerElement(Element* aElement)
 {
   // If we held previous content then unregister for its events.
   RemoveWindowListeners();
 
+  // If we change top-level documents then we need to change our
+  // registration with them.
+  nsRefPtr<nsPIWindowRoot> curTopLevelWin, newTopLevelWin;
+  if (mFrameElement) {
+    curTopLevelWin = nsContentUtils::GetWindowRoot(mFrameElement->OwnerDoc());
+  }
+  if (aElement) {
+    newTopLevelWin = nsContentUtils::GetWindowRoot(aElement->OwnerDoc());
+  }
+  bool isSameTopLevelWin = curTopLevelWin == newTopLevelWin;
+  if (curTopLevelWin && !isSameTopLevelWin) {
+    curTopLevelWin->RemoveBrowser(this);
+  }
+
   // Update to the new content, and register to listen for events from it.
   mFrameElement = aElement;
 
+  if (newTopLevelWin && !isSameTopLevelWin) {
+    newTopLevelWin->AddBrowser(this);
+  }
+
   AddWindowListeners();
   TryCacheDPIAndScale();
 }
 
 void
 TabParent::AddWindowListeners()
 {
   if (mFrameElement && mFrameElement->OwnerDoc()) {
@@ -946,33 +965,46 @@ TabParent::UpdateDimensions(const nsIntR
     return;
   }
   hal::ScreenConfiguration config;
   hal::GetCurrentScreenConfiguration(&config);
   ScreenOrientation orientation = config.orientation();
   LayoutDeviceIntPoint chromeOffset = -GetChildProcessOffset();
 
   nsCOMPtr<nsIWidget> widget = GetWidget();
+  if (!widget) {
+    NS_WARNING("No widget found in TabParent::UpdateDimensions");
+    return;
+  }
   nsIntRect contentRect = rect;
-  if (widget) {
-    contentRect.x += widget->GetClientOffset().x;
-    contentRect.y += widget->GetClientOffset().y;
-  }
+  contentRect.x += widget->GetClientOffset().x;
+  contentRect.y += widget->GetClientOffset().y;
 
   if (!mUpdatedDimensions || mOrientation != orientation ||
       mDimensions != size || !mRect.IsEqualEdges(contentRect) ||
       chromeOffset != mChromeOffset) {
 
     mUpdatedDimensions = true;
     mRect = contentRect;
     mDimensions = size;
     mOrientation = orientation;
     mChromeOffset = chromeOffset;
 
-    unused << SendUpdateDimensions(mRect, mDimensions, mOrientation, mChromeOffset);
+    CSSToLayoutDeviceScale widgetScale = widget->GetDefaultScale();
+
+    LayoutDeviceIntRect devicePixelRect =
+      ViewAs<LayoutDevicePixel>(mRect,
+                                PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+    LayoutDeviceIntSize devicePixelSize =
+      ViewAs<LayoutDevicePixel>(mDimensions.ToUnknownSize(),
+                                PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+
+    CSSRect unscaledRect = devicePixelRect / widgetScale;
+    CSSSize unscaledSize = devicePixelSize / widgetScale;
+    unused << SendUpdateDimensions(unscaledRect, unscaledSize, orientation, chromeOffset);
   }
 }
 
 void
 TabParent::UpdateFrame(const FrameMetrics& aFrameMetrics)
 {
   if (!mIsDestroyed) {
     unused << SendUpdateFrame(aFrameMetrics);
@@ -981,16 +1013,17 @@ TabParent::UpdateFrame(const FrameMetric
 
 void
 TabParent::UIResolutionChanged()
 {
   if (!mIsDestroyed) {
     // TryCacheDPIAndScale()'s cache is keyed off of
     // mDPI being greater than 0, so this invalidates it.
     mDPI = -1;
+    TryCacheDPIAndScale();
     unused << SendUIResolutionChanged();
   }
 }
 
 void
 TabParent::ThemeChanged()
 {
   if (!mIsDestroyed) {
--- a/dom/media/webspeech/recognition/test/mochitest.ini
+++ b/dom/media/webspeech/recognition/test/mochitest.ini
@@ -7,17 +7,17 @@ support-files =
   silence.ogg
   silence.ogg^headers^
 
 [test_abort.html]
 skip-if = toolkit == 'android' || toolkit == 'gonk' # bug 1037287
 [test_audio_capture_error.html]
 [test_call_start_from_end_handler.html]
 tags=capturestream
-skip-if = (android_version == '18' && debug) # bug 967606
+skip-if = ((android_version == '18' || buildapp == 'b2g') && debug) # bug 967606
 [test_nested_eventloop.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(showmodaldialog)
 [test_preference_enable.html]
 [test_recognition_service_error.html]
 skip-if = buildapp == 'b2g' # b2g(timed out)
 [test_success_without_recognition_service.html]
 [test_timeout.html]
 skip-if = os == "win"
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -40,16 +40,22 @@ js::RuntimeFromMainThreadIsHeapMajorColl
 
 bool
 js::CurrentThreadIsIonCompiling()
 {
     return TlsPerThreadData.get()->ionCompiling;
 }
 
 bool
+js::CurrentThreadIsIonCompilingSafeForMinorGC()
+{
+    return TlsPerThreadData.get()->ionCompilingSafeForMinorGC;
+}
+
+bool
 js::CurrentThreadIsGCSweeping()
 {
     return js::TlsPerThreadData.get()->gcSweeping;
 }
 
 bool
 js::CurrentThreadIsHandlingInitFailure()
 {
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -206,16 +206,19 @@ class JitCode;
 
 #ifdef DEBUG
 // Barriers can't be triggered during backend Ion compilation, which may run on
 // a helper thread.
 bool
 CurrentThreadIsIonCompiling();
 
 bool
+CurrentThreadIsIonCompilingSafeForMinorGC();
+
+bool
 CurrentThreadIsGCSweeping();
 
 bool
 CurrentThreadIsHandlingInitFailure();
 #endif
 
 namespace gc {
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1810,18 +1810,22 @@ js::gc::StoreBuffer::WholeCellEdges::tra
         // capture any tenured->nursery edges in the expando as well.
         if (object->is<UnboxedPlainObject>()) {
             if (UnboxedExpandoObject* expando = object->as<UnboxedPlainObject>().maybeExpando())
                 expando->traceChildren(&mover);
         }
 
         return;
     }
-    MOZ_ASSERT(kind == JS::TraceKind::JitCode);
-    static_cast<jit::JitCode*>(edge)->traceChildren(&mover);
+    if (kind == JS::TraceKind::Script)
+        static_cast<JSScript*>(edge)->traceChildren(&mover);
+    else if (kind == JS::TraceKind::JitCode)
+        static_cast<jit::JitCode*>(edge)->traceChildren(&mover);
+    else
+        MOZ_CRASH();
 }
 
 void
 js::gc::StoreBuffer::CellPtrEdge::trace(TenuringTracer& mover) const
 {
     if (!*edge)
         return;
 
@@ -1873,21 +1877,21 @@ js::TenuringTracer::moveToTenured(JSObje
 
 void
 js::Nursery::collectToFixedPoint(TenuringTracer& mover, TenureCountCache& tenureCounts)
 {
     for (RelocationOverlay* p = mover.head; p; p = p->next()) {
         JSObject* obj = static_cast<JSObject*>(p->forwardingAddress());
         mover.traceObject(obj);
 
-        TenureCount& entry = tenureCounts.findEntry(obj->group());
-        if (entry.group == obj->group()) {
+        TenureCount& entry = tenureCounts.findEntry(obj->groupRaw());
+        if (entry.group == obj->groupRaw()) {
             entry.count++;
         } else if (!entry.group) {
-            entry.group = obj->group();
+            entry.group = obj->groupRaw();
             entry.count = 1;
         }
     }
 }
 
 struct TenuringFunctor
 {
     template <typename T>
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -416,16 +416,24 @@ js::Nursery::collect(JSRuntime* rt, JS::
     AutoStopVerifyingBarriers av(rt, false);
     AutoDisableProxyCheck disableStrictProxyChecking(rt);
     mozilla::DebugOnly<AutoEnterOOMUnsafeRegion> oomUnsafeRegion;
 
     // Move objects pointed to by roots from the nursery to the major heap.
     TenuringTracer mover(rt, this);
 
     // Mark the store buffer. This must happen first.
+
+    TIME_START(cancelIonCompilations);
+    if (sb.cancelIonCompilations()) {
+        for (CompartmentsIter c(rt, SkipAtoms); !c.done(); c.next())
+            jit::StopAllOffThreadCompilations(c);
+    }
+    TIME_END(cancelIonCompilations);
+
     TIME_START(traceValues);
     sb.traceValues(mover);
     TIME_END(traceValues);
 
     TIME_START(traceCells);
     sb.traceCells(mover);
     TIME_END(traceCells);
 
@@ -550,21 +558,22 @@ js::Nursery::collect(JSRuntime* rt, JS::
         if (!printedHeader) {
             fprintf(stderr,
                     "MinorGC: Reason               PRate  Size Time   mkVals mkClls mkSlts mkWCll mkRVal mkRCll mkGnrc ckTbls mkRntm mkDbgr clrNOC collct swpABO updtIn runFin frSlts clrSB  sweep resize pretnr\n");
             printedHeader = true;
         }
 
 #define FMT " %6" PRIu64
         fprintf(stderr,
-                "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
+                "MinorGC: %20s %5.1f%% %4d" FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT FMT "\n",
                 js::gcstats::ExplainReason(reason),
                 promotionRate * 100,
                 numActiveChunks_,
                 totalTime,
+                TIME_TOTAL(cancelIonCompilations),
                 TIME_TOTAL(traceValues),
                 TIME_TOTAL(traceCells),
                 TIME_TOTAL(traceSlots),
                 TIME_TOTAL(traceWholeCells),
                 TIME_TOTAL(traceRelocatableValues),
                 TIME_TOTAL(traceRelocatableCells),
                 TIME_TOTAL(traceGenericEntries),
                 TIME_TOTAL(checkHashTables),
--- a/js/src/gc/StoreBuffer.cpp
+++ b/js/src/gc/StoreBuffer.cpp
@@ -68,16 +68,17 @@ StoreBuffer::disable()
 
 bool
 StoreBuffer::clear()
 {
     if (!enabled_)
         return true;
 
     aboutToOverflow_ = false;
+    cancelIonCompilations_ = false;
 
     bufferVal.clear();
     bufferCell.clear();
     bufferSlot.clear();
     bufferWholeCell.clear();
     bufferRelocVal.clear();
     bufferRelocCell.clear();
     bufferGeneric.clear();
--- a/js/src/gc/StoreBuffer.h
+++ b/js/src/gc/StoreBuffer.h
@@ -379,42 +379,45 @@ class StoreBuffer
 
     MonoTypeBuffer<ValueEdge> bufferVal;
     MonoTypeBuffer<CellPtrEdge> bufferCell;
     MonoTypeBuffer<SlotsEdge> bufferSlot;
     MonoTypeBuffer<WholeCellEdges> bufferWholeCell;
     MonoTypeBuffer<ValueEdge> bufferRelocVal;
     MonoTypeBuffer<CellPtrEdge> bufferRelocCell;
     GenericBuffer bufferGeneric;
+    bool cancelIonCompilations_;
 
     JSRuntime* runtime_;
     const Nursery& nursery_;
 
     bool aboutToOverflow_;
     bool enabled_;
     mozilla::DebugOnly<bool> mEntered; /* For ReentrancyGuard. */
 
   public:
     explicit StoreBuffer(JSRuntime* rt, const Nursery& nursery)
       : bufferVal(), bufferCell(), bufferSlot(), bufferWholeCell(),
-        bufferRelocVal(), bufferRelocCell(), bufferGeneric(),
+        bufferRelocVal(), bufferRelocCell(), bufferGeneric(), cancelIonCompilations_(false),
         runtime_(rt), nursery_(nursery), aboutToOverflow_(false), enabled_(false),
         mEntered(false)
     {
     }
 
     bool enable();
     void disable();
     bool isEnabled() const { return enabled_; }
 
     bool clear();
 
     /* Get the overflowed status. */
     bool isAboutToOverflow() const { return aboutToOverflow_; }
 
+    bool cancelIonCompilations() const { return cancelIonCompilations_; }
+
     /* Insert a single edge into the buffer/remembered set. */
     void putValueFromAnyThread(JS::Value* valuep) { putFromAnyThread(bufferVal, ValueEdge(valuep)); }
     void putCellFromAnyThread(Cell** cellp) { putFromAnyThread(bufferCell, CellPtrEdge(cellp)); }
     void putSlotFromAnyThread(NativeObject* obj, int kind, int32_t start, int32_t count) {
         putFromAnyThread(bufferSlot, SlotsEdge(obj, kind, start, count));
     }
     void putWholeCellFromMainThread(Cell* cell) {
         MOZ_ASSERT(cell->isTenured());
@@ -440,16 +443,20 @@ class StoreBuffer
     void putGeneric(const T& t) { putFromAnyThread(bufferGeneric, t);}
 
     /* Insert or update a callback entry. */
     template <typename Key>
     void putCallback(void (*callback)(JSTracer* trc, Key* key, void* data), Key* key, void* data) {
         putFromAnyThread(bufferGeneric, CallbackRef<Key>(callback, key, data));
     }
 
+    void setShouldCancelIonCompilations() {
+        cancelIonCompilations_ = true;
+    }
+
     /* Methods to trace the source of all edges in the store buffer. */
     void traceValues(TenuringTracer& mover)            { bufferVal.trace(this, mover); }
     void traceCells(TenuringTracer& mover)             { bufferCell.trace(this, mover); }
     void traceSlots(TenuringTracer& mover)             { bufferSlot.trace(this, mover); }
     void traceWholeCells(TenuringTracer& mover)        { bufferWholeCell.trace(this, mover); }
     void traceRelocatableValues(TenuringTracer& mover) { bufferRelocVal.trace(this, mover); }
     void traceRelocatableCells(TenuringTracer& mover)  { bufferRelocCell.trace(this, mover); }
     void traceGenericEntries(JSTracer *trc)            { bufferGeneric.trace(this, trc); }
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -2231,29 +2231,16 @@ CodeGenerator::visitPointer(LPointer* li
 {
     if (lir->kind() == LPointer::GC_THING)
         masm.movePtr(ImmGCPtr(lir->gcptr()), ToRegister(lir->output()));
     else
         masm.movePtr(ImmPtr(lir->ptr()), ToRegister(lir->output()));
 }
 
 void
-CodeGenerator::visitNurseryObject(LNurseryObject* lir)
-{
-    Register output = ToRegister(lir->output());
-    uint32_t index = lir->mir()->index();
-
-    // Store a dummy JSObject pointer. We will fix it up on the main thread,
-    // in JitCode::fixupNurseryObjects. The low bit is set to distinguish
-    // it from a real JSObject pointer.
-    JSObject* ptr = reinterpret_cast<JSObject*>((uintptr_t(index) << 1) | 1);
-    masm.movePtr(ImmGCPtr(IonNurseryPtr(ptr)), output);
-}
-
-void
 CodeGenerator::visitKeepAliveObject(LKeepAliveObject* lir)
 {
     // No-op.
 }
 
 void
 CodeGenerator::visitSlots(LSlots* lir)
 {
@@ -3610,21 +3597,26 @@ CodeGenerator::generateArgumentsChecks(b
         } else {
             Label success;
             masm.jump(&success);
             masm.bind(&miss);
 
             // Check for cases where the type set guard might have missed due to
             // changing object groups.
             for (uint32_t i = info.startArgSlot(); i < info.endArgSlot(); i++) {
+                MParameter* param = rp->getOperand(i)->toParameter();
+                const TemporaryTypeSet* types = param->resultTypeSet();
+                if (!types || types->unknown())
+                    continue;
+
                 Label skip;
                 Address addr(StackPointer, ArgToStackOffset((i - info.startArgSlot()) * sizeof(Value)));
                 masm.branchTestObject(Assembler::NotEqual, addr, &skip);
                 Register obj = masm.extractObject(addr, temp);
-                masm.guardTypeSetMightBeIncomplete(obj, temp, &success);
+                masm.guardTypeSetMightBeIncomplete(types, obj, temp, &success);
                 masm.bind(&skip);
             }
 
             masm.assumeUnreachable("Argument check fail.");
             masm.bind(&success);
         }
     }
 }
@@ -3844,17 +3836,17 @@ CodeGenerator::branchIfInvalidated(Regis
     // If IonScript::invalidationCount_ != 0, the script has been invalidated.
     masm.branch32(Assembler::NotEqual,
                   Address(temp, IonScript::offsetOfInvalidationCount()),
                   Imm32(0),
                   invalidated);
 }
 
 void
-CodeGenerator::emitAssertObjectOrStringResult(Register input, MIRType type, TemporaryTypeSet* typeset)
+CodeGenerator::emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset)
 {
     MOZ_ASSERT(type == MIRType_Object || type == MIRType_ObjectOrNull ||
                type == MIRType_String || type == MIRType_Symbol);
 
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(input);
 
     Register temp = regs.takeAny();
@@ -3874,17 +3866,17 @@ CodeGenerator::emitAssertObjectOrStringR
             masm.branchPtr(Assembler::Equal, input, ImmWord(0), &ok);
         if (typeset->getObjectCount() > 0)
             masm.guardObjectType(input, typeset, temp, &miss);
         else
             masm.jump(&miss);
         masm.jump(&ok);
 
         masm.bind(&miss);
-        masm.guardTypeSetMightBeIncomplete(input, temp, &ok);
+        masm.guardTypeSetMightBeIncomplete(typeset, input, temp, &ok);
 
         masm.assumeUnreachable("MIR instruction returned object with unexpected type");
 
         masm.bind(&ok);
     }
 
     // Check that we have a valid GC pointer.
     saveVolatile();
@@ -3914,17 +3906,17 @@ CodeGenerator::emitAssertObjectOrStringR
     masm.callWithABI(callee);
     restoreVolatile();
 
     masm.bind(&done);
     masm.pop(temp);
 }
 
 void
-CodeGenerator::emitAssertResultV(const ValueOperand input, TemporaryTypeSet* typeset)
+CodeGenerator::emitAssertResultV(const ValueOperand input, const TemporaryTypeSet* typeset)
 {
     AllocatableGeneralRegisterSet regs(GeneralRegisterSet::All());
     regs.take(input);
 
     Register temp1 = regs.takeAny();
     Register temp2 = regs.takeAny();
     masm.push(temp1);
     masm.push(temp2);
@@ -3942,17 +3934,17 @@ CodeGenerator::emitAssertResultV(const V
 
         masm.bind(&miss);
 
         // Check for cases where the type set guard might have missed due to
         // changing object groups.
         Label realMiss;
         masm.branchTestObject(Assembler::NotEqual, input, &realMiss);
         Register payload = masm.extractObject(input, temp1);
-        masm.guardTypeSetMightBeIncomplete(payload, temp1, &ok);
+        masm.guardTypeSetMightBeIncomplete(typeset, payload, temp1, &ok);
         masm.bind(&realMiss);
 
         masm.assumeUnreachable("MIR instruction returned value with unexpected type");
 
         masm.bind(&ok);
     }
 
     // Check that we have a valid GC pointer.
@@ -8067,24 +8059,30 @@ CodeGenerator::link(JSContext* cx, Compi
         ionScript->copyBailoutTable(&bailouts_[0]);
     if (osiIndices_.length())
         ionScript->copyOsiIndices(&osiIndices_[0], masm);
     if (snapshots_.listSize())
         ionScript->copySnapshots(&snapshots_);
     MOZ_ASSERT_IF(snapshots_.listSize(), recovers_.size());
     if (recovers_.size())
         ionScript->copyRecovers(&recovers_);
-    if (graph.numConstants())
-        ionScript->copyConstants(graph.constantPool());
+    if (graph.numConstants()) {
+        const Value* vp = graph.constantPool();
+        ionScript->copyConstants(vp);
+        for (size_t i = 0; i < graph.numConstants(); i++) {
+            const Value& v = vp[i];
+            if (v.isObject() && IsInsideNursery(&v.toObject())) {
+                cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(script);
+                break;
+            }
+        }
+    }
     if (patchableBackedges_.length() > 0)
         ionScript->copyPatchableBackedges(cx, code, patchableBackedges_.begin(), masm);
 
-    // Replace dummy JSObject pointers embedded by LNurseryObject.
-    code->fixupNurseryObjects(cx, gen->nurseryObjects());
-
     // The correct state for prebarriers is unknown until the end of compilation,
     // since a GC can occur during code generation. All barriers are emitted
     // off-by-default, and are toggled on here if necessary.
     if (cx->zone()->needsIncrementalBarrier())
         ionScript->toggleBarriers(true);
 
     // Attach any generated script counts to the script.
     if (IonScriptCounts* counts = extractScriptCounts())
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -104,17 +104,16 @@ class CodeGenerator : public CodeGenerat
     void visitOutOfLineRegExpTest(OutOfLineRegExpTest* ool);
     void visitRegExpReplace(LRegExpReplace* lir);
     void visitStringReplace(LStringReplace* lir);
     void visitLambda(LLambda* lir);
     void visitOutOfLineLambdaArrow(OutOfLineLambdaArrow* ool);
     void visitLambdaArrow(LLambdaArrow* lir);
     void visitLambdaForSingleton(LLambdaForSingleton* lir);
     void visitPointer(LPointer* lir);
-    void visitNurseryObject(LNurseryObject* lir);
     void visitKeepAliveObject(LKeepAliveObject* lir);
     void visitSlots(LSlots* lir);
     void visitLoadSlotT(LLoadSlotT* lir);
     void visitLoadSlotV(LLoadSlotV* lir);
     void visitStoreSlotT(LStoreSlotT* lir);
     void visitStoreSlotV(LStoreSlotV* lir);
     void visitElements(LElements* lir);
     void visitConvertElementsToDoubles(LConvertElementsToDoubles* lir);
@@ -366,18 +365,18 @@ class CodeGenerator : public CodeGenerat
 
     void visitAssertRangeI(LAssertRangeI* ins);
     void visitAssertRangeD(LAssertRangeD* ins);
     void visitAssertRangeF(LAssertRangeF* ins);
     void visitAssertRangeV(LAssertRangeV* ins);
 
     void visitAssertResultV(LAssertResultV* ins);
     void visitAssertResultT(LAssertResultT* ins);
-    void emitAssertResultV(const ValueOperand output, TemporaryTypeSet* typeset);
-    void emitAssertObjectOrStringResult(Register input, MIRType type, TemporaryTypeSet* typeset);
+    void emitAssertResultV(const ValueOperand output, const TemporaryTypeSet* typeset);
+    void emitAssertObjectOrStringResult(Register input, MIRType type, const TemporaryTypeSet* typeset);
 
     void visitInterruptCheck(LInterruptCheck* lir);
     void visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir);
     void visitRecompileCheck(LRecompileCheck* ins);
 
     IonScriptCounts* extractScriptCounts() {
         IonScriptCounts* counts = scriptCounts_;
         scriptCounts_ = nullptr;  // prevent delete in dtor
--- a/js/src/jit/CompileWrappers.cpp
+++ b/js/src/jit/CompileWrappers.cpp
@@ -184,16 +184,23 @@ CompileRuntime::maybeGetMathCache()
 }
 
 const Nursery&
 CompileRuntime::gcNursery()
 {
     return runtime()->gc.nursery;
 }
 
+void
+CompileRuntime::setMinorGCShouldCancelIonCompilations()
+{
+    MOZ_ASSERT(onMainThread());
+    runtime()->gc.storeBuffer.setShouldCancelIonCompilations();
+}
+
 Zone*
 CompileZone::zone()
 {
     return reinterpret_cast<Zone*>(this);
 }
 
 /* static */ CompileZone*
 CompileZone::get(Zone* zone)
--- a/js/src/jit/CompileWrappers.h
+++ b/js/src/jit/CompileWrappers.h
@@ -80,16 +80,17 @@ class CompileRuntime
 #endif
 
     // DOM callbacks must be threadsafe (and will hopefully be removed soon).
     const DOMCallbacks* DOMcallbacks();
 
     const MathCache* maybeGetMathCache();
 
     const Nursery& gcNursery();
+    void setMinorGCShouldCancelIonCompilations();
 };
 
 class CompileZone
 {
     Zone* zone();
 
   public:
     static CompileZone* get(Zone* zone);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -163,18 +163,17 @@ JitRuntime::JitRuntime()
     argumentsRectifierReturnAddr_(nullptr),
     invalidator_(nullptr),
     debugTrapHandler_(nullptr),
     baselineDebugModeOSRHandler_(nullptr),
     functionWrappers_(nullptr),
     osrTempData_(nullptr),
     mutatingBackedgeList_(false),
     ionReturnOverride_(MagicValue(JS_ARG_POISON)),
-    jitcodeGlobalTable_(nullptr),
-    hasIonNurseryObjects_(false)
+    jitcodeGlobalTable_(nullptr)
 {
 }
 
 JitRuntime::~JitRuntime()
 {
     js_delete(functionWrappers_);
     freeOsrTempData();
 
@@ -637,19 +636,20 @@ JitCompartment::mark(JSTracer* trc, JSCo
 {
     // Free temporary OSR buffer.
     trc->runtime()->jitRuntime()->freeOsrTempData();
 }
 
 void
 JitCompartment::sweep(FreeOp* fop, JSCompartment* compartment)
 {
-    // Cancel any active or pending off thread compilations. Note that the
-    // MIR graph does not hold any nursery pointers, so there's no need to
-    // do this for minor GCs.
+    // Cancel any active or pending off thread compilations. The MIR graph only
+    // contains nursery pointers if cancelIonCompilations() is set on the store
+    // buffer, in which case store buffer marking will take care of this during
+    // minor GCs.
     MOZ_ASSERT(!fop->runtime()->isHeapMinorCollecting());
     CancelOffThreadIonCompile(compartment, nullptr);
     FinishAllOffThreadCompilations(compartment);
 
     stubCodes_->sweep(fop);
 
     // If the sweep removed the ICCall_Fallback stub, nullptr the baselineCallReturnAddr_ field.
     if (!stubCodes_->lookup(ICCall_Fallback::Compiler::BASELINE_CALL_KEY))
@@ -781,29 +781,16 @@ JitCode::traceChildren(JSTracer* trc)
     if (dataRelocTableBytes_) {
         uint8_t* start = code_ + dataRelocTableOffset();
         CompactBufferReader reader(start, start + dataRelocTableBytes_);
         MacroAssembler::TraceDataRelocations(trc, this, reader);
     }
 }
 
 void
-JitCode::fixupNurseryObjects(JSContext* cx, const ObjectVector& nurseryObjects)
-{
-    if (nurseryObjects.empty() || !dataRelocTableBytes_)
-        return;
-
-    AutoWritableJitCode awjc(this);
-
-    uint8_t* start = code_ + dataRelocTableOffset();
-    CompactBufferReader reader(start, start + dataRelocTableBytes_);
-    MacroAssembler::FixupNurseryObjects(cx, this, reader, nurseryObjects);
-}
-
-void
 JitCode::finalize(FreeOp* fop)
 {
     // If this jitcode had a bytecode map, it must have already been removed.
 #ifdef DEBUG
     JSRuntime* rt = fop->runtime();
     if (hasBytecodeMap_) {
         JitcodeGlobalEntry result;
         MOZ_ASSERT(rt->jitRuntime()->hasJitcodeGlobalTable());
@@ -1733,17 +1720,17 @@ GenerateCode(MIRGenerator* mir, LIRGraph
 
     return codegen;
 }
 
 CodeGenerator*
 CompileBackEnd(MIRGenerator* mir)
 {
     // Everything in CompileBackEnd can potentially run on a helper thread.
-    AutoEnterIonCompilation enter;
+    AutoEnterIonCompilation enter(mir->safeForMinorGC());
     AutoSpewEndFunction spewEndFunction(mir);
 
     if (!OptimizeMIR(mir))
         return nullptr;
 
     LIRGraph* lir = GenerateLIR(mir);
     if (!lir)
         return nullptr;
@@ -1862,75 +1849,16 @@ AttachFinishedCompilations(JSContext* cx
             sliceScripts.infallibleAppend(debugScripts[info.scriptIndex + b]);
 
         Debugger::onIonCompilation(cx, sliceScripts, info.graph);
     }
 
     js_delete(debuggerAlloc);
 }
 
-void
-MIRGenerator::traceNurseryObjects(JSTracer* trc)
-{
-    TraceRootRange(trc, nurseryObjects_.length(), nurseryObjects_.begin(), "ion-nursery-objects");
-}
-
-class MarkOffThreadNurseryObjects : public gc::BufferableRef
-{
-  public:
-    void trace(JSTracer* trc) override;
-};
-
-void
-MarkOffThreadNurseryObjects::trace(JSTracer* trc)
-{
-    JSRuntime* rt = trc->runtime();
-
-    if (trc->runtime()->isHeapMinorCollecting()) {
-        // Only reset hasIonNurseryObjects if we're doing an actual minor GC.
-        MOZ_ASSERT(rt->jitRuntime()->hasIonNurseryObjects());
-        rt->jitRuntime()->setHasIonNurseryObjects(false);
-    }
-
-    AutoLockHelperThreadState lock;
-    if (!HelperThreadState().threads)
-        return;
-
-    // Trace nursery objects of any builders which haven't started yet.
-    GlobalHelperThreadState::IonBuilderVector& worklist = HelperThreadState().ionWorklist();
-    for (size_t i = 0; i < worklist.length(); i++) {
-        jit::IonBuilder* builder = worklist[i];
-        if (builder->script()->runtimeFromAnyThread() == rt)
-            builder->traceNurseryObjects(trc);
-    }
-
-    // Trace nursery objects of in-progress entries.
-    for (size_t i = 0; i < HelperThreadState().threadCount; i++) {
-        HelperThread& helper = HelperThreadState().threads[i];
-        if (helper.ionBuilder && helper.ionBuilder->script()->runtimeFromAnyThread() == rt)
-            helper.ionBuilder->traceNurseryObjects(trc);
-    }
-
-    // Trace nursery objects of any completed entries.
-    GlobalHelperThreadState::IonBuilderVector& finished = HelperThreadState().ionFinishedList();
-    for (size_t i = 0; i < finished.length(); i++) {
-        jit::IonBuilder* builder = finished[i];
-        if (builder->script()->runtimeFromAnyThread() == rt)
-            builder->traceNurseryObjects(trc);
-    }
-
-    // Trace nursery objects of lazy-linked builders.
-    jit::IonBuilder* builder = HelperThreadState().ionLazyLinkList().getFirst();
-    while (builder) {
-        if (builder->script()->runtimeFromAnyThread() == rt)
-            builder->traceNurseryObjects(trc);
-        builder = builder->getNext();
-    }
-}
-
 static void
 TrackAllProperties(JSContext* cx, JSObject* obj)
 {
     MOZ_ASSERT(obj->isSingleton());
 
     for (Shape::Range<NoGC> range(obj->as<NativeObject>().lastProperty()); !range.empty(); range.popFront())
         EnsureTrackPropertyTypes(cx, obj, range.front().propid());
 }
@@ -2054,16 +1982,19 @@ IonCompile(JSContext* cx, JSScript* scri
     IonBuilder* builder = alloc->new_<IonBuilder>((JSContext*) nullptr,
                                                   CompileCompartment::get(cx->compartment()),
                                                   options, temp, graph, constraints,
                                                   inspector, info, optimizationInfo,
                                                   baselineFrameInspector);
     if (!builder)
         return AbortReason_Alloc;
 
+    if (cx->runtime()->gc.storeBuffer.cancelIonCompilations())
+        builder->setNotSafeForMinorGC();
+
     MOZ_ASSERT(recompile == builder->script()->hasIonScript());
     MOZ_ASSERT(builder->script()->canIonCompile());
 
     RootedScript builderScript(cx, builder->script());
 
     if (recompile)
         builderScript->ionScript()->setRecompiling();
 
@@ -2111,25 +2042,16 @@ IonCompile(JSContext* cx, JSScript* scri
     if (options.offThreadCompilationAvailable()) {
         if (!recompile)
             builderScript->setIonScript(cx, ION_COMPILING_SCRIPT);
 
         JitSpew(JitSpew_IonSyncLogs, "Can't log script %s:%" PRIuSIZE
                 ". (Compiled on background thread.)",
                 builderScript->filename(), builderScript->lineno());
 
-        JSRuntime* rt = cx->runtime();
-        if (!builder->nurseryObjects().empty() && !rt->jitRuntime()->hasIonNurseryObjects()) {
-            // Ensure the builder's nursery objects are marked when a nursery
-            // GC happens on the main thread.
-            MarkOffThreadNurseryObjects mark;
-            rt->gc.storeBuffer.putGeneric(mark);
-            rt->jitRuntime()->setHasIonNurseryObjects(true);
-        }
-
         if (!StartOffThreadIonCompile(cx, builder)) {
             JitSpew(JitSpew_IonAbort, "Unable to start off-thread ion compilation.");
             builder->graphSpewer().endFunction();
             return AbortReason_Alloc;
         }
 
         // The allocator and associated data will be destroyed after being
         // processed in the finishedOffThreadCompilations list.
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -227,49 +227,16 @@ void
 IonBuilder::spew(const char* message)
 {
     // Don't call PCToLineNumber in release builds.
 #ifdef DEBUG
     JitSpew(JitSpew_IonMIR, "%s @ %s:%d", message, script()->filename(), PCToLineNumber(script(), pc));
 #endif
 }
 
-MInstruction*
-IonBuilder::constantMaybeNursery(JSObject* obj)
-{
-    MOZ_ASSERT(obj);
-    if (!IsInsideNursery(obj))
-        return constant(ObjectValue(*obj));
-
-    // If |obj| is in the nursery, we have to add it to the list of nursery
-    // objects that get traced during off-thread compilation. We use
-    // MNurseryObject to ensure we will patch the code with the right
-    // pointer after codegen is done.
-
-    ObjectVector& nurseryObjects = outermostBuilder()->nurseryObjects_;
-
-    size_t index = UINT32_MAX;
-    for (size_t i = 0, len = nurseryObjects.length(); i < len; i++) {
-        if (nurseryObjects[i] == obj) {
-            index = i;
-            break;
-        }
-    }
-
-    if (index == UINT32_MAX) {
-        if (!nurseryObjects.append(obj))
-            return nullptr;
-        index = nurseryObjects.length() - 1;
-    }
-
-    MNurseryObject* ins = MNurseryObject::New(alloc(), obj, index, constraints());
-    current->add(ins);
-    return ins;
-}
-
 static inline int32_t
 GetJumpOffset(jsbytecode* pc)
 {
     MOZ_ASSERT(js_CodeSpec[JSOp(*pc)].type() == JOF_JUMP);
     return GET_JUMP_OFFSET(pc);
 }
 
 IonBuilder::CFGState
@@ -970,16 +937,18 @@ IonBuilder::buildInline(IonBuilder* call
         failedBoundsCheck_ = true;
 
     if (callerBuilder->failedShapeGuard_)
         failedShapeGuard_ = true;
 
     if (callerBuilder->failedLexicalCheck_)
         failedLexicalCheck_ = true;
 
+    safeForMinorGC_ = callerBuilder->safeForMinorGC_;
+
     // Generate single entrance block.
     if (!setCurrentAndSpecializePhis(newBlock(pc)))
         return false;
     if (!current)
         return false;
 
     current->setCallerResumePoint(callerResumePoint);
 
@@ -4731,19 +4700,16 @@ IonBuilder::inlineScriptedCall(CallInfo&
             for (size_t i = 0; i < groups.length(); i++)
                 addAbortedPreliminaryGroup(groups[i]);
             abortReason_ = AbortReason_PreliminaryObjects;
         }
 
         return false;
     }
 
-    MOZ_ASSERT(inlineBuilder.nurseryObjects_.empty(),
-               "Nursery objects should be added to outer builder");
-
     // Create return block.
     jsbytecode* postCall = GetNextPc(pc);
     MBasicBlock* returnBlock = newBlock(nullptr, postCall);
     if (!returnBlock)
         return false;
     returnBlock->setCallerResumePoint(callerResumePoint_);
 
     // Inherit the slots from current and pop |fun|.
@@ -5498,17 +5464,17 @@ IonBuilder::inlineCalls(CallInfo& callIn
         if (!inlineBlock)
             return false;
 
         // Create a function MConstant to use in the entry ResumePoint. If we
         // can't use a constant, add a no-op MPolyInlineGuard, to prevent
         // hoisting scope chain gets above the dispatch instruction.
         MInstruction* funcDef;
         if (target->isSingleton())
-            funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints());
+            funcDef = MConstant::New(alloc(), ObjectValue(*target), constraints(), this);
         else
             funcDef = MPolyInlineGuard::New(alloc(), callInfo.fun());
 
         funcDef->setImplicitlyUsedUnchecked();
         dispatchBlock->add(funcDef);
 
         // Use the inlined callee in the inline resume point and on stack.
         int funIndex = inlineBlock->entryResumePoint()->stackDepth() - callInfo.numFormals();
@@ -5813,17 +5779,17 @@ IonBuilder::createThisScriptedSingleton(
         return nullptr;
 
     StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
     if (!thisTypes || !thisTypes->hasType(TypeSet::ObjectType(templateObject)))
         return nullptr;
 
     // Generate an inline path to create a new |this| object with
     // the given singleton prototype.
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
     MCreateThisWithTemplate* createThis =
         MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
                                      templateObject->group()->initialHeap(constraints()));
     current->add(templateConst);
     current->add(createThis);
 
     return createThis;
 }
@@ -5844,17 +5810,17 @@ IonBuilder::createThisScriptedBaseline(M
     Shape* shape = target->lookupPure(compartment->runtime()->names().prototype);
     if (!shape || !shape->hasDefaultGetter() || !shape->hasSlot())
         return nullptr;
 
     Value protov = target->getSlot(shape->slot());
     if (!protov.isObject())
         return nullptr;
 
-    JSObject* proto = &protov.toObject();
+    JSObject* proto = checkNurseryObject(&protov.toObject());
     if (proto != templateObject->getProto())
         return nullptr;
 
     TypeSet::ObjectKey* templateObjectKey = TypeSet::ObjectKey::get(templateObject->group());
     if (templateObjectKey->hasFlags(constraints(), OBJECT_FLAG_NEW_SCRIPT_CLEARED))
         return nullptr;
 
     StackTypeSet* thisTypes = TypeScript::ThisTypes(target->nonLazyScript());
@@ -5865,24 +5831,24 @@ IonBuilder::createThisScriptedBaseline(M
     callee = addShapeGuard(callee, target->lastProperty(), Bailout_ShapeGuard);
 
     // Guard callee.prototype == proto.
     MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot");
     MSlots* slots = MSlots::New(alloc(), callee);
     current->add(slots);
     MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, shape->slot());
     current->add(prototype);
-    MDefinition* protoConst = constantMaybeNursery(proto);
+    MDefinition* protoConst = constant(ObjectValue(*proto));
     MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
                                                             /* bailOnEquality = */ false);
     current->add(guard);
 
     // Generate an inline path to create a new |this| object with
     // the given prototype.
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
     MCreateThisWithTemplate* createThis =
         MCreateThisWithTemplate::New(alloc(), constraints(), templateConst,
                                      templateObject->group()->initialHeap(constraints()));
     current->add(templateConst);
     current->add(createThis);
 
     return createThis;
 }
@@ -6492,17 +6458,17 @@ bool
 IonBuilder::jsop_newarray(uint32_t count)
 {
     JSObject* templateObject = inspector->getTemplateObject(pc);
     gc::InitialHeap heap;
     MConstant* templateConst;
 
     if (templateObject) {
         heap = templateObject->group()->initialHeap(constraints());
-        templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
+        templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
     } else {
         heap = gc::DefaultHeap;
         templateConst = MConstant::New(alloc(), NullValue());
     }
     current->add(templateConst);
 
     MNewArray* ins = MNewArray::New(alloc(), constraints(), count, templateConst, heap, pc);
     current->add(ins);
@@ -6543,17 +6509,17 @@ bool
 IonBuilder::jsop_newobject()
 {
     JSObject* templateObject = inspector->getTemplateObject(pc);
     gc::InitialHeap heap;
     MConstant* templateConst;
 
     if (templateObject) {
         heap = templateObject->group()->initialHeap(constraints());
-        templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
+        templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
     } else {
         heap = gc::DefaultHeap;
         templateConst = MConstant::New(alloc(), NullValue());
     }
 
     current->add(templateConst);
     MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst, heap,
                                       MNewObject::ObjectLiteral);
@@ -7207,17 +7173,17 @@ IonBuilder::testSingletonProperty(JSObje
             if (obj->isSingleton())
                 return property.singleton(constraints());
             return nullptr;
         }
 
         if (ObjectHasExtraOwnProperty(compartment, objKey, name))
             return nullptr;
 
-        obj = obj->getProto();
+        obj = checkNurseryObject(obj->getProto());
     }
 
     return nullptr;
 }
 
 JSObject*
 IonBuilder::testSingletonPropertyTypes(MDefinition* obj, PropertyName* name)
 {
@@ -7274,17 +7240,17 @@ IonBuilder::testSingletonPropertyTypes(M
             if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, name))
                 return nullptr;
             if (key->unknownProperties())
                 return nullptr;
             HeapTypeSetKey property = key->property(NameToId(name));
             if (property.isOwnProperty(constraints()))
                 return nullptr;
 
-            if (JSObject* proto = key->proto().toObjectOrNull()) {
+            if (JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull())) {
                 // Test this type.
                 JSObject* thisSingleton = testSingletonProperty(proto, name);
                 if (!thisSingleton)
                     return nullptr;
                 if (singleton) {
                     if (thisSingleton != singleton)
                         return nullptr;
                 } else {
@@ -9038,17 +9004,17 @@ IonBuilder::setElemTryDense(bool* emitte
     JSValueType unboxedType = UnboxedArrayElementType(constraints(), object, index);
     if (unboxedType == JSVAL_TYPE_MAGIC) {
         if (!ElementAccessIsDenseNative(constraints(), object, index)) {
             trackOptimizationOutcome(TrackedOutcome::AccessNotDense);
             return true;
         }
     }
 
-    if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
+    if (PropertyWriteNeedsTypeBarrier(this, constraints(), current,
                                       &object, nullptr, &value, /* canModify = */ true))
     {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return true;
     }
 
     if (!object->resultTypeSet()) {
         trackOptimizationOutcome(TrackedOutcome::NoTypeInfo);
@@ -9118,17 +9084,17 @@ IonBuilder::setElemTryCache(bool* emitte
     // Temporary disable the cache if non dense native,
     // until the cache supports more ics
     SetElemICInspector icInspect(inspector->setElemICInspector(pc));
     if (!icInspect.sawDenseWrite() && !icInspect.sawTypedArrayWrite()) {
         trackOptimizationOutcome(TrackedOutcome::SetElemNonDenseNonTANotCached);
         return true;
     }
 
-    if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
+    if (PropertyWriteNeedsTypeBarrier(this, constraints(), current,
                                       &object, nullptr, &value, /* canModify = */ true))
     {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return true;
     }
 
     // We can avoid worrying about holes in the IC if we know a priori we are safe
     // from them. If TI can guard that there are no indexed properties on the prototype
@@ -9456,17 +9422,17 @@ IonBuilder::jsop_rest()
         return true;
     }
 
     // We know the exact number of arguments the callee pushed.
     unsigned numActuals = inlineCallInfo_->argc();
     unsigned numFormals = info().nargs() - 1;
     unsigned numRest = numActuals > numFormals ? numActuals - numFormals : 0;
 
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
     current->add(templateConst);
 
     MNewArray* array = MNewArray::New(alloc(), constraints(), numRest, templateConst,
                                       templateObject->group()->initialHeap(constraints()), pc);
     current->add(array);
 
     if (numRest == 0) {
         // No more updating to do. (Note that in this one case the length from
@@ -9710,17 +9676,18 @@ IonBuilder::objectsHaveCommonPrototype(T
             }
             if (singleton) {
                 if (CanHaveEmptyPropertyTypesForOwnProperty(singleton)) {
                     MOZ_ASSERT(singleton->is<GlobalObject>());
                     *guardGlobal = true;
                 }
             }
 
-            JSObject* proto = key->proto().toObjectOrNull();
+            JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
+
             if (proto == foundProto)
                 break;
             if (!proto) {
                 // The foundProto being searched for did not show up on the
                 // object's prototype chain.
                 return false;
             }
             key = TypeSet::ObjectKey::get(proto);
@@ -9805,17 +9772,17 @@ IonBuilder::testCommonGetterSetter(Tempo
             Shape* propShape = nativeProto.lookupPure(name);
             MOZ_ASSERT_IF(isGetter, propShape->getterObject() == getterOrSetter);
             MOZ_ASSERT_IF(!isGetter, propShape->setterObject() == getterOrSetter);
             if (propShape && !propShape->configurable())
                 return true;
         }
     }
 
-    MInstruction* wrapper = constantMaybeNursery(foundProto);
+    MInstruction* wrapper = constant(ObjectValue(*foundProto));
     *guard = addShapeGuard(wrapper, lastProperty, Bailout_ShapeGuard);
     return true;
 }
 
 void
 IonBuilder::replaceMaybeFallbackFunctionGetter(MGetPropertyCache* cache)
 {
     // Discard the last prior resume point of the previous MGetPropertyCache.
@@ -9855,26 +9822,27 @@ IonBuilder::annotateGetPropertyCache(MDe
     // is a single-object typeset containing a JSFunction
     for (unsigned int i = 0; i < objCount; i++) {
         ObjectGroup* group = objTypes->getGroup(i);
         if (!group)
             continue;
         TypeSet::ObjectKey* key = TypeSet::ObjectKey::get(group);
         if (key->unknownProperties() || !key->proto().isObject())
             continue;
+        JSObject* proto = checkNurseryObject(key->proto().toObject());
 
         const Class* clasp = key->clasp();
         if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, name))
             continue;
 
         HeapTypeSetKey ownTypes = key->property(NameToId(name));
         if (ownTypes.isOwnProperty(constraints()))
             continue;
 
-        JSObject* singleton = testSingletonProperty(key->proto().toObject(), name);
+        JSObject* singleton = testSingletonProperty(proto, name);
         if (!singleton || !singleton->is<JSFunction>())
             continue;
 
         // Don't add cases corresponding to non-observed pushes
         if (!pushedTypes->hasType(TypeSet::ObjectType(singleton)))
             continue;
 
         if (!inlinePropTable->addEntry(alloc(), group, &singleton->as<JSFunction>()))
@@ -10700,17 +10668,17 @@ IonBuilder::addShapeGuardsForGetterSette
 
     obj = convertUnboxedObjects(obj, convertUnboxedGroups);
 
     if (isOwnProperty) {
         MOZ_ASSERT(receivers.empty());
         return addShapeGuard(obj, holderShape, Bailout_ShapeGuard);
     }
 
-    MDefinition* holderDef = constantMaybeNursery(holder);
+    MDefinition* holderDef = constant(ObjectValue(*holder));
     addShapeGuard(holderDef, holderShape, Bailout_ShapeGuard);
 
     return addGuardReceiverPolymorphic(obj, receivers);
 }
 
 bool
 IonBuilder::getPropTryCommonGetter(bool* emitted, MDefinition* obj, PropertyName* name,
                                    TemporaryTypeSet* types)
@@ -10794,17 +10762,17 @@ IonBuilder::getPropTryCommonGetter(bool*
         obj = guardObj;
     }
 
     // Spoof stack to expected state for call.
 
     // Make sure there's enough room
     if (!current->ensureHasSlots(2))
         return false;
-    current->push(constantMaybeNursery(commonGetter));
+    current->push(constant(ObjectValue(*commonGetter)));
 
     current->push(obj);
 
     CallInfo callInfo(alloc(), false);
     if (!callInfo.init(current, 0))
         return false;
 
     if (commonGetter->isNative()) {
@@ -11036,17 +11004,17 @@ IonBuilder::getPropTryCache(bool* emitte
     // able to attach stubs for them.
     if (inspector->hasSeenAccessedGetter(pc))
         barrier = BarrierKind::TypeSet;
 
     // Caches can read values from prototypes, so update the barrier to
     // reflect such possible values.
     if (barrier != BarrierKind::TypeSet) {
         BarrierKind protoBarrier =
-            PropertyReadOnPrototypeNeedsTypeBarrier(constraints(), obj, name, types);
+            PropertyReadOnPrototypeNeedsTypeBarrier(this, constraints(), obj, name, types);
         if (protoBarrier != BarrierKind::NoBarrier) {
             MOZ_ASSERT(barrier <= protoBarrier);
             barrier = protoBarrier;
         }
     }
 
     MGetPropertyCache* load = MGetPropertyCache::New(alloc(), obj, name,
                                                      barrier == BarrierKind::TypeSet);
@@ -11205,17 +11173,17 @@ IonBuilder::jsop_setprop(PropertyName* n
 
         // Try to emit stores to known binary data blocks
         trackOptimizationAttempt(TrackedStrategy::SetProp_TypedObject);
         if (!setPropTryTypedObject(&emitted, obj, name, value) || emitted)
             return emitted;
     }
 
     TemporaryTypeSet* objTypes = obj->resultTypeSet();
-    bool barrier = PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &obj, name, &value,
+    bool barrier = PropertyWriteNeedsTypeBarrier(this, constraints(), current, &obj, name, &value,
                                                  /* canModify = */ true);
 
     if (!forceInlineCaches()) {
         // Try to emit stores to unboxed objects.
         trackOptimizationAttempt(TrackedStrategy::SetProp_Unboxed);
         if (!setPropTryUnboxed(&emitted, obj, name, value, barrier, objTypes) || emitted)
             return emitted;
     }
@@ -11299,17 +11267,17 @@ IonBuilder::setPropTryCommonSetter(bool*
         obj = guardObj;
     }
 
     // Dummy up the stack, as in getprop. We are pushing an extra value, so
     // ensure there is enough space.
     if (!current->ensureHasSlots(3))
         return false;
 
-    current->push(constantMaybeNursery(commonSetter));
+    current->push(constant(ObjectValue(*commonSetter)));
     current->push(obj);
     current->push(value);
 
     // Call the setter. Note that we have to push the original value, not
     // the setter's return value.
     CallInfo callInfo(alloc(), false);
     if (!callInfo.init(current, 1))
         return false;
@@ -11847,17 +11815,17 @@ bool
 IonBuilder::jsop_lambda(JSFunction* fun)
 {
     MOZ_ASSERT(analysis().usesScopeChain());
     MOZ_ASSERT(!fun->isArrow());
 
     if (fun->isNative() && IsAsmJSModuleNative(fun->native()))
         return abort("asm.js module function");
 
-    MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun);
+    MConstant* cst = MConstant::NewConstraintlessObject(alloc(), fun, this);
     current->add(cst);
     MLambda* ins = MLambda::New(alloc(), constraints(), current->scopeChain(), cst);
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
@@ -12533,17 +12501,17 @@ IonBuilder::jsop_instanceof()
         rhs = addShapeGuard(rhs, shape, Bailout_ShapeGuard);
 
         // Guard .prototype == protoObject.
         MOZ_ASSERT(shape->numFixedSlots() == 0, "Must be a dynamic slot");
         MSlots* slots = MSlots::New(alloc(), rhs);
         current->add(slots);
         MLoadSlot* prototype = MLoadSlot::New(alloc(), slots, slot);
         current->add(prototype);
-        MConstant* protoConst = MConstant::NewConstraintlessObject(alloc(), protoObject);
+        MConstant* protoConst = MConstant::NewConstraintlessObject(alloc(), protoObject, this);
         current->add(protoConst);
         MGuardObjectIdentity* guard = MGuardObjectIdentity::New(alloc(), prototype, protoConst,
                                                                 /* bailOnEquality = */ false);
         current->add(guard);
 
         if (tryFoldInstanceOf(obj, protoObject))
             return true;
 
@@ -12944,17 +12912,17 @@ IonBuilder::storeReferenceTypedObjectVal
     // Make sure we aren't adding new type information for writes of object and value
     // references.
     if (type != ReferenceTypeDescr::TYPE_STRING) {
         MOZ_ASSERT(type == ReferenceTypeDescr::TYPE_ANY ||
                    type == ReferenceTypeDescr::TYPE_OBJECT);
         MIRType implicitType =
             (type == ReferenceTypeDescr::TYPE_ANY) ? MIRType_Undefined : MIRType_Null;
 
-        if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current, &typedObj, name, &value,
+        if (PropertyWriteNeedsTypeBarrier(this, constraints(), current, &typedObj, name, &value,
                                           /* canModify = */ true, implicitType))
         {
             trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
             return false;
         }
     }
 
     // Find location within the owner object.
@@ -12985,23 +12953,45 @@ IonBuilder::storeReferenceTypedObjectVal
         store = MStoreUnboxedString::New(alloc(), elements, scaledOffset, value, adjustment);
         break;
     }
 
     current->add(store);
     return true;
 }
 
+JSObject*
+IonBuilder::checkNurseryObject(JSObject* obj)
+{
+    // If we try to use any nursery pointers during compilation, make sure that
+    // the main thread will cancel this compilation before performing a minor
+    // GC. All constants used during compilation should either go through this
+    // function or should come from a type set (which has a similar barrier).
+    if (obj && IsInsideNursery(obj)) {
+        compartment->runtime()->setMinorGCShouldCancelIonCompilations();
+        IonBuilder* builder = this;
+        while (builder) {
+            builder->setNotSafeForMinorGC();
+            builder = builder->callerBuilder_;
+        }
+    }
+
+    return obj;
+}
+
 MConstant*
 IonBuilder::constant(const Value& v)
 {
     MOZ_ASSERT(!v.isString() || v.toString()->isAtom(),
                "Handle non-atomized strings outside IonBuilder.");
 
-    MConstant* c = MConstant::New(alloc(), v, constraints());
+    if (v.isObject())
+        checkNurseryObject(&v.toObject());
+
+    MConstant* c = MConstant::New(alloc(), v, constraints(), this);
     current->add(c);
     return c;
 }
 
 MConstant*
 IonBuilder::constantInt(int32_t i)
 {
     return constant(Int32Value(i));
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -223,18 +223,16 @@ class IonBuilder
     bool processIterators();
     bool inspectOpcode(JSOp op);
     uint32_t readIndex(jsbytecode* pc);
     JSAtom* readAtom(jsbytecode* pc);
     bool abort(const char* message, ...);
     void trackActionableAbort(const char* message);
     void spew(const char* message);
 
-    MInstruction* constantMaybeNursery(JSObject* obj);
-
     JSFunction* getSingleCallTarget(TemporaryTypeSet* calleeTypes);
     bool getPolyCallTargets(TemporaryTypeSet* calleeTypes, bool constructing,
                             ObjectVector& targets, uint32_t maxTargets);
 
     void popCfgStack();
     DeferredEdge* filterDeadDeferredEdges(DeferredEdge* edge);
     bool processDeferredContinues(CFGState& state);
     ControlStatus processControlEnd();
@@ -982,16 +980,17 @@ class IonBuilder
     // are recorded here so they may be propagated to the script's
     // corresponding JitcodeGlobalEntry::BaselineEntry.
     JSScript* actionableAbortScript_;
     jsbytecode* actionableAbortPc_;
     const char* actionableAbortMessage_;
 
   public:
     void clearForBackEnd();
+    JSObject* checkNurseryObject(JSObject* obj);
 
     JSScript* script() const { return script_; }
 
     CodeGenerator* backgroundCodegen() const { return backgroundCodegen_; }
     void setBackgroundCodegen(CodeGenerator* codegen) { backgroundCodegen_ = codegen; }
 
     CompilerConstraintList* constraints() {
         return constraints_;
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -409,29 +409,28 @@ GeneratePrototypeGuards(JSContext* cx, I
      */
     MOZ_ASSERT(obj != holder);
 
     if (obj->hasUncacheableProto()) {
         // Note: objectReg and scratchReg may be the same register, so we cannot
         // use objectReg in the rest of this function.
         masm.loadPtr(Address(objectReg, JSObject::offsetOfGroup()), scratchReg);
         Address proto(scratchReg, ObjectGroup::offsetOfProto());
-        masm.branchPtr(Assembler::NotEqual, proto,
-                       ImmMaybeNurseryPtr(obj->getProto()), failures);
+        masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), failures);
     }
 
     JSObject* pobj = IsCacheableDOMProxy(obj)
                      ? obj->getTaggedProto().toObjectOrNull()
                      : obj->getProto();
     if (!pobj)
         return;
     while (pobj != holder) {
         if (pobj->hasUncacheableProto()) {
             MOZ_ASSERT(!pobj->isSingleton());
-            masm.movePtr(ImmMaybeNurseryPtr(pobj), scratchReg);
+            masm.movePtr(ImmGCPtr(pobj), scratchReg);
             Address groupAddr(scratchReg, JSObject::offsetOfGroup());
             masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), failures);
         }
         pobj = pobj->getProto();
     }
 }
 
 // Note: This differs from IsCacheableProtoChain in BaselineIC.cpp in that
@@ -800,17 +799,17 @@ GenerateReadSlot(JSContext* cx, IonScrip
     if (obj != holder) {
         // Note: this may clobber the object register if it's used as scratch.
         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg,
                                 &prototypeFailures);
 
         if (holder) {
             // Guard on the holder's shape.
             holderReg = scratchReg;
-            masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
+            masm.movePtr(ImmGCPtr(holder), holderReg);
             masm.branchPtr(Assembler::NotEqual,
                            Address(holderReg, JSObject::offsetOfShape()),
                            ImmGCPtr(holder->as<NativeObject>().lastProperty()),
                            &prototypeFailures);
         } else {
             // The property does not exist. Guard on everything in the
             // prototype chain.
             JSObject* proto = obj->getTaggedProto().toObjectOrNull();
@@ -976,17 +975,17 @@ EmitGetterCall(JSContext* cx, MacroAssem
         if (obj == holder) {
             // When the holder is also the current receiver, we just have a shape guard,
             // so we might end up with a random object which is also guaranteed to have
             // this JSGetterOp.
             masm.Push(object);
         } else {
             // If the holder is on the prototype chain, the prototype-guarding
             // only allows objects with the same holder.
-            masm.movePtr(ImmMaybeNurseryPtr(holder), scratchReg);
+            masm.movePtr(ImmGCPtr(holder), scratchReg);
             masm.Push(scratchReg);
         }
         masm.moveStackPtrTo(argObjReg);
 
         masm.loadJSContext(argJSContextReg);
 
         if (!masm.icBuildOOLFakeExitFrame(returnAddr, aic))
             return false;
@@ -1029,17 +1028,17 @@ EmitGetterCall(JSContext* cx, MacroAssem
         MOZ_ASSERT(padding % sizeof(uintptr_t) == 0);
         MOZ_ASSERT(padding < JitStackAlignment);
         masm.reserveStack(padding);
 
         for (size_t i = 0; i < target->nargs(); i++)
             masm.Push(UndefinedValue());
         masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
 
-        masm.movePtr(ImmMaybeNurseryPtr(target), scratchReg);
+        masm.movePtr(ImmGCPtr(target), scratchReg);
 
         descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonAccessorIC);
         masm.Push(Imm32(0)); // argc
         masm.Push(scratchReg);
         masm.Push(Imm32(descriptor));
 
         // Check stack alignment. Add sizeof(uintptr_t) for the return address.
         MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
@@ -1086,17 +1085,17 @@ GenerateCallGetter(JSContext* cx, IonScr
     }
 
     // Note: this may clobber the object register if it's used as scratch.
     if (obj != holder)
         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, failures);
 
     // Guard on the holder's shape.
     Register holderReg = scratchReg;
-    masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
+    masm.movePtr(ImmGCPtr(holder), holderReg);
     masm.branchPtr(Assembler::NotEqual,
                    Address(holderReg, JSObject::offsetOfShape()),
                    ImmGCPtr(holder->as<NativeObject>().lastProperty()),
                    maybePopAndFail);
 
     if (spillObjReg)
         masm.pop(object);
 
@@ -1689,17 +1688,17 @@ GetPropertyIC::tryAttachDOMProxyUnshadow
         // getprop.
         Register scratchReg = output().valueReg().scratchReg();
         GeneratePrototypeGuards(cx, ion, masm, obj, holder, object(), scratchReg, &failures);
 
         // Rename scratch for clarity.
         Register holderReg = scratchReg;
 
         // Guard on the holder of the property
-        masm.movePtr(ImmMaybeNurseryPtr(holder), holderReg);
+        masm.movePtr(ImmGCPtr(holder), holderReg);
         masm.branchPtr(Assembler::NotEqual,
                     Address(holderReg, JSObject::offsetOfShape()),
                     ImmGCPtr(holder->lastProperty()),
                     &failures);
 
         if (canCache == CanAttachReadSlot) {
             EmitLoadSlot(masm, holder, shape, holderReg, output(), scratchReg);
         } else {
@@ -2399,17 +2398,17 @@ GenerateCallSetter(JSContext* cx, IonScr
 
         Label protoFailure;
         Label protoSuccess;
 
         // Generate prototype/shape guards.
         if (obj != holder)
             GeneratePrototypeGuards(cx, ion, masm, obj, holder, object, scratchReg, &protoFailure);
 
-        masm.movePtr(ImmMaybeNurseryPtr(holder), scratchReg);
+        masm.movePtr(ImmGCPtr(holder), scratchReg);
         masm.branchPtr(Assembler::NotEqual,
                        Address(scratchReg, JSObject::offsetOfShape()),
                        ImmGCPtr(holder->as<NativeObject>().lastProperty()),
                        &protoFailure);
 
         masm.jump(&protoSuccess);
 
         masm.bind(&protoFailure);
@@ -2584,17 +2583,17 @@ GenerateCallSetter(JSContext* cx, IonScr
         MOZ_ASSERT(padding < JitStackAlignment);
         masm.reserveStack(padding);
 
         for (size_t i = 1; i < target->nargs(); i++)
             masm.Push(UndefinedValue());
         masm.Push(value);
         masm.Push(TypedOrValueRegister(MIRType_Object, AnyRegister(object)));
 
-        masm.movePtr(ImmMaybeNurseryPtr(target), scratchReg);
+        masm.movePtr(ImmGCPtr(target), scratchReg);
 
         descriptor = MakeFrameDescriptor(argSize + padding, JitFrame_IonAccessorIC);
         masm.Push(Imm32(1)); // argc
         masm.Push(scratchReg);
         masm.Push(Imm32(descriptor));
 
         // Check stack alignment. Add sizeof(uintptr_t) for the return address.
         MOZ_ASSERT(((masm.framePushed() + sizeof(uintptr_t)) % JitStackAlignment) == 0);
@@ -3583,25 +3582,24 @@ GenerateDenseElementHole(JSContext* cx, 
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(object, JSObject::offsetOfShape()),
                                    ImmGCPtr(obj->as<NativeObject>().lastProperty()), &failures);
 
 
     if (obj->hasUncacheableProto()) {
         masm.loadPtr(Address(object, JSObject::offsetOfGroup()), scratchReg);
         Address proto(scratchReg, ObjectGroup::offsetOfProto());
-        masm.branchPtr(Assembler::NotEqual, proto,
-                       ImmMaybeNurseryPtr(obj->getProto()), &failures);
+        masm.branchPtr(Assembler::NotEqual, proto, ImmGCPtr(obj->getProto()), &failures);
     }
 
     JSObject* pobj = obj->getProto();
     while (pobj) {
         MOZ_ASSERT(pobj->as<NativeObject>().lastProperty());
 
-        masm.movePtr(ImmMaybeNurseryPtr(pobj), scratchReg);
+        masm.movePtr(ImmGCPtr(pobj), scratchReg);
         if (pobj->hasUncacheableProto()) {
             MOZ_ASSERT(!pobj->isSingleton());
             Address groupAddr(scratchReg, JSObject::offsetOfGroup());
             masm.branchPtr(Assembler::NotEqual, groupAddr, ImmGCPtr(pobj->group()), &failures);
         }
 
         // Make sure the shape matches, to avoid non-dense elements.
         masm.branchPtr(Assembler::NotEqual, Address(scratchReg, JSObject::offsetOfShape()),
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -109,18 +109,16 @@ class JitCode : public gc::TenuredCell
 
     void traceChildren(JSTracer* trc);
     void finalize(FreeOp* fop);
     void fixupAfterMovingGC() {}
     void setInvalidated() {
         invalidated_ = true;
     }
 
-    void fixupNurseryObjects(JSContext* cx, const ObjectVector& nurseryObjects);
-
     void setHasBytecodeMap() {
         hasBytecodeMap_ = true;
     }
 
     void togglePreBarriers(bool enabled);
 
     // If this JitCode object has been, effectively, corrupted due to
     // invalidation patching, then we have to remember this so we don't try and
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -223,18 +223,16 @@ class JitRuntime
     // value that will be temporarily corrupt. This special override value is set
     // only in callVM() targets that are about to return *and* have invalidated
     // their callee.
     js::Value ionReturnOverride_;
 
     // Global table of jitcode native address => bytecode address mappings.
     JitcodeGlobalTable* jitcodeGlobalTable_;
 
-    bool hasIonNurseryObjects_;
-
   private:
     JitCode* generateLazyLinkStub(JSContext* cx);
     JitCode* generateProfilerExitFrameTailStub(JSContext* cx);
     JitCode* generateExceptionTailStub(JSContext* cx, void* handler);
     JitCode* generateBailoutTailStub(JSContext* cx);
     JitCode* generateEnterJIT(JSContext* cx, EnterJitType type);
     JitCode* generateArgumentsRectifier(JSContext* cx, void** returnAddrOut);
     JitCode* generateBailoutTable(JSContext* cx, uint32_t frameClass);
@@ -371,23 +369,16 @@ class JitRuntime
         return v;
     }
     void setIonReturnOverride(const js::Value& v) {
         MOZ_ASSERT(!hasIonReturnOverride());
         MOZ_ASSERT(!v.isMagic());
         ionReturnOverride_ = v;
     }
 
-    bool hasIonNurseryObjects() const {
-        return hasIonNurseryObjects_;
-    }
-    void setHasIonNurseryObjects(bool b)  {
-        hasIonNurseryObjects_ = b;
-    }
-
     bool hasJitcodeGlobalTable() const {
         return jitcodeGlobalTable_ != nullptr;
     }
 
     JitcodeGlobalTable* getJitcodeGlobalTable() {
         MOZ_ASSERT(hasJitcodeGlobalTable());
         return jitcodeGlobalTable_;
     }
--- a/js/src/jit/LIR-Common.h
+++ b/js/src/jit/LIR-Common.h
@@ -706,26 +706,16 @@ class LValue : public LInstructionHelper
       : v_(v)
     { }
 
     Value value() const {
         return v_;
     }
 };
 
-class LNurseryObject : public LInstructionHelper<1, 0, 0>
-{
-  public:
-    LIR_HEADER(NurseryObject);
-
-    MNurseryObject* mir() const {
-        return mir_->toNurseryObject();
-    }
-};
-
 // Clone an object literal such as we are not modifying the object contained in
 // the sources.
 class LCloneLiteral : public LCallInstructionHelper<1, 1, 0>
 {
   public:
     LIR_HEADER(CloneLiteral)
 
     explicit LCloneLiteral(const LAllocation& obj)
--- a/js/src/jit/LOpcodes.h
+++ b/js/src/jit/LOpcodes.h
@@ -344,17 +344,16 @@
     _(AssertRangeI)                 \
     _(AssertRangeD)                 \
     _(AssertRangeF)                 \
     _(AssertRangeV)                 \
     _(AssertResultV)                \
     _(AssertResultT)                \
     _(LexicalCheck)                 \
     _(ThrowUninitializedLexical)    \
-    _(NurseryObject)                \
     _(Debugger)                     \
     _(NewTarget)                    \
     _(ArrowNewTarget)
 
 #if defined(JS_CODEGEN_X86)
 # include "jit/x86/LOpcodes-x86.h"
 #elif defined(JS_CODEGEN_X64)
 # include "jit/x64/LOpcodes-x64.h"
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -4202,22 +4202,16 @@ LIRGenerator::visitThrowUninitializedLex
 void
 LIRGenerator::visitDebugger(MDebugger* ins)
 {
     LDebugger* lir = new(alloc()) LDebugger(tempFixed(CallTempReg0), tempFixed(CallTempReg1));
     assignSnapshot(lir, Bailout_Debugger);
     add(lir, ins);
 }
 
-void
-LIRGenerator::visitNurseryObject(MNurseryObject* ins)
-{
-    define(new(alloc()) LNurseryObject(), ins);
-}
-
 static void
 SpewResumePoint(MBasicBlock* block, MInstruction* ins, MResumePoint* resumePoint)
 {
     Fprinter& out = JitSpewPrinter();
     out.printf("Current resume point %p details:\n", (void*)resumePoint);
     out.printf("    frame count: %u\n", resumePoint->frameCount());
 
     if (ins) {
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -291,17 +291,16 @@ class LIRGenerator : public LIRGenerator
     void visitPhi(MPhi* ins);
     void visitBeta(MBeta* ins);
     void visitObjectState(MObjectState* ins);
     void visitArrayState(MArrayState* ins);
     void visitUnknownValue(MUnknownValue* ins);
     void visitLexicalCheck(MLexicalCheck* ins);
     void visitThrowUninitializedLexical(MThrowUninitializedLexical* ins);
     void visitDebugger(MDebugger* ins);
-    void visitNurseryObject(MNurseryObject* ins);
     void visitNewTarget(MNewTarget* ins);
     void visitArrowNewTarget(MArrowNewTarget* ins);
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_Lowering_h */
--- a/js/src/jit/MCallOptimize.cpp
+++ b/js/src/jit/MCallOptimize.cpp
@@ -586,17 +586,17 @@ IonBuilder::inlineArray(CallInfo& callIn
 
         // Don't inline large allocations.
         if (initLength > ArrayObject::EagerAllocationMaxLength)
             return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
     current->add(templateConst);
 
     MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
                                     templateObject->group()->initialHeap(constraints()), pc);
     current->add(ins);
     current->push(ins);
 
     if (callInfo.argc() >= 2) {
@@ -760,17 +760,17 @@ IonBuilder::inlineArrayPush(CallInfo& ca
 {
     if (callInfo.argc() != 1 || callInfo.constructing()) {
         trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
         return InliningStatus_NotInlined;
     }
 
     MDefinition* obj = callInfo.thisArg();
     MDefinition* value = callInfo.getArg(0);
-    if (PropertyWriteNeedsTypeBarrier(alloc(), constraints(), current,
+    if (PropertyWriteNeedsTypeBarrier(this, constraints(), current,
                                       &obj, nullptr, &value, /* canModify = */ false))
     {
         trackOptimizationOutcome(TrackedOutcome::NeedsTypeBarrier);
         return InliningStatus_NotInlined;
     }
     MOZ_ASSERT(obj == callInfo.thisArg() && value == callInfo.getArg(0));
 
     if (getInlineReturnType() != MIRType_Int32)
@@ -1016,16 +1016,26 @@ IonBuilder::inlineArraySlice(CallInfo& c
             return InliningStatus_NotInlined;
     }
 
     // Inline the call.
     JSObject* templateObj = inspector->getTemplateObjectForNative(pc, js::array_slice);
     if (!templateObj)
         return InliningStatus_NotInlined;
 
+    if (unboxedType == JSVAL_TYPE_MAGIC) {
+        if (!templateObj->is<ArrayObject>())
+            return InliningStatus_NotInlined;
+    } else {
+        if (!templateObj->is<UnboxedArrayObject>())
+            return InliningStatus_NotInlined;
+        if (templateObj->as<UnboxedArrayObject>().elementType() != unboxedType)
+            return InliningStatus_NotInlined;
+    }
+
     callInfo.setImplicitlyUsedUnchecked();
 
     MDefinition* begin;
     if (callInfo.argc() > 0)
         begin = callInfo.getArg(0);
     else
         begin = constant(Int32Value(0));
 
@@ -1686,17 +1696,17 @@ IonBuilder::inlineConstantStringSplit(Ca
     }
     callInfo.setImplicitlyUsedUnchecked();
 
     TemporaryTypeSet::DoubleConversion conversion =
             getInlineReturnTypeSet()->convertDoubleElements(constraints());
     if (conversion == TemporaryTypeSet::AlwaysConvertToDoubles)
         return InliningStatus_NotInlined;
 
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
     current->add(templateConst);
 
     MNewArray* ins = MNewArray::New(alloc(), constraints(), initLength, templateConst,
                                     templateObject->group()->initialHeap(constraints()), pc);
 
     current->add(ins);
     current->push(ins);
 
@@ -1756,17 +1766,18 @@ IonBuilder::inlineStringSplit(CallInfo& 
         return InliningStatus_NotInlined;
 
     if (!key.maybeTypes()->hasType(TypeSet::StringType())) {
         key.freeze(constraints());
         return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
-    MConstant* templateObjectDef = MConstant::New(alloc(), ObjectValue(*templateObject), constraints());
+    MConstant* templateObjectDef = MConstant::New(alloc(), ObjectValue(*templateObject),
+                                                  constraints(), this);
     current->add(templateObjectDef);
 
     MStringSplit* ins = MStringSplit::New(alloc(), constraints(), callInfo.thisArg(),
                                           callInfo.getArg(0), templateObjectDef);
     current->add(ins);
     current->push(ins);
 
     return InliningStatus_Inlined;
@@ -2081,17 +2092,17 @@ IonBuilder::inlineObjectCreate(CallInfo&
         MOZ_ASSERT(types->getKnownMIRType() == MIRType_Object);
     } else {
         if (arg->type() != MIRType_Null)
             return InliningStatus_NotInlined;
     }
 
     callInfo.setImplicitlyUsedUnchecked();
 
-    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject);
+    MConstant* templateConst = MConstant::NewConstraintlessObject(alloc(), templateObject, this);
     current->add(templateConst);
     MNewObject* ins = MNewObject::New(alloc(), constraints(), templateConst,
                                       templateObject->group()->initialHeap(constraints()),
                                       MNewObject::ObjectCreate);
     current->add(ins);
     current->push(ins);
     if (!resumeAfter(ins))
         return InliningStatus_Error;
--- a/js/src/jit/MIR.cpp
+++ b/js/src/jit/MIR.cpp
@@ -636,43 +636,46 @@ MDefinition::replaceAllLiveUsesWith(MDef
 
 bool
 MDefinition::emptyResultTypeSet() const
 {
     return resultTypeSet() && resultTypeSet()->empty();
 }
 
 MConstant*
-MConstant::New(TempAllocator& alloc, const Value& v, CompilerConstraintList* constraints)
-{
-    return new(alloc) MConstant(v, constraints);
+MConstant::New(TempAllocator& alloc, const Value& v,
+               CompilerConstraintList* constraints, MIRGenerator* gen)
+{
+    return new(alloc) MConstant(v, constraints, gen);
 }
 
 MConstant*
-MConstant::NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type, CompilerConstraintList* constraints)
+MConstant::NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
+                         CompilerConstraintList* constraints, MIRGenerator* gen)
 {
     MOZ_ASSERT(!IsSimdType(type));
-    MOZ_ASSERT_IF(type == MIRType_Float32, IsNaN(v.toDouble()) || v.toDouble() == double(float(v.toDouble())));
-    MConstant* constant = new(alloc) MConstant(v, constraints);
+    MOZ_ASSERT_IF(type == MIRType_Float32,
+                  IsNaN(v.toDouble()) || v.toDouble() == double(float(v.toDouble())));
+    MConstant* constant = new(alloc) MConstant(v, constraints, gen);
     constant->setResultType(type);
     return constant;
 }
 
 MConstant*
 MConstant::NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type)
 {
     if (type == MIRType_Float32)
         return NewTypedValue(alloc, Float32Value(v.toNumber()), type);
     return NewTypedValue(alloc, v, type);
 }
 
 MConstant*
-MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v)
-{
-    return new(alloc) MConstant(v);
+MConstant::NewConstraintlessObject(TempAllocator& alloc, JSObject* v, MIRGenerator* gen)
+{
+    return new(alloc) MConstant(v, gen);
 }
 
 static TemporaryTypeSet*
 MakeSingletonTypeSetFromKey(CompilerConstraintList* constraints, TypeSet::ObjectKey* key)
 {
     // Invalidate when this object's ObjectGroup gets unknown properties. This
     // happens for instance when we mutate an object's __proto__, in this case
     // we want to invalidate and mark this TypeSet as containing AnyObject
@@ -698,24 +701,25 @@ jit::MakeSingletonTypeSet(CompilerConstr
 
 static TemporaryTypeSet*
 MakeUnknownTypeSet()
 {
     LifoAlloc* alloc = GetJitContext()->temp->lifoAlloc();
     return alloc->new_<TemporaryTypeSet>(alloc, TypeSet::UnknownType());
 }
 
-MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints)
+MConstant::MConstant(const js::Value& vp, CompilerConstraintList* constraints, MIRGenerator* gen)
   : value_(vp)
 {
     setResultType(MIRTypeFromValue(vp));
     if (vp.isObject()) {
         // Create a singleton type set for the object. This isn't necessary for
         // other types as the result type encodes all needed information.
-        MOZ_ASSERT(!IsInsideNursery(&vp.toObject()));
+        MOZ_ASSERT(gen);
+        MOZ_ASSERT_IF(IsInsideNursery(&vp.toObject()), !gen->safeForMinorGC());
         setResultTypeSet(MakeSingletonTypeSet(constraints, &vp.toObject()));
     }
     if (vp.isMagic() && vp.whyMagic() == JS_UNINITIALIZED_LEXICAL) {
         // JS_UNINITIALIZED_LEXICAL does not escape to script and is not
         // observed in type sets. However, it may flow around freely during
         // Ion compilation. Give it an unknown typeset to poison any type sets
         // it merges with.
         //
@@ -724,20 +728,21 @@ MConstant::MConstant(const js::Value& vp
         setResultTypeSet(MakeUnknownTypeSet());
     }
 
     MOZ_ASSERT_IF(vp.isString(), vp.toString()->isAtom());
 
     setMovable();
 }
 
-MConstant::MConstant(JSObject* obj)
+MConstant::MConstant(JSObject* obj, MIRGenerator* gen)
   : value_(ObjectValue(*obj))
 {
-    MOZ_ASSERT(!IsInsideNursery(obj));
+    MOZ_ASSERT(gen);
+    MOZ_ASSERT_IF(IsInsideNursery(obj), !gen->safeForMinorGC());
     setResultType(MIRType_Object);
     setMovable();
 }
 
 HashNumber
 MConstant::valueHash() const
 {
     // Fold all 64 bits into the 32-bit result. It's tempting to just discard
@@ -837,49 +842,16 @@ MConstant::canProduceFloat32() const
 
     if (type() == MIRType_Int32)
         return IsFloat32Representable(static_cast<double>(value_.toInt32()));
     if (type() == MIRType_Double)
         return IsFloat32Representable(value_.toDouble());
     return true;
 }
 
-MNurseryObject::MNurseryObject(JSObject* obj, uint32_t index, CompilerConstraintList* constraints)
-  : index_(index)
-{
-    setResultType(MIRType_Object);
-
-    MOZ_ASSERT(IsInsideNursery(obj));
-    MOZ_ASSERT(!obj->isSingleton());
-    setResultTypeSet(MakeSingletonTypeSet(constraints, obj));
-
-    setMovable();
-}
-
-MNurseryObject*
-MNurseryObject::New(TempAllocator& alloc, JSObject* obj, uint32_t index,
-                    CompilerConstraintList* constraints)
-{
-    return new(alloc) MNurseryObject(obj, index, constraints);
-}
-
-HashNumber
-MNurseryObject::valueHash() const
-{
-    return HashNumber(index_);
-}
-
-bool
-MNurseryObject::congruentTo(const MDefinition* ins) const
-{
-    if (!ins->isNurseryObject())
-        return false;
-    return ins->toNurseryObject()->index_ == index_;
-}
-
 MDefinition*
 MSimdValueX4::foldsTo(TempAllocator& alloc)
 {
     DebugOnly<MIRType> scalarType = SimdTypeToScalarType(type());
     bool allConstants = true;
     bool allSame = true;
 
     for (size_t i = 0; i < 4; ++i) {
@@ -5015,17 +4987,18 @@ jit::PropertyReadNeedsTypeBarrier(JSCont
             }
         }
     }
 
     return res;
 }
 
 BarrierKind
-jit::PropertyReadOnPrototypeNeedsTypeBarrier(CompilerConstraintList* constraints,
+jit::PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder,
+                                             CompilerConstraintList* constraints,
                                              MDefinition* obj, PropertyName* name,
                                              TemporaryTypeSet* observed)
 {
     if (observed->unknown())
         return BarrierKind::NoBarrier;
 
     TypeSet* types = obj->resultTypeSet();
     if (!types || types->unknownObject())
@@ -5037,17 +5010,18 @@ jit::PropertyReadOnPrototypeNeedsTypeBar
         TypeSet::ObjectKey* key = types->getObject(i);
         if (!key)
             continue;
         while (true) {
             if (!key->hasStableClassAndProto(constraints))
                 return BarrierKind::TypeSet;
             if (!key->proto().isObject())
                 break;
-            key = TypeSet::ObjectKey::get(key->proto().toObject());
+            JSObject* proto = builder->checkNurseryObject(key->proto().toObject());
+            key = TypeSet::ObjectKey::get(proto);
             BarrierKind kind = PropertyReadNeedsTypeBarrier(constraints, key, name, observed);
             if (kind == BarrierKind::TypeSet)
                 return BarrierKind::TypeSet;
 
             if (kind == BarrierKind::TypeTagOnly) {
                 MOZ_ASSERT(res == BarrierKind::NoBarrier || res == BarrierKind::TypeTagOnly);
                 res = BarrierKind::TypeTagOnly;
             } else {
@@ -5229,28 +5203,28 @@ TryAddTypeBarrierForWrite(TempAllocator&
         kind = BarrierKind::TypeTagOnly;
 
     MInstruction* ins = MMonitorTypes::New(alloc, *pvalue, types, kind);
     current->add(ins);
     return true;
 }
 
 static MInstruction*
-AddGroupGuard(TempAllocator& alloc, MBasicBlock* current, MDefinition* obj,
+AddGroupGuard(MIRGenerator* gen, MBasicBlock* current, MDefinition* obj,
               TypeSet::ObjectKey* key, bool bailOnEquality)
 {
     MInstruction* guard;
 
     if (key->isGroup()) {
-        guard = MGuardObjectGroup::New(alloc, obj, key->group(), bailOnEquality,
+        guard = MGuardObjectGroup::New(gen->alloc(), obj, key->group(), bailOnEquality,
                                        Bailout_ObjectIdentityOrTypeGuard);
     } else {
-        MConstant* singletonConst = MConstant::NewConstraintlessObject(alloc, key->singleton());
+        MConstant* singletonConst = MConstant::NewConstraintlessObject(gen->alloc(), key->singleton(), gen);
         current->add(singletonConst);
-        guard = MGuardObjectIdentity::New(alloc, obj, singletonConst, bailOnEquality);
+        guard = MGuardObjectIdentity::New(gen->alloc(), obj, singletonConst, bailOnEquality);
     }
 
     current->add(guard);
 
     // For now, never move object group / identity guards.
     guard->setNotMovable();
 
     return guard;
@@ -5263,17 +5237,17 @@ jit::CanWriteProperty(TempAllocator& all
                       MIRType implicitType /* = MIRType_None */)
 {
     if (property.couldBeConstant(constraints))
         return false;
     return PropertyTypeIncludes(alloc, property, value, implicitType);
 }
 
 bool
-jit::PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* constraints,
+jit::PropertyWriteNeedsTypeBarrier(MIRGenerator* gen, CompilerConstraintList* constraints,
                                    MBasicBlock* current, MDefinition** pobj,
                                    PropertyName* name, MDefinition** pvalue,
                                    bool canModify, MIRType implicitType)
 {
     // If any value being written is not reflected in the type information for
     // objects which obj could represent, a type barrier is needed when writing
     // the value. As for propertyReadNeedsTypeBarrier, this only applies for
     // properties that are accounted for by type information, i.e. normal data
@@ -5296,24 +5270,24 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
 
         // TI doesn't track TypedArray indexes and should never insert a type
         // barrier for them.
         if (!name && IsAnyTypedArrayClass(key->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         HeapTypeSetKey property = key->property(id);
-        if (!CanWriteProperty(alloc, constraints, property, *pvalue, implicitType)) {
+        if (!CanWriteProperty(gen->alloc(), constraints, property, *pvalue, implicitType)) {
             // Either pobj or pvalue needs to be modified to filter out the
             // types which the value could have but are not in the property,
             // or a VM call is required. A VM call is always required if pobj
             // and pvalue cannot be modified.
             if (!canModify)
                 return true;
-            success = TryAddTypeBarrierForWrite(alloc, constraints, current, types, name, pvalue,
+            success = TryAddTypeBarrierForWrite(gen->alloc(), constraints, current, types, name, pvalue,
                                                 implicitType);
             break;
         }
     }
 
     if (success)
         return false;
 
@@ -5329,17 +5303,17 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
         TypeSet::ObjectKey* key = types->getObject(i);
         if (!key || key->unknownProperties())
             continue;
         if (!name && IsAnyTypedArrayClass(key->clasp()))
             continue;
 
         jsid id = name ? NameToId(name) : JSID_VOID;
         HeapTypeSetKey property = key->property(id);
-        if (CanWriteProperty(alloc, constraints, property, *pvalue, implicitType))
+        if (CanWriteProperty(gen->alloc(), constraints, property, *pvalue, implicitType))
             continue;
 
         if ((property.maybeTypes() && !property.maybeTypes()->empty()) || excluded)
             return true;
         excluded = key;
     }
 
     MOZ_ASSERT(excluded);
@@ -5350,11 +5324,11 @@ jit::PropertyWriteNeedsTypeBarrier(TempA
     if (excluded->isGroup()) {
         if (UnboxedLayout* layout = excluded->group()->maybeUnboxedLayout()) {
             if (layout->nativeGroup())
                 return true;
             excluded->watchStateChangeForUnboxedConvertedToNative(constraints);
         }
     }
 
-    *pobj = AddGroupGuard(alloc, current, *pobj, excluded, /* bailOnEquality = */ true);
+    *pobj = AddGroupGuard(gen, current, *pobj, excluded, /* bailOnEquality = */ true);
     return false;
 }
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -1304,27 +1304,30 @@ class MLimitedTruncate
 };
 
 // A constant js::Value.
 class MConstant : public MNullaryInstruction
 {
     Value value_;
 
   protected:
-    MConstant(const Value& v, CompilerConstraintList* constraints);
-    explicit MConstant(JSObject* obj);
+    MConstant(const Value& v, CompilerConstraintList* constraints, MIRGenerator* gen);
+    explicit MConstant(JSObject* obj, MIRGenerator* gen);
 
   public:
     INSTRUCTION_HEADER(Constant)
     static MConstant* New(TempAllocator& alloc, const Value& v,
-                          CompilerConstraintList* constraints = nullptr);
+                          CompilerConstraintList* constraints = nullptr,
+                          MIRGenerator* gen = nullptr);
     static MConstant* NewTypedValue(TempAllocator& alloc, const Value& v, MIRType type,
-                                    CompilerConstraintList* constraints = nullptr);
+                                    CompilerConstraintList* constraints = nullptr,
+                                    MIRGenerator* gen = nullptr);
     static MConstant* NewAsmJS(TempAllocator& alloc, const Value& v, MIRType type);
-    static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v);
+    static MConstant* NewConstraintlessObject(TempAllocator& alloc, JSObject* v,
+                                              MIRGenerator* gen);
 
     const js::Value& value() const {
         return value_;
     }
     const js::Value* vp() const {
         return &value_;
     }
     bool valueToBoolean() const {
@@ -1356,43 +1359,16 @@ class MConstant : public MNullaryInstruc
     bool needTruncation(TruncateKind kind) override;
     void truncate() override;
 
     bool canProduceFloat32() const override;
 
     ALLOW_CLONE(MConstant)
 };
 
-class MNurseryObject : public MNullaryInstruction
-{
-    // Index in MIRGenerator::nurseryObjects_.
-    uint32_t index_;
-
-  protected:
-    MNurseryObject(JSObject* obj, uint32_t index, CompilerConstraintList* constraints);
-
-  public:
-    INSTRUCTION_HEADER(NurseryObject)
-    static MNurseryObject* New(TempAllocator& alloc, JSObject* obj, uint32_t index,
-                               CompilerConstraintList* constraints = nullptr);
-
-    HashNumber valueHash() const override;
-    bool congruentTo(const MDefinition* ins) const override;
-
-    uint32_t index() const {
-        return index_;
-    }
-
-    AliasSet getAliasSet() const override {
-        return AliasSet::None();
-    }
-
-    ALLOW_CLONE(MNurseryObject)
-};
-
 // Generic constructor of SIMD valuesX4.
 class MSimdValueX4
   : public MQuaternaryInstruction,
     public Mix4Policy<SimdScalarPolicy<0>, SimdScalarPolicy<1>,
                       SimdScalarPolicy<2>, SimdScalarPolicy<3> >::Data
 {
   protected:
     MSimdValueX4(MIRType type, MDefinition* x, MDefinition* y, MDefinition* z, MDefinition* w)
@@ -13618,27 +13594,28 @@ MIRType DenseNativeElementType(CompilerC
 BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx,
                                          CompilerConstraintList* constraints,
                                          TypeSet::ObjectKey* key, PropertyName* name,
                                          TemporaryTypeSet* observed, bool updateObserved);
 BarrierKind PropertyReadNeedsTypeBarrier(JSContext* propertycx,
                                          CompilerConstraintList* constraints,
                                          MDefinition* obj, PropertyName* name,
                                          TemporaryTypeSet* observed);
-BarrierKind PropertyReadOnPrototypeNeedsTypeBarrier(CompilerConstraintList* constraints,
+BarrierKind PropertyReadOnPrototypeNeedsTypeBarrier(IonBuilder* builder,
+                                                    CompilerConstraintList* constraints,
                                                     MDefinition* obj, PropertyName* name,
                                                     TemporaryTypeSet* observed);
 bool PropertyReadIsIdempotent(CompilerConstraintList* constraints,
                               MDefinition* obj, PropertyName* name);
 void AddObjectsForPropertyRead(MDefinition* obj, PropertyName* name,
                                TemporaryTypeSet* observed);
 bool CanWriteProperty(TempAllocator& alloc, CompilerConstraintList* constraints,
                       HeapTypeSetKey property, MDefinition* value,
                       MIRType implicitType = MIRType_None);
-bool PropertyWriteNeedsTypeBarrier(TempAllocator& alloc, CompilerConstraintList* constraints,
+bool PropertyWriteNeedsTypeBarrier(MIRGenerator* gen, CompilerConstraintList* constraints,
                                    MBasicBlock* current, MDefinition** pobj,
                                    PropertyName* name, MDefinition** pvalue,
                                    bool canModify, MIRType implicitType = MIRType_None);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_MIR_h */
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -88,16 +88,23 @@ class MIRGenerator
     bool isProfilerInstrumentationEnabled() {
         return !compilingAsmJS() && instrumentedProfiling();
     }
 
     bool isOptimizationTrackingEnabled() {
         return isProfilerInstrumentationEnabled() && !info().isAnalysis();
     }
 
+    bool safeForMinorGC() const {
+        return safeForMinorGC_;
+    }
+    void setNotSafeForMinorGC() {
+        safeForMinorGC_ = false;
+    }
+
     // Whether the main thread is trying to cancel this build.
     bool shouldCancel(const char* why) {
         maybePause();
         return cancelBuild_;
     }
     void cancel() {
         cancelBuild_ = true;
     }
@@ -189,22 +196,17 @@ class MIRGenerator
 
     // Keep track of whether frame arguments are modified during execution.
     // RegAlloc needs to know this as spilling values back to their register
     // slots is not compatible with that.
     bool modifiesFrameArguments_;
 
     bool instrumentedProfiling_;
     bool instrumentedProfilingIsCached_;
-
-    // List of nursery objects used by this compilation. Can be traced by a
-    // minor GC while compilation happens off-thread. This Vector should only
-    // be accessed on the main thread (IonBuilder, nursery GC or
-    // CodeGenerator::link).
-    ObjectVector nurseryObjects_;
+    bool safeForMinorGC_;
 
     void addAbortedPreliminaryGroup(ObjectGroup* group);
 
     Label* outOfBoundsLabel_;
     // Label where we should jump in asm.js mode, in the case where we have an
     // invalid conversion or a loss of precision (when converting from a
     // floating point SIMD type into an integer SIMD type).
     Label* conversionErrorLabel_;
@@ -224,22 +226,16 @@ class MIRGenerator
 
   public:
     AsmJSPerfSpewer& perfSpewer() { return asmJSPerfSpewer_; }
 #endif
 
   public:
     const JitCompileOptions options;
 
-    void traceNurseryObjects(JSTracer* trc);
-
-    const ObjectVector& nurseryObjects() const {
-        return nurseryObjects_;
-    }
-
     Label* conversionErrorLabel() const {
         MOZ_ASSERT((conversionErrorLabel_ != nullptr) == compilingAsmJS());
         return conversionErrorLabel_;
     }
     Label* outOfBoundsLabel() const {
         MOZ_ASSERT(compilingAsmJS());
         return outOfBoundsLabel_;
     }
--- a/js/src/jit/MIRGraph.cpp
+++ b/js/src/jit/MIRGraph.cpp
@@ -37,17 +37,17 @@ MIRGenerator::MIRGenerator(CompileCompar
     maxAsmJSStackArgBytes_(0),
     performsCall_(false),
     usesSimd_(false),
     usesSimdCached_(false),
     minAsmJSHeapLength_(0),
     modifiesFrameArguments_(false),
     instrumentedProfiling_(false),
     instrumentedProfilingIsCached_(false),
-    nurseryObjects_(*alloc),
+    safeForMinorGC_(true),
     outOfBoundsLabel_(outOfBoundsLabel),
     conversionErrorLabel_(conversionErrorLabel),
 #if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB)
     usesSignalHandlersForAsmJSOOB_(usesSignalHandlersForAsmJSOOB),
 #endif
     options(options),
     gs_(alloc)
 { }
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -7,17 +7,16 @@
 #ifndef jit_MOpcodes_h
 #define jit_MOpcodes_h
 
 namespace js {
 namespace jit {
 
 #define MIR_OPCODE_LIST(_)                                                  \
     _(Constant)                                                             \
-    _(NurseryObject)                                                        \
     _(SimdBox)                                                              \
     _(SimdUnbox)                                                            \
     _(SimdValueX4)                                                          \
     _(SimdSplatX4)                                                          \
     _(SimdConstant)                                                         \
     _(SimdConvert)                                                          \
     _(SimdReinterpretCast)                                                  \
     _(SimdExtractElement)                                                   \
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -97,39 +97,57 @@ MacroAssembler::guardTypeSet(const Sourc
         Label fail;
         Register obj = extractObject(address, scratch);
         guardObjectType(obj, types, scratch, &fail);
         jump(&matched);
         bind(&fail);
 
         if (obj == scratch)
             extractObject(address, scratch);
-        guardTypeSetMightBeIncomplete(obj, scratch, &matched);
+        guardTypeSetMightBeIncomplete(types, obj, scratch, &matched);
 
         assumeUnreachable("Unexpected object type");
 #endif
     }
 
     bind(&matched);
 }
 
+template <typename TypeSet>
 void
-MacroAssembler::guardTypeSetMightBeIncomplete(Register obj, Register scratch, Label* label)
+MacroAssembler::guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Register scratch, Label* label)
 {
     // Type set guards might miss when an object's group changes. In this case
-    // either its properties will become unknown, or it will change to a native
-    // object with an original unboxed group. Jump to label if this might have
-    // happened for the input object.
+    // either its old group's properties will become unknown, or it will change
+    // to a native object with an original unboxed group. Jump to label if this
+    // might have happened for the input object.
+
+    if (types->unknownObject()) {
+        jump(label);
+        return;
+    }
 
     loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
     load32(Address(scratch, ObjectGroup::offsetOfFlags()), scratch);
-    branchTest32(Assembler::NonZero, scratch, Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), label);
     and32(Imm32(OBJECT_FLAG_ADDENDUM_MASK), scratch);
     branch32(Assembler::Equal,
              scratch, Imm32(ObjectGroup::addendumOriginalUnboxedGroupValue()), label);
+
+    for (size_t i = 0; i < types->getObjectCount(); i++) {
+        if (JSObject* singleton = types->getSingletonNoBarrier(i)) {
+            movePtr(ImmGCPtr(singleton), scratch);
+            loadPtr(Address(scratch, JSObject::offsetOfGroup()), scratch);
+        } else if (ObjectGroup* group = types->getGroupNoBarrier(i)) {
+            movePtr(ImmGCPtr(group), scratch);
+        } else {
+            continue;
+        }
+        branchTest32(Assembler::NonZero, Address(scratch, ObjectGroup::offsetOfFlags()),
+                     Imm32(OBJECT_FLAG_UNKNOWN_PROPERTIES), label);
+    }
 }
 
 void
 MacroAssembler::guardObjectType(Register obj, const TypeSet *types,
                                 Register scratch, Label* miss)
 {
     MOZ_ASSERT(!types->unknown());
     MOZ_ASSERT(!types->hasType(TypeSet::AnyObjectType()));
@@ -200,16 +218,20 @@ MacroAssembler::guardObjectType(Register
 
 template void MacroAssembler::guardTypeSet(const Address& address, const TypeSet* types,
                                            BarrierKind kind, Register scratch, Label* miss);
 template void MacroAssembler::guardTypeSet(const ValueOperand& value, const TypeSet* types,
                                            BarrierKind kind, Register scratch, Label* miss);
 template void MacroAssembler::guardTypeSet(const TypedOrValueRegister& value, const TypeSet* types,
                                            BarrierKind kind, Register scratch, Label* miss);
 
+template void MacroAssembler::guardTypeSetMightBeIncomplete(const TemporaryTypeSet* types,
+                                                            Register obj, Register scratch,
+                                                            Label* label);
+
 template<typename S, typename T>
 static void
 StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, const T& dest,
                        unsigned numElems)
 {
     switch (arrayType) {
       case Scalar::Float32:
         masm.storeFloat32(value, dest);
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -350,17 +350,18 @@ class MacroAssembler : public MacroAssem
 
     // Emits a test of a value against all types in a TypeSet. A scratch
     // register is required.
     template <typename Source>
     void guardTypeSet(const Source& address, const TypeSet* types, BarrierKind kind, Register scratch, Label* miss);
 
     void guardObjectType(Register obj, const TypeSet* types, Register scratch, Label* miss);
 
-    void guardTypeSetMightBeIncomplete(Register obj, Register scratch, Label* label);
+    template <typename TypeSet>
+    void guardTypeSetMightBeIncomplete(TypeSet* types, Register obj, Register scratch, Label* label);
 
     void loadObjShape(Register objReg, Register dest) {
         loadPtr(Address(objReg, JSObject::offsetOfShape()), dest);
     }
     void loadObjGroup(Register objReg, Register dest) {
         loadPtr(Address(objReg, JSObject::offsetOfGroup()), dest);
     }
     void loadBaseShape(Register objReg, Register dest) {
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -809,22 +809,16 @@ static void
 TraceOneDataRelocation(JSTracer* trc, Iter* iter)
 {
     Instruction* ins = iter->cur();
     Register dest;
     Assembler::RelocStyle rs;
     const void* prior = Assembler::GetPtr32Target(iter, &dest, &rs);
     void* ptr = const_cast<void*>(prior);
 
-    // The low bit shouldn't be set. If it is, we probably got a dummy
-    // pointer inserted by CodeGenerator::visitNurseryObject, but we
-    // shouldn't be able to trigger GC before those are patched to their
-    // real values.
-    MOZ_ASSERT(!(uintptr_t(ptr) & 0x1));
-
     // No barrier needed since these are constants.
     TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(&ptr),
                                              "ion-masm-ptr");
 
     if (ptr != prior) {
         MacroAssemblerARM::ma_mov_patch(Imm32(int32_t(ptr)), dest, Assembler::Always, rs, ins);
 
         // L_LDR won't cause any instructions to be updated.
@@ -858,60 +852,16 @@ TraceDataRelocations(JSTracer* trc, ARMB
 
 void
 Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader)
 {
     ::TraceDataRelocations(trc, code->raw(), reader);
 }
 
 void
-Assembler::FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader,
-                               const ObjectVector& nurseryObjects)
-{
-    MOZ_ASSERT(!nurseryObjects.empty());
-
-    uint8_t* buffer = code->raw();
-    bool hasNurseryPointers = false;
-
-    while (reader.more()) {
-        size_t offset = reader.readUnsigned();
-        InstructionIterator iter((Instruction*)(buffer + offset));
-        Instruction* ins = iter.cur();
-        Register dest;
-        Assembler::RelocStyle rs;
-        const void* prior = Assembler::GetPtr32Target(&iter, &dest, &rs);
-        void* ptr = const_cast<void*>(prior);
-        uintptr_t word = reinterpret_cast<uintptr_t>(ptr);
-
-        if (!(word & 0x1))
-            continue;
-
-        uint32_t index = word >> 1;
-        JSObject* obj = nurseryObjects[index];
-        MacroAssembler::ma_mov_patch(Imm32(int32_t(obj)), dest, Assembler::Always, rs, ins);
-
-        if (rs != Assembler::L_LDR) {
-            // L_LDR won't cause any instructions to be updated.
-            AutoFlushICache::flush(uintptr_t(ins), 4);
-            AutoFlushICache::flush(uintptr_t(ins->next()), 4);
-        }
-
-        // Either all objects are still in the nursery, or all objects are
-        // tenured.
-        MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj));
-
-        if (!hasNurseryPointers && IsInsideNursery(obj))
-            hasNurseryPointers = true;
-    }
-
-    if (hasNurseryPointers)
-        cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code);
-}
-
-void
 Assembler::copyJumpRelocationTable(uint8_t* dest)
 {
     if (jumpRelocations_.length())
         memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length());
 }
 
 void
 Assembler::copyDataRelocationTable(uint8_t* dest)
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1280,18 +1280,22 @@ class Assembler : public AssemblerShared
     void trace(JSTracer* trc);
     void writeRelocation(BufferOffset src) {
         tmpJumpRelocations_.append(src);
     }
 
     // As opposed to x86/x64 version, the data relocation has to be executed
     // before to recover the pointer, and not after.
     void writeDataRelocation(ImmGCPtr ptr) {
-        if (ptr.value)
-            tmpDataRelocations_.append(nextOffset());
+        if (ptr.value) {
+            if (gc::IsInsideNursery(ptr.value))
+                embedsNurseryPointers_ = true;
+            if (ptr.value)
+                tmpDataRelocations_.append(nextOffset());
+        }
     }
     void writePrebarrierOffset(CodeOffsetLabel label) {
         tmpPreBarriers_.append(BufferOffset(label.offset()));
     }
 
     enum RelocBranchStyle {
         B_MOVWT,
         B_LDR_BX,
@@ -1638,19 +1642,16 @@ class Assembler : public AssemblerShared
     void call(void* target);
 
     void as_bkpt();
 
   public:
     static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
     static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
 
-    static void FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader,
-                                    const ObjectVector& nurseryObjects);
-
     static bool SupportsFloatingPoint() {
         return HasVFP();
     }
     static bool SupportsSimd() {
         return js::jit::SupportsSimd;
     }
 
   protected:
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -2069,21 +2069,16 @@ MacroAssemblerARMCompat::movePtr(ImmWord
     ma_mov(Imm32(imm.value), dest);
 }
 void
 MacroAssemblerARMCompat::movePtr(ImmGCPtr imm, Register dest)
 {
     ma_mov(imm, dest);
 }
 void
-MacroAssemblerARMCompat::movePtr(ImmMaybeNurseryPtr imm, Register dest)
-{
-    movePtr(noteMaybeNurseryPtr(imm), dest);
-}
-void
 MacroAssemblerARMCompat::movePtr(ImmPtr imm, Register dest)
 {
     movePtr(ImmWord(uintptr_t(imm.value)), dest);
 }
 void
 MacroAssemblerARMCompat::movePtr(AsmJSImmPtr imm, Register dest)
 {
     RelocStyle rs;
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -638,19 +638,16 @@ class MacroAssemblerARMCompat : public M
     }
     void push(ImmWord imm) {
         push(Imm32(imm.value));
     }
     void push(ImmGCPtr imm) {
         ma_mov(imm, ScratchRegister);
         ma_push(ScratchRegister);
     }
-    void push(ImmMaybeNurseryPtr imm) {
-        push(noteMaybeNurseryPtr(imm));
-    }
     void push(const Address& addr) {
         ma_ldr(addr, ScratchRegister);
         ma_push(ScratchRegister);
     }
     void push(Register reg) {
         ma_push(reg);
     }
     void push(FloatRegister reg) {
@@ -1069,19 +1066,16 @@ class MacroAssemblerARMCompat : public M
         ma_cmp(secondScratchReg_, ptr);
         return jumpWithPatch(label, cond);
     }
     void branchPtr(Condition cond, Address addr, ImmGCPtr ptr, Label* label) {
         ma_ldr(addr, secondScratchReg_);
         ma_cmp(secondScratchReg_, ptr);
         ma_b(label, cond);
     }
-    void branchPtr(Condition cond, Address addr, ImmMaybeNurseryPtr ptr, Label* label) {
-        branchPtr(cond, addr, noteMaybeNurseryPtr(ptr), label);
-    }
     void branchPtr(Condition cond, Address addr, ImmWord ptr, Label* label) {
         ma_ldr(addr, secondScratchReg_);
         ma_cmp(secondScratchReg_, ptr);
         ma_b(label, cond);
     }
     void branchPtr(Condition cond, Address addr, ImmPtr ptr, Label* label) {
         branchPtr(cond, addr, ImmWord(uintptr_t(ptr.value)), label);
     }
@@ -1210,17 +1204,17 @@ class MacroAssemblerARMCompat : public M
     void tagValue(JSValueType type, Register payload, ValueOperand dest);
 
     void pushValue(ValueOperand val);
     void popValue(ValueOperand val);
     void pushValue(const Value& val) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         push(Imm32(jv.s.tag));
         if (val.isMarkable())
-            push(ImmMaybeNurseryPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())));
+            push(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())));
         else
             push(Imm32(jv.s.payload.i32));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         ma_push(reg);
     }
     void pushValue(const Address& addr);
@@ -1293,17 +1287,16 @@ class MacroAssemblerARMCompat : public M
     void move32(Imm32 imm, Register dest);
     void move32(Register src, Register dest);
 
     void movePtr(Register src, Register dest);
     void movePtr(ImmWord imm, Register dest);
     void movePtr(ImmPtr imm, Register dest);
     void movePtr(AsmJSImmPtr imm, Register dest);
     void movePtr(ImmGCPtr imm, Register dest);
-    void movePtr(ImmMaybeNurseryPtr imm, Register dest);
 
     void load8SignExtend(const Address& address, Register dest);
     void load8SignExtend(const BaseIndex& src, Register dest);
 
     void load8ZeroExtend(const Address& address, Register dest);
     void load8ZeroExtend(const BaseIndex& src, Register dest);
 
     void load16SignExtend(const Address& address, Register dest);
--- a/js/src/jit/mips/Assembler-mips.cpp
+++ b/js/src/jit/mips/Assembler-mips.cpp
@@ -283,22 +283,16 @@ Assembler::TraceJumpRelocations(JSTracer
 }
 
 static void
 TraceOneDataRelocation(JSTracer* trc, Instruction* inst)
 {
     void* ptr = (void*)Assembler::ExtractLuiOriValue(inst, inst->next());
     void* prior = ptr;
 
-    // The low bit shouldn't be set. If it is, we probably got a dummy
-    // pointer inserted by CodeGenerator::visitNurseryObject, but we
-    // shouldn't be able to trigger GC before those are patched to their
-    // real values.
-    MOZ_ASSERT(!(uintptr_t(ptr) & 0x1));
-
     // No barrier needed since these are constants.
     TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(&ptr),
                                                  "ion-masm-ptr");
     if (ptr != prior) {
         Assembler::UpdateLuiOriValue(inst, inst->next(), uint32_t(ptr));
         AutoFlushICache::flush(uintptr_t(inst), 8);
     }
 }
@@ -325,53 +319,16 @@ TraceDataRelocations(JSTracer* trc, MIPS
 
 void
 Assembler::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader)
 {
     ::TraceDataRelocations(trc, code->raw(), reader);
 }
 
 void
-Assembler::FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader,
-                               const ObjectVector& nurseryObjects)
-{
-    MOZ_ASSERT(!nurseryObjects.empty());
-
-    uint8_t* buffer = code->raw();
-    bool hasNurseryPointers = false;
-
-    while (reader.more()) {
-        size_t offset = reader.readUnsigned();
-        Instruction* inst = (Instruction*)(buffer + offset);
-
-        void* ptr = (void*)Assembler::ExtractLuiOriValue(inst, inst->next());
-        uintptr_t word = uintptr_t(ptr);
-
-        if (!(word & 0x1))
-            continue;
-
-        uint32_t index = word >> 1;
-        JSObject* obj = nurseryObjects[index];
-
-        Assembler::UpdateLuiOriValue(inst, inst->next(), uint32_t(obj));
-        AutoFlushICache::flush(uintptr_t(inst), 8);
-
-        // Either all objects are still in the nursery, or all objects are
-        // tenured.
-        MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj));
-
-        if (!hasNurseryPointers && IsInsideNursery(obj))
-            hasNurseryPointers = true;
-    }
-
-    if (hasNurseryPointers)
-        cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code);
-}
-
-void
 Assembler::copyJumpRelocationTable(uint8_t* dest)
 {
     if (jumpRelocations_.length())
         memcpy(dest, jumpRelocations_.buffer(), jumpRelocations_.length());
 }
 
 void
 Assembler::copyDataRelocationTable(uint8_t* dest)
--- a/js/src/jit/mips/Assembler-mips.h
+++ b/js/src/jit/mips/Assembler-mips.h
@@ -766,18 +766,21 @@ class Assembler : public AssemblerShared
     void trace(JSTracer* trc);
     void writeRelocation(BufferOffset src) {
         jumpRelocations_.writeUnsigned(src.getOffset());
     }
 
     // As opposed to x86/x64 version, the data relocation has to be executed
     // before to recover the pointer, and not after.
     void writeDataRelocation(ImmGCPtr ptr) {
-        if (ptr.value)
+        if (ptr.value) {
+            if (gc::IsInsideNursery(ptr.value))
+                embedsNurseryPointers_ = true;
             dataRelocations_.writeUnsigned(nextOffset().getOffset());
+        }
     }
     void writePrebarrierOffset(CodeOffsetLabel label) {
         preBarriers_.writeUnsigned(label.offset());
     }
 
   public:
     static uintptr_t GetPointer(uint8_t*);
 
@@ -1015,19 +1018,16 @@ class Assembler : public AssemblerShared
     void call(void* target);
 
     void as_break(uint32_t code);
 
   public:
     static void TraceJumpRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
     static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
 
-    static void FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader,
-                                    const ObjectVector& nurseryObjects);
-
     static bool SupportsFloatingPoint() {
 #if (defined(__mips_hard_float) && !defined(__mips_single_float)) || defined(JS_MIPS_SIMULATOR)
         return true;
 #else
         return false;
 #endif
     }
     static bool SupportsSimd() {
--- a/js/src/jit/mips/MacroAssembler-mips.cpp
+++ b/js/src/jit/mips/MacroAssembler-mips.cpp
@@ -1746,21 +1746,16 @@ MacroAssemblerMIPSCompat::movePtr(ImmWor
 
 void
 MacroAssemblerMIPSCompat::movePtr(ImmGCPtr imm, Register dest)
 {
     ma_li(dest, imm);
 }
 
 void
-MacroAssemblerMIPSCompat::movePtr(ImmMaybeNurseryPtr imm, Register dest)
-{
-    movePtr(noteMaybeNurseryPtr(imm), dest);
-}
-void
 MacroAssemblerMIPSCompat::movePtr(ImmPtr imm, Register dest)
 {
     movePtr(ImmWord(uintptr_t(imm.value)), dest);
 }
 void
 MacroAssemblerMIPSCompat::movePtr(AsmJSImmPtr imm, Register dest)
 {
     append(AsmJSAbsoluteLink(CodeOffsetLabel(nextOffset().getOffset()), imm.kind()));
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -810,19 +810,16 @@ public:
         bind(&skipJump);
         return off;
     }
     void branchPtr(Condition cond, Address addr, ImmGCPtr ptr, Label* label) {
         ma_lw(SecondScratchReg, addr);
         ma_li(ScratchRegister, ptr);
         ma_b(SecondScratchReg, ScratchRegister, label, cond);
     }
-    void branchPtr(Condition cond, Address addr, ImmMaybeNurseryPtr ptr, Label* label) {
-        branchPtr(cond, addr, noteMaybeNurseryPtr(ptr), label);
-    }
 
     void branchPtr(Condition cond, Address addr, ImmWord ptr, Label* label) {
         ma_lw(SecondScratchReg, addr);
         ma_b(SecondScratchReg, Imm32(ptr.value), label, cond);
     }
     void branchPtr(Condition cond, Address addr, ImmPtr ptr, Label* label) {
         branchPtr(cond, addr, ImmWord(uintptr_t(ptr.value)), label);
     }
@@ -1223,17 +1220,16 @@ public:
     void move32(Imm32 imm, Register dest);
     void move32(Register src, Register dest);
 
     void movePtr(Register src, Register dest);
     void movePtr(ImmWord imm, Register dest);
     void movePtr(ImmPtr imm, Register dest);
     void movePtr(AsmJSImmPtr imm, Register dest);
     void movePtr(ImmGCPtr imm, Register dest);
-    void movePtr(ImmMaybeNurseryPtr imm, Register dest);
 
     void load8SignExtend(const Address& address, Register dest);
     void load8SignExtend(const BaseIndex& src, Register dest);
 
     void load8ZeroExtend(const Address& address, Register dest);
     void load8ZeroExtend(const BaseIndex& src, Register dest);
 
     void load16SignExtend(const Address& address, Register dest);
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -161,22 +161,16 @@ class MacroAssemblerNone : public Assemb
 
     size_t numCodeLabels() const { MOZ_CRASH(); }
     CodeLabel codeLabel(size_t) { MOZ_CRASH(); }
 
     void trace(JSTracer*) { MOZ_CRASH(); }
     static void TraceJumpRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); }
     static void TraceDataRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); }
 
-    static void FixupNurseryObjects(JSContext*, JitCode*, CompactBufferReader&,
-                                    const ObjectVector&)
-    {
-        MOZ_CRASH();
-    }
-
     static bool SupportsFloatingPoint() { return false; }
     static bool SupportsSimd() { return false; }
 
     void executableCopy(void*) { MOZ_CRASH(); }
     void copyJumpRelocationTable(uint8_t*) { MOZ_CRASH(); }
     void copyDataRelocationTable(uint8_t*) { MOZ_CRASH(); }
     void copyPreBarrierTable(uint8_t*) { MOZ_CRASH(); }
     void processCodeLabels(uint8_t*) { MOZ_CRASH(); }
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -209,80 +209,35 @@ struct PatchedImmPtr {
     explicit PatchedImmPtr(const void* value)
       : value(const_cast<void*>(value))
     { }
 };
 
 class AssemblerShared;
 class ImmGCPtr;
 
-// Used for immediates which require relocation and may be traced during minor GC.
-class ImmMaybeNurseryPtr
-{
-    friend class AssemblerShared;
-    friend class ImmGCPtr;
-    const gc::Cell* value;
-
-    ImmMaybeNurseryPtr() : value(0) {}
-
-  public:
-    explicit ImmMaybeNurseryPtr(const gc::Cell* ptr) : value(ptr)
-    {
-        // asm.js shouldn't be creating GC things
-        MOZ_ASSERT(!IsCompilingAsmJS());
-    }
-};
-
-// Dummy value used for nursery pointers during Ion compilation, see
-// LNurseryObject.
-class IonNurseryPtr
-{
-    const gc::Cell* ptr;
-
-  public:
-    friend class ImmGCPtr;
-
-    explicit IonNurseryPtr(const gc::Cell* ptr) : ptr(ptr)
-    {
-        MOZ_ASSERT(ptr);
-        MOZ_ASSERT(uintptr_t(ptr) & 0x1);
-    }
-};
-
 // Used for immediates which require relocation.
 class ImmGCPtr
 {
   public:
     const gc::Cell* value;
 
     explicit ImmGCPtr(const gc::Cell* ptr) : value(ptr)
     {
-        MOZ_ASSERT_IF(ptr, ptr->isTenured());
-
-        // asm.js shouldn't be creating GC things
-        MOZ_ASSERT(!IsCompilingAsmJS());
-    }
-
-    explicit ImmGCPtr(IonNurseryPtr ptr) : value(ptr.ptr)
-    {
-        MOZ_ASSERT(value);
+        // Nursery pointers can't be used if the main thread might be currently
+        // performing a minor GC.
+        MOZ_ASSERT_IF(ptr && !ptr->isTenured(),
+                      !CurrentThreadIsIonCompilingSafeForMinorGC());
 
         // asm.js shouldn't be creating GC things
         MOZ_ASSERT(!IsCompilingAsmJS());
     }
 
   private:
     ImmGCPtr() : value(0) {}
-
-    friend class AssemblerShared;
-    explicit ImmGCPtr(ImmMaybeNurseryPtr ptr) : value(ptr.value)
-    {
-        // asm.js shouldn't be creating GC things
-        MOZ_ASSERT(!IsCompilingAsmJS());
-    }
 };
 
 // Pointer to be embedded as an immediate that is loaded/stored from by an
 // instruction.
 struct AbsoluteAddress
 {
     void* addr;
 
@@ -981,28 +936,16 @@ class AssemblerShared
     void appendProfilerCallSite(CodeOffsetLabel label) {
         enoughMemory_ &= profilerCallSites_.append(label);
     }
 
     bool embedsNurseryPointers() const {
         return embedsNurseryPointers_;
     }
 
-    ImmGCPtr noteMaybeNurseryPtr(ImmMaybeNurseryPtr ptr) {
-        if (ptr.value && gc::IsInsideNursery(ptr.value)) {
-            // noteMaybeNurseryPtr can be reached from off-thread compilation,
-            // though not with an actual nursery pointer argument in that case.
-            MOZ_ASSERT(GetJitContext()->runtime->onMainThread());
-            // Do not be ion-compiling on the main thread.
-            MOZ_ASSERT(!GetJitContext()->runtime->mainThread()->ionCompiling);
-            embedsNurseryPointers_ = true;
-        }
-        return ImmGCPtr(ptr);
-    }
-
     void append(const CallSiteDesc& desc, size_t currentOffset, size_t framePushed) {
         // framePushed does not include sizeof(AsmJSFrame), so add it in here (see
         // CallSite::stackDepth).
         CallSite callsite(desc, currentOffset, framePushed + sizeof(AsmJSFrame));
         enoughMemory_ &= callsites_.append(callsite);
     }
     CallSiteVector&& extractCallSites() { return Move(callsites_); }
 
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -497,19 +497,16 @@ class MacroAssemblerX64 : public MacroAs
     void cmpPtr(Register lhs, const Imm32 rhs) {
         cmpq(rhs, lhs);
     }
     void cmpPtr(const Operand& lhs, const ImmGCPtr rhs) {
         MOZ_ASSERT(!lhs.containsReg(ScratchReg));
         movePtr(rhs, ScratchReg);
         cmpPtr(lhs, ScratchReg);
     }
-    void cmpPtr(const Operand& lhs, const ImmMaybeNurseryPtr rhs) {
-        cmpPtr(lhs, noteMaybeNurseryPtr(rhs));
-    }
     void cmpPtr(const Operand& lhs, const ImmWord rhs) {
         if ((intptr_t)rhs.value <= INT32_MAX && (intptr_t)rhs.value >= INT32_MIN) {
             cmpPtr(lhs, Imm32((int32_t)rhs.value));
         } else {
             movePtr(rhs, ScratchReg);
             cmpPtr(lhs, ScratchReg);
         }
     }
@@ -728,19 +725,16 @@ class MacroAssemblerX64 : public MacroAs
         mov(imm, dest);
     }
     void movePtr(AsmJSImmPtr imm, Register dest) {
         mov(imm, dest);
     }
     void movePtr(ImmGCPtr imm, Register dest) {
         movq(imm, dest);
     }
-    void movePtr(ImmMaybeNurseryPtr imm, Register dest) {
-        movePtr(noteMaybeNurseryPtr(imm), dest);
-    }
     void loadPtr(AbsoluteAddress address, Register dest) {
         if (X86Encoding::IsAddressImmediate(address.addr)) {
             movq(Operand(address), dest);
         } else {
             mov(ImmPtr(address.addr), ScratchReg);
             loadPtr(Address(ScratchReg, 0x0), dest);
         }
     }
--- a/js/src/jit/x86-shared/Assembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.cpp
@@ -63,75 +63,30 @@ TraceDataRelocations(JSTracer* trc, uint
             Value v = IMPL_TO_JSVAL(layout);
             TraceManuallyBarrieredEdge(trc, &v, "ion-masm-value");
             if (*word != JSVAL_TO_IMPL(v).asBits)
                 *word = JSVAL_TO_IMPL(v).asBits;
             continue;
         }
 #endif
 
-        // The low bit shouldn't be set. If it is, we probably got a dummy
-        // pointer inserted by CodeGenerator::visitNurseryObject, but we
-        // shouldn't be able to trigger GC before those are patched to their
-        // real values.
-        MOZ_ASSERT(!(*reinterpret_cast<uintptr_t*>(ptr) & 0x1));
-
         // No barrier needed since these are constants.
         TraceManuallyBarrieredGenericPointerEdge(trc, reinterpret_cast<gc::Cell**>(ptr),
                                                  "ion-masm-ptr");
     }
 }
 
 
 void
 AssemblerX86Shared::TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader)
 {
     ::TraceDataRelocations(trc, code->raw(), reader);
 }
 
 void
-AssemblerX86Shared::FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader,
-                                        const ObjectVector& nurseryObjects)
-{
-    MOZ_ASSERT(!nurseryObjects.empty());
-
-    uint8_t* buffer = code->raw();
-    bool hasNurseryPointers = false;
-
-    while (reader.more()) {
-        size_t offset = reader.readUnsigned();
-        void** ptr = X86Encoding::GetPointerRef(buffer + offset);
-
-        uintptr_t* word = reinterpret_cast<uintptr_t*>(ptr);
-
-#ifdef JS_PUNBOX64
-        if (*word >> JSVAL_TAG_SHIFT)
-            continue; // This is a Value.
-#endif
-
-        if (!(*word & 0x1))
-            continue;
-
-        uint32_t index = *word >> 1;
-        JSObject* obj = nurseryObjects[index];
-        *word = uintptr_t(obj);
-
-        // Either all objects are still in the nursery, or all objects are
-        // tenured.
-        MOZ_ASSERT_IF(hasNurseryPointers, IsInsideNursery(obj));
-
-        if (!hasNurseryPointers && IsInsideNursery(obj))
-            hasNurseryPointers = true;
-    }
-
-    if (hasNurseryPointers)
-        cx->runtime()->gc.storeBuffer.putWholeCellFromMainThread(code);
-}
-
-void
 AssemblerX86Shared::trace(JSTracer* trc)
 {
     for (size_t i = 0; i < jumps_.length(); i++) {
         RelativePatch& rp = jumps_[i];
         if (rp.kind == Relocation::JITCODE) {
             JitCode* code = JitCode::FromExecutable((uint8_t*)rp.target);
             TraceManuallyBarrieredEdge(trc, &code, "masmrel32");
             MOZ_ASSERT(code == JitCode::FromExecutable((uint8_t*)rp.target));
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -224,18 +224,21 @@ class AssemblerX86Shared : public Assemb
 
     Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
     Vector<RelativePatch, 8, SystemAllocPolicy> jumps_;
     CompactBufferWriter jumpRelocations_;
     CompactBufferWriter dataRelocations_;
     CompactBufferWriter preBarriers_;
 
     void writeDataRelocation(ImmGCPtr ptr) {
-        if (ptr.value)
+        if (ptr.value) {
+            if (gc::IsInsideNursery(ptr.value))
+                embedsNurseryPointers_ = true;
             dataRelocations_.writeUnsigned(masm.currentOffset());
+        }
     }
     void writePrebarrierOffset(CodeOffsetLabel label) {
         preBarriers_.writeUnsigned(label.offset());
     }
 
   protected:
     X86Encoding::BaseAssembler masm;
 
@@ -343,19 +346,16 @@ class AssemblerX86Shared : public Assemb
     // handle NaNs properly and may therefore require a secondary condition.
     // Use NaNCondFromDoubleCondition to determine what else is needed.
     static inline Condition ConditionFromDoubleCondition(DoubleCondition cond) {
         return static_cast<Condition>(cond & ~DoubleConditionBits);
     }
 
     static void TraceDataRelocations(JSTracer* trc, JitCode* code, CompactBufferReader& reader);
 
-    static void FixupNurseryObjects(JSContext* cx, JitCode* code, CompactBufferReader& reader,
-                                    const ObjectVector& nurseryObjects);
-
     // MacroAssemblers hold onto gcthings, so they are traced by the GC.
     void trace(JSTracer* trc);
 
     bool oom() const {
         return AssemblerShared::oom() ||
                masm.oom() ||
                jumpRelocations_.oom() ||
                dataRelocations_.oom() ||
--- a/js/src/jit/x86/Assembler-x86.h
+++ b/js/src/jit/x86/Assembler-x86.h
@@ -211,19 +211,16 @@ class Assembler : public AssemblerX86Sha
     void executableCopy(uint8_t* buffer);
 
     // Actual assembly emitting functions.
 
     void push(ImmGCPtr ptr) {
         masm.push_i32(int32_t(ptr.value));
         writeDataRelocation(ptr);
     }
-    void push(ImmMaybeNurseryPtr ptr) {
-        push(noteMaybeNurseryPtr(ptr));
-    }
     void push(const ImmWord imm) {
         push(Imm32(imm.value));
     }
     void push(const ImmPtr imm) {
         push(ImmWord(uintptr_t(imm.value)));
     }
     void push(FloatRegister src) {
         subl(Imm32(sizeof(double)), StackPointer);
@@ -364,19 +361,16 @@ class Assembler : public AssemblerX86Sha
           case Operand::MEM_ADDRESS32:
             masm.cmpl_i32m(uintptr_t(rhs.value), lhs.address());
             writeDataRelocation(rhs);
             break;
           default:
             MOZ_CRASH("unexpected operand kind");
         }
     }
-    void cmpl(ImmMaybeNurseryPtr rhs, const Operand& lhs) {
-        cmpl(noteMaybeNurseryPtr(rhs), lhs);
-    }
     void cmpl(Register rhs, AsmJSAbsoluteAddress lhs) {
         masm.cmpl_rm_disp32(rhs.encoding(), (void*)-1);
         append(AsmJSAbsoluteLink(CodeOffsetLabel(masm.currentOffset()), lhs.kind()));
     }
     void cmpl(Imm32 rhs, AsmJSAbsoluteAddress lhs) {
         JmpSrc src = masm.cmpl_im_disp32(rhs.value, (void*)-1);
         append(AsmJSAbsoluteLink(CodeOffsetLabel(src.offset()), lhs.kind()));
     }
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -233,17 +233,17 @@ class MacroAssemblerX86 : public MacroAs
     void popValue(ValueOperand val) {
         pop(val.payloadReg());
         pop(val.typeReg());
     }
     void pushValue(const Value& val) {
         jsval_layout jv = JSVAL_TO_IMPL(val);
         push(Imm32(jv.s.tag));
         if (val.isMarkable())
-            push(ImmMaybeNurseryPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())));
+            push(ImmGCPtr(reinterpret_cast<gc::Cell*>(val.toGCThing())));
         else
             push(Imm32(jv.s.payload.i32));
     }
     void pushValue(JSValueType type, Register reg) {
         push(ImmTag(JSVAL_TYPE_TO_TAG(type)));
         push(reg);
     }
     void pushValue(const Address& addr) {
@@ -564,19 +564,16 @@ class MacroAssemblerX86 : public MacroAs
         cmpPtr(lhs, ImmWord(uintptr_t(rhs.value)));
     }
     void cmpPtr(const Address& lhs, const ImmGCPtr rhs) {
         cmpPtr(Operand(lhs), rhs);
     }
     void cmpPtr(Register lhs, Register rhs) {
         cmp32(lhs, rhs);
     }
-    void cmpPtr(const Operand& lhs, ImmMaybeNurseryPtr rhs) {
-        cmpl(rhs, lhs);
-    }
     void testPtr(Register lhs, Register rhs) {
         test32(lhs, rhs);
     }
     void testPtr(Register lhs, Imm32 rhs) {
         test32(lhs, rhs);
     }
     void testPtr(Register lhs, ImmWord rhs) {
         test32(lhs, Imm32(rhs.value));
@@ -730,19 +727,16 @@ class MacroAssemblerX86 : public MacroAs
         movl(imm, dest);
     }
     void movePtr(AsmJSImmPtr imm, Register dest) {
         mov(imm, dest);
     }
     void movePtr(ImmGCPtr imm, Register dest) {
         movl(imm, dest);
     }
-    void movePtr(ImmMaybeNurseryPtr imm, Register dest) {
-        movePtr(noteMaybeNurseryPtr(imm), dest);
-    }
     void loadPtr(const Address& address, Register dest) {
         movl(Operand(address), dest);
     }
     void loadPtr(const Operand& src, Register dest) {
         movl(src, dest);
     }
     void loadPtr(const BaseIndex& src, Register dest) {
         movl(Operand(src), dest);
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -2387,16 +2387,32 @@ js::SetClassAndProto(JSContext* cx, Hand
     MarkObjectGroupUnknownProperties(cx, obj->group());
     MarkObjectGroupUnknownProperties(cx, group);
 
     obj->setGroup(group);
 
     return true;
 }
 
+/* static */ bool
+JSObject::changeToSingleton(JSContext* cx, HandleObject obj)
+{
+    MOZ_ASSERT(!obj->isSingleton());
+
+    MarkObjectGroupUnknownProperties(cx, obj->group());
+
+    ObjectGroup* group = ObjectGroup::lazySingletonGroup(cx, obj->getClass(),
+                                                         obj->getTaggedProto());
+    if (!group)
+        return false;
+
+    obj->group_ = group;
+    return true;
+}
+
 static bool
 MaybeResolveConstructor(ExclusiveContext* cxArg, Handle<GlobalObject*> global, JSProtoKey key)
 {
     if (global->isStandardClassResolved(key))
         return true;
     if (!cxArg->shouldBeJSContext())
         return false;
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -306,22 +306,24 @@ class JSObject : public js::gc::Cell
     void addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo* info);
 
     // We can only use addSizeOfExcludingThis on tenured objects: it assumes it
     // can apply mallocSizeOf to bits and pieces of the object, whereas objects
     // in the nursery may have those bits and pieces allocated in the nursery
     // along with them, and are not each their own malloc blocks.
     size_t sizeOfIncludingThisInNursery() const;
 
-    /*
-     * Marks this object as having a singleton type, and leave the group lazy.
-     * Constructs a new, unique shape for the object.
-     */
+    // Marks this object as having a singleton group, and leave the group lazy.
+    // Constructs a new, unique shape for the object. This should only be
+    // called for an object that was just created.
     static inline bool setSingleton(js::ExclusiveContext* cx, js::HandleObject obj);
 
+    // Change an existing object to have a singleton group.
+    static bool changeToSingleton(JSContext* cx, js::HandleObject obj);
+
     inline js::ObjectGroup* getGroup(JSContext* cx);
 
     const js::HeapPtrObjectGroup& groupFromGC() const {
         /* Direct field access for use by GC. */
         return group_;
     }
 
     /*
--- a/js/src/vm/ObjectGroup.cpp
+++ b/js/src/vm/ObjectGroup.cpp
@@ -502,18 +502,30 @@ ObjectGroup::defaultNewGroup(ExclusiveCo
         // function, don't try to construct another one.
         if (associated && associated->wasNewScriptCleared())
             associated = nullptr;
 
         if (!associated)
             clasp = &PlainObject::class_;
     }
 
-    if (proto.isObject() && !proto.toObject()->setDelegate(cx))
-        return nullptr;
+    if (proto.isObject() && !proto.toObject()->isDelegate()) {
+        RootedObject protoObj(cx, proto.toObject());
+        if (!protoObj->setDelegate(cx))
+            return nullptr;
+
+        // Objects which are prototypes of one another should be singletons, so
+        // that their type information can be tracked more precisely. Limit
+        // this group change to plain objects, to avoid issues with other types
+        // of singletons like typed arrays.
+        if (protoObj->is<PlainObject>() && !protoObj->isSingleton()) {
+            if (!JSObject::changeToSingleton(cx->asJSContext(), protoObj))
+                return nullptr;
+        }
+    }
 
     ObjectGroupCompartment::NewTable::AddPtr p =
         table->lookupForAdd(ObjectGroupCompartment::NewEntry::Lookup(clasp, proto, associated));
     if (p) {
         ObjectGroup* group = p->group;
         MOZ_ASSERT_IF(clasp, group->clasp() == clasp);
         MOZ_ASSERT_IF(!clasp, group->clasp() == &PlainObject::class_ ||
                               group->clasp() == &UnboxedPlainObject::class_);
--- a/js/src/vm/ObjectGroup.h
+++ b/js/src/vm/ObjectGroup.h
@@ -152,20 +152,21 @@ enum NewObjectKind {
     TenuredObject
 };
 
 /*
  * Lazy object groups overview.
  *
  * Object groups which represent at most one JS object are constructed lazily.
  * These include groups for native functions, standard classes, scripted
- * functions defined at the top level of global/eval scripts, and in some
- * other cases. Typical web workloads often create many windows (and many
- * copies of standard natives) and many scripts, with comparatively few
- * non-singleton groups.
+ * functions defined at the top level of global/eval scripts, objects which
+ * dynamically become the prototype of some other object, and in some other
+ * cases. Typical web workloads often create many windows (and many copies of
+ * standard natives) and many scripts, with comparatively few non-singleton
+ * groups.
  *
  * We can recover the type information for the object from examining it,
  * so don't normally track the possible types of its properties as it is
  * updated. Property type sets for the object are only constructed when an
  * analyzed script attaches constraints to it: the script is querying that
  * property off the object or another which delegates to it, and the analysis
  * information is sensitive to changes in the property's type. Future changes
  * to the property (whether those uncovered by analysis or those occurring
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -76,16 +76,17 @@ PerThreadData::PerThreadData(JSRuntime* 
 #ifdef JS_TRACE_LOGGING
     traceLogger(nullptr),
 #endif
     autoFlushICache_(nullptr),
     dtoaState(nullptr),
     suppressGC(0),
 #ifdef DEBUG
     ionCompiling(false),
+    ionCompilingSafeForMinorGC(false),
     gcSweeping(false),
 #endif
     activeCompilations(0)
 {}
 
 PerThreadData::~PerThreadData()
 {
     if (dtoaState)
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -518,16 +518,21 @@ class PerThreadData : public PerThreadDa
      * in non-exposed debugging facilities.
      */
     int32_t suppressGC;
 
 #ifdef DEBUG
     // Whether this thread is actively Ion compiling.
     bool ionCompiling;
 
+    // Whether this thread is actively Ion compiling in a context where a minor
+    // GC could happen simultaneously. If this is true, this thread cannot use
+    // any pointers into the nursery.
+    bool ionCompilingSafeForMinorGC;
+
     // Whether this thread is currently sweeping GC things.
     bool gcSweeping;
 #endif
 
     // Number of active bytecode compilation on this thread.
     unsigned activeCompilations;
 
     explicit PerThreadData(JSRuntime* runtime);
@@ -1928,31 +1933,35 @@ class RuntimeAllocPolicy
 
 extern const JSSecurityCallbacks NullSecurityCallbacks;
 
 // Debugging RAII class which marks the current thread as performing an Ion
 // compilation, for use by CurrentThreadCan{Read,Write}CompilationData
 class AutoEnterIonCompilation
 {
   public:
-    explicit AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
+    explicit AutoEnterIonCompilation(bool safeForMinorGC
+                                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
 #ifdef DEBUG
         PerThreadData* pt = js::TlsPerThreadData.get();
         MOZ_ASSERT(!pt->ionCompiling);
+        MOZ_ASSERT(!pt->ionCompilingSafeForMinorGC);
         pt->ionCompiling = true;
+        pt->ionCompilingSafeForMinorGC = safeForMinorGC;
 #endif
     }
 
     ~AutoEnterIonCompilation() {
 #ifdef DEBUG
         PerThreadData* pt = js::TlsPerThreadData.get();
         MOZ_ASSERT(pt->ionCompiling);
         pt->ionCompiling = false;
+        pt->ionCompilingSafeForMinorGC = false;
 #endif
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * AutoInitGCManagedObject is a wrapper for use when initializing a object whose
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -243,37 +243,43 @@ js::ObjectGroupHasProperty(JSContext* cx
         id = IdToTypeId(id);
 
         /* Watch for properties which inference does not monitor. */
         if (id == id___proto__(cx) || id == id_constructor(cx) || id == id_caller(cx))
             return true;
 
         TypeSet::Type type = TypeSet::GetValueType(value);
 
-        // Type set guards might miss when an object's group changes and its
-        // properties become unknown.
-        if (value.isObject() &&
-            !value.toObject().hasLazyGroup() &&
-            ((value.toObject().group()->flags() & OBJECT_FLAG_UNKNOWN_PROPERTIES) ||
-             value.toObject().group()->maybeOriginalUnboxedGroup()))
-        {
-            return true;
-        }
-
         AutoEnterAnalysis enter(cx);
 
         /*
          * We don't track types for properties inherited from prototypes which
          * haven't yet been accessed during analysis of the inheriting object.
          * Don't do the property instantiation now.
          */
         TypeSet* types = group->maybeGetProperty(id);
         if (!types)
             return true;
 
+        // Type set guards might miss when an object's group changes and its
+        // properties become unknown.
+        if (value.isObject()) {
+            if (types->unknownObject())
+                return true;
+            for (size_t i = 0; i < types->getObjectCount(); i++) {
+                if (TypeSet::ObjectKey* key = types->getObject(i)) {
+                    if (key->unknownProperties())
+                        return true;
+                }
+            }
+            JSObject* obj = &value.toObject();
+            if (!obj->hasLazyGroup() && obj->group()->maybeOriginalUnboxedGroup())
+                return true;
+        }
+
         if (!types->hasType(type)) {
             TypeFailure(cx, "Missing type in object %s %s: %s",
                         TypeSet::ObjectGroupString(group), TypeIdString(id),
                         TypeSet::TypeString(type));
         }
     }
     return true;
 }
@@ -600,29 +606,66 @@ TypeSet::addType(Type type, LifoAlloc* a
 
     if (false) {
     unknownObject:
         flags |= TYPE_FLAG_ANYOBJECT;
         clearObjects();
     }
 }
 
+// This class is used for post barriers on type set contents. The only times
+// when type sets contain nursery references is when a nursery object has its
+// group dynamically changed to a singleton. In such cases the type set will
+// need to be traced at the next minor GC.
+//
+// There is no barrier used for TemporaryTypeSets. These type sets are only
+// used during Ion compilation, and if some ConstraintTypeSet contains nursery
+// pointers then any number of TemporaryTypeSets might as well. Thus, if there
+// are any such ConstraintTypeSets in existence, all off thread Ion
+// compilations are canceled by the next minor GC.
+class TypeSetRef : public BufferableRef
+{
+    Zone* zone;
+    ConstraintTypeSet* types;
+
+  public:
+    TypeSetRef(Zone* zone, ConstraintTypeSet* types)
+      : zone(zone), types(types)
+    {}
+
+    void trace(JSTracer* trc) override {
+        types->trace(zone, trc);
+    }
+};
+
+void
+ConstraintTypeSet::postWriteBarrier(ExclusiveContext* cx, Type type)
+{
+    if (type.isSingletonUnchecked() && IsInsideNursery(type.singletonNoBarrier())) {
+        JSRuntime* rt = cx->asJSContext()->runtime();
+        rt->gc.storeBuffer.putGeneric(TypeSetRef(cx->zone(), this));
+        rt->gc.storeBuffer.setShouldCancelIonCompilations();
+    }
+}
+
 void
 ConstraintTypeSet::addType(ExclusiveContext* cxArg, Type type)
 {
     MOZ_ASSERT(cxArg->zone()->types.activeAnalysis);
 
     if (hasType(type))
         return;
 
     TypeSet::addType(type, &cxArg->typeLifoAlloc());
 
     if (type.isObjectUnchecked() && unknownObject())
         type = AnyObjectType();
 
+    postWriteBarrier(cxArg, type);
+
     InferSpew(ISpewOps, "addType: %sT%p%s %s",
               InferSpewColor(this), this, InferSpewColorReset(),
               TypeString(type));
 
     /* Propagate the type to all constraints. */
     if (JSContext* cx = cxArg->maybeJSContext()) {
         TypeConstraint* constraint = constraintList;
         while (constraint) {
@@ -2572,16 +2615,17 @@ UpdatePropertyType(ExclusiveContext* cx,
          * magic values and optimized out values) as appearing in CallObjects.
          */
         MOZ_ASSERT_IF(TypeSet::IsUntrackedValue(value), obj->is<CallObject>());
         if ((indexed || !value.isUndefined() || !CanHaveEmptyPropertyTypesForOwnProperty(obj)) &&
             !TypeSet::IsUntrackedValue(value))
         {
             TypeSet::Type type = TypeSet::GetValueType(value);
             types->TypeSet::addType(type, &cx->typeLifoAlloc());
+            types->postWriteBarrier(cx, type);
         }
 
         if (indexed || shape->hadOverwrite()) {
             types->setNonConstantProperty(cx);
         } else {
             InferSpew(ISpewOps, "typeSet: %sT%p%s property %s %s - setConstant",
                       InferSpewColor(types), types, InferSpewColorReset(),
                       TypeSet::ObjectGroupString(obj->group()), TypeIdString(shape->propid()));
@@ -2623,16 +2667,17 @@ ObjectGroup::updateNewPropertyTypes(Excl
         }
 
         /* Also get values of any dense elements in the object. */
         for (size_t i = 0; i < obj->getDenseInitializedLength(); i++) {
             const Value& value = obj->getDenseElement(i);
             if (!value.isMagic(JS_ELEMENTS_HOLE)) {
                 TypeSet::Type type = TypeSet::GetValueType(value);
                 types->TypeSet::addType(type, &cx->typeLifoAlloc());
+                types->postWriteBarrier(cx, type);
             }
         }
     } else if (!JSID_IS_EMPTY(id)) {
         RootedId rootedId(cx, id);
         Shape* shape = obj->lookup(cx, rootedId);
         if (shape)
             UpdatePropertyType(cx, types, obj, shape, false);
     }
@@ -2852,16 +2897,19 @@ ObjectGroup::markUnknown(ExclusiveContex
     MOZ_ASSERT(cx->zone()->types.activeAnalysis);
     MOZ_ASSERT(!unknownProperties());
 
     InferSpew(ISpewOps, "UnknownProperties: %s", TypeSet::ObjectGroupString(this));
 
     clearNewScript(cx);
     ObjectStateChange(cx, this, true);
 
+    if (ObjectGroup* unboxedGroup = maybeOriginalUnboxedGroup())
+        unboxedGroup->markUnknown(cx);
+
     /*
      * Existing constraints may have already been added to this object, which we need
      * to do the right thing for. We can't ensure that we will mark all unknown
      * objects before they have been accessed, as the __proto__ of a known object
      * could be dynamically set to an unknown object, and we can decide to ignore
      * properties of an object during analysis (i.e. hashmaps). Adding unknown for
      * any properties accessed already accounts for possible values read from them.
      */
@@ -3932,16 +3980,65 @@ TypeNewScript::sweep()
     if (preliminaryObjects)
         preliminaryObjects->sweep();
 }
 
 /////////////////////////////////////////////////////////////////////
 // Tracing
 /////////////////////////////////////////////////////////////////////
 
+static inline void
+TraceObjectKey(JSTracer* trc, TypeSet::ObjectKey** keyp)
+{
+    TypeSet::ObjectKey* key = *keyp;
+    if (key->isGroup()) {
+        ObjectGroup* group = key->groupNoBarrier();
+        TraceManuallyBarrieredEdge(trc, &group, "objectKey_group");
+        *keyp = TypeSet::ObjectKey::get(group);
+    } else {
+        JSObject* singleton = key->singletonNoBarrier();
+        TraceManuallyBarrieredEdge(trc, &singleton, "objectKey_singleton");
+        *keyp = TypeSet::ObjectKey::get(singleton);
+    }
+}
+
+void
+ConstraintTypeSet::trace(Zone* zone, JSTracer* trc)
+{
+    // ConstraintTypeSets only hold strong references during minor collections.
+    MOZ_ASSERT(zone->runtimeFromMainThread()->isHeapMinorCollecting());
+
+    unsigned objectCount = baseObjectCount();
+    if (objectCount >= 2) {
+        unsigned oldCapacity = TypeHashSet::Capacity(objectCount);
+        ObjectKey** oldArray = objectSet;
+
+        clearObjects();
+        objectCount = 0;
+        for (unsigned i = 0; i < oldCapacity; i++) {
+            ObjectKey* key = oldArray[i];
+            if (!key)
+                continue;
+            TraceObjectKey(trc, &key);
+            ObjectKey** pentry =
+                TypeHashSet::Insert<ObjectKey*, ObjectKey, ObjectKey>
+                    (zone->types.typeLifoAlloc, objectSet, objectCount, key);
+            if (pentry)
+                *pentry = key;
+            else
+                CrashAtUnhandlableOOM("ConstraintTypeSet::trace");
+        }
+        setBaseObjectCount(objectCount);
+    } else if (objectCount == 1) {
+        ObjectKey* key = (ObjectKey*) objectSet;
+        TraceObjectKey(trc, &key);
+        objectSet = reinterpret_cast<ObjectKey**>(key);
+    }
+}
+
 void
 ConstraintTypeSet::sweep(Zone* zone, AutoClearTypeInferenceStateOnOOM& oom)
 {
     MOZ_ASSERT(zone->isGCSweepingOrCompacting());
 
     // IsAboutToBeFinalized doesn't work right on tenured objects when called
     // during a minor collection.
     MOZ_ASSERT(!zone->runtimeFromMainThread()->isHeapMinorCollecting());
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -608,20 +608,25 @@ class ConstraintTypeSet : public TypeSet
     ConstraintTypeSet() : constraintList(nullptr) {}
 
     /*
      * Add a type to this set, calling any constraint handlers if this is a new
      * possible type.
      */
     void addType(ExclusiveContext* cx, Type type);
 
+    // Trigger a post barrier when writing to this set, if necessary.
+    // addType(cx, type) takes care of this automatically.
+    void postWriteBarrier(ExclusiveContext* cx, Type type);
+
     /* Add a new constraint to this set. */
     bool addConstraint(JSContext* cx, TypeConstraint* constraint, bool callExisting = true);
 
     inline void sweep(JS::Zone* zone, AutoClearTypeInferenceStateOnOOM& oom);
+    inline void trace(JS::Zone* zone, JSTracer* trc);
 };
 
 class StackTypeSet : public ConstraintTypeSet
 {
   public:
 };
 
 class HeapTypeSet : public ConstraintTypeSet
--- a/js/src/vm/UnboxedObject.cpp
+++ b/js/src/vm/UnboxedObject.cpp
@@ -1883,16 +1883,19 @@ js::TryConvertToUnboxedLayout(ExclusiveC
     JSValueType elementType = JSVAL_TYPE_MAGIC;
 
     size_t objectCount = 0;
     for (size_t i = 0; i < PreliminaryObjectArray::COUNT; i++) {
         JSObject* obj = objects->get(i);
         if (!obj)
             continue;
 
+        if (obj->isSingleton() || obj->group() != group)
+            return true;
+
         objectCount++;
 
         if (isArray) {
             if (!CombineArrayObjectElements(cx, &obj->as<ArrayObject>(), &elementType))
                 return true;
         } else {
             if (!CombinePlainObjectProperties(&obj->as<PlainObject>(), templateShape, properties))
                 return true;
--- a/layout/base/UnitTransforms.h
+++ b/layout/base/UnitTransforms.h
@@ -32,17 +32,20 @@ enum class PixelCastJustification : uint
   // The transform that is usually used to convert between two coordinate
   // systems is not available (for example, because the object that stores it
   // is being destroyed), so fall back to the identity.
   TransformNotAvailable,
   // When an OS event is initially constructed, its reference point is
   // technically in screen pixels, as it has not yet accounted for any
   // asynchronous transforms. This justification is for viewing the initial
   // reference point as a screen point.
-  LayoutDeviceToScreenForUntransformedEvent
+  LayoutDeviceToScreenForUntransformedEvent,
+  // Similar to LayoutDeviceToScreenForUntransformedEvent, PBrowser handles
+  // some widget/tab dimension information as the OS does -- in screen units.
+  LayoutDeviceIsScreenForTabDims
 };
 
 template <class TargetUnits, class SourceUnits>
 gfx::SizeTyped<TargetUnits> ViewAs(const gfx::SizeTyped<SourceUnits>& aSize, PixelCastJustification) {
   return gfx::SizeTyped<TargetUnits>(aSize.width, aSize.height);
 }
 template <class TargetUnits, class SourceUnits>
 gfx::IntSizeTyped<TargetUnits> ViewAs(const gfx::IntSizeTyped<SourceUnits>& aSize, PixelCastJustification) {
--- a/layout/base/nsPresContext.cpp
+++ b/layout/base/nsPresContext.cpp
@@ -1793,50 +1793,72 @@ nsPresContext::UIResolutionChanged()
     nsCOMPtr<nsIRunnable> ev =
       NS_NewRunnableMethod(this, &nsPresContext::UIResolutionChangedInternal);
     if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
       mPendingUIResolutionChanged = true;
     }
   }
 }
 
+void
+nsPresContext::UIResolutionChangedSync()
+{
+  if (!mPendingUIResolutionChanged) {
+    mPendingUIResolutionChanged = true;
+    UIResolutionChangedInternal();
+  }
+}
+
 /*static*/ bool
 nsPresContext::UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument,
                                                       void* aData)
 {
   nsIPresShell* shell = aDocument->GetShell();
   if (shell) {
     nsPresContext* pc = shell->GetPresContext();
     if (pc) {
       pc->UIResolutionChangedInternal();
     }
   }
   return true;
 }
 
 static void
-NotifyUIResolutionChanged(TabParent* aTabParent, void* aArg)
+NotifyTabUIResolutionChanged(TabParent* aTab, void *aArg)
+{
+  aTab->UIResolutionChanged();
+}
+
+static void
+NotifyChildrenUIResolutionChanged(nsIDOMWindow* aWindow)
 {
-  aTabParent->UIResolutionChanged();
+  nsCOMPtr<nsPIDOMWindow> piWin = do_QueryInterface(aWindow);
+  if (!piWin) {
+    return;
+  }
+  nsCOMPtr<nsIDocument> doc = piWin->GetExtantDoc();
+  nsRefPtr<nsPIWindowRoot> topLevelWin = nsContentUtils::GetWindowRoot(doc);
+  if (!topLevelWin) {
+    return;
+  }
+  topLevelWin->EnumerateBrowsers(NotifyTabUIResolutionChanged, nullptr);
 }
 
 void
 nsPresContext::UIResolutionChangedInternal()
 {
   mPendingUIResolutionChanged = false;
 
   mDeviceContext->CheckDPIChange();
   if (mCurAppUnitsPerDevPixel != AppUnitsPerDevPixel()) {
     AppUnitsPerDevPixelChanged();
   }
 
-  // Recursively notify all remote leaf descendants that the
-  // resolution of the user interface has changed.
-  nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
-                                          NotifyUIResolutionChanged, nullptr);
+  // Recursively notify all remote leaf descendants of the change.
+  NotifyChildrenUIResolutionChanged(mDocument->GetWindow());
 
   mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback,
                                    nullptr);
 }
 
 void
 nsPresContext::EmulateMedium(const nsAString& aMediaType)
 {
--- a/layout/base/nsPresContext.h
+++ b/layout/base/nsPresContext.h
@@ -786,19 +786,25 @@ public:
    * has changed.
    */
   void ThemeChanged();
 
   /*
    * Notify the pres context that the resolution of the user interface has
    * changed. This happens if a window is moved between HiDPI and non-HiDPI
    * displays, so that the ratio of points to device pixels changes.
+   * The notification happens asynchronously.
    */
   void UIResolutionChanged();
 
+ /*
+  * Like UIResolutionChanged() but invalidates values immediately.
+  */
+  void UIResolutionChangedSync();
+
   /*
    * Notify the pres context that a system color has changed
    */
   void SysColorChanged();
 
   /** Printing methods below should only be used for Medium() == print **/
   void SetPrintSettings(nsIPrintSettings *aPrintSettings);
 
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -15,16 +15,17 @@
 #include "nsISizeOf.h"
 #include "nsPrintfCString.h"
 #include "mozilla/DebugOnly.h"
 #include "prinrval.h"
 #include "nsIFile.h"
 #include "nsITimer.h"
 #include "mozilla/AutoRestore.h"
 #include <algorithm>
+#include "mozilla/Telemetry.h"
 
 
 #define kMinUnwrittenChanges   300
 #define kMinDumpInterval       20000 // in milliseconds
 #define kMaxBufSize            16384
 #define kIndexVersion          0x00000001
 #define kUpdateIndexStartDelay 50000 // in milliseconds
 
@@ -3125,16 +3126,21 @@ CacheIndex::ChangeState(EState aNewState
   // possible transition is to SHUTDOWN state.
   MOZ_ASSERT(!mShuttingDown || mState != READY || aNewState == SHUTDOWN);
 
   // Start updating process when switching to READY state if needed
   if (aNewState == READY && StartUpdatingIndexIfNeeded(true)) {
     return;
   }
 
+  if ((mState == READING || mState == BUILDING || mState == UPDATING) &&
+      aNewState == READY) {
+    ReportHashStats();
+  }
+
   // Try to evict entries over limit everytime we're leaving state READING,
   // BUILDING or UPDATING, but not during shutdown or when removing all
   // entries.
   if (!mShuttingDown && !mRemovingAll && aNewState != SHUTDOWN &&
       (mState == READING || mState == BUILDING || mState == UPDATING))  {
     CacheFileIOManager::EvictIfOverLimit();
   }
 
@@ -3622,10 +3628,78 @@ CacheIndex::SizeOfExcludingThis(mozilla:
 
 // static
 size_t
 CacheIndex::SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf)
 {
   return mallocSizeOf(gInstance) + SizeOfExcludingThis(mallocSizeOf);
 }
 
+namespace { // anon
+
+class HashComparator
+{
+public:
+  bool Equals(CacheIndexRecord* a, CacheIndexRecord* b) const {
+    return memcmp(&a->mHash, &b->mHash, sizeof(SHA1Sum::Hash)) == 0;
+  }
+  bool LessThan(CacheIndexRecord* a, CacheIndexRecord* b) const {
+    return memcmp(&a->mHash, &b->mHash, sizeof(SHA1Sum::Hash)) < 0;
+  }
+};
+
+void
+ReportHashSizeMatch(const SHA1Sum::Hash *aHash1, const SHA1Sum::Hash *aHash2)
+{
+  const uint32_t *h1 = reinterpret_cast<const uint32_t *>(aHash1);
+  const uint32_t *h2 = reinterpret_cast<const uint32_t *>(aHash2);
+
+  for (uint32_t i = 0; i < 5; ++i) {
+    if (h1[i] != h2[i]) {
+      uint32_t bitsDiff = h1[i] ^ h2[i];
+      bitsDiff = NetworkEndian::readUint32(&bitsDiff);
+
+      // count leading zeros in bitsDiff
+      static const uint8_t debruijn32[32] =
+        { 0, 31, 9, 30, 3, 8, 13, 29, 2, 5, 7, 21, 12, 24, 28, 19,
+          1, 10, 4, 14, 6, 22, 25, 20, 11, 15, 23, 26, 16, 27, 17, 18};
+
+      bitsDiff |= bitsDiff>>1;
+      bitsDiff |= bitsDiff>>2;
+      bitsDiff |= bitsDiff>>4;
+      bitsDiff |= bitsDiff>>8;
+      bitsDiff |= bitsDiff>>16;
+      bitsDiff++;
+
+      uint8_t hashSizeMatch = debruijn32[bitsDiff*0x076be629>>27] + (i<<5);
+      Telemetry::Accumulate(Telemetry::NETWORK_CACHE_HASH_STATS, hashSizeMatch);
+
+      return;
+    }
+  }
+
+  MOZ_ASSERT(false, "Found a collision in the index!");
+}
+
+} // anon
+
+void
+CacheIndex::ReportHashStats()
+{
+  // We're gathering the hash stats only once, exclude too small caches.
+  if (CacheObserver::HashStatsReported() || mFrecencyArray.Length() < 15000) {
+    return;
+  }
+
+  nsTArray<CacheIndexRecord *> records;
+  records.AppendElements(mFrecencyArray);
+
+  records.Sort(HashComparator());
+
+  for (uint32_t i = 1; i < records.Length(); i++) {
+    ReportHashSizeMatch(&records[i-1]->mHash, &records[i]->mHash);
+  }
+
+  CacheObserver::SetHashStatsReported();
+}
+
 } // net
 } // mozilla
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -921,16 +921,18 @@ private:
   void AddRecordToIterators(CacheIndexRecord *aRecord);
   void RemoveRecordFromIterators(CacheIndexRecord *aRecord);
   void ReplaceRecordInIterators(CacheIndexRecord *aOldRecord,
                                 CacheIndexRecord *aNewRecord);
 
   // Memory reporting (private part)
   size_t SizeOfExcludingThisInternal(mozilla::MallocSizeOf mallocSizeOf) const;
 
+  void ReportHashStats();
+
   static CacheIndex *gInstance;
 
   nsCOMPtr<nsIFile> mCacheDirectory;
 
   mozilla::Mutex mLock;
   EState         mState;
   // Timestamp of time when the index was initialized. We use it to delay
   // initial update or build of index.
--- a/netwerk/cache2/CacheObserver.cpp
+++ b/netwerk/cache2/CacheObserver.cpp
@@ -84,16 +84,19 @@ static bool kDefaultSanitizeOnShutdown =
 bool CacheObserver::sSanitizeOnShutdown = kDefaultSanitizeOnShutdown;
 
 static bool kDefaultClearCacheOnShutdown = false;
 bool CacheObserver::sClearCacheOnShutdown = kDefaultClearCacheOnShutdown;
 
 static bool kDefaultCacheFSReported = false;
 bool CacheObserver::sCacheFSReported = kDefaultCacheFSReported;
 
+static bool kDefaultHashStatsReported = false;
+bool CacheObserver::sHashStatsReported = kDefaultHashStatsReported;
+
 NS_IMPL_ISUPPORTS(CacheObserver,
                   nsIObserver,
                   nsISupportsWeakReference)
 
 // static
 nsresult
 CacheObserver::Init()
 {
@@ -342,16 +345,42 @@ CacheObserver::SetCacheFSReported()
 void
 CacheObserver::StoreCacheFSReported()
 {
   mozilla::Preferences::SetInt("browser.cache.disk.filesystem_reported",
                                sCacheFSReported);
 }
 
 // static
+void
+CacheObserver::SetHashStatsReported()
+{
+  sHashStatsReported = true;
+
+  if (!sSelf) {
+    return;
+  }
+
+  if (NS_IsMainThread()) {
+    sSelf->StoreHashStatsReported();
+  } else {
+    nsCOMPtr<nsIRunnable> event =
+      NS_NewRunnableMethod(sSelf, &CacheObserver::StoreHashStatsReported);
+    NS_DispatchToMainThread(event);
+  }
+}
+
+void
+CacheObserver::StoreHashStatsReported()
+{
+  mozilla::Preferences::SetInt("browser.cache.disk.hashstats_reported",
+                               sHashStatsReported);
+}
+
+// static
 void CacheObserver::ParentDirOverride(nsIFile** aDir)
 {
   if (NS_WARN_IF(!aDir))
     return;
 
   *aDir = nullptr;
 
   if (!sSelf)
--- a/netwerk/cache2/CacheObserver.h
+++ b/netwerk/cache2/CacheObserver.h
@@ -59,25 +59,29 @@ class CacheObserver : public nsIObserver
     { return sHalfLifeHours * 60.0F * 60.0F; }
   static int32_t const HalfLifeExperiment()
     { return sHalfLifeExperiment; }
   static bool const ClearCacheOnShutdown()
     { return sSanitizeOnShutdown && sClearCacheOnShutdown; }
   static bool const CacheFSReported()
     { return sCacheFSReported; }
   static void SetCacheFSReported();
+  static bool const HashStatsReported()
+    { return sHashStatsReported; }
+  static void SetHashStatsReported();
   static void ParentDirOverride(nsIFile ** aDir);
 
   static bool const EntryIsTooBig(int64_t aSize, bool aUsingDisk);
 
 private:
   static CacheObserver* sSelf;
 
   void StoreDiskCacheCapacity();
   void StoreCacheFSReported();
+  void StoreHashStatsReported();
   void AttachToPreferences();
 
   static uint32_t sUseNewCache;
   static bool sUseMemoryCache;
   static bool sUseDiskCache;
   static uint32_t sMetadataMemoryLimit;
   static int32_t sMemoryCacheCapacity;
   static int32_t sAutoMemoryCacheCapacity;
@@ -91,16 +95,17 @@ private:
   static uint32_t sMaxDiskChunksMemoryUsage;
   static uint32_t sMaxDiskPriorityChunksMemoryUsage;
   static uint32_t sCompressionLevel;
   static float sHalfLifeHours;
   static int32_t sHalfLifeExperiment;
   static bool sSanitizeOnShutdown;
   static bool sClearCacheOnShutdown;
   static bool sCacheFSReported;
+  static bool sHashStatsReported;
 
   // Non static properties, accessible via sSelf
   nsCOMPtr<nsIFile> mCacheParentDirectoryOverride;
 };
 
 } // net
 } // mozilla
 
--- a/testing/mach_commands.py
+++ b/testing/mach_commands.py
@@ -377,33 +377,16 @@ class JsapiTestsCommand(MachCommandBase)
         if params['test_name']:
             jsapi_tests_cmd.append(params['test_name'])
 
         jsapi_tests_result = subprocess.call(jsapi_tests_cmd)
 
         return jsapi_tests_result
 
 
-AUTOTRY_HELP_MSG = """
-Autotry is in beta, please file bugs blocking 1149670.
-
-Push test from the specified paths to try. A set of test
-jobs will be selected based on the tests present in the tree, however
-specifying platforms is still required with the -p argument (a default
-is taken from the AUTOTRY_PLATFORM_HINT environment variable if set).
-
-The -u argument may be used to specify additional unittest suites to run.
-
-Selected tests will be run in a single chunk of the relevant suite, at this
-time in chunk 1.
-
-The following types of tests are eligible to be selected automatically
-by this command at this time: %s
-""" % list(AutoTry.test_flavors)
-
 @CommandProvider
 class PushToTry(MachCommandBase):
 
     def validate_args(self, paths, tests, builds, platforms):
         if not len(paths) and not tests:
             print("Paths or tests must be specified as an argument to autotry.")
             sys.exit(1)
 
@@ -418,39 +401,62 @@ class PushToTry(MachCommandBase):
                 sys.exit(1)
             if len(p) <= len(self.topsrcdir):
                 print('Specified path "%s" is at the top of the srcdir and would'
                       ' select all tests.' % p)
                 sys.exit(1)
 
         return builds, platforms
 
-    @Command('try', category='testing', description=AUTOTRY_HELP_MSG)
+    @Command('try', category='testing', description='Push selected tests to the try server')
     @CommandArgument('paths', nargs='*', help='Paths to search for tests to run on try.')
-    @CommandArgument('-v', dest='verbose', action='store_true', default=True,
+    @CommandArgument('-n', dest='verbose', action='store_true', default=False,
                      help='Print detailed information about the resulting test selection '
                           'and commands performed.')
     @CommandArgument('-p', dest='platforms', required='AUTOTRY_PLATFORM_HINT' not in os.environ,
                      help='Platforms to run. (required if not found in the environment)')
     @CommandArgument('-u', dest='tests',
-                     help='Test jobs to run. These will be use in place of test jobs '
+                     help='Test jobs to run. These will be used in place of suites '
                           'determined by test paths, if any.')
     @CommandArgument('--extra', dest='extra_tests',
-                     help='Additional tests to run. These will be added to test jobs '
+                     help='Additional tests to run. These will be added to suites '
                           'determined by test paths, if any.')
     @CommandArgument('-b', dest='builds', default='do',
                      help='Build types to run (d for debug, o for optimized)')
     @CommandArgument('--tag', dest='tags', action='append',
                      help='Restrict tests to the given tag (may be specified multiple times)')
     @CommandArgument('--no-push', dest='push', action='store_false',
                      help='Do not push to try as a result of running this command (if '
                           'specified this command will only print calculated try '
                           'syntax and selection info).')
-    def autotry(self, builds=None, platforms=None, paths=None, verbose=None, extra_tests=None,
-                push=None, tags=None, tests=None):
+    def autotry(self, builds=None, platforms=None, paths=None, verbose=None,
+                extra_tests=None, push=None, tags=None, tests=None):
+        """Autotry is in beta, please file bugs blocking 1149670.
+
+        Pushes the specified tests to try. The simplest way to specify tests is
+        by using the -u argument, which will behave as usual for try syntax.
+        This command also provides a mechanism to select test jobs and tests
+        within a job by path based on tests present in the tree under that
+        path. Mochitests, xpcshell tests, and reftests are eligible for
+        selection by this mechanism. Selected tests will be run in a single
+        chunk of the relevant suite, at this time in chunk 1.
+
+        Specifying platforms is still required with the -p argument (a default
+        is taken from the AUTOTRY_PLATFORM_HINT environment variable if set).
+
+        Tests may be further filtered by passing one or more --tag to the
+        command.
+
+        To run suites in addition to those determined from the tree, they
+        can be passed to the --extra arguent.
+
+        The command requires either its own mercurial extension ("push-to-try",
+        installable from mach mercurial-setup) or a git repo using git-cinnabar
+        (available at https://github.com/glandium/git-cinnabar).
+        """
 
         from mozbuild.testing import TestResolver
         from mozbuild.controller.building import BuildDriver
 
         print("mach try is under development, please file bugs blocking 1149670.")
 
         builds, platforms = self.validate_args(paths, tests, builds, platforms)
         resolver = self._spawn(TestResolver)
@@ -473,19 +479,19 @@ class PushToTry(MachCommandBase):
         all_manifests = set()
         for m in manifests_by_flavor.values():
             all_manifests |= m
         all_manifests = list(all_manifests)
 
         msg = at.calc_try_syntax(platforms, manifests_by_flavor.keys(), tests,
                                  extra_tests, builds, all_manifests, tags)
 
-        if verbose:
+        if verbose and manifests_by_flavor:
             print('Tests from the following manifests will be selected: ')
             pprint.pprint(manifests_by_flavor)
 
         if verbose:
-            print('The following try message was calculated:\n\n\t%s\n' % msg)
+            print('The following try syntax was calculated:\n\n\t%s\n' % msg)
 
         if push:
             at.push_to_try(msg, verbose)
 
         return
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -24653,18 +24653,29 @@
         "path": "webdriver/timeouts/page_load_timeouts_tests.py"
       },
       {
         "path": "webdriver/user_input/clear_test.py"
       }
     ]
   },
   "local_changes": {
-    "deleted": [],
-    "items": {},
+    "deleted": [
+      "webaudio/the-audio-api/the-gainnode-interface/test.html"
+    ],
+    "items": {
+      "testharness": {
+        "webaudio/the-audio-api/the-gainnode-interface/test-gainnode.html": [
+          {
+            "path": "webaudio/the-audio-api/the-gainnode-interface/test-gainnode.html",
+            "url": "/webaudio/the-audio-api/the-gainnode-interface/test-gainnode.html"
+          }
+        ]
+      }
+    },
     "reftest_nodes": {}
   },
   "reftest_nodes": {
     "2dcontext/building-paths/canvas_complexshapes_arcto_001.htm": [
       {
         "path": "2dcontext/building-paths/canvas_complexshapes_arcto_001.htm",
         "references": [
           [
@@ -30110,9 +30121,9 @@
         ],
         "url": "/webvtt/rendering/cues-with-video/processing-model/too_many_cues_wrapped.html"
       }
     ]
   },
   "rev": "0fbf63dbe6f6b8b0dccee76d96ec4b7d50c3fff6",
   "url_base": "/",
   "version": 2
-}
\ No newline at end of file
+}
rename from testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/test.html
rename to testing/web-platform/tests/webaudio/the-audio-api/the-gainnode-interface/test-gainnode.html
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -7051,16 +7051,22 @@
   },
   "NETWORK_CACHE_METADATA_SIZE": {
     "expires_in_version": "never",
     "kind": "linear",
     "high": "5119",
     "n_buckets": 256,
     "description": "Actual size of the metadata parsed from the disk."
   },
+  "NETWORK_CACHE_HASH_STATS": {
+    "expires_in_version": "46",
+    "kind": "enumerated",
+    "n_values": "160",
+    "description": "The longest hash match between a newly added entry and all the existing entries."
+  },
   "DATABASE_LOCKED_EXCEPTION": {
     "expires_in_version": "42",
     "kind": "enumerated",
     "description": "Record database locks when opening one of Fennec's databases. The index corresponds to how many attempts, beginning with 0.",
     "n_values": "5"
   },
   "DATABASE_SUCCESSFUL_UNLOCK": {
     "expires_in_version": "42",