Merge mozilla-central and inbound
authorEd Morley <emorley@mozilla.com>
Tue, 18 Jun 2013 12:05:32 +0100
changeset 135462 a4d01597aecbd4a295c176a65c05df1f9dc36863
parent 135417 58b1f6b4b57092c69417511f92f6f456cac1d3c9 (current diff)
parent 135461 0fcf27567179423917443e46f967e3cfe8dff05d (diff)
child 135463 bef58eae5f12f908822b0a355abd558622d03418
push id24841
push userryanvm@gmail.com
push dateTue, 18 Jun 2013 23:04:53 +0000
treeherdermozilla-central@d2a7cfa34154 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central and inbound
--- a/addon-sdk/source/test/tabs/test-firefox-tabs.js
+++ b/addon-sdk/source/test/tabs/test-firefox-tabs.js
@@ -1145,8 +1145,12 @@ function openBrowserWindow(callback, url
 // Helper for calling code at window close
 function closeBrowserWindow(window, callback) {
   window.addEventListener("unload", function unload() {
     window.removeEventListener("unload", unload, false);
     callback();
   }, false);
   window.close();
 }
+
+// Test disabled on Linux because of bug 882867
+if (require("sdk/system/runtime").OS == "Linux")
+  module.exports = {};
--- a/browser/metro/base/content/helperui/SelectionHelperUI.js
+++ b/browser/metro/base/content/helperui/SelectionHelperUI.js
@@ -827,45 +827,35 @@ var SelectionHelperUI = {
       // shutdown but leave focus alone. the event will fall through
       // and the dom will update focus for us. If the user tapped on
       // another input, we'll get a attachToCaret call soonish on the
       // new input.
       this.closeEditSession(false);
       return;
     }
 
-    let selectionTap = this._hitTestSelection(aEvent);
-
-    // If the tap is in the selection, just ignore it. We disallow this
-    // since we always get a single tap before a double, and double tap
-    // copies selected text.
-    if (selectionTap) {
-      if (!this._targetIsEditable) {
-        this.closeEditSession(false);
-        return;
-      }
+    if (this._hitTestSelection(aEvent) && this._targetIsEditable) {
       // Attach to the newly placed caret position
       this._sendAsyncMessage("Browser:CaretAttach", {
         xPos: aEvent.clientX,
         yPos: aEvent.clientY
       });
       return;
     }
 
     // A tap within an editable but outside active selection, clear the
     // selection and flip back to caret mode.
     if (this.startMark.visible && pointInTargetElement &&
         this._targetIsEditable) {
       this._transitionFromSelectionToCaret(clientCoords.x, clientCoords.y);
+      return;
     }
 
-    // If we have active selection in anything else don't let the event get
-    // to content. Prevents random taps from killing active selection.
-    aEvent.stopPropagation();
-    aEvent.preventDefault();
+    // Close when we get a single tap in content.
+    this.closeEditSession(false);
   },
 
   _onKeypress: function _onKeypress() {
     this.closeEditSession();
   },
 
   _onResize: function _onResize() {
     this._sendAsyncMessage("Browser:SelectionUpdate", {});
--- a/browser/metro/base/tests/mochitest/browser_selection_basic.js
+++ b/browser/metro/base/tests/mochitest/browser_selection_basic.js
@@ -301,125 +301,56 @@ gTests.push({
       clearSelection(gFrame);
     yield waitForCondition(function () {
         return !SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
     yield hideContextUI();
   },
 });
 
-/*
-disable until bug 860248 is addressed.
 gTests.push({
-  desc: "double-tap copy text in content",
-  setUp: setUpHelper,
+  desc: "tap on selection clears selection in content",
+  setUp: setUpAndTearDown,
   run: function test() {
 
     sendContextMenuClick(30, 20);
 
     yield waitForCondition(function () {
         return SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
 
-    sendDoubleTap(gWindow, 30, 20);
+    sendTap(gWindow, 30, 20);
 
     yield waitForCondition(function () {
         return !SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
-    
-    // check copy text results
-    let text = SpecialPowers.getClipboardData("text/unicode").trim();
-    is(text, "There", "copy text test");
-
-    // check for active selection
-    is(getTrimmedSelection(gWindow).toString(), "", "selection test");
   },
-  tearDown: tearDownHelper,
+  tearDown: setUpAndTearDown,
 });
 
 gTests.push({
-  desc: "double-tap copy text in scrolled content",
-  setUp: setUpHelper,
+  desc: "tap off selection clears selection in content",
+  setUp: setUpAndTearDown,
   run: function test() {
-    let scrollPromise = waitForEvent(gWindow, "scroll");
-    gWindow.scrollBy(0, 200);
-    yield scrollPromise;
-    ok(scrollPromise && !(scrollPromise instanceof Error), "scrollPromise error");
 
-    sendContextMenuClick(30, 100);
+    sendContextMenuClick(30, 20);
 
     yield waitForCondition(function () {
         return SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
 
-    sendDoubleTap(gWindow, 42, 100);
-
-    yield waitForCondition(function () {
-        return !SelectionHelperUI.isSelectionUIVisible;
-      }, kCommonWaitMs, kCommonPollMs);
-
-    // check copy text results
-    let text = SpecialPowers.getClipboardData("text/unicode");
-    is(text, "suddenly", "copy text test");
+    sendTap(gWindow, 30, 100);
 
-    // check for active selection
-    is(getTrimmedSelection(gWindow).toString(), "", "selection test");
-  },
-  tearDown: function tearDown() {
-    emptyClipboard();
-    clearSelection(gWindow);
-    let scrollPromise = waitForEvent(gWindow, "scroll");
-    gWindow.scrollBy(0, -200);
-    yield scrollPromise;
     yield waitForCondition(function () {
         return !SelectionHelperUI.isSelectionUIVisible;
       }, kCommonWaitMs, kCommonPollMs);
   },
+  tearDown: setUpAndTearDown,
 });
 
-gTests.push({
-  desc: "single clicks on selection in non-editable content",
-  setUp: setUpHelper,
-  run: function test() {
-    sendContextMenuClick(100, 20);
-
-    yield waitForCondition(function () {
-        return SelectionHelperUI.isSelectionUIVisible;
-      }, kCommonWaitMs, kCommonPollMs);
-
-    // active state
-    is(SelectionHelperUI.isActive, true, "selection active");
-
-    let ypos = SelectionHelperUI.endMark.yPos + kMarkerOffsetY;
-    let touchdrag = new TouchDragAndHold();
-    yield touchdrag.start(gWindow, SelectionHelperUI.endMark.xPos, ypos, 190, ypos);
-    touchdrag.end();
-
-    yield waitForCondition(function () {
-        return !SelectionHelperUI.hasActiveDrag;
-      }, kCommonWaitMs, kCommonPollMs);
-    yield SelectionHelperUI.pingSelectionHandler();
-
-    // active state
-    is(SelectionHelperUI.isActive, true, "selection active");
-
-    // click on selected text - nothing should change
-    sendTap(gWindow, 240, 20);
-
-    is(SelectionHelperUI.isActive, true, "selection active");
-
-    // click outside the text - nothing should change
-    sendTap(gWindow, 197, 119);
-
-    is(SelectionHelperUI.isActive, true, "selection active");
-  },
-  tearDown: tearDownHelper,
-});
-*/
-
 function test() {
   if (!isLandscapeMode()) {
     todo(false, "browser_selection_tests need landscape mode to run.");
     return;
   }
 
   requestLongerTimeout(3);
   runTests();
--- a/content/base/src/nsDOMFileReader.h
+++ b/content/base/src/nsDOMFileReader.h
@@ -140,12 +140,12 @@ protected:
   nsCString mCharset;
   uint32_t mDataLen;
 
   eDataFormat mDataFormat;
 
   nsString mResult;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   
-  JSObject* mResultArrayBuffer;
+  JS::Heap<JSObject*> mResultArrayBuffer;
 };
 
 #endif
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -1781,17 +1781,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 struct CustomPrototypeTraceArgs {
   const TraceCallbacks& callbacks;
   void* closure;
 };
 
 
 static PLDHashOperator
-CustomPrototypeTrace(const nsAString& aName, JSObject*& aObject, void *aArg)
+CustomPrototypeTrace(const nsAString& aName, JS::Heap<JSObject*>& aObject, void *aArg)
 {
   CustomPrototypeTraceArgs* traceArgs = static_cast<CustomPrototypeTraceArgs*>(aArg);
   MOZ_ASSERT(aObject, "Protocol object value must not be null");
   traceArgs->callbacks.Trace(&aObject, "mCustomPrototypes entry", traceArgs->closure);
   return PL_DHASH_NEXT;
 }
 
 
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -26,16 +26,17 @@
 #include "nsIContent.h"
 #include "nsEventListenerManager.h"
 #include "nsIDOMNodeSelector.h"
 #include "nsIPrincipal.h"
 #include "nsIParser.h"
 #include "nsBindingManager.h"
 #include "nsINodeInfo.h"
 #include "nsInterfaceHashtable.h"
+#include "nsJSThingHashtable.h"
 #include "nsIBoxObject.h"
 #include "nsPIBoxObject.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIURI.h"
 #include "nsScriptLoader.h"
 #include "nsIRadioGroupContainer.h"
 #include "nsILayoutHistoryState.h"
 #include "nsIRequest.h"
@@ -1198,17 +1199,17 @@ protected:
   nsTArray<nsWeakPtr> mFullScreenStack;
 
   // The root of the doc tree in which this document is in. This is only
   // non-null when this document is in fullscreen mode.
   nsWeakPtr mFullscreenRoot;
 
   // Hashtable for custom element prototypes in web components.
   // Custom prototypes are in the document's compartment.
-  nsDataHashtable<nsStringHashKey, JSObject*> mCustomPrototypes;
+  nsJSThingHashtable<nsStringHashKey, JSObject*> mCustomPrototypes;
 
   nsRefPtr<nsEventListenerManager> mListenerManager;
   nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
   nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
   nsRefPtr<nsScriptLoader> mScriptLoader;
   nsDocHeaderData* mHeaderData;
   /* mIdentifierMap works as follows for IDs:
    * 1) Attribute changes affect the table immediately (removing and adding
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -777,22 +777,24 @@ nsresult
 nsXMLHttpRequest::CreateResponseParsedJSON(JSContext* aCx)
 {
   if (!aCx) {
     return NS_ERROR_FAILURE;
   }
   RootJSResultObjects();
 
   // The Unicode converter has already zapped the BOM if there was one
+  JS::Rooted<JS::Value> value(aCx);
   if (!JS_ParseJSON(aCx,
                     static_cast<const jschar*>(mResponseText.get()), mResponseText.Length(),
-                    JS::MutableHandle<JS::Value>::fromMarkedLocation(&mResultJSON))) {
+                    &value)) {
     return NS_ERROR_FAILURE;
   }
 
+  mResultJSON = value;
   return NS_OK;
 }
 
 void
 nsXMLHttpRequest::CreatePartialBlob()
 {
   if (mDOMFile) {
     if (mLoadTotal == mLoadTransferred) {
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -652,24 +652,24 @@ protected:
    * @param aType The progress event type.
    * @param aFlag A XML_HTTP_REQUEST_* state flag defined in
    *              nsXMLHttpRequest.cpp.
    */
   void CloseRequestWithError(const nsAString& aType, const uint32_t aFlag);
 
   bool mFirstStartRequestSeen;
   bool mInLoadProgressEvent;
-  
+
   nsCOMPtr<nsIAsyncVerifyRedirectCallback> mRedirectCallback;
   nsCOMPtr<nsIChannel> mNewRedirectChannel;
-  
-  JS::Value mResultJSON;
+
+  JS::Heap<JS::Value> mResultJSON;
 
   js::ArrayBufferBuilder mArrayBufferBuilder;
-  JSObject* mResultArrayBuffer;
+  JS::Heap<JSObject*> mResultArrayBuffer;
 
   void ResetResponse();
 
   struct RequestHeader
   {
     nsCString header;
     nsCString value;
   };
--- a/content/canvas/src/ImageData.h
+++ b/content/canvas/src/ImageData.h
@@ -64,15 +64,15 @@ public:
 
 private:
   void HoldData();
   void DropData();
 
   ImageData() MOZ_DELETE;
 
   uint32_t mWidth, mHeight;
-  JSObject* mData;
+  JS::Heap<JSObject*> mData;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ImageData_h
--- a/content/events/src/nsDOMMessageEvent.h
+++ b/content/events/src/nsDOMMessageEvent.h
@@ -60,15 +60,15 @@ public:
                         nsIDOMWindow* aSource,
                         mozilla::ErrorResult& aRv)
   {
     aRv = InitMessageEvent(aType, aCanBubble, aCancelable, aData,
                            aOrigin, aLastEventId, aSource);
   }
 
 private:
-  JS::Value mData;
+  JS::Heap<JS::Value> mData;
   nsString mOrigin;
   nsString mLastEventId;
   nsCOMPtr<nsIDOMWindow> mSource;
 };
 
 #endif // nsDOMMessageEvent_h__
--- a/content/events/src/nsDOMNotifyAudioAvailableEvent.h
+++ b/content/events/src/nsDOMNotifyAudioAvailableEvent.h
@@ -65,13 +65,13 @@ public:
                                uint32_t aFrameBufferLength,
                                float aTime,
                                bool aAllowAudioData,
                                mozilla::ErrorResult& aRv);
 private:
   nsAutoArrayPtr<float> mFrameBuffer;
   uint32_t mFrameBufferLength;
   float mTime;
-  JSObject* mCachedArray;
+  JS::Heap<JSObject*> mCachedArray;
   bool mAllowAudioData;
 };
 
 #endif // nsDOMNotifyAudioAvailableEvent_h_
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -292,17 +292,17 @@ protected:
   nsRefPtr<nsContentList> mApplets;
   nsRefPtr<nsContentList> mEmbeds;
   nsRefPtr<nsContentList> mLinks;
   nsRefPtr<nsContentList> mAnchors;
   nsRefPtr<nsContentList> mScripts;
   nsRefPtr<nsContentList> mForms;
   nsRefPtr<nsContentList> mFormControls;
 
-  JSObject* mAll;
+  JS::Heap<JSObject*> mAll;
 
   /** # of forms in the document, synchronously set */
   int32_t mNumForms;
 
   static uint32_t gWyciwygSessionCnt;
 
   static void TryHintCharset(nsIMarkupDocumentViewer* aMarkupDV,
                              int32_t& aCharsetSource,
--- a/content/media/MediaDecoder.cpp
+++ b/content/media/MediaDecoder.cpp
@@ -1041,42 +1041,16 @@ void MediaDecoder::NotifyBytesConsumed(i
   NS_ENSURE_TRUE_VOID(mDecoderStateMachine);
   MOZ_ASSERT(OnStateMachineThread() || OnDecodeThread());
   if (!mIgnoreProgressData) {
     mDecoderPosition += aBytes;
     mPlaybackStatistics.AddBytes(aBytes);
   }
 }
 
-void MediaDecoder::NextFrameUnavailableBuffering()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mOwner || mShuttingDown || !mDecoderStateMachine)
-    return;
-
-  mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING);
-}
-
-void MediaDecoder::NextFrameAvailable()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mOwner || mShuttingDown || !mDecoderStateMachine)
-    return;
-
-  mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_AVAILABLE);
-}
-
-void MediaDecoder::NextFrameUnavailable()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mOwner || mShuttingDown || !mDecoderStateMachine)
-    return;
-  mOwner->UpdateReadyStateForData(MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE);
-}
-
 void MediaDecoder::UpdateReadyStateForData()
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!mOwner || mShuttingDown || !mDecoderStateMachine)
     return;
   MediaDecoderOwner::NextFrameStatus frameStatus =
     mDecoderStateMachine->GetNextFrameStatus();
   mOwner->UpdateReadyStateForData(frameStatus);
--- a/content/media/MediaDecoder.h
+++ b/content/media/MediaDecoder.h
@@ -705,22 +705,16 @@ public:
   // thread.
   void SeekingStarted();
 
   // Called when the backend has changed the current playback
   // position. It dispatches a timeupdate event and invalidates the frame.
   // This must be called on the main thread only.
   void PlaybackPositionChanged();
 
-  // Calls mElement->UpdateReadyStateForData, telling it which state we have
-  // entered.  Main thread only.
-  void NextFrameUnavailableBuffering();
-  void NextFrameAvailable();
-  void NextFrameUnavailable();
-
   // Calls mElement->UpdateReadyStateForData, telling it whether we have
   // data for the next frame and if we're buffering. Main thread only.
   void UpdateReadyStateForData();
 
   // Find the end of the cached data starting at the current decoder
   // position.
   int64_t GetDownloadPosition();
 
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -2615,31 +2615,26 @@ void MediaDecoderStateMachine::UpdateRea
   mDecoder->GetReentrantMonitor().AssertCurrentThreadIn();
 
   MediaDecoderOwner::NextFrameStatus nextFrameStatus = GetNextFrameStatus();
   if (nextFrameStatus == mLastFrameStatus) {
     return;
   }
   mLastFrameStatus = nextFrameStatus;
 
+  /* This is a bit tricky. MediaDecoder::UpdateReadyStateForData will run on
+   * the main thread and re-evaluate GetNextFrameStatus there, passing it to
+   * HTMLMediaElement::UpdateReadyStateForData. It doesn't use the value of
+   * GetNextFrameStatus we computed here, because what we're computing here
+   * could be stale by the time MediaDecoder::UpdateReadyStateForData runs.
+   * We only compute GetNextFrameStatus here to avoid posting runnables to the main
+   * thread unnecessarily.
+   */
   nsCOMPtr<nsIRunnable> event;
-  switch (nextFrameStatus) {
-    case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING:
-      event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailableBuffering);
-      break;
-    case MediaDecoderOwner::NEXT_FRAME_AVAILABLE:
-      event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameAvailable);
-      break;
-    case MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE:
-      event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::NextFrameUnavailable);
-      break;
-    default:
-      PR_NOT_REACHED("unhandled frame state");
-  }
-
+  event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::UpdateReadyStateForData);
   NS_DispatchToMainThread(event, NS_DISPATCH_NORMAL);
 }
 
 bool MediaDecoderStateMachine::JustExitedQuickBuffering()
 {
   return !mDecodeStartTime.IsNull() &&
     mQuickBuffering &&
     (TimeStamp::Now() - mDecodeStartTime) < TimeDuration::FromMicroseconds(QUICK_BUFFER_THRESHOLD_USECS);
--- a/content/media/webaudio/AudioBuffer.cpp
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -74,17 +74,17 @@ AudioBuffer::InitializeBuffers(uint32_t 
   if (!mJSChannels.SetCapacity(aNumberOfChannels)) {
     return false;
   }
   for (uint32_t i = 0; i < aNumberOfChannels; ++i) {
     JS::RootedObject array(aJSContext, JS_NewFloat32Array(aJSContext, mLength));
     if (!array) {
       return false;
     }
-    mJSChannels.AppendElement(array);
+    mJSChannels.AppendElement(array.get());
   }
 
   return true;
 }
 
 JSObject*
 AudioBuffer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
 {
--- a/content/media/webaudio/AudioBuffer.h
+++ b/content/media/webaudio/AudioBuffer.h
@@ -111,17 +111,17 @@ public:
   void MixToMono(JSContext* aJSContext);
 
 protected:
   bool RestoreJSChannelData(JSContext* aJSContext);
   void ClearJSChannels();
 
   nsRefPtr<AudioContext> mContext;
   // Float32Arrays
-  AutoFallibleTArray<JSObject*,2> mJSChannels;
+  AutoFallibleTArray<JS::Heap<JSObject*>, 2> mJSChannels;
 
   // mSharedChannels aggregates the data from mJSChannels. This is non-null
   // if and only if the mJSChannels are neutered.
   nsRefPtr<ThreadSharedFloatArrayBufferList> mSharedChannels;
 
   uint32_t mLength;
   float mSampleRate;
 };
--- a/content/media/webaudio/WaveShaperNode.h
+++ b/content/media/webaudio/WaveShaperNode.h
@@ -32,15 +32,15 @@ public:
     return mCurve;
   }
   void SetCurve(const Float32Array* aData);
 
 private:
   void ClearCurve();
 
 private:
-  JSObject* mCurve;
+  JS::Heap<JSObject*> mCurve;
 };
 
 }
 }
 
 #endif
new file mode 100644
--- /dev/null
+++ b/content/xbl/src/nsXBLMaybeCompiled.h
@@ -0,0 +1,145 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsXBLMaybeCompiled_h__
+#define nsXBLMaybeCompiled_h__
+
+#include "js/RootingAPI.h"
+
+/*
+ * A union containing either a pointer representing uncompiled source or a
+ * JSObject* representing the compiled result.  The class is templated on the
+ * source object type.
+ *
+ * The purpose of abstracting this as a separate class is to allow it to be
+ * wrapped in a JS::Heap<T> to correctly handle post-barriering of the JSObject
+ * pointer, when present.
+ */
+template <class UncompiledT>
+class nsXBLMaybeCompiled
+{
+public:
+  nsXBLMaybeCompiled() : mUncompiled(BIT_UNCOMPILED) {}
+
+  nsXBLMaybeCompiled(UncompiledT* uncompiled)
+    : mUncompiled(reinterpret_cast<uintptr_t>(uncompiled) | BIT_UNCOMPILED) {}
+
+  nsXBLMaybeCompiled(JSObject* compiled) : mCompiled(compiled) {}
+
+  bool IsCompiled() const
+  {
+    return !(mUncompiled & BIT_UNCOMPILED);
+  }
+
+  UncompiledT* GetUncompiled() const
+  {
+    MOZ_ASSERT(!IsCompiled(), "Attempt to get compiled function as uncompiled");
+    uintptr_t unmasked = mUncompiled & ~BIT_UNCOMPILED;
+    return reinterpret_cast<UncompiledT*>(unmasked);
+  }
+
+  JSObject* GetJSFunction() const
+  {
+    MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled");
+    return mCompiled;
+  }
+
+private:
+  JSObject*& UnsafeGetJSFunction()
+  {
+    MOZ_ASSERT(IsCompiled(), "Attempt to get uncompiled function as compiled");
+    return mCompiled;
+  }
+
+  enum { BIT_UNCOMPILED = 1 << 0 };
+
+  union
+  {
+    // An pointer that represents the function before being compiled, with
+    // BIT_UNCOMPILED set.
+    uintptr_t mUncompiled;
+
+    // The JS object for the compiled result.
+    JSObject* mCompiled;
+  };
+
+  friend class js::RootMethods<nsXBLMaybeCompiled<UncompiledT> >;
+};
+
+/* Add support for JS::Heap<nsXBLMaybeCompiled>. */
+namespace js {
+
+template <class UncompiledT>
+struct RootMethods<nsXBLMaybeCompiled<UncompiledT> > : public RootMethods<JSObject *>
+{
+  typedef struct RootMethods<JSObject *> Base;
+
+  static nsXBLMaybeCompiled<UncompiledT> initial() { return nsXBLMaybeCompiled<UncompiledT>(); }
+
+  static bool poisoned(nsXBLMaybeCompiled<UncompiledT> function)
+  {
+    return function.IsCompiled() && Base::poisoned(function.GetJSFunction());
+  }
+
+  static bool needsPostBarrier(nsXBLMaybeCompiled<UncompiledT> function)
+  {
+    return function.IsCompiled() && Base::needsPostBarrier(function.GetJSFunction());
+  }
+
+#ifdef JSGC_GENERATIONAL
+  static void postBarrier(nsXBLMaybeCompiled<UncompiledT>* functionp)
+  {
+    Base::postBarrier(&functionp->UnsafeGetJSFunction());
+  }
+
+  static void relocate(nsXBLMaybeCompiled<UncompiledT>* functionp)
+  {
+    Base::relocate(&functionp->UnsafeGetJSFunction());
+  }
+#endif
+};
+
+template <class UncompiledT>
+class HeapBase<nsXBLMaybeCompiled<UncompiledT> >
+{
+  const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() const {
+    return *static_cast<const JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this);
+  }
+
+  JS::Heap<nsXBLMaybeCompiled<UncompiledT> >& wrapper() {
+    return *static_cast<JS::Heap<nsXBLMaybeCompiled<UncompiledT> >*>(this);
+  }
+
+  const nsXBLMaybeCompiled<UncompiledT>* extract() const {
+    return wrapper().address();
+  }
+
+  nsXBLMaybeCompiled<UncompiledT>* extract() {
+    return wrapper().unsafeGet();
+  }
+
+public:
+  bool IsCompiled() const { return extract()->IsCompiled(); }
+  UncompiledT* GetUncompiled() const { return extract()->GetUncompiled(); }
+  JSObject* GetJSFunction() const { return extract()->GetJSFunction(); }
+
+  void SetUncompiled(UncompiledT* source) {
+    wrapper().set(nsXBLMaybeCompiled<UncompiledT>(source));
+  }
+
+  void SetJSFunction(JSObject* function) {
+    wrapper().set(nsXBLMaybeCompiled<UncompiledT>(function));
+  }
+
+  JS::Heap<JSObject*>& AsHeapObject()
+  {
+    MOZ_ASSERT(extract()->IsCompiled());
+    return *reinterpret_cast<JS::Heap<JSObject*>*>(this);
+  }
+};
+
+} /* namespace js */
+
+#endif // nsXBLMaybeCompiled_h__
--- a/content/xbl/src/nsXBLProtoImplMethod.cpp
+++ b/content/xbl/src/nsXBLProtoImplMethod.cpp
@@ -19,18 +19,18 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsIXPConnect.h"
 #include "xpcpublic.h"
 #include "nsXBLPrototypeBinding.h"
 
 using namespace mozilla;
 
 nsXBLProtoImplMethod::nsXBLProtoImplMethod(const PRUnichar* aName) :
-  nsXBLProtoImplMember(aName), 
-  mUncompiledMethod(BIT_UNCOMPILED)
+  nsXBLProtoImplMember(aName),
+  mMethod()
 {
   MOZ_COUNT_CTOR(nsXBLProtoImplMethod);
 }
 
 nsXBLProtoImplMethod::~nsXBLProtoImplMethod()
 {
   MOZ_COUNT_DTOR(nsXBLProtoImplMethod);
 
@@ -103,22 +103,23 @@ nsXBLProtoImplMethod::InstallMember(JSCo
                   "Should not be installing an uncompiled method");
   MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
 
   JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
   JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScope(aCx, globalObject));
   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
 
   // now we want to reevaluate our property using aContext and the script object for this window...
-  if (mJSMethodObject) {
+  JS::Rooted<JSObject*> jsMethodObject(aCx, GetCompiledMethod());
+  if (jsMethodObject) {
     nsDependentString name(mName);
 
     // First, make the function in the compartment of the scope object.
     JSAutoCompartment ac(aCx, scopeObject);
-    JS::Rooted<JSObject*> method(aCx, ::JS_CloneFunctionObject(aCx, mJSMethodObject, scopeObject));
+    JS::Rooted<JSObject*> method(aCx, ::JS_CloneFunctionObject(aCx, jsMethodObject, scopeObject));
     if (!method) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Then, enter the content compartment, wrap the method pointer, and define
     // the wrapped version on the class object.
     JSAutoCompartment ac2(aCx, aTargetClassObject);
     if (!JS_WrapObject(aCx, method.address()))
@@ -144,27 +145,27 @@ nsXBLProtoImplMethod::CompileMember(nsIS
   NS_PRECONDITION(aClassObject,
                   "Must have class object to compile");
 
   nsXBLUncompiledMethod* uncompiledMethod = GetUncompiledMethod();
 
   // No parameters or body was supplied, so don't install method.
   if (!uncompiledMethod) {
     // Early return after which we consider ourselves compiled.
-    mJSMethodObject = nullptr;
+    SetCompiledMethod(nullptr);
 
     return NS_OK;
   }
 
   // Don't install method if no name was supplied.
   if (!mName) {
     delete uncompiledMethod;
 
     // Early return after which we consider ourselves compiled.
-    mJSMethodObject = nullptr;
+    SetCompiledMethod(nullptr);
 
     return NS_OK;
   }
 
   // We have a method.
   // Allocate an array for our arguments.
   int32_t paramCount = uncompiledMethod->GetParameterCount();
   char** args = nullptr;
@@ -214,73 +215,69 @@ nsXBLProtoImplMethod::CompileMember(nsIS
   // Destroy our uncompiled method and delete our arg list.
   delete uncompiledMethod;
   delete [] args;
   if (NS_FAILED(rv)) {
     SetUncompiledMethod(nullptr);
     return rv;
   }
 
-  mJSMethodObject = methodObject;
+  SetCompiledMethod(methodObject);
 
   return NS_OK;
 }
 
 void
 nsXBLProtoImplMethod::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
 {
-  if (IsCompiled() && mJSMethodObject) {
-    aCallbacks.Trace(&mJSMethodObject, "mJSMethodObject", aClosure);
+  if (IsCompiled() && GetCompiledMethod()) {
+    aCallbacks.Trace(&mMethod.AsHeapObject(), "mMethod", aClosure);
   }
 }
 
 nsresult
 nsXBLProtoImplMethod::Read(nsIScriptContext* aContext,
                            nsIObjectInputStream* aStream)
 {
   JS::Rooted<JSObject*> methodObject(aContext->GetNativeContext());
   nsresult rv = XBL_DeserializeFunction(aContext, aStream, &methodObject);
   if (NS_FAILED(rv)) {
     SetUncompiledMethod(nullptr);
     return rv;
   }
 
-  mJSMethodObject = methodObject;
-
-#ifdef DEBUG
-  mIsCompiled = true;
-#endif
+  SetCompiledMethod(methodObject);
 
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplMethod::Write(nsIScriptContext* aContext,
                             nsIObjectOutputStream* aStream)
 {
-  if (mJSMethodObject) {
+  MOZ_ASSERT(IsCompiled());
+  if (GetCompiledMethod()) {
     nsresult rv = aStream->Write8(XBLBinding_Serialize_Method);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = aStream->WriteWStringZ(mName);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    return XBL_SerializeFunction(aContext, aStream,
-                                 JS::Handle<JSObject*>::fromMarkedLocation(&mJSMethodObject));
+    return XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject());
   }
 
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplAnonymousMethod::Execute(nsIContent* aBoundElement)
 {
   NS_PRECONDITION(IsCompiled(), "Can't execute uncompiled method");
   
-  if (!mJSMethodObject) {
+  if (!GetCompiledMethod()) {
     // Nothing to do here
     return NS_OK;
   }
 
   // Get the script context the same way
   // nsXBLProtoImpl::InstallImplementation does.
   nsIDocument* document = aBoundElement->OwnerDoc();
 
@@ -322,17 +319,17 @@ nsXBLProtoImplAnonymousMethod::Execute(n
 
   JSAutoCompartment ac(cx, scopeObject);
   if (!JS_WrapObject(cx, thisObject.address()))
       return NS_ERROR_OUT_OF_MEMORY;
 
   // Clone the function object, using thisObject as the parent so "this" is in
   // the scope chain of the resulting function (for backwards compat to the
   // days when this was an event handler).
-  JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, mJSMethodObject, thisObject));
+  JS::Rooted<JSObject*> method(cx, ::JS_CloneFunctionObject(cx, GetCompiledMethod(), thisObject));
   if (!method)
     return NS_ERROR_OUT_OF_MEMORY;
 
   // Now call the method
 
   // Check whether it's OK to call the method.
   rv = nsContentUtils::GetSecurityManager()->CheckFunctionAccess(cx, method,
                                                                  thisObject);
@@ -359,19 +356,19 @@ nsXBLProtoImplAnonymousMethod::Execute(n
   return NS_OK;
 }
 
 nsresult
 nsXBLProtoImplAnonymousMethod::Write(nsIScriptContext* aContext,
                                      nsIObjectOutputStream* aStream,
                                      XBLBindingSerializeDetails aType)
 {
-  if (mJSMethodObject) {
+  MOZ_ASSERT(IsCompiled());
+  if (GetCompiledMethod()) {
     nsresult rv = aStream->Write8(aType);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    rv = XBL_SerializeFunction(aContext, aStream,
-                               JS::Handle<JSObject*>::fromMarkedLocation(&mJSMethodObject));
+    rv = XBL_SerializeFunction(aContext, aStream, mMethod.AsHeapObject());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
--- a/content/xbl/src/nsXBLProtoImplMethod.h
+++ b/content/xbl/src/nsXBLProtoImplMethod.h
@@ -6,16 +6,17 @@
 #ifndef nsXBLProtoImplMethod_h__
 #define nsXBLProtoImplMethod_h__
 
 #include "mozilla/Attributes.h"
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "jsapi.h"
 #include "nsString.h"
+#include "nsXBLMaybeCompiled.h"
 #include "nsXBLProtoImplMember.h"
 #include "nsXBLSerialize.h"
 
 class nsIContent;
 
 struct nsXBLParameter {
   nsXBLParameter* mNext;
   char* mName;
@@ -97,39 +98,41 @@ public:
 
   virtual void Trace(const TraceCallbacks& aCallbacks, void *aClosure) MOZ_OVERRIDE;
 
   nsresult Read(nsIScriptContext* aContext, nsIObjectInputStream* aStream);
   virtual nsresult Write(nsIScriptContext* aContext, nsIObjectOutputStream* aStream) MOZ_OVERRIDE;
 
   bool IsCompiled() const
   {
-    return !(mUncompiledMethod & BIT_UNCOMPILED);
+    return mMethod.IsCompiled();
   }
+
   void SetUncompiledMethod(nsXBLUncompiledMethod* aUncompiledMethod)
   {
-    mUncompiledMethod = uintptr_t(aUncompiledMethod) | BIT_UNCOMPILED;
+    mMethod.SetUncompiled(aUncompiledMethod);
   }
+
   nsXBLUncompiledMethod* GetUncompiledMethod() const
   {
-    uintptr_t unmasked = mUncompiledMethod & ~BIT_UNCOMPILED;
-    return reinterpret_cast<nsXBLUncompiledMethod*>(unmasked);
+    return mMethod.GetUncompiled();
   }
 
 protected:
-  enum { BIT_UNCOMPILED = 1 << 0 };
+  void SetCompiledMethod(JSObject* aCompiledMethod)
+  {
+    mMethod.SetJSFunction(aCompiledMethod);
+  }
 
-  union {
-    uintptr_t mUncompiledMethod; // An object that represents the method before being compiled.
-    JSObject* mJSMethodObject;    // The JS object for the method (after compilation)
-  };
+  JSObject* GetCompiledMethod() const
+  {
+    return mMethod.GetJSFunction();
+  }
 
-#ifdef DEBUG
-  bool mIsCompiled;
-#endif
+  JS::Heap<nsXBLMaybeCompiled<nsXBLUncompiledMethod> > mMethod;
 };
 
 class nsXBLProtoImplAnonymousMethod : public nsXBLProtoImplMethod {
 public:
   nsXBLProtoImplAnonymousMethod() :
     nsXBLProtoImplMethod(EmptyString().get())
   {}
   
--- a/content/xbl/src/nsXBLProtoImplProperty.cpp
+++ b/content/xbl/src/nsXBLProtoImplProperty.cpp
@@ -22,18 +22,16 @@
 using namespace mozilla;
 
 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName,
                                                const PRUnichar* aGetter, 
                                                const PRUnichar* aSetter,
                                                const PRUnichar* aReadOnly,
                                                uint32_t aLineNumber) :
   nsXBLProtoImplMember(aName), 
-  mGetterText(nullptr),
-  mSetterText(nullptr),
   mJSAttributes(JSPROP_ENUMERATE)
 #ifdef DEBUG
   , mIsCompiled(false)
 #endif
 {
   MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
 
   if (aReadOnly) {
@@ -50,125 +48,112 @@ nsXBLProtoImplProperty::nsXBLProtoImplPr
     AppendSetterText(nsDependentString(aSetter));
     SetSetterLineNumber(aLineNumber);
   }
 }
 
 nsXBLProtoImplProperty::nsXBLProtoImplProperty(const PRUnichar* aName,
                                                const bool aIsReadOnly)
   : nsXBLProtoImplMember(aName),
-    mGetterText(nullptr),
-    mSetterText(nullptr),
     mJSAttributes(JSPROP_ENUMERATE)
 #ifdef DEBUG
   , mIsCompiled(false)
 #endif
 {
   MOZ_COUNT_CTOR(nsXBLProtoImplProperty);
 
   if (aIsReadOnly)
     mJSAttributes |= JSPROP_READONLY;
 }
 
 nsXBLProtoImplProperty::~nsXBLProtoImplProperty()
 {
   MOZ_COUNT_DTOR(nsXBLProtoImplProperty);
 
-  if (!(mJSAttributes & JSPROP_GETTER)) {
-    delete mGetterText;
+  if (!mGetter.IsCompiled()) {
+    delete mGetter.GetUncompiled();
   }
 
-  if (!(mJSAttributes & JSPROP_SETTER)) {
-    delete mSetterText;
+  if (!mSetter.IsCompiled()) {
+    delete mSetter.GetUncompiled();
+  }
+}
+
+void nsXBLProtoImplProperty::EnsureUncompiledText(PropertyOp& aPropertyOp)
+{
+  if (!aPropertyOp.GetUncompiled()) {
+    nsXBLTextWithLineNumber* text = new nsXBLTextWithLineNumber();
+    aPropertyOp.SetUncompiled(text);
   }
 }
 
 void 
 nsXBLProtoImplProperty::AppendGetterText(const nsAString& aText)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Must not be compiled when accessing getter text");
-  if (!mGetterText) {
-    mGetterText = new nsXBLTextWithLineNumber();
-    if (!mGetterText)
-      return;
-  }
-
-  mGetterText->AppendText(aText);
+  EnsureUncompiledText(mGetter);
+  mGetter.GetUncompiled()->AppendText(aText);
 }
 
 void 
 nsXBLProtoImplProperty::AppendSetterText(const nsAString& aText)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Must not be compiled when accessing setter text");
-  if (!mSetterText) {
-    mSetterText = new nsXBLTextWithLineNumber();
-    if (!mSetterText)
-      return;
-  }
-
-  mSetterText->AppendText(aText);
+  EnsureUncompiledText(mSetter);
+  mSetter.GetUncompiled()->AppendText(aText);
 }
 
 void
 nsXBLProtoImplProperty::SetGetterLineNumber(uint32_t aLineNumber)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Must not be compiled when accessing getter text");
-  if (!mGetterText) {
-    mGetterText = new nsXBLTextWithLineNumber();
-    if (!mGetterText)
-      return;
-  }
-
-  mGetterText->SetLineNumber(aLineNumber);
+  EnsureUncompiledText(mGetter);
+  mGetter.GetUncompiled()->SetLineNumber(aLineNumber);
 }
 
 void
 nsXBLProtoImplProperty::SetSetterLineNumber(uint32_t aLineNumber)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Must not be compiled when accessing setter text");
-  if (!mSetterText) {
-    mSetterText = new nsXBLTextWithLineNumber();
-    if (!mSetterText)
-      return;
-  }
-
-  mSetterText->SetLineNumber(aLineNumber);
+  EnsureUncompiledText(mSetter);
+  mSetter.GetUncompiled()->SetLineNumber(aLineNumber);
 }
 
 const char* gPropertyArgs[] = { "val" };
 
 nsresult
 nsXBLProtoImplProperty::InstallMember(JSContext *aCx,
                                       JS::Handle<JSObject*> aTargetClassObject)
 {
   NS_PRECONDITION(mIsCompiled,
                   "Should not be installing an uncompiled property");
+  MOZ_ASSERT(mGetter.IsCompiled() && mSetter.IsCompiled());
   MOZ_ASSERT(js::IsObjectInContextCompartment(aTargetClassObject, aCx));
   JS::Rooted<JSObject*> globalObject(aCx, JS_GetGlobalForObject(aCx, aTargetClassObject));
   JS::Rooted<JSObject*> scopeObject(aCx, xpc::GetXBLScope(aCx, globalObject));
   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
 
   // now we want to reevaluate our property using aContext and the script object for this window...
-  if (mJSGetterObject || mJSSetterObject) {
+  if (mGetter.GetJSFunction() || mSetter.GetJSFunction()) {
     // First, enter the compartment of the scope object and clone the functions.
     JSAutoCompartment ac(aCx, scopeObject);
 
     JS::Rooted<JSObject*> getter(aCx, nullptr);
-    if (mJSGetterObject) {
-      if (!(getter = ::JS_CloneFunctionObject(aCx, mJSGetterObject, scopeObject)))
+    if (mGetter.GetJSFunction()) {
+      if (!(getter = ::JS_CloneFunctionObject(aCx, mGetter.GetJSFunction(), scopeObject)))
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     JS::Rooted<JSObject*> setter(aCx, nullptr);
-    if (mJSSetterObject) {
-      if (!(setter = ::JS_CloneFunctionObject(aCx, mJSSetterObject, scopeObject)))
+    if (mSetter.GetJSFunction()) {
+      if (!(setter = ::JS_CloneFunctionObject(aCx, mSetter.GetJSFunction(), scopeObject)))
         return NS_ERROR_OUT_OF_MEMORY;
     }
 
     // Now, enter the content compartment, wrap the getter/setter, and define
     // them on the class object.
     JSAutoCompartment ac2(aCx, aTargetClassObject);
     nsDependentString name(mName);
     if (!JS_WrapObject(aCx, getter.address()) ||
@@ -187,161 +172,164 @@ nsXBLProtoImplProperty::InstallMember(JS
 nsresult
 nsXBLProtoImplProperty::CompileMember(nsIScriptContext* aContext, const nsCString& aClassStr,
                                       JS::Handle<JSObject*> aClassObject)
 {
   NS_PRECONDITION(!mIsCompiled,
                   "Trying to compile an already-compiled property");
   NS_PRECONDITION(aClassObject,
                   "Must have class object to compile");
+  MOZ_ASSERT(!mGetter.IsCompiled() && !mSetter.IsCompiled());
 
   if (!mName)
     return NS_ERROR_FAILURE; // Without a valid name, we can't install the member.
 
   // We have a property.
   nsresult rv = NS_OK;
 
   nsAutoCString functionUri;
-  if (mGetterText || mSetterText) {
+  if (mGetter.GetUncompiled() || mSetter.GetUncompiled()) {
     functionUri = aClassStr;
     int32_t hash = functionUri.RFindChar('#');
     if (hash != kNotFound) {
       functionUri.Truncate(hash);
     }
   }
 
   bool deletedGetter = false;
-  if (mGetterText && mGetterText->GetText()) {
-    nsDependentString getter(mGetterText->GetText());
+  nsXBLTextWithLineNumber *getterText = mGetter.GetUncompiled();
+  if (getterText && getterText->GetText()) {
+    nsDependentString getter(getterText->GetText());
     if (!getter.IsEmpty()) {
       AutoPushJSContext cx(aContext->GetNativeContext());
       JSAutoCompartment ac(cx, aClassObject);
       JS::CompileOptions options(cx);
-      options.setFileAndLine(functionUri.get(), mGetterText->GetLineNumber())
+      options.setFileAndLine(functionUri.get(), getterText->GetLineNumber())
              .setVersion(JSVERSION_LATEST);
       nsCString name = NS_LITERAL_CSTRING("get_") + NS_ConvertUTF16toUTF8(mName);
       JS::RootedObject rootedNull(cx, nullptr); // See bug 781070.
       JS::RootedObject getterObject(cx);
       rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 0, nullptr,
                                       getter, getterObject.address());
 
-      // Make sure we free mGetterText here before setting mJSGetterObject, since
-      // that'll overwrite mGetterText
-      delete mGetterText;
+      delete getterText;
       deletedGetter = true;
-      mJSGetterObject = getterObject;
+
+      mGetter.SetJSFunction(getterObject);
     
-      if (mJSGetterObject && NS_SUCCEEDED(rv)) {
+      if (mGetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
         mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
       }
       if (NS_FAILED(rv)) {
-        mJSGetterObject = nullptr;
+        mGetter.SetJSFunction(nullptr);
         mJSAttributes &= ~JSPROP_GETTER;
         /*chaining to return failure*/
       }
     }
   } // if getter is not empty
 
   if (!deletedGetter) {  // Empty getter
-    delete mGetterText;
-    mJSGetterObject = nullptr;
+    delete getterText;
+    mGetter.SetJSFunction(nullptr);
   }
   
   if (NS_FAILED(rv)) {
     // We failed to compile our getter.  So either we've set it to null, or
     // it's still set to the text object.  In either case, it's safe to return
     // the error here, since then we'll be cleaned up as uncompiled and that
     // will be ok.  Going on and compiling the setter and _then_ returning an
     // error, on the other hand, will try to clean up a compiled setter as
     // uncompiled and crash.
     return rv;
   }
 
   bool deletedSetter = false;
-  if (mSetterText && mSetterText->GetText()) {
-    nsDependentString setter(mSetterText->GetText());
+  nsXBLTextWithLineNumber *setterText = mSetter.GetUncompiled();
+  if (setterText && setterText->GetText()) {
+    nsDependentString setter(setterText->GetText());
     if (!setter.IsEmpty()) {
       AutoPushJSContext cx(aContext->GetNativeContext());
       JSAutoCompartment ac(cx, aClassObject);
       JS::CompileOptions options(cx);
-      options.setFileAndLine(functionUri.get(), mSetterText->GetLineNumber())
+      options.setFileAndLine(functionUri.get(), setterText->GetLineNumber())
              .setVersion(JSVERSION_LATEST);
       nsCString name = NS_LITERAL_CSTRING("set_") + NS_ConvertUTF16toUTF8(mName);
       JS::RootedObject rootedNull(cx, nullptr); // See bug 781070.
       JS::RootedObject setterObject(cx);
       rv = nsJSUtils::CompileFunction(cx, rootedNull, options, name, 1,
                                       gPropertyArgs, setter, setterObject.address());
 
-      // Make sure we free mSetterText here before setting mJSGetterObject, since
-      // that'll overwrite mSetterText
-      delete mSetterText;
+      delete setterText;
       deletedSetter = true;
-      mJSSetterObject = setterObject;
+      mSetter.SetJSFunction(setterObject);
 
-      if (mJSSetterObject && NS_SUCCEEDED(rv)) {
+      if (mSetter.GetJSFunction() && NS_SUCCEEDED(rv)) {
         mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
       }
       if (NS_FAILED(rv)) {
-        mJSSetterObject = nullptr;
+        mSetter.SetJSFunction(nullptr);
         mJSAttributes &= ~JSPROP_SETTER;
         /*chaining to return failure*/
       }
     }
   } // if setter wasn't empty....
 
   if (!deletedSetter) {  // Empty setter
-    delete mSetterText;
-    mJSSetterObject = nullptr;
+    delete setterText;
+    mSetter.SetJSFunction(nullptr);
   }
 
 #ifdef DEBUG
   mIsCompiled = NS_SUCCEEDED(rv);
 #endif
 
   return rv;
 }
 
 void
 nsXBLProtoImplProperty::Trace(const TraceCallbacks& aCallbacks, void *aClosure)
 {
   if (mJSAttributes & JSPROP_GETTER) {
-    aCallbacks.Trace(&mJSGetterObject, "mJSGetterObject", aClosure);
+    aCallbacks.Trace(&mGetter.AsHeapObject(), "mGetter", aClosure);
   }
 
   if (mJSAttributes & JSPROP_SETTER) {
-    aCallbacks.Trace(&mJSSetterObject, "mJSSetterObject", aClosure);
+    aCallbacks.Trace(&mSetter.AsHeapObject(), "mSetter", aClosure);
   }
 }
 
 nsresult
 nsXBLProtoImplProperty::Read(nsIScriptContext* aContext,
                              nsIObjectInputStream* aStream,
                              XBLBindingSerializeDetails aType)
 {
+  MOZ_ASSERT(!mIsCompiled);
+  MOZ_ASSERT(!mGetter.GetUncompiled() && !mSetter.GetUncompiled());
+
   JSContext *cx = aContext->GetNativeContext();
 
+  JS::Rooted<JSObject*> getterObject(cx);
   if (aType == XBLBinding_Serialize_GetterProperty ||
       aType == XBLBinding_Serialize_GetterSetterProperty) {
-    JS::Rooted<JSObject*> getterObject(cx);
     nsresult rv = XBL_DeserializeFunction(aContext, aStream, &getterObject);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mJSGetterObject = getterObject;
     mJSAttributes |= JSPROP_GETTER | JSPROP_SHARED;
   }
-
+  mGetter.SetJSFunction(getterObject);
+  
+  JS::Rooted<JSObject*> setterObject(cx);
   if (aType == XBLBinding_Serialize_SetterProperty ||
       aType == XBLBinding_Serialize_GetterSetterProperty) {
-    JS::Rooted<JSObject*> setterObject(cx);
     nsresult rv = XBL_DeserializeFunction(aContext, aStream, &setterObject);
     NS_ENSURE_SUCCESS(rv, rv);
 
-    mJSSetterObject = setterObject;
     mJSAttributes |= JSPROP_SETTER | JSPROP_SHARED;
   }
+  mSetter.SetJSFunction(setterObject);
 
 #ifdef DEBUG
   mIsCompiled = true;
 #endif
 
   return NS_OK;
 }
 
@@ -365,21 +353,19 @@ nsXBLProtoImplProperty::Write(nsIScriptC
   }
 
   nsresult rv = aStream->Write8(type);
   NS_ENSURE_SUCCESS(rv, rv);
   rv = aStream->WriteWStringZ(mName);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mJSAttributes & JSPROP_GETTER) {
-    rv = XBL_SerializeFunction(aContext, aStream,
-      JS::Handle<JSObject*>::fromMarkedLocation(&mJSGetterObject));
+    rv = XBL_SerializeFunction(aContext, aStream, mGetter.AsHeapObject());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   if (mJSAttributes & JSPROP_SETTER) {
-    rv = XBL_SerializeFunction(aContext, aStream,
-      JS::Handle<JSObject*>::fromMarkedLocation(&mJSSetterObject));
+    rv = XBL_SerializeFunction(aContext, aStream, mSetter.AsHeapObject());
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   return NS_OK;
 }
--- a/content/xbl/src/nsXBLProtoImplProperty.h
+++ b/content/xbl/src/nsXBLProtoImplProperty.h
@@ -7,16 +7,17 @@
 #define nsXBLProtoImplProperty_h__
 
 #include "mozilla/Attributes.h"
 #include "nsIAtom.h"
 #include "nsString.h"
 #include "jsapi.h"
 #include "nsString.h"
 #include "nsXBLSerialize.h"
+#include "nsXBLMaybeCompiled.h"
 #include "nsXBLProtoImplMember.h"
 
 class nsXBLProtoImplProperty: public nsXBLProtoImplMember
 {
 public:
   nsXBLProtoImplProperty(const PRUnichar* aName,
                          const PRUnichar* aGetter, 
                          const PRUnichar* aSetter,
@@ -43,30 +44,26 @@ public:
 
   nsresult Read(nsIScriptContext* aContext,
                 nsIObjectInputStream* aStream,
                 XBLBindingSerializeDetails aType);
   virtual nsresult Write(nsIScriptContext* aContext,
                          nsIObjectOutputStream* aStream) MOZ_OVERRIDE;
 
 protected:
-  union {
-    // The raw text for the getter (prior to compilation).
-    nsXBLTextWithLineNumber* mGetterText;
-    // The JS object for the getter (after compilation)
-    JSObject *               mJSGetterObject;
-  };
+  typedef JS::Heap<nsXBLMaybeCompiled<nsXBLTextWithLineNumber> > PropertyOp;
+
+  void EnsureUncompiledText(PropertyOp& aPropertyOp);
 
-  union {
-    // The raw text for the setter (prior to compilation).
-    nsXBLTextWithLineNumber* mSetterText;
-    // The JS object for the setter (after compilation)
-    JSObject *               mJSSetterObject;
-  };
+  // The raw text for the getter, or the JS object (after compilation).
+  PropertyOp mGetter;
+
+  // The raw text for the setter, or the JS object (after compilation).
+  PropertyOp mSetter;
   
-  unsigned mJSAttributes;          // A flag for all our JS properties (getter/setter/readonly/shared/enum)
+  unsigned mJSAttributes;  // A flag for all our JS properties (getter/setter/readonly/shared/enum)
 
 #ifdef DEBUG
   bool mIsCompiled;
 #endif
 };
 
 #endif // nsXBLProtoImplProperty_h__
--- a/content/xul/content/src/nsXULElement.cpp
+++ b/content/xul/content/src/nsXULElement.cpp
@@ -2378,18 +2378,17 @@ nsXULPrototypeScript::Serialize(nsIObjec
 
     // Write basic prototype data
     nsresult rv;
     rv = aStream->Write32(mLineNo);
     if (NS_FAILED(rv)) return rv;
     rv = aStream->Write32(mLangVersion);
     if (NS_FAILED(rv)) return rv;
     // And delegate the writing to the nsIScriptContext
-    rv = context->Serialize(aStream,
-                            JS::Handle<JSScript*>::fromMarkedLocation(&mScriptObject));
+    rv = context->Serialize(aStream, mScriptObject);
     if (NS_FAILED(rv)) return rv;
 
     return NS_OK;
 }
 
 nsresult
 nsXULPrototypeScript::SerializeOutOfLine(nsIObjectOutputStream* aStream,
                                          nsIScriptGlobalObject* aGlobal)
--- a/content/xul/content/src/nsXULElement.h
+++ b/content/xul/content/src/nsXULElement.h
@@ -238,23 +238,23 @@ public:
     void Set(JSScript* aObject);
 
     // It's safe to return a handle because we trace mScriptObject, no one ever
     // uses the handle (or the script object) past the point at which the
     // nsXULPrototypeScript dies, and we can't get memmoved so the
     // &mScriptObject pointer can't go stale.
     JS::Handle<JSScript*> GetScriptObject()
     {
-        return JS::Handle<JSScript*>::fromMarkedLocation(&mScriptObject);
+        return JS::Handle<JSScript*>(mScriptObject);
     }
 
     void TraceScriptObject(JSTracer* aTrc)
     {
         if (mScriptObject) {
-            JS_CallScriptTracer(aTrc, &mScriptObject, "active window XUL prototype script");
+            JS_CallHeapScriptTracer(aTrc, &mScriptObject, "active window XUL prototype script");
         }
     }
 
     void Trace(const TraceCallbacks& aCallbacks, void* aClosure)
     {
         if (mScriptObject) {
             aCallbacks.Trace(&mScriptObject, "mScriptObject", aClosure);
         }
@@ -262,17 +262,17 @@ public:
 
     nsCOMPtr<nsIURI>         mSrcURI;
     uint32_t                 mLineNo;
     bool                     mSrcLoading;
     bool                     mOutOfLine;
     mozilla::dom::XULDocument* mSrcLoadWaiters;   // [OWNER] but not COMPtr
     uint32_t                 mLangVersion;
 private:
-    JSScript*                mScriptObject;
+    JS::Heap<JSScript*>      mScriptObject;
 };
 
 class nsXULPrototypeText : public nsXULPrototypeNode
 {
 public:
     nsXULPrototypeText()
         : nsXULPrototypeNode(eType_Text)
     {
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -7885,27 +7885,16 @@ nsDocShell::RestoreFromHistory()
     newVM = nullptr;
 
     // Simulate the completion of the load.
     nsDocShell::FinishRestore();
 
     // Restart plugins, and paint the content.
     if (shell) {
         shell->Thaw();
-
-        newVM = shell->GetViewManager();
-        if (newVM) {
-            // When we insert the root view above the resulting invalidate is
-            // dropped because painting is suppressed in the presshell until we
-            // call Thaw. So we issue the invalidate here.
-            newRootView = newVM->GetRootView();
-            if (newRootView) {
-                newVM->InvalidateView(newRootView);
-            }
-        }
     }
 
     return privWin->FireDelayedDOMEvents();
 }
 
 NS_IMETHODIMP
 nsDocShell::CreateContentViewer(const char *aContentType,
                                 nsIRequest * request,
--- a/dom/base/DOMRequest.h
+++ b/dom/base/DOMRequest.h
@@ -17,17 +17,17 @@
 
 namespace mozilla {
 namespace dom {
 
 class DOMRequest : public nsDOMEventTargetHelper,
                    public nsIDOMDOMRequest
 {
 protected:
-  JS::Value mResult;
+  JS::Heap<JS::Value> mResult;
   nsRefPtr<DOMError> mError;
   bool mDone;
   bool mRooted;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMDOMREQUEST
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(nsDOMEventTargetHelper)
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -89,17 +89,17 @@ CPP_SOURCES += [
     'nsMimeTypeArray.cpp',
     'nsPerformance.cpp',
     'nsPluginArray.cpp',
     'nsQueryContentEventResult.cpp',
     'nsScreen.cpp',
     'nsScriptNameSpaceManager.cpp',
     'nsStructuredCloneContainer.cpp',
     'nsWindowMemoryReporter.cpp',
-    'nsWindowRoot.cpp',
+    'nsWindowRoot.cpp'
 ]
 
 EXTRA_COMPONENTS += [
     'ConsoleAPI.js',
     'ConsoleAPI.manifest',
     'SiteSpecificUserAgent.js',
     'SiteSpecificUserAgent.manifest',
 ]
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1583,26 +1583,26 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   END_OUTER_WINDOW_ONLY
 NS_INTERFACE_MAP_END
 
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGlobalWindow)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGlobalWindow)
 
 static PLDHashOperator
-MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject* aData, void* aClosure)
+MarkXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
 {
   xpc_UnmarkGrayObject(aData);
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGlobalWindow)
   if (tmp->IsBlackForCC()) {
     if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) {
-      tmp->mCachedXBLPrototypeHandlers.EnumerateRead(MarkXBLHandlers, nullptr);
+      tmp->mCachedXBLPrototypeHandlers.Enumerate(MarkXBLHandlers, nullptr);
     }
     nsEventListenerManager* elm = tmp->GetListenerManager(false);
     if (elm) {
       elm->MarkForCC();
     }
     tmp->UnmarkGrayTimers();
     return true;
   }
@@ -1744,17 +1744,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 struct TraceData
 {
   const TraceCallbacks& callbacks;
   void* closure;
 };
 
 static PLDHashOperator
-TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JSObject*& aData, void* aClosure)
+TraceXBLHandlers(nsXBLPrototypeHandler* aKey, JS::Heap<JSObject*>& aData, void* aClosure)
 {
   TraceData* data = static_cast<TraceData*>(aClosure);
   data->callbacks.Trace(&aData, "Cached XBL prototype handler", data->closure);
   return PL_DHASH_NEXT;
 }
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGlobalWindow)
   if (tmp->mCachedXBLPrototypeHandlers.IsInitialized()) {
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -10,16 +10,17 @@
 #include "mozilla/XPCOM.h" // for TimeStamp/TimeDuration
 
 // Local Includes
 // Helper Classes
 #include "nsCOMPtr.h"
 #include "nsAutoPtr.h"
 #include "nsWeakReference.h"
 #include "nsDataHashtable.h"
+#include "nsJSThingHashtable.h"
 #include "nsCycleCollectionParticipant.h"
 
 // Interfaces Needed
 #include "nsDOMWindowList.h"
 #include "nsIBaseWindow.h"
 #include "nsIBrowserDOMWindow.h"
 #include "nsIDocShellTreeOwner.h"
 #include "nsIDocShellTreeItem.h"
@@ -1229,17 +1230,17 @@ protected:
   bool mSetOpenerWindowCalled;
   nsCOMPtr<nsIURI> mLastOpenedURI;
 #endif
 
   bool mCleanedUp, mCallCleanUpAfterModalDialogCloses;
 
   nsCOMPtr<nsIDOMOfflineResourceList> mApplicationCache;
 
-  nsDataHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> mCachedXBLPrototypeHandlers;
+  nsJSThingHashtable<nsPtrHashKey<nsXBLPrototypeHandler>, JSObject*> mCachedXBLPrototypeHandlers;
 
   nsCOMPtr<nsIDocument> mSuspendedDoc;
 
   nsRefPtr<mozilla::dom::indexedDB::IDBFactory> mIndexedDB;
 
   // This counts the number of windows that have been opened in rapid succession
   // (i.e. within dom.successive_dialog_time_limit of each other). It is reset
   // to 0 once a dialog is opened after dom.successive_dialog_time_limit seconds
--- a/dom/base/nsIJSEventListener.h
+++ b/dom/base/nsIJSEventListener.h
@@ -263,17 +263,17 @@ protected:
     NS_ASSERTION(!mTarget, "Should have called Disconnect()!");
   }
 
   // Update our mScopeObject; we have to make sure we properly handle
   // the hold/drop stuff, so have to do it in nsJSEventListener.
   virtual void UpdateScopeObject(JS::Handle<JSObject*> aScopeObject) = 0;
 
   nsCOMPtr<nsIScriptContext> mContext;
-  JSObject* mScopeObject;
+  JS::Heap<JSObject*> mScopeObject;
   nsISupports* mTarget;
   nsCOMPtr<nsIAtom> mEventName;
   nsEventHandler mHandler;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSEventListener, NS_IJSEVENTLISTENER_IID)
 
 /* factory function.  aHandler must already be bound to aTarget.
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -3518,30 +3518,31 @@ public:
 
   // nsIJSArgArray
   nsresult GetArgs(uint32_t *argc, void **argv);
 
   void ReleaseJSObjects();
 
 protected:
   JSContext *mContext;
-  JS::Value *mArgv;
+  JS::Heap<JS::Value> *mArgv;
   uint32_t mArgc;
 };
 
 nsJSArgArray::nsJSArgArray(JSContext *aContext, uint32_t argc, JS::Value *argv,
                            nsresult *prv) :
     mContext(aContext),
     mArgv(nullptr),
     mArgc(argc)
 {
   // copy the array - we don't know its lifetime, and ours is tied to xpcom
-  // refcounting.  Alloc zero'd array so cleanup etc is safe.
+  // refcounting.
   if (argc) {
-    mArgv = (JS::Value *) PR_CALLOC(argc * sizeof(JS::Value));
+    static const fallible_t fallible = fallible_t();
+    mArgv = new (fallible) JS::Heap<JS::Value>[argc];
     if (!mArgv) {
       *prv = NS_ERROR_OUT_OF_MEMORY;
       return;
     }
   }
 
   // Callers are allowed to pass in a null argv even for argc > 0. They can
   // then use GetArgs to initialize the values.
@@ -3561,17 +3562,17 @@ nsJSArgArray::~nsJSArgArray()
 {
   ReleaseJSObjects();
 }
 
 void
 nsJSArgArray::ReleaseJSObjects()
 {
   if (mArgv) {
-    PR_DELETE(mArgv);
+    delete [] mArgv;
   }
   if (mArgc > 0) {
     mArgc = 0;
     NS_DROP_JS_OBJECTS(this, nsJSArgArray);
   }
 }
 
 // QueryInterface implementation for nsJSArgArray
--- a/dom/base/nsJSTimeoutHandler.cpp
+++ b/dom/base/nsJSTimeoutHandler.cpp
@@ -62,20 +62,21 @@ public:
 
   void ReleaseJSObjects();
 
 private:
   // filename, line number and JS language version string of the
   // caller of setTimeout()
   nsCString mFileName;
   uint32_t mLineNo;
-  nsTArray<JS::Value> mArgs;
+  nsTArray<JS::Heap<JS::Value> > mArgs;
 
   // The JS expression to evaluate or function to call, if !mExpr
-  JSFlatString *mExpr;
+  // Note this is always a flat string.
+  JS::Heap<JSString*> mExpr;
   nsRefPtr<Function> mFunction;
 };
 
 
 // nsJSScriptTimeoutHandler
 // QueryInterface implementation for nsJSScriptTimeoutHandler
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSScriptTimeoutHandler)
   tmp->ReleaseJSObjects();
@@ -276,17 +277,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
           // Note: Our only caller knows to turn NS_ERROR_DOM_TYPE_ERR into NS_OK.
           return NS_ERROR_DOM_TYPE_ERR;
         }
       }
     } // if there's no document, we don't have to do anything.
 
     NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
 
-    mExpr = expr;
+    mExpr = JS_FORGET_STRING_FLATNESS(expr);
 
     // Get the calling location.
     const char *filename;
     if (nsJSUtils::GetCallingLocation(cx, &filename, &mLineNo)) {
       mFileName.Assign(filename);
     }
   } else if (funobj) {
     NS_HOLD_JS_OBJECTS(this, nsJSScriptTimeoutHandler);
@@ -294,17 +295,18 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
     mFunction = new Function(funobj);
 
     // Create our arg array.  argc is the number of arguments passed
     // to setTimeout or setInterval; the first two are our callback
     // and the delay, so only arguments after that need to go in our
     // array.
     // std::max(argc - 2, 0) wouldn't work right because argc is unsigned.
     uint32_t argCount = std::max(argc, 2u) - 2;
-    FallibleTArray<JS::Value> args;
+
+    FallibleTArray<JS::Heap<JS::Value> > args;
     if (!args.SetCapacity(argCount)) {
       // No need to drop here, since we already have a non-null mFunction
       return NS_ERROR_OUT_OF_MEMORY;
     }
     for (uint32_t idx = 0; idx < argCount; ++idx) {
       *args.AppendElement() = argv[idx + 2];
     }
     args.SwapElements(mArgs);
@@ -314,17 +316,17 @@ nsJSScriptTimeoutHandler::Init(nsGlobalW
   *aInterval = interval;
   return NS_OK;
 }
 
 const PRUnichar *
 nsJSScriptTimeoutHandler::GetHandlerText()
 {
   NS_ASSERTION(mExpr, "No expression, so no handler text!");
-  return ::JS_GetFlatStringChars(mExpr);
+  return ::JS_GetFlatStringChars(JS_ASSERT_STRING_IS_FLAT(mExpr));
 }
 
 nsresult NS_CreateJSTimeoutHandler(nsGlobalWindow *aWindow,
                                    bool *aIsInterval,
                                    int32_t *aInterval,
                                    nsIScriptTimeoutHandler **aRet)
 {
   *aRet = nullptr;
--- a/dom/base/nsWrapperCache.h
+++ b/dom/base/nsWrapperCache.h
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsWrapperCache_h___
 #define nsWrapperCache_h___
 
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/Assertions.h"
-#include "js/RootingAPI.h"
+#include "js/Value.h"
 
 struct JSTracer;
 class JSObject;
 struct JSContext;
 class XPCWrappedNativeScope;
 
 namespace mozilla {
 namespace dom {
@@ -177,17 +177,17 @@ public:
     else {
       UnsetWrapperFlags(WRAPPER_BIT_PRESERVED);
     }
   }
 
   void TraceWrapper(const TraceCallbacks& aCallbacks, void* aClosure)
   {
     if (PreservingWrapper() && mWrapper) {
-        aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure);
+      aCallbacks.Trace(&mWrapper, "Preserved wrapper", aClosure);
     }
   }
 
   /* 
    * The following methods for getting and manipulating flags allow the unused
    * bits of mFlags to be used by derived classes.
    */
 
@@ -275,18 +275,18 @@ private:
    * (regular JS object or proxy) that has a system only wrapper for same-origin
    * access.
    */
   enum { WRAPPER_HAS_SOW = 1 << 2 };
 
   enum { kWrapperFlagsMask = (WRAPPER_BIT_PRESERVED | WRAPPER_IS_DOM_BINDING |
                               WRAPPER_HAS_SOW) };
 
-  JSObject* mWrapper;
-  uint32_t  mFlags;
+  JS::Heap<JSObject*> mWrapper;
+  uint32_t            mFlags;
 };
 
 enum { WRAPPER_CACHE_FLAGS_BITS_USED = 3 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsWrapperCache, NS_WRAPPERCACHE_IID)
 
 #define NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY                                   \
   if ( aIID.Equals(NS_GET_IID(nsWrapperCache)) ) {                            \
--- a/dom/base/nsWrapperCacheInlines.h
+++ b/dom/base/nsWrapperCacheInlines.h
@@ -45,12 +45,12 @@ nsWrapperCache::IsBlackAndDoesNotNeedTra
     return !hasGrayObjects;
   }
   return false;
 }
 
 inline void
 nsWrapperCache::TraceWrapperJSObject(JSTracer* aTrc, const char* aName)
 {
-  JS_CallObjectTracer(aTrc, &mWrapper, aName);
+  JS_CallHeapObjectTracer(aTrc, &mWrapper, aName);
 }
 
 #endif /* nsWrapperCache_h___ */
--- a/dom/bindings/CallbackObject.h
+++ b/dom/bindings/CallbackObject.h
@@ -71,17 +71,17 @@ public:
    * This should only be called if you are certain that the return value won't
    * be passed into a JS API function and that it won't be stored without being
    * rooted (or otherwise signaling the stored value to the CC).
    *
    * This can return a handle because we trace our mCallback.
    */
   JS::Handle<JSObject*> CallbackPreserveColor() const
   {
-    return JS::Handle<JSObject*>::fromMarkedLocation(&mCallback);
+    return mCallback;
   }
 
   enum ExceptionHandling {
     eReportExceptions,
     eRethrowExceptions
   };
 
 protected:
@@ -106,17 +106,17 @@ protected:
   {
     if (mCallback) {
       mCallback = nullptr;
       NS_DROP_JS_OBJECTS(this, CallbackObject);
       nsLayoutStatics::Release();
     }
   }
 
-  JSObject* mCallback;
+  JS::Heap<JSObject*> mCallback;
 
   class MOZ_STACK_CLASS CallSetup
   {
     /**
      * A class that performs whatever setup we need to safely make a
      * call while this class is on the stack, After the constructor
      * returns, the call is safe to make if GetContext() returns
      * non-null.
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -257,39 +257,43 @@ BluetoothAdapter::SetPropertyByValue(con
     mClass = value.get_uint32_t();
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     NS_ENSURE_SUCCESS_VOID(rv);
 
     AutoPushJSContext cx(sc->GetNativeContext());
-    if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &mJsUuids))) {
+    JS::Rooted<JSObject*> uuids(cx);
+    if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) {
       NS_WARNING("Cannot set JS UUIDs object!");
       return;
     }
+    mJsUuids = uuids;
     Root();
   } else if (name.EqualsLiteral("Devices")) {
     mDeviceAddresses = value.get_ArrayOfnsString();
 
     uint32_t length = mDeviceAddresses.Length();
     for (int i = 0; i < length; i++) {
       mDeviceAddresses[i] = GetAddressFromObjectPath(mDeviceAddresses[i]);
     }
 
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     NS_ENSURE_SUCCESS_VOID(rv);
 
     AutoPushJSContext cx(sc->GetNativeContext());
+    JS::Rooted<JSObject*> deviceAddresses(cx);
     if (NS_FAILED(nsTArrayToJSArray(cx, mDeviceAddresses,
-                                    &mJsDeviceAddresses))) {
+                                    deviceAddresses.address()))) {
       NS_WARNING("Cannot set JS Devices object!");
       return;
     }
+    mJsDeviceAddresses = deviceAddresses;
     Root();
   } else {
 #ifdef DEBUG
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling adapter property: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(name));
     NS_WARNING(warningMsg.get());
 #endif
--- a/dom/bluetooth/BluetoothAdapter.h
+++ b/dom/bluetooth/BluetoothAdapter.h
@@ -67,16 +67,16 @@ private:
   bool mDiscovering;
   bool mPairable;
   bool mPowered;
   uint32_t mPairableTimeout;
   uint32_t mDiscoverableTimeout;
   uint32_t mClass;
   nsTArray<nsString> mDeviceAddresses;
   nsTArray<nsString> mUuids;
-  JSObject* mJsUuids;
-  JSObject* mJsDeviceAddresses;
+  JS::Heap<JSObject*> mJsUuids;
+  JS::Heap<JSObject*> mJsDeviceAddresses;
   bool mIsRooted;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -119,33 +119,37 @@ BluetoothDevice::SetPropertyByValue(cons
   } else if (name.EqualsLiteral("UUIDs")) {
     mUuids = value.get_ArrayOfnsString();
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     NS_ENSURE_SUCCESS_VOID(rv);
 
     AutoPushJSContext cx(sc->GetNativeContext());
 
-    if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, &mJsUuids))) {
+    JS::Rooted<JSObject*> uuids(cx);
+    if (NS_FAILED(nsTArrayToJSArray(cx, mUuids, uuids.address()))) {
       NS_WARNING("Cannot set JS UUIDs object!");
       return;
     }
+    mJsUuids = uuids;
     Root();
   } else if (name.EqualsLiteral("Services")) {
     mServices = value.get_ArrayOfnsString();
     nsresult rv;
     nsIScriptContext* sc = GetContextForEventHandlers(&rv);
     NS_ENSURE_SUCCESS_VOID(rv);
 
     AutoPushJSContext cx(sc->GetNativeContext());
 
-    if (NS_FAILED(nsTArrayToJSArray(cx, mServices, &mJsServices))) {
+    JS::Rooted<JSObject*> services(cx);
+    if (NS_FAILED(nsTArrayToJSArray(cx, mServices, services.address()))) {
       NS_WARNING("Cannot set JS Services object!");
       return;
     }
+    mJsServices = services;
     Root();
   } else {
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling device property: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(name));
     NS_WARNING(warningMsg.get());
   }
 }
--- a/dom/bluetooth/BluetoothDevice.h
+++ b/dom/bluetooth/BluetoothDevice.h
@@ -53,18 +53,18 @@ public:
 
   void Unroot();
 private:
   BluetoothDevice(nsPIDOMWindow* aOwner, const nsAString& aAdapterPath,
                   const BluetoothValue& aValue);
   ~BluetoothDevice();
   void Root();
 
-  JSObject* mJsUuids;
-  JSObject* mJsServices;
+  JS::Heap<JSObject*> mJsUuids;
+  JS::Heap<JSObject*> mJsServices;
 
   nsString mAdapterPath;
   nsString mAddress;
   nsString mName;
   nsString mIcon;
   uint32_t mClass;
   bool mConnected;
   bool mPaired;
--- a/dom/future/Future.h
+++ b/dom/future/Future.h
@@ -102,17 +102,17 @@ private:
 
   nsRefPtr<nsPIDOMWindow> mWindow;
 
   nsRefPtr<FutureResolver> mResolver;
 
   nsTArray<nsRefPtr<FutureCallback> > mResolveCallbacks;
   nsTArray<nsRefPtr<FutureCallback> > mRejectCallbacks;
 
-  JS::Value mResult;
+  JS::Heap<JS::Value> mResult;
   FutureState mState;
   bool mTaskPending;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_Future_h
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -539,17 +539,17 @@ IDBCursor::GetKey(JSContext* aCx,
   }
 
   if (!mHaveCachedKey) {
     if (!mRooted) {
       NS_HOLD_JS_OBJECTS(this, IDBCursor);
       mRooted = true;
     }
 
-    nsresult rv = mKey.ToJSVal(aCx, &mCachedKey);
+    nsresult rv = mKey.ToJSVal(aCx, mCachedKey);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedKey = true;
   }
 
   *aKey = mCachedKey;
   return NS_OK;
 }
@@ -573,17 +573,17 @@ IDBCursor::GetPrimaryKey(JSContext* aCx,
 
     JSAutoRequest ar(aCx);
 
     NS_ASSERTION(mType == OBJECTSTORE ? !mKey.IsUnset() :
                                         !mObjectKey.IsUnset(), "Bad key!");
 
     const Key& key = mType == OBJECTSTORE ? mKey : mObjectKey;
 
-    nsresult rv = key.ToJSVal(aCx, &mCachedPrimaryKey);
+    nsresult rv = key.ToJSVal(aCx, mCachedPrimaryKey);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedPrimaryKey = true;
   }
 
   *aValue = mCachedPrimaryKey;
   return NS_OK;
 }
@@ -734,17 +734,17 @@ IDBCursor::Update(const jsval& aValue,
 
     rv = mObjectStore->Put(aValue, JSVAL_VOID, aCx, 0, getter_AddRefs(request));
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
   else {
     JS::Rooted<JS::Value> keyVal(aCx);
-    rv = objectKey.ToJSVal(aCx, keyVal.address());
+    rv = objectKey.ToJSVal(aCx, &keyVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mObjectStore->Put(aValue, keyVal, aCx, 1, getter_AddRefs(request));
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
 
@@ -805,17 +805,17 @@ IDBCursor::Delete(JSContext* aCx,
   }
 
   NS_ASSERTION(mObjectStore, "This cannot be null!");
   NS_ASSERTION(!mKey.IsUnset() , "Bad key!");
 
   Key& objectKey = (mType == OBJECTSTORE) ? mKey : mObjectKey;
 
   JS::Rooted<JS::Value> key(aCx);
-  nsresult rv = objectKey.ToJSVal(aCx, key.address());
+  nsresult rv = objectKey.ToJSVal(aCx, &key);
   NS_ENSURE_SUCCESS(rv, rv);
 
   nsCOMPtr<nsIIDBRequest> request;
   rv = mObjectStore->Delete(key, aCx, getter_AddRefs(request));
   if (NS_FAILED(rv)) {
     return rv;
   }
 
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -163,27 +163,27 @@ protected:
                const nsACString& aContinueQuery,
                const nsACString& aContinueToQuery);
 
   nsRefPtr<IDBRequest> mRequest;
   nsRefPtr<IDBTransaction> mTransaction;
   nsRefPtr<IDBObjectStore> mObjectStore;
   nsRefPtr<IDBIndex> mIndex;
 
-  JSObject* mScriptOwner;
+  JS::Heap<JSObject*> mScriptOwner;
 
   Type mType;
   Direction mDirection;
   nsCString mContinueQuery;
   nsCString mContinueToQuery;
 
   // These are cycle-collected!
-  jsval mCachedKey;
-  jsval mCachedPrimaryKey;
-  jsval mCachedValue;
+  JS::Heap<JS::Value> mCachedKey;
+  JS::Heap<JS::Value> mCachedPrimaryKey;
+  JS::Heap<JS::Value> mCachedValue;
 
   Key mRangeKey;
 
   Key mKey;
   Key mObjectKey;
   StructuredCloneReadInfo mCloneReadInfo;
   Key mContinueToKey;
 
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -178,17 +178,17 @@ private:
   Open(JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
        const Optional<uint64_t>& aVersion, bool aDelete, ErrorResult& aRv);
 
   nsCString mASCIIOrigin;
 
   // If this factory lives on a window then mWindow must be non-null. Otherwise
   // mOwningObject must be non-null.
   nsCOMPtr<nsPIDOMWindow> mWindow;
-  JSObject* mOwningObject;
+  JS::Heap<JSObject*> mOwningObject;
 
   IndexedDBChild* mActorChild;
   IndexedDBParent* mActorParent;
 
   mozilla::dom::ContentParent* mContentParent;
 
   bool mRootedOwningObject;
 };
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -805,17 +805,17 @@ IDBIndex::GetKeyPath(JSContext* aCx,
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!JSVAL_IS_VOID(mCachedKeyPath)) {
     *aVal = mCachedKeyPath;
     return NS_OK;
   }
 
-  nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath);
+  nsresult rv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (JSVAL_IS_GCTHING(mCachedKeyPath)) {
     NS_HOLD_JS_OBJECTS(this, IDBIndex);
     mRooted = true;
   }
 
   *aVal = mCachedKeyPath;
@@ -1175,17 +1175,22 @@ GetKeyHelper::DoDatabaseWork(mozIStorage
 
   return NS_OK;
 }
 
 nsresult
 GetKeyHelper::GetSuccessResult(JSContext* aCx,
                                jsval* aVal)
 {
-  return mKey.ToJSVal(aCx, aVal);
+  JS::Rooted<JS::Value> value(aCx);
+  nsresult rv = mKey.ToJSVal(aCx, &value);
+  if (NS_SUCCEEDED(rv)) {
+    *aVal = value;
+  }
+  return rv;
 }
 
 void
 GetKeyHelper::ReleaseMainThreadObjects()
 {
   mKeyRange = nullptr;
   IndexHelper::ReleaseMainThreadObjects();
 }
@@ -1503,17 +1508,17 @@ GetAllKeysHelper::GetSuccessResult(JSCon
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     for (uint32_t index = 0, count = keys.Length(); index < count; index++) {
       const Key& key = keys[index];
       NS_ASSERTION(!key.IsUnset(), "Bad key!");
 
       JS::Rooted<JS::Value> value(aCx);
-      nsresult rv = key.ToJSVal(aCx, value.address());
+      nsresult rv = key.ToJSVal(aCx, &value);
       if (NS_FAILED(rv)) {
         NS_WARNING("Failed to get jsval for key!");
         return rv;
       }
 
       if (!JS_SetElement(aCx, array, index, value.address())) {
         NS_WARNING("Failed to set array element!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
--- a/dom/indexedDB/IDBIndex.h
+++ b/dom/indexedDB/IDBIndex.h
@@ -152,17 +152,17 @@ private:
   IDBIndex();
   ~IDBIndex();
 
   nsRefPtr<IDBObjectStore> mObjectStore;
 
   int64_t mId;
   nsString mName;
   KeyPath mKeyPath;
-  JS::Value mCachedKeyPath;
+  JS::Heap<JS::Value> mCachedKeyPath;
 
   IndexedDBIndexChild* mActorChild;
   IndexedDBIndexParent* mActorParent;
 
   bool mUnique;
   bool mMultiEntry;
   bool mRooted;
 };
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -358,17 +358,17 @@ IDBKeyRange::GetLower(JSContext* aCx,
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedLowerVal) {
     if (!mRooted) {
       NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
       mRooted = true;
     }
 
-    nsresult rv = Lower().ToJSVal(aCx, &mCachedLowerVal);
+    nsresult rv = Lower().ToJSVal(aCx, mCachedLowerVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedLowerVal = true;
   }
 
   *aLower = mCachedLowerVal;
   return NS_OK;
 }
@@ -380,17 +380,17 @@ IDBKeyRange::GetUpper(JSContext* aCx,
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedUpperVal) {
     if (!mRooted) {
       NS_HOLD_JS_OBJECTS(this, IDBKeyRange);
       mRooted = true;
     }
 
-    nsresult rv = Upper().ToJSVal(aCx, &mCachedUpperVal);
+    nsresult rv = Upper().ToJSVal(aCx, mCachedUpperVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     mHaveCachedUpperVal = true;
   }
 
   *aUpper = mCachedUpperVal;
   return NS_OK;
 }
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -152,18 +152,18 @@ public:
 
   void DropJSObjects();
 
 private:
   ~IDBKeyRange();
 
   Key mLower;
   Key mUpper;
-  jsval mCachedLowerVal;
-  jsval mCachedUpperVal;
+  JS::Heap<JS::Value> mCachedLowerVal;
+  JS::Heap<JS::Value> mCachedUpperVal;
   bool mLowerOpen;
   bool mUpperOpen;
   bool mIsOnly;
   bool mHaveCachedLowerVal;
   bool mHaveCachedUpperVal;
   bool mRooted;
 };
 
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -2382,17 +2382,17 @@ IDBObjectStore::GetKeyPath(JSContext* aC
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!JSVAL_IS_VOID(mCachedKeyPath)) {
     *aVal = mCachedKeyPath;
     return NS_OK;
   }
 
-  nsresult rv = GetKeyPath().ToJSVal(aCx, &mCachedKeyPath);
+  nsresult rv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (JSVAL_IS_GCTHING(mCachedKeyPath)) {
     NS_HOLD_JS_OBJECTS(this, IDBObjectStore);
     mRooted = true;
   }
 
   *aVal = mCachedKeyPath;
@@ -3129,17 +3129,22 @@ AddHelper::DoDatabaseWork(mozIStorageCon
 nsresult
 AddHelper::GetSuccessResult(JSContext* aCx,
                             jsval* aVal)
 {
   NS_ASSERTION(!mKey.IsUnset(), "Badness!");
 
   mCloneWriteInfo.mCloneBuffer.clear();
 
-  return mKey.ToJSVal(aCx, aVal);
+  JS::Rooted<JS::Value> value(aCx);
+  nsresult rv = mKey.ToJSVal(aCx, &value);
+  if (NS_SUCCEEDED(rv)) {
+    *aVal = value;
+  }
+  return rv;
 }
 
 void
 AddHelper::ReleaseMainThreadObjects()
 {
   IDBObjectStore::ClearCloneWriteInfo(mCloneWriteInfo);
   ObjectStoreHelper::ReleaseMainThreadObjects();
 }
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -282,17 +282,17 @@ protected:
                  uint32_t aTag,
                  BlobOrFileData* aRetval);
 private:
   nsRefPtr<IDBTransaction> mTransaction;
 
   int64_t mId;
   nsString mName;
   KeyPath mKeyPath;
-  JS::Value mCachedKeyPath;
+  JS::Heap<JS::Value> mCachedKeyPath;
   bool mRooted;
   bool mAutoIncrement;
   nsCOMPtr<nsIAtom> mDatabaseId;
   nsRefPtr<ObjectStoreInfo> mInfo;
 
   nsTArray<nsRefPtr<IDBIndex> > mCreatedIndexes;
 
   IndexedDBObjectStoreChild* mActorChild;
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -115,23 +115,25 @@ IDBRequest::NotifyHelperCompleted(Helper
   }
 
   JS::Rooted<JSObject*> global(cx, GetParentObject());
   NS_ASSERTION(global, "This should never be null!");
 
   JSAutoCompartment ac(cx, global);
   AssertIsRooted();
 
-  rv = aHelper->GetSuccessResult(cx, &mResultVal);
+  JS::Rooted<JS::Value> value(cx);
+  rv = aHelper->GetSuccessResult(cx, value.address());
   if (NS_FAILED(rv)) {
     NS_WARNING("GetSuccessResult failed!");
   }
 
   if (NS_SUCCEEDED(rv)) {
     mError = nullptr;
+    mResultVal = value;
   }
   else {
     SetError(rv);
     mResultVal = JSVAL_VOID;
   }
 
   return rv;
 }
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -104,17 +104,17 @@ public:
 
 protected:
   IDBRequest();
   ~IDBRequest();
 
   nsCOMPtr<nsISupports> mSource;
   nsRefPtr<IDBTransaction> mTransaction;
 
-  jsval mResultVal;
+  JS::Heap<JS::Value> mResultVal;
   nsRefPtr<mozilla::dom::DOMError> mError;
   IndexedDBRequestParentBase* mActorParent;
   nsString mFilename;
 #ifdef MOZ_ENABLE_PROFILER_SPS
   uint64_t mSerialNumber;
 #endif
   nsresult mErrorCode;
   uint32_t mLineNo;
--- a/dom/indexedDB/IDBWrapperCache.h
+++ b/dom/indexedDB/IDBWrapperCache.h
@@ -57,14 +57,14 @@ public:
 protected:
   IDBWrapperCache()
   : mScriptOwner(nullptr)
   { }
 
   virtual ~IDBWrapperCache();
 
 private:
-  JSObject* mScriptOwner;
+  JS::Heap<JSObject*> mScriptOwner;
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif // mozilla_dom_indexeddb_idbwrappercache_h__
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -180,17 +180,17 @@ Key::EncodeJSValInternal(JSContext* aCx,
   }
 
   return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
 }
 
 // static
 nsresult
 Key::DecodeJSValInternal(const unsigned char*& aPos, const unsigned char* aEnd,
-                         JSContext* aCx, uint8_t aTypeOffset, jsval* aVal,
+                         JSContext* aCx, uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal,
                          uint16_t aRecursionDepth)
 {
   NS_ENSURE_TRUE(aRecursionDepth < MaxRecursionDepth, NS_ERROR_DOM_INDEXEDDB_DATA_ERR);
 
   if (*aPos - aTypeOffset >= eArray) {
     JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0, nullptr));
     if (!array) {
       NS_WARNING("Failed to make array!");
@@ -203,52 +203,52 @@ Key::DecodeJSValInternal(const unsigned 
       ++aPos;
       aTypeOffset = 0;
     }
 
     uint32_t index = 0;
     while (aPos < aEnd && *aPos - aTypeOffset != eTerminator) {
       JS::Rooted<JS::Value> val(aCx);
       nsresult rv = DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset,
-                                        val.address(), aRecursionDepth + 1);
+                                        &val, aRecursionDepth + 1);
       NS_ENSURE_SUCCESS(rv, rv);
 
       aTypeOffset = 0;
 
       if (!JS_SetElement(aCx, array, index++, val.address())) {
         NS_WARNING("Failed to set array element!");
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
     }
 
     NS_ASSERTION(aPos >= aEnd || (*aPos % eMaxType) == eTerminator,
                  "Should have found end-of-array marker");
     ++aPos;
 
-    *aVal = OBJECT_TO_JSVAL(array);
+    aVal.setObject(*array);
   }
   else if (*aPos - aTypeOffset == eString) {
     nsString key;
     DecodeString(aPos, aEnd, key);
-    if (!xpc::StringToJsval(aCx, key, aVal)) {
+    if (!xpc::StringToJsval(aCx, key, aVal.address())) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
   }
   else if (*aPos - aTypeOffset == eDate) {
     double msec = static_cast<double>(DecodeNumber(aPos, aEnd));
     JSObject* date = JS_NewDateObjectMsec(aCx, msec);
     if (!date) {
       NS_WARNING("Failed to make date!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
-    *aVal = OBJECT_TO_JSVAL(date);
+    aVal.setObject(*date);
   }
   else if (*aPos - aTypeOffset == eFloat) {
-    *aVal = DOUBLE_TO_JSVAL(DecodeNumber(aPos, aEnd));
+    aVal.setDouble(DecodeNumber(aPos, aEnd));
   }
   else {
     NS_NOTREACHED("Unknown key type!");
   }
 
   return NS_OK;
 }
 
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -174,33 +174,44 @@ public:
       return rv;
     }
     TrimBuffer();
 
     return NS_OK;
   }
 
   nsresult ToJSVal(JSContext* aCx,
-                   jsval* aVal) const
+                   JS::MutableHandle<JS::Value> aVal) const
   {
     if (IsUnset()) {
-      *aVal = JSVAL_VOID;
+      aVal.set(JSVAL_VOID);
       return NS_OK;
     }
 
     const unsigned char* pos = BufferStart();
     nsresult rv = DecodeJSVal(pos, BufferEnd(), aCx, 0, aVal);
     NS_ENSURE_SUCCESS(rv, rv);
 
     NS_ASSERTION(pos >= BufferEnd(),
                  "Didn't consume whole buffer");
 
     return NS_OK;
   }
 
+  nsresult ToJSVal(JSContext* aCx,
+                   JS::Heap<JS::Value>& aVal) const
+  {
+    JS::Rooted<JS::Value> value(aCx);
+    nsresult rv = ToJSVal(aCx, &value);
+    if (NS_SUCCEEDED(rv)) {
+      aVal = value;
+    }
+    return rv;
+  }
+
   nsresult AppendItem(JSContext* aCx,
                       bool aFirstOfArray,
                       const jsval aVal)
   {
     nsresult rv = EncodeJSVal(aCx, aVal, aFirstOfArray ? eMaxType : 0);
     if (NS_FAILED(rv)) {
       Unset();
       return rv;
@@ -299,17 +310,17 @@ private:
   }
   void EncodeString(const nsAString& aString, uint8_t aTypeOffset);
   void EncodeNumber(double aFloat, uint8_t aType);
 
   // Decoding functions. aPos points into mBuffer and is adjusted to point
   // past the consumed value.
   static inline nsresult DecodeJSVal(const unsigned char*& aPos,
                                      const unsigned char* aEnd, JSContext* aCx,
-                                     uint8_t aTypeOffset, jsval* aVal)
+                                     uint8_t aTypeOffset, JS::MutableHandle<JS::Value> aVal)
   {
     return DecodeJSValInternal(aPos, aEnd, aCx, aTypeOffset, aVal, 0);
   }
 
   static void DecodeString(const unsigned char*& aPos,
                            const unsigned char* aEnd,
                            nsString& aString);
   static double DecodeNumber(const unsigned char*& aPos,
@@ -319,14 +330,14 @@ private:
 
 private:
   nsresult EncodeJSValInternal(JSContext* aCx, const jsval aVal,
                                uint8_t aTypeOffset, uint16_t aRecursionDepth);
 
   static nsresult DecodeJSValInternal(const unsigned char*& aPos,
                                       const unsigned char* aEnd,
                                       JSContext* aCx, uint8_t aTypeOffset,
-                                      jsval* aVal, uint16_t aRecursionDepth);
+                                      JS::MutableHandle<JS::Value> aVal, uint16_t aRecursionDepth);
 };
 
 END_INDEXEDDB_NAMESPACE
 
 #endif /* mozilla_dom_indexeddb_key_h__ */
--- a/dom/indexedDB/KeyPath.cpp
+++ b/dom/indexedDB/KeyPath.cpp
@@ -452,17 +452,17 @@ KeyPath::DeserializeFromString(const nsA
 
   keyPath.SetType(STRING);
   keyPath.mStrings.AppendElement(aString);
 
   return keyPath;
 }
 
 nsresult
-KeyPath::ToJSVal(JSContext* aCx, JS::Value* aValue) const
+KeyPath::ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const
 {
   if (IsArray()) {
     uint32_t len = mStrings.Length();
     JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, len, nullptr));
     if (!array) {
       NS_WARNING("Failed to make array!");
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
@@ -474,32 +474,43 @@ KeyPath::ToJSVal(JSContext* aCx, JS::Val
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
 
       if (!JS_SetElement(aCx, array, i, val.address())) {
         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
       }
     }
 
-    *aValue = OBJECT_TO_JSVAL(array);
+    aValue.setObject(*array);
     return NS_OK;
   }
 
   if (IsString()) {
     nsString tmp(mStrings[0]);
-    if (!xpc::StringToJsval(aCx, tmp, aValue)) {
+    if (!xpc::StringToJsval(aCx, tmp, aValue.address())) {
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
     return NS_OK;
   }
 
-  *aValue = JSVAL_NULL;
+  aValue.setNull();
   return NS_OK;
 }
 
+nsresult
+KeyPath::ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const
+{
+  JS::Rooted<JS::Value> value(aCx);
+  nsresult rv = ToJSVal(aCx, &value);
+  if (NS_SUCCEEDED(rv)) {
+    aValue = value;
+  }
+  return rv;
+}
+
 bool
 KeyPath::IsAllowedForObjectStore(bool aAutoIncrement) const
 {
   // Any keypath that passed validation is allowed for non-autoIncrement
   // objectStores.
   if (!aAutoIncrement) {
     return true;
   }
--- a/dom/indexedDB/KeyPath.h
+++ b/dom/indexedDB/KeyPath.h
@@ -82,17 +82,18 @@ public:
   bool operator==(const KeyPath& aOther) const
   {
     return mType == aOther.mType && mStrings == aOther.mStrings;
   }
 
   void SerializeToString(nsAString& aString) const;
   static KeyPath DeserializeFromString(const nsAString& aString);
 
-  nsresult ToJSVal(JSContext* aCx, JS::Value* aValue) const;
+  nsresult ToJSVal(JSContext* aCx, JS::MutableHandle<JS::Value> aValue) const;
+  nsresult ToJSVal(JSContext* aCx, JS::Heap<JS::Value>& aValue) const;
 
   bool IsAllowedForObjectStore(bool aAutoIncrement) const;
 
   KeyPathType mType;
 
   nsTArray<nsString> mStrings;
 };
 
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -1159,22 +1159,17 @@ public:
   NS_METHOD GetDescription(nsAString& aDescription)
   {
     CopyUTF8toUTF16(mPluginTag.mDescription, aDescription);
     return NS_OK;
   }
 
   NS_METHOD GetFilename(nsAString& aFilename)
   {
-    if (Preferences::GetBool("plugin.expose_full_path", false)) {
-      CopyUTF8toUTF16(mPluginTag.mFullPath, aFilename);
-    } else {
-      CopyUTF8toUTF16(mPluginTag.mFileName, aFilename);
-    }
-
+    CopyUTF8toUTF16(mPluginTag.mFileName, aFilename);
     return NS_OK;
   }
 
   NS_METHOD GetVersion(nsAString& aVersion)
   {
     CopyUTF8toUTF16(mPluginTag.mVersion, aVersion);
     return NS_OK;
   }
--- a/dom/telephony/Telephony.h
+++ b/dom/telephony/Telephony.h
@@ -36,17 +36,17 @@ class Telephony : public nsDOMEventTarge
   nsCOMPtr<nsITelephonyProvider> mProvider;
   nsRefPtr<Listener> mListener;
 
   TelephonyCall* mActiveCall;
   nsTArray<nsRefPtr<TelephonyCall> > mCalls;
 
   // Cached calls array object. Cleared whenever mCalls changes and then rebuilt
   // once a page looks for the liveCalls attribute.
-  JSObject* mCallsArray;
+  JS::Heap<JSObject*> mCallsArray;
 
   bool mRooted;
   bool mEnumerated;
 
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIDOMTELEPHONY
   NS_DECL_NSITELEPHONYLISTENER
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -461,16 +461,31 @@ public:
    */
   virtual void ClearCachedResources(Layer* aSubtree = nullptr) {}
 
   /**
    * Flag the next paint as the first for a document.
    */
   virtual void SetIsFirstPaint() {}
 
+  /**
+   * Make sure that the previous transaction has been entirely
+   * completed.
+   *
+   * Note: This may sychronously wait on a remote compositor
+   * to complete rendering.
+   */
+  virtual void FlushRendering() { }
+
+  /**
+   * Checks if we need to invalidate the OS widget to trigger
+   * painting when updating this layer manager.
+   */
+  virtual bool NeedsWidgetInvalidation() { return true; }
+
   // We always declare the following logging symbols, because it's
   // extremely tricky to conditionally declare them.  However, for
   // ifndef MOZ_LAYERS_HAVE_LOG builds, they only have trivial
   // definitions in Layers.cpp.
   virtual const char* Name() const { return "???"; }
 
   /**
    * Dump information about this layer manager and its managed tree to
--- a/gfx/layers/client/ClientLayerManager.cpp
+++ b/gfx/layers/client/ClientLayerManager.cpp
@@ -256,16 +256,26 @@ ClientLayerManager::MakeSnapshotIfRequir
         ShadowLayerForwarder::DestroySharedSurface(&snapshot);
       }
     }
   }
   mShadowTarget = nullptr;
 }
 
 void
+ClientLayerManager::FlushRendering()
+{
+  if (mWidget) {
+    if (CompositorChild* remoteRenderer = mWidget->GetRemoteRenderer()) {
+      remoteRenderer->SendFlushRendering();
+    }
+  }
+}
+
+void
 ClientLayerManager::ForwardTransaction()
 {
   mPhase = PHASE_FORWARD;
 
   // forward this transaction's changeset to our LayerManagerComposite
   AutoInfallibleTArray<EditReply, 10> replies;
   if (HasShadowManager() && ShadowLayerForwarder::EndTransaction(&replies)) {
     for (nsTArray<EditReply>::size_type i = 0; i < replies.Length(); ++i) {
--- a/gfx/layers/client/ClientLayerManager.h
+++ b/gfx/layers/client/ClientLayerManager.h
@@ -48,16 +48,20 @@ public:
 
   virtual already_AddRefed<ThebesLayer> CreateThebesLayer();
   virtual already_AddRefed<ContainerLayer> CreateContainerLayer();
   virtual already_AddRefed<ImageLayer> CreateImageLayer();
   virtual already_AddRefed<CanvasLayer> CreateCanvasLayer();
   virtual already_AddRefed<ColorLayer> CreateColorLayer();
   virtual already_AddRefed<RefLayer> CreateRefLayer();
 
+  virtual void FlushRendering() MOZ_OVERRIDE;
+
+  virtual bool NeedsWidgetInvalidation() MOZ_OVERRIDE { return Compositor::GetBackend() == LAYERS_BASIC; }
+
   ShadowableLayer* Hold(Layer* aLayer);
 
   bool HasShadowManager() const { return ShadowLayerForwarder::HasShadowManager(); }
 
   virtual bool IsCompositingCheap();
   virtual bool HasShadowManagerInternal() const { return HasShadowManager(); }
 
   virtual void SetIsFirstPaint() MOZ_OVERRIDE;
--- a/gfx/layers/ipc/CompositorParent.cpp
+++ b/gfx/layers/ipc/CompositorParent.cpp
@@ -244,16 +244,28 @@ CompositorParent::RecvMakeSnapshot(const
 {
   AutoOpenSurface opener(OPEN_READ_WRITE, aInSnapshot);
   nsRefPtr<gfxContext> target = new gfxContext(opener.Get());
   ComposeToTarget(target);
   *aOutSnapshot = aInSnapshot;
   return true;
 }
 
+bool
+CompositorParent::RecvFlushRendering()
+{
+  // If we're waiting to do a composite, then cancel it
+  // and do it immediately instead.
+  if (mCurrentCompositeTask) {
+    mCurrentCompositeTask->Cancel();
+    ComposeToTarget(nullptr);
+  }
+  return true;
+}
+
 void
 CompositorParent::ActorDestroy(ActorDestroyReason why)
 {
   mPaused = true;
   RemoveCompositor(mCompositorID);
 
   if (mLayerManager) {
     mLayerManager->Destroy();
@@ -738,16 +750,17 @@ public:
   // FIXME/bug 774388: work out what shutdown protocol we need.
   virtual bool RecvWillStop() MOZ_OVERRIDE { return true; }
   virtual bool RecvStop() MOZ_OVERRIDE { return true; }
   virtual bool RecvPause() MOZ_OVERRIDE { return true; }
   virtual bool RecvResume() MOZ_OVERRIDE { return true; }
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 SurfaceDescriptor* aOutSnapshot)
   { return true; }
+  virtual bool RecvFlushRendering() MOZ_OVERRIDE { return true; }
 
   virtual PLayerTransactionParent*
     AllocPLayerTransaction(const LayersBackend& aBackendType,
                            const uint64_t& aId,
                            TextureFactoryIdentifier* aTextureFactoryIdentifier) MOZ_OVERRIDE;
 
   virtual bool DeallocPLayerTransaction(PLayerTransactionParent* aLayers) MOZ_OVERRIDE;
 
--- a/gfx/layers/ipc/CompositorParent.h
+++ b/gfx/layers/ipc/CompositorParent.h
@@ -50,16 +50,17 @@ public:
   virtual ~CompositorParent();
 
   virtual bool RecvWillStop() MOZ_OVERRIDE;
   virtual bool RecvStop() MOZ_OVERRIDE;
   virtual bool RecvPause() MOZ_OVERRIDE;
   virtual bool RecvResume() MOZ_OVERRIDE;
   virtual bool RecvMakeSnapshot(const SurfaceDescriptor& aInSnapshot,
                                 SurfaceDescriptor* aOutSnapshot);
+  virtual bool RecvFlushRendering() MOZ_OVERRIDE;
 
   virtual void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;
 
   virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree,
                                    const TargetConfig& aTargetConfig,
                                    bool isFirstPaint) MOZ_OVERRIDE;
   /**
    * This forces the is-first-paint flag to true. This is intended to
--- a/gfx/layers/ipc/PCompositor.ipdl
+++ b/gfx/layers/ipc/PCompositor.ipdl
@@ -48,14 +48,18 @@ parent:
   // or format of |inSnapshot| doesn't match our render target,
   // results are undefined.
   //
   // NB: this message will result in animations, transforms, effects,
   // and so forth being interpolated.  That's what we want to happen.
   sync MakeSnapshot(SurfaceDescriptor inSnapshot)
     returns (SurfaceDescriptor outSnapshot);
 
+  // Make sure any pending composites are started immediately and
+  // block until they are completed.
+  sync FlushRendering();
+
   sync PLayerTransaction(LayersBackend layersBackendHint, uint64_t id)
     returns (TextureFactoryIdentifier textureFactoryIdentifier);
 };
 
 } // layers
 } // mozilla
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -176,28 +176,28 @@ struct JS_PUBLIC_API(NullPtr)
  */
 template <typename T>
 class Heap : public js::HeapBase<T>
 {
   public:
     Heap() {
         MOZ_STATIC_ASSERT(sizeof(T) == sizeof(Heap<T>),
                           "Heap<T> must be binary compatible with T.");
-        set(js::RootMethods<T>::initial());
+        init(js::RootMethods<T>::initial());
     }
-    explicit Heap(T p) { set(p); }
-    explicit Heap(const Heap<T> &p) { set(p.ptr); }
+    explicit Heap(T p) { init(p); }
+    explicit Heap(const Heap<T> &p) { init(p.ptr); }
 
     ~Heap() {
         if (js::RootMethods<T>::needsPostBarrier(ptr))
             relocate();
     }
 
-    bool operator!=(const T &other) { return *ptr != other; }
-    bool operator==(const T &other) { return *ptr == other; }
+    bool operator!=(const T &other) const { return ptr != other; }
+    bool operator==(const T &other) const { return ptr == other; }
 
     operator T() const { return ptr; }
     T operator->() const { return ptr; }
     const T *address() const { return &ptr; }
     const T &get() const { return ptr; }
 
     T *unsafeGet() { return &ptr; }
 
@@ -215,16 +215,23 @@ class Heap : public js::HeapBase<T>
             relocate();  /* Called before overwriting ptr. */
             ptr = newPtr;
         } else {
             ptr = newPtr;
         }
     }
 
   private:
+    void init(T newPtr) {
+        JS_ASSERT(!js::RootMethods<T>::poisoned(newPtr));
+        ptr = newPtr;
+        if (js::RootMethods<T>::needsPostBarrier(ptr))
+            post();
+    }
+
     void post() {
 #ifdef JSGC_GENERATIONAL
         JS_ASSERT(js::RootMethods<T>::needsPostBarrier(ptr));
         js::RootMethods<T>::postBarrier(&ptr);
 #endif
     }
 
     void relocate() {
@@ -315,18 +322,18 @@ class MOZ_NONHEAP_CLASS Handle : public 
 
     /*
      * Return a reference so passing a Handle<T> to something that
      * takes a |const T&| is not a GC hazard.
      */
     operator const T&() const { return get(); }
     T operator->() const { return get(); }
 
-    bool operator!=(const T &other) { return *ptr != other; }
-    bool operator==(const T &other) { return *ptr == other; }
+    bool operator!=(const T &other) const { return *ptr != other; }
+    bool operator==(const T &other) const { return *ptr == other; }
 
   private:
     Handle() {}
 
     const T *ptr;
 
     template <typename S>
     void operator=(S v) MOZ_DELETE;
@@ -600,18 +607,18 @@ class MOZ_STACK_CLASS Rooted : public js
         return ptr;
     }
 
     void set(T value) {
         JS_ASSERT(!js::RootMethods<T>::poisoned(value));
         ptr = value;
     }
 
-    bool operator!=(const T &other) { return ptr != other; }
-    bool operator==(const T &other) { return ptr == other; }
+    bool operator!=(const T &other) const { return ptr != other; }
+    bool operator==(const T &other) const { return ptr == other; }
 
   private:
     void commonInit(Rooted<void*> **thingGCRooters) {
 #if defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING)
         js::ThingRootKind kind = js::RootMethods<T>::kind();
         this->stack = &thingGCRooters[kind];
         this->prev = *stack;
         *stack = reinterpret_cast<Rooted<void*>*>(this);
@@ -761,18 +768,18 @@ class FakeRooted : public RootedBase<T>
     const T &get() const { return ptr; }
 
     T &operator=(T value) {
         JS_ASSERT(!RootMethods<T>::poisoned(value));
         ptr = value;
         return ptr;
     }
 
-    bool operator!=(const T &other) { return ptr != other; }
-    bool operator==(const T &other) { return ptr == other; }
+    bool operator!=(const T &other) const { return ptr != other; }
+    bool operator==(const T &other) const { return ptr == other; }
 
   private:
     T ptr;
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     FakeRooted(const FakeRooted &) MOZ_DELETE;
 };
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1415,28 +1415,31 @@ template <> struct RootMethods<JS::Value
     static bool poisoned(const JS::Value &v) { return JS::IsPoisonedValue(v); }
     static bool needsPostBarrier(const JS::Value &v) { return v.isMarkable(); }
 #ifdef JSGC_GENERATIONAL
     static void postBarrier(JS::Value *v) { JS::HeapValuePostBarrier(v); }
     static void relocate(JS::Value *v) { JS::HeapValueRelocate(v); }
 #endif
 };
 
+template <class Outer> class UnbarrieredMutableValueOperations;
 template <class Outer> class MutableValueOperations;
 
 /*
  * A class designed for CRTP use in implementing the non-mutating parts of the
  * Value interface in Value-like classes.  Outer must be a class inheriting
  * ValueOperations<Outer> with a visible extract() method returning the
  * const Value* abstracted by Outer.
  */
 template <class Outer>
 class ValueOperations
 {
+    friend class UnbarrieredMutableValueOperations<Outer>;
     friend class MutableValueOperations<Outer>;
+
     const JS::Value * value() const { return static_cast<const Outer*>(this)->extract(); }
 
   public:
     bool isUndefined() const { return value()->isUndefined(); }
     bool isNull() const { return value()->isNull(); }
     bool isBoolean() const { return value()->isBoolean(); }
     bool isTrue() const { return value()->isTrue(); }
     bool isFalse() const { return value()->isFalse(); }
@@ -1464,51 +1467,92 @@ class ValueOperations
     void *toGCThing() const { return value()->toGCThing(); }
 
     JSValueType extractNonDoubleType() const { return value()->extractNonDoubleType(); }
 
     JSWhyMagic whyMagic() const { return value()->whyMagic(); }
 };
 
 /*
- * A class designed for CRTP use in implementing the mutating parts of the
- * Value interface in Value-like classes.  Outer must be a class inheriting
- * MutableValueOperations<Outer> with visible extractMutable() and extract()
- * methods returning the const Value* and Value* abstracted by Outer.
+ * A class designed for CRTP use in implementing the mutating parts of the Value
+ * interface in Value-like classes that don't need post barriers.  Outer must be
+ * a class inheriting UnbarrieredMutableValueOperations<Outer> with visible
+ * extractMutable() and extract() methods returning the const Value* and Value*
+ * abstracted by Outer.
  */
 template <class Outer>
-class MutableValueOperations : public ValueOperations<Outer>
+class UnbarrieredMutableValueOperations : public ValueOperations<Outer>
 {
+    friend class MutableValueOperations<Outer>;
     JS::Value * value() { return static_cast<Outer*>(this)->extractMutable(); }
 
   public:
     void setNull() { value()->setNull(); }
     void setUndefined() { value()->setUndefined(); }
     void setInt32(int32_t i) { value()->setInt32(i); }
     void setDouble(double d) { value()->setDouble(d); }
-    void setString(JSString *str) { value()->setString(str); }
-    void setString(const JS::Anchor<JSString *> &str) { value()->setString(str); }
-    void setObject(JSObject &obj) { value()->setObject(obj); }
     void setBoolean(bool b) { value()->setBoolean(b); }
     void setMagic(JSWhyMagic why) { value()->setMagic(why); }
     bool setNumber(uint32_t ui) { return value()->setNumber(ui); }
     bool setNumber(double d) { return value()->setNumber(d); }
-    void setObjectOrNull(JSObject *arg) { value()->setObjectOrNull(arg); }
+};
+
+/*
+ * A class designed for CRTP use in implementing all the mutating parts of the
+ * Value interface in Value-like classes.  Outer must be a class inheriting
+ * MutableValueOperations<Outer> with visible extractMutable() and extract()
+ * methods returning the const Value* and Value* abstracted by Outer.
+ */
+template <class Outer>
+class MutableValueOperations : public UnbarrieredMutableValueOperations<Outer>
+{
+  public:
+    void setString(JSString *str) { this->value()->setString(str); }
+    void setString(const JS::Anchor<JSString *> &str) { this->value()->setString(str); }
+    void setObject(JSObject &obj) { this->value()->setObject(obj); }
+    void setObjectOrNull(JSObject *arg) { this->value()->setObjectOrNull(arg); }
 };
 
 /*
- * Augment the generic Heap<T> interface when T = Value with type-querying
- * and value-extracting operations.
+ * Augment the generic Heap<T> interface when T = Value with
+ * type-querying, value-extracting, and mutating operations.
  */
 template <>
-class HeapBase<JS::Value> : public ValueOperations<JS::Heap<JS::Value> >
+class HeapBase<JS::Value> : public UnbarrieredMutableValueOperations<JS::Heap<JS::Value> >
 {
-    friend class ValueOperations<JS::Heap<JS::Value> >;
-    const JS::Value * extract() const {
-        return static_cast<const JS::Heap<JS::Value>*>(this)->address();
+    typedef JS::Heap<JS::Value> Outer;
+
+    friend class ValueOperations<Outer>;
+    friend class UnbarrieredMutableValueOperations<Outer>;
+
+    const JS::Value * extract() const { return static_cast<const Outer*>(this)->address(); }
+    JS::Value * extractMutable() { return static_cast<Outer*>(this)->unsafeGet(); }
+
+    /*
+     * Setters that potentially change the value to a GC thing from a non-GC
+     * thing must call JS::Heap::set() to trigger the post barrier.
+     *
+     * Changing from a GC thing to a non-GC thing value will leave the heap
+     * value in the store buffer, but it will be ingored so this is not a
+     * problem.
+     */
+    void setBarriered(const JS::Value &v) {
+        static_cast<JS::Heap<JS::Value> *>(this)->set(v);
+    }
+
+  public:
+    void setString(JSString *str) { setBarriered(JS::StringValue(str)); }
+    void setString(const JS::Anchor<JSString *> &str) { setBarriered(JS::StringValue(str.get())); }
+    void setObject(JSObject &obj) { setBarriered(JS::ObjectValue(obj)); }
+
+    void setObjectOrNull(JSObject *arg) {
+        if (arg)
+            setObject(*arg);
+        else
+            setNull();
     }
 };
 
 /*
  * Augment the generic Handle<T> interface when T = Value with type-querying
  * and value-extracting operations.
  */
 template <>
@@ -1527,16 +1571,17 @@ class HandleBase<JS::Value> : public Val
 template <>
 class MutableHandleBase<JS::Value> : public MutableValueOperations<JS::MutableHandle<JS::Value> >
 {
     friend class ValueOperations<JS::MutableHandle<JS::Value> >;
     const JS::Value * extract() const {
         return static_cast<const JS::MutableHandle<JS::Value>*>(this)->address();
     }
 
+    friend class UnbarrieredMutableValueOperations<JS::MutableHandle<JS::Value> >;
     friend class MutableValueOperations<JS::MutableHandle<JS::Value> >;
     JS::Value * extractMutable() {
         return static_cast<JS::MutableHandle<JS::Value>*>(this)->address();
     }
 };
 
 /*
  * Augment the generic Rooted<T> interface when T = Value with type-querying,
@@ -1545,16 +1590,17 @@ class MutableHandleBase<JS::Value> : pub
 template <>
 class RootedBase<JS::Value> : public MutableValueOperations<JS::Rooted<JS::Value> >
 {
     friend class ValueOperations<JS::Rooted<JS::Value> >;
     const JS::Value * extract() const {
         return static_cast<const JS::Rooted<JS::Value>*>(this)->address();
     }
 
+    friend class UnbarrieredMutableValueOperations<JS::Rooted<JS::Value> >;
     friend class MutableValueOperations<JS::Rooted<JS::Value> >;
     JS::Value * extractMutable() {
         return static_cast<JS::Rooted<JS::Value>*>(this)->address();
     }
 };
 
 } // namespace js
 
--- a/js/src/ion/CodeGenerator.cpp
+++ b/js/src/ion/CodeGenerator.cpp
@@ -1134,32 +1134,32 @@ CodeGenerator::visitParDump(LParDump *li
     masm.freeStack(sizeof(Value));
     return true;
 }
 
 bool
 CodeGenerator::visitTypeBarrier(LTypeBarrier *lir)
 {
     ValueOperand operand = ToValue(lir, LTypeBarrier::Input);
-    Register scratch = ToRegister(lir->temp());
+    Register scratch = ToTempUnboxRegister(lir->temp());
 
     Label matched, miss;
     masm.guardTypeSet(operand, lir->mir()->resultTypeSet(), scratch, &matched, &miss);
     masm.jump(&miss);
     if (!bailoutFrom(&miss, lir->snapshot()))
         return false;
     masm.bind(&matched);
     return true;
 }
 
 bool
 CodeGenerator::visitMonitorTypes(LMonitorTypes *lir)
 {
     ValueOperand operand = ToValue(lir, LMonitorTypes::Input);
-    Register scratch = ToRegister(lir->temp());
+    Register scratch = ToTempUnboxRegister(lir->temp());
 
     Label matched, miss;
     masm.guardTypeSet(operand, lir->mir()->typeSet(), scratch, &matched, &miss);
     masm.jump(&miss);
     if (!bailoutFrom(&miss, lir->snapshot()))
         return false;
     masm.bind(&matched);
     return true;
@@ -1283,17 +1283,17 @@ CodeGenerator::visitPostWriteBarrierV(LP
     } else {
         Label tenured;
         Register objreg = ToRegister(lir->object());
         masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.start()), &tenured);
         masm.branchPtr(Assembler::Below, objreg, ImmWord(nursery.heapEnd()), ool->rejoin());
         masm.bind(&tenured);
     }
 
-    Register valuereg = masm.extractObject(value, ToRegister(lir->temp()));
+    Register valuereg = masm.extractObject(value, ToTempUnboxRegister(lir->temp()));
     masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.start()), ool->rejoin());
     masm.branchPtr(Assembler::Below, valuereg, ImmWord(nursery.heapEnd()), ool->entry());
 
     masm.bind(ool->rejoin());
 #endif
     return true;
 }
 
@@ -3539,26 +3539,27 @@ bool
 CodeGenerator::visitCompareStrictS(LCompareStrictS *lir)
 {
     JSOp op = lir->mir()->jsop();
     JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
     const ValueOperand leftV = ToValue(lir, LCompareStrictS::Lhs);
     Register right = ToRegister(lir->right());
     Register output = ToRegister(lir->output());
-    Register temp = ToRegister(lir->temp0());
+    Register temp = ToRegister(lir->temp());
+    Register tempToUnbox = ToTempUnboxRegister(lir->tempToUnbox());
 
     Label string, done;
 
     masm.branchTestString(Assembler::Equal, leftV, &string);
     masm.move32(Imm32(op == JSOP_STRICTNE), output);
     masm.jump(&done);
 
     masm.bind(&string);
-    Register left = masm.extractString(leftV, ToRegister(lir->temp1()));
+    Register left = masm.extractString(leftV, tempToUnbox);
     if (!emitCompareS(lir, op, left, right, output, temp))
         return false;
 
     masm.bind(&done);
 
     return true;
 }
 
@@ -3705,19 +3706,19 @@ CodeGenerator::visitIsNullOrLikeUndefine
         masm.branchTestNull(Assembler::Equal, tag, nullOrLikeUndefined);
         masm.branchTestUndefined(Assembler::Equal, tag, nullOrLikeUndefined);
 
         if (ool) {
             // Check whether it's a truthy object or a falsy object that emulates
             // undefined.
             masm.branchTestObject(Assembler::NotEqual, tag, notNullOrLikeUndefined);
 
-            Register objreg = masm.extractObject(value, ToRegister(lir->temp0()));
+            Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
             testObjectTruthy(objreg, notNullOrLikeUndefined, nullOrLikeUndefined,
-                             ToRegister(lir->temp1()), ool);
+                             ToRegister(lir->temp()), ool);
         }
 
         Label done;
 
         // It's not null or undefined, and if it's an object it doesn't
         // emulate undefined, so it's not like undefined.
         masm.bind(notNullOrLikeUndefined);
         masm.move32(Imm32(op == JSOP_NE), output);
@@ -3784,18 +3785,18 @@ CodeGenerator::visitIsNullOrLikeUndefine
 
         masm.branchTestNull(Assembler::Equal, tag, ifTrueLabel);
         masm.branchTestUndefined(Assembler::Equal, tag, ifTrueLabel);
 
         if (ool) {
             masm.branchTestObject(Assembler::NotEqual, tag, ifFalseLabel);
 
             // Objects that emulate undefined are loosely equal to null/undefined.
-            Register objreg = masm.extractObject(value, ToRegister(lir->temp0()));
-            testObjectTruthy(objreg, ifFalseLabel, ifTrueLabel, ToRegister(lir->temp1()), ool);
+            Register objreg = masm.extractObject(value, ToTempUnboxRegister(lir->tempToUnbox()));
+            testObjectTruthy(objreg, ifFalseLabel, ifTrueLabel, ToRegister(lir->temp()), ool);
         } else {
             masm.jump(ifFalseLabel);
         }
         return true;
     }
 
     JS_ASSERT(op == JSOP_STRICTEQ || op == JSOP_STRICTNE);
 
--- a/js/src/ion/IonMacroAssembler.cpp
+++ b/js/src/ion/IonMacroAssembler.cpp
@@ -78,16 +78,17 @@ MacroAssembler::guardTypeSet(const Sourc
     if (types->hasType(types::Type::NullType()))
         branchTestNull(Equal, tag, matched);
     if (types->hasType(types::Type::MagicArgType()))
         branchTestMagic(Equal, tag, matched);
 
     if (types->hasType(types::Type::AnyObjectType())) {
         branchTestObject(Equal, tag, matched);
     } else if (types->getObjectCount()) {
+        JS_ASSERT(scratch != InvalidReg);
         branchTestObject(NotEqual, tag, miss);
         Register obj = extractObject(address, scratch);
 
         unsigned count = types->getObjectCount();
         for (unsigned i = 0; i < count; i++) {
             if (JSObject *object = types->getSingleObject(i))
                 branchPtr(Equal, obj, ImmGCPtr(object), matched);
         }
--- a/js/src/ion/LIR-Common.h
+++ b/js/src/ion/LIR-Common.h
@@ -1556,20 +1556,20 @@ class LCompareStrictS : public LInstruct
         setTemp(1, temp1);
     }
 
     static const size_t Lhs = 0;
 
     const LAllocation *right() {
         return getOperand(BOX_PIECES);
     }
-    const LDefinition *temp0() {
+    const LDefinition *temp() {
         return getTemp(0);
     }
-    const LDefinition *temp1() {
+    const LDefinition *tempToUnbox() {
         return getTemp(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
 };
 
 // Used for strict-equality comparisons where one side is a boolean
@@ -1675,65 +1675,65 @@ class LCompareVM : public LCallInstructi
     }
 };
 
 class LIsNullOrLikeUndefined : public LInstructionHelper<1, BOX_PIECES, 2>
 {
   public:
     LIR_HEADER(IsNullOrLikeUndefined)
 
-    LIsNullOrLikeUndefined(const LDefinition &temp0, const LDefinition &temp1)
+    LIsNullOrLikeUndefined(const LDefinition &temp, const LDefinition &tempToUnbox)
     {
-        setTemp(0, temp0);
-        setTemp(1, temp1);
+        setTemp(0, temp);
+        setTemp(1, tempToUnbox);
     }
 
     static const size_t Value = 0;
 
     MCompare *mir() {
         return mir_->toCompare();
     }
 
-    const LDefinition *temp0() {
+    const LDefinition *temp() {
         return getTemp(0);
     }
 
-    const LDefinition *temp1() {
+    const LDefinition *tempToUnbox() {
         return getTemp(1);
     }
 };
 
 class LIsNullOrLikeUndefinedAndBranch : public LControlInstructionHelper<2, BOX_PIECES, 2>
 {
   public:
     LIR_HEADER(IsNullOrLikeUndefinedAndBranch)
 
-    LIsNullOrLikeUndefinedAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse, const LDefinition &temp0, const LDefinition &temp1)
+    LIsNullOrLikeUndefinedAndBranch(MBasicBlock *ifTrue, MBasicBlock *ifFalse, const LDefinition &temp, const LDefinition &tempToUnbox)
     {
         setSuccessor(0, ifTrue);
         setSuccessor(1, ifFalse);
-        setTemp(0, temp0);
-        setTemp(1, temp1);
+        setTemp(0, temp);
+        setTemp(1, tempToUnbox);
     }
 
     static const size_t Value = 0;
 
     MBasicBlock *ifTrue() const {
         return getSuccessor(0);
     }
     MBasicBlock *ifFalse() const {
         return getSuccessor(1);
     }
     MCompare *mir() {
         return mir_->toCompare();
     }
-    const LDefinition *temp0() {
+    const LDefinition *temp() {
         return getTemp(0);
     }
-    const LDefinition *temp1() {
+    const LDefinition *tempToUnbox() {
         return getTemp(1);
     }
 };
 
 // Takes an object and tests whether it emulates |undefined|, as determined by
 // the JSCLASS_EMULATES_UNDEFINED class flag on unwrapped objects.  See also
 // js::EmulatesUndefined.
 class LEmulatesUndefined : public LInstructionHelper<1, 1, 0>
--- a/js/src/ion/Lowering.cpp
+++ b/js/src/ion/Lowering.cpp
@@ -601,27 +601,27 @@ LIRGenerator::visitTest(MTest *test)
                 MOZ_ASSERT(comp->operandMightEmulateUndefined(),
                            "MCompare::tryFold should handle the never-emulates-undefined case");
 
                 LEmulatesUndefinedAndBranch *lir =
                     new LEmulatesUndefinedAndBranch(useRegister(left), ifTrue, ifFalse, temp());
                 return add(lir, comp);
             }
 
-            LDefinition temp0, temp1;
+            LDefinition tmp, tmpToUnbox;
             if (comp->operandMightEmulateUndefined()) {
-                temp0 = temp();
-                temp1 = temp();
+                tmp = temp();
+                tmpToUnbox = tempToUnbox();
             } else {
-                temp0 = LDefinition::BogusTemp();
-                temp1 = LDefinition::BogusTemp();
+                tmp = LDefinition::BogusTemp();
+                tmpToUnbox = LDefinition::BogusTemp();
             }
 
             LIsNullOrLikeUndefinedAndBranch *lir =
-                new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, temp0, temp1);
+                new LIsNullOrLikeUndefinedAndBranch(ifTrue, ifFalse, tmp, tmpToUnbox);
             if (!useBox(lir, LIsNullOrLikeUndefinedAndBranch::Value, left))
                 return false;
             return add(lir, comp);
         }
 
         // Compare and branch booleans.
         if (comp->compareType() == MCompare::Compare_Boolean) {
             JS_ASSERT(left->type() == MIRType_Value);
@@ -744,17 +744,17 @@ LIRGenerator::visitCompare(MCompare *com
         return assignSafepoint(lir, comp);
     }
 
     // Strict compare between value and string
     if (comp->compareType() == MCompare::Compare_StrictString) {
         JS_ASSERT(left->type() == MIRType_Value);
         JS_ASSERT(right->type() == MIRType_String);
 
-        LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), temp());
+        LCompareStrictS *lir = new LCompareStrictS(useRegister(right), temp(), tempToUnbox());
         if (!useBox(lir, LCompareStrictS::Lhs, left))
             return false;
         if (!define(lir, comp))
             return false;
         return assignSafepoint(lir, comp);
     }
 
     // Unknown/unspecialized compare use a VM call.
@@ -780,26 +780,26 @@ LIRGenerator::visitCompare(MCompare *com
     {
         if (left->type() == MIRType_Object) {
             MOZ_ASSERT(comp->operandMightEmulateUndefined(),
                        "MCompare::tryFold should have folded this away");
 
             return define(new LEmulatesUndefined(useRegister(left)), comp);
         }
 
-        LDefinition temp0, temp1;
+        LDefinition tmp, tmpToUnbox;
         if (comp->operandMightEmulateUndefined()) {
-            temp0 = temp();
-            temp1 = temp();
+            tmp = temp();
+            tmpToUnbox = tempToUnbox();
         } else {
-            temp0 = LDefinition::BogusTemp();
-            temp1 = LDefinition::BogusTemp();
+            tmp = LDefinition::BogusTemp();
+            tmpToUnbox = LDefinition::BogusTemp();
         }
 
-        LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(temp0, temp1);
+        LIsNullOrLikeUndefined *lir = new LIsNullOrLikeUndefined(tmp, tmpToUnbox);
         if (!useBox(lir, LIsNullOrLikeUndefined::Value, left))
             return false;
         return define(lir, comp);
     }
 
     // Compare booleans.
     if (comp->compareType() == MCompare::Compare_Boolean) {
         JS_ASSERT(left->type() == MIRType_Value);
@@ -1727,30 +1727,40 @@ LIRGenerator::visitStoreSlot(MStoreSlot 
     return true;
 }
 
 bool
 LIRGenerator::visitTypeBarrier(MTypeBarrier *ins)
 {
     // Requesting a non-GC pointer is safe here since we never re-enter C++
     // from inside a type barrier test.
-    LTypeBarrier *barrier = new LTypeBarrier(temp());
+
+    const types::StackTypeSet *types = ins->resultTypeSet();
+    bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
+    LDefinition tmp = needTemp ? temp() : tempToUnbox();
+
+    LTypeBarrier *barrier = new LTypeBarrier(tmp);
     if (!useBox(barrier, LTypeBarrier::Input, ins->input()))
         return false;
     if (!assignSnapshot(barrier, ins->bailoutKind()))
         return false;
     return redefine(ins, ins->input()) && add(barrier, ins);
 }
 
 bool
 LIRGenerator::visitMonitorTypes(MMonitorTypes *ins)
 {
     // Requesting a non-GC pointer is safe here since we never re-enter C++
     // from inside a type check.
-    LMonitorTypes *lir = new LMonitorTypes(temp());
+
+    const types::StackTypeSet *types = ins->typeSet();
+    bool needTemp = !types->unknownObject() && types->getObjectCount() > 0;
+    LDefinition tmp = needTemp ? temp() : tempToUnbox();
+
+    LMonitorTypes *lir = new LMonitorTypes(tmp);
     if (!useBox(lir, LMonitorTypes::Input, ins->input()))
         return false;
     return assignSnapshot(lir, Bailout_Normal) && add(lir, ins);
 }
 
 bool
 LIRGenerator::visitPostWriteBarrier(MPostWriteBarrier *ins)
 {
@@ -1758,17 +1768,17 @@ LIRGenerator::visitPostWriteBarrier(MPos
     switch (ins->value()->type()) {
       case MIRType_Object: {
         LPostWriteBarrierO *lir = new LPostWriteBarrierO(useRegisterOrConstant(ins->object()),
                                                          useRegister(ins->value()));
         return add(lir, ins) && assignSafepoint(lir, ins);
       }
       case MIRType_Value: {
         LPostWriteBarrierV *lir =
-            new LPostWriteBarrierV(useRegisterOrConstant(ins->object()), temp());
+            new LPostWriteBarrierV(useRegisterOrConstant(ins->object()), tempToUnbox());
         if (!useBox(lir, LPostWriteBarrierV::Input, ins->value()))
             return false;
         return add(lir, ins) && assignSafepoint(lir, ins);
       }
       default:
         // Currently, only objects can be in the nursery. Other instruction
         // types cannot hold nursery pointers.
         return true;
--- a/js/src/ion/arm/Lowering-arm.h
+++ b/js/src/ion/arm/Lowering-arm.h
@@ -21,16 +21,20 @@ class LIRGeneratorARM : public LIRGenera
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
+    inline LDefinition tempToUnbox() {
+        return LDefinition::BogusTemp();
+    }
+
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
     bool lowerForShift(LInstructionHelper<1, 2, 0> *ins, MDefinition *mir, MDefinition *lhs,
                        MDefinition *rhs);
     bool lowerUrshD(MUrsh *mir);
 
     bool lowerForALU(LInstructionHelper<1, 1, 0> *ins, MDefinition *mir,
                      MDefinition *input);
--- a/js/src/ion/shared/CodeGenerator-shared-inl.h
+++ b/js/src/ion/shared/CodeGenerator-shared-inl.h
@@ -41,16 +41,24 @@ ToRegister(const LAllocation *a)
 
 static inline Register
 ToRegister(const LDefinition *def)
 {
     return ToRegister(*def->output());
 }
 
 static inline Register
+ToTempUnboxRegister(const LDefinition *def)
+{
+    if (def->isBogusTemp())
+        return InvalidReg;
+    return ToRegister(def);
+}
+
+static inline Register
 ToRegisterOrInvalid(const LAllocation *a)
 {
     return a ? ToRegister(*a) : InvalidReg;
 }
 
 static inline FloatRegister
 ToFloatRegister(const LAllocation &a)
 {
--- a/js/src/ion/x64/Lowering-x64.h
+++ b/js/src/ion/x64/Lowering-x64.h
@@ -23,16 +23,20 @@ class LIRGeneratorX64 : public LIRGenera
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
     // Adds a use at operand |n| of a value-typed insturction.
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register);
 
+    inline LDefinition tempToUnbox() {
+        return temp();
+    }
+
     LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
 
   public:
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
     bool visitReturn(MReturn *ret);
     bool visitStoreTypedArrayElement(MStoreTypedArrayElement *ins);
     bool visitStoreTypedArrayElementHole(MStoreTypedArrayElementHole *ins);
--- a/js/src/ion/x86/Lowering-x86.h
+++ b/js/src/ion/x86/Lowering-x86.h
@@ -21,16 +21,20 @@ class LIRGeneratorX86 : public LIRGenera
 
   protected:
     // Adds a box input to an instruction, setting operand |n| to the type and
     // |n+1| to the payload.
     bool useBox(LInstruction *lir, size_t n, MDefinition *mir,
                 LUse::Policy policy = LUse::REGISTER, bool useAtStart = false);
     bool useBoxFixed(LInstruction *lir, size_t n, MDefinition *mir, Register reg1, Register reg2);
 
+    inline LDefinition tempToUnbox() {
+        return LDefinition::BogusTemp();
+    }
+
     void lowerUntypedPhiInput(MPhi *phi, uint32_t inputPosition, LBlock *block, size_t lirIndex);
     bool defineUntypedPhi(MPhi *phi, size_t lirIndex);
 
     LGetPropertyCacheT *newLGetPropertyCacheT(MGetPropertyCache *ins);
 
   public:
     bool visitBox(MBox *box);
     bool visitUnbox(MUnbox *unbox);
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -26,16 +26,21 @@
     ((uintptr_t)(sp) < (limit)+(tolerance))
 #else
 # define JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, sp, tolerance)  \
     ((uintptr_t)(sp) > (limit)-(tolerance))
 #endif
 
 #define JS_CHECK_STACK_SIZE(limit, lval) JS_CHECK_STACK_SIZE_WITH_TOLERANCE(limit, lval, 0)
 
+namespace JS {
+template <class T>
+class Heap;
+} /* namespace JS */
+
 extern JS_FRIEND_API(void)
 JS_SetGrayGCRootsTracer(JSRuntime *rt, JSTraceDataOp traceOp, void *data);
 
 extern JS_FRIEND_API(JSString *)
 JS_GetAnonymousString(JSRuntime *rt);
 
 extern JS_FRIEND_API(JSObject *)
 JS_FindCompilationScope(JSContext *cx, JSObject *obj);
@@ -885,17 +890,17 @@ NukeCrossCompartmentWrappers(JSContext* 
  */
 
 struct ExpandoAndGeneration {
   ExpandoAndGeneration()
     : expando(UndefinedValue()),
       generation(0)
   {}
 
-  Value expando;
+  JS::Heap<JS::Value> expando;
   uint32_t generation;
 };
 
 typedef enum DOMProxyShadowsResult {
   ShadowCheckFailed,
   Shadows,
   DoesntShadow,
   DoesntShadowUnique
--- a/js/xpconnect/src/XPCJSRuntime.cpp
+++ b/js/xpconnect/src/XPCJSRuntime.cpp
@@ -498,30 +498,30 @@ void XPCJSRuntime::TraceGrayJS(JSTracer*
     XPCJSRuntime* self = (XPCJSRuntime*)data;
 
     // Mark these roots as gray so the CC can walk them later.
     self->TraceXPConnectRoots(trc);
 }
 
 struct JsGcTracer : public TraceCallbacks
 {
-    virtual void Trace(JS::Value *p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallValueTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<JS::Value> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapValueTracer(static_cast<JSTracer*>(closure), p, name);
     }
-    virtual void Trace(jsid *p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallIdTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<jsid> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapIdTracer(static_cast<JSTracer*>(closure), p, name);
     }
-    virtual void Trace(JSObject **p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallObjectTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<JSObject *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapObjectTracer(static_cast<JSTracer*>(closure), p, name);
     }
-    virtual void Trace(JSString **p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallStringTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<JSString *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapStringTracer(static_cast<JSTracer*>(closure), p, name);
     }
-    virtual void Trace(JSScript **p, const char *name, void *closure) const MOZ_OVERRIDE {
-        JS_CallScriptTracer(static_cast<JSTracer*>(closure), p, name);
+    virtual void Trace(JS::Heap<JSScript *> *p, const char *name, void *closure) const MOZ_OVERRIDE {
+        JS_CallHeapScriptTracer(static_cast<JSTracer*>(closure), p, name);
     }
 };
 
 static PLDHashOperator
 TraceJSHolder(void *holder, nsScriptObjectTracer *&tracer, void *arg)
 {
     tracer->Trace(holder, JsGcTracer(), arg);
 
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -322,17 +322,18 @@ NS_NewResizerFrame (nsIPresShell* aPresS
 
 
 #endif
 
 nsIFrame*
 NS_NewHTMLScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot);
 
 nsIFrame*
-NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot);
+NS_NewXULScrollFrame (nsIPresShell* aPresShell, nsStyleContext* aContext,
+                      bool aIsRoot, bool aClipAllDescendants);
 
 nsIFrame*
 NS_NewSliderFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 nsIFrame*
 NS_NewScrollbarFrame (nsIPresShell* aPresShell, nsStyleContext* aContext);
 
 nsIFrame*
@@ -4111,18 +4112,21 @@ nsCSSFrameConstructor::BeginBuildingScro
 
   nsRefPtr<nsStyleContext> contentStyle = aContentStyle;
 
   if (!gfxScrollFrame) {
     // Build a XULScrollFrame when the child is a box, otherwise an
     // HTMLScrollFrame
     // XXXbz this is the lone remaining consumer of IsXULDisplayType.
     // I wonder whether we can eliminate that somehow.
-    if (IsXULDisplayType(aContentStyle->StyleDisplay())) {
-      gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot);
+    const nsStyleDisplay* displayStyle = aContentStyle->StyleDisplay();
+    if (IsXULDisplayType(displayStyle)) {
+      gfxScrollFrame = NS_NewXULScrollFrame(mPresShell, contentStyle, aIsRoot,
+          displayStyle->mDisplay == NS_STYLE_DISPLAY_STACK ||
+          displayStyle->mDisplay == NS_STYLE_DISPLAY_INLINE_STACK);
     } else {
       gfxScrollFrame = NS_NewHTMLScrollFrame(mPresShell, contentStyle, aIsRoot);
     }
 
     InitAndRestoreFrame(aState, aContent, aParentFrame, gfxScrollFrame);
   }
 
   // if there are any anonymous children for the scroll frame, create
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -593,18 +593,20 @@ void nsDisplayListBuilder::MarkOutOfFlow
   }
 
   if (mHasDisplayPort && IsFixedFrame(aFrame)) {
     dirty = overflowRect;
   }
 
   if (!dirty.IntersectRect(dirty, overflowRect))
     return;
-  aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(),
-    new OutOfFlowDisplayData(mClipState.GetClipForContainingBlockDescendants(), dirty));
+  const DisplayItemClip* clip = mClipState.GetClipForContainingBlockDescendants();
+  OutOfFlowDisplayData* data = clip ? new OutOfFlowDisplayData(*clip, dirty)
+    : new OutOfFlowDisplayData(dirty);
+  aFrame->Properties().Set(nsDisplayListBuilder::OutOfFlowDisplayDataProperty(), data);
 
   MarkFrameForDisplay(aFrame, aDirtyFrame);
 }
 
 static void UnmarkFrameForDisplay(nsIFrame* aFrame) {
   nsPresContext* presContext = aFrame->PresContext();
   presContext->PropertyTable()->
     Delete(aFrame, nsDisplayListBuilder::OutOfFlowDisplayDataProperty());
@@ -803,24 +805,30 @@ void
 nsDisplayListBuilder::LeavePresShell(nsIFrame* aReferenceFrame,
                                      const nsRect& aDirtyRect) {
   if (CurrentPresShellState()->mPresShell != aReferenceFrame->PresContext()->PresShell()) {
     // Must have not allocated a state for this presshell, presumably due
     // to OOM.
     return;
   }
 
+  ResetMarkedFramesForDisplayList();
+  mPresShellStates.SetLength(mPresShellStates.Length() - 1);
+}
+
+void
+nsDisplayListBuilder::ResetMarkedFramesForDisplayList()
+{
   // Unmark and pop off the frames marked for display in this pres shell.
   uint32_t firstFrameForShell = CurrentPresShellState()->mFirstFrameMarkedForDisplay;
   for (uint32_t i = firstFrameForShell;
        i < mFramesMarkedForDisplay.Length(); ++i) {
     UnmarkFrameForDisplay(mFramesMarkedForDisplay[i]);
   }
   mFramesMarkedForDisplay.SetLength(firstFrameForShell);
-  mPresShellStates.SetLength(mPresShellStates.Length() - 1);
 }
 
 void
 nsDisplayListBuilder::MarkFramesForDisplayList(nsIFrame* aDirtyFrame,
                                                const nsFrameList& aFrames,
                                                const nsRect& aDirtyRect) {
   for (nsFrameList::Enumerator e(aFrames); !e.AtEnd(); e.Next()) {
     mFramesMarkedForDisplay.AppendElement(e.get());
@@ -1194,28 +1202,31 @@ void nsDisplayList::PaintForFrame(nsDisp
 
   nsIntRegion invalid;
   if (props) {
     invalid = props->ComputeDifferences(root, computeInvalidFunc);
   } else if (widgetTransaction) {
     LayerProperties::ClearInvalidations(root);
   }
 
+  bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
   if (view) {
     if (props) {
       if (!invalid.IsEmpty()) {
         nsIntRect bounds = invalid.GetBounds();
         nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
                     presContext->DevPixelsToAppUnits(bounds.y),
                     presContext->DevPixelsToAppUnits(bounds.width),
                     presContext->DevPixelsToAppUnits(bounds.height));
-        view->GetViewManager()->InvalidateViewNoSuppression(view, rect);
+        if (shouldInvalidate) {
+          view->GetViewManager()->InvalidateViewNoSuppression(view, rect);
+        }
         presContext->NotifyInvalidation(bounds, 0);
       }
-    } else {
+    } else if (shouldInvalidate) {
       view->GetViewManager()->InvalidateView(view);
     }
   }
 
   if (aFlags & PAINT_FLUSH_LAYERS) {
     FrameLayerBuilder::InvalidateAllLayers(layerManager);
   }
 
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -330,16 +330,24 @@ public:
   nsCaret* GetCaret();
   /**
    * Notify the display list builder that we're entering a presshell.
    * aReferenceFrame should be a frame in the new presshell and aDirtyRect
    * should be the current dirty rect in aReferenceFrame's coordinate space.
    */
   void EnterPresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect);
   /**
+   * For print-preview documents, we sometimes need to build display items for
+   * the same frames multiple times in the same presentation, with different
+   * clipping. Between each such batch of items, call
+   * ResetMarkedFramesForDisplayList to make sure that the results of
+   * MarkFramesForDisplayList do not carry over between batches.
+   */
+  void ResetMarkedFramesForDisplayList();
+  /**
    * Notify the display list builder that we're leaving a presshell.
    */
   void LeavePresShell(nsIFrame* aReferenceFrame, const nsRect& aDirtyRect);
 
   /**
    * Returns true if we're currently building a display list that's
    * directly or indirectly under an nsDisplayTransform.
    */
@@ -561,22 +569,25 @@ public:
     bool                  mOldValue;
   };
 
   // Helpers for tables
   nsDisplayTableItem* GetCurrentTableItem() { return mCurrentTableItem; }
   void SetCurrentTableItem(nsDisplayTableItem* aTableItem) { mCurrentTableItem = aTableItem; }
 
   struct OutOfFlowDisplayData {
-    OutOfFlowDisplayData(const DisplayItemClip* aContainingBlockClip,
+    OutOfFlowDisplayData(const DisplayItemClip& aContainingBlockClip,
                          const nsRect &aDirtyRect)
       : mContainingBlockClip(aContainingBlockClip)
       , mDirtyRect(aDirtyRect)
     {}
-    const DisplayItemClip* mContainingBlockClip;
+    OutOfFlowDisplayData(const nsRect &aDirtyRect)
+      : mDirtyRect(aDirtyRect)
+    {}
+    DisplayItemClip mContainingBlockClip;
     nsRect mDirtyRect;
   };
   static void DestroyOutOfFlowDisplayData(void* aPropertyValue)
   {
     delete static_cast<OutOfFlowDisplayData*>(aPropertyValue);
   }
 
   NS_DECLARE_FRAME_PROPERTY(OutOfFlowDisplayDataProperty, DestroyOutOfFlowDisplayData)
--- a/layout/base/nsDocumentViewer.cpp
+++ b/layout/base/nsDocumentViewer.cpp
@@ -1536,22 +1536,16 @@ nsDocumentViewer::Destroy()
 
     // Remove our root view from the view hierarchy.
     if (mPresShell) {
       nsViewManager *vm = mPresShell->GetViewManager();
       if (vm) {
         nsView *rootView = vm->GetRootView();
 
         if (rootView) {
-          // The invalidate that removing this view causes is dropped because
-          // the Freeze call above sets painting to be suppressed for our
-          // document. So we do it ourselves and make it happen.
-          vm->InvalidateViewNoSuppression(rootView,
-            rootView->GetBounds() - rootView->GetPosition());
-
           nsView *rootViewParent = rootView->GetParent();
           if (rootViewParent) {
             nsViewManager *parentVM = rootViewParent->GetViewManager();
             if (parentVM) {
               parentVM->RemoveChild(rootView);
             }
           }
         }
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -5484,27 +5484,32 @@ PresShell::Paint(nsView*        aViewToP
   NS_ASSERTION(aViewToPaint, "null view");
 
   MOZ_ASSERT(!mImageVisibilityVisited, "should have been cleared");
 
   if (!mIsActive || mIsZombie) {
     return;
   }
 
-  nsAutoNotifyDidPaint notifyDidPaint(this, aFlags);
-
   nsPresContext* presContext = GetPresContext();
   AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
 
   nsIFrame* frame = aViewToPaint->GetFrame();
 
   bool isRetainingManager;
   LayerManager* layerManager =
     aViewToPaint->GetWidget()->GetLayerManager(&isRetainingManager);
   NS_ASSERTION(layerManager, "Must be in paint event");
+  bool shouldInvalidate = layerManager->NeedsWidgetInvalidation();
+
+  uint32_t didPaintFlags = aFlags;
+  if (!shouldInvalidate) {
+    didPaintFlags |= PAINT_COMPOSITE;
+  }
+  nsAutoNotifyDidPaint notifyDidPaint(this, didPaintFlags);
 
   // Whether or not we should set first paint when painting is
   // suppressed is debatable. For now we'll do it because
   // B2G relies on first paint to configure the viewport and
   // we only want to do that when we have real content to paint.
   // See Bug 798245
   if (mIsFirstPaint && !mPaintingSuppressed) {
     layerManager->SetIsFirstPaint();
@@ -5514,19 +5519,16 @@ PresShell::Paint(nsView*        aViewToP
   if (frame && isRetainingManager) {
     // Try to do an empty transaction, if the frame tree does not
     // need to be updated. Do not try to do an empty transaction on
     // a non-retained layer manager (like the BasicLayerManager that
     // draws the window title bar on Mac), because a) it won't work
     // and b) below we don't want to clear NS_FRAME_UPDATE_LAYER_TREE,
     // that will cause us to forget to update the real layer manager!
     if (!(aFlags & PAINT_LAYERS)) {
-      if (layerManager->HasShadowManager() && Compositor::GetBackend() != LAYERS_BASIC) {
-        return;
-      }
       layerManager->BeginTransaction();
       if (layerManager->EndEmptyTransaction()) {
         return;
       }
       NS_WARNING("Must complete empty transaction when compositing!");
     } else {
       layerManager->BeginTransaction();
     }
@@ -5551,20 +5553,22 @@ PresShell::Paint(nsView*        aViewToP
         }
         if (props) {
           if (!invalid.IsEmpty()) {
             nsIntRect bounds = invalid.GetBounds();
             nsRect rect(presContext->DevPixelsToAppUnits(bounds.x),
                         presContext->DevPixelsToAppUnits(bounds.y),
                         presContext->DevPixelsToAppUnits(bounds.width),
                         presContext->DevPixelsToAppUnits(bounds.height));
-            aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect);
+            if (shouldInvalidate) {
+              aViewToPaint->GetViewManager()->InvalidateViewNoSuppression(aViewToPaint, rect);
+            }
             presContext->NotifyInvalidation(bounds, 0);
           }
-        } else {
+        } else if (shouldInvalidate) {
           aViewToPaint->GetViewManager()->InvalidateView(aViewToPaint);
         }
 
         frame->UpdatePaintCountForPaintedPresShells();
         return;
       }
     }
     frame->RemoveStateBits(NS_FRAME_UPDATE_LAYER_TREE);
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2102,17 +2102,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
     IsRootScrollFrameActive(PresContext()->PresShell()) && !isSVG;
 
   nsDisplayListBuilder::AutoBuildingDisplayList
     buildingForChild(aBuilder, child, pseudoStackingContext, buildFixedPositionItem);
   DisplayListClipState::AutoClipMultiple clipState(aBuilder);
 
   if (savedOutOfFlowData) {
     clipState.SetClipForContainingBlockDescendants(
-      savedOutOfFlowData->mContainingBlockClip);
+      &savedOutOfFlowData->mContainingBlockClip);
   }
 
   // Setup clipping for the parent's overflow:-moz-hidden-unscrollable,
   // or overflow:hidden on elements that don't support scrolling (and therefore
   // don't create nsHTML/XULScrollFrame). This clipping needs to not clip
   // anything directly rendered by the parent, only the rendering of its
   // children.
   // Don't use overflowClip to restrict the dirty rect, since some of the
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -885,28 +885,32 @@ NS_QUERYFRAME_HEAD(nsHTMLScrollFrame)
   NS_QUERYFRAME_ENTRY(nsIScrollbarOwner)
   NS_QUERYFRAME_ENTRY(nsIScrollableFrame)
   NS_QUERYFRAME_ENTRY(nsIStatefulFrame)
 NS_QUERYFRAME_TAIL_INHERITING(nsContainerFrame)
 
 //----------nsXULScrollFrame-------------------------------------------
 
 nsIFrame*
-NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot)
+NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
+                     bool aIsRoot, bool aClipAllDescendants)
 {
-  return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot);
+  return new (aPresShell) nsXULScrollFrame(aPresShell, aContext, aIsRoot,
+                                           aClipAllDescendants);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsXULScrollFrame)
 
-nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot)
+nsXULScrollFrame::nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext,
+                                   bool aIsRoot, bool aClipAllDescendants)
   : nsBoxFrame(aShell, aContext, aIsRoot),
     mInner(ALLOW_THIS_IN_INITIALIZER_LIST(this), aIsRoot)
 {
   SetLayoutManager(nullptr);
+  mInner.mClipAllDescendants = aClipAllDescendants;
 }
 
 nsMargin
 nsGfxScrollFrameInner::GetDesiredScrollbarSizes(nsBoxLayoutState* aState)
 {
   NS_ASSERTION(aState && aState->GetRenderingContext(),
                "Must have rendering context in layout state for size "
                "computations");
@@ -1498,16 +1502,17 @@ nsGfxScrollFrameInner::nsGfxScrollFrameI
   , mLastUpdateImagesPos(-1, -1)
   , mNeverHasVerticalScrollbar(false)
   , mNeverHasHorizontalScrollbar(false)
   , mHasVerticalScrollbar(false)
   , mHasHorizontalScrollbar(false)
   , mFrameIsUpdatingScrollbar(false)
   , mDidHistoryRestore(false)
   , mIsRoot(aIsRoot)
+  , mClipAllDescendants(aIsRoot)
   , mSupppressScrollbarUpdate(false)
   , mSkippedScrollbarLayout(false)
   , mHadNonInitialReflow(false)
   , mHorizontalOverflow(false)
   , mVerticalOverflow(false)
   , mPostedReflowCallback(false)
   , mMayHaveDirtyFixedChildren(false)
   , mUpdateScrollbarAttributes(false)
@@ -2193,26 +2198,26 @@ nsGfxScrollFrameInner::BuildDisplayList(
   }
 
   nsDisplayListCollection set;
   {
     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
 
     if (usingDisplayport) {
       nsRect clip = displayPort + aBuilder->ToReferenceFrame(mOuter);
-      if (mIsRoot) {
+      if (mClipAllDescendants) {
         clipState.ClipContentDescendants(clip);
       } else {
         clipState.ClipContainingBlockDescendants(clip, nullptr);
       }
     } else {
       nsRect clip = mScrollPort + aBuilder->ToReferenceFrame(mOuter);
       // Our override of GetBorderRadii ensures we never have a radius at
       // the corners where we have a scrollbar.
-      if (mIsRoot) {
+      if (mClipAllDescendants) {
 #ifdef DEBUG
         nscoord radii[8];
 #endif
         NS_ASSERTION(!mOuter->GetPaddingBoxBorderRadii(radii),
                      "Roots with radii not supported");
         clipState.ClipContentDescendants(clip);
       } else {
         nscoord radii[8];
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -315,16 +315,19 @@ public:
   bool mNeverHasVerticalScrollbar:1;
   bool mNeverHasHorizontalScrollbar:1;
   bool mHasVerticalScrollbar:1;
   bool mHasHorizontalScrollbar:1;
   bool mFrameIsUpdatingScrollbar:1;
   bool mDidHistoryRestore:1;
   // Is this the scrollframe for the document's viewport?
   bool mIsRoot:1;
+  // True if we should clip all descendants, false if we should only clip
+  // descendants for which we are the containing block.
+  bool mClipAllDescendants:1;
   // If true, don't try to layout the scrollbars in Reflow().  This can be
   // useful if multiple passes are involved, because we don't want to place the
   // scrollbars at the wrong size.
   bool mSupppressScrollbarUpdate:1;
   // If true, we skipped a scrollbar layout due to mSupppressScrollbarUpdate
   // being set at some point.  That means we should lay out scrollbars even if
   // it might not strictly be needed next time mSupppressScrollbarUpdate is
   // false.
@@ -625,17 +628,18 @@ private:
 class nsXULScrollFrame : public nsBoxFrame,
                          public nsIScrollableFrame,
                          public nsIAnonymousContentCreator,
                          public nsIStatefulFrame {
 public:
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
-  friend nsIFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, bool aIsRoot);
+  friend nsIFrame* NS_NewXULScrollFrame(nsIPresShell* aPresShell, nsStyleContext* aContext,
+                                        bool aIsRoot, bool aClipAllDescendants);
 
   // Called to set the child frames. We typically have three: the scroll area,
   // the vertical scrollbar, and the horizontal scrollbar.
   NS_IMETHOD SetInitialChildList(ChildListID     aListID,
                                  nsFrameList&    aChildList) MOZ_OVERRIDE;
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
@@ -848,17 +852,18 @@ public:
     return nsBoxFrame::IsFrameOfType(aFlags);
   }
 
 #ifdef DEBUG
   NS_IMETHOD GetFrameName(nsAString& aResult) const MOZ_OVERRIDE;
 #endif
 
 protected:
-  nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot);
+  nsXULScrollFrame(nsIPresShell* aShell, nsStyleContext* aContext, bool aIsRoot,
+                   bool aClipAllDescendants);
 
   void ClampAndSetBounds(nsBoxLayoutState& aState, 
                          nsRect& aRect,
                          nsPoint aScrollPosition,
                          bool aRemoveOverflowAreas = false) {
     /* 
      * For RTL frames, restore the original scrolled position of the right
      * edge, then subtract the current width to find the physical position.
--- a/layout/generic/nsSimplePageSequence.cpp
+++ b/layout/generic/nsSimplePageSequence.cpp
@@ -813,16 +813,17 @@ nsSimplePageSequenceFrame::BuildDisplayL
     // nsDisplayTransform, since they'll be in a different coordinate system.
     DisplayListClipState::AutoSaveRestore clipState(aBuilder);
     clipState.Clear();
 
     nsIFrame* child = GetFirstPrincipalChild();
     while (child) {
       child->BuildDisplayListForStackingContext(aBuilder,
           child->GetVisualOverflowRectRelativeToSelf(), &content);
+      aBuilder->ResetMarkedFramesForDisplayList();
       child = child->GetNextSibling();
     }
   }
 
   content.AppendNewToTop(new (aBuilder)
       nsDisplayTransform(aBuilder, this, &content, ::ComputePageSequenceTransform));
 
   aLists.Content()->AppendToTop(&content);
--- a/layout/reftests/bugs/598726-1.html
+++ b/layout/reftests/bugs/598726-1.html
@@ -1,10 +1,10 @@
 <!DOCTYPE html>
-<html class="reftest-wait">
+<html class="reftest-wait reftest-snapshot-all">
   <head>
     <style type="text/css">
       input {
           -webkit-transition: -webkit-transform 200ms ease-in-out;
           transition: transform 200ms ease-in-out;
       }
       input:focus {
           -webkit-transform: scale(1.05);
@@ -26,17 +26,17 @@
           i.getClientRects();
           i.addEventListener("transitionend", function(aEvent) {
             if (aEvent.propertyName != 'transform' &&
                 aEvent.propertyName != '-webkit-transform') {
               return;
             }
             i.removeEventListener("transitionend", arguments.callee, true);
             i.getClientRects();
-            document.documentElement.removeAttribute("class");
+            document.documentElement.setAttribute("class", "reftest-snapshot-all");
           }, true);
           i.blur();
           i.getClientRects();
         }, true);
         i.focus();
         i.getClientRects();
       }
     </script>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/875060-1-ref.html
@@ -0,0 +1,7 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style="display:-moz-inline-stack; width:100px; height:100px; background:yellow">
+</div>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/875060-1.html
@@ -0,0 +1,8 @@
+<!DOCTYPE HTML>
+<html>
+<body>
+<div style="display:-moz-inline-stack; overflow:hidden; position:relative; width:100px; height:100px;">
+  <div style="width:200px; height:200px; background:yellow; position:absolute"></div>
+</div>
+</body>
+</html>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1755,8 +1755,9 @@ fails-if(Android) == 836844-1.html 83684
 == 846144-1.html 846144-1-ref.html
 == 847850-1.html 847850-1-ref.html
 == 848421-1.html 848421-1-ref.html
 test-pref(layout.css.flexbox.enabled,true) == 849407-1.html 849407-1-ref.html
 == 849996-1.html 849996-1-ref.html
 == 858803-1.html 858803-1-ref.html
 != 860370.html 860370-notref.html
 == 871338-1.html 871338-1-ref.html
+== 875060-1.html 875060-1-ref.html
--- a/layout/reftests/forms/input/range/reftest.list
+++ b/layout/reftests/forms/input/range/reftest.list
@@ -16,18 +16,18 @@ default-preferences pref(dom.experimenta
 
 # dynamic value changes:
 == input-value-prop-unthemed.html input-75pct-unthemed-common-ref.html
 == input-value-prop.html input-75pct-common-ref.html
 == input-valueAsNumber-prop-unthemed.html input-75pct-unthemed-common-ref.html
 == input-valueAsNumber-prop.html input-75pct-common-ref.html
 fails-if(B2G) == input-stepDown-unthemed.html input-75pct-unthemed-common-ref.html
 fails-if(B2G) == input-stepDown.html input-75pct-common-ref.html
-fails-if(B2G) == input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html
-fails-if(B2G) == input-stepUp.html input-75pct-common-ref.html
+== input-stepUp-unthemed.html input-75pct-unthemed-common-ref.html
+== input-stepUp.html input-75pct-common-ref.html
 
 # 'direction' property:
 == input-range-direction-unthemed-1.html input-range-direction-unthemed-1-ref.html
 
 # ::-moz-range-progress pseudo-element:
 fails-if(B2G) == input-range-moz-range-progress-1.html input-range-moz-range-progress-1-ref.html
 == input-range-moz-range-progress-2.html input-range-moz-range-progress-2-ref.html
 == input-range-moz-range-progress-3.html input-range-moz-range-progress-3-ref.html
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -299,17 +299,20 @@ CommonElementAnimationData::CanAnimatePr
     }
   }
   bool enabled = nsLayoutUtils::AreAsyncAnimationsEnabled();
   if (!enabled && shouldLog) {
     nsCString message;
     message.AppendLiteral("Performance warning: Async animations are disabled");
     LogAsyncAnimationFailure(message);
   }
-  return enabled && (aFlags & CanAnimate_AllowPartial);
+  bool propertyAllowed = (aProperty == eCSSProperty_transform) ||
+                         (aProperty == eCSSProperty_opacity) ||
+                         (aFlags & CanAnimate_AllowPartial);
+  return enabled && propertyAllowed;
 }
 
 /* static */ void
 CommonElementAnimationData::LogAsyncAnimationFailure(nsCString& aMessage,
                                                      const nsIContent* aContent)
 {
   if (aContent) {
     aMessage.AppendLiteral(" [");
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -236,16 +236,24 @@ function shouldWaitForPendingPaints() {
 function shouldWaitForReftestWaitRemoval(contentRootElement) {
     // use getAttribute because className works differently in HTML and SVG
     return contentRootElement &&
            contentRootElement.hasAttribute('class') &&
            contentRootElement.getAttribute('class').split(/\s+/)
                              .indexOf("reftest-wait") != -1;
 }
 
+function shouldSnapshotWholePage(contentRootElement) {
+    // use getAttribute because className works differently in HTML and SVG
+    return contentRootElement &&
+           contentRootElement.hasAttribute('class') &&
+           contentRootElement.getAttribute('class').split(/\s+/)
+                             .indexOf("reftest-snapshot-all") != -1;
+}
+
 function getNoPaintElements(contentRootElement) {
   return contentRootElement.getElementsByClassName('reftest-no-paint');
 }
 
 // Initial state. When the document has loaded and all MozAfterPaint events and
 // all explicit paint waits are flushed, we can fire the MozReftestInvalidate
 // event and move to the next state.
 const STATE_WAITING_TO_FIRE_INVALIDATE_EVENT = 0;
@@ -301,17 +309,17 @@ function WaitForTestEnd(contentRootEleme
     function AfterPaintListener(event) {
         LogInfo("AfterPaintListener in " + event.target.document.location.href);
         if (event.target.document != currentDoc) {
             // ignore paint events for subframes or old documents in the window.
             // Invalidation in subframes will cause invalidation in the toplevel document anyway.
             return;
         }
 
-        SendUpdateCanvasForEvent(event);
+        SendUpdateCanvasForEvent(event, contentRootElement);
         // These events are fired immediately after a paint. Don't
         // confuse ourselves by firing synchronously if we triggered the
         // paint ourselves.
         setTimeout(MakeProgress, 0);
     }
 
     function AttrModifiedListener() {
         LogInfo("AttrModifiedListener fired");
@@ -835,22 +843,34 @@ function SendTestDone(runtimeMs)
     sendAsyncMessage("reftest:TestDone", { runtimeMs: runtimeMs });
 }
 
 function roundTo(x, fraction)
 {
     return Math.round(x/fraction)*fraction;
 }
 
-function SendUpdateCanvasForEvent(event)
+function SendUpdateCanvasForEvent(event, contentRootElement)
 {
     var win = content;
     var scale = markupDocumentViewer().fullZoom;
 
     var rects = [ ];
+    if (shouldSnapshotWholePage) {
+      // See comments in SendInitCanvasWithSnapshot() re: the split
+      // logic here.
+      if (!gBrowserIsRemote) {
+          sendSyncMessage("reftest:UpdateWholeCanvasForInvalidation");
+      } else {
+          SynchronizeForSnapshot(SYNC_ALLOW_DISABLE);
+          sendAsyncMessage("reftest:UpdateWholeCanvasForInvalidation");
+      }
+      return;
+    }
+    
     var rectList = event.clientRects;
     LogInfo("SendUpdateCanvasForEvent with " + rectList.length + " rects");
     for (var i = 0; i < rectList.length; ++i) {
         var r = rectList[i];
         // Set left/top/right/bottom to "device pixel" boundaries
         var left = Math.floor(roundTo(r.left*scale, 0.001));
         var top = Math.floor(roundTo(r.top*scale, 0.001));
         var right = Math.ceil(roundTo(r.right*scale, 0.001));
--- a/layout/tools/reftest/reftest.js
+++ b/layout/tools/reftest/reftest.js
@@ -1387,16 +1387,28 @@ function UpdateCurrentCanvasForInvalidat
 
         ctx.save();
         ctx.translate(left, top);
         DoDrawWindow(ctx, left, top, right - left, bottom - top);
         ctx.restore();
     }
 }
 
+function UpdateWholeCurrentCanvasForInvalidation()
+{
+    LogInfo("Updating entire canvas for invalidation");
+
+    if (!gCurrentCanvas) {
+        return;
+    }
+
+    var ctx = gCurrentCanvas.getContext("2d");
+    DoDrawWindow(ctx, 0, 0, gCurrentCanvas.width, gCurrentCanvas.height);
+}
+
 function RecordResult(testRunTime, errorMsg, scriptResults)
 {
     LogInfo("RecordResult fired");
 
     // Keep track of which test was slowest, and how long it took.
     if (testRunTime > gSlowestTestTime) {
         gSlowestTestTime = testRunTime;
         gSlowestTestURL  = gCurrentURL;
@@ -1786,16 +1798,20 @@ function RegisterMessageListenersAndLoad
         "reftest:TestDone",
         function (m) { RecvTestDone(m.json.runtimeMs); }
     );
     gBrowserMessageManager.addMessageListener(
         "reftest:UpdateCanvasForInvalidation",
         function (m) { RecvUpdateCanvasForInvalidation(m.json.rects); }
     );
     gBrowserMessageManager.addMessageListener(
+        "reftest:UpdateWholeCanvasForInvalidation",
+        function (m) { RecvUpdateWholeCanvasForInvalidation(); }
+    );
+    gBrowserMessageManager.addMessageListener(
         "reftest:ExpectProcessCrash",
         function (m) { RecvExpectProcessCrash(); }
     );
     gBrowserMessageManager.addMessageListener(
         "reftest:EnableAsyncScroll",
         function (m) { SetAsyncScroll(true); }
     );
 
@@ -1865,16 +1881,21 @@ function RecvTestDone(runtimeMs)
     RecordResult(runtimeMs, '', [ ]);
 }
 
 function RecvUpdateCanvasForInvalidation(rects)
 {
     UpdateCurrentCanvasForInvalidation(rects);
 }
 
+function RecvUpdateWholeCanvasForInvalidation()
+{
+    UpdateWholeCurrentCanvasForInvalidation();
+}
+
 function OnProcessCrashed(subject, topic, data)
 {
     var id;
     subject = subject.QueryInterface(CI.nsIPropertyBag2);
     if (topic == "plugin-crashed") {
         id = subject.getPropertyAsAString("pluginDumpID");
     } else if (topic == "ipc:content-shutdown") {
         id = subject.getPropertyAsAString("dumpID");
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1799,17 +1799,16 @@ pref("layout.frame_rate", -1);
 // heavily loaded.
 pref("layout.frame_rate.precise", false);
 
 // pref to permit users to make verified SOAP calls by default
 pref("capability.policy.default.SOAPCall.invokeVerifySourceHeader", "allAccess");
 
 // if true, allow plug-ins to override internal imglib decoder mime types in full-page mode
 pref("plugin.override_internal_types", false);
-pref("plugin.expose_full_path", false); // if true navigator.plugins reveals full path
 
 // See bug 136985.  Gives embedders a pref to hook into to show
 // a popup blocker if they choose.
 pref("browser.popups.showPopupBlocker", true);
 
 // Pref to control whether the viewmanager code does double-buffering or not
 // See http://bugzilla.mozilla.org/show_bug.cgi?id=169483 for further details...
 pref("viewmanager.do_doublebuffering", true);
--- a/view/public/nsViewManager.h
+++ b/view/public/nsViewManager.h
@@ -339,20 +339,16 @@ private:
   void ReparentWidgets(nsView* aView, nsView *aParent);
   void InvalidateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion);
 
   void InvalidateViews(nsView *aView);
 
   // aView is the view for aWidget and aRegion is relative to aWidget.
   void Refresh(nsView *aView, const nsIntRegion& aRegion);
 
-  void InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut);
-  void InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
-                                          nscoord aY1, nscoord aY2, bool aInCutOut);
-
   // Utilities
 
   bool IsViewInserted(nsView *aView);
 
   /**
    * Intersects aRect with aView's bounds and then transforms it from aView's
    * coordinate system to the coordinate system of the widget attached to
    * aView.
--- a/view/src/nsViewManager.cpp
+++ b/view/src/nsViewManager.cpp
@@ -20,16 +20,17 @@
 #include "nsPresContext.h"
 #include "nsEventStateManager.h"
 #include "mozilla/StartupTimeline.h"
 #include "GeckoProfiler.h"
 #include "nsRefreshDriver.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h"
 #include "nsLayoutUtils.h"
+#include "mozilla/layers/Compositor.h"
 
 /**
    XXX TODO XXX
 
    DeCOMify newly private methods
    Optimize view storage
 */
 
@@ -37,16 +38,18 @@
    A note about platform assumptions:
 
    We assume that a widget is z-ordered on top of its parent.
    
    We do NOT assume anything about the relative z-ordering of sibling widgets. Even though
    we ask for a specific z-order, we don't assume that widget z-ordering actually works.
 */
 
+using namespace mozilla::layers;
+
 #define NSCOORD_NONE      INT32_MIN
 
 #undef DEBUG_MOUSE_LOCATION
 
 int32_t nsViewManager::mVMCount = 0;
 
 // Weakly held references to all of the view managers
 nsVoidArray* nsViewManager::gViewManagers = nullptr;
@@ -325,18 +328,24 @@ void nsViewManager::Refresh(nsView *aVie
                  "Widgets that we paint must all be display roots");
 
     if (mPresShell) {
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf("--COMPOSITE-- %p\n", mPresShell);
       }
 #endif
-      mPresShell->Paint(aView, damageRegion,
-                        nsIPresShell::PAINT_COMPOSITE);
+      uint32_t paintFlags = nsIPresShell::PAINT_COMPOSITE;
+      LayerManager *manager = widget->GetLayerManager();
+      if (!manager->NeedsWidgetInvalidation()) {
+        manager->FlushRendering();
+      } else {
+        mPresShell->Paint(aView, damageRegion,
+                          paintFlags);
+      }
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf("--ENDCOMPOSITE--\n");
       }
 #endif
       mozilla::StartupTimeline::RecordOnce(mozilla::StartupTimeline::FIRST_PAINT);
     }
 
@@ -379,17 +388,16 @@ void nsViewManager::ProcessPendingUpdate
       for (nsViewManager *vm = this; vm;
            vm = vm->mRootView->GetParent()
                   ? vm->mRootView->GetParent()->GetViewManager()
                   : nullptr) {
         if (vm->mDelayedResize != nsSize(NSCOORD_NONE, NSCOORD_NONE) &&
             vm->mRootView->IsEffectivelyVisible() &&
             mPresShell && mPresShell->IsVisible()) {
           vm->FlushDelayedResize(true);
-          vm->InvalidateView(vm->mRootView);
         }
       }
 
       NS_ASSERTION(aView->HasWidget(), "Must have a widget!");
 
 #ifdef MOZ_DUMP_PAINTING
       if (nsLayoutUtils::InvalidationDebuggingIsEnabled()) {
         printf("---- PAINT START ----PresShell(%p), nsView(%p), nsIWidget(%p)\n", mPresShell, aView, widget);
@@ -837,21 +845,16 @@ nsViewManager::InsertChild(nsView *aPare
           aParent->InsertChild(aChild, kid);
           ReparentWidgets(aChild, aParent);
         }
       }
 
       // if the parent view is marked as "floating", make the newly added view float as well.
       if (aParent->GetFloating())
         aChild->SetFloating(true);
-
-      //and mark this area as dirty if the view is visible...
-
-      if (nsViewVisibility_kHide != aChild->GetVisibility())
-        aChild->GetViewManager()->InvalidateView(aChild);
     }
 }
 
 void
 nsViewManager::InsertChild(nsView *aParent, nsView *aChild, int32_t aZIndex)
 {
   // no-one really calls this with anything other than aZIndex == 0 on a fresh view
   // XXX this method should simply be eliminated and its callers redirected to the real method
@@ -864,103 +867,35 @@ nsViewManager::RemoveChild(nsView *aChil
 {
   NS_ASSERTION(aChild, "aChild must not be null");
 
   nsView* parent = aChild->GetParent();
 
   if (nullptr != parent) {
     NS_ASSERTION(aChild->GetViewManager() == this ||
                  parent->GetViewManager() == this, "wrong view manager");
-    aChild->GetViewManager()->InvalidateView(aChild);
     parent->RemoveChild(aChild);
   }
 }
 
 void
 nsViewManager::MoveViewTo(nsView *aView, nscoord aX, nscoord aY)
 {
   NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
-  nsPoint oldPt = aView->GetPosition();
-  nsRect oldBounds = aView->GetBoundsInParentUnits();
   aView->SetPosition(aX, aY);
-
-  // only do damage control if the view is visible
-
-  if ((aX != oldPt.x) || (aY != oldPt.y)) {
-    if (aView->GetVisibility() != nsViewVisibility_kHide) {
-      nsView* parentView = aView->GetParent();
-      if (parentView) {
-        nsViewManager* parentVM = parentView->GetViewManager();
-        parentVM->InvalidateView(parentView, oldBounds);
-        parentVM->InvalidateView(parentView, aView->GetBoundsInParentUnits());
-      }
-    }
-  }
-}
-
-void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
-  nscoord aY1, nscoord aY2, bool aInCutOut) {
-  nscoord height = aY2 - aY1;
-  if (aRect.x < aCutOut.x) {
-    nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height);
-    InvalidateView(aView, r);
-  }
-  if (!aInCutOut && aCutOut.x < aCutOut.XMost()) {
-    nsRect r(aCutOut.x, aY1, aCutOut.width, height);
-    InvalidateView(aView, r);
-  }
-  if (aCutOut.XMost() < aRect.XMost()) {
-    nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height);
-    InvalidateView(aView, r);
-  }
-}
-
-void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut) {
-  NS_ASSERTION(aView->GetViewManager() == this,
-               "InvalidateRectDifference called on view we don't own");
-  if (aRect.y < aCutOut.y) {
-    InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aRect.y, aCutOut.y, false);
-  }
-  if (aCutOut.y < aCutOut.YMost()) {
-    InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.y, aCutOut.YMost(), true);
-  }
-  if (aCutOut.YMost() < aRect.YMost()) {
-    InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.YMost(), aRect.YMost(), false);
-  }
 }
 
 void
 nsViewManager::ResizeView(nsView *aView, const nsRect &aRect, bool aRepaintExposedAreaOnly)
 {
   NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
 
   nsRect oldDimensions = aView->GetDimensions();
   if (!oldDimensions.IsEqualEdges(aRect)) {
-    // resize the view.
-    // Prevent Invalidation of hidden views 
-    if (aView->GetVisibility() == nsViewVisibility_kHide) {
-      aView->SetDimensions(aRect, false);
-    } else {
-      nsView* parentView = aView->GetParent();
-      if (!parentView) {
-        parentView = aView;
-      }
-      nsRect oldBounds = aView->GetBoundsInParentUnits();
-      aView->SetDimensions(aRect, true);
-      nsViewManager* parentVM = parentView->GetViewManager();
-      if (!aRepaintExposedAreaOnly) {
-        // Invalidate the union of the old and new size
-        InvalidateView(aView, aRect);
-        parentVM->InvalidateView(parentView, oldBounds);
-      } else {
-        InvalidateRectDifference(aView, aRect, oldDimensions);
-        nsRect newBounds = aView->GetBoundsInParentUnits();
-        parentVM->InvalidateRectDifference(parentView, oldBounds, newBounds);
-      } 
-    }
+    aView->SetDimensions(aRect, true);
   }
 
   // Note that if layout resizes the view and the view has a custom clip
   // region set, then we expect layout to update the clip region too. Thus
   // in the case where mClipRect has been optimized away to just be a null
   // pointer, and this resize is implicitly changing the clip rect, it's OK
   // because layout will change it back again if necessary.
 }
@@ -975,31 +910,16 @@ nsViewManager::SetViewFloating(nsView *a
 
 void
 nsViewManager::SetViewVisibility(nsView *aView, nsViewVisibility aVisible)
 {
   NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
 
   if (aVisible != aView->GetVisibility()) {
     aView->SetVisibility(aVisible);
-
-    if (IsViewInserted(aView)) {
-      if (!aView->HasWidget()) {
-        if (nsViewVisibility_kHide == aVisible) {
-          nsView* parentView = aView->GetParent();
-          if (parentView) {
-            parentView->GetViewManager()->
-              InvalidateView(parentView, aView->GetBoundsInParentUnits());
-          }
-        }
-        else {
-          InvalidateView(aView);
-        }
-      }
-    }
   }
 }
 
 bool nsViewManager::IsViewInserted(nsView *aView)
 {
   if (mRootView == aView) {
     return true;
   } else if (aView->GetParent() == nullptr) {
@@ -1022,30 +942,21 @@ nsViewManager::SetViewZIndex(nsView *aVi
   NS_ASSERTION((aView != nullptr), "no view");
 
   // don't allow the root view's z-index to be changed. It should always be zero.
   // This could be removed and replaced with a style rule, or just removed altogether, with interesting consequences
   if (aView == mRootView) {
     return;
   }
 
-  bool oldTopMost = aView->IsTopMost();
-  bool oldIsAuto = aView->GetZIndexIsAuto();
-
   if (aAutoZIndex) {
     aZIndex = 0;
   }
 
-  int32_t oldidx = aView->GetZIndex();
   aView->SetZIndex(aAutoZIndex, aZIndex, aTopMost);
-
-  if (oldidx != aZIndex || oldTopMost != aTopMost ||
-      oldIsAuto != aAutoZIndex) {
-    InvalidateView(aView);
-  }
 }
 
 nsViewManager*
 nsViewManager::IncrementDisableRefreshCount()
 {
   if (!IsRootVM()) {
     return RootViewManager()->IncrementDisableRefreshCount();
   }
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -1381,16 +1381,20 @@ static void blinkRgn(RgnHandle rgn)
 // Invalidate this component's visible area
 NS_IMETHODIMP nsChildView::Invalidate(const nsIntRect &aRect)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   if (!mView || !mVisible)
     return NS_OK;
 
+  NS_ASSERTION(GetLayerManager()->GetBackendType() != LAYERS_CLIENT ||
+               Compositor::GetBackend() == LAYERS_BASIC,
+               "Shouldn't need to invalidate with accelerated OMTC layers!");
+
   if ([NSView focusView]) {
     // if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
     // don't lose it.
     [mView setNeedsPendingDisplayInRect:DevPixelsToCocoaPoints(aRect)];
   }
   else {
     [mView setNeedsDisplayInRect:DevPixelsToCocoaPoints(aRect)];
   }
--- a/xpcom/glue/Makefile.in
+++ b/xpcom/glue/Makefile.in
@@ -41,16 +41,17 @@ SDK_HEADERS = \
 		nsIClassInfoImpl.h \
 		nsID.h \
 		nsIInterfaceRequestorUtils.h \
 		nsINIParser.h \
 		nsISupportsImpl.h \
 		nsISupportsUtils.h \
 		nsIWeakReferenceUtils.h \
 		nsInterfaceHashtable.h \
+		nsJSThingHashtable.h \
 		nsMemory.h \
 		nsQuickSort.h \
 		nsRefPtrHashtable.h \
 		nsServiceManagerUtils.h \
 		nsStringAPI.h \
 		nsStringGlue.h \
 		nsTArray.h \
 		nsTArray-inl.h \
--- a/xpcom/glue/nsCycleCollectionParticipant.cpp
+++ b/xpcom/glue/nsCycleCollectionParticipant.cpp
@@ -64,39 +64,39 @@ CycleCollectionNoteEdgeNameImpl(nsCycleC
   nsAutoCString arrayEdgeName(aName);
   if (aFlags & CycleCollectionEdgeNameArrayFlag) {
     arrayEdgeName.AppendLiteral("[i]");
   }
   aCallback.NoteNextEdgeName(arrayEdgeName.get());
 }
 
 void
-TraceCallbackFunc::Trace(JS::Value* p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const
 {
-  mCallback(JSVAL_TO_TRACEABLE(*p), name, closure);
+  mCallback(JSVAL_TO_TRACEABLE(p->get()), name, closure);
 }
 
 void
-TraceCallbackFunc::Trace(jsid* p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<jsid>* p, const char* name, void* closure) const
 {
   void *thing = JSID_TO_GCTHING(*p);
   if (thing) {
     mCallback(thing, name, closure);
   }
 }
 
 void
-TraceCallbackFunc::Trace(JSObject** p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const
 {
   mCallback(*p, name, closure);
 }
 
 void
-TraceCallbackFunc::Trace(JSString** p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const
 {
   mCallback(*p, name, closure);
 }
 
 void
-TraceCallbackFunc::Trace(JSScript** p, const char* name, void* closure) const
+TraceCallbackFunc::Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const
 {
-   mCallback(*p, name, closure);
+  mCallback(p->get(), name, closure);
 }
--- a/xpcom/glue/nsCycleCollectionParticipant.h
+++ b/xpcom/glue/nsCycleCollectionParticipant.h
@@ -46,49 +46,49 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsCycleCol
 
 /**
  * Forward declarations
  */
 class nsCycleCollectionParticipant;
 class nsScriptObjectTracer;
 class nsXPCOMCycleCollectionParticipant;
 
+namespace JS {
+template <class T> class Heap;
+} /* namespace JS */
+
 /*
  * A struct defining pure virtual methods which are called when tracing cycle
  * collection paticipants.  The appropriate method is called depending on the
  * type of JS GC thing.
  */
 struct TraceCallbacks
 {
-    virtual void Trace(JS::Value* p, const char* name, void* closure) const = 0;
-    virtual void Trace(jsid* p, const char* name, void* closure) const = 0;
-    virtual void Trace(JSObject** p, const char* name, void* closure) const = 0;
-    virtual void Trace(JSString** p, const char* name, void* closure) const = 0;
-    virtual void Trace(JSScript** p, const char* name, void* closure) const = 0;
-
-    void Trace(JSFlatString** p, const char* name, void* closure) const {
-        Trace(reinterpret_cast<JSString**>(p), name, closure);
-    }
+    virtual void Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const = 0;
+    virtual void Trace(JS::Heap<jsid>* p, const char* name, void* closure) const = 0;
+    virtual void Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const = 0;
+    virtual void Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const = 0;
+    virtual void Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const = 0;
 };
 
 /*
  * An implementation of TraceCallbacks that calls a single function for all JS
  * GC thing types encountered.
  */
 struct TraceCallbackFunc : public TraceCallbacks
 {
     typedef void (* Func)(void* p, const char* name, void* closure);
 
     explicit TraceCallbackFunc(Func cb) : mCallback(cb) {}
 
-    virtual void Trace(JS::Value* p, const char* name, void* closure) const MOZ_OVERRIDE;
-    virtual void Trace(jsid* p, const char* name, void* closure) const MOZ_OVERRIDE;
-    virtual void Trace(JSObject** p, const char* name, void* closure) const MOZ_OVERRIDE;
-    virtual void Trace(JSString** p, const char* name, void* closure) const MOZ_OVERRIDE;
-    virtual void Trace(JSScript** p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<JS::Value>* p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<jsid>* p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<JSObject*>* p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<JSString*>* p, const char* name, void* closure) const MOZ_OVERRIDE;
+    virtual void Trace(JS::Heap<JSScript*>* p, const char* name, void* closure) const MOZ_OVERRIDE;
 
   private:
     Func mCallback;
 };
 
 /**
  * VTables
  *
new file mode 100644
--- /dev/null
+++ b/xpcom/glue/nsJSThingHashtable.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsJSThingHashtable_h__
+#define nsJSThingHashtable_h__
+
+#include "nsHashKeys.h"
+#include "nsBaseHashtable.h"
+
+namespace JS {
+template <class T>
+class Heap;
+} /* namespace JS */
+
+/**
+ * A wrapper for hash keys that sets ALLOW_MEMMOVE to false.
+ *
+ * This is used in the implementation of nsJSThingHashtable and is not intended
+ * to be used directly.
+ *
+ * It is necessary for hash tables containing JS::Heap<T> values as these must
+ * be copied rather than memmoved.
+ */
+template<class T>
+class nsHashKeyDisallowMemmove : public T
+{
+ public:
+  nsHashKeyDisallowMemmove(const T& key) : T(key) {}
+  enum { ALLOW_MEMMOVE = false };
+};
+
+
+/**
+ * Templated hashtable class for use on the heap where the values are JS GC things.
+ *
+ * Storing JS GC thing pointers on the heap requires wrapping them in a
+ * JS::Heap<T>, and this class takes care of that while presenting an interface
+ * in terms of the wrapped type T.
+ *
+ * For example, to store a hashtable mapping strings to JSObject pointers, you
+ * can declare a data member like this:
+ *
+ *   nsJSThingHashtable<nsStringHashKey, JSObject*> mStringToObjectMap;
+ *
+ * See nsBaseHashtable for complete declaration
+ * @param KeyClass a wrapper-class for the hashtable key, see nsHashKeys.h
+ *   for a complete specification.
+ * @param DataType the datatype being wrapped, must be a JS GC thing.
+ * @see nsInterfaceHashtable, nsClassHashtable
+ */
+template<class KeyClass,class DataType>
+class nsJSThingHashtable :
+  public nsBaseHashtable<nsHashKeyDisallowMemmove<KeyClass>, JS::Heap<DataType>, DataType>
+{ };
+
+#endif // nsJSThingHashtable_h__
--- a/xpcom/glue/nsTArray-inl.h
+++ b/xpcom/glue/nsTArray-inl.h
@@ -3,32 +3,32 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsTArray_h__
 #  error "Don't include this file directly"
 #endif
 
-template<class Alloc>
-nsTArray_base<Alloc>::nsTArray_base()
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::nsTArray_base()
   : mHdr(EmptyHdr()) {
   MOZ_COUNT_CTOR(nsTArray_base);
 }
 
-template<class Alloc>
-nsTArray_base<Alloc>::~nsTArray_base() {
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::~nsTArray_base() {
   if (mHdr != EmptyHdr() && !UsesAutoArrayBuffer()) {
     Alloc::Free(mHdr);
   }
   MOZ_COUNT_DTOR(nsTArray_base);
 }
 
-template<class Alloc>
-const nsTArrayHeader* nsTArray_base<Alloc>::GetAutoArrayBufferUnsafe(size_t elemAlign) const {
+template<class Alloc, class Copy>
+const nsTArrayHeader* nsTArray_base<Alloc, Copy>::GetAutoArrayBufferUnsafe(size_t elemAlign) const {
   // Assuming |this| points to an nsAutoArray, we want to get a pointer to
   // mAutoBuf.  So just cast |this| to nsAutoArray* and read &mAutoBuf!
 
   const void* autoBuf = &reinterpret_cast<const nsAutoArrayBase<nsTArray<uint32_t>, 1>*>(this)->mAutoBuf;
 
   // If we're on a 32-bit system and elemAlign is 8, we need to adjust our
   // pointer to take into account the extra alignment in the auto array.
 
@@ -43,18 +43,18 @@ const nsTArrayHeader* nsTArray_base<Allo
   NS_ABORT_IF_FALSE(elemAlign <= 4 || elemAlign == 8, "unsupported alignment.");
   if (sizeof(void*) == 4 && elemAlign == 8) {
     autoBuf = reinterpret_cast<const char*>(autoBuf) + 4;
   }
 
   return reinterpret_cast<const Header*>(autoBuf);
 }
 
-template<class Alloc>
-bool nsTArray_base<Alloc>::UsesAutoArrayBuffer() const {
+template<class Alloc, class Copy>
+bool nsTArray_base<Alloc, Copy>::UsesAutoArrayBuffer() const {
   if (!mHdr->mIsAutoArray) {
     return false;
   }
 
   // This is nuts.  If we were sane, we'd pass elemAlign as a parameter to
   // this function.  Unfortunately this function is called in nsTArray_base's
   // destructor, at which point we don't know elem_type's alignment.
   //
@@ -91,19 +91,19 @@ bool nsTArray_base<Alloc>::UsesAutoArray
                    reinterpret_cast<const char*>(GetAutoArrayBuffer(4));
   NS_ABORT_IF_FALSE(diff >= 0 && diff <= 4, "GetAutoArrayBuffer doesn't do what we expect.");
 #endif
 
   return mHdr == GetAutoArrayBuffer(4) || mHdr == GetAutoArrayBuffer(8);
 }
 
 
-template<class Alloc>
+template<class Alloc, class Copy>
 typename Alloc::ResultTypeProxy
-nsTArray_base<Alloc>::EnsureCapacity(size_type capacity, size_type elemSize) {
+nsTArray_base<Alloc, Copy>::EnsureCapacity(size_type capacity, size_type elemSize) {
   // This should be the most common case so test this first
   if (capacity <= mHdr->mCapacity)
     return Alloc::SuccessResult();
 
   // If the requested memory allocation exceeds size_type(-1)/2, then
   // our doubling algorithm may not be able to allocate it.
   // Additionally we couldn't fit in the Header::mCapacity
   // member. Just bail out in cases like that.  We don't want to be
@@ -150,23 +150,26 @@ nsTArray_base<Alloc>::EnsureCapacity(siz
     bytesToAlloc |= bytesToAlloc >> 16;
     bytesToAlloc++;
 
     MOZ_ASSERT((bytesToAlloc & (bytesToAlloc - 1)) == 0,
                "nsTArray's allocation size should be a power of two!");
   }
 
   Header *header;
-  if (UsesAutoArrayBuffer()) {
+  if (UsesAutoArrayBuffer() || !Copy::allowRealloc) {
     // Malloc() and copy
     header = static_cast<Header*>(Alloc::Malloc(bytesToAlloc));
     if (!header)
       return Alloc::FailureResult();
 
-    memcpy(header, mHdr, sizeof(Header) + Length() * elemSize);
+    Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize);
+
+    if (!UsesAutoArrayBuffer())
+      Alloc::Free(mHdr);
   } else {
     // Realloc() existing data
     header = static_cast<Header*>(Alloc::Realloc(mHdr, bytesToAlloc));
     if (!header)
       return Alloc::FailureResult();
   }
 
   // How many elements can we fit in bytesToAlloc?
@@ -174,33 +177,33 @@ nsTArray_base<Alloc>::EnsureCapacity(siz
   MOZ_ASSERT(newCapacity >= capacity, "Didn't enlarge the array enough!");
   header->mCapacity = newCapacity;
 
   mHdr = header;
 
   return Alloc::SuccessResult();
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 void
-nsTArray_base<Alloc>::ShrinkCapacity(size_type elemSize, size_t elemAlign) {
+nsTArray_base<Alloc, Copy>::ShrinkCapacity(size_type elemSize, size_t elemAlign) {
   if (mHdr == EmptyHdr() || UsesAutoArrayBuffer())
     return;
 
   if (mHdr->mLength >= mHdr->mCapacity)  // should never be greater than...
     return;
 
   size_type length = Length();
 
   if (IsAutoArray() && GetAutoArrayBuffer(elemAlign)->mCapacity >= length) {
     Header* header = GetAutoArrayBuffer(elemAlign);
 
     // Copy data, but don't copy the header to avoid overwriting mCapacity
     header->mLength = length;
-    memcpy(header + 1, mHdr + 1, length * elemSize);
+    Copy::CopyElements(header + 1, mHdr + 1, length, elemSize);
 
     Alloc::Free(mHdr);
     mHdr = header;
     return;
   }
 
   if (length == 0) {
     MOZ_ASSERT(!IsAutoArray(), "autoarray should have fit 0 elements");
@@ -212,19 +215,19 @@ nsTArray_base<Alloc>::ShrinkCapacity(siz
   size_type size = sizeof(Header) + length * elemSize;
   void *ptr = Alloc::Realloc(mHdr, size);
   if (!ptr)
     return;
   mHdr = static_cast<Header*>(ptr);
   mHdr->mCapacity = length;
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 void
-nsTArray_base<Alloc>::ShiftData(index_type start,
+nsTArray_base<Alloc, Copy>::ShiftData(index_type start,
                                 size_type oldLen, size_type newLen,
                                 size_type elemSize, size_t elemAlign) {
   if (oldLen == newLen)
     return;
 
   // Determine how many elements need to be shifted
   size_type num = mHdr->mLength - (start + oldLen);
 
@@ -235,88 +238,86 @@ nsTArray_base<Alloc>::ShiftData(index_ty
   } else {
     // Maybe nothing needs to be shifted
     if (num == 0)
       return;
     // Perform shift (change units to bytes first)
     start *= elemSize;
     newLen *= elemSize;
     oldLen *= elemSize;
-    num *= elemSize;
     char *base = reinterpret_cast<char*>(mHdr + 1) + start;
-    memmove(base + newLen, base + oldLen, num);
+    Copy::MoveElements(base + newLen, base + oldLen, num, elemSize);
   }
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 bool
-nsTArray_base<Alloc>::InsertSlotsAt(index_type index, size_type count,
+nsTArray_base<Alloc, Copy>::InsertSlotsAt(index_type index, size_type count,
                                     size_type elementSize, size_t elemAlign)  {
   MOZ_ASSERT(index <= Length(), "Bogus insertion index");
   size_type newLen = Length() + count;
 
   EnsureCapacity(newLen, elementSize);
 
   // Check for out of memory conditions
   if (Capacity() < newLen)
     return false;
 
   // Move the existing elements as needed.  Note that this will
   // change our mLength, so no need to call IncrementLength.
   ShiftData(index, 0, count, elementSize, elemAlign);
-      
+
   return true;
 }
 
 // nsTArray_base::IsAutoArrayRestorer is an RAII class which takes
 // |nsTArray_base &array| in its constructor.  When it's destructed, it ensures
 // that
 //
 //   * array.mIsAutoArray has the same value as it did when we started, and
 //   * if array has an auto buffer and mHdr would otherwise point to sEmptyHdr,
 //     array.mHdr points to array's auto buffer.
 
-template<class Alloc>
-nsTArray_base<Alloc>::IsAutoArrayRestorer::IsAutoArrayRestorer(
-  nsTArray_base<Alloc> &array,
-  size_t elemAlign) 
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::IsAutoArrayRestorer(
+  nsTArray_base<Alloc, Copy> &array,
+  size_t elemAlign)
   : mArray(array),
     mElemAlign(elemAlign),
     mIsAuto(array.IsAutoArray())
 {
 }
 
-template<class Alloc>
-nsTArray_base<Alloc>::IsAutoArrayRestorer::~IsAutoArrayRestorer() {
+template<class Alloc, class Copy>
+nsTArray_base<Alloc, Copy>::IsAutoArrayRestorer::~IsAutoArrayRestorer() {
   // Careful: We don't want to set mIsAutoArray = 1 on sEmptyHdr.
   if (mIsAuto && mArray.mHdr == mArray.EmptyHdr()) {
     // Call GetAutoArrayBufferUnsafe() because GetAutoArrayBuffer() asserts
     // that mHdr->mIsAutoArray is true, which surely isn't the case here.
     mArray.mHdr = mArray.GetAutoArrayBufferUnsafe(mElemAlign);
     mArray.mHdr->mLength = 0;
   }
   else if (mArray.mHdr != mArray.EmptyHdr()) {
     mArray.mHdr->mIsAutoArray = mIsAuto;
   }
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 template<class Allocator>
 typename Alloc::ResultTypeProxy
-nsTArray_base<Alloc>::SwapArrayElements(nsTArray_base<Allocator>& other,
-                                        size_type elemSize,
-                                        size_t elemAlign) {
+nsTArray_base<Alloc, Copy>::SwapArrayElements(nsTArray_base<Allocator, Copy>& other,
+                                              size_type elemSize, size_t elemAlign) {
 
   // EnsureNotUsingAutoArrayBuffer will set mHdr = sEmptyHdr even if we have an
   // auto buffer.  We need to point mHdr back to our auto buffer before we
   // return, otherwise we'll forget that we have an auto buffer at all!
   // IsAutoArrayRestorer takes care of this for us.
 
   IsAutoArrayRestorer ourAutoRestorer(*this, elemAlign);
-  typename nsTArray_base<Allocator>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign);
+  typename nsTArray_base<Allocator, Copy>::IsAutoArrayRestorer otherAutoRestorer(other, elemAlign);
 
   // If neither array uses an auto buffer which is big enough to store the
   // other array's elements, then ensure that both arrays use malloc'ed storage
   // and swap their mHdr pointers.
   if ((!UsesAutoArrayBuffer() || Capacity() < other.Length()) &&
       (!other.UsesAutoArrayBuffer() || other.Capacity() < Length())) {
 
     if (!EnsureNotUsingAutoArrayBuffer(elemSize) ||
@@ -326,17 +327,17 @@ nsTArray_base<Alloc>::SwapArrayElements(
 
     Header *temp = mHdr;
     mHdr = other.mHdr;
     other.mHdr = temp;
 
     return Alloc::SuccessResult();
   }
 
-  // Swap the two arrays using memcpy, since at least one is using an auto
+  // Swap the two arrays by copying, since at least one is using an auto
   // buffer which is large enough to hold all of the other's elements.  We'll
   // copy the shorter array into temporary storage.
   //
   // (We could do better than this in some circumstances.  Suppose we're
   // swapping arrays X and Y.  X has space for 2 elements in its auto buffer,
   // but currently has length 4, so it's using malloc'ed storage.  Y has length
   // 2.  When we swap X and Y, we don't need to use a temporary buffer; we can
   // write Y straight into X's auto buffer, write X's malloc'ed buffer on top
@@ -370,34 +371,34 @@ nsTArray_base<Alloc>::SwapArrayElements(
   // job for AutoTArray!  (One of the two arrays we're swapping is using an
   // auto buffer, so we're likely not allocating a lot of space here.  But one
   // could, in theory, allocate a huge AutoTArray on the heap.)
   nsAutoArrayBase<nsTArray_Impl<uint8_t, Alloc>, 64> temp;
   if (!Alloc::Successful(temp.EnsureCapacity(smallerLength, elemSize))) {
     return Alloc::FailureResult();
   }
 
-  memcpy(temp.Elements(), smallerElements, smallerLength * elemSize);
-  memcpy(smallerElements, largerElements, largerLength * elemSize);
-  memcpy(largerElements, temp.Elements(), smallerLength * elemSize);
+  Copy::CopyElements(temp.Elements(), smallerElements, smallerLength, elemSize);
+  Copy::CopyElements(smallerElements, largerElements, largerLength, elemSize);
+  Copy::CopyElements(largerElements, temp.Elements(), smallerLength, elemSize);
 
   // Swap the arrays' lengths.
   NS_ABORT_IF_FALSE((other.Length() == 0 || mHdr != EmptyHdr()) &&
                     (Length() == 0 || other.mHdr != EmptyHdr()),
                     "Don't set sEmptyHdr's length.");
   size_type tempLength = Length();
   mHdr->mLength = other.Length();
   other.mHdr->mLength = tempLength;
 
   return Alloc::SuccessResult();
 }
 
-template<class Alloc>
+template<class Alloc, class Copy>
 bool
-nsTArray_base<Alloc>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) {
+nsTArray_base<Alloc, Copy>::EnsureNotUsingAutoArrayBuffer(size_type elemSize) {
   if (UsesAutoArrayBuffer()) {
 
     // If you call this on a 0-length array, we'll set that array's mHdr to
     // sEmptyHdr, in flagrant violation of the nsAutoTArray invariants.  It's
     // up to you to set it back!  (If you don't, the nsAutoTArray will forget
     // that it has an auto buffer.)
     if (Length() == 0) {
       mHdr = EmptyHdr();
@@ -405,15 +406,15 @@ nsTArray_base<Alloc>::EnsureNotUsingAuto
     }
 
     size_type size = sizeof(Header) + Length() * elemSize;
 
     Header* header = static_cast<Header*>(Alloc::Malloc(size));
     if (!header)
       return false;
 
-    memcpy(header, mHdr, size);
+    Copy::CopyHeaderAndElements(header, mHdr, Length(), elemSize);
     header->mCapacity = Length();
     mHdr = header;
   }
-  
+
   return true;
 }
--- a/xpcom/glue/nsTArray.h
+++ b/xpcom/glue/nsTArray.h
@@ -17,16 +17,21 @@
 #include "nsCycleCollectionNoteChild.h"
 #include "nsAlgorithm.h"
 #include "nscore.h"
 #include "nsQuickSort.h"
 #include "nsDebug.h"
 #include "nsTraceRefcnt.h"
 #include NEW_H
 
+namespace JS {
+template <class T>
+class Heap;
+} /* namespace JS */
+
 //
 // nsTArray is a resizable array class, like std::vector.
 //
 // Unlike std::vector, which follows C++'s construction/destruction rules,
 // nsTArray assumes that your "T" can be memmoved()'ed safely.
 //
 // The public classes defined in this header are
 //
@@ -51,16 +56,19 @@
 // The template parameter (i.e., T in nsTArray<T>) specifies the type of the
 // elements and has the following requirements:
 //
 //   T MUST be safely memmove()'able.
 //   T MUST define a copy-constructor.
 //   T MAY define operator< for sorting.
 //   T MAY define operator== for searching.
 //
+// (Note that the memmove requirement may be relaxed for certain types - see
+// nsTArray_CopyElements below.)
+//
 // For methods taking a Comparator instance, the Comparator must be a class
 // defining the following methods:
 //
 //   class Comparator {
 //     public:
 //       /** @return True if the elements are equals; false otherwise. */
 //       bool Equals(const elem_type& a, const Item& b) const;
 //
@@ -337,23 +345,23 @@ struct nsTArray_SafeElementAtHelper<nsRe
 {
 };
 
 //
 // This class serves as a base class for nsTArray.  It shouldn't be used
 // directly.  It holds common implementation code that does not depend on the
 // element type of the nsTArray.
 //
-template<class Alloc>
+template<class Alloc, class Copy>
 class nsTArray_base
 {
   // Allow swapping elements with |nsTArray_base|s created using a
   // different allocator.  This is kosher because all allocators use
   // the same free().
-  template<class Allocator>
+  template<class Allocator, class Copier>
   friend class nsTArray_base;
 
 protected:
   typedef nsTArrayHeader Header;
 
 public:
   typedef uint32_t size_type;
   typedef uint32_t index_type;
@@ -391,17 +399,17 @@ protected:
   // @param elemSize     The size of an array element.
   // @return False if insufficient memory is available; true otherwise.
   typename Alloc::ResultTypeProxy EnsureCapacity(size_type capacity, size_type elemSize);
 
   // Resize the storage to the minimum required amount.
   // @param elemSize     The size of an array element.
   // @param elemAlign    The alignment in bytes of an array element.
   void ShrinkCapacity(size_type elemSize, size_t elemAlign);
-    
+
   // This method may be called to resize a "gap" in the array by shifting
   // elements around.  It updates mLength appropriately.  If the resulting
   // array has zero elements, then the array's memory is free'd.
   // @param start        The starting index of the gap.
   // @param oldLen       The current length of the gap.
   // @param newLen       The desired length of the gap.
   // @param elemSize     The size of an array element.
   // @param elemAlign    The alignment in bytes of an array element.
@@ -430,28 +438,28 @@ protected:
   // @param elementSize the size of an array element.
   // @param elemAlign the alignment in bytes of an array element.
   bool InsertSlotsAt(index_type index, size_type count,
                        size_type elementSize, size_t elemAlign);
 
 protected:
   template<class Allocator>
   typename Alloc::ResultTypeProxy
-  SwapArrayElements(nsTArray_base<Allocator>& other,
+  SwapArrayElements(nsTArray_base<Allocator, Copy>& other,
                     size_type elemSize,
                     size_t elemAlign);
 
   // This is an RAII class used in SwapArrayElements.
   class IsAutoArrayRestorer {
     public:
-      IsAutoArrayRestorer(nsTArray_base<Alloc> &array, size_t elemAlign);
+      IsAutoArrayRestorer(nsTArray_base<Alloc, Copy> &array, size_t elemAlign);
       ~IsAutoArrayRestorer();
 
     private:
-      nsTArray_base<Alloc> &mArray;
+      nsTArray_base<Alloc, Copy> &mArray;
       size_t mElemAlign;
       bool mIsAuto;
   };
 
   // Helper function for SwapArrayElements. Ensures that if the array
   // is an nsAutoTArray that it doesn't use the built-in buffer.
   bool EnsureNotUsingAutoArrayBuffer(size_type elemSize);
 
@@ -468,30 +476,30 @@ protected:
   const Header* GetAutoArrayBuffer(size_t elemAlign) const {
     MOZ_ASSERT(IsAutoArray(), "Should be an auto array to call this");
     return GetAutoArrayBufferUnsafe(elemAlign);
   }
 
   // Returns a Header for the built-in buffer of this nsAutoTArray, but doesn't
   // assert that we are an nsAutoTArray.
   Header* GetAutoArrayBufferUnsafe(size_t elemAlign) {
-    return const_cast<Header*>(static_cast<const nsTArray_base<Alloc>*>(this)->
+    return const_cast<Header*>(static_cast<const nsTArray_base<Alloc, Copy>*>(this)->
                                GetAutoArrayBufferUnsafe(elemAlign));
   }
   const Header* GetAutoArrayBufferUnsafe(size_t elemAlign) const;
 
   // Returns true if this is an nsAutoTArray and it currently uses the
   // built-in buffer to store its elements.
   bool UsesAutoArrayBuffer() const;
 
   // The array's elements (prefixed with a Header).  This pointer is never
   // null.  If the array is empty, then this will point to sEmptyHdr.
   Header *mHdr;
 
-  Header* Hdr() const { 
+  Header* Hdr() const {
     return mHdr;
   }
 
   Header** PtrToHdr() {
     return &mHdr;
   }
 
   static Header* EmptyHdr() {
@@ -559,38 +567,172 @@ struct AssignRangeAlgorithm<true, true> 
   template<class Item, class ElemType, class IndexType, class SizeType>
   static void implementation(ElemType* elements, IndexType start,
                              SizeType count, const Item *values) {
     memcpy(elements + start, values, count * sizeof(ElemType));
   }
 };
 
 //
+// Normally elements are copied with memcpy and memmove, but for some element
+// types that is problematic.  The nsTArray_CopyElements template class can be
+// specialized to ensure that copying calls constructors and destructors
+// instead, as is done below for JS::Heap<E> elements.
+//
+
+//
+// A class that defines how to copy elements using memcpy/memmove.
+//
+struct nsTArray_CopyWithMemutils
+{
+  const static bool allowRealloc = true;
+
+  static void CopyElements(void* dest, const void* src, size_t count, size_t elemSize) {
+    memcpy(dest, src, count * elemSize);
+  }
+
+  static void CopyHeaderAndElements(void* dest, const void* src, size_t count, size_t elemSize) {
+    memcpy(dest, src, sizeof(nsTArrayHeader) + count * elemSize);
+  }
+
+  static void MoveElements(void* dest, const void* src, size_t count, size_t elemSize) {
+    memmove(dest, src, count * elemSize);
+  }
+};
+
+//
+// A template class that defines how to copy elements calling their constructors
+// and destructors appropriately.
+//
+template <class ElemType>
+struct nsTArray_CopyWithConstructors
+{
+  typedef nsTArrayElementTraits<ElemType> traits;
+
+  const static bool allowRealloc = false;
+
+  static void CopyElements(void* dest, void* src, size_t count, size_t elemSize) {
+    ElemType* destElem = static_cast<ElemType*>(dest);
+    ElemType* srcElem = static_cast<ElemType*>(src);
+    ElemType* destElemEnd = destElem + count;
+#ifdef DEBUG
+    ElemType* srcElemEnd = srcElem + count;
+    MOZ_ASSERT(srcElemEnd <= destElem || srcElemEnd > destElemEnd);
+#endif
+    while (destElem != destElemEnd) {
+      traits::Construct(destElem, *srcElem);
+      traits::Destruct(srcElem);
+      ++destElem;
+      ++srcElem;
+    }
+  }
+
+  static void CopyHeaderAndElements(void* dest, void* src, size_t count, size_t elemSize) {
+    nsTArrayHeader* destHeader = static_cast<nsTArrayHeader*>(dest);
+    nsTArrayHeader* srcHeader = static_cast<nsTArrayHeader*>(src);
+    *destHeader = *srcHeader;
+    CopyElements(static_cast<uint8_t*>(dest) + sizeof(nsTArrayHeader),
+                 static_cast<uint8_t*>(src) + sizeof(nsTArrayHeader),
+                 count, elemSize);
+  }
+
+  static void MoveElements(void* dest, void* src, size_t count, size_t elemSize) {
+    ElemType* destElem = static_cast<ElemType*>(dest);
+    ElemType* srcElem = static_cast<ElemType*>(src);
+    ElemType* destElemEnd = destElem + count;
+    ElemType* srcElemEnd = srcElem + count;
+    if (destElem == srcElem) {
+      return;  // In practice, we don't do this.
+    } else if (srcElemEnd > destElem && srcElemEnd < destElemEnd) {
+      while (destElemEnd != destElem) {
+        --destElemEnd;
+        --srcElemEnd;
+        traits::Construct(destElemEnd, *srcElemEnd);
+        traits::Destruct(srcElem);
+      }
+    } else {
+      CopyElements(dest, src, count, elemSize);
+    }
+  }
+};
+
+//
+// The default behaviour is to use memcpy/memmove for everything.
+//
+template <class E>
+struct nsTArray_CopyElements : public nsTArray_CopyWithMemutils {};
+
+//
+// JS::Heap<E> elements require constructors/destructors to be called and so is
+// specialized here.
+//
+template <class E>
+struct nsTArray_CopyElements<JS::Heap<E> > : public nsTArray_CopyWithConstructors<E> {};
+
+//
+// Base class for nsTArray_Impl that is templated on element type and derived
+// nsTArray_Impl class, to allow extra conversions to be added for specific
+// types.
+//
+template <class E, class Derived>
+struct nsTArray_TypedBase : public nsTArray_SafeElementAtHelper<E, Derived> {};
+
+//
+// Specialization of nsTArray_TypedBase for arrays containing JS::Heap<E>
+// elements.
+//
+// These conversions are safe because JS::Heap<E> and E share the same
+// representation, and since the result of the conversions are const references
+// we won't miss any barriers.
+//
+// The static_cast is necessary to obtain the correct address for the derived
+// class since we are a base class used in multiple inheritance.
+//
+template <class E, class Derived>
+struct nsTArray_TypedBase<JS::Heap<E>, Derived>
+ : public nsTArray_SafeElementAtHelper<JS::Heap<E>, Derived>
+{
+  operator const nsTArray<E>& () {
+    MOZ_STATIC_ASSERT(sizeof(E) == sizeof(JS::Heap<E>),
+                      "JS::Heap<E> must be binary compatible with E.");
+    Derived* self = static_cast<Derived*>(this);
+    return *reinterpret_cast<nsTArray<E> *>(self);
+  }
+
+  operator const FallibleTArray<E>& () {
+    Derived* self = static_cast<Derived*>(this);
+    return *reinterpret_cast<FallibleTArray<E> *>(self);
+  }
+};
+
+
+//
 // nsTArray_Impl contains most of the guts supporting nsTArray, FallibleTArray,
 // nsAutoTArray, and AutoFallibleTArray.
 //
 // The only situation in which you might need to use nsTArray_Impl in your code
 // is if you're writing code which mutates a TArray which may or may not be
 // infallible.
 //
 // Code which merely reads from a TArray which may or may not be infallible can
 // simply cast the TArray to |const nsTArray&|; both fallible and infallible
 // TArrays can be cast to |const nsTArray&|.
 //
 template<class E, class Alloc>
-class nsTArray_Impl : public nsTArray_base<Alloc>,
-                      public nsTArray_SafeElementAtHelper<E, nsTArray_Impl<E, Alloc> >
+class nsTArray_Impl : public nsTArray_base<Alloc, nsTArray_CopyElements<E> >,
+                      public nsTArray_TypedBase<E, nsTArray_Impl<E, Alloc> >
 {
 public:
-  typedef nsTArray_base<Alloc>           base_type;
-  typedef typename base_type::size_type  size_type;
-  typedef typename base_type::index_type index_type;
-  typedef E                              elem_type;
-  typedef nsTArray_Impl<E, Alloc>        self_type;
-  typedef nsTArrayElementTraits<E>       elem_traits;
+  typedef nsTArray_CopyElements<E>                   copy_type;
+  typedef nsTArray_base<Alloc, copy_type>            base_type;
+  typedef typename base_type::size_type              size_type;
+  typedef typename base_type::index_type             index_type;
+  typedef E                                          elem_type;
+  typedef nsTArray_Impl<E, Alloc>                    self_type;
+  typedef nsTArrayElementTraits<E>                   elem_traits;
   typedef nsTArray_SafeElementAtHelper<E, self_type> safeelementat_helper_type;
 
   using safeelementat_helper_type::SafeElementAt;
   using base_type::EmptyHdr;
 
   // A special value that is used to indicate an invalid or unknown index
   // into the array.
   enum {
@@ -711,17 +853,17 @@ public:
   }
 
   // This method provides direct, readonly access to the array elements.
   // @return A pointer to the first element of the array.  If the array is
   // empty, then this pointer must not be dereferenced.
   const elem_type* Elements() const {
     return reinterpret_cast<const elem_type *>(Hdr() + 1);
   }
-    
+
   // This method provides direct access to the i'th element of the array.
   // The given index must be within the array bounds.
   // @param i  The index of an element in the array.
   // @return   A reference to the i'th element of the array.
   elem_type& ElementAt(index_type i) {
     MOZ_ASSERT(i < Length(), "invalid array index");
     return Elements()[i];
   }
@@ -1078,28 +1220,28 @@ public:
 
   // Append a new element without copy-constructing. This is useful to avoid
   // temporaries.
   // @return A pointer to the newly appended element, or null on OOM.
   elem_type *AppendElement() {
     return AppendElements(1);
   }
 
-  // Move all elements from another array to the end of this array without 
+  // Move all elements from another array to the end of this array without
   // calling copy constructors or destructors.
   // @return A pointer to the newly appended elements, or null on OOM.
   template<class Item, class Allocator>
   elem_type *MoveElementsFrom(nsTArray_Impl<Item, Allocator>& array) {
     MOZ_ASSERT(&array != this, "argument must be different array");
     index_type len = Length();
     index_type otherLen = array.Length();
     if (!Alloc::Successful(this->EnsureCapacity(len + otherLen, sizeof(elem_type))))
       return nullptr;
-    memcpy(Elements() + len, array.Elements(), otherLen * sizeof(elem_type));
-    this->IncrementLength(otherLen);      
+    copy_type::CopyElements(Elements() + len, array.Elements(), otherLen, sizeof(elem_type));
+    this->IncrementLength(otherLen);
     array.ShiftData(0, otherLen, 0, sizeof(elem_type), MOZ_ALIGNOF(elem_type));
     return Elements() + len;
   }
 
   // This method removes a range of elements from this array.
   // @param start  The starting index of the elements to remove.
   // @param count  The number of elements to remove.
   void RemoveElementsAt(index_type start, size_type count) {
@@ -1196,17 +1338,17 @@ public:
   // @return        True if the operation succeeded; false otherwise.
   // See also TruncateLength if the new length is guaranteed to be
   // smaller than the old.
   bool SetLength(size_type newLen) {
     size_type oldLen = Length();
     if (newLen > oldLen) {
       return InsertElementsAt(oldLen, newLen - oldLen) != nullptr;
     }
-      
+
     TruncateLength(newLen);
     return true;
   }
 
   // This method modifies the length of the array, but may only be
   // called when the new length is shorter than the old.  It can
   // therefore be called when elem_type has no default constructor,
   // unlike SetLength.  It removes elements from the array (see also
@@ -1278,17 +1420,17 @@ typename Alloc::ResultType EnsureLengthA
   // This method may be called to minimize the memory used by this array.
   void Compact() {
     ShrinkCapacity(sizeof(elem_type), MOZ_ALIGNOF(elem_type));
   }
 
   //
   // Sorting
   //
- 
+
   // This function is meant to be used with the NS_QuickSort function.  It
   // maps the callback API expected by NS_QuickSort to the Comparator API
   // used by nsTArray_Impl.  See nsTArray_Impl::Sort.
   template<class Comparator>
   static int Compare(const void* e1, const void* e2, void *data) {
     const Comparator* c = reinterpret_cast<const Comparator*>(data);
     const elem_type* a = static_cast<const elem_type*>(e1);
     const elem_type* b = static_cast<const elem_type*>(e2);
@@ -1391,18 +1533,18 @@ protected:
     elem_type *iter = Elements() + start, *end = iter + count;
     for (; iter != end; ++iter) {
       elem_traits::Destruct(iter);
     }
   }
 
   // This method invokes elem_type's copy-constructor on a range of elements.
   // @param start   The index of the first element to construct.
-  // @param count   The number of elements to construct. 
-  // @param values  The array of elements to copy. 
+  // @param count   The number of elements to construct.
+  // @param values  The array of elements to copy.
   template<class Item>
   void AssignRange(index_type start, size_type count,
                    const Item *values) {
     AssignRangeAlgorithm<mozilla::IsPod<Item>::value,
                          mozilla::IsSame<Item, elem_type>::value>
       ::implementation(Elements(), start, count, values);
   }
 
@@ -1528,17 +1670,17 @@ protected:
   nsAutoArrayBase(const TArrayBase &aOther) {
     Init();
     AppendElements(aOther);
   }
 
 private:
   // nsTArray_base casts itself as an nsAutoArrayBase in order to get a pointer
   // to mAutoBuf.
-  template<class Allocator>
+  template<class Allocator, class Copier>
   friend class nsTArray_base;
 
   void Init() {
     MOZ_STATIC_ASSERT(MOZ_ALIGNOF(elem_type) <= 8,
                       "can't handle alignments greater than 8, "
                       "see nsTArray_base::UsesAutoArrayBuffer()");
     // Temporary work around for VS2012 RC compiler crash
     Header** phdr = base_type::PtrToHdr();