Merge m-c to fx-team
authorWes Kocher <wkocher@mozilla.com>
Wed, 11 Jun 2014 18:52:12 -0700
changeset 188331 354a7e95abb84cdb6b25e85a8b70da8b610016c7
parent 188330 abfe8ef3a851249b32542b3575db3e6a5ed90f90 (current diff)
parent 188273 9e8e3e903484f1986d18f07aba2a60d226241d70 (diff)
child 188332 4bfd917b48393172dbce48302f8c32d5f393e976
push id26951
push useremorley@mozilla.com
push dateThu, 12 Jun 2014 14:07:43 +0000
treeherdermozilla-central@4f98802de6ce [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone33.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 m-c to fx-team
dom/base/ObjectWrapper.jsm
--- a/accessible/src/jsat/ContentControl.jsm
+++ b/accessible/src/jsat/ContentControl.jsm
@@ -208,17 +208,18 @@ this.ContentControl.prototype = {
         // Keys will typically have a sound of their own.
         this._contentScope.get().sendAsyncMessage('AccessFu:Present',
           Presentation.actionInvoked(aAccessible, 'click'));
       }
     };
 
     let focusedAcc = Utils.AccRetrieval.getAccessibleFor(
       this.document.activeElement);
-    if (focusedAcc && focusedAcc.role === Roles.ENTRY) {
+    if (focusedAcc && this.vc.position === focusedAcc
+        && focusedAcc.role === Roles.ENTRY) {
       let accText = focusedAcc.QueryInterface(Ci.nsIAccessibleText);
       let oldOffset = accText.caretOffset;
       let newOffset = aMessage.json.offset;
       let text = accText.getText(0, accText.characterCount);
 
       if (newOffset >= 0 && newOffset <= accText.characterCount) {
         accText.caretOffset = newOffset;
       }
--- a/browser/devtools/webconsole/test/browser_webconsole_output_04.js
+++ b/browser/devtools/webconsole/test/browser_webconsole_output_04.js
@@ -45,17 +45,17 @@ let inputTests = [
     inspectable: true,
     variablesViewLabel: "TypeError",
   },
 
   // 4
   {
     input: "testDOMException()",
     output: 'DOMException [SyntaxError: "An invalid or illegal string was specified"',
-    printOutput: '[object XrayWrapper [object DOMException]]"',
+    printOutput: '"SyntaxError: An invalid or illegal string was specified"',
     inspectable: true,
     variablesViewLabel: "SyntaxError",
   },
 
   // 5
   {
     input: "testCSSStyleDeclaration()",
     output: 'CSS2Properties { color: "green", font-size: "2em" }',
--- a/content/base/public/nsIDocument.h
+++ b/content/base/public/nsIDocument.h
@@ -2082,19 +2082,20 @@ public:
   virtual void EnqueueLifecycleCallback(ElementCallbackType aType,
                                         Element* aCustomElement,
                                         mozilla::dom::LifecycleCallbackArgs* aArgs = nullptr,
                                         mozilla::dom::CustomElementDefinition* aDefinition = nullptr) = 0;
   virtual void SwizzleCustomElement(Element* aElement,
                                     const nsAString& aTypeExtension,
                                     uint32_t aNamespaceID,
                                     mozilla::ErrorResult& rv) = 0;
-  virtual JSObject*
+  virtual void
     RegisterElement(JSContext* aCx, const nsAString& aName,
                     const mozilla::dom::ElementRegistrationOptions& aOptions,
+                    JS::MutableHandle<JSObject*> aRetval,
                     mozilla::ErrorResult& rv) = 0;
 
   /**
    * In some cases, new document instances must be associated with
    * an existing web components custom element registry as specified.
    */
   virtual void UseRegistryFromDocument(nsIDocument* aDocument) = 0;
 
--- a/content/base/public/nsINode.h
+++ b/content/base/public/nsINode.h
@@ -1612,22 +1612,24 @@ public:
     } else {
       aLocalName.SetStringBuffer(nsStringBuffer::FromString(localName),
                                  localName.Length());
     }
   }
   // HasAttributes is defined inline in Element.h.
   bool HasAttributes() const;
   nsDOMAttributeMap* GetAttributes();
-  JS::Value SetUserData(JSContext* aCx, const nsAString& aKey,
-                        JS::Handle<JS::Value> aData,
-                        nsIDOMUserDataHandler* aHandler,
-                        mozilla::ErrorResult& aError);
-  JS::Value GetUserData(JSContext* aCx, const nsAString& aKey,
-                        mozilla::ErrorResult& aError);
+  void SetUserData(JSContext* aCx, const nsAString& aKey,
+                   JS::Handle<JS::Value> aData,
+                   nsIDOMUserDataHandler* aHandler,
+                   JS::MutableHandle<JS::Value> aRetval,
+                   mozilla::ErrorResult& aError);
+  void GetUserData(JSContext* aCx, const nsAString& aKey,
+                   JS::MutableHandle<JS::Value> aRetval,
+                   mozilla::ErrorResult& aError);
 
   // Helper method to remove this node from its parent. This is not exposed
   // through WebIDL.
   // Only call this if the node has a parent node.
   nsresult RemoveFromParent()
   {
     nsINode* parent = GetParentNode();
     mozilla::ErrorResult rv;
--- a/content/base/src/FileIOObject.cpp
+++ b/content/base/src/FileIOObject.cpp
@@ -20,36 +20,33 @@ namespace dom {
 
 const uint64_t kUnknownSize = uint64_t(-1);
 
 NS_IMPL_ADDREF_INHERITED(FileIOObject, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(FileIOObject, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(FileIOObject)
   NS_INTERFACE_MAP_ENTRY(nsITimerCallback)
-  NS_INTERFACE_MAP_ENTRY(nsIStreamListener)
-  NS_INTERFACE_MAP_ENTRY(nsIRequestObserver)
+  NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(FileIOObject)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(FileIOObject,
                                                   DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mProgressNotifier)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mError)
-  // Can't traverse mChannel because it's a multithreaded object.
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(FileIOObject,
                                                 DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mProgressNotifier)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mError)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
- 
+
 NS_IMPL_EVENT_HANDLER(FileIOObject, abort)
 NS_IMPL_EVENT_HANDLER(FileIOObject, error)
 NS_IMPL_EVENT_HANDLER(FileIOObject, progress)
 
 FileIOObject::FileIOObject()
   : mProgressEventWasDelayed(false),
     mTimerIsActive(false),
     mReadyState(0),
@@ -136,88 +133,92 @@ FileIOObject::Notify(nsITimer* aTimer)
     NS_ENSURE_SUCCESS(rv, rv);
 
     StartProgressEventTimer();
   }
 
   return NS_OK;
 }
 
-// nsIStreamListener
+// InputStreamCallback
 NS_IMETHODIMP
-FileIOObject::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
-{
-  return DoOnStartRequest(aRequest, aContext);
-}
-
-NS_IMETHODIMP
-FileIOObject::DoOnStartRequest(nsIRequest *request, nsISupports *ctxt)
+FileIOObject::OnInputStreamReady(nsIAsyncInputStream* aStream)
 {
-  return NS_OK;
-}
+  if (mReadyState != 1 || aStream != mAsyncStream) {
+    return NS_OK;
+  }
+
+  uint64_t aCount;
+  nsresult rv = aStream->Available(&aCount);
+
+  if (NS_SUCCEEDED(rv) && aCount) {
+    rv = DoReadData(aStream, aCount);
+  }
 
-NS_IMETHODIMP
-FileIOObject::OnDataAvailable(nsIRequest *aRequest,
-                              nsISupports *aContext,
-                              nsIInputStream *aInputStream,
-                              uint64_t aOffset,
-                              uint32_t aCount)
-{
-  nsresult rv;
-  rv = DoOnDataAvailable(aRequest, aContext, aInputStream, aOffset, aCount);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_SUCCEEDED(rv)) {
+    rv = DoAsyncWait(aStream);
+  }
+
+  if (!aCount || NS_FAILED(rv)) {
+    if (rv == NS_BASE_STREAM_CLOSED) {
+      rv = NS_OK;
+    }
+    return OnLoadEnd(rv);
+  }
 
   mTransferred += aCount;
 
   //Notify the timer is the appropriate timeframe has passed
   if (mTimerIsActive) {
     mProgressEventWasDelayed = true;
   } else {
     rv = DispatchProgressEvent(NS_LITERAL_STRING(PROGRESS_STR));
     NS_ENSURE_SUCCESS(rv, rv);
 
     StartProgressEventTimer();
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-FileIOObject::OnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
-                            nsresult aStatus)
+nsresult
+FileIOObject::OnLoadEnd(nsresult aStatus)
 {
-  // If we're here as a result of a call from Abort(),
-  // simply ignore the request.
-  if (aRequest != mChannel)
-    return NS_OK;
-
   // Cancel the progress event timer
   ClearProgressEventTimer();
 
   // FileIOObject must be in DONE stage after an operation
   mReadyState = 2;
 
   nsString successEvent, termEvent;
-  nsresult rv = DoOnStopRequest(aRequest, aContext, aStatus,
-                                successEvent, termEvent);
+  nsresult rv = DoOnLoadEnd(aStatus, successEvent, termEvent);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Set the status field as appropriate
   if (NS_FAILED(aStatus)) {
     DispatchError(aStatus, termEvent);
     return NS_OK;
   }
 
   // Dispatch event to signify end of a successful operation
   DispatchProgressEvent(successEvent);
   DispatchProgressEvent(termEvent);
 
   return NS_OK;
 }
 
+nsresult
+FileIOObject::DoAsyncWait(nsIAsyncInputStream* aStream)
+{
+  return aStream->AsyncWait(this,
+                            /* aFlags*/ 0,
+                            /* aRequestedCount */ 0,
+                            NS_GetCurrentThread());
+}
+
 void
 FileIOObject::Abort(ErrorResult& aRv)
 {
   if (mReadyState != 1) {
     // XXX The spec doesn't say this
     aRv.Throw(NS_ERROR_DOM_FILE_ABORT_ERR);
     return;
   }
--- a/content/base/src/FileIOObject.h
+++ b/content/base/src/FileIOObject.h
@@ -2,39 +2,38 @@
 /* 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 FileIOObject_h__
 #define FileIOObject_h__
 
 #include "mozilla/DOMEventTargetHelper.h"
-#include "nsIChannel.h"
 #include "nsIFile.h"
 #include "nsIDOMFile.h"
-#include "nsIStreamListener.h"
 #include "nsITimer.h"
 #include "nsCOMPtr.h"
+#include "nsIAsyncInputStream.h"
 
 #include "mozilla/dom/DOMError.h"
 
 #define NS_PROGRESS_EVENT_INTERVAL 50
 
 namespace mozilla {
 
 class ErrorResult;
 
 namespace dom {
 
 extern const uint64_t kUnknownSize;
 
 // A common base class for FileReader and FileSaver
 
 class FileIOObject : public DOMEventTargetHelper,
-                     public nsIStreamListener,
+                     public nsIInputStreamCallback,
                      public nsITimerCallback
 {
 public:
   FileIOObject();
 
   NS_DECL_ISUPPORTS_INHERITED
 
   // Common methods
@@ -56,48 +55,44 @@ public:
   NS_METHOD SetOnprogress(JSContext* aCx, JS::Handle<JS::Value> aValue);
 
   IMPL_EVENT_HANDLER(abort)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(progress)
 
   NS_DECL_NSITIMERCALLBACK
 
-  NS_DECL_NSISTREAMLISTENER
-
-  NS_DECL_NSIREQUESTOBSERVER
+  NS_DECL_NSIINPUTSTREAMCALLBACK
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FileIOObject, DOMEventTargetHelper)
 
 protected:
   // Implemented by the derived class to do whatever it needs to do for abort
   virtual void DoAbort(nsAString& aEvent) = 0;
-  // for onStartRequest (this has a default impl since FileReader doesn't need
-  // special handling
-  NS_IMETHOD DoOnStartRequest(nsIRequest *aRequest, nsISupports *aContext);
-  // for onStopRequest
-  NS_IMETHOD DoOnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
-                             nsresult aStatus, nsAString& aSuccessEvent,
-                             nsAString& aTerminationEvent) = 0;
-  // and for onDataAvailable
-  NS_IMETHOD DoOnDataAvailable(nsIRequest *aRequest, nsISupports *aContext,
-                               nsIInputStream *aInputStream, uint64_t aOffset,
-                               uint32_t aCount) = 0;
+
+  virtual nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) = 0;
+
+  virtual nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent,
+                               nsAString& aTerminationEvent) = 0;
+
+  nsresult OnLoadEnd(nsresult aStatus);
+  nsresult DoAsyncWait(nsIAsyncInputStream* aStream);
 
   void StartProgressEventTimer();
   void ClearProgressEventTimer();
   void DispatchError(nsresult rv, nsAString& finalEvent);
   nsresult DispatchProgressEvent(const nsAString& aType);
 
   nsCOMPtr<nsITimer> mProgressNotifier;
   bool mProgressEventWasDelayed;
   bool mTimerIsActive;
 
+  nsCOMPtr<nsIAsyncInputStream> mAsyncStream;
+
   nsRefPtr<DOMError> mError;
-  nsCOMPtr<nsIChannel> mChannel;
 
   uint16_t mReadyState;
 
   uint64_t mTotal;
   uint64_t mTransferred;
 };
 
 } // namespace dom
--- a/content/base/src/nsDOMFileReader.cpp
+++ b/content/base/src/nsDOMFileReader.cpp
@@ -5,83 +5,66 @@
 
 #include "nsDOMFileReader.h"
 
 #include "nsContentCID.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsDOMFile.h"
 #include "nsError.h"
-#include "nsIConverterInputStream.h"
-#include "nsIDocument.h"
 #include "nsIFile.h"
-#include "nsIFileStreams.h"
-#include "nsIInputStream.h"
-#include "nsIMIMEService.h"
-#include "nsIUnicodeDecoder.h"
 #include "nsNetCID.h"
 #include "nsNetUtil.h"
 
-#include "nsLayoutCID.h"
-#include "nsXPIDLString.h"
-#include "nsReadableUtils.h"
-#include "nsIURI.h"
-#include "nsStreamUtils.h"
 #include "nsXPCOM.h"
 #include "nsIDOMEventListener.h"
 #include "nsJSEnvironment.h"
-#include "nsIScriptGlobalObject.h"
-#include "nsCExternalHandlerService.h"
-#include "nsIStreamConverterService.h"
 #include "nsCycleCollectionParticipant.h"
-#include "nsIScriptObjectPrincipal.h"
-#include "nsHostObjectProtocolHandler.h"
 #include "mozilla/Base64.h"
-#include "mozilla/DOMEventTargetHelper.h"
-#include "mozilla/Preferences.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "mozilla/dom/FileReaderBinding.h"
 #include "xpcpublic.h"
-#include "nsIScriptSecurityManager.h"
 #include "nsDOMJSUtils.h"
 
 #include "jsfriendapi.h"
 
+#include "nsITransport.h"
+#include "nsIStreamTransportService.h"
+
 using namespace mozilla;
 using namespace mozilla::dom;
 
 #define LOAD_STR "load"
 #define LOADSTART_STR "loadstart"
 #define LOADEND_STR "loadend"
 
+static NS_DEFINE_CID(kStreamTransportServiceCID, NS_STREAMTRANSPORTSERVICE_CID);
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMFileReader)
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDOMFileReader,
                                                   FileIOObject)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFile)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDOMFileReader,
                                                 FileIOObject)
   tmp->mResultArrayBuffer = nullptr;
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFile)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsDOMFileReader,
                                                DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mResultArrayBuffer)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMFileReader)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIDOMFileReader)
-  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END_INHERITING(FileIOObject)
 
 NS_IMPL_ADDREF_INHERITED(nsDOMFileReader, FileIOObject)
 NS_IMPL_RELEASE_INHERITED(nsDOMFileReader, FileIOObject)
 
 NS_IMPL_EVENT_HANDLER(nsDOMFileReader, load)
 NS_IMPL_EVENT_HANDLER(nsDOMFileReader, loadend)
@@ -116,24 +99,16 @@ nsDOMFileReader::~nsDOMFileReader()
 
 
 /**
  * This Init method is called from the factory constructor.
  */
 nsresult
 nsDOMFileReader::Init()
 {
-  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
-  nsCOMPtr<nsIPrincipal> principal;
-  if (secMan) {
-    secMan->GetSystemPrincipal(getter_AddRefs(principal));
-  }
-  NS_ENSURE_STATE(principal);
-  mPrincipal.swap(principal);
-
   // Instead of grabbing some random global from the context stack,
   // let's use the default one (junk scope) for now.
   // We should move away from this Init...
   nsCOMPtr<nsIGlobalObject> global = xpc::GetJunkScopeGlobal();
   NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
   BindToOwner(global);
   return NS_OK;
 }
@@ -146,25 +121,16 @@ nsDOMFileReader::Constructor(const Globa
   nsCOMPtr<nsPIDOMWindow> owner = do_QueryInterface(aGlobal.GetAsSupports());
   if (!owner) {
     NS_WARNING("Unexpected nsIJSNativeInitializer owner");
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   fileReader->BindToOwner(owner);
-
-  // This object is bound to a |window|,
-  // so reset the principal.
-  nsCOMPtr<nsIScriptObjectPrincipal> scriptPrincipal = do_QueryInterface(owner);
-  if (!scriptPrincipal) {
-    aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
-  }
-  fileReader->mPrincipal = scriptPrincipal->GetPrincipal();
   return fileReader.forget();
 }
 
 // nsIInterfaceRequestor
 
 NS_IMETHODIMP
 nsDOMFileReader::GetInterface(const nsIID & aIID, void **aResult)
 {
@@ -175,22 +141,21 @@ nsDOMFileReader::GetInterface(const nsII
 
 NS_IMETHODIMP
 nsDOMFileReader::GetReadyState(uint16_t *aReadyState)
 {
   *aReadyState = ReadyState();
   return NS_OK;
 }
 
-JS::Value
-nsDOMFileReader::GetResult(JSContext* aCx, ErrorResult& aRv)
+void
+nsDOMFileReader::GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                           ErrorResult& aRv)
 {
-  JS::Rooted<JS::Value> result(aCx);
-  aRv = GetResult(aCx, &result);
-  return result;
+  aRv = GetResult(aCx, aResult);
 }
 
 NS_IMETHODIMP
 nsDOMFileReader::GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult)
 {
   JS::Rooted<JS::Value> result(aCx);
   if (mDataFormat == FILE_AS_ARRAYBUFFER) {
     if (mReadyState == nsIDOMFileReader::DONE && mResultArrayBuffer) {
@@ -265,22 +230,19 @@ nsDOMFileReader::Abort()
 }
 
 /* virtual */ void
 nsDOMFileReader::DoAbort(nsAString& aEvent)
 {
   // Revert status and result attributes
   SetDOMStringToNull(mResult);
   mResultArrayBuffer = nullptr;
-    
-  // Non-null channel indicates a read is currently active
-  if (mChannel) {
-    // Cancel request requires an error status
-    mChannel->Cancel(NS_ERROR_FAILURE);
-    mChannel = nullptr;
+
+  if (mAsyncStream) {
+    mAsyncStream = nullptr;
   }
   mFile = nullptr;
 
   //Clean up memory buffer
   FreeFileData();
 
   // Tell the base class which event to dispatch
   aEvent = NS_LITERAL_STRING(LOADEND_STR);
@@ -304,75 +266,24 @@ ReadFuncBinaryString(nsIInputStream* in,
     ++source;
   }
   *writeCount = count;
 
   return NS_OK;
 }
 
 nsresult
-nsDOMFileReader::DoOnDataAvailable(nsIRequest *aRequest,
-                                   nsISupports *aContext,
-                                   nsIInputStream *aInputStream,
-                                   uint64_t aOffset,
-                                   uint32_t aCount)
+nsDOMFileReader::DoOnLoadEnd(nsresult aStatus,
+                             nsAString& aSuccessEvent,
+                             nsAString& aTerminationEvent)
 {
-  if (mDataFormat == FILE_AS_BINARY) {
-    //Continuously update our binary string as data comes in
-    NS_ASSERTION(mResult.Length() == aOffset,
-                 "unexpected mResult length");
-    uint32_t oldLen = mResult.Length();
-    if (uint64_t(oldLen) + aCount > UINT32_MAX)
-      return NS_ERROR_OUT_OF_MEMORY;
-
-    char16_t *buf = nullptr;
-    mResult.GetMutableData(&buf, oldLen + aCount, fallible_t());
-    NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
 
-    uint32_t bytesRead = 0;
-    aInputStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
-                               &bytesRead);
-    NS_ASSERTION(bytesRead == aCount, "failed to read data");
-  }
-  else if (mDataFormat == FILE_AS_ARRAYBUFFER) {
-    uint32_t bytesRead = 0;
-    aInputStream->Read((char*)JS_GetArrayBufferData(mResultArrayBuffer) + aOffset,
-                       aCount, &bytesRead);
-    NS_ASSERTION(bytesRead == aCount, "failed to read data");
-  }
-  else {
-    //Update memory buffer to reflect the contents of the file
-    if (aOffset + aCount > UINT32_MAX) {
-      // PR_Realloc doesn't support over 4GB memory size even if 64-bit OS
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
-    mFileData = (char *)moz_realloc(mFileData, aOffset + aCount);
-    NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY);
-
-    uint32_t bytesRead = 0;
-    aInputStream->Read(mFileData + aOffset, aCount, &bytesRead);
-    NS_ASSERTION(bytesRead == aCount, "failed to read data");
-
-    mDataLen += aCount;
-  }
-
-  return NS_OK;
-}
-
-nsresult
-nsDOMFileReader::DoOnStopRequest(nsIRequest *aRequest,
-                                 nsISupports *aContext,
-                                 nsresult aStatus,
-                                 nsAString& aSuccessEvent,
-                                 nsAString& aTerminationEvent)
-{
   // Make sure we drop all the objects that could hold files open now.
-  nsCOMPtr<nsIChannel> channel;
-  mChannel.swap(channel);
-
+  nsCOMPtr<nsIAsyncInputStream> stream;
+  mAsyncStream.swap(stream);
   nsCOMPtr<nsIDOMBlob> file;
   mFile.swap(file);
 
   aSuccessEvent = NS_LITERAL_STRING(LOAD_STR);
   aTerminationEvent = NS_LITERAL_STRING(LOADEND_STR);
 
   // Clear out the data if necessary
   if (NS_FAILED(aStatus)) {
@@ -396,24 +307,69 @@ nsDOMFileReader::DoOnStopRequest(nsIRequ
         break;
       }
       rv = GetAsText(file, mCharset, mFileData, mDataLen, mResult);
       break;
     case FILE_AS_DATAURL:
       rv = GetAsDataURL(file, mFileData, mDataLen, mResult);
       break;
   }
-  
+
   mResult.SetIsVoid(false);
 
   FreeFileData();
 
   return rv;
 }
 
+nsresult
+nsDOMFileReader::DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount)
+{
+  MOZ_ASSERT(aStream);
+
+  if (mDataFormat == FILE_AS_BINARY) {
+    //Continuously update our binary string as data comes in
+    uint32_t oldLen = mResult.Length();
+    NS_ASSERTION(mResult.Length() == mDataLen,
+                 "unexpected mResult length");
+    if (uint64_t(oldLen) + aCount > UINT32_MAX)
+      return NS_ERROR_OUT_OF_MEMORY;
+    char16_t *buf = nullptr;
+    mResult.GetMutableData(&buf, oldLen + aCount, fallible_t());
+    NS_ENSURE_TRUE(buf, NS_ERROR_OUT_OF_MEMORY);
+
+    uint32_t bytesRead = 0;
+    aStream->ReadSegments(ReadFuncBinaryString, buf + oldLen, aCount,
+                          &bytesRead);
+    NS_ASSERTION(bytesRead == aCount, "failed to read data");
+  }
+  else if (mDataFormat == FILE_AS_ARRAYBUFFER) {
+    uint32_t bytesRead = 0;
+    aStream->Read((char*) JS_GetArrayBufferData(mResultArrayBuffer) + mDataLen,
+                  aCount, &bytesRead);
+    NS_ASSERTION(bytesRead == aCount, "failed to read data");
+  }
+  else {
+    //Update memory buffer to reflect the contents of the file
+    if (mDataLen + aCount > UINT32_MAX) {
+      // PR_Realloc doesn't support over 4GB memory size even if 64-bit OS
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    mFileData = (char *) moz_realloc(mFileData, mDataLen + aCount);
+    NS_ENSURE_TRUE(mFileData, NS_ERROR_OUT_OF_MEMORY);
+
+    uint32_t bytesRead = 0;
+    aStream->Read(mFileData + mDataLen, aCount, &bytesRead);
+    NS_ASSERTION(bytesRead == aCount, "failed to read data");
+  }
+
+  mDataLen += aCount;
+  return NS_OK;
+}
+
 // Helper methods
 
 void
 nsDOMFileReader::ReadFileContent(JSContext* aCx,
                                  nsIDOMBlob* aFile,
                                  const nsAString &aCharset,
                                  eDataFormat aDataFormat,
                                  ErrorResult& aRv)
@@ -428,50 +384,65 @@ nsDOMFileReader::ReadFileContent(JSConte
   mTotal = 0;
   mReadyState = nsIDOMFileReader::EMPTY;
   FreeFileData();
 
   mFile = aFile;
   mDataFormat = aDataFormat;
   CopyUTF16toUTF8(aCharset, mCharset);
 
-  //Establish a channel with our file
-  {
-    // Hold the internal URL alive only as long as necessary
-    // After the channel is created it will own whatever is backing
-    // the DOMFile.
-    nsDOMFileInternalUrlHolder urlHolder(mFile, mPrincipal);
-
-    nsCOMPtr<nsIURI> uri;
-    aRv = NS_NewURI(getter_AddRefs(uri), urlHolder.mUrl);
-    NS_ENSURE_SUCCESS_VOID(aRv.ErrorCode());
+  nsresult rv;
 
-    nsCOMPtr<nsILoadGroup> loadGroup;
-    if (HasOrHasHadOwner()) {
-      if (!GetOwner()) {
-        aRv.Throw(NS_ERROR_FAILURE);
-        return;
-      }
-      nsIDocument* doc = GetOwner()->GetExtantDoc();
-      if (doc) {
-        loadGroup = doc->GetDocumentLoadGroup();
-      }
-    }
+  nsCOMPtr<nsIStreamTransportService> sts =
+    do_GetService(kStreamTransportServiceCID, &rv);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(rv);
+    return;
+  }
 
-    aRv = NS_NewChannel(getter_AddRefs(mChannel), uri, nullptr, loadGroup,
-                        nullptr, nsIRequest::LOAD_BACKGROUND);
-    NS_ENSURE_SUCCESS_VOID(aRv.ErrorCode());
+  nsCOMPtr<nsIInputStream> stream;
+  rv = mFile->GetInternalStream(getter_AddRefs(stream));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(rv);
+    return;
   }
 
-  //Obtain the total size of the file before reading
+  nsCOMPtr<nsITransport> transport;
+  rv = sts->CreateInputTransport(stream,
+                                 /* aStartOffset */ 0,
+                                 /* aReadLimit */ -1,
+                                 /* aCloseWhenDone */ true,
+                                 getter_AddRefs(transport));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  nsCOMPtr<nsIInputStream> wrapper;
+  rv = transport->OpenInputStream(/* aFlags */ 0,
+                                  /* aSegmentSize */ 0,
+                                  /* aSegmentCount */ 0,
+                                  getter_AddRefs(wrapper));
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(rv);
+    return;
+  }
+
+  MOZ_ASSERT(!mAsyncStream);
+  mAsyncStream = do_QueryInterface(wrapper);
+  MOZ_ASSERT(mAsyncStream);
+
   mTotal = mozilla::dom::kUnknownSize;
   mFile->GetSize(&mTotal);
 
-  aRv = mChannel->AsyncOpen(this, nullptr);
-  NS_ENSURE_SUCCESS_VOID(aRv.ErrorCode());
+  rv = DoAsyncWait(mAsyncStream);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    aRv.Throw(rv);
+    return;
+  }
 
   //FileReader should be in loading state here
   mReadyState = nsIDOMFileReader::LOADING;
   DispatchProgressEvent(NS_LITERAL_STRING(LOADSTART_STR));
 
   if (mDataFormat == FILE_AS_ARRAYBUFFER) {
     RootResultArrayBuffer();
     mResultArrayBuffer = JS_NewArrayBuffer(aCx, mTotal);
--- a/content/base/src/nsDOMFileReader.h
+++ b/content/base/src/nsDOMFileReader.h
@@ -2,34 +2,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 nsDOMFileReader_h__
 #define nsDOMFileReader_h__
 
 #include "mozilla/Attributes.h"
-#include "nsISupportsUtils.h"      
+#include "nsISupportsUtils.h"
 #include "nsString.h"
 #include "nsWeakReference.h"
-#include "nsIStreamListener.h"     
-#include "nsIInterfaceRequestor.h" 
-#include "nsJSUtils.h"             
-#include "nsTArray.h"              
+#include "nsIStreamListener.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsJSUtils.h"
+#include "nsTArray.h"
 #include "nsIJSNativeInitializer.h"
-#include "prtime.h"                
-#include "nsITimer.h"              
+#include "prtime.h"
+#include "nsITimer.h"
+#include "nsIAsyncInputStream.h"
 
 #include "nsIDOMFile.h"
 #include "nsIDOMFileReader.h"
 #include "nsIDOMFileList.h"
-#include "nsIInputStream.h"
 #include "nsCOMPtr.h"
-#include "nsIStreamLoader.h"
-#include "nsIChannel.h"
 
 #include "FileIOObject.h"
 
 class nsDOMFileReader : public mozilla::dom::FileIOObject,
                         public nsIDOMFileReader,
                         public nsIInterfaceRequestor,
                         public nsSupportsWeakReference
 {
@@ -45,53 +43,55 @@ public:
 
   NS_REALLY_FORWARD_NSIDOMEVENTTARGET(mozilla::DOMEventTargetHelper)
 
   // nsIInterfaceRequestor 
   NS_DECL_NSIINTERFACEREQUESTOR
 
   // FileIOObject overrides
   virtual void DoAbort(nsAString& aEvent) MOZ_OVERRIDE;
-  NS_IMETHOD DoOnStopRequest(nsIRequest* aRequest, nsISupports* aContext,
-                             nsresult aStatus, nsAString& aSuccessEvent,
-                             nsAString& aTerminationEvent) MOZ_OVERRIDE;
-  NS_IMETHOD DoOnDataAvailable(nsIRequest* aRequest, nsISupports* aContext,
-                               nsIInputStream* aInputStream, uint64_t aOffset,
-                               uint32_t aCount) MOZ_OVERRIDE;
+
+  virtual nsresult DoReadData(nsIAsyncInputStream* aStream, uint64_t aCount) MOZ_OVERRIDE;
+
+  virtual nsresult DoOnLoadEnd(nsresult aStatus, nsAString& aSuccessEvent,
+                               nsAString& aTerminationEvent) MOZ_OVERRIDE;
 
   nsPIDOMWindow* GetParentObject() const
   {
     return GetOwner();
   }
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   // WebIDL
   static already_AddRefed<nsDOMFileReader>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
   void ReadAsArrayBuffer(JSContext* aCx, nsIDOMBlob* aBlob, ErrorResult& aRv)
   {
     MOZ_ASSERT(aBlob);
     ReadFileContent(aCx, aBlob, EmptyString(), FILE_AS_ARRAYBUFFER, aRv);
   }
+
   void ReadAsText(nsIDOMBlob* aBlob, const nsAString& aLabel, ErrorResult& aRv)
   {
     MOZ_ASSERT(aBlob);
     ReadFileContent(nullptr, aBlob, aLabel, FILE_AS_TEXT, aRv);
   }
+
   void ReadAsDataURL(nsIDOMBlob* aBlob, ErrorResult& aRv)
   {
     MOZ_ASSERT(aBlob);
     ReadFileContent(nullptr, aBlob, EmptyString(), FILE_AS_DATAURL, aRv);
   }
 
   using FileIOObject::Abort;
 
   // Inherited ReadyState().
 
-  JS::Value GetResult(JSContext* aCx, ErrorResult& aRv);
+  void GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                 ErrorResult& aRv);
 
   using FileIOObject::GetError;
 
   IMPL_EVENT_HANDLER(loadstart)
   using FileIOObject::GetOnprogress;
   using FileIOObject::SetOnprogress;
   IMPL_EVENT_HANDLER(load)
   using FileIOObject::GetOnabort;
@@ -121,30 +121,29 @@ protected:
     FILE_AS_DATAURL
   };
 
   void ReadFileContent(JSContext* aCx, nsIDOMBlob* aBlob,
                        const nsAString &aCharset, eDataFormat aDataFormat,
                        ErrorResult& aRv);
   nsresult GetAsText(nsIDOMBlob *aFile, const nsACString &aCharset,
                      const char *aFileData, uint32_t aDataLen, nsAString &aResult);
-  nsresult GetAsDataURL(nsIDOMBlob *aFile, const char *aFileData, uint32_t aDataLen, nsAString &aResult); 
+  nsresult GetAsDataURL(nsIDOMBlob *aFile, const char *aFileData, uint32_t aDataLen, nsAString &aResult);
 
   void FreeFileData() {
     moz_free(mFileData);
     mFileData = nullptr;
     mDataLen = 0;
   }
 
   char *mFileData;
   nsCOMPtr<nsIDOMBlob> mFile;
   nsCString mCharset;
   uint32_t mDataLen;
 
   eDataFormat mDataFormat;
 
   nsString mResult;
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  
+
   JS::Heap<JSObject*> mResultArrayBuffer;
 };
 
 #endif
--- a/content/base/src/nsDocument.cpp
+++ b/content/base/src/nsDocument.cpp
@@ -5762,24 +5762,25 @@ nsDocument::RegisterEnabled()
 // static
 Maybe<nsTArray<mozilla::dom::CustomElementData*>>
 nsDocument::sProcessingStack;
 
 // static
 bool
 nsDocument::sProcessingBaseElementQueue;
 
-JSObject*
+void
 nsDocument::RegisterElement(JSContext* aCx, const nsAString& aType,
                             const ElementRegistrationOptions& aOptions,
+                            JS::MutableHandle<JSObject*> aRetval,
                             ErrorResult& rv)
 {
   if (!mRegistry) {
     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
+    return;
   }
 
   Registry::DefinitionMap& definitions = mRegistry->mCustomDefinitions;
 
   // Unconditionally convert TYPE to lowercase.
   nsAutoString lcType;
   nsContentUtils::ASCIIToLower(aType, lcType);
 
@@ -5790,88 +5791,88 @@ nsDocument::RegisterElement(JSContext* a
     nsContentUtils::ASCIIToLower(aOptions.mExtends, lcName);
   } else {
     lcName.Assign(aOptions.mExtends);
   }
 
   nsCOMPtr<nsIAtom> typeAtom(do_GetAtom(lcType));
   if (!nsContentUtils::IsCustomElementName(typeAtom)) {
     rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-    return nullptr;
+    return;
   }
 
   // If there already exists a definition with the same TYPE, set ERROR to
   // DuplicateDefinition and stop.
   // Note that we need to find existing custom elements from either namespace.
   CustomElementHashKey duplicateFinder(kNameSpaceID_Unknown, typeAtom);
   if (definitions.Get(&duplicateFinder)) {
     rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-    return nullptr;
+    return;
   }
 
   nsIGlobalObject* sgo = GetScopeObject();
   if (!sgo) {
     rv.Throw(NS_ERROR_UNEXPECTED);
-    return nullptr;
+    return;
   }
   JS::Rooted<JSObject*> global(aCx, sgo->GetGlobalJSObject());
 
   JSAutoCompartment ac(aCx, global);
 
   JS::Handle<JSObject*> htmlProto(
     HTMLElementBinding::GetProtoObject(aCx, global));
   if (!htmlProto) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return nullptr;
+    return;
   }
 
   int32_t namespaceID = kNameSpaceID_XHTML;
   JS::Rooted<JSObject*> protoObject(aCx);
   if (!aOptions.mPrototype) {
     protoObject = JS_NewObject(aCx, nullptr, htmlProto, JS::NullPtr());
     if (!protoObject) {
       rv.Throw(NS_ERROR_UNEXPECTED);
-      return nullptr;
+      return;
     }
   } else {
     // If a prototype is provided, we must check to ensure that it is from the
     // same browsing context as us.
     protoObject = aOptions.mPrototype;
     if (JS_GetGlobalForObject(aCx, protoObject) != global) {
       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-      return nullptr;
+      return;
     }
 
     // If PROTOTYPE is already an interface prototype object for any interface
     // object or PROTOTYPE has a non-configurable property named constructor,
     // throw a NotSupportedError and stop.
     const js::Class* clasp = js::GetObjectClass(protoObject);
     if (IsDOMIfaceAndProtoClass(clasp)) {
       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-      return nullptr;
+      return;
     }
 
     JS::Rooted<JSPropertyDescriptor> descRoot(aCx);
     JS::MutableHandle<JSPropertyDescriptor> desc(&descRoot);
     if (!JS_GetPropertyDescriptor(aCx, protoObject, "constructor", desc)) {
       rv.Throw(NS_ERROR_UNEXPECTED);
-      return nullptr;
+      return;
     }
 
     // Check if non-configurable
     if (desc.isPermanent()) {
       rv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
-      return nullptr;
+      return;
     }
 
     JS::Handle<JSObject*> svgProto(
       SVGElementBinding::GetProtoObject(aCx, global));
     if (!svgProto) {
       rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return nullptr;
+      return;
     }
 
     JS::Rooted<JSObject*> protoProto(aCx, protoObject);
 
     // If PROTOTYPE's interface inherits from SVGElement, set NAMESPACE to SVG
     // Namespace.
     while (protoProto) {
       if (protoProto == htmlProto) {
@@ -5880,61 +5881,62 @@ nsDocument::RegisterElement(JSContext* a
 
       if (protoProto == svgProto) {
         namespaceID = kNameSpaceID_SVG;
         break;
       }
 
       if (!JS_GetPrototype(aCx, protoProto, &protoProto)) {
         rv.Throw(NS_ERROR_UNEXPECTED);
-        return nullptr;
+        return;
       }
     }
   }
 
   // If name was provided and not null...
   nsCOMPtr<nsIAtom> nameAtom;
   if (!lcName.IsEmpty()) {
     // Let BASE be the element interface for NAME and NAMESPACE.
     bool known = false;
     nameAtom = do_GetAtom(lcName);
     if (namespaceID == kNameSpaceID_XHTML) {
       nsIParserService* ps = nsContentUtils::GetParserService();
       if (!ps) {
         rv.Throw(NS_ERROR_UNEXPECTED);
-        return nullptr;
+        return;
       }
 
       known =
         ps->HTMLCaseSensitiveAtomTagToId(nameAtom) != eHTMLTag_userdefined;
     } else {
       known = SVGElementFactory::Exists(nameAtom);
     }
 
     // If BASE does not exist or is an interface for a custom element, set ERROR
     // to InvalidName and stop.
     // If BASE exists, then it cannot be an interface for a custom element.
     if (!known) {
       rv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-      return nullptr;
+      return;
     }
   } else {
     // If NAMESPACE is SVG Namespace, set ERROR to InvalidName and stop.
     if (namespaceID == kNameSpaceID_SVG) {
       rv.Throw(NS_ERROR_UNEXPECTED);
-      return nullptr;
+      return;
     }
 
     nameAtom = typeAtom;
   }
 
   nsAutoPtr<LifecycleCallbacks> callbacksHolder(new LifecycleCallbacks());
   JS::RootedValue rootedv(aCx, JS::ObjectValue(*protoObject));
   if (!callbacksHolder->Init(aCx, rootedv)) {
-    return nullptr;
+    rv.Throw(NS_ERROR_FAILURE);
+    return;
   }
 
   // Associate the definition with the custom element.
   CustomElementHashKey key(namespaceID, typeAtom);
   LifecycleCallbacks* callbacks = callbacksHolder.forget();
   CustomElementDefinition* definition =
     new CustomElementDefinition(protoObject,
                                 typeAtom,
@@ -5983,18 +5985,22 @@ nsDocument::RegisterElement(JSContext* a
     }
   }
 
   // Create constructor to return. Store the name of the custom element as the
   // name of the function.
   JSFunction* constructor = JS_NewFunction(aCx, nsDocument::CustomElementConstructor, 0,
                                            JSFUN_CONSTRUCTOR, JS::NullPtr(),
                                            NS_ConvertUTF16toUTF8(lcType).get());
-  JSObject* constructorObject = JS_GetFunctionObject(constructor);
-  return constructorObject;
+  if (!constructor) {
+    rv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+
+  aRetval.set(JS_GetFunctionObject(constructor));
 }
 
 void
 nsDocument::UseRegistryFromDocument(nsIDocument* aDocument)
 {
   nsDocument* doc = static_cast<nsDocument*>(aDocument);
   MOZ_ASSERT(!mRegistry, "There should be no existing registry.");
   mRegistry = doc->mRegistry;
--- a/content/base/src/nsDocument.h
+++ b/content/base/src/nsDocument.h
@@ -1221,19 +1221,20 @@ public:
   static bool RegisterEnabled();
 
   virtual nsresult RegisterUnresolvedElement(mozilla::dom::Element* aElement,
                                              nsIAtom* aTypeName = nullptr) MOZ_OVERRIDE;
 
   // WebIDL bits
   virtual mozilla::dom::DOMImplementation*
     GetImplementation(mozilla::ErrorResult& rv) MOZ_OVERRIDE;
-  virtual JSObject*
+  virtual void
     RegisterElement(JSContext* aCx, const nsAString& aName,
                     const mozilla::dom::ElementRegistrationOptions& aOptions,
+                    JS::MutableHandle<JSObject*> aRetval,
                     mozilla::ErrorResult& rv) MOZ_OVERRIDE;
   virtual mozilla::dom::StyleSheetList* StyleSheets() MOZ_OVERRIDE;
   virtual void SetSelectedStyleSheetSet(const nsAString& aSheetSet) MOZ_OVERRIDE;
   virtual void GetLastStyleSheetSet(nsString& aSheetSet) MOZ_OVERRIDE;
   virtual mozilla::dom::DOMStringList* StyleSheetSets() MOZ_OVERRIDE;
   virtual void EnableStyleSheetsForSet(const nsAString& aSheetSet) MOZ_OVERRIDE;
   using nsIDocument::CreateElement;
   using nsIDocument::CreateElementNS;
--- a/content/base/src/nsINode.cpp
+++ b/content/base/src/nsINode.cpp
@@ -774,70 +774,70 @@ nsINode::SetUserData(const nsAString &aK
     DeleteProperty(DOM_USER_DATA_HANDLER, key);
   }
 
   oldData.swap(*aResult);
 
   return NS_OK;
 }
 
-JS::Value
+void
 nsINode::SetUserData(JSContext* aCx, const nsAString& aKey,
                      JS::Handle<JS::Value> aData,
-                     nsIDOMUserDataHandler* aHandler, ErrorResult& aError)
+                     nsIDOMUserDataHandler* aHandler,
+                     JS::MutableHandle<JS::Value> aRetval,
+                     ErrorResult& aError)
 {
   nsCOMPtr<nsIVariant> data;
-  JS::Rooted<JS::Value> dataVal(aCx, aData);
-  aError = nsContentUtils::XPConnect()->JSValToVariant(aCx, dataVal, getter_AddRefs(data));
+  aError = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData, getter_AddRefs(data));
   if (aError.Failed()) {
-    return JS::UndefinedValue();
+    return;
   }
 
   nsCOMPtr<nsIVariant> oldData;
   aError = SetUserData(aKey, data, aHandler, getter_AddRefs(oldData));
   if (aError.Failed()) {
-    return JS::UndefinedValue();
+    return;
   }
 
   if (!oldData) {
-    return JS::NullValue();
+    aRetval.setNull();
+    return;
   }
 
-  JS::Rooted<JS::Value> result(aCx);
   JSAutoCompartment ac(aCx, GetWrapper());
   aError = nsContentUtils::XPConnect()->VariantToJS(aCx, GetWrapper(), oldData,
-                                                    &result);
-  return result;
+                                                    aRetval);
 }
 
 nsIVariant*
 nsINode::GetUserData(const nsAString& aKey)
 {
   OwnerDoc()->WarnOnceAbout(nsIDocument::eGetSetUserData);
   nsCOMPtr<nsIAtom> key = do_GetAtom(aKey);
   if (!key) {
     return nullptr;
   }
 
   return static_cast<nsIVariant*>(GetProperty(DOM_USER_DATA, key));
 }
 
-JS::Value
-nsINode::GetUserData(JSContext* aCx, const nsAString& aKey, ErrorResult& aError)
+void
+nsINode::GetUserData(JSContext* aCx, const nsAString& aKey,
+                     JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
 {
   nsIVariant* data = GetUserData(aKey);
   if (!data) {
-    return JS::NullValue();
+    aRetval.setNull();
+    return;
   }
 
-  JS::Rooted<JS::Value> result(aCx);
   JSAutoCompartment ac(aCx, GetWrapper());
   aError = nsContentUtils::XPConnect()->VariantToJS(aCx, GetWrapper(), data,
-                                                    &result);
-  return result;
+                                                    aRetval);
 }
 
 uint16_t
 nsINode::CompareDocumentPosition(nsINode& aOtherNode) const
 {
   if (this == &aOtherNode) {
     return 0;
   }
--- a/content/base/src/nsObjectLoadingContent.cpp
+++ b/content/base/src/nsObjectLoadingContent.cpp
@@ -3199,99 +3199,98 @@ nsObjectLoadingContent::GetContentDocume
   // Return null for cross-origin contentDocument.
   if (!nsContentUtils::SubjectPrincipal()->SubsumesConsideringDomain(sub_doc->NodePrincipal())) {
     return nullptr;
   }
 
   return sub_doc;
 }
 
-JS::Value
+void
 nsObjectLoadingContent::LegacyCall(JSContext* aCx,
                                    JS::Handle<JS::Value> aThisVal,
                                    const Sequence<JS::Value>& aArguments,
+                                   JS::MutableHandle<JS::Value> aRetval,
                                    ErrorResult& aRv)
 {
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   JS::Rooted<JSObject*> obj(aCx, thisContent->GetWrapper());
   MOZ_ASSERT(obj, "How did we get called?");
 
   // Make sure we're not dealing with an Xray.  Our DoCall code can't handle
   // random cross-compartment wrappers, so we're going to have to wrap
   // everything up into our compartment, but that means we need to check that
   // this is not an Xray situation by hand.
   if (!JS_WrapObject(aCx, &obj)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
-    return JS::UndefinedValue();
+    return;
   }
 
   if (nsDOMClassInfo::ObjectIsNativeWrapper(aCx, obj)) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-    return JS::UndefinedValue();
+    return;
   }
 
   obj = thisContent->GetWrapper();
   // Now wrap things up into the compartment of "obj"
   JSAutoCompartment ac(aCx, obj);
   JS::AutoValueVector args(aCx);
   if (!args.append(aArguments.Elements(), aArguments.Length())) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return JS::UndefinedValue();
+    return;
   }
 
   for (size_t i = 0; i < args.length(); i++) {
     if (!JS_WrapValue(aCx, args[i])) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
-      return JS::UndefinedValue();
+      return;
     }
   }
 
   JS::Rooted<JS::Value> thisVal(aCx, aThisVal);
   if (!JS_WrapValue(aCx, &thisVal)) {
     aRv.Throw(NS_ERROR_UNEXPECTED);
-    return JS::UndefinedValue();
+    return;
   }
 
   nsRefPtr<nsNPAPIPluginInstance> pi;
   nsresult rv = ScriptRequestPluginInstance(aCx, getter_AddRefs(pi));
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
-    return JS::UndefinedValue();
+    return;
   }
 
   // if there's no plugin around for this object, throw.
   if (!pi) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-    return JS::UndefinedValue();
+    return;
   }
 
   JS::Rooted<JSObject*> pi_obj(aCx);
   JS::Rooted<JSObject*> pi_proto(aCx);
 
   rv = GetPluginJSObject(aCx, obj, pi, &pi_obj, &pi_proto);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
-    return JS::UndefinedValue();
+    return;
   }
 
   if (!pi_obj) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-    return JS::UndefinedValue();
+    return;
   }
 
-  JS::Rooted<JS::Value> retval(aCx);
-  bool ok = JS::Call(aCx, thisVal, pi_obj, args, &retval);
+  bool ok = JS::Call(aCx, thisVal, pi_obj, args, aRetval);
   if (!ok) {
     aRv.Throw(NS_ERROR_FAILURE);
-    return JS::UndefinedValue();
+    return;
   }
 
   Telemetry::Accumulate(Telemetry::PLUGIN_CALLED_DIRECTLY, true);
-  return retval;
 }
 
 void
 nsObjectLoadingContent::SetupProtoChain(JSContext* aCx,
                                         JS::Handle<JSObject*> aObject)
 {
   MOZ_ASSERT(nsCOMPtr<nsIContent>(do_QueryInterface(
     static_cast<nsIObjectLoadingContent*>(this)))->IsDOMBinding());
--- a/content/base/src/nsObjectLoadingContent.h
+++ b/content/base/src/nsObjectLoadingContent.h
@@ -209,19 +209,20 @@ class nsObjectLoadingContent : public ns
     void CancelPlayPreview(mozilla::ErrorResult& aRv)
     {
       aRv = CancelPlayPreview();
     }
     void SwapFrameLoaders(nsXULElement& aOtherOwner, mozilla::ErrorResult& aRv)
     {
       aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
     }
-    JS::Value LegacyCall(JSContext* aCx, JS::Handle<JS::Value> aThisVal,
-                         const mozilla::dom::Sequence<JS::Value>& aArguments,
-                         mozilla::ErrorResult& aRv);
+    void LegacyCall(JSContext* aCx, JS::Handle<JS::Value> aThisVal,
+                    const mozilla::dom::Sequence<JS::Value>& aArguments,
+                    JS::MutableHandle<JS::Value> aRetval,
+                    mozilla::ErrorResult& aRv);
 
   protected:
     /**
      * Begins loading the object when called
      *
      * Attributes of |this| QI'd to nsIContent will be inspected, depending on
      * the node type. This function currently assumes it is a <applet>,
      * <object>, or <embed> tag.
--- a/content/base/src/nsXMLHttpRequest.cpp
+++ b/content/base/src/nsXMLHttpRequest.cpp
@@ -931,118 +931,125 @@ nsXMLHttpRequest::SetResponseType(nsXMLH
 
 }
 
 /* readonly attribute jsval response; */
 NS_IMETHODIMP
 nsXMLHttpRequest::GetResponse(JSContext *aCx, JS::MutableHandle<JS::Value> aResult)
 {
   ErrorResult rv;
-  aResult.set(GetResponse(aCx, rv));
+  GetResponse(aCx, aResult, rv);
   return rv.ErrorCode();
 }
 
-JS::Value
-nsXMLHttpRequest::GetResponse(JSContext* aCx, ErrorResult& aRv)
+void
+nsXMLHttpRequest::GetResponse(JSContext* aCx,
+                              JS::MutableHandle<JS::Value> aResponse,
+                              ErrorResult& aRv)
 {
   switch (mResponseType) {
   case XML_HTTP_RESPONSE_TYPE_DEFAULT:
   case XML_HTTP_RESPONSE_TYPE_TEXT:
   case XML_HTTP_RESPONSE_TYPE_CHUNKED_TEXT:
   {
     nsString str;
     aRv = GetResponseText(str);
     if (aRv.Failed()) {
-      return JSVAL_NULL;
+      return;
     }
-    JS::Rooted<JS::Value> result(aCx);
-    if (!xpc::StringToJsval(aCx, str, &result)) {
+    if (!xpc::StringToJsval(aCx, str, aResponse)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return JSVAL_NULL;
     }
-    return result;
+    return;
   }
 
   case XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER:
   case XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER:
   {
     if (!(mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
           mState & XML_HTTP_REQUEST_DONE) &&
         !(mResponseType == XML_HTTP_RESPONSE_TYPE_CHUNKED_ARRAYBUFFER &&
           mInLoadProgressEvent)) {
-      return JSVAL_NULL;
+      aResponse.setNull();
+      return;
     }
 
     if (!mResultArrayBuffer) {
       RootJSResultObjects();
 
       mResultArrayBuffer = mArrayBufferBuilder.getArrayBuffer(aCx);
       if (!mResultArrayBuffer) {
         aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-        return JSVAL_NULL;
+        return;
       }
     }
-    return OBJECT_TO_JSVAL(mResultArrayBuffer);
+    JS::ExposeObjectToActiveJS(mResultArrayBuffer);
+    aResponse.setObject(*mResultArrayBuffer);
+    return;
   }
   case XML_HTTP_RESPONSE_TYPE_BLOB:
   case XML_HTTP_RESPONSE_TYPE_MOZ_BLOB:
   {
     if (!(mState & XML_HTTP_REQUEST_DONE)) {
       if (mResponseType != XML_HTTP_RESPONSE_TYPE_MOZ_BLOB) {
-        return JSVAL_NULL;
+        aResponse.setNull();
+        return;
       }
 
       if (!mResponseBlob) {
         CreatePartialBlob();
       }
     }
 
     if (!mResponseBlob) {
-      return JSVAL_NULL;
+      aResponse.setNull();
+      return;
     }
 
-    JS::Rooted<JS::Value> result(aCx);
-    aRv = nsContentUtils::WrapNative(aCx, mResponseBlob, &result);
-    return result;
+    aRv = nsContentUtils::WrapNative(aCx, mResponseBlob, aResponse);
+    return;
   }
   case XML_HTTP_RESPONSE_TYPE_DOCUMENT:
   {
     if (!(mState & XML_HTTP_REQUEST_DONE) || !mResponseXML) {
-      return JSVAL_NULL;
+      aResponse.setNull();
+      return;
     }
 
-    JS::Rooted<JS::Value> result(aCx);
-    aRv = nsContentUtils::WrapNative(aCx, mResponseXML, &result);
-    return result;
+    aRv = nsContentUtils::WrapNative(aCx, mResponseXML, aResponse);
+    return;
   }
   case XML_HTTP_RESPONSE_TYPE_JSON:
   {
     if (!(mState & XML_HTTP_REQUEST_DONE)) {
-      return JSVAL_NULL;
+      aResponse.setNull();
+      return;
     }
 
-    if (mResultJSON == JSVAL_VOID) {
+    if (mResultJSON.isUndefined()) {
       aRv = CreateResponseParsedJSON(aCx);
       mResponseText.Truncate();
       if (aRv.Failed()) {
         // Per spec, errors aren't propagated. null is returned instead.
         aRv = NS_OK;
         // It would be nice to log the error to the console. That's hard to
         // do without calling window.onerror as a side effect, though.
         JS_ClearPendingException(aCx);
-        mResultJSON = JSVAL_NULL;
+        mResultJSON.setNull();
       }
     }
-    return mResultJSON;
+    JS::ExposeValueToActiveJS(mResultJSON);
+    aResponse.set(mResultJSON);
+    return;
   }
   default:
     NS_ERROR("Should not happen");
   }
 
-  return JSVAL_NULL;
+  aResponse.setNull();
 }
 
 bool
 nsXMLHttpRequest::IsDeniedCrossSiteRequest()
 {
   if ((mState & XML_HTTP_REQUEST_USE_XSITE_AC) && mChannel) {
     nsresult rv;
     mChannel->GetStatus(&rv);
@@ -3752,20 +3759,22 @@ nsXMLHttpRequest::GetInterface(const nsI
   else if (aIID.Equals(NS_GET_IID(nsITimerCallback))) {
     *aResult = static_cast<nsITimerCallback*>(EnsureXPCOMifier().take());
     return NS_OK;
   }
 
   return QueryInterface(aIID, aResult);
 }
 
-JS::Value
-nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aRv)
+void
+nsXMLHttpRequest::GetInterface(JSContext* aCx, nsIJSID* aIID,
+                               JS::MutableHandle<JS::Value> aRetval,
+                               ErrorResult& aRv)
 {
-  return dom::GetInterface(aCx, this, aIID, aRv);
+  dom::GetInterface(aCx, this, aIID, aRetval, aRv);
 }
 
 nsXMLHttpRequestUpload*
 nsXMLHttpRequest::Upload()
 {
   if (!mUpload) {
     mUpload = new nsXMLHttpRequestUpload(this);
   }
--- a/content/base/src/nsXMLHttpRequest.h
+++ b/content/base/src/nsXMLHttpRequest.h
@@ -489,33 +489,35 @@ public:
     // XXX Should we do some validation here?
     mOverrideMimeType = aMimeType;
   }
   XMLHttpRequestResponseType ResponseType()
   {
     return XMLHttpRequestResponseType(mResponseType);
   }
   void SetResponseType(XMLHttpRequestResponseType aType, ErrorResult& aRv);
-  JS::Value GetResponse(JSContext* aCx, ErrorResult& aRv);
+  void GetResponse(JSContext* aCx, JS::MutableHandle<JS::Value> aResponse,
+                   ErrorResult& aRv);
   void GetResponseText(nsString& aResponseText, ErrorResult& aRv);
   nsIDocument* GetResponseXML(ErrorResult& aRv);
 
   bool MozBackgroundRequest();
   void SetMozBackgroundRequest(bool aMozBackgroundRequest, nsresult& aRv);
 
   bool MozAnon();
   bool MozSystem();
 
   nsIChannel* GetChannel()
   {
     return mChannel;
   }
 
   // We need a GetInterface callable from JS for chrome JS
-  JS::Value GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aRv);
+  void GetInterface(JSContext* aCx, nsIJSID* aIID,
+                    JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv);
 
   // This creates a trusted readystatechange event, which is not cancelable and
   // doesn't bubble.
   nsresult CreateReadystatechangeEvent(nsIDOMEvent** aDOMEvent);
   void DispatchProgressEvent(mozilla::DOMEventTargetHelper* aTarget,
                              const nsAString& aType,
                              bool aLengthComputable,
                              uint64_t aLoaded, uint64_t aTotal);
--- a/content/base/test/test_fileapi.html
+++ b/content/base/test/test_fileapi.html
@@ -59,17 +59,21 @@ while (testBinaryData.length < minFileSi
 }
 
 
 //Set up files for testing
 var asciiFile = createFileWithData(testASCIIData);
 var binaryFile = createFileWithData(testBinaryData);
 
 var fileList = document.getElementById('fileList');
-SpecialPowers.wrap(fileList).value = "/none/existing/path/fileAPI/testing";
+SpecialPowers.Cu.import("resource://gre/modules/FileUtils.jsm", window);
+var FU = SpecialPowers.wrap(FileUtils);
+var fakeFile = FU.getDir("TmpD", [], false);
+fakeFile.append("FileThatDoesntExist.abc");
+SpecialPowers.wrap(fileList).value = fakeFile.path;
 var nonExistingFile = fileList.files[0];
 
 // Test that plain reading works and fires events as expected, both
 // for text and binary reading
 
 var onloadHasRunText = false;
 var onloadStartHasRunText = false;
 r = new FileReader();
@@ -351,24 +355,31 @@ r.readAsText(asciiFile);
 is(reuseAbortHasRun, true, "abort should fire sync");
 r.onload = getLoadHandler(testASCIIData, testASCIIData.length, "reuse-as-abort reading");
 expectedTestCount++;
 
 
 // Test reading from nonexistent files
 r = new FileReader();
 var didThrow = false;
+r.onerror = function (event) {
+  is(event.target.readyState, FileReader.DONE, "should be DONE while firing onerror");
+  is(event.target.error.name, "NotFoundError", "error set to NotFoundError for nonexistent files");
+  is(event.target.result, null, "file data should be null on aborted reads");
+  testHasRun();
+};
 try {
   r.readAsDataURL(nonExistingFile);
+  expectedTestCount++;
 } catch(ex) {
   didThrow = true;
 }
 // Once this test passes, we should test that onerror gets called and
 // that the FileReader object is in the right state during that call.
-todo(!didThrow, "shouldn't throw when opening nonexistent file, should fire error instead");
+is(didThrow, false, "shouldn't throw when opening nonexistent file, should fire error instead");
 
 
 function getLoadHandler(expectedResult, expectedLength, testName) {
   return function (event) {
     is(event.target.readyState, FileReader.DONE,
        "readyState in test " + testName);
     is(event.target.error, null,
        "no error in test " + testName);
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -1262,31 +1262,31 @@ CanvasRenderingContext2D::SetTransform(d
     error.Throw(NS_ERROR_FAILURE);
     return;
   }
 
   Matrix matrix(m11, m12, m21, m22, dx, dy);
   mTarget->SetTransform(matrix);
 }
 
-JSObject*
-MatrixToJSObject(JSContext* cx, const Matrix& matrix, ErrorResult& error)
+static void
+MatrixToJSObject(JSContext* cx, const Matrix& matrix,
+                 JS::MutableHandle<JSObject*> result, ErrorResult& error)
 {
   double elts[6] = { matrix._11, matrix._12,
                      matrix._21, matrix._22,
                      matrix._31, matrix._32 };
 
   // XXX Should we enter GetWrapper()'s compartment?
   JS::Rooted<JS::Value> val(cx);
   if (!ToJSValue(cx, elts, &val)) {
     error.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return nullptr;
-  }
-
-  return &val.toObject();
+  } else {
+    result.set(&val.toObject());
+  }
 }
 
 static bool
 ObjectToMatrix(JSContext* cx, JS::Handle<JSObject*> obj, Matrix& matrix,
                ErrorResult& error)
 {
   uint32_t length;
   if (!JS_GetArrayLength(cx, obj, &length) || length != 6) {
@@ -1329,21 +1329,23 @@ CanvasRenderingContext2D::SetMozCurrentT
   }
 
   Matrix newCTM;
   if (ObjectToMatrix(cx, currentTransform, newCTM, error)) {
     mTarget->SetTransform(newCTM);
   }
 }
 
-JSObject*
+void
 CanvasRenderingContext2D::GetMozCurrentTransform(JSContext* cx,
+                                                 JS::MutableHandle<JSObject*> result,
                                                  ErrorResult& error) const
 {
-  return MatrixToJSObject(cx, mTarget ? mTarget->GetTransform() : Matrix(), error);
+  MatrixToJSObject(cx, mTarget ? mTarget->GetTransform() : Matrix(),
+                   result, error);
 }
 
 void
 CanvasRenderingContext2D::SetMozCurrentTransformInverse(JSContext* cx,
                                                         JS::Handle<JSObject*> currentTransform,
                                                         ErrorResult& error)
 {
   EnsureTarget();
@@ -1356,32 +1358,34 @@ CanvasRenderingContext2D::SetMozCurrentT
   if (ObjectToMatrix(cx, currentTransform, newCTMInverse, error)) {
     // XXX ERRMSG we need to report an error to developers here! (bug 329026)
     if (newCTMInverse.Invert()) {
       mTarget->SetTransform(newCTMInverse);
     }
   }
 }
 
-JSObject*
+void
 CanvasRenderingContext2D::GetMozCurrentTransformInverse(JSContext* cx,
+                                                        JS::MutableHandle<JSObject*> result,
                                                         ErrorResult& error) const
 {
   if (!mTarget) {
-    return MatrixToJSObject(cx, Matrix(), error);
+    MatrixToJSObject(cx, Matrix(), result, error);
+    return;
   }
 
   Matrix ctm = mTarget->GetTransform();
 
   if (!ctm.Invert()) {
     double NaN = JS_GetNaNValue(cx).toDouble();
     ctm = Matrix(NaN, NaN, NaN, NaN, NaN, NaN);
   }
 
-  return MatrixToJSObject(cx, ctm, error);
+  MatrixToJSObject(cx, ctm, result, error);
 }
 
 //
 // colors
 //
 
 void
 CanvasRenderingContext2D::SetStyleFromUnion(const StringOrCanvasGradientOrCanvasPattern& value,
@@ -3106,20 +3110,22 @@ CanvasRenderingContext2D::SetMozDash(JSC
     ContextState& state = CurrentState();
     state.dash = dash;
     if (state.dash.IsEmpty()) {
       state.dashOffset = 0;
     }
   }
 }
 
-JS::Value
-CanvasRenderingContext2D::GetMozDash(JSContext* cx, ErrorResult& error)
+void
+CanvasRenderingContext2D::GetMozDash(JSContext* cx,
+                                     JS::MutableHandle<JS::Value> retval,
+                                     ErrorResult& error)
 {
-  return DashArrayToJSVal(CurrentState().dash, cx, error);
+  DashArrayToJSVal(CurrentState().dash, cx, retval, error);
 }
 
 void
 CanvasRenderingContext2D::SetMozDashOffset(double mozDashOffset)
 {
   ContextState& state = CurrentState();
   if (!state.dash.IsEmpty()) {
     state.dashOffset = mozDashOffset;
--- a/content/canvas/src/CanvasRenderingContext2D.h
+++ b/content/canvas/src/CanvasRenderingContext2D.h
@@ -383,29 +383,32 @@ public:
   }
 
   void ArcTo(double x1, double y1, double x2, double y2, double radius,
              mozilla::ErrorResult& error);
   void Rect(double x, double y, double w, double h);
   void Arc(double x, double y, double radius, double startAngle,
            double endAngle, bool anticlockwise, mozilla::ErrorResult& error);
 
-  JSObject* GetMozCurrentTransform(JSContext* cx,
-                                   mozilla::ErrorResult& error) const;
+  void GetMozCurrentTransform(JSContext* cx,
+			      JS::MutableHandle<JSObject*> result,
+			      mozilla::ErrorResult& error) const;
   void SetMozCurrentTransform(JSContext* cx,
                               JS::Handle<JSObject*> currentTransform,
                               mozilla::ErrorResult& error);
-  JSObject* GetMozCurrentTransformInverse(JSContext* cx,
-                                          mozilla::ErrorResult& error) const;
+  void GetMozCurrentTransformInverse(JSContext* cx,
+				     JS::MutableHandle<JSObject*> result,
+				     mozilla::ErrorResult& error) const;
   void SetMozCurrentTransformInverse(JSContext* cx,
                                      JS::Handle<JSObject*> currentTransform,
                                      mozilla::ErrorResult& error);
   void GetFillRule(nsAString& fillRule);
   void SetFillRule(const nsAString& fillRule);
-  JS::Value GetMozDash(JSContext* cx, mozilla::ErrorResult& error);
+  void GetMozDash(JSContext* cx, JS::MutableHandle<JS::Value> retval,
+		  mozilla::ErrorResult& error);
   void SetMozDash(JSContext* cx, const JS::Value& mozDash,
                   mozilla::ErrorResult& error);
 
   void SetLineDash(const Sequence<double>& mSegments);
   void GetLineDash(nsTArray<double>& mSegments) const;
 
   void SetLineDashOffset(double mOffset);
   double LineDashOffset() const;
--- a/content/canvas/src/CanvasUtils.h
+++ b/content/canvas/src/CanvasUtils.h
@@ -146,26 +146,28 @@ JSValToDashArray(JSContext* cx, const JS
         // random garbage is a type error.
         return NS_ERROR_INVALID_ARG;
     }
 
     return NS_OK;
 }
 
 template<typename T>
-JS::Value
+void
 DashArrayToJSVal(FallibleTArray<T>& dashes,
-                 JSContext* cx, mozilla::ErrorResult& rv)
+                 JSContext* cx,
+                 JS::MutableHandle<JS::Value> retval,
+                 mozilla::ErrorResult& rv)
 {
     if (dashes.IsEmpty()) {
-        return JS::NullValue();
+        retval.setNull();
+        return;
     }
     JS::Rooted<JS::Value> val(cx);
-    if (!mozilla::dom::ToJSValue(cx, dashes, &val)) {
+    if (!mozilla::dom::ToJSValue(cx, dashes, retval)) {
         rv.Throw(NS_ERROR_OUT_OF_MEMORY);
     }
-    return val;
 }
 
 }
 }
 
 #endif /* _CANVASUTILS_H_ */
--- a/content/canvas/src/ImageData.h
+++ b/content/canvas/src/ImageData.h
@@ -56,19 +56,19 @@ public:
   uint32_t Width() const
   {
     return mWidth;
   }
   uint32_t Height() const
   {
     return mHeight;
   }
-  JSObject* Data(JSContext* cx) const
+  void GetData(JSContext* cx, JS::MutableHandle<JSObject*> aData) const
   {
-    return GetDataObject();
+    aData.set(GetDataObject());
   }
   JSObject* GetDataObject() const
   {
     JS::ExposeObjectToActiveJS(mData);
     return mData;
   }
 
   JSObject* WrapObject(JSContext* cx);
--- a/content/canvas/src/WebGLContext.h
+++ b/content/canvas/src/WebGLContext.h
@@ -280,17 +280,19 @@ public:
     // WebIDL WebGLRenderingContext API
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
     GLsizei DrawingBufferWidth() const { return IsContextLost() ? 0 : mWidth; }
     GLsizei DrawingBufferHeight() const { return IsContextLost() ? 0 : mHeight; }
 
     void GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
     bool IsContextLost() const { return mContextStatus != ContextNotLost; }
     void GetSupportedExtensions(JSContext *cx, dom::Nullable< nsTArray<nsString> > &retval);
-    JSObject* GetExtension(JSContext* cx, const nsAString& aName, ErrorResult& rv);
+    void GetExtension(JSContext* cx, const nsAString& aName,
+                      JS::MutableHandle<JSObject*> aRetval,
+                      ErrorResult& rv);
     void ActiveTexture(GLenum texture);
     void AttachShader(WebGLProgram* program, WebGLShader* shader);
     void BindAttribLocation(WebGLProgram* program, GLuint location,
                             const nsAString& name);
     void BindFramebuffer(GLenum target, WebGLFramebuffer* wfb);
     void BindRenderbuffer(GLenum target, WebGLRenderbuffer* wrb);
     void BindTexture(GLenum target, WebGLTexture *tex);
     void BindVertexArray(WebGLVertexArray *vao);
@@ -352,56 +354,75 @@ public:
     already_AddRefed<WebGLActiveInfo> GetActiveAttrib(WebGLProgram *prog,
                                                       GLuint index);
     already_AddRefed<WebGLActiveInfo> GetActiveUniform(WebGLProgram *prog,
                                                        GLuint index);
     void GetAttachedShaders(WebGLProgram* prog,
                             dom::Nullable< nsTArray<WebGLShader*> > &retval);
     GLint GetAttribLocation(WebGLProgram* prog, const nsAString& name);
     JS::Value GetBufferParameter(GLenum target, GLenum pname);
-    JS::Value GetBufferParameter(JSContext* /* unused */, GLenum target,
-                                 GLenum pname) {
-        return GetBufferParameter(target, pname);
+    void GetBufferParameter(JSContext* /* unused */, GLenum target,
+                            GLenum pname,
+                            JS::MutableHandle<JS::Value> retval) {
+        retval.set(GetBufferParameter(target, pname));
     }
     GLenum GetError();
     JS::Value GetFramebufferAttachmentParameter(JSContext* cx,
                                                 GLenum target,
                                                 GLenum attachment,
                                                 GLenum pname,
                                                 ErrorResult& rv);
+    void GetFramebufferAttachmentParameter(JSContext* cx,
+                                           GLenum target,
+                                           GLenum attachment,
+                                           GLenum pname,
+                                           JS::MutableHandle<JS::Value> retval,
+                                           ErrorResult& rv) {
+        retval.set(GetFramebufferAttachmentParameter(cx, target, attachment,
+                                                     pname, rv));
+    }
     JS::Value GetProgramParameter(WebGLProgram *prog, GLenum pname);
-    JS::Value GetProgramParameter(JSContext* /* unused */, WebGLProgram *prog,
-                                  GLenum pname) {
-        return GetProgramParameter(prog, pname);
+    void  GetProgramParameter(JSContext* /* unused */, WebGLProgram *prog,
+                              GLenum pname,
+                              JS::MutableHandle<JS::Value> retval) {
+        retval.set(GetProgramParameter(prog, pname));
     }
     void GetProgramInfoLog(WebGLProgram *prog, nsACString& retval);
     void GetProgramInfoLog(WebGLProgram *prog, nsAString& retval);
     JS::Value GetRenderbufferParameter(GLenum target, GLenum pname);
-    JS::Value GetRenderbufferParameter(JSContext* /* unused */,
-                                       GLenum target, GLenum pname) {
-        return GetRenderbufferParameter(target, pname);
+    void GetRenderbufferParameter(JSContext* /* unused */,
+                                  GLenum target, GLenum pname,
+                                  JS::MutableHandle<JS::Value> retval) {
+        retval.set(GetRenderbufferParameter(target, pname));
     }
     JS::Value GetShaderParameter(WebGLShader *shader, GLenum pname);
-    JS::Value GetShaderParameter(JSContext* /* unused */, WebGLShader *shader,
-                                 GLenum pname) {
-        return GetShaderParameter(shader, pname);
+    void GetShaderParameter(JSContext* /* unused */, WebGLShader *shader,
+                            GLenum pname,
+                            JS::MutableHandle<JS::Value> retval) {
+        retval.set(GetShaderParameter(shader, pname));
     }
     already_AddRefed<WebGLShaderPrecisionFormat>
       GetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype);
     void GetShaderInfoLog(WebGLShader *shader, nsACString& retval);
     void GetShaderInfoLog(WebGLShader *shader, nsAString& retval);
     void GetShaderSource(WebGLShader *shader, nsAString& retval);
     void GetShaderTranslatedSource(WebGLShader *shader, nsAString& retval);
     JS::Value GetTexParameter(GLenum target, GLenum pname);
-    JS::Value GetTexParameter(JSContext * /* unused */, GLenum target,
-                              GLenum pname) {
-        return GetTexParameter(target, pname);
+    void GetTexParameter(JSContext * /* unused */, GLenum target,
+                         GLenum pname,
+                         JS::MutableHandle<JS::Value> retval) {
+        retval.set(GetTexParameter(target, pname));
     }
     JS::Value GetUniform(JSContext* cx, WebGLProgram *prog,
                          WebGLUniformLocation *location);
+    void GetUniform(JSContext* cx, WebGLProgram *prog,
+                    WebGLUniformLocation *location,
+                    JS::MutableHandle<JS::Value> retval) {
+        retval.set(GetUniform(cx, prog, location));
+    }
     already_AddRefed<WebGLUniformLocation>
       GetUniformLocation(WebGLProgram *prog, const nsAString& name);
     void Hint(GLenum target, GLenum mode);
     bool IsFramebuffer(WebGLFramebuffer *fb);
     bool IsProgram(WebGLProgram *prog);
     bool IsRenderbuffer(WebGLRenderbuffer *rb);
     bool IsShader(WebGLShader *shader);
     bool IsTexture(WebGLTexture *tex);
@@ -686,16 +707,20 @@ public:
 public:
     already_AddRefed<WebGLQuery> CreateQuery();
     void DeleteQuery(WebGLQuery *query);
     void BeginQuery(GLenum target, WebGLQuery *query);
     void EndQuery(GLenum target);
     bool IsQuery(WebGLQuery *query);
     already_AddRefed<WebGLQuery> GetQuery(GLenum target, GLenum pname);
     JS::Value GetQueryObject(JSContext* cx, WebGLQuery *query, GLenum pname);
+    void GetQueryObject(JSContext* cx, WebGLQuery *query, GLenum pname,
+                        JS::MutableHandle<JS::Value> retval) {
+        retval.set(GetQueryObject(cx, query, pname));
+    }
 
 private:
     // ANY_SAMPLES_PASSED(_CONSERVATIVE) slot
     WebGLRefPtr<WebGLQuery> mActiveOcclusionQuery;
 
     // LOCAL_GL_TRANSFORM_FEEDBACK_PRIMITIVES_WRITTEN slot
     WebGLRefPtr<WebGLQuery> mActiveTransformFeedbackQuery;
 
@@ -735,17 +760,22 @@ private:
     bool ValidateBufferUsageEnum(GLenum target, const char* infos);
 
 // -----------------------------------------------------------------------------
 // State and State Requests (WebGLContextState.cpp)
 public:
     void Disable(GLenum cap);
     void Enable(GLenum cap);
     JS::Value GetParameter(JSContext* cx, GLenum pname, ErrorResult& rv);
-    JS::Value GetParameterIndexed(JSContext* cx, GLenum pname, GLuint index);
+    void GetParameter(JSContext* cx, GLenum pname,
+                      JS::MutableHandle<JS::Value> retval, ErrorResult& rv) {
+        retval.set(GetParameter(cx, pname, rv));
+    }
+    void GetParameterIndexed(JSContext* cx, GLenum pname, GLuint index,
+                             JS::MutableHandle<JS::Value> retval);
     bool IsEnabled(GLenum cap);
 
 private:
     // State tracking slots
     realGLboolean mDitherEnabled;
     realGLboolean mRasterizerDiscardEnabled;
     realGLboolean mScissorTestEnabled;
 
@@ -761,16 +791,21 @@ public:
     void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type,
                                WebGLintptr byteOffset, GLsizei primcount);
 
     void EnableVertexAttribArray(GLuint index);
     void DisableVertexAttribArray(GLuint index);
 
     JS::Value GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
                               ErrorResult& rv);
+    void GetVertexAttrib(JSContext* cx, GLuint index, GLenum pname,
+                         JS::MutableHandle<JS::Value> retval,
+                         ErrorResult& rv) {
+        retval.set(GetVertexAttrib(cx, index, pname, rv));
+    }
     WebGLsizeiptr GetVertexAttribOffset(GLuint index, GLenum pname);
 
     void VertexAttrib1f(GLuint index, GLfloat x0);
     void VertexAttrib2f(GLuint index, GLfloat x0, GLfloat x1);
     void VertexAttrib3f(GLuint index, GLfloat x0, GLfloat x1,
                         GLfloat x2);
     void VertexAttrib4f(GLuint index, GLfloat x0, GLfloat x1,
                         GLfloat x2, GLfloat x3);
--- a/content/canvas/src/WebGLContextExtensions.cpp
+++ b/content/canvas/src/WebGLContextExtensions.cpp
@@ -177,21 +177,25 @@ bool WebGLContext::IsExtensionSupported(
 }
 
 static bool
 CompareWebGLExtensionName(const nsACString& name, const char *other)
 {
     return name.Equals(other, nsCaseInsensitiveCStringComparator());
 }
 
-JSObject*
-WebGLContext::GetExtension(JSContext *cx, const nsAString& aName, ErrorResult& rv)
+void
+WebGLContext::GetExtension(JSContext *cx, const nsAString& aName,
+                           JS::MutableHandle<JSObject*> aRetval,
+                           ErrorResult& rv)
 {
-    if (IsContextLost())
-        return nullptr;
+    if (IsContextLost()) {
+        aRetval.set(nullptr);
+        return;
+    }
 
     NS_LossyConvertUTF16toASCII name(aName);
 
     WebGLExtensionID ext = WebGLExtensionID::Unknown;
 
     // step 1: figure what extension is wanted
     for (size_t i = 0; i < size_t(WebGLExtensionID::Max); i++)
     {
@@ -230,30 +234,32 @@ WebGLContext::GetExtension(JSContext *cx
             GenerateWarning("getExtension('%s'): MOZ_ prefixed WebGL extension strings are deprecated. "
                             "Support for them will be removed in the future. Use unprefixed extension strings. "
                             "To get draft extensions, set the webgl.enable-draft-extensions preference.",
                             name.get());
         }
     }
 
     if (ext == WebGLExtensionID::Unknown) {
-        return nullptr;
+        aRetval.set(nullptr);
+        return;
     }
 
     // step 2: check if the extension is supported
     if (!IsExtensionSupported(cx, ext)) {
-        return nullptr;
+        aRetval.set(nullptr);
+        return;
     }
 
     // step 3: if the extension hadn't been previously been created, create it now, thus enabling it
     if (!IsExtensionEnabled(ext)) {
         EnableExtension(ext);
     }
 
-    return WebGLObjectAsJSObject(cx, mExtensions[ext].get(), rv);
+    aRetval.set(WebGLObjectAsJSObject(cx, mExtensions[ext].get(), rv));
 }
 
 void
 WebGLContext::EnableExtension(WebGLExtensionID ext)
 {
     MOZ_ASSERT(IsExtensionEnabled(ext) == false);
 
     WebGLExtensionBase* obj = nullptr;
--- a/content/canvas/src/WebGLContextState.cpp
+++ b/content/canvas/src/WebGLContextState.cpp
@@ -449,40 +449,45 @@ WebGLContext::GetParameter(JSContext* cx
         default:
             break;
     }
 
     ErrorInvalidEnumInfo("getParameter: parameter", pname);
     return JS::NullValue();
 }
 
-JS::Value
-WebGLContext::GetParameterIndexed(JSContext* cx, GLenum pname, GLuint index)
+void
+WebGLContext::GetParameterIndexed(JSContext* cx, GLenum pname, GLuint index,
+                                  JS::MutableHandle<JS::Value> retval)
 {
-    if (IsContextLost())
-        return JS::NullValue();
+    if (IsContextLost()) {
+        retval.setNull();
+        return;
+    }
 
     MakeContextCurrent();
 
     switch (pname) {
         case LOCAL_GL_TRANSFORM_FEEDBACK_BUFFER_BINDING:
         {
             if (index >= mGLMaxTransformFeedbackSeparateAttribs) {
                 ErrorInvalidValue("getParameterIndexed: index should be less than MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS", index);
-                return JS::NullValue();
+                retval.setNull();
+                return;
             }
-            return JS::NullValue(); // See bug 903594
+            retval.setNull(); // See bug 903594
+            return;
         }
 
         default:
             break;
     }
 
     ErrorInvalidEnumInfo("getParameterIndexed: parameter", pname);
-    return JS::NullValue();
+    retval.setNull();
 }
 
 bool
 WebGLContext::IsEnabled(GLenum cap)
 {
     if (IsContextLost())
         return false;
 
--- a/content/html/content/public/HTMLMediaElement.h
+++ b/content/html/content/public/HTMLMediaElement.h
@@ -524,17 +524,18 @@ public:
 
   already_AddRefed<DOMMediaStream> MozCaptureStreamUntilEnded(ErrorResult& aRv);
 
   bool MozAudioCaptured() const
   {
     return mAudioCaptured;
   }
 
-  JSObject* MozGetMetadata(JSContext* aCx, ErrorResult& aRv);
+  void MozGetMetadata(JSContext* aCx, JS::MutableHandle<JSObject*> aResult,
+                      ErrorResult& aRv);
 
   double MozFragmentEnd();
 
   AudioChannel MozAudioChannelType() const
   {
     return mAudioChannel;
   }
 
--- a/content/html/content/src/HTMLMediaElement.cpp
+++ b/content/html/content/src/HTMLMediaElement.cpp
@@ -1668,48 +1668,50 @@ HTMLMediaElement::BuildObjectFromTags(ns
     NS_WARNING("Failed to set metadata property");
     args->error = true;
     return PL_DHASH_STOP;
   }
 
   return PL_DHASH_NEXT;
 }
 
-JSObject*
-HTMLMediaElement::MozGetMetadata(JSContext* cx, ErrorResult& aRv)
+void
+HTMLMediaElement::MozGetMetadata(JSContext* cx,
+                                 JS::MutableHandle<JSObject*> aRetval,
+                                 ErrorResult& aRv)
 {
   if (mReadyState < nsIDOMHTMLMediaElement::HAVE_METADATA) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
-    return nullptr;
+    return;
   }
 
   JS::Rooted<JSObject*> tags(cx, JS_NewObject(cx, nullptr, JS::NullPtr(), JS::NullPtr()));
   if (!tags) {
     aRv.Throw(NS_ERROR_FAILURE);
-    return nullptr;
+    return;
   }
   if (mTags) {
     MetadataIterCx iter = {cx, tags, false};
     mTags->EnumerateRead(BuildObjectFromTags, static_cast<void*>(&iter));
     if (iter.error) {
       NS_WARNING("couldn't create metadata object!");
       aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
+      return;
     }
   }
 
-  return tags;
+  aRetval.set(tags);
 }
 
 NS_IMETHODIMP
 HTMLMediaElement::MozGetMetadata(JSContext* cx, JS::MutableHandle<JS::Value> aValue)
 {
   ErrorResult rv;
-
-  JSObject* obj = MozGetMetadata(cx, rv);
+  JS::Rooted<JSObject*> obj(cx);
+  MozGetMetadata(cx, &obj, rv);
   if (!rv.Failed()) {
     MOZ_ASSERT(obj);
     aValue.setObject(*obj);
   }
 
   return rv.ErrorCode();
 }
 
--- a/content/html/content/src/HTMLPropertiesCollection.cpp
+++ b/content/html/content/src/HTMLPropertiesCollection.cpp
@@ -421,17 +421,18 @@ PropertyNodeList::GetValues(JSContext* a
                             ErrorResult& aError)
 {
   EnsureFresh();
 
   JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
   JSAutoCompartment ac(aCx, wrapper);
   uint32_t length = mElements.Length();
   for (uint32_t i = 0; i < length; ++i) {
-    JS::Value v = mElements.ElementAt(i)->GetItemValue(aCx, wrapper, aError);
+    JS::Rooted<JS::Value> v(aCx);
+    mElements.ElementAt(i)->GetItemValue(aCx, wrapper, &v, aError);
     if (aError.Failed()) {
       return;
     }
     aResult.AppendElement(v);
   }
 }
 
 void
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -3036,43 +3036,41 @@ nsGenericHTMLFormElementWithState::Resto
 }
 
 void
 nsGenericHTMLFormElementWithState::NodeInfoChanged(nsINodeInfo* aOldNodeInfo)
 {
   mStateKey.SetIsVoid(true);
 }
 
-JS::Value
+void
 nsGenericHTMLElement::GetItemValue(JSContext* aCx, JSObject* aScope,
+                                   JS::MutableHandle<JS::Value> aRetval,
                                    ErrorResult& aError)
 {
   JS::Rooted<JSObject*> scope(aCx, aScope);
   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::itemprop)) {
-    return JS::NullValue();
+    aRetval.setNull();
+    return;
   }
 
   if (ItemScope()) {
     JS::Rooted<JS::Value> v(aCx);
     JSAutoCompartment ac(aCx, scope);
-    if (!mozilla::dom::WrapObject(aCx, this, &v)) {
+    if (!mozilla::dom::WrapObject(aCx, this, aRetval)) {
       aError.Throw(NS_ERROR_FAILURE);
-      return JS::UndefinedValue();
     }
-    return v;
+    return;
   }
 
   nsString string;
   GetItemValueText(string);
-  JS::Rooted<JS::Value> v(aCx);
-  if (!xpc::NonVoidStringToJsval(aCx, string, &v)) {
+  if (!xpc::NonVoidStringToJsval(aCx, string, aRetval)) {
     aError.Throw(NS_ERROR_FAILURE);
-    return JS::UndefinedValue();
   }
-  return v;
 }
 
 NS_IMETHODIMP
 nsGenericHTMLElement::GetItemValue(nsIVariant** aValue)
 {
   nsCOMPtr<nsIWritableVariant> out = new nsVariant();
 
   if (!HasAttr(kNameSpaceID_None, nsGkAtoms::itemprop)) {
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -119,21 +119,23 @@ public:
   {
     return GetTokenList(nsGkAtoms::itemref);
   }
   nsDOMSettableTokenList* ItemProp()
   {
     return GetTokenList(nsGkAtoms::itemprop);
   }
   mozilla::dom::HTMLPropertiesCollection* Properties();
-  JS::Value GetItemValue(JSContext* aCx, JSObject* aScope,
-                         mozilla::ErrorResult& aError);
-  JS::Value GetItemValue(JSContext* aCx, mozilla::ErrorResult& aError)
+  void GetItemValue(JSContext* aCx, JSObject* aScope,
+                    JS::MutableHandle<JS::Value> aRetval,
+                    mozilla::ErrorResult& aError);
+  void GetItemValue(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
+                    mozilla::ErrorResult& aError)
   {
-    return GetItemValue(aCx, GetWrapperPreserveColor(), aError);
+    GetItemValue(aCx, GetWrapperPreserveColor(), aRetval, aError);
   }
   void SetItemValue(JSContext* aCx, JS::Value aValue,
                     mozilla::ErrorResult& aError);
   bool Hidden() const
   {
     return GetBoolAttr(nsGkAtoms::hidden);
   }
   void SetHidden(bool aHidden, mozilla::ErrorResult& aError)
--- a/content/html/document/src/nsHTMLDocument.cpp
+++ b/content/html/document/src/nsHTMLDocument.cpp
@@ -2239,36 +2239,38 @@ nsHTMLDocument::ResolveName(const nsAStr
     *aCache = e;
     return e;
   }
 
   *aCache = nullptr;
   return nullptr;
 }
 
-JSObject*
+void
 nsHTMLDocument::NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
+                            JS::MutableHandle<JSObject*> aRetval,
                             ErrorResult& rv)
 {
   nsWrapperCache* cache;
   nsISupports* supp = ResolveName(aName, &cache);
   if (!supp) {
     aFound = false;
-    return nullptr;
+    aRetval.set(nullptr);
+    return;
   }
 
   JS::Rooted<JS::Value> val(cx);
   // XXXbz Should we call the (slightly misnamed, really) WrapNativeParent
   // here?
   if (!dom::WrapObject(cx, supp, cache, nullptr, &val)) {
     rv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return nullptr;
+    return;
   }
   aFound = true;
-  return &val.toObject();
+  aRetval.set(&val.toObject());
 }
 
 bool
 nsHTMLDocument::NameIsEnumerable(const nsAString& aName)
 {
   return true;
 }
 
--- a/content/html/document/src/nsHTMLDocument.h
+++ b/content/html/document/src/nsHTMLDocument.h
@@ -169,18 +169,19 @@ public:
 
   // WebIDL API
   virtual JSObject* WrapNode(JSContext* aCx)
     MOZ_OVERRIDE;
   void GetDomain(nsAString& aDomain, mozilla::ErrorResult& rv);
   void SetDomain(const nsAString& aDomain, mozilla::ErrorResult& rv);
   void GetCookie(nsAString& aCookie, mozilla::ErrorResult& rv);
   void SetCookie(const nsAString& aCookie, mozilla::ErrorResult& rv);
-  JSObject* NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
-                        mozilla::ErrorResult& rv);
+  void NamedGetter(JSContext* cx, const nsAString& aName, bool& aFound,
+                   JS::MutableHandle<JSObject*> aRetval,
+                   mozilla::ErrorResult& rv);
   bool NameIsEnumerable(const nsAString& aName);
   void GetSupportedNames(unsigned, nsTArray<nsString>& aNames);
   nsGenericHTMLElement *GetBody();
   void SetBody(nsGenericHTMLElement* aBody, mozilla::ErrorResult& rv);
   mozilla::dom::HTMLSharedElement *GetHead() {
     return static_cast<mozilla::dom::HTMLSharedElement*>(GetHeadElement());
   }
   nsIHTMLCollection* Images();
--- a/content/media/eme/MediaKeyMessageEvent.cpp
+++ b/content/media/eme/MediaKeyMessageEvent.cpp
@@ -94,32 +94,34 @@ MediaKeyMessageEvent::Constructor(const 
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return nullptr;
   }
   e->mDestinationURL = aEventInitDict.mDestinationURL;
   e->SetTrusted(trusted);
   return e.forget();
 }
 
-JSObject*
-MediaKeyMessageEvent::GetMessage(JSContext* cx, ErrorResult& aRv)
+void
+MediaKeyMessageEvent::GetMessage(JSContext* cx,
+                                 JS::MutableHandle<JSObject*> aMessage,
+                                 ErrorResult& aRv)
 {
   if (!mMessage) {
     mMessage = Uint8Array::Create(cx,
                                   this,
                                   mRawMessage.Length(),
                                   mRawMessage.Elements());
     if (!mMessage) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return nullptr;
+      return;
     }
     mRawMessage.Clear();
   }
   JS::ExposeObjectToActiveJS(mMessage);
-  return mMessage;
+  aMessage.set(mMessage);
 }
 
 void
 MediaKeyMessageEvent::GetDestinationURL(nsString& aRetVal) const
 {
   aRetVal = mDestinationURL;
 }
 
--- a/content/media/eme/MediaKeyMessageEvent.h
+++ b/content/media/eme/MediaKeyMessageEvent.h
@@ -45,17 +45,19 @@ public:
                 const nsTArray<uint8_t>& aMessage);
 
   static already_AddRefed<MediaKeyMessageEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const MediaKeyMessageEventInit& aEventInitDict,
               ErrorResult& aRv);
 
-  JSObject* GetMessage(JSContext* cx, ErrorResult& aRv);
+  void GetMessage(JSContext* cx,
+                  JS::MutableHandle<JSObject*> aMessage,
+                  ErrorResult& aRv);
 
   void GetDestinationURL(nsString& aRetVal) const;
 
 private:
   nsTArray<uint8_t> mRawMessage;
 };
 
 
--- a/content/media/eme/MediaKeyNeededEvent.cpp
+++ b/content/media/eme/MediaKeyNeededEvent.cpp
@@ -89,30 +89,32 @@ MediaKeyNeededEvent::Constructor(const G
 }
 
 void
 MediaKeyNeededEvent::GetInitDataType(nsString& aRetVal) const
 {
   aRetVal = mInitDataType;
 }
 
-JSObject*
-MediaKeyNeededEvent::GetInitData(JSContext* cx, ErrorResult& aRv)
+void
+MediaKeyNeededEvent::GetInitData(JSContext* cx,
+                                 JS::MutableHandle<JSObject*> aData,
+                                 ErrorResult& aRv)
 {
   if (mRawInitData.Length()) {
     mInitData = Uint8Array::Create(cx,
                                    this,
                                    mRawInitData.Length(),
                                    mRawInitData.Elements());
     if (!mInitData) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return nullptr;
+      return;
     }
     mRawInitData.Clear();
   }
   if (mInitData) {
     JS::ExposeObjectToActiveJS(mInitData);
   }
-  return mInitData;
+  aData.set(mInitData);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/content/media/eme/MediaKeyNeededEvent.h
+++ b/content/media/eme/MediaKeyNeededEvent.h
@@ -46,17 +46,19 @@ public:
   static already_AddRefed<MediaKeyNeededEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const MediaKeyNeededEventInit& aEventInitDict,
               ErrorResult& aRv);
 
   void GetInitDataType(nsString& aRetVal) const;
 
-  JSObject* GetInitData(JSContext* cx, ErrorResult& aRv);
+  void GetInitData(JSContext* cx,
+                   JS::MutableHandle<JSObject*> aData,
+                   ErrorResult& aRv);
 private:
   nsTArray<uint8_t> mRawInitData;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_MediaKeyNeededEvent_h__
--- a/content/media/webaudio/AudioBuffer.cpp
+++ b/content/media/webaudio/AudioBuffer.cpp
@@ -187,31 +187,35 @@ AudioBuffer::CopyToChannel(JSContext* aJ
 void
 AudioBuffer::SetRawChannelContents(uint32_t aChannel, float* aContents)
 {
   MOZ_ASSERT(!GetWrapperPreserveColor() && !mSharedChannels,
              "The AudioBuffer object should not have been handed to JS or have C++ callers neuter its typed array");
   PodCopy(JS_GetFloat32ArrayData(mJSChannels[aChannel]), aContents, mLength);
 }
 
-JSObject*
+void
 AudioBuffer::GetChannelData(JSContext* aJSContext, uint32_t aChannel,
+                            JS::MutableHandle<JSObject*> aRetval,
                             ErrorResult& aRv)
 {
   if (aChannel >= NumberOfChannels()) {
     aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
-    return nullptr;
+    return;
   }
 
   if (!RestoreJSChannelData(aJSContext)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return nullptr;
+    return;
   }
 
-  return mJSChannels[aChannel];
+  if (mJSChannels[aChannel]) {
+    JS::ExposeObjectToActiveJS(mJSChannels[aChannel]);
+  }
+  aRetval.set(mJSChannels[aChannel]);
 }
 
 static already_AddRefed<ThreadSharedFloatArrayBufferList>
 StealJSArrayDataIntoThreadSharedFloatArrayBufferList(JSContext* aJSContext,
                                                      const nsTArray<JSObject*>& aJSArrays)
 {
   nsRefPtr<ThreadSharedFloatArrayBufferList> result =
     new ThreadSharedFloatArrayBufferList(aJSArrays.Length());
--- a/content/media/webaudio/AudioBuffer.h
+++ b/content/media/webaudio/AudioBuffer.h
@@ -69,18 +69,19 @@ public:
   {
     return mJSChannels.Length();
   }
 
   /**
    * If mSharedChannels is non-null, copies its contents to
    * new Float32Arrays in mJSChannels. Returns a Float32Array.
    */
-  JSObject* GetChannelData(JSContext* aJSContext, uint32_t aChannel,
-                           ErrorResult& aRv);
+  void GetChannelData(JSContext* aJSContext, uint32_t aChannel,
+                      JS::MutableHandle<JSObject*> aRetval,
+                      ErrorResult& aRv);
 
   void CopyFromChannel(const Float32Array& aDestination, uint32_t aChannelNumber,
                        uint32_t aStartInChannel, ErrorResult& aRv);
   void CopyToChannel(JSContext* aJSContext, const Float32Array& aSource,
                      uint32_t aChannelNumber, uint32_t aStartInChannel,
                      ErrorResult& aRv);
 
   /**
--- a/content/media/webaudio/WaveShaperNode.h
+++ b/content/media/webaudio/WaveShaperNode.h
@@ -22,19 +22,22 @@ public:
   explicit WaveShaperNode(AudioContext *aContext);
   virtual ~WaveShaperNode();
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WaveShaperNode, AudioNode)
 
   virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE;
 
-  JSObject* GetCurve(JSContext* aCx) const
+  void GetCurve(JSContext* aCx, JS::MutableHandle<JSObject*> aRetval) const
   {
-    return mCurve;
+    if (mCurve) {
+      JS::ExposeObjectToActiveJS(mCurve);
+    }
+    aRetval.set(mCurve);
   }
   void SetCurve(const Nullable<Float32Array>& aData);
 
   OverSampleType Oversample() const
   {
     return mType;
   }
   void SetOversample(OverSampleType aType);
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1,9 +1,9 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=4 sw=4 tw=80 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDocShell.h"
 
 #include <algorithm>
@@ -24,16 +24,17 @@
 #include "mozilla/VisualEventTracer.h"
 
 #ifdef MOZ_LOGGING
 // so we can get logging even in release builds (but only for some things)
 #define FORCE_PR_LOG 1
 #endif
 
 #include "nsIContent.h"
+#include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 #include "nsIContentViewer.h"
 #include "nsIDocumentLoaderFactory.h"
 #include "nsCURILoader.h"
@@ -331,30 +332,39 @@ CheckPingURI(nsIURI* uri, nsIContent* co
                                  nullptr, //extra
                                  &shouldLoad);
   return NS_SUCCEEDED(rv) && NS_CP_ACCEPTED(shouldLoad);
 }
 
 typedef void (* ForEachPingCallback)(void *closure, nsIContent *content,
                                      nsIURI *uri, nsIIOService *ios);
 
+static bool
+IsElementAnchor(nsIContent* aContent)
+{
+  // Make sure we are dealing with either an <A> or <AREA> element in the HTML
+  // or XHTML namespace.
+  if (!aContent->IsHTML()) {
+    return false;
+  }
+  nsIAtom* nameAtom = aContent->Tag();
+  return nameAtom == nsGkAtoms::a || nameAtom == nsGkAtoms::area;
+}
+
 static void
 ForEachPing(nsIContent *content, ForEachPingCallback callback, void *closure)
 {
   // NOTE: Using nsIDOMHTMLAnchorElement::GetPing isn't really worth it here
   //       since we'd still need to parse the resulting string.  Instead, we
   //       just parse the raw attribute.  It might be nice if the content node
   //       implemented an interface that exposed an enumeration of nsIURIs.
 
   // Make sure we are dealing with either an <A> or <AREA> element in the HTML
   // or XHTML namespace.
-  if (!content->IsHTML())
-    return;
-  nsIAtom *nameAtom = content->Tag();
-  if (nameAtom != nsGkAtoms::a && nameAtom != nsGkAtoms::area)
+  if (!IsElementAnchor(content))
     return;
 
   nsCOMPtr<nsIAtom> pingAtom = do_GetAtom("ping");
   if (!pingAtom)
     return;
 
   nsAutoString value;
   content->GetAttr(kNameSpaceID_None, pingAtom, value);
@@ -9075,16 +9085,21 @@ nsDocShell::InternalLoad(nsIURI * aURI,
             // window being opened.  We can detect these cases by examining the
             // document in |newWin|, if any.
             nsCOMPtr<nsPIDOMWindow> piNewWin = do_QueryInterface(newWin);
             if (piNewWin) {
                 nsCOMPtr<nsIDocument> newDoc = piNewWin->GetExtantDoc();
                 if (!newDoc || newDoc->IsInitialDocument()) {
                     isNewWindow = true;
                     aFlags |= INTERNAL_LOAD_FLAGS_FIRST_LOAD;
+
+                    // set opener object to null for noreferrer
+                    if (aFlags & INTERNAL_LOAD_FLAGS_NO_OPENER) {
+                      piNewWin->SetOpenerWindow(nullptr, false);
+                    }
                 }
             }
 
             nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(newWin);
             targetDocShell = do_QueryInterface(webNav);
         }
 
         //
@@ -12629,16 +12644,28 @@ nsDocShell::OnLinkClickSync(nsIContent *
         nsresult rv = extProtService->IsExposedProtocol(scheme.get(), &isExposed);
         if (NS_SUCCEEDED(rv) && !isExposed) {
           return extProtService->LoadURI(aURI, this); 
         }
       }
     }
   }
 
+  uint32_t flags = INTERNAL_LOAD_FLAGS_NONE;
+  if (IsElementAnchor(aContent)) {
+    MOZ_ASSERT(aContent->IsHTML());
+    if (aContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::rel,
+                              NS_LITERAL_STRING("noreferrer"),
+                              aContent->IsInHTMLDocument() ?
+                              eIgnoreCase : eCaseMatters)) {
+        flags |= INTERNAL_LOAD_FLAGS_DONT_SEND_REFERRER |
+                 INTERNAL_LOAD_FLAGS_NO_OPENER;
+    }
+  }
+
   // Get the owner document of the link that was clicked, this will be
   // the document that the link is in, or the last document that the
   // link was in. From that document, we'll get the URI to use as the
   // referer, since the current URI in this docshell may be a
   // new document that we're in the process of loading.
   nsCOMPtr<nsIDocument> refererDoc = aContent->OwnerDoc();
   NS_ENSURE_TRUE(refererDoc, NS_ERROR_UNEXPECTED);
 
@@ -12679,17 +12706,17 @@ nsDocShell::OnLinkClickSync(nsIContent *
   if (!clonedURI) {
     return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsresult rv = InternalLoad(clonedURI,                 // New URI
                              referer,                   // Referer URI
                              aContent->NodePrincipal(), // Owner is our node's
                                                         // principal
-                             INTERNAL_LOAD_FLAGS_NONE,
+                             flags,
                              target.get(),              // Window target
                              NS_LossyConvertUTF16toASCII(typeHint).get(),
                              aFileName,                 // Download as file
                              aPostDataStream,           // Post data stream
                              aHeadersDataStream,        // Headers stream
                              LOAD_LINK,                 // Load type
                              nullptr,                   // No SHEntry
                              true,                      // first party site
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -109,16 +109,17 @@ interface nsIDocShell : nsIDocShellTreeI
 
   const long INTERNAL_LOAD_FLAGS_BYPASS_CLASSIFIER       = 0x10;
   const long INTERNAL_LOAD_FLAGS_FORCE_ALLOW_COOKIES     = 0x20;
 
   // Whether the load should be treated as srcdoc load, rather than a URI one.
   const long INTERNAL_LOAD_FLAGS_IS_SRCDOC               = 0x40;
 
   const long INTERNAL_LOAD_FLAGS_FIXUP_SCHEME_TYPOS      = 0x80;
+  const long INTERNAL_LOAD_FLAGS_NO_OPENER               = 0x100;
 
   /**
    * Loads the given URI.  This method is identical to loadURI(...) except
    * that its parameter list is broken out instead of being packaged inside
    * of an nsIDocShellLoadInfo object...
    *
    * @param aURI            - The URI to load.
    * @param aReferrer       - Referring URI
new file mode 100644
--- /dev/null
+++ b/docshell/test/bug530396-noref.sjs
@@ -0,0 +1,20 @@
+function handleRequest(request, response) {
+  response.setHeader("Content-Type", "text/html");
+  response.setHeader("Cache-Control", "no-cache");
+  response.write("<body onload='");
+
+  if (!request.hasHeader('Referer')) {
+    response.write("window.parent.onloadCount++;");
+  }
+
+  if (request.queryString == "newwindow") {
+      response.write("if (window.opener) { window.opener.parent.onloadCount++; window.opener.parent.doNextStep(); }");
+      response.write("if (!window.opener) window.close();");
+      response.write("'>");
+  } else {
+    response.write("window.parent.doNextStep();'>");
+  }
+
+  response.write(request.method + " " + Date.now());
+  response.write("</body>");
+}
new file mode 100644
--- /dev/null
+++ b/docshell/test/bug530396-subframe.html
@@ -0,0 +1,7 @@
+<!DOCTYPE html>
+<html>
+  <body onload="window.parent.onloadCount++">
+    <a href="bug530396-noref.sjs" rel="noreferrer" id="target1">bug530396-noref.sjs</a>
+    <a href="bug530396-noref.sjs?newwindow" rel="noreferrer" id="target2" target="newwindow">bug530396-noref.sjs with new window</a>
+  </body>
+</html>
--- a/docshell/test/mochitest.ini
+++ b/docshell/test/mochitest.ini
@@ -55,16 +55,19 @@ skip-if = true || toolkit == 'android' |
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug511449.html]
 skip-if = toolkit != "cocoa" || e10s
 support-files = file_bug511449.html
 [test_bug529119-1.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug529119-2.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) # b2g-debug(debug-only failure) b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
+[test_bug530396.html]
+skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Timeouts on B2G desktop
+support-files = bug530396-noref.sjs bug530396-subframe.html
 [test_bug540462.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug551225.html]
 [test_bug570341.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_bug580069.html]
 [test_bug590573.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #bug 823022 # b2g(queryinterfaces into webnavigation, might suffer from something similar as bug 823022) b2g-debug(queryinterfaces into webnavigation, might suffer from something similar as bug 823022) b2g-desktop(queryinterfaces into webnavigation, might suffer from something similar as bug 823022)
new file mode 100644
--- /dev/null
+++ b/docshell/test/test_bug530396.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=530396
+-->
+<head>
+  <title>Test for Bug 530396</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=530396">Mozilla Bug 530396</a>
+
+<p>
+
+<iframe id="testFrame" src="http://mochi.test:8888/tests/docshell/test/bug530396-subframe.html"></iframe>
+
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+// NOTE: If we ever make subframes do bfcache stuff, this test will need to be
+// modified accordingly!  It assumes that subframes do NOT get bfcached.
+var onloadCount = 0;
+
+var step = 0;
+
+var gTestFrame = document.getElementById('testFrame');
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(doNextStep);
+
+function doNextStep() {
+  ++step;
+  switch (step) {
+    case 1:
+      is(onloadCount, 1, "Loaded initial page");
+      sendMouseEvent({type: "click"}, "target2", gTestFrame.contentWindow);
+      window.setTimeout(doNextStep, 1000);
+      break;
+
+    case 2:
+      is(onloadCount, 1, "opener must be null");
+      sendMouseEvent({type: "click"}, "target1", gTestFrame.contentWindow);
+      break;
+
+    case 3:
+      is(onloadCount, 2, "don't send referrer with rel=referrer"); 
+      SimpleTest.finish();
+      break;
+  }
+}
+</script>
+</pre>
+</html>
--- a/dom/activities/src/Activity.cpp
+++ b/dom/activities/src/Activity.cpp
@@ -1,14 +1,14 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "Activity.h"
-
+#include "mozilla/dom/ToJSValue.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfo.h"
 #include "nsIConsoleService.h"
 #include "nsIDocShell.h"
 #include "nsIDocument.h"
 
 using namespace mozilla::dom;
 
@@ -62,17 +62,17 @@ Activity::Initialize(nsPIDOMWindow* aWin
 
   // Instantiate a JS proxy that will do the child <-> parent communication
   // with the JS implementation of the backend.
   nsresult rv;
   mProxy = do_CreateInstance("@mozilla.org/dom/activities/proxy;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   JS::Rooted<JS::Value> optionsValue(aCx);
-  if (!aOptions.ToObject(aCx, &optionsValue)) {
+  if (!ToJSValue(aCx, aOptions, &optionsValue)) {
     return NS_ERROR_FAILURE;
   }
 
   mProxy->StartActivity(static_cast<nsIDOMDOMRequest*>(this), optionsValue, aWindow);
   return NS_OK;
 }
 
 Activity::~Activity()
--- a/dom/apps/src/Webapps.js
+++ b/dom/apps/src/Webapps.js
@@ -16,17 +16,24 @@ Cu.import("resource://gre/modules/Browse
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 
 function convertAppsArray(aApps, aWindow) {
   let apps = new aWindow.Array();
   for (let i = 0; i < aApps.length; i++) {
     let app = aApps[i];
-    apps.push(createApplicationObject(aWindow, app));
+    // Our application objects are JS-implemented XPCOM objects with DOM_OBJECT
+    // set in classinfo. These objects are reflector-per-scope, so as soon as we
+    // pass them to content, we'll end up with a new object in content. But from
+    // this code, they _appear_ to be chrome objects, and so the Array Xray code
+    // vetos the attempt to define a chrome-privileged object on a content Array.
+    // Very carefully waive Xrays so that this can keep working until we convert
+    // mozApps to WebIDL in bug 899322.
+    Cu.waiveXrays(apps)[i] = createApplicationObject(aWindow, app);
   }
 
   return apps;
 }
 
 function WebappsRegistry() {
 }
 
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -692,17 +692,17 @@ Console::ProfileMethod(JSContext* aCx, c
   event.mArguments.Construct();
   Sequence<JS::Value>& sequence = event.mArguments.Value();
 
   for (uint32_t i = 0; i < aData.Length(); ++i) {
     sequence.AppendElement(aData[i]);
   }
 
   JS::Rooted<JS::Value> eventValue(aCx);
-  if (!event.ToObject(aCx, &eventValue)) {
+  if (!ToJSValue(aCx, event, &eventValue)) {
     return;
   }
 
   JS::Rooted<JSObject*> eventObj(aCx, &eventValue.toObject());
   MOZ_ASSERT(eventObj);
 
   if (!JS_DefineProperty(aCx, eventObj, "wrappedJSObject", eventValue,
       JSPROP_ENUMERATE)) {
@@ -1106,17 +1106,17 @@ Console::ProcessCallData(ConsoleCallData
   // Object Xrays.  So we want to wrap in a system-principal scope here.  But
   // which one?  We could cheat and try to get the underlying JSObject* of
   // mStorage, but that's a bit fragile.  Instead, we just use the junk scope,
   // with explicit permission from the XPConnect module owner.  If you're
   // tempted to do that anywhere else, talk to said module owner first.
   JSAutoCompartment ac2(cx, xpc::GetJunkScope());
 
   JS::Rooted<JS::Value> eventValue(cx);
-  if (!event.ToObject(cx, &eventValue)) {
+  if (!ToJSValue(cx, event, &eventValue)) {
     return;
   }
 
   JS::Rooted<JSObject*> eventObj(cx, &eventValue.toObject());
   MOZ_ASSERT(eventObj);
 
   if (!JS_DefineProperty(cx, eventObj, "wrappedJSObject", eventValue, JSPROP_ENUMERATE)) {
     return;
@@ -1480,17 +1480,17 @@ Console::ComposeGroupName(JSContext* aCx
 JS::Value
 Console::StartTimer(JSContext* aCx, const JS::Value& aName,
                     DOMHighResTimeStamp aTimestamp)
 {
   if (mTimerRegistry.Count() >= MAX_PAGE_TIMERS) {
     RootedDictionary<ConsoleTimerError> error(aCx);
 
     JS::Rooted<JS::Value> value(aCx);
-    if (!error.ToObject(aCx, &value)) {
+    if (!ToJSValue(aCx, error, &value)) {
       return JS::UndefinedValue();
     }
 
     return value;
   }
 
   RootedDictionary<ConsoleTimerStart> timer(aCx);
 
@@ -1512,17 +1512,17 @@ Console::StartTimer(JSContext* aCx, cons
     mTimerRegistry.Put(key, aTimestamp);
   } else {
     aTimestamp = entry;
   }
 
   timer.mStarted = aTimestamp;
 
   JS::Rooted<JS::Value> value(aCx);
-  if (!timer.ToObject(aCx, &value)) {
+  if (!ToJSValue(aCx, timer, &value)) {
     return JS::UndefinedValue();
   }
 
   return value;
 }
 
 JS::Value
 Console::StopTimer(JSContext* aCx, const JS::Value& aName,
@@ -1546,17 +1546,17 @@ Console::StopTimer(JSContext* aCx, const
 
   mTimerRegistry.Remove(key);
 
   RootedDictionary<ConsoleTimerEnd> timer(aCx);
   timer.mName = key;
   timer.mDuration = aTimestamp - entry;
 
   JS::Rooted<JS::Value> value(aCx);
-  if (!timer.ToObject(aCx, &value)) {
+  if (!ToJSValue(aCx, timer, &value)) {
     return JS::UndefinedValue();
   }
 
   return value;
 }
 
 void
 Console::ArgumentsToValueList(const nsTArray<JS::Heap<JS::Value>>& aData,
@@ -1594,33 +1594,33 @@ Console::IncreaseCounter(JSContext* aCx,
   }
 
   uint32_t count = 0;
   if (!mCounterRegistry.Get(key, &count)) {
     if (mCounterRegistry.Count() >= MAX_PAGE_COUNTERS) {
       RootedDictionary<ConsoleCounterError> error(aCx);
 
       JS::Rooted<JS::Value> value(aCx);
-      if (!error.ToObject(aCx, &value)) {
+      if (!ToJSValue(aCx, error, &value)) {
         return JS::UndefinedValue();
       }
 
       return value;
     }
   }
 
   ++count;
   mCounterRegistry.Put(key, count);
 
   RootedDictionary<ConsoleCounter> data(aCx);
   data.mLabel = label;
   data.mCount = count;
 
   JS::Rooted<JS::Value> value(aCx);
-  if (!data.ToObject(aCx, &value)) {
+  if (!ToJSValue(aCx, data, &value)) {
     return JS::UndefinedValue();
   }
 
   return value;
 }
 
 void
 Console::ClearConsoleData()
--- a/dom/base/Crypto.cpp
+++ b/dom/base/Crypto.cpp
@@ -50,18 +50,19 @@ Crypto::Init(nsIDOMWindow* aWindow)
 }
 
 /* virtual */ JSObject*
 Crypto::WrapObject(JSContext* aCx)
 {
   return CryptoBinding::Wrap(aCx, this);
 }
 
-JSObject *
+void
 Crypto::GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
+			JS::MutableHandle<JSObject*> aRetval,
 			ErrorResult& aRv)
 {
   NS_ABORT_IF_FALSE(NS_IsMainThread(), "Called on the wrong thread");
 
   JS::Rooted<JSObject*> view(aCx, aArray.Obj());
 
   // Throw if the wrong type of ArrayBufferView is passed in
   // (Part of the Web Crypto API spec)
@@ -71,56 +72,57 @@ Crypto::GetRandomValues(JSContext* aCx, 
     case TYPE_UINT8_CLAMPED:
     case TYPE_INT16:
     case TYPE_UINT16:
     case TYPE_INT32:
     case TYPE_UINT32:
       break;
     default:
       aRv.Throw(NS_ERROR_DOM_TYPE_MISMATCH_ERR);
-      return nullptr;
+      return;
   }
 
   aArray.ComputeLengthAndData();
   uint32_t dataLen = aArray.Length();
   if (dataLen == 0) {
     NS_WARNING("ArrayBufferView length is 0, cannot continue");
-    return view;
+    aRetval.set(view);
+    return;
   } else if (dataLen > 65536) {
     aRv.Throw(NS_ERROR_DOM_QUOTA_EXCEEDED_ERR);
-    return nullptr;
+    return;
   }
 
   uint8_t* data = aArray.Data();
 
   if (XRE_GetProcessType() != GeckoProcessType_Default) {
     InfallibleTArray<uint8_t> randomValues;
     // Tell the parent process to generate random values via PContent
     ContentChild* cc = ContentChild::GetSingleton();
     if (!cc->SendGetRandomValues(dataLen, &randomValues) ||
         randomValues.Length() == 0) {
       aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
+      return;
     }
     NS_ASSERTION(dataLen == randomValues.Length(),
                  "Invalid length returned from parent process!");
     memcpy(data, randomValues.Elements(), dataLen);
   } else {
     uint8_t *buf = GetRandomValues(dataLen);
 
     if (!buf) {
       aRv.Throw(NS_ERROR_FAILURE);
-      return nullptr;
+      return;
     }
 
     memcpy(data, buf, dataLen);
     NS_Free(buf);
   }
 
-  return view;
+  aRetval.set(view);
 }
 
 SubtleCrypto*
 Crypto::Subtle()
 {
   if(!mSubtle) {
     mSubtle = new SubtleCrypto(GetParentObject());
   }
--- a/dom/base/Crypto.h
+++ b/dom/base/Crypto.h
@@ -36,18 +36,19 @@ public:
   Crypto();
   virtual ~Crypto();
 
   NS_DECL_NSIDOMCRYPTO
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Crypto)
 
-  JSObject *
+  void
   GetRandomValues(JSContext* aCx, const ArrayBufferView& aArray,
+		  JS::MutableHandle<JSObject*> aRetval,
 		  ErrorResult& aRv);
 
   SubtleCrypto*
   Subtle();
 
 #ifndef MOZ_DISABLE_CRYPTOLEGACY
   virtual bool EnableSmartCardEvents();
   virtual void SetEnableSmartCardEvents(bool aEnable, ErrorResult& aRv);
--- a/dom/base/DOMRequest.cpp
+++ b/dom/base/DOMRequest.cpp
@@ -78,17 +78,17 @@ DOMRequest::GetReadyState(nsAString& aRe
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMRequest::GetResult(JS::MutableHandle<JS::Value> aResult)
 {
-  aResult.set(Result());
+  GetResult(nullptr, aResult);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DOMRequest::GetError(nsISupports** aError)
 {
   NS_IF_ADDREF(*aError = GetError());
   return NS_OK;
--- a/dom/base/DOMRequest.h
+++ b/dom/base/DOMRequest.h
@@ -44,21 +44,22 @@ public:
 
   // WebIDL Interface
   DOMRequestReadyState ReadyState() const
   {
     return mDone ? DOMRequestReadyState::Done
                  : DOMRequestReadyState::Pending;
   }
 
-  JS::Value Result(JSContext* = nullptr) const
+  void GetResult(JSContext*, JS::MutableHandle<JS::Value> aRetval) const
   {
     NS_ASSERTION(mDone || mResult == JSVAL_VOID,
-               "Result should be undefined when pending");
-    return mResult;
+                 "Result should be undefined when pending");
+    JS::ExposeValueToActiveJS(mResult);
+    aRetval.set(mResult);
   }
 
   DOMError* GetError() const
   {
     NS_ASSERTION(mDone || !mError,
                  "Error should be null when pending");
     return mError;
   }
deleted file mode 100644
--- a/dom/base/ObjectWrapper.jsm
+++ /dev/null
@@ -1,52 +0,0 @@
-/* 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/. */
-
-"use strict"
-
-const Cu = Components.utils;
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-this.EXPORTED_SYMBOLS = ["ObjectWrapper"];
-
-// Makes sure that we expose correctly chrome JS objects to content.
-
-const TypedArrayThings = [
-  "Int8Array",
-  "Uint8Array",
-  "Uint8ClampedArray",
-  "Int16Array",
-  "Uint16Array",
-  "Int32Array",
-  "Uint32Array",
-  "Float32Array",
-  "Float64Array",
-];
-
-this.ObjectWrapper = {
-  getObjectKind: function objWrapper_getObjectKind(aObject) {
-    if (aObject === null || aObject === undefined) {
-      return "primitive";
-    } else if (Array.isArray(aObject)) {
-      return "array";
-    } else if (aObject instanceof Ci.nsIDOMFile) {
-      return "file";
-    } else if (aObject instanceof Ci.nsIDOMBlob) {
-      return "blob";
-    } else if (aObject instanceof Date) {
-      return "date";
-    } else if (TypedArrayThings.indexOf(aObject.constructor.name) !== -1) {
-      return aObject.constructor.name;
-    } else if (typeof aObject == "object") {
-      return "object";
-    } else {
-      return "primitive";
-    }
-  },
-
-  wrap: function objWrapper_wrap(aObject, aCtxt) {
-    dump("-*- ObjectWrapper is deprecated. Use Components.utils.cloneInto() instead.\n");
-    return Cu.cloneInto(aObject, aCtxt, { cloneFunctions: true });
-  }
-}
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -132,17 +132,16 @@ EXTRA_COMPONENTS += [
     'SiteSpecificUserAgent.manifest',
     'SlowScriptDebug.js',
     'SlowScriptDebug.manifest',
 ]
 
 EXTRA_JS_MODULES += [
     'DOMRequestHelper.jsm',
     'IndexedDBHelper.jsm',
-    'ObjectWrapper.jsm',
 ]
 
 FAIL_ON_WARNINGS = True
 
 MSVC_ENABLE_PGO = True
 
 LOCAL_INCLUDES += [
     '../battery',
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -54,16 +54,17 @@
 
 // Other Classes
 #include "mozilla/dom/BarProps.h"
 #include "nsContentCID.h"
 #include "nsLayoutStatics.h"
 #include "nsCCUncollectableMarker.h"
 #include "mozilla/dom/workers/Workers.h"
 #include "mozilla/dom/MessagePortList.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "nsJSPrincipals.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/Debug.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/MouseEvents.h"
 #include "AudioChannelService.h"
 #include "MessageEvent.h"
@@ -3923,55 +3924,58 @@ nsGlobalWindow::GetRealTop(nsIDOMWindow*
       return NS_ERROR_NOT_INITIALIZED;
     }
   } else {
     outer = this;
   }
   return GetTopImpl(outer, aTop, /* aScriptable = */ false);
 }
 
-JSObject*
-nsGlobalWindow::GetContent(JSContext* aCx, ErrorResult& aError)
-{
-  FORWARD_TO_OUTER_OR_THROW(GetContent, (aCx, aError), aError, nullptr);
+void
+nsGlobalWindow::GetContent(JSContext* aCx,
+                           JS::MutableHandle<JSObject*> aRetval,
+                           ErrorResult& aError)
+{
+  FORWARD_TO_OUTER_OR_THROW(GetContent, (aCx, aRetval, aError), aError, );
 
   nsCOMPtr<nsIDOMWindow> content = GetContentInternal(aError);
   if (aError.Failed()) {
-    return nullptr;
+    return;
   }
 
   if (content) {
     JS::Rooted<JS::Value> val(aCx);
     aError = nsContentUtils::WrapNative(aCx, content, &val);
     if (aError.Failed()) {
-      return nullptr;
-    }
-
-    return &val.toObject();
+      return;
+    }
+
+    aRetval.set(&val.toObject());
+    return;
   }
 
   if (!nsContentUtils::IsCallerChrome() || !IsChromeWindow()) {
     aError.Throw(NS_ERROR_FAILURE);
-    return nullptr;
+    return;
   }
 
   // Something tries to get .content on a ChromeWindow, try to fetch the CPOW.
   nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
   if (!treeOwner) {
     aError.Throw(NS_ERROR_FAILURE);
-    return nullptr;
+    return;
   }
 
   JS::Rooted<JS::Value> val(aCx, JS::NullValue());
   aError = treeOwner->GetContentWindow(aCx, &val);
   if (aError.Failed()) {
-    return nullptr;
-  }
-
-  return val.toObjectOrNull();
+    return;
+  }
+
+  aRetval.set(val.toObjectOrNull());
 }
 
 already_AddRefed<nsIDOMWindow>
 nsGlobalWindow::GetContentInternal(ErrorResult& aError)
 {
   // First check for a named frame named "content"
   nsCOMPtr<nsIDOMWindow> domWindow =
     GetChildWindow(NS_LITERAL_STRING("content"));
@@ -4029,17 +4033,18 @@ nsGlobalWindow::GetContent(nsIDOMWindow*
 
   return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetScriptableContent(JSContext* aCx, JS::MutableHandle<JS::Value> aVal)
 {
   ErrorResult rv;
-  JS::Rooted<JSObject*> content(aCx, GetContent(aCx, rv));
+  JS::Rooted<JSObject*> content(aCx);
+  GetContent(aCx, &content, rv);
   if (!rv.Failed()) {
     aVal.setObjectOrNull(content);
   }
 
   return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
@@ -4497,35 +4502,35 @@ nsGlobalWindow::GetOpenerWindow(ErrorRes
         return opener;
       }
     }
   }
 
   return nullptr;
 }
 
-JS::Value
-nsGlobalWindow::GetOpener(JSContext* aCx, ErrorResult& aError)
+void
+nsGlobalWindow::GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
+                          ErrorResult& aError)
 {
   nsCOMPtr<nsIDOMWindow> opener = GetOpenerWindow(aError);
   if (aError.Failed() || !opener) {
-    return JS::NullValue();
-  }
-
-  JS::Rooted<JS::Value> val(aCx);
-  aError = nsContentUtils::WrapNative(aCx, opener, &val);
-  return val;
+    aRetval.setNull();
+    return;
+  }
+
+  aError = nsContentUtils::WrapNative(aCx, opener, aRetval);
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetScriptableOpener(JSContext* aCx,
                                     JS::MutableHandle<JS::Value> aOpener)
 {
   ErrorResult rv;
-  aOpener.set(GetOpener(aCx, rv));
+  GetOpener(aCx, aOpener, rv);
 
   return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::GetOpener(nsIDOMWindow** aOpener)
 {
   ErrorResult rv;
@@ -5820,17 +5825,17 @@ nsGlobalWindow::DispatchResizeEvent(cons
   }
 
   AutoSafeJSContext cx;
   JSAutoCompartment ac(cx, GetWrapperPreserveColor());
   DOMWindowResizeEventDetail detail;
   detail.mWidth = aSize.width;
   detail.mHeight = aSize.height;
   JS::Rooted<JS::Value> detailValue(cx);
-  if (!detail.ToObject(cx, &detailValue)) {
+  if (!ToJSValue(cx, detail, &detailValue)) {
     return false;
   }
 
   CustomEvent* customEvent = static_cast<CustomEvent*>(domEvent.get());
   customEvent->InitCustomEvent(cx,
                                NS_LITERAL_STRING("DOMWindowResize"),
                                /* bubbles = */ true,
                                /* cancelable = */ true,
@@ -9104,44 +9109,44 @@ nsGlobalWindow::ShowModalDialog(const ns
 
   nsCOMPtr<nsIVariant> retVal;
   aError = dialog->GetReturnValue(getter_AddRefs(retVal));
   MOZ_ASSERT(!aError.Failed());
 
   return retVal.forget();
 }
 
-JS::Value
+void
 nsGlobalWindow::ShowModalDialog(JSContext* aCx, const nsAString& aUrl,
                                 JS::Handle<JS::Value> aArgument,
                                 const nsAString& aOptions,
+                                JS::MutableHandle<JS::Value> aRetval,
                                 ErrorResult& aError)
 {
   nsCOMPtr<nsIVariant> args;
   aError = nsContentUtils::XPConnect()->JSToVariant(aCx,
                                                     aArgument,
                                                     getter_AddRefs(args));
   if (aError.Failed()) {
-    return JS::UndefinedValue();
+    return;
   }
 
   nsCOMPtr<nsIVariant> retVal = ShowModalDialog(aUrl, args, aOptions, aError);
   if (aError.Failed()) {
-    return JS::UndefinedValue();
+    return;
   }
 
   JS::Rooted<JS::Value> result(aCx);
   if (retVal) {
     aError = nsContentUtils::XPConnect()->VariantToJS(aCx,
                                                       FastGetGlobalJSObject(),
-                                                      retVal, &result);
+                                                      retVal, aRetval);
   } else {
-    result = JS::NullValue();
-  }
-  return result;
+    aRetval.setNull();
+  }
 }
 
 NS_IMETHODIMP
 nsGlobalWindow::ShowModalDialog(const nsAString& aURI, nsIVariant *aArgs_,
                                 const nsAString& aOptions, uint8_t aArgc,
                                 nsIVariant **aRetVal)
 {
   // Per-spec the |arguments| parameter is supposed to pass through unmodified.
@@ -10579,20 +10584,22 @@ nsGlobalWindow::GetInterface(const nsIID
   }
   else {
     return QueryInterface(aIID, aSink);
   }
 
   return *aSink ? NS_OK : NS_ERROR_NO_INTERFACE;
 }
 
-JS::Value
-nsGlobalWindow::GetInterface(JSContext* aCx, nsIJSID* aIID, ErrorResult& aError)
-{
-  return dom::GetInterface(aCx, this, aIID, aError);
+void
+nsGlobalWindow::GetInterface(JSContext* aCx, nsIJSID* aIID,
+                             JS::MutableHandle<JS::Value> aRetval,
+                             ErrorResult& aError)
+{
+  dom::GetInterface(aCx, this, aIID, aRetval, aError);
 }
 
 void
 nsGlobalWindow::FireOfflineStatusEvent()
 {
   if (!IsCurrentInnerWindow())
     return;
   nsAutoString name;
@@ -12092,17 +12099,18 @@ nsGlobalWindow::RunTimeoutHandler(nsTime
            .setVersion(JSVERSION_DEFAULT);
     JS::Rooted<JSObject*> global(entryScript.cx(), FastGetGlobalJSObject());
     nsJSUtils::EvaluateString(entryScript.cx(), nsDependentString(script),
                               global, options);
   } else {
     // Hold strong ref to ourselves while we call the callback.
     nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
     ErrorResult ignored;
-    callback->Call(me, handler->GetArgs(), ignored);
+    JS::Rooted<JS::Value> ignoredVal(CycleCollectedJSRuntime::Get()->Runtime());
+    callback->Call(me, handler->GetArgs(), &ignoredVal, ignored);
   }
 
   // We ignore any failures from calling EvaluateString() on the context or
   // Call() on a Function here since we're in a loop
   // where we're likely to be running timeouts whose OS timers
   // didn't fire in time and we don't want to not fire those timers
   // now just because execution of one timer failed. We can't
   // propagate the error to anyone who cares about it from this
@@ -13617,68 +13625,71 @@ NS_INTERFACE_MAP_BEGIN(nsGlobalModalWind
   NS_INTERFACE_MAP_ENTRY(nsIDOMModalContentWindow)
   NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ModalContentWindow)
 NS_INTERFACE_MAP_END_INHERITING(nsGlobalWindow)
 
 NS_IMPL_ADDREF_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 NS_IMPL_RELEASE_INHERITED(nsGlobalModalWindow, nsGlobalWindow)
 
 
-JS::Value
-nsGlobalWindow::GetDialogArguments(JSContext* aCx, ErrorResult& aError)
-{
-  FORWARD_TO_OUTER_OR_THROW(GetDialogArguments, (aCx, aError), aError,
-                            JS::UndefinedValue());
+void
+nsGlobalWindow::GetDialogArguments(JSContext* aCx,
+                                   JS::MutableHandle<JS::Value> aRetval,
+                                   ErrorResult& aError)
+{
+  FORWARD_TO_OUTER_OR_THROW(GetDialogArguments, (aCx, aRetval, aError),
+                            aError, );
 
   MOZ_ASSERT(IsModalContentWindow(),
              "This should only be called on modal windows!");
 
   if (!mDialogArguments) {
     MOZ_ASSERT(mIsClosed, "This window should be closed!");
-    return JS::UndefinedValue();
+    aRetval.setUndefined();
+    return;
   }
 
   // This does an internal origin check, and returns undefined if the subject
   // does not subsumes the origin of the arguments.
   JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
   JSAutoCompartment ac(aCx, wrapper);
-  JS::Rooted<JS::Value> args(aCx);
   mDialogArguments->Get(aCx, wrapper, nsContentUtils::SubjectPrincipal(),
-                        &args, aError);
-  return args;
+                        aRetval, aError);
 }
 
 NS_IMETHODIMP
 nsGlobalModalWindow::GetDialogArguments(nsIVariant **aArguments)
 {
   FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetDialogArguments, (aArguments),
                                         NS_ERROR_NOT_INITIALIZED);
 
   // This does an internal origin check, and returns undefined if the subject
   // does not subsumes the origin of the arguments.
   return mDialogArguments->Get(nsContentUtils::SubjectPrincipal(), aArguments);
 }
 
-JS::Value
-nsGlobalWindow::GetReturnValue(JSContext* aCx, ErrorResult& aError)
-{
-  FORWARD_TO_OUTER_OR_THROW(GetReturnValue, (aCx, aError), aError,
-                            JS::UndefinedValue());
+void
+nsGlobalWindow::GetReturnValue(JSContext* aCx,
+                               JS::MutableHandle<JS::Value> aReturnValue,
+                               ErrorResult& aError)
+{
+  FORWARD_TO_OUTER_OR_THROW(GetReturnValue, (aCx, aReturnValue, aError),
+                            aError, );
 
   MOZ_ASSERT(IsModalContentWindow(),
              "This should only be called on modal windows!");
 
-  JS::Rooted<JS::Value> returnValue(aCx);
   if (mReturnValue) {
     JS::Rooted<JSObject*> wrapper(aCx, GetWrapper());
     JSAutoCompartment ac(aCx, wrapper);
     mReturnValue->Get(aCx, wrapper, nsContentUtils::SubjectPrincipal(),
-                      &returnValue, aError);
-  }
-  return returnValue;
+                      aReturnValue, aError);
+  } else {
+    aReturnValue.setUndefined();
+  }
 }
 
 NS_IMETHODIMP
 nsGlobalModalWindow::GetReturnValue(nsIVariant **aRetVal)
 {
   FORWARD_TO_OUTER_MODAL_CONTENT_WINDOW(GetReturnValue, (aRetVal), NS_OK);
 
   nsCOMPtr<nsIVariant> result;
--- a/dom/base/nsGlobalWindow.h
+++ b/dom/base/nsGlobalWindow.h
@@ -832,17 +832,18 @@ public:
   {
     nsCOMPtr<nsIDOMWindow> top;
     aError = GetScriptableTop(getter_AddRefs(top));
     return top.forget();
   }
 protected:
   nsIDOMWindow* GetOpenerWindow(mozilla::ErrorResult& aError);
 public:
-  JS::Value GetOpener(JSContext* aCx, mozilla::ErrorResult& aError);
+  void GetOpener(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
+                 mozilla::ErrorResult& aError);
   void SetOpener(JSContext* aCx, JS::Handle<JS::Value> aOpener,
                  mozilla::ErrorResult& aError);
   using nsIDOMWindow::GetParent;
   already_AddRefed<nsIDOMWindow> GetParent(mozilla::ErrorResult& aError);
   mozilla::dom::Element* GetFrameElement(mozilla::ErrorResult& aError);
   already_AddRefed<nsIDOMWindow> Open(const nsAString& aUrl,
                                       const nsAString& aName,
                                       const nsAString& aOptions,
@@ -862,17 +863,21 @@ protected:
 
 public:
   void Alert(mozilla::ErrorResult& aError);
   void Alert(const nsAString& aMessage, mozilla::ErrorResult& aError);
   bool Confirm(const nsAString& aMessage, mozilla::ErrorResult& aError);
   void Prompt(const nsAString& aMessage, const nsAString& aInitial,
               nsAString& aReturn, mozilla::ErrorResult& aError);
   void Print(mozilla::ErrorResult& aError);
-  JS::Value ShowModalDialog(JSContext* aCx, const nsAString& aUrl, JS::Handle<JS::Value> aArgument, const nsAString& aOptions, mozilla::ErrorResult& aError);
+  void ShowModalDialog(JSContext* aCx, const nsAString& aUrl,
+                       JS::Handle<JS::Value> aArgument,
+                       const nsAString& aOptions,
+                       JS::MutableHandle<JS::Value> aRetval,
+                       mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const nsAString& aTargetOrigin,
                       const mozilla::dom::Optional<mozilla::dom::Sequence<JS::Value > >& aTransfer,
                       mozilla::ErrorResult& aError);
   int32_t SetTimeout(JSContext* aCx, mozilla::dom::Function& aFunction,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& aArguments,
                      mozilla::ErrorResult& aError);
@@ -981,23 +986,27 @@ public:
             bool aShowDialog, mozilla::ErrorResult& aError);
   uint64_t GetMozPaintCount(mozilla::ErrorResult& aError);
   already_AddRefed<nsIDOMWindow> OpenDialog(JSContext* aCx,
                                             const nsAString& aUrl,
                                             const nsAString& aName,
                                             const nsAString& aOptions,
                                             const mozilla::dom::Sequence<JS::Value>& aExtraArgument,
                                             mozilla::ErrorResult& aError);
-  JSObject* GetContent(JSContext* aCx, mozilla::ErrorResult& aError);
-  JSObject* Get_content(JSContext* aCx, mozilla::ErrorResult& aError)
+  void GetContent(JSContext* aCx,
+                  JS::MutableHandle<JSObject*> aRetval,
+                  mozilla::ErrorResult& aError);
+  void Get_content(JSContext* aCx,
+                   JS::MutableHandle<JSObject*> aRetval,
+                   mozilla::ErrorResult& aError)
   {
     if (mDoc) {
       mDoc->WarnOnceAbout(nsIDocument::eWindow_Content);
     }
-    return GetContent(aCx, aError);
+    GetContent(aCx, aRetval, aError);
   }
 
   // ChromeWindow bits.  Do NOT call these unless your window is in
   // fact an nsGlobalChromeWindow.
   uint16_t WindowState();
   nsIBrowserDOMWindow* GetBrowserDOMWindow(mozilla::ErrorResult& aError);
   void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserWindow,
                            mozilla::ErrorResult& aError);
@@ -1012,23 +1021,26 @@ public:
                                  mozilla::ErrorResult& aError);
   nsIMessageBroadcaster* GetMessageManager(mozilla::ErrorResult& aError);
   nsIMessageBroadcaster* GetGroupMessageManager(const nsAString& aGroup,
                                                 mozilla::ErrorResult& aError);
   void BeginWindowMove(mozilla::dom::Event& aMouseDownEvent,
                        mozilla::dom::Element* aPanel,
                        mozilla::ErrorResult& aError);
 
-  JS::Value GetDialogArguments(JSContext* aCx, mozilla::ErrorResult& aError);
-  JS::Value GetReturnValue(JSContext* aCx, mozilla::ErrorResult& aError);
+  void GetDialogArguments(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
+                          mozilla::ErrorResult& aError);
+  void GetReturnValue(JSContext* aCx, JS::MutableHandle<JS::Value> aReturnValue,
+                      mozilla::ErrorResult& aError);
   void SetReturnValue(JSContext* aCx, JS::Handle<JS::Value> aReturnValue,
                       mozilla::ErrorResult& aError);
 
-  JS::Value GetInterface(JSContext* aCx, nsIJSID* aIID,
-                         mozilla::ErrorResult& aError);
+  void GetInterface(JSContext* aCx, nsIJSID* aIID,
+                    JS::MutableHandle<JS::Value> aRetval,
+                    mozilla::ErrorResult& aError);
 
 protected:
   // Array of idle observers that are notified of idle events.
   nsTObserverArray<IdleObserverHolder> mIdleObservers;
 
   // Idle timer used for function callbacks to notify idle observers.
   nsCOMPtr<nsITimer> mIdleTimer;
 
--- a/dom/base/nsHistory.cpp
+++ b/dom/base/nsHistory.cpp
@@ -91,60 +91,56 @@ nsHistory::GetLength(ErrorResult& aRv) c
     aRv.Throw(rv);
 
     return 0;
   }
 
   return len >= 0 ? len : 0;
 }
 
-JS::Value
-nsHistory::GetState(JSContext* aCx, ErrorResult& aRv) const
+void
+nsHistory::GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                    ErrorResult& aRv) const
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mInnerWindow));
   if (!win) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-
-    return JS::UndefinedValue();
+    return;
   }
 
   if (!win->HasActiveDocument()) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
-
-    return JS::UndefinedValue();
+    return;
   }
 
   nsCOMPtr<nsIDocument> doc =
     do_QueryInterface(win->GetExtantDoc());
   if (!doc) {
     aRv.Throw(NS_ERROR_NOT_AVAILABLE);
-
-    return JS::UndefinedValue();
+    return;
   }
 
   nsCOMPtr<nsIVariant> variant;
   doc->GetStateObject(getter_AddRefs(variant));
 
   if (variant) {
-    JS::Rooted<JS::Value> jsData(aCx);
-    aRv = variant->GetAsJSVal(&jsData);
+    aRv = variant->GetAsJSVal(aResult);
 
     if (aRv.Failed()) {
-      return JS::UndefinedValue();
+      return;
     }
 
-    if (!JS_WrapValue(aCx, &jsData)) {
+    if (!JS_WrapValue(aCx, aResult)) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return JS::UndefinedValue();
     }
 
-    return jsData;
+    return;
   }
 
-  return JS::NullValue();
+  aResult.setNull();
 }
 
 void
 nsHistory::Go(int32_t aDelta, ErrorResult& aRv)
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryReferent(mInnerWindow));
   if (!win || !win->HasActiveDocument()) {
     aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
--- a/dom/base/nsHistory.h
+++ b/dom/base/nsHistory.h
@@ -32,17 +32,18 @@ public:
 public:
   nsHistory(nsPIDOMWindow* aInnerWindow);
   virtual ~nsHistory();
 
   nsPIDOMWindow* GetParentObject() const;
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   uint32_t GetLength(mozilla::ErrorResult& aRv) const;
-  JS::Value GetState(JSContext* aCx, mozilla::ErrorResult& aRv) const;
+  void GetState(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                mozilla::ErrorResult& aRv) const;
   void Go(int32_t aDelta, mozilla::ErrorResult& aRv);
   void Back(mozilla::ErrorResult& aRv);
   void Forward(mozilla::ErrorResult& aRv);
   void PushState(JSContext* aCx, JS::Handle<JS::Value> aData,
                  const nsAString& aTitle, const nsAString& aUrl,
                  mozilla::ErrorResult& aRv);
   void ReplaceState(JSContext* aCx, JS::Handle<JS::Value> aData,
                     const nsAString& aTitle, const nsAString& aUrl,
--- a/dom/bindings/BindingUtils.cpp
+++ b/dom/bindings/BindingUtils.cpp
@@ -879,35 +879,32 @@ QueryInterface(JSContext* cx, unsigned a
   if (NS_FAILED(rv)) {
     return Throw(cx, rv);
   }
 
   *vp = thisv;
   return true;
 }
 
-JS::Value
+void
 GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
-                 nsWrapperCache* aCache, nsIJSID* aIID, ErrorResult& aError)
+                 nsWrapperCache* aCache, nsIJSID* aIID,
+                 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
 {
   const nsID* iid = aIID->GetID();
 
   nsRefPtr<nsISupports> result;
   aError = aRequestor->GetInterface(*iid, getter_AddRefs(result));
   if (aError.Failed()) {
-    return JS::NullValue();
+    return;
   }
 
-  JS::Rooted<JS::Value> v(aCx, JSVAL_NULL);
-  if (!WrapObject(aCx, result, iid, &v)) {
+  if (!WrapObject(aCx, result, iid, aRetval)) {
     aError.Throw(NS_ERROR_FAILURE);
-    return JS::NullValue();
   }
-
-  return v;
 }
 
 bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
   return ThrowErrorMessage(cx, MSG_ILLEGAL_CONSTRUCTOR);
 }
 
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -1644,25 +1644,27 @@ WantsQueryInterface
   static_assert(IsBaseOf<nsISupports, T>::value,
                 "QueryInterface can't work without an nsISupports.");
   static bool Enabled(JSContext* aCx, JSObject* aGlobal)
   {
     return NS_IsMainThread() && IsChromeOrXBL(aCx, aGlobal);
   }
 };
 
-JS::Value
+void
 GetInterfaceImpl(JSContext* aCx, nsIInterfaceRequestor* aRequestor,
-                 nsWrapperCache* aCache, nsIJSID* aIID, ErrorResult& aError);
+                 nsWrapperCache* aCache, nsIJSID* aIID,
+                 JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError);
 
 template<class T>
-JS::Value
-GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID, ErrorResult& aError)
+void
+GetInterface(JSContext* aCx, T* aThis, nsIJSID* aIID,
+             JS::MutableHandle<JS::Value> aRetval, ErrorResult& aError)
 {
-  return GetInterfaceImpl(aCx, aThis, aThis, aIID, aError);
+  GetInterfaceImpl(aCx, aThis, aThis, aIID, aRetval, aError);
 }
 
 bool
 ThrowingConstructor(JSContext* cx, unsigned argc, JS::Value* vp);
 
 bool
 ThrowConstructorWithoutNew(JSContext* cx, const char* name);
 
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -2138,20 +2138,19 @@ class MethodDefiner(PropertyDefiner):
                     if m.get("allowCrossOriginThis", False):
                         if m.get("returnsPromise", False):
                             raise TypeError("%s returns a Promise but should "
                                             "be allowed cross-origin?" %
                                             accessor)
                         accessor = "genericCrossOriginMethod"
                     elif self.descriptor.needsSpecialGenericOps():
                         if m.get("returnsPromise", False):
-                            raise TypeError("%s returns a Promise but needs "
-                                            "special generic ops?" %
-                                            accessor)
-                        accessor = "genericMethod"
+                            accessor = "genericPromiseReturningMethod"
+                        else:
+                            accessor = "genericMethod"
                     elif m.get("returnsPromise", False):
                         accessor = "GenericPromiseReturningBindingMethod"
                     else:
                         accessor = "GenericBindingMethod"
                 else:
                     if m.get("returnsPromise", False):
                         jitinfo = "&%s_methodinfo" % accessor
                         accessor = "StaticMethodPromiseWrapper"
@@ -5439,17 +5438,17 @@ def getWrapTemplateForType(type, descrip
         return (setObject("*%s.Obj()" % result,
                           wrapAsType=type), False)
 
     if type.isUnion():
         return (wrapAndSetPtr("%s.ToJSVal(cx, ${obj}, ${jsvalHandle})" % result),
                 False)
 
     if type.isDictionary():
-        return (wrapAndSetPtr("%s.ToObject(cx, ${jsvalHandle})" % result),
+        return (wrapAndSetPtr("%s.ToObjectInternal(cx, ${jsvalHandle})" % result),
                 False)
 
     if type.isDate():
         return (wrapAndSetPtr("%s.ToDateObject(cx, ${jsvalHandle})" % result),
                 False)
 
     tag = type.tag()
 
@@ -5589,62 +5588,68 @@ def getRetvalDeclarationForType(returnTy
                                 resultAlreadyAddRefed,
                                 isMember=False):
     """
     Returns a tuple containing four things:
 
     1) A CGThing for the type of the return value, or None if there is no need
        for a return value.
 
-    2) A boolean indicating whether the return value is passed as an out
-       parameter.
+    2) A value indicating the kind of ourparam to pass the value as.  Valid
+       options are None to not pass as an out param at all, "ref" (to pass a
+       reference as an out param), and "ptr" (to pass a pointer as an out
+       param).
 
     3) A CGThing for a tracer for the return value, or None if no tracing is
        needed.
 
     4) An argument string to pass to the retval declaration
        constructor or None if there are no arguments.
     """
     if returnType is None or returnType.isVoid():
         # Nothing to declare
-        return None, False, None, None
+        return None, None, None, None
     if returnType.isPrimitive() and returnType.tag() in builtinNames:
         result = CGGeneric(builtinNames[returnType.tag()])
         if returnType.nullable():
             result = CGTemplatedType("Nullable", result)
-        return result, False, None, None
+        return result, None, None, None
     if returnType.isDOMString():
         if isMember:
-            return CGGeneric("nsString"), True, None, None
-        return CGGeneric("DOMString"), True, None, None
+            return CGGeneric("nsString"), "ref", None, None
+        return CGGeneric("DOMString"), "ref", None, None
     if returnType.isByteString():
-        return CGGeneric("nsCString"), True, None, None
+        return CGGeneric("nsCString"), "ref", None, None
     if returnType.isEnum():
         result = CGGeneric(returnType.unroll().inner.identifier.name)
         if returnType.nullable():
             result = CGTemplatedType("Nullable", result)
-        return result, False, None, None
+        return result, None, None, None
     if returnType.isGeckoInterface():
         result = CGGeneric(descriptorProvider.getDescriptor(
             returnType.unroll().inner.identifier.name).nativeType)
         if descriptorProvider.getDescriptor(
                 returnType.unroll().inner.identifier.name).nativeOwnership == 'owned':
             result = CGTemplatedType("nsAutoPtr", result)
         elif resultAlreadyAddRefed:
             result = CGTemplatedType("nsRefPtr", result)
         else:
             result = CGWrapper(result, post="*")
-        return result, False, None, None
+        return result, None, None, None
     if returnType.isCallback():
         name = returnType.unroll().identifier.name
-        return CGGeneric("nsRefPtr<%s>" % name), False, None, None
+        return CGGeneric("nsRefPtr<%s>" % name), None, None, None
     if returnType.isAny():
-        return CGGeneric("JS::Value"), False, None, None
+        if isMember:
+            return CGGeneric("JS::Value"), None, None, None
+        return CGGeneric("JS::Rooted<JS::Value>"), "ptr", None, "cx"
     if returnType.isObject() or returnType.isSpiderMonkeyInterface():
-        return CGGeneric("JSObject*"), False, None, None
+        if isMember:
+            return CGGeneric("JSObject*"), None, None, None
+        return CGGeneric("JS::Rooted<JSObject*>"), "ptr", None, "cx"
     if returnType.isSequence():
         nullable = returnType.nullable()
         if nullable:
             returnType = returnType.inner
         # If our result is already addrefed, use the right type in the
         # sequence argument here.
         result, _, _, _ = getRetvalDeclarationForType(returnType.inner,
                                                       descriptorProvider,
@@ -5654,17 +5659,17 @@ def getRetvalDeclarationForType(returnTy
         if not isMember and typeNeedsRooting(returnType):
             rooter = CGGeneric("SequenceRooter<%s > resultRooter(cx, &result);\n" %
                                result.define())
         else:
             rooter = None
         result = CGTemplatedType("nsTArray", result)
         if nullable:
             result = CGTemplatedType("Nullable", result)
-        return result, True, rooter, None
+        return result, "ref", rooter, None
     if returnType.isMozMap():
         nullable = returnType.nullable()
         if nullable:
             returnType = returnType.inner
         # If our result is already addrefed, use the right type in the
         # MozMap argument here.
         result, _, _, _ = getRetvalDeclarationForType(returnType.inner,
                                                       descriptorProvider,
@@ -5674,50 +5679,50 @@ def getRetvalDeclarationForType(returnTy
         if not isMember and typeNeedsRooting(returnType):
             rooter = CGGeneric("MozMapRooter<%s> resultRooter(cx, &result);\n" %
                                result.define())
         else:
             rooter = None
         result = CGTemplatedType("MozMap", result)
         if nullable:
             result = CGTemplatedType("Nullable", result)
-        return result, True, rooter, None
+        return result, "ref", rooter, None
     if returnType.isDictionary():
         nullable = returnType.nullable()
         dictName = CGDictionary.makeDictionaryName(returnType.unroll().inner)
         result = CGGeneric(dictName)
         if not isMember and typeNeedsRooting(returnType):
             if nullable:
                 result = CGTemplatedType("NullableRootedDictionary", result)
             else:
                 result = CGTemplatedType("RootedDictionary", result)
             resultArgs = "cx"
         else:
             if nullable:
                 result = CGTemplatedType("Nullable", result)
             resultArgs = None
-        return result, True, None, resultArgs
+        return result, "ref", None, resultArgs
     if returnType.isUnion():
         result = CGGeneric(CGUnionStruct.unionTypeName(returnType.unroll(), True))
         if not isMember and typeNeedsRooting(returnType):
             if returnType.nullable():
                 result = CGTemplatedType("NullableRootedUnion", result)
             else:
                 result = CGTemplatedType("RootedUnion", result)
             resultArgs = "cx"
         else:
             if returnType.nullable():
                 result = CGTemplatedType("Nullable", result)
             resultArgs = None
-        return result, True, None, resultArgs
+        return result, "ref", None, resultArgs
     if returnType.isDate():
         result = CGGeneric("Date")
         if returnType.nullable():
             result = CGTemplatedType("Nullable", result)
-        return result, False, None, None
+        return result, None, None, None
     raise TypeError("Don't know how to declare return value for %s" %
                     returnType)
 
 
 def isResultAlreadyAddRefed(extendedAttributes):
     return 'resultNotAddRefed' not in extendedAttributes
 
 
@@ -5796,18 +5801,23 @@ class CGCallGenerator(CGThing):
                 arg = CGWrapper(arg, pre="Constify(", post=")")
             # And convert NonNull<T> to T&
             if (((a.type.isGeckoInterface() or a.type.isCallback()) and not a.type.nullable()) or
                 a.type.isDOMString()):
                 arg = CGWrapper(arg, pre="NonNullHelper(", post=")")
             args.append(arg)
 
         # Return values that go in outparams go here
-        if resultOutParam:
-            args.append(CGGeneric("result"))
+        if resultOutParam is not None:
+            if resultOutParam is "ref":
+                args.append(CGGeneric("result"))
+            else:
+                assert resultOutParam is "ptr"
+                args.append(CGGeneric("&result"))
+
         if isFallible:
             args.append(CGGeneric("rv"))
         args.extend(CGGeneric(arg) for arg in argsPost)
 
         # Build up our actual call
         self.cgRoot = CGList([])
 
         call = CGGeneric(nativeMethodName)
@@ -6899,16 +6909,63 @@ class CGGenericMethod(CGAbstractBindingM
             #ifdef DEBUG
             if (ok) {
               AssertReturnTypeMatchesJitinfo(info, args.rval());
             }
             #endif
             return ok;
             """))
 
+class CGGenericPromiseReturningMethod(CGAbstractBindingMethod):
+    """
+    A class for generating the C++ code for an IDL method that returns a Promise.
+
+    Does not handle cross-origin this.
+    """
+    def __init__(self, descriptor):
+        args = [Argument('JSContext*', 'cx'),
+                Argument('unsigned', 'argc'),
+                Argument('JS::Value*', 'vp')]
+        unwrapFailureCode = dedent("""
+            ThrowInvalidThis(cx, args, GetInvalidThisErrorForMethod(%%(securityError)s), "%s");\n
+            return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
+                                             args.rval());\n""" %
+            descriptor.interface.identifier.name)
+
+        name = "genericPromiseReturningMethod"
+        customCallArgs = dedent("""
+            JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+            // Make sure to save the callee before someone maybe messes with rval().
+            JS::Rooted<JSObject*> callee(cx, &args.callee());
+        """)
+
+        CGAbstractBindingMethod.__init__(self, descriptor, name,
+                                         args,
+                                         callArgs=customCallArgs,
+                                         unwrapFailureCode=unwrapFailureCode)
+
+    def generate_code(self):
+        return CGGeneric(dedent("""
+            const JSJitInfo *info = FUNCTION_VALUE_TO_JITINFO(args.calleev());
+            MOZ_ASSERT(info->type() == JSJitInfo::Method);
+            JSJitMethodOp method = info->method;
+            bool ok = method(cx, obj, self, JSJitMethodCallArgs(args));
+            if (ok) {
+            #ifdef DEBUG
+              AssertReturnTypeMatchesJitinfo(info, args.rval());
+            #endif
+              return true;
+            }
+
+            MOZ_ASSERT(info->returnType() == JSVAL_TYPE_OBJECT);
+            return ConvertExceptionToPromise(cx, xpc::XrayAwareCalleeGlobal(callee),
+                                             args.rval());
+            """))
+
+
 
 class CGSpecializedMethod(CGAbstractStaticMethod):
     """
     A class for generating the C++ code for a specialized method that the JIT
     can call with lower overhead.
     """
     def __init__(self, descriptor, method):
         self.method = method
@@ -10148,17 +10205,18 @@ class CGDescriptor(CGThing):
         self._deps = descriptor.interface.getDeps()
 
         cgThings = []
         cgThings.append(CGGeneric(declare="typedef %s NativeType;\n" %
                                   descriptor.nativeType))
         # These are set to true if at least one non-static
         # method/getter/setter or jsonifier exist on the interface.
         (hasMethod, hasGetter, hasLenientGetter, hasSetter, hasJsonifier,
-            hasLenientSetter) = False, False, False, False, False, False
+            hasLenientSetter,
+            hasPromiseReturningMethod) = False, False, False, False, False, False, False
         crossOriginMethods, crossOriginGetters, crossOriginSetters = set(), set(), set()
         for n in descriptor.interface.namedConstructors:
             cgThings.append(CGClassConstructor(descriptor, n,
                                                NamedConstructorName(n)))
         for m in descriptor.interface.members:
             if m.isMethod() and m.identifier.name == 'queryInterface':
                 continue
             if m.isMethod() and m == descriptor.operations['Jsonifier']:
@@ -10171,16 +10229,18 @@ class CGDescriptor(CGThing):
                     assert descriptor.interface.hasInterfaceObject()
                     cgThings.append(CGStaticMethod(descriptor, m))
                     if m.returnsPromise():
                         cgThings.append(CGStaticMethodJitinfo(m))
                 elif descriptor.interface.hasInterfacePrototypeObject():
                     specializedMethod = CGSpecializedMethod(descriptor, m)
                     cgThings.append(specializedMethod)
                     if m.returnsPromise():
+                        if descriptor.needsSpecialGenericOps():
+                            hasPromiseReturningMethod = True
                         cgThings.append(CGMethodPromiseWrapper(descriptor, specializedMethod))
                     cgThings.append(CGMemberJITInfo(descriptor, m))
                     if m.getExtendedAttribute("CrossOriginCallable"):
                         crossOriginMethods.add(m.identifier.name)
                     elif descriptor.needsSpecialGenericOps():
                         hasMethod = True
             elif m.isAttr():
                 if m.stringifier:
@@ -10229,16 +10289,18 @@ class CGDescriptor(CGThing):
                 if (not m.isStatic() and
                     descriptor.interface.hasInterfacePrototypeObject()):
                     cgThings.append(CGMemberJITInfo(descriptor, m))
         if hasJsonifier:
             cgThings.append(CGJsonifierMethod(descriptor, jsonifierMethod))
             cgThings.append(CGMemberJITInfo(descriptor, jsonifierMethod))
         if hasMethod:
             cgThings.append(CGGenericMethod(descriptor))
+        if hasPromiseReturningMethod:
+            cgThings.append(CGGenericPromiseReturningMethod(descriptor))
         if len(crossOriginMethods):
             cgThings.append(CGGenericMethod(descriptor,
                                             allowCrossOriginThis=True))
         if hasGetter:
             cgThings.append(CGGenericGetter(descriptor))
         if hasLenientGetter:
             cgThings.append(CGGenericGetter(descriptor, lenientThis=True))
         if len(crossOriginGetters):
@@ -10595,34 +10657,34 @@ class CGDictionary(CGThing):
                 MOZ_ASSERT(NS_IsMainThread());
                 AutoSafeJSContext cx;
                 JS::Rooted<JS::Value> json(cx);
                 bool ok = ParseJSON(cx, aJSON, &json);
                 NS_ENSURE_TRUE(ok, false);
                 return Init(cx, json);
                 """))
 
-    def toObjectMethod(self):
+    def toObjectInternalMethod(self):
         body = ""
         if self.needToInitIds:
             body += fill(
                 """
                 ${dictName}Atoms* atomsCache = GetAtomCache<${dictName}Atoms>(cx);
                 if (!*reinterpret_cast<jsid**>(atomsCache) && !InitIds(cx, atomsCache)) {
                   return false;
                 }
 
                 """,
                 dictName=self.makeClassName(self.dictionary))
 
         if self.dictionary.parent:
             body += fill(
                 """
                 // Per spec, we define the parent's members first
-                if (!${dictName}::ToObject(cx, rval)) {
+                if (!${dictName}::ToObjectInternal(cx, rval)) {
                   return false;
                 }
                 JS::Rooted<JSObject*> obj(cx, &rval.toObject());
 
                 """,
                 dictName=self.makeClassName(self.dictionary.parent))
         else:
             body += fill(
@@ -10635,17 +10697,17 @@ class CGDictionary(CGThing):
 
                 """)
 
         if self.memberInfo:
             body += "\n".join(self.getMemberDefinition(m).define()
                               for m in self.memberInfo)
         body += "\nreturn true;\n"
 
-        return ClassMethod("ToObject", "bool", [
+        return ClassMethod("ToObjectInternal", "bool", [
             Argument('JSContext*', 'cx'),
             Argument('JS::MutableHandle<JS::Value>', 'rval'),
         ], const=True, body=body)
 
     def initIdsMethod(self):
         assert self.needToInitIds
         return initIdsClassMethod([m.identifier.name for m in self.dictionary.members],
                                   "%sAtoms" % self.makeClassName(self.dictionary))             
@@ -10721,21 +10783,21 @@ class CGDictionary(CGThing):
         methods = []
 
         if self.needToInitIds:
             methods.append(self.initIdsMethod())
 
         methods.append(self.initMethod())
         methods.append(self.initFromJSONMethod())
         try:
-            methods.append(self.toObjectMethod())
+            methods.append(self.toObjectInternalMethod())
         except MethodNotNewObjectError:
-            # If we can't have a ToObject() because one of our members can only
-            # be returned from [NewObject] methods, then just skip generating
-            # ToObject().
+            # If we can't have a ToObjectInternal() because one of our members
+            # can only be returned from [NewObject] methods, then just skip
+            # generating ToObjectInternal().
             pass
         methods.append(self.traceDictionaryMethod())
 
         if CGDictionary.isDictionaryCopyConstructible(d):
             disallowCopyConstruction = False
             # Note: no base constructors because our operator= will
             # deal with that.
             ctors.append(ClassConstructor([Argument("const %s&" % selfName,
@@ -11516,25 +11578,36 @@ class CGNativeMember(ClassMethod):
             # Since we always force an owning type for callback return values,
             # our ${declName} is an OwningNonNull or nsRefPtr.  So we can just
             # .forget() to get our already_AddRefed.
             return result.define(), "nullptr", "return ${declName}.forget();\n"
         if type.isCallback():
             return ("already_AddRefed<%s>" % type.unroll().identifier.name,
                     "nullptr", "return ${declName}.forget();\n")
         if type.isAny():
-            return "JS::Value", "JS::UndefinedValue()", "return ${declName};\n"
+            if isMember:
+                # No need for a third element in the isMember case
+                return "JS::Value", None, None
+            # Outparam
+            return "void", "", "aRetVal.set(${declName});\n"
+
         if type.isObject():
-            return "JSObject*", "nullptr", "return ${declName};\n"
+            if isMember:
+                # No need for a third element in the isMember case
+                return "JSObject*", None, None
+            return "void", "", "aRetVal.set(${declName});\n"
         if type.isSpiderMonkeyInterface():
+            if isMember:
+                # No need for a third element in the isMember case
+                return "JSObject*", None, None
             if type.nullable():
-                returnCode = "return ${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();\n"
-            else:
-                returnCode = "return ${declName}.Obj();\n"
-            return "JSObject*", "nullptr", returnCode
+                returnCode = "${declName}.IsNull() ? nullptr : ${declName}.Value().Obj();\n"
+            else:
+                returnCode = "${declName}.Obj();\n"
+            return "void", "", "aRetVal.set(%s);\n" % returnCode
         if type.isSequence():
             # If we want to handle sequence-of-sequences return values, we're
             # going to need to fix example codegen to not produce nsTArray<void>
             # for the relevant argument...
             assert not isMember
             # Outparam.
             if type.nullable():
                 returnCode = dedent("""
@@ -11614,16 +11687,21 @@ class CGNativeMember(ClassMethod):
             dictType = CGGeneric(CGDictionary.makeDictionaryName(returnType.inner))
             if nullable:
                 dictType = CGTemplatedType("Nullable", dictType)
             args.append(Argument("%s&" % dictType.define(), "aRetVal"))
         elif returnType.isUnion():
             args.append(Argument("%s&" %
                                  CGUnionStruct.unionTypeDecl(returnType, True),
                                  "aRetVal"))
+        elif returnType.isAny():
+            args.append(Argument("JS::MutableHandle<JS::Value>", "aRetVal"))
+        elif returnType.isObject() or returnType.isSpiderMonkeyInterface():
+            args.append(Argument("JS::MutableHandle<JSObject*>", "aRetVal"))
+
         # And the ErrorResult
         if 'infallible' not in self.extendedAttrs:
             # Use aRv so it won't conflict with local vars named "rv"
             args.append(Argument("ErrorResult&", "aRv"))
         # The legacycaller thisval
         if self.member.isMethod() and self.member.isLegacycaller():
             # If it has an identifier, we can't deal with it yet
             assert self.member.isIdentifierLess()
@@ -11792,16 +11870,19 @@ class CGNativeMember(ClassMethod):
         decl, ref = self.getArgType(arg.type,
                                     arg.optional and not arg.defaultValue,
                                     "Variadic" if arg.variadic else False)
         if ref:
             decl = CGWrapper(decl, pre="const ", post="&")
 
         return Argument(decl.define(), arg.identifier.name)
 
+    def arguments(self):
+        return self.member.signatures()[0][1]
+
 
 class CGExampleMethod(CGNativeMember):
     def __init__(self, descriptor, method, signature, isConstructor, breakAfter=True):
         CGNativeMember.__init__(self, descriptor, method,
                                 CGSpecializedMethod.makeNativeName(descriptor,
                                                                    method),
                                 signature,
                                 descriptor.getExtendedAttributes(method),
@@ -13493,24 +13574,26 @@ class CGEventGetter(CGNativeMember):
         if type.isDOMString() or type.isByteString():
             return "aRetVal = " + memberName + ";\n"
         if type.isSpiderMonkeyInterface() or type.isObject():
             return fill(
                 """
                 if (${memberName}) {
                   JS::ExposeObjectToActiveJS(${memberName});
                 }
-                return ${memberName};
+                aRetVal.set(${memberName});
+                return;
                 """,
                 memberName=memberName)
         if type.isAny():
             return fill(
                 """
                 JS::ExposeValueToActiveJS(${memberName});
-                return ${memberName};
+                aRetVal.set(${memberName});
+                return;
                 """,
                 memberName=memberName)
         if type.isUnion():
             return "aRetVal = " + memberName + ";\n"
         raise TypeError("Event code generator does not support this type!")
 
     def declare(self, cgClass):
         if getattr(self.member, "originatingInterface",
@@ -13557,19 +13640,19 @@ class CGEventMethod(CGNativeMember):
 
         name = arg.identifier.name
         name = "a" + name[0].upper() + name[1:]
         return Argument(decl.define(), name)
 
     def declare(self, cgClass):
         self.args = list(self.originalArgs)
         self.args.insert(0, Argument("mozilla::dom::EventTarget*", "aOwner"))
-        constructorForNativeCaller = CGNativeMember.declare(self, cgClass) + "\n"
+        constructorForNativeCaller = CGNativeMember.declare(self, cgClass)
         self.args = list(self.originalArgs)
-        if needCx(None, self.descriptorProvider.interface.members, [], True):
+        if needCx(None, self.arguments(), [], True):
             self.args.insert(0, Argument("JSContext*", "aCx"))
         self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
         self.args.append(Argument('ErrorResult&', 'aRv'))
         return constructorForNativeCaller + CGNativeMember.declare(self, cgClass)
 
     def define(self, cgClass):
         self.args = list(self.originalArgs)
         members = ""
@@ -13610,17 +13693,17 @@ class CGEventMethod(CGNativeMember):
         self.args = list(self.originalArgs)
         self.body = fill(
             """
             nsCOMPtr<mozilla::dom::EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
             return Constructor(owner, ${arg0}, ${arg1});
             """,
             arg0=self.args[0].name,
             arg1=self.args[1].name)
-        if needCx(None, self.descriptorProvider.interface.members, [], True):
+        if needCx(None, self.arguments(), [], True):
             self.args.insert(0, Argument("JSContext*", "aCx"))
         self.args.insert(0, Argument("const GlobalObject&", "aGlobal"))
         self.args.append(Argument('ErrorResult&', 'aRv'))
         return constructorForNativeCaller + CGNativeMember.define(self, cgClass)
 
 
 class CGEventClass(CGBindingImplClass):
     """
--- a/dom/bindings/ToJSValue.h
+++ b/dom/bindings/ToJSValue.h
@@ -204,17 +204,17 @@ ToJSValue(JSContext* aCx,
 
 // Accept WebIDL dictionaries
 template <class T>
 typename EnableIf<IsBaseOf<DictionaryBase, T>::value, bool>::Type
 ToJSValue(JSContext* aCx,
           const T& aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
-  return aArgument.ToObject(aCx, aValue);
+  return aArgument.ToObjectInternal(aCx, aValue);
 }
 
 // Accept existing JS values (which may not be same-compartment with us
 inline bool
 ToJSValue(JSContext* aCx, JS::Handle<JS::Value> aArgument,
           JS::MutableHandle<JS::Value> aValue)
 {
   aValue.set(aArgument);
--- a/dom/bindings/test/TestBindingHeader.h
+++ b/dom/bindings/test/TestBindingHeader.h
@@ -445,17 +445,17 @@ public:
   void PassFloat32Array(const Float32Array&);
   void PassFloat64Array(const Float64Array&);
   void PassSequenceOfArrayBuffers(const Sequence<ArrayBuffer>&);
   void PassSequenceOfNullableArrayBuffers(const Sequence<Nullable<ArrayBuffer> >&);
   void PassMozMapOfArrayBuffers(const MozMap<ArrayBuffer>&);
   void PassMozMapOfNullableArrayBuffers(const MozMap<Nullable<ArrayBuffer> >&);
   void PassVariadicTypedArray(const Sequence<Float32Array>&);
   void PassVariadicNullableTypedArray(const Sequence<Nullable<Float32Array> >&);
-  JSObject* ReceiveUint8Array(JSContext*);
+  void ReceiveUint8Array(JSContext*, JS::MutableHandle<JSObject*>);
 
   // DOMString types
   void PassString(const nsAString&);
   void PassNullableString(const nsAString&);
   void PassOptionalString(const Optional<nsAString>&);
   void PassOptionalStringWithDefaultValue(const nsAString&);
   void PassOptionalNullableString(const Optional<nsAString>&);
   void PassOptionalNullableStringWithDefaultValue(const nsAString&);
@@ -518,34 +518,34 @@ public:
   void PassOptionalNullableMozMapOfAny(JSContext*, const Optional<Nullable<MozMap<JS::Value> > >&);
   void PassOptionalMozMapOfAnyWithDefaultValue(JSContext*, const Nullable<MozMap<JS::Value> >&);
   void PassMozMapOfMozMapOfAny(JSContext*, const MozMap<MozMap<JS::Value> >&);
   void PassMozMapOfNullableMozMapOfAny(JSContext*, const MozMap<Nullable<MozMap<JS::Value> > >&);
   void PassNullableMozMapOfNullableMozMapOfAny(JSContext*, const Nullable<MozMap<Nullable<MozMap<JS::Value> > > >&);
   void PassOptionalNullableMozMapOfNullableMozMapOfAny(JSContext*, const Optional<Nullable<MozMap<Nullable<MozMap<JS::Value>>>>>&);
   void PassOptionalNullableMozMapOfNullableSequenceOfAny(JSContext*, const Optional<Nullable<MozMap<Nullable<Sequence<JS::Value>>>>>&);
   void PassOptionalNullableSequenceOfNullableMozMapOfAny(JSContext*, const Optional<Nullable<Sequence<Nullable<MozMap<JS::Value>>>>>&);
-  JS::Value ReceiveAny(JSContext*);
+  void ReceiveAny(JSContext*, JS::MutableHandle<JS::Value>);
 
   // object types
   void PassObject(JSContext*, JS::Handle<JSObject*>);
   void PassVariadicObject(JSContext*, const Sequence<JSObject*>&);
   void PassNullableObject(JSContext*, JS::Handle<JSObject*>);
   void PassVariadicNullableObject(JSContext*, const Sequence<JSObject*>&);
   void PassOptionalObject(JSContext*, const Optional<JS::Handle<JSObject*> >&);
   void PassOptionalNullableObject(JSContext*, const Optional<JS::Handle<JSObject*> >&);
   void PassOptionalNullableObjectWithDefaultValue(JSContext*, JS::Handle<JSObject*>);
   void PassSequenceOfObject(JSContext*, const Sequence<JSObject*>&);
   void PassSequenceOfNullableObject(JSContext*, const Sequence<JSObject*>&);
   void PassNullableSequenceOfObject(JSContext*, const Nullable<Sequence<JSObject*> >&);
   void PassOptionalNullableSequenceOfNullableSequenceOfObject(JSContext*, const Optional<Nullable<Sequence<Nullable<Sequence<JSObject*> > > > >&);
   void PassOptionalNullableSequenceOfNullableSequenceOfNullableObject(JSContext*, const Optional<Nullable<Sequence<Nullable<Sequence<JSObject*> > > > >&);
   void PassMozMapOfObject(JSContext*, const MozMap<JSObject*>&);
-  JSObject* ReceiveObject(JSContext*);
-  JSObject* ReceiveNullableObject(JSContext*);
+  void ReceiveObject(JSContext*, JS::MutableHandle<JSObject*>);
+  void ReceiveNullableObject(JSContext*, JS::MutableHandle<JSObject*>);
 
   // Union types
   void PassUnion(JSContext*, const ObjectOrLong& arg);
   void PassUnionWithNullable(JSContext* cx, const ObjectOrNullOrLong& arg)
   {
     OwningObjectOrLong returnValue;
     if (arg.IsNull()) {
     } else if (arg.IsObject()) {
@@ -762,17 +762,17 @@ public:
   void SetAttrWithLenientThis(int32_t);
   uint32_t UnforgeableAttr();
   uint32_t UnforgeableAttr2();
   void Stringify(nsString&);
   void PassRenamedInterface(nsRenamedInterface&);
   TestInterface* PutForwardsAttr();
   TestInterface* PutForwardsAttr2();
   TestInterface* PutForwardsAttr3();
-  JS::Value JsonifierShouldSkipThis(JSContext*);
+  void GetJsonifierShouldSkipThis(JSContext*, JS::MutableHandle<JS::Value>);
   void SetJsonifierShouldSkipThis(JSContext*, JS::Rooted<JS::Value>&);
   TestParentInterface* JsonifierShouldSkipThis2();
   void SetJsonifierShouldSkipThis2(TestParentInterface&);
   TestCallbackInterface* JsonifierShouldSkipThis3();
   void SetJsonifierShouldSkipThis3(TestCallbackInterface&);
   void ThrowingMethod(ErrorResult& aRv);
   bool GetThrowingAttr(ErrorResult& aRv) const;
   void SetThrowingAttr(bool arg, ErrorResult& aRv);
--- a/dom/bluetooth/BluetoothAdapter.cpp
+++ b/dom/bluetooth/BluetoothAdapter.cpp
@@ -424,40 +424,44 @@ BluetoothAdapter::StartDiscovery(ErrorRe
 }
 
 already_AddRefed<DOMRequest>
 BluetoothAdapter::StopDiscovery(ErrorResult& aRv)
 {
   return StartStopDiscovery(false, aRv);
 }
 
-JS::Value
-BluetoothAdapter::GetDevices(JSContext* aContext, ErrorResult& aRv)
+void
+BluetoothAdapter::GetDevices(JSContext* aContext,
+                             JS::MutableHandle<JS::Value> aDevices,
+                             ErrorResult& aRv)
 {
   if (!mJsDeviceAddresses) {
     BT_WARNING("Devices not yet set!\n");
     aRv.Throw(NS_ERROR_FAILURE);
-    return JS::NullValue();
+    return;
   }
 
   JS::ExposeObjectToActiveJS(mJsDeviceAddresses);
-  return JS::ObjectValue(*mJsDeviceAddresses);
+  aDevices.setObject(*mJsDeviceAddresses);
 }
 
-JS::Value
-BluetoothAdapter::GetUuids(JSContext* aContext, ErrorResult& aRv)
+void
+BluetoothAdapter::GetUuids(JSContext* aContext,
+                           JS::MutableHandle<JS::Value> aUuids,
+                           ErrorResult& aRv)
 {
   if (!mJsUuids) {
     BT_WARNING("UUIDs not yet set!\n");
     aRv.Throw(NS_ERROR_FAILURE);
-    return JS::NullValue();
+    return;
   }
 
   JS::ExposeObjectToActiveJS(mJsUuids);
-  return JS::ObjectValue(*mJsUuids);
+  aUuids.setObject(*mJsUuids);
 }
 
 already_AddRefed<DOMRequest>
 BluetoothAdapter::SetName(const nsAString& aName, ErrorResult& aRv)
 {
   if (mName.Equals(aName)) {
     return FirePropertyAlreadySet(GetOwner(), aRv);
   }
--- a/dom/bluetooth/BluetoothAdapter.h
+++ b/dom/bluetooth/BluetoothAdapter.h
@@ -78,18 +78,20 @@ public:
   }
 
   uint32_t
   DiscoverableTimeout() const
   {
     return mDiscoverableTimeout;
   }
 
-  JS::Value GetDevices(JSContext* aContext, ErrorResult& aRv);
-  JS::Value GetUuids(JSContext* aContext, ErrorResult& aRv);
+  void GetDevices(JSContext* aContext, JS::MutableHandle<JS::Value> aDevices,
+                  ErrorResult& aRv);
+  void GetUuids(JSContext* aContext, JS::MutableHandle<JS::Value> aUuids,
+                ErrorResult& aRv);
 
   already_AddRefed<mozilla::dom::DOMRequest>
     SetName(const nsAString& aName, ErrorResult& aRv);
 
   already_AddRefed<DOMRequest>
     SetDiscoverable(bool aDiscoverable, ErrorResult& aRv);
   already_AddRefed<DOMRequest>
     SetDiscoverableTimeout(uint32_t aTimeout, ErrorResult& aRv);
--- a/dom/bluetooth/BluetoothDevice.cpp
+++ b/dom/bluetooth/BluetoothDevice.cpp
@@ -201,39 +201,43 @@ BluetoothDevice::Notify(const BluetoothS
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling device signal: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
     BT_WARNING(warningMsg.get());
 #endif
   }
 }
 
-JS::Value
-BluetoothDevice::GetUuids(JSContext* aCx, ErrorResult& aRv)
+void
+BluetoothDevice::GetUuids(JSContext* aContext,
+                           JS::MutableHandle<JS::Value> aUuids,
+                           ErrorResult& aRv)
 {
   if (!mJsUuids) {
     BT_WARNING("UUIDs not yet set!");
     aRv.Throw(NS_ERROR_FAILURE);
-    return JS::NullValue();
+    return;
   }
 
   JS::ExposeObjectToActiveJS(mJsUuids);
-  return JS::ObjectValue(*mJsUuids);
+  aUuids.setObject(*mJsUuids);
 }
 
-JS::Value
-BluetoothDevice::GetServices(JSContext* aCx, ErrorResult& aRv)
+void
+BluetoothDevice::GetServices(JSContext* aCx,
+                             JS::MutableHandle<JS::Value> aServices,
+                             ErrorResult& aRv)
 {
   if (!mJsServices) {
     BT_WARNING("Services not yet set!");
     aRv.Throw(NS_ERROR_FAILURE);
-    return JS::Value(JSVAL_NULL);
+    return;
   }
 
   JS::ExposeObjectToActiveJS(mJsServices);
-  return JS::ObjectValue(*mJsServices);
+  aServices.setObject(*mJsServices);
 }
 
 JSObject*
 BluetoothDevice::WrapObject(JSContext* aContext)
 {
   return BluetoothDeviceBinding::Wrap(aContext, this);
 }
--- a/dom/bluetooth/BluetoothDevice.h
+++ b/dom/bluetooth/BluetoothDevice.h
@@ -61,18 +61,20 @@ public:
     return mPaired;
   }
 
   bool Connected() const
   {
     return mConnected;
   }
 
-  JS::Value GetUuids(JSContext* aContext, ErrorResult& aRv);
-  JS::Value GetServices(JSContext* aContext, ErrorResult& aRv);
+  void GetUuids(JSContext* aContext, JS::MutableHandle<JS::Value> aUuids,
+                ErrorResult& aRv);
+  void GetServices(JSContext* aContext, JS::MutableHandle<JS::Value> aServices,
+                   ErrorResult& aRv);
 
   nsISupports*
   ToISupports()
   {
     return static_cast<EventTarget*>(this);
   }
 
   void SetPropertyByValue(const BluetoothNamedValue& aValue) MOZ_OVERRIDE;
--- a/dom/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth2/BluetoothAdapter.cpp
@@ -452,40 +452,44 @@ BluetoothAdapter::StartDiscovery(ErrorRe
 }
 
 already_AddRefed<DOMRequest>
 BluetoothAdapter::StopDiscovery(ErrorResult& aRv)
 {
   return StartStopDiscovery(false, aRv);
 }
 
-JS::Value
-BluetoothAdapter::GetDevices(JSContext* aContext, ErrorResult& aRv)
+void
+BluetoothAdapter::GetDevices(JSContext* aContext,
+                             JS::MutableHandle<JS::Value> aDevices,
+                             ErrorResult& aRv)
 {
   if (!mJsDeviceAddresses) {
     BT_WARNING("Devices not yet set!\n");
     aRv.Throw(NS_ERROR_FAILURE);
-    return JS::NullValue();
+    return;
   }
 
   JS::ExposeObjectToActiveJS(mJsDeviceAddresses);
-  return JS::ObjectValue(*mJsDeviceAddresses);
+  aDevices.setObject(*mJsDeviceAddresses);
 }
 
-JS::Value
-BluetoothAdapter::GetUuids(JSContext* aContext, ErrorResult& aRv)
+void
+BluetoothAdapter::GetUuids(JSContext* aContext,
+                           JS::MutableHandle<JS::Value> aUuids,
+                           ErrorResult& aRv)
 {
   if (!mJsUuids) {
     BT_WARNING("UUIDs not yet set!\n");
     aRv.Throw(NS_ERROR_FAILURE);
-    return JS::NullValue();
+    return;
   }
 
   JS::ExposeObjectToActiveJS(mJsUuids);
-  return JS::ObjectValue(*mJsUuids);
+  aUuids.setObject(*mJsUuids);
 }
 
 already_AddRefed<DOMRequest>
 BluetoothAdapter::SetName(const nsAString& aName, ErrorResult& aRv)
 {
   if (mName.Equals(aName)) {
     return FirePropertyAlreadySet(GetOwner(), aRv);
   }
--- a/dom/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth2/BluetoothAdapter.h
@@ -85,18 +85,20 @@ public:
   }
 
   uint32_t
   DiscoverableTimeout() const
   {
     return mDiscoverableTimeout;
   }
 
-  JS::Value GetDevices(JSContext* aContext, ErrorResult& aRv);
-  JS::Value GetUuids(JSContext* aContext, ErrorResult& aRv);
+  void GetDevices(JSContext* aContext, JS::MutableHandle<JS::Value> aDevices,
+                  ErrorResult& aRv);
+  void GetUuids(JSContext* aContext, JS::MutableHandle<JS::Value> aUuids,
+                ErrorResult& aRv);
 
   already_AddRefed<mozilla::dom::DOMRequest>
     SetName(const nsAString& aName, ErrorResult& aRv);
 
   already_AddRefed<DOMRequest>
     SetDiscoverable(bool aDiscoverable, ErrorResult& aRv);
   already_AddRefed<DOMRequest>
     SetDiscoverableTimeout(uint32_t aTimeout, ErrorResult& aRv);
--- a/dom/bluetooth2/BluetoothDevice.cpp
+++ b/dom/bluetooth2/BluetoothDevice.cpp
@@ -201,39 +201,43 @@ BluetoothDevice::Notify(const BluetoothS
     nsCString warningMsg;
     warningMsg.AssignLiteral("Not handling device signal: ");
     warningMsg.Append(NS_ConvertUTF16toUTF8(aData.name()));
     BT_WARNING(warningMsg.get());
 #endif
   }
 }
 
-JS::Value
-BluetoothDevice::GetUuids(JSContext* aCx, ErrorResult& aRv)
+void
+BluetoothDevice::GetUuids(JSContext* aContext,
+                           JS::MutableHandle<JS::Value> aUuids,
+                           ErrorResult& aRv)
 {
   if (!mJsUuids) {
     BT_WARNING("UUIDs not yet set!");
     aRv.Throw(NS_ERROR_FAILURE);
-    return JS::NullValue();
+    return;
   }
 
   JS::ExposeObjectToActiveJS(mJsUuids);
-  return JS::ObjectValue(*mJsUuids);
+  aUuids.setObject(*mJsUuids);
 }
 
-JS::Value
-BluetoothDevice::GetServices(JSContext* aCx, ErrorResult& aRv)
+void
+BluetoothDevice::GetServices(JSContext* aCx,
+                             JS::MutableHandle<JS::Value> aServices,
+                             ErrorResult& aRv)
 {
   if (!mJsServices) {
     BT_WARNING("Services not yet set!");
     aRv.Throw(NS_ERROR_FAILURE);
-    return JS::Value(JSVAL_NULL);
+    return;
   }
 
   JS::ExposeObjectToActiveJS(mJsServices);
-  return JS::ObjectValue(*mJsServices);
+  aServices.setObject(*mJsServices);
 }
 
 JSObject*
 BluetoothDevice::WrapObject(JSContext* aContext)
 {
   return BluetoothDeviceBinding::Wrap(aContext, this);
 }
--- a/dom/bluetooth2/BluetoothDevice.h
+++ b/dom/bluetooth2/BluetoothDevice.h
@@ -61,18 +61,20 @@ public:
     return mPaired;
   }
 
   bool Connected() const
   {
     return mConnected;
   }
 
-  JS::Value GetUuids(JSContext* aContext, ErrorResult& aRv);
-  JS::Value GetServices(JSContext* aContext, ErrorResult& aRv);
+  void GetUuids(JSContext* aContext, JS::MutableHandle<JS::Value> aUuids,
+                ErrorResult& aRv);
+  void GetServices(JSContext* aContext, JS::MutableHandle<JS::Value> aServices,
+                   ErrorResult& aRv);
 
   nsISupports*
   ToISupports()
   {
     return static_cast<EventTarget*>(this);
   }
 
   void SetPropertyByValue(const BluetoothNamedValue& aValue) MOZ_OVERRIDE;
--- a/dom/browser-element/BrowserElementParent.cpp
+++ b/dom/browser-element/BrowserElementParent.cpp
@@ -10,16 +10,17 @@
 
 #ifdef CreateEvent
 #undef CreateEvent
 #endif
 
 #include "BrowserElementParent.h"
 #include "mozilla/EventDispatcher.h"
 #include "mozilla/dom/HTMLIFrameElement.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "nsIDOMCustomEvent.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsVariant.h"
 #include "mozilla/dom/BrowserElementDictionariesBinding.h"
 #include "nsCxPusher.h"
 #include "GeneratedEventClasses.h"
 
 using namespace mozilla;
@@ -155,17 +156,17 @@ BrowserElementParent::DispatchOpenWindow
 
   nsIGlobalObject* sgo = aPopupFrameElement->OwnerDoc()->GetScopeObject();
   if (!sgo) {
     return BrowserElementParent::OPEN_WINDOW_IGNORED;
   }
 
   JS::Rooted<JSObject*> global(cx, sgo->GetGlobalJSObject());
   JSAutoCompartment ac(cx, global);
-  if (!detail.ToObject(cx, &val)) {
+  if (!ToJSValue(cx, detail, &val)) {
     MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
     return BrowserElementParent::OPEN_WINDOW_IGNORED;
   }
 
   nsEventStatus status;
   bool dispatchSucceeded =
     DispatchCustomDOMEvent(aOpenerFrameElement,
                            NS_LITERAL_STRING("mozbrowseropenwindow"),
@@ -327,17 +328,17 @@ NS_IMETHODIMP DispatchAsyncScrollEventRu
 
   AutoSafeJSContext cx;
   JS::Rooted<JSObject*> globalJSObject(cx, globalObject->GetGlobalJSObject());
   NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
 
   JSAutoCompartment ac(cx, globalJSObject);
   JS::Rooted<JS::Value> val(cx);
 
-  if (!detail.ToObject(cx, &val)) {
+  if (!ToJSValue(cx, detail, &val)) {
     MOZ_CRASH("Failed to convert dictionary to JS::Value due to OOM.");
     return NS_ERROR_FAILURE;
   }
 
   nsEventStatus status = nsEventStatus_eIgnore;
   DispatchCustomDOMEvent(frameElement,
                          NS_LITERAL_STRING("mozbrowserasyncscroll"),
                          cx,
--- a/dom/camera/DOMCameraCapabilities.cpp
+++ b/dom/camera/DOMCameraCapabilities.cpp
@@ -288,20 +288,22 @@ CameraCapabilities::MaxExposureCompensat
 }
 
 double
 CameraCapabilities::ExposureCompensationStep() const
 {
   return mExposureCompensationStep;
 }
 
-JS::Value
-CameraCapabilities::RecorderProfiles(JSContext* aCx) const
+void
+CameraCapabilities::GetRecorderProfiles(JSContext* aCx,
+                                        JS::MutableHandle<JS::Value> aRetval) const
 {
-  return mRecorderProfiles;
+  JS::ExposeValueToActiveJS(mRecorderProfiles);
+  aRetval.set(mRecorderProfiles);
 }
 
 void
 CameraCapabilities::GetIsoModes(nsTArray<nsString>& retval) const
 {
   retval = mIsoModes;
 }
 
--- a/dom/camera/DOMCameraCapabilities.h
+++ b/dom/camera/DOMCameraCapabilities.h
@@ -66,17 +66,17 @@ public:
   void GetFocusModes(nsTArray<nsString>& aRetVal) const;
   void GetZoomRatios(nsTArray<double>& aRetVal) const;
   uint32_t MaxFocusAreas() const;
   uint32_t MaxMeteringAreas() const;
   uint32_t MaxDetectedFaces() const;
   double MinExposureCompensation() const;
   double MaxExposureCompensation() const;
   double ExposureCompensationStep() const;
-  JS::Value RecorderProfiles(JSContext* cx) const;
+  void GetRecorderProfiles(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval) const;
   void GetIsoModes(nsTArray<nsString>& aRetVal) const;
 
 protected:
   ~CameraCapabilities();
 
   nsresult TranslateToDictionary(ICameraControl* aCameraControl,
                                  uint32_t aKey, nsTArray<CameraSize>& aSizes);
 
--- a/dom/crypto/RsaKeyAlgorithm.h
+++ b/dom/crypto/RsaKeyAlgorithm.h
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 mozilla_dom_RsaKeyAlgorithm_h
 #define mozilla_dom_RsaKeyAlgorithm_h
 
+#include "mozilla/ErrorResult.h"
 #include "mozilla/dom/KeyAlgorithm.h"
 #include "js/TypeDecls.h"
 
 namespace mozilla {
 namespace dom {
 
 class RsaKeyAlgorithm : public KeyAlgorithm
 {
@@ -30,20 +31,26 @@ public:
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
   uint32_t ModulusLength() const
   {
     return mModulusLength;
   }
 
-  JSObject* PublicExponent(JSContext* cx) const
+  void GetPublicExponent(JSContext* cx, JS::MutableHandle<JSObject*> aRetval,
+                         ErrorResult& aError) const
   {
     TypedArrayCreator<Uint8Array> creator(mPublicExponent);
-    return creator.Create(cx);
+    JSObject* retval = creator.Create(cx);
+    if (!retval) {
+      aError.Throw(NS_ERROR_OUT_OF_MEMORY);
+    } else {
+      aRetval.set(retval);
+    }
   }
 
   virtual bool WriteStructuredClone(JSStructuredCloneWriter* aWriter) const MOZ_OVERRIDE;
   static KeyAlgorithm* Create(nsIGlobalObject* aGlobal,
                               JSStructuredCloneReader* aReader);
 
 protected:
   uint32_t mModulusLength;
--- a/dom/datastore/DataStoreDB.cpp
+++ b/dom/datastore/DataStoreDB.cpp
@@ -124,17 +124,18 @@ DataStoreDB::HandleEvent(nsIDOMEvent* aE
 nsresult
 DataStoreDB::UpgradeSchema()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   AutoSafeJSContext cx;
 
   ErrorResult error;
-  JS::Rooted<JS::Value> result(cx, mRequest->GetResult(error));
+  JS::Rooted<JS::Value> result(cx);
+  mRequest->GetResult(&result, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.ErrorCode();
   }
 
   MOZ_ASSERT(result.isObject());
 
   IDBDatabase* database = nullptr;
   nsresult rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), database);
@@ -185,17 +186,18 @@ DataStoreDB::UpgradeSchema()
 nsresult
 DataStoreDB::DatabaseOpened()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   AutoSafeJSContext cx;
 
   ErrorResult error;
-  JS::Rooted<JS::Value> result(cx, mRequest->GetResult(error));
+  JS::Rooted<JS::Value> result(cx);
+  mRequest->GetResult(&result, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.ErrorCode();
   }
 
   MOZ_ASSERT(result.isObject());
 
   nsresult rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), mDatabase);
   if (NS_FAILED(rv)) {
--- a/dom/datastore/DataStoreRevision.cpp
+++ b/dom/datastore/DataStoreRevision.cpp
@@ -5,16 +5,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DataStoreRevision.h"
 
 #include "DataStoreCallbacks.h"
 #include "DataStoreService.h"
 #include "mozilla/dom/DataStoreBinding.h"
 #include "mozilla/dom/indexedDB/IDBObjectStore.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "nsIDOMEvent.h"
 
 namespace mozilla {
 namespace dom {
 
 using namespace indexedDB;
 
 NS_IMPL_ISUPPORTS(DataStoreRevision, nsIDOMEventListener)
@@ -52,17 +53,17 @@ DataStoreRevision::AddRevision(JSContext
       break;
 
     default:
       MOZ_ASSUME_UNREACHABLE("This should not happen");
       break;
   }
 
   JS::Rooted<JS::Value> value(aCx);
-  if (!data.ToObject(aCx, &value)) {
+  if (!ToJSValue(aCx, data, &value)) {
     return NS_ERROR_FAILURE;
   }
 
   ErrorResult error;
   mRequest = aStore->Put(aCx, value, JS::UndefinedHandleValue, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.ErrorCode();
   }
--- a/dom/datastore/DataStoreService.cpp
+++ b/dom/datastore/DataStoreService.cpp
@@ -582,17 +582,18 @@ public:
 
     mRequest->RemoveEventListener(NS_LITERAL_STRING("success"), this, false);
 
     // Note: this cx is only used for rooting and AddRevision, neither of which
     // actually care which compartment we're in.
     AutoSafeJSContext cx;
 
     ErrorResult error;
-    JS::Rooted<JS::Value> result(cx, mRequest->GetResult(error));
+    JS::Rooted<JS::Value> result(cx);
+    mRequest->GetResult(cx, &result, error);
     if (NS_WARN_IF(error.Failed())) {
       return error.ErrorCode();
     }
 
     // This means that the content is a IDBCursor, so the first revision already
     // exists.
     if (result.isObject()) {
       nsRefPtr<DataStoreService> service = DataStoreService::Get();
--- a/dom/encoding/TextEncoder.cpp
+++ b/dom/encoding/TextEncoder.cpp
@@ -29,39 +29,40 @@ TextEncoder::Init(const nsAString& aEnco
     aRv.ThrowTypeError(MSG_DOM_ENCODING_NOT_UTF);
     return;
   }
 
   // Create an encoder object for mEncoding.
   mEncoder = EncodingUtils::EncoderForEncoding(mEncoding);
 }
 
-JSObject*
+void
 TextEncoder::Encode(JSContext* aCx,
                     JS::Handle<JSObject*> aObj,
                     const nsAString& aString,
                     const bool aStream,
+		    JS::MutableHandle<JSObject*> aRetval,
                     ErrorResult& aRv)
 {
   // Run the steps of the encoding algorithm.
   int32_t srcLen = aString.Length();
   int32_t maxLen;
   const char16_t* data = PromiseFlatString(aString).get();
   nsresult rv = mEncoder->GetMaxLength(data, srcLen, &maxLen);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
-    return nullptr;
+    return;
   }
   // Need a fallible allocator because the caller may be a content
   // and the content can specify the length of the string.
   static const fallible_t fallible = fallible_t();
   nsAutoArrayPtr<char> buf(new (fallible) char[maxLen + 1]);
   if (!buf) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return nullptr;
+    return;
   }
 
   int32_t dstLen = maxLen;
   rv = mEncoder->Convert(data, &srcLen, buf, &dstLen);
 
   // If the internal streaming flag is not set, then reset
   // the encoding algorithm state to the default values for encoding.
   if (!aStream) {
@@ -75,24 +76,24 @@ TextEncoder::Encode(JSContext* aCx,
   JSObject* outView = nullptr;
   if (NS_SUCCEEDED(rv)) {
     buf[dstLen] = '\0';
     JSAutoCompartment ac(aCx, aObj);
     outView = Uint8Array::Create(aCx, dstLen,
                                  reinterpret_cast<uint8_t*>(buf.get()));
     if (!outView) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return nullptr;
+      return;
     }
   }
 
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
   }
-  return outView;
+  aRetval.set(outView);
 }
 
 void
 TextEncoder::GetEncoding(nsAString& aEncoding)
 {
   CopyASCIItoUTF16(mEncoding, aEncoding);
   nsContentUtils::ASCIIToLower(aEncoding);
 }
--- a/dom/encoding/TextEncoder.h
+++ b/dom/encoding/TextEncoder.h
@@ -41,22 +41,23 @@ public:
   ~TextEncoder()
   {}
 
   JSObject* WrapObject(JSContext* aCx, bool* aTookOwnership)
   {
     return TextEncoderBinding::Wrap(aCx, this, aTookOwnership);
   }
 
-  JSObject* Encode(JSContext* aCx,
-                   JS::Handle<JSObject*> aObj,
-                   const nsAString& aString,
-                   const TextEncodeOptions& aOptions,
-                   ErrorResult& aRv) {
-    return TextEncoder::Encode(aCx, aObj, aString, aOptions.mStream, aRv);
+  void Encode(JSContext* aCx,
+	      JS::Handle<JSObject*> aObj,
+	      const nsAString& aString,
+	      const TextEncodeOptions& aOptions,
+	      JS::MutableHandle<JSObject*> aRetval,
+	      ErrorResult& aRv) {
+    TextEncoder::Encode(aCx, aObj, aString, aOptions.mStream, aRetval, aRv);
   }
 
 protected:
 
   /**
    * Validates provided encoding and throws an exception if invalid encoding.
    * If no encoding is provided then mEncoding is default initialised to "utf-8".
    *
@@ -80,23 +81,25 @@ public:
    *
    * @param aCx        Javascript context.
    * @param aObj       the wrapper of the TextEncoder
    * @param aString    utf-16 code units to be encoded.
    * @param aOptions   Streaming option. Initialised by default to false.
    *                   If the streaming option is false, then the encoding
    *                   algorithm state will get reset. If set to true then
    *                   the previous encoding is reused/continued.
-   * @return JSObject* The Uint8Array wrapped in a JS object.
+   * @return JSObject* The Uint8Array wrapped in a JS object.  Returned via
+   *                   the aRetval out param.
    */
-  JSObject* Encode(JSContext* aCx,
-                   JS::Handle<JSObject*> aObj,
-                   const nsAString& aString,
-                   const bool aStream,
-                   ErrorResult& aRv);
+  void Encode(JSContext* aCx,
+	      JS::Handle<JSObject*> aObj,
+	      const nsAString& aString,
+	      const bool aStream,
+	      JS::MutableHandle<JSObject*> aRetval,
+	      ErrorResult& aRv);
 
 private:
   nsCString mEncoding;
   nsCOMPtr<nsIUnicodeEncoder> mEncoder;
 };
 
 } // dom
 } // mozilla
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -635,37 +635,37 @@ DataTransfer::MozGetDataAt(const nsAStri
       NS_IF_ADDREF(*aData);
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
-JS::Value
+void
 DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
-                           uint32_t aIndex, mozilla::ErrorResult& aRv)
+                           uint32_t aIndex,
+                           JS::MutableHandle<JS::Value> aRetval,
+                           mozilla::ErrorResult& aRv)
 {
   nsCOMPtr<nsIVariant> data;
   aRv = MozGetDataAt(aFormat, aIndex, getter_AddRefs(data));
   if (aRv.Failed()) {
-    return JS::UndefinedValue();
+    return;
   }
 
   if (!data) {
-    return JS::NullValue();
+    return;
   }
 
   JS::Rooted<JS::Value> result(aCx);
-  if (!VariantToJsval(aCx, data, &result)) {
+  if (!VariantToJsval(aCx, data, aRetval)) {
     aRv = NS_ERROR_FAILURE;
-    return JS::UndefinedValue();
+    return;
   }
-
-  return result;
 }
 
 NS_IMETHODIMP
 DataTransfer::MozSetDataAt(const nsAString& aFormat, nsIVariant* aData,
                            uint32_t aIndex)
 {
   if (aFormat.IsEmpty()) {
     return NS_OK;
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -159,18 +159,19 @@ public:
   }
   already_AddRefed<DOMStringList> MozTypesAt(uint32_t aIndex,
                                              mozilla::ErrorResult& aRv);
   void MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
                       mozilla::ErrorResult& aRv);
   void MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
                     JS::Handle<JS::Value> aData, uint32_t aIndex,
                     mozilla::ErrorResult& aRv);
-  JS::Value MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
-                         uint32_t aIndex, mozilla::ErrorResult& aRv);
+  void MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
+                    uint32_t aIndex, JS::MutableHandle<JS::Value> aRetval,
+                    mozilla::ErrorResult& aRv);
   bool MozUserCancelled()
   {
     return mUserCancelled;
   }
   already_AddRefed<nsINode> GetMozSourceNode();
 
   mozilla::dom::Element* GetDragTarget()
   {
--- a/dom/events/JSEventHandler.cpp
+++ b/dom/events/JSEventHandler.cpp
@@ -13,16 +13,17 @@
 #include "nsVariant.h"
 #include "nsIDOMBeforeUnloadEvent.h"
 #include "nsGkAtoms.h"
 #include "xpcpublic.h"
 #include "nsJSEnvironment.h"
 #include "nsDOMJSUtils.h"
 #include "WorkerPrivate.h"
 #include "mozilla/ContentEvents.h"
+#include "mozilla/CycleCollectedJSRuntime.h"
 #include "mozilla/HoldDropJSObjects.h"
 #include "mozilla/JSEventHandler.h"
 #include "mozilla/Likely.h"
 #include "mozilla/dom/ErrorEvent.h"
 #include "mozilla/dom/UnionTypes.h"
 
 namespace mozilla {
 
@@ -150,17 +151,17 @@ JSEventHandler::HandleEvent(nsIDOMEvent*
       lineNumber.Construct();
       lineNumber.Value() = scriptEvent->Lineno();
 
       columnNumber.Construct();
       columnNumber.Value() = scriptEvent->Colno();
 
       ThreadsafeAutoJSContext cx;
       error.Construct(cx);
-      error.Value() = scriptEvent->Error(cx);
+      scriptEvent->GetError(cx, &error.Value());
     } else {
       msgOrEvent.SetAsEvent() = aEvent->InternalDOMEvent();
     }
 
     nsRefPtr<OnErrorEventHandlerNonNull> handler =
       mTypedHandler.OnErrorEventHandler();
     ErrorResult rv;
     bool handled = handler->Call(mTarget, msgOrEvent, fileName, lineNumber,
@@ -205,18 +206,18 @@ JSEventHandler::HandleEvent(nsIDOMEvent*
     }
 
     return NS_OK;
   }
 
   MOZ_ASSERT(mTypedHandler.Type() == TypedEventHandler::eNormal);
   ErrorResult rv;
   nsRefPtr<EventHandlerNonNull> handler = mTypedHandler.NormalEventHandler();
-  JS::Value retval =
-    handler->Call(mTarget, *(aEvent->InternalDOMEvent()), rv);
+  JS::Rooted<JS::Value> retval(CycleCollectedJSRuntime::Get()->Runtime());
+  handler->Call(mTarget, *(aEvent->InternalDOMEvent()), &retval, rv);
   if (rv.Failed()) {
     return rv.ErrorCode();
   }
 
   // If the handler returned false and its sense is not reversed,
   // or the handler returned true and its sense is reversed from
   // the usual (false means cancel), then prevent default.
   if (retval.isBoolean() &&
--- a/dom/events/MessageEvent.cpp
+++ b/dom/events/MessageEvent.cpp
@@ -61,28 +61,29 @@ MessageEvent::WrapObject(JSContext* aCx)
 {
   return mozilla::dom::MessageEventBinding::Wrap(aCx, this);
 }
 
 NS_IMETHODIMP
 MessageEvent::GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData)
 {
   ErrorResult rv;
-  aData.set(GetData(aCx, rv));
+  GetData(aCx, aData, rv);
   return rv.ErrorCode();
 }
 
-JS::Value
-MessageEvent::GetData(JSContext* aCx, ErrorResult& aRv)
+void
+MessageEvent::GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData,
+                      ErrorResult& aRv)
 {
-  JS::Rooted<JS::Value> data(aCx, mData);
-  if (!JS_WrapValue(aCx, &data)) {
+  JS::ExposeValueToActiveJS(mData);
+  aData.set(mData);
+  if (!JS_WrapValue(aCx, aData)) {
     aRv.Throw(NS_ERROR_FAILURE);
   }
-  return data;
 }
 
 NS_IMETHODIMP
 MessageEvent::GetOrigin(nsAString& aOrigin)
 {
   aOrigin = mOrigin;
   return NS_OK;
 }
--- a/dom/events/MessageEvent.h
+++ b/dom/events/MessageEvent.h
@@ -41,17 +41,18 @@ public:
 
   NS_DECL_NSIDOMMESSAGEEVENT
 
   // Forward to base class
   NS_FORWARD_TO_EVENT
 
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
-  JS::Value GetData(JSContext* aCx, ErrorResult& aRv);
+  void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData,
+               ErrorResult& aRv);
 
   void GetSource(Nullable<OwningWindowProxyOrMessagePort>& aValue) const;
 
   MessagePortList* GetPorts()
   {
     return mPorts;
   }
 
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -605,98 +605,111 @@ IDBCursor::GetSource(OwningIDBObjectStor
       aSource.SetAsIDBIndex() = mIndex;
       break;
 
     default:
       MOZ_ASSERT_UNREACHABLE("Bad type!");
   }
 }
 
-JS::Value
-IDBCursor::GetKey(JSContext* aCx, ErrorResult& aRv)
+void
+IDBCursor::GetKey(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                  ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!mKey.IsUnset() || !mHaveValue);
 
   if (!mHaveValue) {
-    return JSVAL_VOID;
+    aResult.setUndefined();
+    return;
   }
 
   if (!mHaveCachedKey) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     aRv = mKey.ToJSVal(aCx, mCachedKey);
-    ENSURE_SUCCESS(aRv, JSVAL_VOID);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
 
     mHaveCachedKey = true;
   }
 
-  return mCachedKey;
+  JS::ExposeValueToActiveJS(mCachedKey);
+  aResult.set(mCachedKey);
 }
 
-JS::Value
-IDBCursor::GetPrimaryKey(JSContext* aCx, ErrorResult& aRv)
+void
+IDBCursor::GetPrimaryKey(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                         ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mHaveValue) {
-    return JSVAL_VOID;
+    aResult.setUndefined();
+    return;
   }
 
   if (!mHaveCachedPrimaryKey) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     const Key& key =
       (mType == OBJECTSTORE || mType == OBJECTSTOREKEY) ? mKey : mObjectKey;
     MOZ_ASSERT(!key.IsUnset());
 
     aRv = key.ToJSVal(aCx, mCachedPrimaryKey);
-    ENSURE_SUCCESS(aRv, JSVAL_VOID);
+    if (NS_WARN_IF(aRv.Failed())) {
+      return;
+    }
 
     mHaveCachedPrimaryKey = true;
   }
 
-  return mCachedPrimaryKey;
+  JS::ExposeValueToActiveJS(mCachedPrimaryKey);
+  aResult.set(mCachedPrimaryKey);
 }
 
-JS::Value
-IDBCursor::GetValue(JSContext* aCx, ErrorResult& aRv)
+void
+IDBCursor::GetValue(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                    ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mType == OBJECTSTORE || mType == INDEXOBJECT);
 
   if (!mHaveValue) {
-    return JSVAL_VOID;
+    aResult.setUndefined();
+    return;
   }
 
   if (!mHaveCachedValue) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     JS::Rooted<JS::Value> val(aCx);
     if (!IDBObjectStore::DeserializeValue(aCx, mCloneReadInfo, &val)) {
       aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
-      return JSVAL_VOID;
+      return;
     }
 
     mCloneReadInfo.mCloneBuffer.clear();
 
     mCachedValue = val;
     mHaveCachedValue = true;
   }
 
-  return mCachedValue;
+  JS::ExposeValueToActiveJS(mCachedValue);
+  aResult.set(mCachedValue);
 }
 
 void
 IDBCursor::Continue(JSContext* aCx,
                     JS::Handle<JS::Value> aKey,
                     ErrorResult &aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/indexedDB/IDBCursor.h
+++ b/dom/indexedDB/IDBCursor.h
@@ -179,36 +179,39 @@ public:
   }
 
   void
   GetSource(OwningIDBObjectStoreOrIDBIndex& aSource) const;
 
   IDBCursorDirection
   GetDirection() const;
 
-  JS::Value
-  GetKey(JSContext* aCx, ErrorResult& aRv);
+  void
+  GetKey(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+         ErrorResult& aRv);
 
-  JS::Value
-  GetPrimaryKey(JSContext* aCx, ErrorResult& aRv);
+  void
+  GetPrimaryKey(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                ErrorResult& aRv);
 
   already_AddRefed<IDBRequest>
   Update(JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv);
 
   void
   Advance(uint32_t aCount, ErrorResult& aRv);
 
   void
   Continue(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv);
 
   already_AddRefed<IDBRequest>
   Delete(JSContext* aCx, ErrorResult& aRv);
 
-  JS::Value
-  GetValue(JSContext* aCx, ErrorResult& aRv);
+  void
+  GetValue(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+           ErrorResult& aRv);
 
 protected:
   IDBCursor();
   ~IDBCursor();
 
   void DropJSObjects();
 
   static
--- a/dom/indexedDB/IDBIndex.cpp
+++ b/dom/indexedDB/IDBIndex.cpp
@@ -821,34 +821,40 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBIndex
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBIndex)
 
 JSObject*
 IDBIndex::WrapObject(JSContext* aCx)
 {
   return IDBIndexBinding::Wrap(aCx, this);
 }
 
-JS::Value
-IDBIndex::GetKeyPath(JSContext* aCx, ErrorResult& aRv)
+void
+IDBIndex::GetKeyPath(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                     ErrorResult& aRv)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mCachedKeyPath.isUndefined()) {
-    return mCachedKeyPath;
+    JS::ExposeValueToActiveJS(mCachedKeyPath);
+    aResult.set(mCachedKeyPath);
+    return;
   }
 
   aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
-  ENSURE_SUCCESS(aRv, JSVAL_VOID);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
 
   if (mCachedKeyPath.isGCThing()) {
     mozilla::HoldJSObjects(this);
     mRooted = true;
   }
 
-  return mCachedKeyPath;
+  JS::ExposeValueToActiveJS(mCachedKeyPath);
+  aResult.set(mCachedKeyPath);
 }
 
 already_AddRefed<IDBRequest>
 IDBIndex::Get(JSContext* aCx, JS::Handle<JS::Value> aKey, ErrorResult& aRv)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   IDBTransaction* transaction = mObjectStore->Transaction();
--- a/dom/indexedDB/IDBIndex.h
+++ b/dom/indexedDB/IDBIndex.h
@@ -171,18 +171,19 @@ public:
 
   IDBObjectStore*
   ObjectStore() const
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     return mObjectStore;
   }
 
-  JS::Value
-  GetKeyPath(JSContext* aCx, ErrorResult& aRv);
+  void
+  GetKeyPath(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+             ErrorResult& aRv);
 
   bool
   MultiEntry() const
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     return mMultiEntry;
   }
 
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -159,58 +159,62 @@ IDBKeyRange::~IDBKeyRange()
 }
 
 JSObject*
 IDBKeyRange::WrapObject(JSContext* aCx)
 {
   return IDBKeyRangeBinding::Wrap(aCx, this);
 }
 
-JS::Value
-IDBKeyRange::GetLower(JSContext* aCx, ErrorResult& aRv)
+void
+IDBKeyRange::GetLower(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                      ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedLowerVal) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     aRv = Lower().ToJSVal(aCx, mCachedLowerVal);
     if (aRv.Failed()) {
-      return JS::UndefinedValue();
+      return;
     }
 
     mHaveCachedLowerVal = true;
   }
 
-  return mCachedLowerVal;
+  JS::ExposeValueToActiveJS(mCachedLowerVal);
+  aResult.set(mCachedLowerVal);
 }
 
-JS::Value
-IDBKeyRange::GetUpper(JSContext* aCx, ErrorResult& aRv)
+void
+IDBKeyRange::GetUpper(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                      ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveCachedUpperVal) {
     if (!mRooted) {
       mozilla::HoldJSObjects(this);
       mRooted = true;
     }
 
     aRv = Upper().ToJSVal(aCx, mCachedUpperVal);
     if (aRv.Failed()) {
-      return JS::UndefinedValue();
+      return;
     }
 
     mHaveCachedUpperVal = true;
   }
 
-  return mCachedUpperVal;
+  JS::ExposeValueToActiveJS(mCachedUpperVal);
+  aResult.set(mCachedUpperVal);
 }
 
 // static
 already_AddRefed<IDBKeyRange>
 IDBKeyRange::Only(const GlobalObject& aGlobal, JSContext* aCx,
                   JS::Handle<JS::Value> aValue, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
--- a/dom/indexedDB/IDBKeyRange.h
+++ b/dom/indexedDB/IDBKeyRange.h
@@ -151,21 +151,23 @@ public:
   WrapObject(JSContext* aCx);
 
   nsISupports*
   GetParentObject() const
   {
     return mGlobal;
   }
 
-  JS::Value
-  GetLower(JSContext* aCx, ErrorResult& aRv);
+  void
+  GetLower(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+           ErrorResult& aRv);
 
-  JS::Value
-  GetUpper(JSContext* aCx, ErrorResult& aRv);
+  void
+  GetUpper(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+           ErrorResult& aRv);
 
   bool
   LowerOpen() const
   {
     return mLowerOpen;
   }
 
   bool
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -2617,34 +2617,40 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBObjec
 NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBObjectStore)
 
 JSObject*
 IDBObjectStore::WrapObject(JSContext* aCx)
 {
   return IDBObjectStoreBinding::Wrap(aCx, this);
 }
 
-JS::Value
-IDBObjectStore::GetKeyPath(JSContext* aCx, ErrorResult& aRv)
+void
+IDBObjectStore::GetKeyPath(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+                           ErrorResult& aRv)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mCachedKeyPath.isUndefined()) {
-    return mCachedKeyPath;
+    JS::ExposeValueToActiveJS(mCachedKeyPath);
+    aResult.set(mCachedKeyPath);
+    return;
   }
 
   aRv = GetKeyPath().ToJSVal(aCx, mCachedKeyPath);
-  ENSURE_SUCCESS(aRv, JSVAL_VOID);
+  if (NS_WARN_IF(aRv.Failed())) {
+    return;
+  }
 
   if (mCachedKeyPath.isGCThing()) {
     mozilla::HoldJSObjects(this);
     mRooted = true;
   }
 
-  return mCachedKeyPath;
+  JS::ExposeValueToActiveJS(mCachedKeyPath);
+  aResult.set(mCachedKeyPath);
 }
 
 already_AddRefed<DOMStringList>
 IDBObjectStore::GetIndexNames(ErrorResult& aRv)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   nsRefPtr<DOMStringList> list(new DOMStringList());
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -277,18 +277,19 @@ public:
 
   void
   GetName(nsString& aName) const
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     aName.Assign(mName);
   }
 
-  JS::Value
-  GetKeyPath(JSContext* aCx, ErrorResult& aRv);
+  void
+  GetKeyPath(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+             ErrorResult& aRv);
 
   already_AddRefed<DOMStringList>
   GetIndexNames(ErrorResult& aRv);
 
   IDBTransaction*
   Transaction() const
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -321,27 +321,29 @@ IDBRequest::ReadyState() const
 }
 
 JSObject*
 IDBRequest::WrapObject(JSContext* aCx)
 {
   return IDBRequestBinding::Wrap(aCx, this);
 }
 
-JS::Value
-IDBRequest::GetResult(mozilla::ErrorResult& aRv) const
+void
+IDBRequest::GetResult(JS::MutableHandle<JS::Value> aResult,
+                      ErrorResult& aRv) const
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveResultOrErrorCode) {
     // XXX Need a real error code here.
     aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
   }
 
-  return mResultVal;
+  JS::ExposeValueToActiveJS(mResultVal);
+  aResult.set(mResultVal);
 }
 
 mozilla::dom::DOMError*
 IDBRequest::GetError(mozilla::ErrorResult& aRv)
 {
   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 
   if (!mHaveResultOrErrorCode) {
--- a/dom/indexedDB/IDBRequest.h
+++ b/dom/indexedDB/IDBRequest.h
@@ -126,23 +126,24 @@ public:
 
   // WebIDL
   nsPIDOMWindow*
   GetParentObject() const
   {
     return GetOwner();
   }
 
-  JS::Value
-  GetResult(ErrorResult& aRv) const;
+  void
+  GetResult(JS::MutableHandle<JS::Value> aResult, ErrorResult& aRv) const;
 
-  JS::Value
-  GetResult(JSContext* aCx, ErrorResult& aRv) const
+  void
+  GetResult(JSContext* aCx, JS::MutableHandle<JS::Value> aResult,
+            ErrorResult& aRv) const
   {
-    return GetResult(aRv);
+    GetResult(aResult, aRv);
   }
 
   IDBTransaction*
   GetTransaction() const
   {
     NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
     return mTransaction;
   }
--- a/dom/indexedDB/ipc/IndexedDBParent.cpp
+++ b/dom/indexedDB/ipc/IndexedDBParent.cpp
@@ -403,17 +403,18 @@ IndexedDBDatabaseParent::HandleRequestEv
     }
 
     return NS_OK;
   }
 
   AutoSafeJSContext cx;
 
   ErrorResult error;
-  JS::Rooted<JS::Value> result(cx, mOpenRequest->GetResult(cx, error));
+  JS::Rooted<JS::Value> result(cx);
+  mOpenRequest->GetResult(cx, &result, error);
   ENSURE_SUCCESS(error, error.ErrorCode());
 
   MOZ_ASSERT(!result.isPrimitive());
 
   IDBDatabase *database;
   rv = UNWRAP_OBJECT(IDBDatabase, &result.toObject(), database);
   if (NS_FAILED(rv)) {
     NS_WARNING("Didn't get the object we expected!");
--- a/dom/mobileconnection/src/MobileConnection.cpp
+++ b/dom/mobileconnection/src/MobileConnection.cpp
@@ -5,16 +5,17 @@
 #include "MobileConnection.h"
 
 #include "GeneratedEvents.h"
 #include "mozilla/dom/CFStateChangeEvent.h"
 #include "mozilla/dom/DataErrorEvent.h"
 #include "mozilla/dom/MozClirModeEvent.h"
 #include "mozilla/dom/MozEmergencyCbModeEvent.h"
 #include "mozilla/dom/MozOtaStatusEvent.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "mozilla/dom/USSDReceivedEvent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIPermissionManager.h"
 #include "nsIVariant.h"
 #include "nsJSON.h"
 #include "nsJSUtils.h"
@@ -600,17 +601,17 @@ MobileConnection::SetCallForwardingOptio
 
   if (!mProvider) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoSafeJSContext cx;
   JS::Rooted<JS::Value> options(cx);
-  if (!aOptions.ToObject(cx, &options)) {
+  if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
   nsCOMPtr<nsIDOMDOMRequest> request;
   nsresult rv = mProvider->SetCallForwarding(mClientId, GetOwner(), options,
                                              getter_AddRefs(request));
   if (NS_FAILED(rv)) {
@@ -631,17 +632,17 @@ MobileConnection::GetCallBarringOption(c
 
   if (!mProvider) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoSafeJSContext cx;
   JS::Rooted<JS::Value> options(cx);
-  if (!aOptions.ToObject(cx, &options)) {
+  if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
   nsCOMPtr<nsIDOMDOMRequest> request;
   nsresult rv = mProvider->GetCallBarring(mClientId, GetOwner(), options,
                                           getter_AddRefs(request));
   if (NS_FAILED(rv)) {
@@ -662,17 +663,17 @@ MobileConnection::SetCallBarringOption(c
 
   if (!mProvider) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoSafeJSContext cx;
   JS::Rooted<JS::Value> options(cx);
-  if (!aOptions.ToObject(cx, &options)) {
+  if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
   nsCOMPtr<nsIDOMDOMRequest> request;
   nsresult rv = mProvider->SetCallBarring(mClientId, GetOwner(), options,
                                           getter_AddRefs(request));
   if (NS_FAILED(rv)) {
@@ -693,17 +694,17 @@ MobileConnection::ChangeCallBarringPassw
 
   if (!mProvider) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   AutoSafeJSContext cx;
   JS::Rooted<JS::Value> options(cx);
-  if (!aOptions.ToObject(cx, &options)) {
+  if (!ToJSValue(cx, aOptions, &options)) {
     aRv.Throw(NS_ERROR_TYPE_ERR);
     return nullptr;
   }
 
   nsCOMPtr<nsIDOMDOMRequest> request;
   nsresult rv = mProvider->ChangeCallBarringPassword(mClientId,
                                                      GetOwner(),
                                                      options,
--- a/dom/nfc/MozNDEFRecord.h
+++ b/dom/nfc/MozNDEFRecord.h
@@ -55,56 +55,38 @@ public:
               const Optional<Uint8Array>& aId,
               const Optional<Uint8Array>& aPayload, ErrorResult& aRv);
 
   uint8_t Tnf() const
   {
     return mTnf;
   }
 
-  JSObject* GetType(JSContext* cx) const
+  void GetType(JSContext* cx, JS::MutableHandle<JSObject*> retval) const
   {
     if (mType) {
-      return GetTypeObject();
-    } else {
-      return nullptr;
+      JS::ExposeObjectToActiveJS(mType);
     }
-  }
-  JSObject* GetTypeObject() const
-  {
-    JS::ExposeObjectToActiveJS(mType);
-    return mType;
+    retval.set(mType);
   }
 
-  JSObject* GetId(JSContext* cx) const
+  void GetId(JSContext* cx, JS::MutableHandle<JSObject*> retval) const
   {
     if (mId) {
-      return GetIdObject();
-    } else {
-      return nullptr;
+      JS::ExposeObjectToActiveJS(mId);
     }
-  }
-  JSObject* GetIdObject() const
-  {
-    JS::ExposeObjectToActiveJS(mId);
-    return mId;
+    retval.set(mId);
   }
 
-  JSObject* GetPayload(JSContext* cx) const
+  void GetPayload(JSContext* cx, JS::MutableHandle<JSObject*> retval) const
   {
     if (mPayload) {
-      return GetPayloadObject();
-    } else {
-      return nullptr;
+      JS::ExposeObjectToActiveJS(mPayload);
     }
-  }
-  JSObject* GetPayloadObject() const
-  {
-    JS::ExposeObjectToActiveJS(mPayload);
-    return mPayload;
+    retval.set(mPayload);
   }
 
 private:
   MozNDEFRecord() MOZ_DELETE;
   nsRefPtr<nsPIDOMWindow> mWindow;
   void HoldData();
   void DropData();
 
--- a/dom/nfc/nsNfc.js
+++ b/dom/nfc/nsNfc.js
@@ -12,17 +12,16 @@ function debug(s) {
 }
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/ObjectWrapper.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this,
                                    "appsService",
                                    "@mozilla.org/AppsService;1",
                                    "nsIAppsService");
 const NFC_PEER_EVENT_READY = 0x01;
 const NFC_PEER_EVENT_LOST  = 0x02;
 
--- a/dom/promise/PromiseCallback.cpp
+++ b/dom/promise/PromiseCallback.cpp
@@ -214,18 +214,18 @@ WrapperPromiseCallback::Call(JSContext* 
     NS_WARNING("Failed to wrap value into the right compartment.");
     return;
   }
 
   ErrorResult rv;
 
   // If invoking callback threw an exception, run resolver's reject with the
   // thrown exception as argument and the synchronous flag set.
-  JS::Rooted<JS::Value> retValue(aCx,
-    mCallback->Call(value, rv, CallbackObject::eRethrowExceptions));
+  JS::Rooted<JS::Value> retValue(aCx);
+  mCallback->Call(value, &retValue, rv, CallbackObject::eRethrowExceptions);
 
   rv.WouldReportJSException();
 
   if (rv.Failed() && rv.IsJSException()) {
     JS::Rooted<JS::Value> value(aCx);
     rv.StealJSException(aCx, &value);
 
     if (!JS_WrapValue(aCx, &value)) {
--- a/dom/settings/SettingsDB.jsm
+++ b/dom/settings/SettingsDB.jsm
@@ -4,25 +4,36 @@
 
 "use strict";
 
 let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/ObjectWrapper.jsm");
 
 this.EXPORTED_SYMBOLS = ["SettingsDB", "SETTINGSDB_NAME", "SETTINGSSTORE_NAME"];
 
 const DEBUG = false;
 function debug(s) {
   if (DEBUG) dump("-*- SettingsDB: " + s + "\n");
 }
 
+const TYPED_ARRAY_THINGS = new Set([
+  "Int8Array",
+  "Uint8Array",
+  "Uint8ClampedArray",
+  "Int16Array",
+  "Uint16Array",
+  "Int32Array",
+  "Uint32Array",
+  "Float32Array",
+  "Float64Array",
+]);
+
 this.SETTINGSDB_NAME = "settings";
 this.SETTINGSDB_VERSION = 4;
 this.SETTINGSSTORE_NAME = "settings";
 
 Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 
@@ -171,19 +182,39 @@ SettingsDB.prototype = {
                         { type: mimeType });
       } catch(e) {
         dump(e);
       }
     }
     return aValue
   },
 
+  getObjectKind: function(aObject) {
+    if (aObject === null || aObject === undefined) {
+      return "primitive";
+    } else if (Array.isArray(aObject)) {
+      return "array";
+    } else if (aObject instanceof Ci.nsIDOMFile) {
+      return "file";
+    } else if (aObject instanceof Ci.nsIDOMBlob) {
+      return "blob";
+    } else if (aObject.constructor.name == "Date") {
+      return "date";
+    } else if (TYPED_ARRAY_THINGS.has(aObject.constructor.name)) {
+      return aObject.constructor.name;
+    } else if (typeof aObject == "object") {
+      return "object";
+    } else {
+      return "primitive";
+    }
+  },
+
   // Makes sure any property that is a data: uri gets converted to a Blob.
   prepareValue: function(aObject) {
-    let kind = ObjectWrapper.getObjectKind(aObject);
+    let kind = this.getObjectKind(aObject);
     if (kind == "array") {
       let res = [];
       aObject.forEach(function(aObj) {
         res.push(this.prepareValue(aObj));
       }, this);
       return res;
     } else if (kind == "file" || kind == "blob" || kind == "date") {
       return aObject;
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -12,17 +12,16 @@ function debug(s) {
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cu = Components.utils;
 
 Cu.import("resource://gre/modules/SettingsQueue.jsm");
 Cu.import("resource://gre/modules/SettingsDB.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/ObjectWrapper.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
                                    "@mozilla.org/childprocessmessagemanager;1",
                                    "nsIMessageSender");
 XPCOMUtils.defineLazyServiceGetter(this, "mrm",
                                    "@mozilla.org/memory-reporter-manager;1",
                                    "nsIMemoryReporterManager");
 
@@ -192,27 +191,33 @@ SettingsLock.prototype = {
       return req;
     } else {
       if (DEBUG) debug("get not allowed");
       throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
     }
   },
 
   _serializePreservingBinaries: function _serializePreservingBinaries(aObject) {
+    function needsUUID(aValue) {
+      if (!aValue || !aValue.constructor) {
+        return false;
+      }
+      return (aValue.constructor.name == "Date") || (aValue instanceof Ci.nsIDOMFile) ||
+             (aValue instanceof Ci.nsIDOMBlob);
+    }
     // We need to serialize settings objects, otherwise they can change between
     // the set() call and the enqueued request being processed. We can't simply
     // parse(stringify(obj)) because that breaks things like Blobs, Files and
     // Dates, so we use stringify's replacer and parse's reviver parameters to
     // preserve binaries.
     let manager = this._settingsManager;
     let binaries = Object.create(null);
     let stringified = JSON.stringify(aObject, function(key, value) {
       value = manager._settingsDB.prepareValue(value);
-      let kind = ObjectWrapper.getObjectKind(value);
-      if (kind == "file" || kind == "blob" || kind == "date") {
+      if (needsUUID(value)) {
         let uuid = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator)
                                                       .generateUUID().toString();
         binaries[uuid] = value;
         return uuid;
       }
       return value;
     });
     return JSON.parse(stringified, function(key, value) {
--- a/dom/src/notification/DesktopNotification.cpp
+++ b/dom/src/notification/DesktopNotification.cpp
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "mozilla/dom/DesktopNotification.h"
 #include "mozilla/dom/DesktopNotificationBinding.h"
 #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "nsContentPermissionHelper.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "nsIDOMDesktopNotification.h"
 #include "TabChild.h"
 #include "mozilla/Preferences.h"
 #include "nsGlobalWindow.h"
 #include "nsIAppsService.h"
@@ -96,17 +97,17 @@ DesktopNotification::PostDesktopNotifica
       nsString manifestUrl = EmptyString();
       appsService->GetManifestURLByLocalId(appId, manifestUrl);
       mozilla::AutoSafeJSContext cx;
       JS::Rooted<JS::Value> val(cx);
       AppNotificationServiceOptions ops;
       ops.mTextClickable = true;
       ops.mManifestURL = manifestUrl;
 
-      if (!ops.ToObject(cx, &val)) {
+      if (!ToJSValue(cx, ops, &val)) {
         return NS_ERROR_FAILURE;
       }
 
       return appNotifier->ShowAppNotification(mIconURL, mTitle, mDescription,
                                               mObserver, val);
     }
   }
 #endif
--- a/dom/src/notification/Notification.cpp
+++ b/dom/src/notification/Notification.cpp
@@ -601,17 +601,17 @@ Notification::ShowInternal()
         ops.mTextClickable = true;
         ops.mManifestURL = manifestUrl;
         ops.mId = mAlertName;
         ops.mDbId = mID;
         ops.mDir = DirectionToString(mDir);
         ops.mLang = mLang;
         ops.mTag = mTag;
 
-        if (!ops.ToObject(cx, &val)) {
+        if (!ToJSValue(cx, ops, &val)) {
           NS_WARNING("Converting dict to object failed!");
           return;
         }
 
         appNotifier->ShowAppNotification(mIconUrl, mTitle, mBody,
                                          observer, val);
         return;
       }
--- a/dom/system/gonk/NetworkWorker.cpp
+++ b/dom/system/gonk/NetworkWorker.cpp
@@ -2,16 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "NetworkWorker.h"
 #include "NetworkUtils.h"
 #include <nsThreadUtils.h>
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "nsXULAppAPI.h"
 #include "nsCxPusher.h"
 
 #define NS_NETWORKWORKER_CID \
   { 0x6df093e1, 0x8127, 0x4fa7, {0x90, 0x13, 0xa3, 0xaa, 0xa7, 0x79, 0xbb, 0xdd} }
 
 using namespace mozilla;
 using namespace mozilla::dom;
@@ -235,17 +236,17 @@ NetworkWorker::PostMessage(JS::Handle<JS
 void
 NetworkWorker::DispatchNetworkResult(const NetworkResultOptions& aOptions)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mozilla::AutoSafeJSContext cx;
   JS::RootedValue val(cx);
 
-  if (!aOptions.ToObject(cx, &val)) {
+  if (!ToJSValue(cx, aOptions, &val)) {
     return;
   }
 
   // Call the listener with a JS value.
   if (mListener) {
     mListener->OnEvent(val);
   }
 }
--- a/dom/tests/mochitest/geolocation/mochitest.ini
+++ b/dom/tests/mochitest/geolocation/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = os == 'mac'
 support-files =
   geolocation.html
   geolocation_common.js
   network_geolocation.sjs
   windowTest.html
 
 [test_allowCurrent.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(Bug 910235 - Error: no message manager set when calling method: [nsIObserver::observe]) b2g-debug(Bug 910235 - Error: no message manager set when calling method: [nsIObserver::observe]) b2g-desktop(Bug 910235 - Error: no message manager set when calling method: [nsIObserver::observe])
--- a/dom/webidl/SubtleCrypto.webidl
+++ b/dom/webidl/SubtleCrypto.webidl
@@ -27,16 +27,17 @@ interface AesKeyAlgorithm : KeyAlgorithm
 interface HmacKeyAlgorithm : KeyAlgorithm {
   readonly attribute KeyAlgorithm hash;
   readonly attribute unsigned long length;
 };
 
 [NoInterfaceObject]
 interface RsaKeyAlgorithm : KeyAlgorithm {
   readonly attribute unsigned long modulusLength;
+  [Throws]
   readonly attribute BigInteger publicExponent;
 };
 
 [NoInterfaceObject]
 interface RsaHashedKeyAlgorithm : RsaKeyAlgorithm {
   readonly attribute KeyAlgorithm hash;
 };
 
--- a/dom/wifi/WifiCertService.cpp
+++ b/dom/wifi/WifiCertService.cpp
@@ -3,16 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WifiCertService.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "cert.h"
 #include "certdb.h"
 #include "CryptoTask.h"
 #include "nsCxPusher.h"
 #include "nsIDOMFile.h"
 #include "nsIWifiService.h"
 #include "nsNetUtil.h"
 #include "nsServiceManagerUtils.h"
@@ -250,17 +251,17 @@ void
 WifiCertService::DispatchResult(const WifiCertServiceResultOptions& aOptions)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mozilla::AutoSafeJSContext cx;
   JS::RootedValue val(cx);
   nsCString dummyInterface;
 
-  if (!aOptions.ToObject(cx, &val)) {
+  if (!ToJSValue(cx, aOptions, &val)) {
     return;
   }
 
   // Certll the listener with a JS value.
   mListener->OnCommand(val, dummyInterface);
 }
 
 WifiCertService::WifiCertService()
--- a/dom/wifi/WifiProxyService.cpp
+++ b/dom/wifi/WifiProxyService.cpp
@@ -1,16 +1,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WifiProxyService.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/ModuleUtils.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/ToJSValue.h"
 #include "nsXULAppAPI.h"
 #include "WifiUtils.h"
 #include "nsCxPusher.h"
 
 #ifdef MOZ_TASK_TRACER
 #include "GeckoTaskTracer.h"
 using namespace mozilla::tasktracer;
 #endif
@@ -286,17 +287,17 @@ WifiProxyService::WaitForEvent(const nsA
 void
 WifiProxyService::DispatchWifiResult(const WifiResultOptions& aOptions, const nsACString& aInterface)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   mozilla::AutoSafeJSContext cx;
   JS::Rooted<JS::Value> val(cx);
 
-  if (!aOptions.ToObject(cx, &val)) {
+  if (!ToJSValue(cx, aOptions, &val)) {
     return;
   }
 
   // Call the listener with a JS value.
   mListener->OnCommand(val, aInterface);
 }
 
 void
--- a/dom/workers/FileReaderSync.cpp
+++ b/dom/workers/FileReaderSync.cpp
@@ -42,66 +42,67 @@ FileReaderSync::Constructor(const Global
 }
 
 JSObject*
 FileReaderSync::WrapObject(JSContext* aCx)
 {
   return FileReaderSyncBinding_workers::Wrap(aCx, this);
 }
 
-JSObject*
+void
 FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
                                   JS::Handle<JSObject*> aScopeObj,
                                   JS::Handle<JSObject*> aBlob,
+                                  JS::MutableHandle<JSObject*> aRetval,
                                   ErrorResult& aRv)
 {
   nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
   if (!blob) {
     aRv.Throw(NS_ERROR_INVALID_ARG);
-    return nullptr;
+    return;
   }
 
   uint64_t blobSize;
   nsresult rv = blob->GetSize(&blobSize);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
-    return nullptr;
+    return;
   }
 
   JS::Rooted<JSObject*> jsArrayBuffer(aCx, JS_NewArrayBuffer(aCx, blobSize));
   if (!jsArrayBuffer) {
     // XXXkhuey we need a way to indicate to the bindings that the call failed
     // but there's already a pending exception that we should not clobber.
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return nullptr;
+    return;
   }
 
   uint32_t bufferLength = JS_GetArrayBufferByteLength(jsArrayBuffer);
   uint8_t* arrayBuffer = JS_GetStableArrayBufferData(aCx, jsArrayBuffer);
   if (!arrayBuffer) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-    return nullptr;
+    return;
   }
 
   nsCOMPtr<nsIInputStream> stream;
   rv = blob->GetInternalStream(getter_AddRefs(stream));
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
-    return nullptr;
+    return;
   }
 
   uint32_t numRead;
   rv = stream->Read((char*)arrayBuffer, bufferLength, &numRead);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
-    return nullptr;
+    return;
   }
   NS_ASSERTION(numRead == bufferLength, "failed to read data");
 
-  return jsArrayBuffer;
+  aRetval.set(jsArrayBuffer);
 }
 
 void
 FileReaderSync::ReadAsBinaryString(JS::Handle<JSObject*> aBlob,
                                    nsAString& aResult,
                                    ErrorResult& aRv)
 {
   nsIDOMBlob* blob = file::GetDOMBlobFromJSObject(aBlob);
--- a/dom/workers/FileReaderSync.h
+++ b/dom/workers/FileReaderSync.h
@@ -37,19 +37,20 @@ private:
                          nsAString &aResult);
 
 public:
   static already_AddRefed<FileReaderSync>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
 
   JSObject* WrapObject(JSContext* aCx);
 
-  JSObject* ReadAsArrayBuffer(JSContext* aCx, JS::Handle<JSObject*> aScopeObj,
-                              JS::Handle<JSObject*> aBlob,
-                              ErrorResult& aRv);
+  void ReadAsArrayBuffer(JSContext* aCx, JS::Handle<JSObject*> aScopeObj,
+                         JS::Handle<JSObject*> aBlob,
+                         JS::MutableHandle<JSObject*> aRetval,
+                         ErrorResult& aRv);
   void ReadAsBinaryString(JS::Handle<JSObject*> aBlob, nsAString& aResult,
                           ErrorResult& aRv);
   void ReadAsText(JS::Handle<JSObject*> aBlob,
                   const Optional<nsAString>& aEncoding,
                   nsAString& aResult, ErrorResult& aRv);
   void ReadAsDataURL(JS::Handle<JSObject*> aBlob, nsAString& aResult,
                      ErrorResult& aRv);
 };
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -2312,38 +2312,41 @@ XMLHttpRequest::SetResponseType(XMLHttpR
   }
 
   nsString acceptedResponseTypeString;
   runnable->GetResponseType(acceptedResponseTypeString);
 
   mResponseType = ConvertStringToResponseType(acceptedResponseTypeString);
 }
 
-jsval
-XMLHttpRequest::GetResponse(JSContext* /* unused */, ErrorResult& aRv)
+void
+XMLHttpRequest::GetResponse(JSContext* /* unused */,
+                            JS::MutableHandle<JS::Value> aResponse,
+                            ErrorResult& aRv)
 {
   if (NS_SUCCEEDED(mStateData.mResponseTextResult) &&
       mStateData.mResponse.isUndefined()) {
     MOZ_ASSERT(mStateData.mResponseText.Length());
     MOZ_ASSERT(NS_SUCCEEDED(mStateData.mResponseResult));
 
     JSString* str =
       JS_NewUCStringCopyN(mWorkerPrivate->GetJSContext(),
                           mStateData.mResponseText.get(),
                           mStateData.mResponseText.Length());
     if (!str) {
       aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
-      return JSVAL_VOID;
+      return;
     }
 
     mStateData.mResponse = STRING_TO_JSVAL(str);
   }
 
+  JS::ExposeValueToActiveJS(mStateData.mResponse);
   aRv = mStateData.mResponseResult;
-  return mStateData.mResponse;
+  aResponse.set(mStateData.mResponse);
 }
 
 void
 XMLHttpRequest::GetResponseText(nsAString& aResponseText, ErrorResult& aRv)
 {
   aRv = mStateData.mResponseTextResult;
   aResponseText = mStateData.mResponseText;
 }
--- a/dom/workers/XMLHttpRequest.h
+++ b/dom/workers/XMLHttpRequest.h
@@ -208,39 +208,40 @@ public:
   ResponseType() const
   {
     return mResponseType;
   }
 
   void
   SetResponseType(XMLHttpRequestResponseType aResponseType, ErrorResult& aRv);
 
-  jsval
-  GetResponse(JSContext* /* unused */, ErrorResult& aRv);
+  void
+  GetResponse(JSContext* /* unused */, JS::MutableHandle<JS::Value> aResponse,
+              ErrorResult& aRv);
 
   void
   GetResponseText(nsAString& aResponseText, ErrorResult& aRv);
 
   JSObject*
   GetResponseXML() const
   {
     return nullptr;
   }
 
   JSObject*
   GetChannel() const
   {
     return nullptr;
   }
 
-  JS::Value
-  GetInterface(JSContext* cx, JS::Handle<JSObject*> aIID, ErrorResult& aRv)
+  void
+  GetInterface(JSContext* cx, JS::Handle<JSObject*> aIID,
+               JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv)
   {
     aRv.Throw(NS_ERROR_FAILURE);
-    return JSVAL_NULL;
   }
 
   XMLHttpRequestUpload*
   GetUploadObjectNoCreate() const
   {
     return mUpload;
   }
 
--- a/gfx/2d/2D.h
+++ b/gfx/2d/2D.h
@@ -993,16 +993,28 @@ protected:
 
 class DrawEventRecorder : public RefCounted<DrawEventRecorder>
 {
 public:
   MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(DrawEventRecorder)
   virtual ~DrawEventRecorder() { }
 };
 
+struct Tile
+{
+  RefPtr<DrawTarget> mDrawTarget;
+  IntPoint mTileOrigin;
+};
+
+struct TileSet
+{
+  Tile* mTiles;
+  size_t mTileCount;
+};
+
 class GFX2D_API Factory
 {
 public:
   static bool HasSSE2();
 
   /** Make sure that the given dimensions don't overflow a 32-bit signed int
    * using 4 bytes per pixel; optionally, make sure that either dimension
    * doesn't exceed the given limit.
@@ -1086,16 +1098,24 @@ public:
 
 #if defined(USE_SKIA) && defined(MOZ_ENABLE_FREETYPE)
   static TemporaryRef<GlyphRenderingOptions>
     CreateCairoGlyphRenderingOptions(FontHinting aHinting, bool aAutoHinting);
 #endif
   static TemporaryRef<DrawTarget>
     CreateDualDrawTarget(DrawTarget *targetA, DrawTarget *targetB);
 
+  /*
+   * This creates a new tiled DrawTarget. When a tiled drawtarget is used the
+   * drawing is distributed over number of tiles which may each hold an
+   * individual offset. The tiles in the set must each have the same backend
+   * and format.
+   */
+  static TemporaryRef<DrawTarget> CreateTiledDrawTarget(const TileSet& aTileSet);
+
 #ifdef XP_MACOSX
   static TemporaryRef<DrawTarget> CreateDrawTargetForCairoCGContext(CGContextRef cg, const IntSize& aSize);
 #endif
 
 #ifdef WIN32
   static TemporaryRef<DrawTarget> CreateDrawTargetForD3D10Texture(ID3D10Texture2D *aTexture, SurfaceFormat aFormat);
   static TemporaryRef<DrawTarget>
     CreateDualDrawTargetForD3D10Textures(ID3D10Texture2D *aTextureA,
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -649,40 +649,63 @@ DrawTargetSkia::CreateSourceSurfaceFromN
 
 void
 DrawTargetSkia::CopySurface(SourceSurface *aSurface,
                             const IntRect& aSourceRect,
                             const IntPoint &aDestination)
 {
   //TODO: We could just use writePixels() here if the sourceRect is the entire source
 
-  if (aSurface->GetType() != SurfaceType::SKIA) {
+  if (aSurface->GetType() != SurfaceType::SKIA && aSurface->GetType() != SurfaceType::DATA) {
     return;
   }
 
   MarkChanged();
 
   TempBitmap bitmap = GetBitmapForSurface(aSurface);
 
+  // This is a fast path that is disabled for now to mimimize risk
+  if (false && !bitmap.mBitmap.getTexture() && mCanvas->getDevice()->config() == bitmap.mBitmap.config()) {
+	SkBitmap bm(bitmap.mBitmap);
+	bm.lockPixels();
+	if (bm.getPixels()) {
+	  SkImageInfo info = bm.info();
+	  info.fWidth = aSourceRect.width;
+	  info.fHeight = aSourceRect.height;
+	  uint8_t* pixels = static_cast<uint8_t*>(bm.getPixels());
+	  // adjust pixels for the source offset
+	  pixels += aSourceRect.x + aSourceRect.y*bm.rowBytes();
+	  mCanvas->writePixels(info, pixels, bm.rowBytes(), aDestination.x, aDestination.y);
+	  return;
+	}
+  }
+
   mCanvas->save();
   mCanvas->resetMatrix();
   SkRect dest = IntRectToSkRect(IntRect(aDestination.x, aDestination.y, aSourceRect.width, aSourceRect.height)); 
   SkIRect source = IntRectToSkIRect(aSourceRect);
   mCanvas->clipRect(dest, SkRegion::kReplace_Op);
   SkPaint paint;
 
   if (mCanvas->getDevice()->config() == SkBitmap::kRGB_565_Config) {
     // Set the xfermode to SOURCE_OVER to workaround
     // http://code.google.com/p/skia/issues/detail?id=628
     // RGB565 is opaque so they're equivalent anyway
     paint.setXfermodeMode(SkXfermode::kSrcOver_Mode);
   } else {
     paint.setXfermodeMode(SkXfermode::kSrc_Mode);
   }
-
+  // drawBitmapRect with A8 bitmaps ends up doing a mask operation
+  // so we need to clear before
+  if (bitmap.mBitmap.config() == SkBitmap::kA8_Config) {
+    SkPaint clearPaint;
+    clearPaint.setColor(SkColorSetARGB(0, 0, 0, 0));
+    clearPaint.setXfermodeMode(SkXfermode::kSrc_Mode);
+    mCanvas->drawPaint(clearPaint);
+  }
   mCanvas->drawBitmapRect(bitmap.mBitmap, &source, dest, &paint);
   mCanvas->restore();
 }
 
 bool
 DrawTargetSkia::Init(const IntSize &aSize, SurfaceFormat aFormat)
 {
   SkAutoTUnref<SkBaseDevice> device(new SkBitmapDevice(GfxFormatToSkiaConfig(aFormat),
new file mode 100644
--- /dev/null
+++ b/gfx/2d/DrawTargetTiled.cpp
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DrawTargetTiled.h"
+
+using namespace std;
+
+namespace mozilla {
+namespace gfx {
+
+DrawTargetTiled::DrawTargetTiled()
+{
+}
+
+bool
+DrawTargetTiled::Init(const TileSet& aTiles)
+{
+  if (!aTiles.mTileCount) {
+    return false;
+  }
+
+  mTiles.resize(aTiles.mTileCount);
+  memcpy(&mTiles.front(), aTiles.mTiles, aTiles.mTileCount * sizeof(Tile));
+
+  for (size_t i = 0; i < mTiles.size(); i++) {
+    if (mTiles[0].mDrawTarget->GetFormat() != mTiles[i].mDrawTarget->GetFormat() ||
+        mTiles[0].mDrawTarget->GetType() != mTiles[i].mDrawTarget->GetType()) {
+      return false;
+    }
+    uint32_t newXMost = max(mRect.XMost(),
+                            mTiles[i].mTileOrigin.x + mTiles[i].mDrawTarget->GetSize().width);
+    uint32_t newYMost = max(mRect.YMost(),
+                            mTiles[i].mTileOrigin.y + mTiles[i].mDrawTarget->GetSize().height);
+    mRect.x = min(mRect.x, mTiles[i].mTileOrigin.x);
+    mRect.y = min(mRect.y, mTiles[i].mTileOrigin.y);
+    mRect.width = newXMost - mRect.x;
+    mRect.height = newYMost - mRect.y;
+  }
+  return true;
+}
+
+class SnapshotTiled : public SourceSurface
+{
+public:
+  SnapshotTiled(const vector<Tile>& aTiles, const IntRect& aRect)
+    : mRect(aRect)
+  {
+    for (size_t i = 0; i < aTiles.size(); i++) {
+      mSnapshots.push_back(aTiles[i].mDrawTarget->Snapshot());
+      mOrigins.push_back(aTiles[i].mTileOrigin);
+    }
+  }
+
+  virtual SurfaceType GetType() const { return SurfaceType::TILED; }
+  virtual IntSize GetSize() const { return IntSize(mRect.XMost(), mRect.YMost()); }
+  virtual SurfaceFormat GetFormat() const { return mSnapshots[0]->GetFormat(); }
+
+  virtual TemporaryRef<DataSourceSurface> GetDataSurface()
+  {
+    RefPtr<DataSourceSurface> surf = Factory::CreateDataSourceSurface(GetSize(), GetFormat());
+
+    DataSourceSurface::MappedSurface mappedSurf;
+    surf->Map(DataSourceSurface::MapType::WRITE, &mappedSurf);
+
+    {
+      RefPtr<DrawTarget> dt =
+        Factory::CreateDrawTargetForData(BackendType::CAIRO, mappedSurf.mData,
+        GetSize(), mappedSurf.mStride, GetFormat());
+
+      for (size_t i = 0; i < mSnapshots.size(); i++) {
+        RefPtr<DataSourceSurface> dataSurf = mSnapshots[i]->GetDataSurface();
+        dt->CopySurface(dataSurf, IntRect(IntPoint(0, 0), mSnapshots[i]->GetSize()), mOrigins[i]);
+      }
+    }
+    surf->Unmap();
+
+    return surf;
+  }
+private:
+  vector<RefPtr<SourceSurface>> mSnapshots;
+  vector<IntPoint> mOrigins;
+  IntRect mRect;
+};
+
+TemporaryRef<SourceSurface>
+DrawTargetTiled::Snapshot()
+{
+  return new SnapshotTiled(mTiles, mRect);
+}
+
+#define TILED_COMMAND(command) \
+  void \
+  DrawTargetTiled::command() \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(); \
+    } \
+  }
+#define TILED_COMMAND1(command, type1) \
+  void \
+  DrawTargetTiled::command(type1 arg1) \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(arg1); \
+    } \
+  }
+#define TILED_COMMAND3(command, type1, type2, type3) \
+  void \
+  DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3) \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(arg1, arg2, arg3); \
+    } \
+  }
+#define TILED_COMMAND4(command, type1, type2, type3, type4) \
+  void \
+  DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4); \
+    } \
+  }
+#define TILED_COMMAND5(command, type1, type2, type3, type4, type5) \
+  void \
+  DrawTargetTiled::command(type1 arg1, type2 arg2, type3 arg3, type4 arg4, type5 arg5) \
+  { \
+    for (size_t i = 0; i < mTiles.size(); i++) { \
+    \
+    \
+    mTiles[i].mDrawTarget->command(arg1, arg2, arg3, arg4, arg5); \
+    } \
+  }
+
+TILED_COMMAND(Flush)
+TILED_COMMAND5(DrawSurface, SourceSurface*, const Rect&,
+                            const Rect&, const DrawSurfaceOptions&,
+                            const DrawOptions&)
+TILED_COMMAND4(DrawFilter, FilterNode*, const Rect&, const Point&, const DrawOptions&)
+TILED_COMMAND1(ClearRect, const Rect&)
+TILED_COMMAND4(MaskSurface, const Pattern&, SourceSurface*, Point, const DrawOptions&)
+TILED_COMMAND3(FillRect, const Rect&, const Pattern&, const DrawOptions&)
+TILED_COMMAND4(StrokeRect, const Rect&, const Pattern&, const StrokeOptions&, const DrawOptions&)
+TILED_COMMAND5(StrokeLine, const Point&, const Point&, const Pattern&, const StrokeOptions&, const DrawOptions&)
+TILED_COMMAND4(Stroke, const Path*, const Pattern&, const StrokeOptions&, const DrawOptions&)
+TILED_COMMAND3(Fill, const Path*, const Pattern&, const DrawOptions&)
+TILED_COMMAND5(FillGlyphs, ScaledFont*, const GlyphBuffer&, const Pattern&, const DrawOptions&, const GlyphRenderingOptions*)
+TILED_COMMAND3(Mask, const Pattern&, const Pattern&, const DrawOptions&)
+TILED_COMMAND1(PushClip, const Path*)
+TILED_COMMAND1(PushClipRect, const Rect&)
+TILED_COMMAND(PopClip)
+
+void
+DrawTargetTiled::CopySurface(SourceSurface *aSurface,
+                             const IntRect &aSourceRect,
+                             const IntPoint &aDestination)
+{
+  // CopySurface ignores the transform, account for that here.
+  for (size_t i = 0; i < mTiles.size(); i++) {
+    IntRect src = aSourceRect;
+    src.x += mTiles[i].mTileOrigin.x;
+    src.width -= mTiles[i].mTileOrigin.x;
+    src.y = mTiles[i].mTileOrigin.y;
+    src.height -= mTiles[i].mTileOrigin.y;
+
+    if (src.width <= 0 || src.height <= 0) {
+      continue;
+    }
+
+    mTiles[i].mDrawTarget->CopySurface(aSurface, src, aDestination);
+  }
+}
+
+void
+DrawTargetTiled::SetTransform(const Matrix& aTransform)
+{
+  for (size_t i = 0; i < mTiles.size(); i++) {
+    Matrix mat = aTransform;
+    mat.PostTranslate(Float(-mTiles[i].mTileOrigin.x), Float(-mTiles[i].mTileOrigin.y));
+    mTiles[i].mDrawTarget->SetTransform(mat);
+  }
+  DrawTarget::SetTransform(aTransform);
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/2d/DrawTargetTiled.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 20; 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 MOZILLA_GFX_DRAWTARGETTILED_H_
+#define MOZILLA_GFX_DRAWTARGETTILED_H_
+
+#include "2D.h"
+#include "Filters.h"
+
+#include <vector>
+
+namespace mozilla {
+namespace gfx {
+
+class DrawTargetTiled : public DrawTarget
+{
+public:
+  DrawTargetTiled();
+
+  bool Init(const TileSet& mTiles);
+
+  virtual BackendType GetType() const { return mTiles[0].mDrawTarget->GetType(); }
+  virtual TemporaryRef<SourceSurface> Snapshot();
+  virtual IntSize GetSize() { return IntSize(mRect.XMost(), mRect.YMost()); }
+
+  virtual void Flush();
+  virtual void DrawSurface(SourceSurface *aSurface,
+                           const Rect &aDest,
+                           const Rect &aSource,
+                           const DrawSurfaceOptions &aSurfOptions,
+                           const DrawOptions &aOptions);
+  virtual void DrawFilter(FilterNode *aNode,
+                          const Rect &aSourceRect,
+                          const Point &aDestPoint,
+                          const DrawOptions &aOptions = DrawOptions());
+  virtual void DrawSurfaceWithShadow(SourceSurface *aSurface,
+                                     const Point &aDest,
+                                     const Color &aColor,
+                                     const Point &aOffset,
+                                     Float aSigma,
+                                     CompositionOp aOperator) { /* Not implemented */ MOZ_CRASH(); }
+
+  virtual void ClearRect(const Rect &aRect);
+  virtual void MaskSurface(const Pattern &aSource,
+                           SourceSurface *aMask,
+                           Point aOffset,
+                           const DrawOptions &aOptions = DrawOptions());
+
+  virtual void CopySurface(SourceSurface *aSurface,
+                           const IntRect &aSourceRect,
+                           const IntPoint &aDestination);
+
+  virtual void FillRect(const Rect &aRect,
+                        const Pattern &aPattern,
+                        const DrawOptions &aOptions = DrawOptions());
+  virtual void StrokeRect(const Rect &aRect,
+                          const Pattern &aPattern,
+                          const StrokeOptions &aStrokeOptions = StrokeOptions(),
+                          const DrawOptions &aOptions = DrawOptions());
+  virtual void StrokeLine(const Point &aStart,
+                          const Point &aEnd,
+                          const Pattern &aPattern,
+                          const StrokeOptions &aStrokeOptions = StrokeOptions(),
+                          const DrawOptions &aOptions = DrawOptions());
+  virtual void Stroke(const Path *aPath,
+                      const Pattern &aPattern,
+                      const StrokeOptions &aStrokeOptions = StrokeOptions(),
+                      const DrawOptions &aOptions = DrawOptions());
+  virtual void Fill(const Path *aPath,
+                    const Pattern &aPattern,
+                    const DrawOptions &aOptions = DrawOptions());
+  virtual void FillGlyphs(ScaledFont *aFont,
+                          const GlyphBuffer &aBuffer,
+                          const Pattern &aPattern,
+                          const DrawOptions &aOptions = DrawOptions(),
+                          const GlyphRenderingOptions *aRenderingOptions = nullptr);
+  virtual void Mask(const Pattern &aSource,
+                    const Pattern &aMask,
+                    const DrawOptions &aOptions = DrawOptions());
+  virtual void PushClip(const Path *aPath);
+  virtual void PushClipRect(const Rect &aRect);
+  virtual void PopClip();
+
+  virtual void SetTransform(const Matrix &aTransform);
+
+  virtual TemporaryRef<SourceSurface> CreateSourceSurfaceFromData(unsigned char *aData,
+                                                                  const IntSize &aSize,
+                                                                  int32_t aStride,
+                                                                  SurfaceFormat aFormat) const
+  {
+    return mTiles[0].mDrawTarget->CreateSourceSurfaceFromData(aData, aSize, aStride, aFormat);
+  }
+  virtual TemporaryRef<SourceSurface> OptimizeSourceSurface(SourceSurface *aSurface) const
+  {
+    return mTiles[0].mDrawTarget->OptimizeSourceSurface(aSurface);
+  }
+
+  virtual TemporaryRef<SourceSurface>
+    CreateSourceSurfaceFromNativeSurface(const NativeSurface &aSurface) const
+  {
+    return mTiles[0].mDrawTarget->CreateSourceSurfaceFromNativeSurface(aSurface);
+  }
+
+  virtual TemporaryRef<DrawTarget>
+    CreateSimilarDrawTarget(const IntSize &aSize, SurfaceFormat aFormat) const
+  {
+    return mTiles[0].mDrawTarget->CreateSimilarDrawTarget(aSize, aFormat);
+  }
+
+  virtual TemporaryRef<PathBuilder> CreatePathBuilder(FillRule aFillRule = FillRule::FILL_WINDING) const
+  {
+    return mTiles[0].mDrawTarget->CreatePathBuilder(aFillRule);
+  }
+
+  virtual TemporaryRef<GradientStops>
+    CreateGradientStops(GradientStop *aStops,
+                        uint32_t aNumStops,
+                        ExtendMode aExtendMode = ExtendMode::CLAMP) const
+  {
+    return mTiles[0].mDrawTarget->CreateGradientStops(aStops, aNumStops, aExtendMode);
+  }
+  virtual TemporaryRef<FilterNode> CreateFilter(FilterType aType)
+  {
+    return mTiles[0].mDrawTarget->CreateFilter(aType);
+  }
+
+private:
+  std::vector<Tile> mTiles;
+  IntRect mRect;
+};
+
+}
+}
+
+#endif
--- a/gfx/2d/Factory.cpp
+++ b/gfx/2d/Factory.cpp
@@ -38,16 +38,17 @@
 #include "DrawTargetD2D1.h"
 #endif
 #include "ScaledFontDWrite.h"
 #include <d3d10_1.h>
 #include "HelpersD2D.h"
 #endif
 
 #include "DrawTargetDual.h"
+#include "DrawTargetTiled.h"
 #include "DrawTargetRecording.h"
 
 #include "SourceSurfaceRawData.h"
 
 #include "DrawEventRecorder.h"
 
 #include "Logging.h"
 
@@ -373,16 +374,28 @@ Factory::CreateDrawTargetForData(Backend
 
   if (!retVal) {
     gfxDebug() << "Failed to create DrawTarget, Type: " << int(aBackend) << " Size: " << aSize;
   }
 
   return retVal;
 }
 
+TemporaryRef<DrawTarget>
+Factory::CreateTiledDrawTarget(const TileSet& aTileSet)
+{
+  RefPtr<DrawTargetTiled> dt = new DrawTargetTiled();
+
+  if (!dt->Init(aTileSet)) {
+    return nullptr;
+  }
+
+  return dt;
+}
+
 TemporaryRef<ScaledFont>
 Factory::CreateScaledFontForNativeFont(const NativeFont &aNativeFont, Float aSize)
 {
   switch (aNativeFont.mType) {
 #ifdef WIN32
   case NativeFontType::DWRITE_FONT_FACE:
     {
       return new ScaledFontDWrite(static_cast<IDWriteFontFace*>(aNativeFont.mFont), aSize);
--- a/gfx/2d/QuartzSupport.h
+++ b/gfx/2d/QuartzSupport.h
@@ -48,17 +48,17 @@ public:
                   double aContentsScaleFactor,
                   CGImageRef *aOutCAImage);
   bool isInit() { return mCARenderer != nullptr; }
   /*
    * Render the CALayer to an IOSurface. If no IOSurface
    * is attached then an internal pixel buffer will be
    * used.
    */
-  void AttachIOSurface(mozilla::RefPtr<MacIOSurface> aSurface);
+  void AttachIOSurface(MacIOSurface *aSurface);
   IOSurfaceID GetIOSurfaceID();
   // aX, aY, aWidth and aHeight are in "display pixels".  Multiply by
   // surf->GetContentsScaleFactor() to get device pixels.
   static nsresult DrawSurfaceToCGContext(CGContextRef aContext,
                                          MacIOSurface *surf,
                                          CGColorSpaceRef aColorSpace,
                                          int aX, int aY,
                                          size_t aWidth, size_t aHeight);
--- a/gfx/2d/QuartzSupport.mm
+++ b/gfx/2d/QuartzSupport.mm
@@ -332,22 +332,19 @@ void nsCARenderer::SetViewport(int aWidt
   ::glLoadIdentity();
   ::glOrtho (0.0, aWidth, 0.0, aHeight, -1, 1);
 
   // Render upside down to speed up CGContextDrawImage
   ::glTranslatef(0.0f, aHeight, 0.0);
   ::glScalef(1.0, -1.0, 1.0);
 }
 
-void nsCARenderer::AttachIOSurface(RefPtr<MacIOSurface> aSurface) {
+void nsCARenderer::AttachIOSurface(MacIOSurface *aSurface) {
   if (mIOSurface &&
       aSurface->GetIOSurfaceID() == mIOSurface->GetIOSurfaceID()) {
-    // This object isn't needed since we already have a
-    // handle to the same io surface.
-    aSurface = nullptr;
     return;
   }
 
   mIOSurface = aSurface;
 
   // Update the framebuffer and viewport
   if (mCARenderer) {
     CARenderer* caRenderer = (CARenderer*)mCARenderer;
--- a/gfx/2d/Types.h
+++ b/gfx/2d/Types.h
@@ -23,17 +23,18 @@ MOZ_BEGIN_ENUM_CLASS(SurfaceType, int8_t
   D2D1_DRAWTARGET, /* Surface made from a D2D draw target */
   CAIRO, /* Surface wrapping a cairo surface */
   CAIRO_IMAGE, /* Data surface wrapping a cairo image surface */
   COREGRAPHICS_IMAGE, /* Surface wrapping a CoreGraphics Image */
   COREGRAPHICS_CGCONTEXT, /* Surface wrapping a CG context */
   SKIA, /* Surface wrapping a Skia bitmap */
   DUAL_DT, /* Snapshot of a dual drawtarget */
   D2D1_1_IMAGE, /* A D2D 1.1 ID2D1Image SourceSurface */
-  RECORDING /* Surface used for recording */
+  RECORDING, /* Surface used for recording */
+  TILED /* Surface from a tiled DrawTarget */
 MOZ_END_ENUM_CLASS(SurfaceType)
 
 MOZ_BEGIN_ENUM_CLASS(SurfaceFormat, int8_t)
   B8G8R8A8,
   B8G8R8X8,
   R8G8B8A8,
   R8G8B8X8,
   R5G6B5,
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -97,16 +97,17 @@ if CONFIG['INTEL_ARCHITECTURE']:
 UNIFIED_SOURCES += [
     'Blur.cpp',
     'DataSourceSurface.cpp',
     'DataSurfaceHelpers.cpp',
     'DrawEventRecorder.cpp',
     'DrawTargetCairo.cpp',
     'DrawTargetDual.cpp',
     'DrawTargetRecording.cpp',
+    'DrawTargetTiled.cpp',
     'Factory.cpp',
     'FilterNodeSoftware.cpp',
     'FilterProcessing.cpp',
     'FilterProcessingScalar.cpp',
     'ImageScaling.cpp',
     'Matrix.cpp',
     'Path.cpp',
     'PathCairo.cpp',
--- a/gfx/layers/ImageContainer.cpp
+++ b/gfx/layers/ImageContainer.cpp
@@ -636,22 +636,24 @@ CairoImage::GetTextureClient(Compositabl
     return nullptr;
   }
   MOZ_ASSERT(textureClient->CanExposeDrawTarget());
   if (!textureClient->AllocateForSurface(surface->GetSize()) ||
       !textureClient->Lock(OpenMode::OPEN_WRITE_ONLY)) {
     return nullptr;
   }
 
+  TextureClientAutoUnlock autoUnolck(textureClient);
   {
     // We must not keep a reference to the DrawTarget after it has been unlocked.
-    RefPtr<DrawTarget> dt = textureClient->GetAsDrawTarget();
+    DrawTarget* dt = textureClient->BorrowDrawTarget();
+    if (!dt) {
+      return nullptr;
+    }
     dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
   }
 
-  textureClient->Unlock();
-
   mTextureClients.Put(forwarder->GetSerial(), textureClient);
   return textureClient;
 }
 
 } // namespace
 } // namespace
--- a/gfx/layers/RotatedBuffer.cpp
+++ b/gfx/layers/RotatedBuffer.cpp
@@ -327,32 +327,32 @@ RotatedContentBuffer::BufferSizeOkFor(co
 }
 
 bool
 RotatedContentBuffer::EnsureBuffer()
 {
   NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
   if (!mDTBuffer) {
     if (mBufferProvider) {
-      mDTBuffer = mBufferProvider->GetAsDrawTarget();
+      mDTBuffer = mBufferProvider->BorrowDrawTarget();
     }
   }
 
   NS_WARN_IF_FALSE(mDTBuffer, "no buffer");
   return !!mDTBuffer;
 }
 
 bool
 RotatedContentBuffer::EnsureBufferOnWhite()
 {
   NS_ASSERTION(!mLoanedDrawTarget, "Loaned draw target must be returned");
   if (!mDTBufferOnWhite) {
     if (mBufferProviderOnWhite) {
       mDTBufferOnWhite =
-        mBufferProviderOnWhite->GetAsDrawTarget();
+        mBufferProviderOnWhite->BorrowDrawTarget();
     }
   }
 
   NS_WARN_IF_FALSE(mDTBufferOnWhite, "no buffer");
   return mDTBufferOnWhite;
 }
 
 bool
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -2256,24 +2256,27 @@ void AsyncPanZoomController::NotifyLayer
     mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect;
     mFrameMetrics.mCompositionBounds = aLayerMetrics.mCompositionBounds;
     mFrameMetrics.SetRootCompositionSize(aLayerMetrics.GetRootCompositionSize());
     mFrameMetrics.mResolution = aLayerMetrics.mResolution;
     mFrameMetrics.mCumulativeResolution = aLayerMetrics.mCumulativeResolution;
     mFrameMetrics.mHasScrollgrab = aLayerMetrics.mHasScrollgrab;
 
     if (scrollOffsetUpdated) {
-      CancelAnimation();
-
       APZC_LOG("%p updating scroll offset from (%f, %f) to (%f, %f)\n", this,
         mFrameMetrics.GetScrollOffset().x, mFrameMetrics.GetScrollOffset().y,
         aLayerMetrics.GetScrollOffset().x, aLayerMetrics.GetScrollOffset().y);
 
       mFrameMetrics.CopyScrollInfoFrom(aLayerMetrics);
 
+      // Cancel the animation (which will also trigger a repaint request)
+      // after we update the scroll offset above. Otherwise we can be left
+      // in a state where things are out of sync.
+      CancelAnimation();
+
       // Because of the scroll offset update, any inflight paint requests are
       // going to be ignored by layout, and so mLastDispatchedPaintMetrics
       // becomes incorrect for the purposes of calculating the LD transform. To
       // correct this we need to update mLastDispatchedPaintMetrics to be the
       // last thing we know was painted by Gecko.
       mLastDispatchedPaintMetrics = aLayerMetrics;
     }
   }
--- a/gfx/layers/basic/TextureClientX11.cpp
+++ b/gfx/layers/basic/TextureClientX11.cpp
@@ -13,19 +13,20 @@
 
 #include "mozilla/X11Util.h"
 #include <X11/Xlib.h>
 
 using namespace mozilla;
 using namespace mozilla::gfx;
 using namespace mozilla::layers;
 
-TextureClientX11::TextureClientX11(SurfaceFormat aFormat, TextureFlags aFlags)
+TextureClientX11::TextureClientX11(ISurfaceAllocator* aAllocator, SurfaceFormat aFormat, TextureFlags aFlags)
   : TextureClient(aFlags),
     mFormat(aFormat),
+    mAllocator(aAllocator),
     mLocked(false)
 {
   MOZ_COUNT_CTOR(TextureClientX11);
 }
 
 TextureClientX11::~TextureClientX11()
 {
   MOZ_COUNT_DTOR(TextureClientX11);
@@ -46,17 +47,28 @@ TextureClientX11::Lock(OpenMode aMode)
 }
 
 void
 TextureClientX11::Unlock()
 {
   MOZ_ASSERT(mLocked, "The TextureClient is already Unlocked!");
   mLocked = false;
 
-  if (mSurface) {
+  if (mDrawTarget) {
+    // see the comment on TextureClient::BorrowDrawTarget.
+    // This DrawTarget is internal to the TextureClient and is only exposed to the
+    // outside world between Lock() and Unlock(). This assertion checks that no outside
+    // reference remains by the time Unlock() is called.
+    MOZ_ASSERT(mDrawTarget->refCount() == 1);
+
+    mDrawTarget->Flush();
+    mDrawTarget = nullptr;
+  }
+
+  if (mSurface && !mAllocator->IsSameProcess()) {
     FinishX(DefaultXDisplay());
   }
 }
 
 bool
 TextureClientX11::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
 {
   MOZ_ASSERT(IsValid());
@@ -81,22 +93,33 @@ TextureClientX11::AllocateForSurface(Int
     return false;
   }
 
   mSize = aSize;
   mSurface = static_cast<gfxXlibSurface*>(surface.get());
 
   // The host is always responsible for freeing the pixmap.
   mSurface->ReleasePixmap();
+
+  if (!mAllocator->IsSameProcess()) {
+    FinishX(DefaultXDisplay());
+  }
+
   return true;
 }
 
-TemporaryRef<DrawTarget>
-TextureClientX11::GetAsDrawTarget()
+DrawTarget*
+TextureClientX11::BorrowDrawTarget()
 {
   MOZ_ASSERT(IsValid());
+  MOZ_ASSERT(mLocked);
+
   if (!mSurface) {
     return nullptr;
   }
 
-  IntSize size = ToIntSize(mSurface->GetSize());
-  return Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
+  if (!mDrawTarget) {
+    IntSize size = ToIntSize(mSurface->GetSize());
+    mDrawTarget = Factory::CreateDrawTargetForCairoSurface(mSurface->CairoSurface(), size);
+  }
+
+  return mDrawTarget;
 }
--- a/gfx/layers/basic/TextureClientX11.h
+++ b/gfx/layers/basic/TextureClientX11.h
@@ -14,17 +14,17 @@ namespace mozilla {
 namespace layers {
 
 /**
  * A TextureClient implementation based on Xlib.
  */
 class TextureClientX11 : public TextureClient
 {
  public:
-  TextureClientX11(gfx::SurfaceFormat format, TextureFlags aFlags = TextureFlags::DEFAULT);
+  TextureClientX11(ISurfaceAllocator* aAllocator, gfx::SurfaceFormat format, TextureFlags aFlags = TextureFlags::DEFAULT);
 
   ~TextureClientX11();
 
   // TextureClient
 
   virtual bool IsAllocated() const MOZ_OVERRIDE;
 
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
@@ -36,25 +36,27 @@ class TextureClientX11 : public TextureC
   virtual void Unlock() MOZ_OVERRIDE;
 
   virtual bool IsLocked() const MOZ_OVERRIDE { return mLocked; }
 
   virtual bool AllocateForSurface(gfx::IntSize aSize, TextureAllocationFlags flags) MOZ_OVERRIDE;
 
   virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; }
 
-  virtual TemporaryRef<gfx::DrawTarget> GetAsDrawTarget() MOZ_OVERRIDE;
+  virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual gfx::SurfaceFormat GetFormat() const { return mFormat; }
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return false; }
 
  private:
   gfx::SurfaceFormat mFormat;
   gfx::IntSize mSize;
   RefPtr<gfxXlibSurface> mSurface;
+  RefPtr<ISurfaceAllocator> mAllocator;
+  RefPtr<gfx::DrawTarget> mDrawTarget;
   bool mLocked;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -85,17 +85,17 @@ CanvasClient2D::Update(gfx::IntSize aSiz
     mBuffer = nullptr;
     return;
   }
 
   bool updated = false;
   {
     // Restrict drawTarget to a scope so that terminates before Unlock.
     RefPtr<DrawTarget> target =
-      mBuffer->GetAsDrawTarget();
+      mBuffer->BorrowDrawTarget();
     if (target) {
       aLayer->UpdateTarget(target);
       updated = true;
     }
   }
   mBuffer->Unlock();
 
   if (bufferCreated && !AddTextureClient(mBuffer)) {
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -275,22 +275,22 @@ ContentClientRemoteBuffer::CreateBuffer(
     return;
   }
 
   // We just created the textures and we are about to get their draw targets
   // so we have to lock them here.
   DebugOnly<bool> locked = mTextureClient->Lock(OpenMode::OPEN_READ_WRITE);
   MOZ_ASSERT(locked, "Could not lock the TextureClient");
 
-  *aBlackDT = mTextureClient->GetAsDrawTarget();
+  *aBlackDT = mTextureClient->BorrowDrawTarget();
   if (aFlags & BUFFER_COMPONENT_ALPHA) {
     locked = mTextureClientOnWhite->Lock(OpenMode::OPEN_READ_WRITE);
     MOZ_ASSERT(locked, "Could not lock the second TextureClient for component alpha");
 
-    *aWhiteDT = mTextureClientOnWhite->GetAsDrawTarget();
+    *aWhiteDT = mTextureClientOnWhite->BorrowDrawTarget();
   }
 }
 
 nsIntRegion
 ContentClientRemoteBuffer::GetUpdatedRegion(const nsIntRegion& aRegionToDraw,
                                             const nsIntRegion& aVisibleRegion,
                                             bool aDidSelfCopy)
 {
@@ -494,19 +494,19 @@ ContentClientDoubleBuffered::FinalizeFra
       !mFrontClientOnWhite->Lock(OpenMode::OPEN_READ_ONLY)) {
     mFrontClient->Unlock();
     return;
   }
   {
     // Restrict the DrawTargets and frontBuffer to a scope to make
     // sure there is no more external references to the DrawTargets
     // when we Unlock the TextureClients.
-    RefPtr<DrawTarget> dt = mFrontClient->GetAsDrawTarget();
+    RefPtr<DrawTarget> dt = mFrontClient->BorrowDrawTarget();
     RefPtr<DrawTarget> dtOnWhite = mFrontClientOnWhite
-      ? mFrontClientOnWhite->GetAsDrawTarget()
+      ? mFrontClientOnWhite->BorrowDrawTarget()
       : nullptr;
     RotatedBuffer frontBuffer(dt,
                               dtOnWhite,
                               mFrontBufferRect,
                               mFrontBufferRotation);
     UpdateDestinationFrom(frontBuffer, updateRegion);
   }
 
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -289,17 +289,17 @@ ImageClientSingle::UpdateImageInternal(I
 
     if (!mFrontBuffer->Lock(OpenMode::OPEN_WRITE_ONLY)) {
       mFrontBuffer = nullptr;
       return false;
     }
 
     {
       // We must not keep a reference to the DrawTarget after it has been unlocked.
-      RefPtr<DrawTarget> dt = mFrontBuffer->GetAsDrawTarget();
+      DrawTarget* dt = mFrontBuffer->BorrowDrawTarget();
       MOZ_ASSERT(surface.get());
       dt->CopySurface(surface, IntRect(IntPoint(), surface->GetSize()), IntPoint());
     }
 
     mFrontBuffer->Unlock();
 
     if (bufferCreated) {
       if (!AddTextureClient(mFrontBuffer)) {
--- a/gfx/layers/client/SimpleTiledContentClient.cpp
+++ b/gfx/layers/client/SimpleTiledContentClient.cpp
@@ -114,17 +114,17 @@ SimpleTiledLayerBuffer::ValidateTile(Sim
   RefPtr<DrawTarget> srcDT;
   uint8_t* srcData = nullptr;
   int32_t srcStride = 0;
   gfx::IntSize srcSize;
   gfx::SurfaceFormat srcFormat = gfx::SurfaceFormat::UNKNOWN;
 
   if (doBufferedDrawing) {
     // try to directly access the pixels of the TextureClient
-    srcDT = textureClient->GetAsDrawTarget();
+    srcDT = textureClient->BorrowDrawTarget();
     if (srcDT->LockBits(&srcData, &srcSize, &srcStride, &srcFormat)) {
       if (!aTile.mCachedBuffer) {
         aTile.mCachedBuffer = SharedBuffer::Create(srcStride * srcSize.height);
         fullPaint = true;
       }
       bufferData = (unsigned char*) aTile.mCachedBuffer->Data();
 
       drawTarget = gfxPlatform::GetPlatform()->CreateDrawTargetForData(bufferData,
@@ -145,17 +145,17 @@ SimpleTiledLayerBuffer::ValidateTile(Sim
     } else {
       // failed to obtain the client as an ImageSurface
       doBufferedDrawing = false;
     }
   }
 
   // this might get set above if we couldn't extract out a buffer
   if (!doBufferedDrawing) {
-    drawTarget = textureClient->GetAsDrawTarget();
+    drawTarget = textureClient->BorrowDrawTarget();
 
     fullPaint = true;
     drawBounds = nsIntRect(aTileOrigin.x, aTileOrigin.y, GetScaledTileSize().width, GetScaledTileSize().height);
     drawRegion = nsIntRegion(drawBounds);
 
     if (GetContentType() == gfxContentType::COLOR_ALPHA)
       drawTarget->ClearRect(Rect(0, 0, drawBounds.width, drawBounds.height));
   }
--- a/gfx/layers/client/TextureClient.cpp
+++ b/gfx/layers/client/TextureClient.cpp
@@ -279,26 +279,26 @@ TextureClient::CreateTextureClientForDra
   gfxSurfaceType type =
     gfxPlatform::GetPlatform()->ScreenReferenceSurface()->GetType();
 
   if (parentBackend == LayersBackend::LAYERS_BASIC &&
       aMoz2DBackend == gfx::BackendType::CAIRO &&
       type == gfxSurfaceType::Xlib &&
       !(aTextureFlags & TextureFlags::ALLOC_FALLBACK))
   {
-    result = new TextureClientX11(aFormat, aTextureFlags);
+    result = new TextureClientX11(aAllocator, aFormat, aTextureFlags);
   }
 #ifdef GL_PROVIDER_GLX
   if (parentBackend == LayersBackend::LAYERS_OPENGL &&
       type == gfxSurfaceType::Xlib &&
       !(aTextureFlags & TextureFlags::ALLOC_FALLBACK) &&
       aFormat != SurfaceFormat::A8 &&
       gl::sGLXLibrary.UseTextureFromPixmap())
   {
-    result = new TextureClientX11(aFormat, aTextureFlags);
+    result = new TextureClientX11(aAllocator, aFormat, aTextureFlags);
   }
 #endif
 #endif
 
 #ifdef MOZ_WIDGET_GONK
   if (!DisableGralloc(aFormat, aSizeHint)) {
     // Don't allow Gralloc texture clients to exceed the maximum texture size.
     // BufferTextureClients have code to handle tiling the surface client-side.
@@ -320,17 +320,17 @@ TextureClient::CreateTextureClientForDra
 
 // static
 TemporaryRef<BufferTextureClient>
 TextureClient::CreateBufferTextureClient(ISurfaceAllocator* aAllocator,
                                          SurfaceFormat aFormat,
                                          TextureFlags aTextureFlags,
                                          gfx::BackendType aMoz2DBackend)
 {
-  if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) {
+  if (aAllocator->IsSameProcess()) {
     RefPtr<BufferTextureClient> result = new MemoryTextureClient(aAllocator, aFormat,
                                                                  aMoz2DBackend,
                                                                  aTextureFlags);
     return result.forget();
   }
   RefPtr<BufferTextureClient> result = new ShmemTextureClient(aAllocator, aFormat,
                                                               aMoz2DBackend,
                                                               aTextureFlags);
@@ -372,25 +372,23 @@ bool TextureClient::CopyToTextureClient(
 {
   MOZ_ASSERT(IsLocked());
   MOZ_ASSERT(aTarget->IsLocked());
 
   if (!aTarget->CanExposeDrawTarget() || !CanExposeDrawTarget()) {
     return false;
   }
 
-  RefPtr<DrawTarget> destinationTarget = aTarget->GetAsDrawTarget();
-  RefPtr<DrawTarget> sourceTarget = GetAsDrawTarget();
+  DrawTarget* destinationTarget = aTarget->BorrowDrawTarget();
+  DrawTarget* sourceTarget = BorrowDrawTarget();
   RefPtr<gfx::SourceSurface> source = sourceTarget->Snapshot();
   destinationTarget->CopySurface(source,
                                  aRect ? *aRect : gfx::IntRect(gfx::IntPoint(0, 0), GetSize()),
                                  aPoint ? *aPoint : gfx::IntPoint(0, 0));
-  destinationTarget = nullptr;
   source = nullptr;
-  sourceTarget = nullptr;
 
   return true;
 }
 
 void
 TextureClient::Finalize()
 {
   MOZ_ASSERT(!IsLocked());
@@ -571,21 +569,21 @@ BufferTextureClient::AllocateForSurface(
   }
 
   ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
   serializer.InitializeBufferInfo(aSize, mFormat);
   mSize = aSize;
   return true;
 }
 
-TemporaryRef<gfx::DrawTarget>
-BufferTextureClient::GetAsDrawTarget()
+gfx::DrawTarget*
+BufferTextureClient::BorrowDrawTarget()
 {
   MOZ_ASSERT(IsValid());
-  MOZ_ASSERT(mLocked, "GetAsDrawTarget should be called on locked textures only");
+  MOZ_ASSERT(mLocked, "BorrowDrawTarget should be called on locked textures only");
 
   if (mDrawTarget) {
     return mDrawTarget;
   }
 
   ImageDataSerializer serializer(GetBuffer(), GetBufferSize());
   if (!serializer.IsValid()) {
     return nullptr;
@@ -614,17 +612,17 @@ void
 BufferTextureClient::Unlock()
 {
   MOZ_ASSERT(mLocked, "The TextureClient is already Unlocked!");
   mLocked = false;
   if (!mDrawTarget) {
     return;
   }
 
-  // see the comment on TextureClient::GetAsDrawTarget.
+  // see the comment on TextureClient::BorrowDrawTarget.
   // This DrawTarget is internal to the TextureClient and is only exposed to the
   // outside world between Lock() and Unlock(). This assertion checks that no outside
   // reference remains by the time Unlock() is called.
   MOZ_ASSERT(mDrawTarget->refCount() == 1);
 
   mDrawTarget->Flush();
   mDrawTarget = nullptr;
 }
--- a/gfx/layers/client/TextureClient.h
+++ b/gfx/layers/client/TextureClient.h
@@ -154,23 +154,23 @@ public:
    * This is typically used as follows:
    *
    * if (!texture->Lock(OpenMode::OPEN_READ_WRITE)) {
    *   return false;
    * }
    * {
    *   // Restrict this code's scope to ensure all references to dt are gone
    *   // when Unlock is called.
-   *   RefPtr<DrawTarget> dt = texture->GetAsDrawTarget();
+   *   DrawTarget* dt = texture->BorrowDrawTarget();
    *   // use the draw target ...
    * }
    * texture->Unlock();
    *
    */
-  virtual TemporaryRef<gfx::DrawTarget> GetAsDrawTarget() { return nullptr; }
+  virtual gfx::DrawTarget* BorrowDrawTarget() { return nullptr; }
 
   // TextureClients that can expose a DrawTarget should override this method.
   virtual gfx::SurfaceFormat GetFormat() const
   {
     return gfx::SurfaceFormat::UNKNOWN;
   }
 
   /**
@@ -390,17 +390,17 @@ public:
   virtual bool Lock(OpenMode aMode) MOZ_OVERRIDE;
 
   virtual void Unlock() MOZ_OVERRIDE;
 
   virtual bool IsLocked() const MOZ_OVERRIDE { return mLocked; }
 
   virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; }
 
-  virtual TemporaryRef<gfx::DrawTarget> GetAsDrawTarget() MOZ_OVERRIDE;
+  virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE;
 
   // TextureClientYCbCr
 
   virtual TextureClientYCbCr* AsTextureClientYCbCr() MOZ_OVERRIDE { return this; }
 
--- a/gfx/layers/client/TiledContentClient.cpp
+++ b/gfx/layers/client/TiledContentClient.cpp
@@ -522,17 +522,17 @@ TileClient::GetBackBuffer(const nsIntReg
     if (mBackBuffer) {
       // Our current back-buffer is still locked by the compositor. This can occur
       // when the client is producing faster than the compositor can consume. In
       // this case we just want to drop it and not return it to the pool.
       aPool->ReportClientLost();
     }
     mBackBuffer = aPool->GetTextureClient();
     // Create a lock for our newly created back-buffer.
-    if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) {
+    if (mManager->AsShadowForwarder()->IsSameProcess()) {
       // If our compositor is in the same process, we can save some cycles by not
       // using shared memory.
       mBackLock = new gfxMemorySharedReadLock();
     } else {
       mBackLock = new gfxShmSharedReadLock(mManager->AsShadowForwarder());
     }
 
     MOZ_ASSERT(mBackLock->IsValid());
@@ -767,17 +767,17 @@ ClientTiledLayerBuffer::ValidateTile(Til
     NS_WARNING("Failed to lock tile TextureClient for updating.");
     aTile.DiscardFrontBuffer();
     return aTile;
   }
 
   // We must not keep a reference to the DrawTarget after it has been unlocked,
   // make sure these are null'd before unlocking as destruction of the context
   // may cause the target to be flushed.
-  RefPtr<DrawTarget> drawTarget = backBuffer->GetAsDrawTarget();
+  RefPtr<DrawTarget> drawTarget = backBuffer->BorrowDrawTarget();
   drawTarget->SetTransform(Matrix());
 
   RefPtr<gfxContext> ctxt = new gfxContext(drawTarget);
 
   if (usingSinglePaintBuffer) {
     // XXX Perhaps we should just copy the bounding rectangle here?
     RefPtr<gfx::SourceSurface> source = mSinglePaintDrawTarget->Snapshot();
     nsIntRegionRectIterator it(aDirtyRegion);
--- a/gfx/layers/d3d11/TextureD3D11.cpp
+++ b/gfx/layers/d3d11/TextureD3D11.cpp
@@ -175,48 +175,48 @@ TextureClientD3D11::Lock(OpenMode aMode)
   if (!mTexture) {
     return false;
   }
   MOZ_ASSERT(!mIsLocked, "The Texture is already locked!");
   LockD3DTexture(mTexture.get());
   mIsLocked = true;
 
   if (mNeedsClear) {
-    mDrawTarget = GetAsDrawTarget();
+    mDrawTarget = BorrowDrawTarget();
     mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
     mNeedsClear = false;
   }
 
   return true;
 }
 
 void
 TextureClientD3D11::Unlock()
 {
   MOZ_ASSERT(mIsLocked, "Unlocked called while the texture is not locked!");
 
   if (mDrawTarget) {
-    // see the comment on TextureClient::GetAsDrawTarget.
+    // see the comment on TextureClient::BorrowDrawTarget.
     // This DrawTarget is internal to the TextureClient and is only exposed to the
     // outside world between Lock() and Unlock(). This assertion checks that no outside
     // reference remains by the time Unlock() is called.
     MOZ_ASSERT(mDrawTarget->refCount() == 1);
     mDrawTarget->Flush();
   }
 
   // The DrawTarget is created only once, and is only usable between calls
   // to Lock and Unlock.
   UnlockD3DTexture(mTexture.get());
   mIsLocked = false;
 }
 
-TemporaryRef<DrawTarget>
-TextureClientD3D11::GetAsDrawTarget()
+DrawTarget*
+TextureClientD3D11::BorrowDrawTarget()
 {
-  MOZ_ASSERT(mIsLocked, "Calling TextureClient::GetAsDrawTarget without locking :(");
+  MOZ_ASSERT(mIsLocked, "Calling TextureClient::BorrowDrawTarget without locking :(");
 
   if (!mTexture) {
     return nullptr;
   }
 
   if (mDrawTarget) {
     return mDrawTarget;
   }
--- a/gfx/layers/d3d11/TextureD3D11.h
+++ b/gfx/layers/d3d11/TextureD3D11.h
@@ -49,17 +49,17 @@ public:
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
 
   virtual gfx::IntSize GetSize() const MOZ_OVERRIDE { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const MOZ_OVERRIDE { return mFormat; }
 
   virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; }
 
-  virtual TemporaryRef<gfx::DrawTarget> GetAsDrawTarget() MOZ_OVERRIDE;
+  virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE;
 
 protected:
   gfx::IntSize mSize;
   RefPtr<ID3D10Texture2D> mTexture;
   RefPtr<gfx::DrawTarget> mDrawTarget;
--- a/gfx/layers/d3d9/TextureD3D9.cpp
+++ b/gfx/layers/d3d9/TextureD3D9.cpp
@@ -582,17 +582,17 @@ CairoTextureClientD3D9::Lock(OpenMode)
       NS_WARNING("Failed to get texture surface level.");
       return false;
     }
   }
 
   mIsLocked = true;
 
   if (mNeedsClear) {
-    mDrawTarget = GetAsDrawTarget();
+    mDrawTarget = BorrowDrawTarget();
     mDrawTarget->ClearRect(Rect(0, 0, GetSize().width, GetSize().height));
     mNeedsClear = false;
   }
 
   return true;
 }
 
 void
@@ -623,18 +623,18 @@ CairoTextureClientD3D9::ToSurfaceDescrip
     return false;
   }
 
   mTexture->AddRef(); // Release in TextureHostD3D9::TextureHostD3D9
   aOutDescriptor = SurfaceDescriptorD3D9(reinterpret_cast<uintptr_t>(mTexture.get()));
   return true;
 }
 
-TemporaryRef<gfx::DrawTarget>
-CairoTextureClientD3D9::GetAsDrawTarget()
+gfx::DrawTarget*
+CairoTextureClientD3D9::BorrowDrawTarget()
 {
   MOZ_ASSERT(mIsLocked && mD3D9Surface);
   if (mDrawTarget) {
     return mDrawTarget;
   }
 
   if (ContentForFormat(mFormat) == gfxContentType::COLOR) {
     mSurface = new gfxWindowsSurface(mD3D9Surface);
@@ -726,18 +726,18 @@ DIBTextureClientD3D9::ToSurfaceDescripto
   MOZ_ASSERT(mSurface);
   // The host will release this ref when it receives the surface descriptor.
   // We AddRef in case we die before the host receives the pointer.
   aOutDescriptor = SurfaceDescriptorDIB(reinterpret_cast<uintptr_t>(mSurface.get()));
   mSurface->AddRef();
   return true;
 }
 
-TemporaryRef<gfx::DrawTarget>
-DIBTextureClientD3D9::GetAsDrawTarget()
+gfx::DrawTarget*
+DIBTextureClientD3D9::BorrowDrawTarget()
 {
   MOZ_ASSERT(mIsLocked && IsAllocated());
 
   if (!mDrawTarget) {
     mDrawTarget =
       gfxPlatform::GetPlatform()->CreateDrawTargetForSurface(mSurface, mSize);
   }
 
--- a/gfx/layers/d3d9/TextureD3D9.h
+++ b/gfx/layers/d3d9/TextureD3D9.h
@@ -204,17 +204,17 @@ public:
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
 
   virtual gfx::IntSize GetSize() const { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const { return mFormat; }
 
   virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; }
 
-  virtual TemporaryRef<gfx::DrawTarget> GetAsDrawTarget() MOZ_OVERRIDE;
+  virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE;
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; }
 
 private:
   RefPtr<IDirect3DTexture9> mTexture;
@@ -253,17 +253,17 @@ public:
   virtual bool ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor) MOZ_OVERRIDE;
 
   virtual gfx::IntSize GetSize() const { return mSize; }
 
   virtual gfx::SurfaceFormat GetFormat() const { return mFormat; }
 
   virtual bool CanExposeDrawTarget() const MOZ_OVERRIDE { return true; }
 
-  virtual TemporaryRef<gfx::DrawTarget> GetAsDrawTarget() MOZ_OVERRIDE;
+  virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE;
 
   virtual bool HasInternalBuffer() const MOZ_OVERRIDE { return true; }
 
 protected:
   nsRefPtr<gfxWindowsSurface> mSurface;
--- a/gfx/layers/ipc/ISurfaceAllocator.cpp
+++ b/gfx/layers/ipc/ISurfaceAllocator.cpp
@@ -106,17 +106,17 @@ bool
 ISurfaceAllocator::AllocSurfaceDescriptorWithCaps(const gfx::IntSize& aSize,
                                                   gfxContentType aContent,
                                                   uint32_t aCaps,
                                                   SurfaceDescriptor* aBuffer)
 {
   gfx::SurfaceFormat format =
     gfxPlatform::GetPlatform()->Optimal2DFormatForContent(aContent);
   size_t size = ImageDataSerializer::ComputeMinBufferSize(aSize, format);
-  if (gfxPlatform::GetPlatform()->PreferMemoryOverShmem()) {
+  if (IsSameProcess()) {
     uint8_t *data = new (std::nothrow) uint8_t[size];
     if (!data) {
       return false;
     }
     GfxMemoryImageReporter::DidAlloc(data);
 #ifdef XP_MACOSX
     // Workaround a bug in Quartz where drawing an a8 surface to another a8
     // surface with OPERATOR_SOURCE still requires the destination to be clear.
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -482,17 +482,20 @@ ImageBridgeChild::EndTransaction()
     return;
   }
 
   AutoInfallibleTArray<CompositableOperation, 10> cset;
   cset.SetCapacity(mTxn->mOperations.size());
   if (!mTxn->mOperations.empty()) {
     cset.AppendElements(&mTxn->mOperations.front(), mTxn->mOperations.size());
   }
-  ShadowLayerForwarder::PlatformSyncBeforeUpdate();
+
+  if (!IsSameProcess()) {
+    ShadowLayerForwarder::PlatformSyncBeforeUpdate();
+  }
 
   AutoInfallibleTArray<EditReply, 10> replies;
 
   if (mTxn->mSwapRequired) {
     if (!SendUpdate(cset, &replies)) {
       NS_WARNING("could not send async texture transaction");
       return;
     }
--- a/gfx/layers/ipc/ImageBridgeParent.cpp
+++ b/gfx/layers/ipc/ImageBridgeParent.cpp
@@ -96,20 +96,22 @@ ImageBridgeParent::RecvUpdate(const Edit
     }
   }
 
   aReply->SetCapacity(replyv.size());
   if (replyv.size() > 0) {
     aReply->AppendElements(&replyv.front(), replyv.size());
   }
 
-  // Ensure that any pending operations involving back and front
-  // buffers have completed, so that neither process stomps on the
-  // other's buffer contents.
-  LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+  if (!IsSameProcess()) {
+    // Ensure that any pending operations involving back and front
+    // buffers have completed, so that neither process stomps on the
+    // other's buffer contents.
+    LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+  }
 
   return true;
 }
 
 bool
 ImageBridgeParent::RecvUpdateNoSwap(const EditArray& aEdits)
 {
   InfallibleTArray<EditReply> noReplies;
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -546,20 +546,22 @@ LayerTransactionParent::RecvUpdate(const
 
   if (reply) {
     reply->SetCapacity(replyv.size());
     if (replyv.size() > 0) {
       reply->AppendElements(&replyv.front(), replyv.size());
     }
   }
 
-  // Ensure that any pending operations involving back and front
-  // buffers have completed, so that neither process stomps on the
-  // other's buffer contents.
-  LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+  if (!IsSameProcess()) {
+    // Ensure that any pending operations involving back and front
+    // buffers have completed, so that neither process stomps on the
+    // other's buffer contents.
+    LayerManagerComposite::PlatformSyncBeforeReplyUpdate();
+  }
 
   mShadowLayersManager->ShadowLayersUpdated(this, aTransactionId, targetConfig,
                                             isFirstPaint, scheduleComposite, paintSequenceNumber);
 
 #ifdef COMPOSITOR_PERFORMANCE_WARNING
   int compositeTime = (int)(mozilla::TimeStamp::Now() - updateStart).ToMilliseconds();
   if (compositeTime > 15) {
     printf_stderr("Compositor: Layers update took %i ms (blocking gecko).\n", compositeTime);
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -564,18 +564,20 @@ ShadowLayerForwarder::EndTransaction(Inf
   mWindowOverlayChanged = false;
 
   TargetConfig targetConfig(mTxn->mTargetBounds,
                             mTxn->mTargetRotation,
                             mTxn->mClientBounds,
                             mTxn->mTargetOrientation,
                             aRegionToClear);
 
-  MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send..."));
-  PlatformSyncBeforeUpdate();
+  if (!IsSameProcess()) {
+    MOZ_LAYERS_LOG(("[LayersForwarder] syncing before send..."));
+    PlatformSyncBeforeUpdate();
+  }
 
   profiler_tracing("Paint", "Rasterize", TRACING_INTERVAL_END);
   if (mTxn->mSwapRequired) {
     MOZ_LAYERS_LOG(("[LayersForwarder] sending transaction..."));
     RenderTraceScope rendertrace3("Forward Transaction", "000093");
     if (!HasShadowManager() ||
         !mShadowManager->IPCOpen() ||
         !mShadowManager->SendUpdate(cset, aId, targetConfig, mIsFirstPaint,
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -166,21 +166,21 @@ SurfaceFormatForPixelFormat(android::Pix
   case PIXEL_FORMAT_RGB_565:
     return gfx::SurfaceFormat::R5G6B5;
   default:
     MOZ_CRASH("Unknown gralloc pixel format");
   }
   return gfx::SurfaceFormat::R8G8B8A8;
 }
 
-TemporaryRef<gfx::DrawTarget>
-GrallocTextureClientOGL::GetAsDrawTarget()
+gfx::DrawTarget*
+GrallocTextureClientOGL::BorrowDrawTarget()
 {
   MOZ_ASSERT(IsValid());
-  MOZ_ASSERT(mMappedBuffer, "Calling TextureClient::GetAsDrawTarget without locking :(");
+  MOZ_ASSERT(mMappedBuffer, "Calling TextureClient::BorrowDrawTarget without locking :(");
 
   if (!IsValid() || !IsAllocated()) {
     return nullptr;
   }
 
   if (mDrawTarget) {
     return mDrawTarget;
   }
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -77,17 +77,17 @@ public:
 
   android::PixelFormat GetPixelFormat()
   {
     return mGraphicBuffer->getPixelFormat();
   }
 
   virtual uint8_t* GetBuffer() const MOZ_OVERRIDE;
 
-  virtual TemporaryRef<gfx::DrawTarget> GetAsDrawTarget() MOZ_OVERRIDE;
+  virtual gfx::DrawTarget* BorrowDrawTarget() MOZ_OVERRIDE;
 
   virtual bool AllocateForSurface(gfx::IntSize aSize,
                                   TextureAllocationFlags aFlags = ALLOC_DEFAULT) MOZ_OVERRIDE;
 
   virtual bool AllocateForYCbCr(gfx::IntSize aYSize,
                                 gfx::IntSize aCbCrSize,
                                 StereoMode aStereoMode) MOZ_OVERRIDE;
 
--- a/gfx/tests/gtest/TestTextures.cpp
+++ b/gfx/tests/gtest/TestTextures.cpp
@@ -148,17 +148,17 @@ void TestTextureClientSurface(TextureCli
 
   // client allocation
   ASSERT_TRUE(texture->CanExposeDrawTarget());
   texture->AllocateForSurface(ToIntSize(surface->GetSize()));
   ASSERT_TRUE(texture->IsAllocated());
 
   ASSERT_TRUE(texture->Lock(OpenMode::OPEN_READ_WRITE));
   // client painting
-  RefPtr<DrawTarget> dt = texture->GetAsDrawTarget();
+  RefPtr<DrawTarget> dt = texture->BorrowDrawTarget();
   RefPtr<SourceSurface> source =
     gfxPlatform::GetPlatform()->GetSourceSurfaceForSurface(dt, surface);
   dt->CopySurface(source, IntRect(IntPoint(), source->GetSize()), IntPoint());
 
   RefPtr<SourceSurface> snapshot = dt->Snapshot();
 
   AssertSurfacesEqual(snapshot, source);
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -267,18 +267,16 @@ gfxPlatform::gfxPlatform()
     mFallbackUsesCmaps = UNINITIALIZED_VALUE;
 
     mWordCacheCharLimit = UNINITIALIZED_VALUE;
     mWordCacheMaxEntries = UNINITIALIZED_VALUE;
     mGraphiteShapingEnabled = UNINITIALIZED_VALUE;
     mOpenTypeSVGEnabled = UNINITIALIZED_VALUE;
     mBidiNumeralOption = UNINITIALIZED_VALUE;
 
-    mLayersPreferMemoryOverShmem = XRE_GetProcessType() == GeckoProcessType_Default;
-
     mSkiaGlue = nullptr;
 
     uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO) | BackendTypeBit(BackendType::SKIA);
     uint32_t contentMask = BackendTypeBit(BackendType::CAIRO);
     InitBackendPrefs(canvasMask, BackendType::CAIRO,
                      contentMask, BackendType::CAIRO);
 }
 
@@ -529,22 +527,16 @@ gfxPlatform::~gfxPlatform()
 #endif
 
 #if MOZ_TREE_CAIRO
     cairo_debug_reset_static_data();
 #endif
 #endif
 }
 
-bool
-gfxPlatform::PreferMemoryOverShmem() const {
-  MOZ_ASSERT(!CompositorParent::IsInCompositorThread());
-  return mLayersPreferMemoryOverShmem;
-}
-
 cairo_user_data_key_t kDrawTarget;
 
 RefPtr<DrawTarget>
 gfxPlatform::CreateDrawTargetForSurface(gfxASurface *aSurface, const IntSize& aSize)
 {
   SurfaceFormat format = Optimal2DFormatForContent(aSurface->GetContentType());
   RefPtr<DrawTarget> drawTarget = Factory::CreateDrawTargetForCairoSurface(aSurface->CairoSurface(), aSize, &format);
   aSurface->SetData(&kDrawTarget, drawTarget, nullptr);
--- a/gfx/thebes/gfxPlatform.h
+++ b/gfx/thebes/gfxPlatform.h
@@ -536,24 +536,16 @@ public:
     mozilla::layers::DiagnosticTypes GetLayerDiagnosticTypes();
 
     static nsIntRect FrameCounterBounds() {
       int bits = 16;
       int sizeOfBit = 3;
       return nsIntRect(0, 0, bits * sizeOfBit, sizeOfBit);
     }
 
-    /**
-     * Returns true if we should use raw memory to send data to the compositor
-     * rather than using shmems.
-     *
-     * This method should not be called from the compositor thread.
-     */
-    bool PreferMemoryOverShmem() const;
-
     mozilla::gl::SkiaGLGlue* GetSkiaGLGlue();
     void PurgeSkiaCache();
 
     virtual bool IsInGonkEmulator() const { return false; }
 
 protected:
     gfxPlatform();
     virtual ~gfxPlatform();
@@ -656,13 +648,12 @@ private:
     // The backend to use for content
     mozilla::gfx::BackendType mContentBackend;
     // Bitmask of backend types we can use to render content
     uint32_t mContentBackendBitmask;
 
     mozilla::widget::GfxInfoCollector<gfxPlatform> mAzureCanvasBackendCollector;
 
     mozilla::RefPtr<mozilla::gfx::DrawEventRecorder> mRecorder;
-    bool mLayersPreferMemoryOverShmem;
     mozilla::RefPtr<mozilla::gl::SkiaGLGlue> mSkiaGlue;
 };
 
 #endif /* GFX_PLATFORM_H */
--- a/ipc/glue/Shmem.cpp
+++ b/ipc/glue/Shmem.cpp
@@ -618,16 +618,21 @@ Shmem::GetSysVID() const
 
 IPC::Message*
 Shmem::ShareTo(IHadBetterBeIPDLCodeCallingThis_OtherwiseIAmADoodyhead,
                base::ProcessHandle aProcess,
                int32_t routingId)
 {
   AssertInvariants();
 
+  // kInvalidProcessHandle is used to indicate that it's the same process.
+  if (aProcess == kInvalidProcessHandle) {
+    aProcess = base::GetCurrentProcessHandle();
+  }
+
   if (SharedMemory::TYPE_BASIC == mSegment->Type()) {
     SharedMemoryBasic* seg = static_cast<SharedMemoryBasic*>(mSegment);
     SharedMemoryBasic::Handle handle;
     if (!seg->ShareToProcess(aProcess, &handle))
       return 0;
 
     return new ShmemCreated(routingId, mId, mSize, handle);
   }
--- a/ipc/ipdl/ipdl/lower.py
+++ b/ipc/ipdl/ipdl/lower.py
@@ -2956,17 +2956,17 @@ class _GenerateProtocolActorCode(ipdl.as
                              Param(Type('MessageLoop', ptr=True),
                                    aMessageLoop.name),
                              Param(Type('mozilla::ipc::Side'),
                                    sidevar.name,
                                    default=ExprVar('mozilla::ipc::UnknownSide')) ],
                     ret=Type.BOOL))
 
             openmeth.addstmts([
-                StmtExpr(ExprAssn(p.otherProcessVar(), ExprCall(ExprVar('base::GetCurrentProcessHandle')))),
+                StmtExpr(ExprAssn(p.otherProcessVar(), ExprVar('ipc::kInvalidProcessHandle'))),
                 StmtReturn(ExprCall(ExprSelect(p.channelVar(), '.', 'Open'),
                                     [ aChannel, aMessageLoop, sidevar ]))
             ])
             self.cls.addstmts([
                 openmeth,
                 Whitespace.NL ])
 
             # Close()
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -7299,16 +7299,19 @@ Parser<ParseHandler>::primaryExpr(TokenK
         return objectLiteral();
 
       case TOK_LET:
         return letBlock(LetExpresion);
 
       case TOK_LP:
         return parenExprOrGeneratorComprehension();
 
+#ifdef JS_HAS_TEMPLATE_STRINGS
+      case TOK_TEMPLATE_STRING:
+#endif
       case TOK_STRING:
         return stringLiteral();
 
       case TOK_YIELD:
         if (!checkYieldNameValidity())
             return null();
         // Fall through.
       case TOK_NAME:
--- a/js/src/frontend/TokenStream.cpp
+++ b/js/src/frontend/TokenStream.cpp
@@ -302,16 +302,19 @@ TokenStream::TokenStream(ExclusiveContex
     maybeEOL[unsigned('\n')] = true;
     maybeEOL[unsigned('\r')] = true;
     maybeEOL[unsigned(LINE_SEPARATOR & 0xff)] = true;
     maybeEOL[unsigned(PARA_SEPARATOR & 0xff)] = true;
 
     // See getTokenInternal() for an explanation of maybeStrSpecial[].
     memset(maybeStrSpecial, 0, sizeof(maybeStrSpecial));
     maybeStrSpecial[unsigned('"')] = true;
+#ifdef JS_HAS_TEMPLATE_STRINGS
+    maybeStrSpecial[unsigned('`')] = true;
+#endif
     maybeStrSpecial[unsigned('\'')] = true;
     maybeStrSpecial[unsigned('\\')] = true;
     maybeStrSpecial[unsigned('\n')] = true;
     maybeStrSpecial[unsigned('\r')] = true;
     maybeStrSpecial[unsigned(LINE_SEPARATOR & 0xff)] = true;
     maybeStrSpecial[unsigned(PARA_SEPARATOR & 0xff)] = true;
     maybeStrSpecial[unsigned(EOF & 0xff)] = true;
 
@@ -1007,16 +1010,19 @@ enum FirstCharKind {
 
     Space = TOK_LIMIT,
     Ident,
     Dec,
     String,
     EOL,
     BasePrefix,
     Other,
+#ifdef JS_HAS_TEMPLATE_STRINGS
+    TemplateString,
+#endif
 
     LastCharKind = Other
 };
 
 // OneChar: 40,  41,  44,  58,  59,  63,  91,  93,  123, 125, 126:
 //          '(', ')', ',', ':', ';', '?', '[', ']', '{', '}', '~'
 // Ident:   36, 65..90, 95, 97..122: '$', 'A'..'Z', '_', 'a'..'z'
 // Dot:     46: '.'
@@ -1026,36 +1032,42 @@ enum FirstCharKind {
 // Plus:    43: '+'
 // BasePrefix:  48: '0'
 // Space:   9, 11, 12, 32: '\t', '\v', '\f', ' '
 // EOL:     10, 13: '\n', '\r'
 //
 #define T_COMMA     TOK_COMMA
 #define T_COLON     TOK_COLON
 #define T_BITNOT    TOK_BITNOT
-#define _______ Other
+#ifdef JS_HAS_TEMPLATE_STRINGS
+#define Templat     TemplateString
+#else
+#define Templat     Other
+#endif
+#define _______     Other
 static const uint8_t firstCharKinds[] = {
 /*         0        1        2        3        4        5        6        7        8        9    */
 /*   0+ */ _______, _______, _______, _______, _______, _______, _______, _______, _______,   Space,
 /*  10+ */     EOL,   Space,   Space,     EOL, _______, _______, _______, _______, _______, _______,
 /*  20+ */ _______, _______, _______, _______, _______, _______, _______, _______, _______, _______,
 /*  30+ */ _______, _______,   Space, _______,  String, _______,   Ident, _______, _______,  String,
 /*  40+ */  TOK_LP,  TOK_RP, _______, _______, T_COMMA,_______,  _______, _______,BasePrefix,  Dec,
 /*  50+ */     Dec,     Dec,     Dec,     Dec,     Dec,     Dec,     Dec,    Dec,  T_COLON,TOK_SEMI,
 /*  60+ */ _______, _______, _______,TOK_HOOK, _______,   Ident,   Ident,   Ident,   Ident,   Ident,
 /*  70+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
 /*  80+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
-/*  90+ */   Ident,  TOK_LB, _______,  TOK_RB, _______,   Ident, _______,   Ident,   Ident,   Ident,
+/*  90+ */   Ident,  TOK_LB, _______,  TOK_RB, _______,   Ident, Templat,   Ident,   Ident,   Ident,
 /* 100+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
 /* 110+ */   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,   Ident,
 /* 120+ */   Ident,   Ident,   Ident,  TOK_LC, _______,  TOK_RC,T_BITNOT, _______
 };
 #undef T_COMMA
 #undef T_COLON
 #undef T_BITNOT
+#undef Templat
 #undef _______
 
 static_assert(LastCharKind < (1 << (sizeof(firstCharKinds[0]) * 8)),
               "Elements of firstCharKinds[] are too small");
 
 TokenKind
 TokenStream::getTokenInternal(Modifier modifier)
 {
@@ -1244,19 +1256,23 @@ TokenStream::getTokenInternal(Modifier m
             if (!js_strtod(cx, numStart, userbuf.addressOfNextRawChar(), &dummy, &dval))
                 goto error;
         }
         tp->type = TOK_NUMBER;
         tp->setNumber(dval, decimalPoint);
         goto out;
     }
 
-    // Look for a string.
+    // Look for a string or a template string.
     //
-    if (c1kind == String) {
+    if (c1kind == String
+#ifdef JS_HAS_TEMPLATE_STRINGS
+         || c1kind == TemplateString
+#endif
+         ) {
         tp = newToken(-1);
         qc = c;
         tokenbuf.clear();
         while (true) {
             // We need to detect any of these chars:  " or ', \n (or its
             // equivalents), \\, EOF.  We use maybeStrSpecial[] in a manner
             // similar to maybeEOL[], see above.  Because we detect EOL
             // sequences here and put them back immediately, we can use
@@ -1276,16 +1292,22 @@ TokenStream::getTokenInternal(Modifier m
 
                       default:
                         if ('0' <= c && c < '8') {
                             int32_t val = JS7_UNDEC(c);
 
                             c = peekChar();
                             // Strict mode code allows only \0, then a non-digit.
                             if (val != 0 || JS7_ISDEC(c)) {
+#ifdef JS_HAS_TEMPLATE_STRINGS
+                                if (c1kind == TemplateString) {
+                                    reportError(JSMSG_DEPRECATED_OCTAL);
+                                    goto error;
+                                }
+#endif
                                 if (!reportStrictModeError(JSMSG_DEPRECATED_OCTAL))
                                     goto error;
                                 flags.sawOctalEscape = true;
                             }
                             if ('0' <= c && c < '8') {
                                 val = 8 * val + JS7_UNDEC(c);
                                 getChar();
                                 c = peekChar();
@@ -1326,29 +1348,52 @@ TokenStream::getTokenInternal(Modifier m
                             }
                         } else if (c == '\n') {
                             // ES5 7.8.4: an escaped line terminator represents
                             // no character.
                             continue;
                         }
                         break;
                     }
-                } else if (TokenBuf::isRawEOLChar(c) || c == EOF) {
+                } else if (c == EOF) {
                     ungetCharIgnoreEOL(c);
                     reportError(JSMSG_UNTERMINATED_STRING);
                     goto error;
+                } else if (TokenBuf::isRawEOLChar(c)) {
+#ifdef JS_HAS_TEMPLATE_STRINGS
+                    if (c1kind == String) {
+#endif
+                        ungetCharIgnoreEOL(c);
+                        reportError(JSMSG_UNTERMINATED_STRING);
+                        goto error;
+#ifdef JS_HAS_TEMPLATE_STRINGS
+                    }
+                    if (c == '\r') {
+                        c = '\n';
+                        if (peekChar() == '\n')
+                            skipChars(1);
+                    }
+#endif
                 }
             }
             if (!tokenbuf.append(c))
                 goto error;
         }
         JSAtom *atom = atomize(cx, tokenbuf);
         if (!atom)
             goto error;
-        tp->type = TOK_STRING;
+#ifdef JS_HAS_TEMPLATE_STRINGS
+        if (c1kind == String) {
+#endif
+            tp->type = TOK_STRING;
+#ifdef JS_HAS_TEMPLATE_STRINGS
+        } else {
+            tp->type = TOK_TEMPLATE_STRING;
+        }
+#endif
         tp->setAtom(atom);
         goto out;
     }
 
     // Skip over EOL chars, updating line state along the way.
     //
     if (c1kind == EOL) {
         // If it's a \r\n sequence: treat as a single EOL, skip over the \n.
@@ -1753,16 +1798,19 @@ TokenKindToString(TokenKind tt)
       case TOK_LC:              return "TOK_LC";
       case TOK_RC:              return "TOK_RC";
       case TOK_LP:              return "TOK_LP";
       case TOK_RP:              return "TOK_RP";
       case TOK_ARROW:           return "TOK_ARROW";
       case TOK_NAME:            return "TOK_NAME";
       case TOK_NUMBER:          return "TOK_NUMBER";
       case TOK_STRING:          return "TOK_STRING";
+#ifdef JS_HAS_TEMPLATE_STRINGS
+      case TOK_TEMPLATE_STRING: return "TOK_TEMPLATE_STRING";
+#endif
       case TOK_REGEXP:          return "TOK_REGEXP";
       case TOK_TRUE:            return "TOK_TRUE";
       case TOK_FALSE:           return "TOK_FALSE";
       case TOK_NULL:            return "TOK_NULL";
       case TOK_THIS:            return "TOK_THIS";
       case TOK_FUNCTION:        return "TOK_FUNCTION";
       case TOK_IF:              return "TOK_IF";
       case TOK_ELSE:            return "TOK_ELSE";
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -38,16 +38,17 @@ enum TokenKind {
     TOK_DOT,                       // member operator (.)
     TOK_TRIPLEDOT,                 // for rest arguments (...)
     TOK_LB, TOK_RB,                // left and right brackets
     TOK_LC, TOK_RC,                // left and right curlies (braces)
     TOK_LP, TOK_RP,                // left and right parentheses
     TOK_NAME,                      // identifier
     TOK_NUMBER,                    // numeric constant
     TOK_STRING,                    // string constant
+    TOK_TEMPLATE_STRING,           // template string
     TOK_REGEXP,                    // RegExp constant
     TOK_TRUE,                      // true
     TOK_FALSE,                     // false
     TOK_NULL,                      // null
     TOK_THIS,                      // this
     TOK_FUNCTION,                  // function keyword
     TOK_IF,                        // if keyword
     TOK_ELSE,                      // else keyword
@@ -270,17 +271,17 @@ struct Token
 
     void setName(PropertyName *name) {
         JS_ASSERT(type == TOK_NAME);
         JS_ASSERT(!IsPoisonedPtr(name));
         u.name = name;
     }
 
     void setAtom(JSAtom *atom) {
-        JS_ASSERT(type == TOK_STRING);
+        JS_ASSERT (type == TOK_STRING || type == TOK_TEMPLATE_STRING);
         JS_ASSERT(!IsPoisonedPtr(atom));
         u.atom = atom;
     }
 
     void setRegExpFlags(js::RegExpFlag flags) {
         JS_ASSERT(type == TOK_REGEXP);
         JS_ASSERT((flags & AllFlags) == flags);
         u.reflags = flags;
@@ -295,17 +296,17 @@ struct Token
     // Type-safe accessors
 
     PropertyName *name() const {
         JS_ASSERT(type == TOK_NAME);
         return u.name->asPropertyName(); // poor-man's type verification
     }
 
     JSAtom *atom() const {
-        JS_ASSERT(type == TOK_STRING);
+        JS_ASSERT (type == TOK_STRING || type == TOK_TEMPLATE_STRING);
         return u.atom;
     }
 
     js::RegExpFlag regExpFlags() const {
         JS_ASSERT(type == TOK_REGEXP);
         JS_ASSERT((u.reflags & AllFlags) == u.reflags);
         return u.reflags;
     }
--- a/js/src/gc/Barrier.cpp
+++ b/js/src/gc/Barrier.cpp
@@ -11,16 +11,17 @@
 
 #include "gc/Zone.h"
 
 namespace js {
 
 void
 ValueReadBarrier(const Value &value)
 {
+    JS_ASSERT(!CurrentThreadIsIonCompiling());
     if (value.isObject())
         JSObject::readBarrier(&value.toObject());
     else if (value.isString())
         JSString::readBarrier(value.toString());
     else
         JS_ASSERT(!value.isMarkable());
 }
 
@@ -50,16 +51,22 @@ HeapSlot::preconditionForWriteBarrierPos
          : static_cast<HeapSlot *>(obj->getDenseElements() + slot)->get() == target;
 }
 
 bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone)
 {
     return shadowZone->runtimeFromMainThread()->isHeapMajorCollecting();
 }
+
+bool
+CurrentThreadIsIonCompiling()
+{
+    return TlsPerThreadData.get()->ionCompiling;
+}
 #endif // DEBUG
 
 bool
 StringIsPermanentAtom(JSString *str)
 {
     return str->isPermanentAtom();
 }
 
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -156,16 +156,21 @@ class JSLinearString;
 
 namespace js {
 
 class PropertyName;
 
 #ifdef DEBUG
 bool
 RuntimeFromMainThreadIsHeapMajorCollecting(JS::shadow::Zone *shadowZone);
+
+// Barriers can't be triggered during backend Ion compilation, which may run on
+// a helper thread.
+bool
+CurrentThreadIsIonCompiling();
 #endif
 
 bool
 StringIsPermanentAtom(JSString *str);
 
 namespace gc {
 
 template <typename T>
@@ -193,16 +198,17 @@ class BarrieredCell : public gc::Cell
     MOZ_ALWAYS_INLINE JS::shadow::Zone *shadowZone() const { return JS::shadow::Zone::asShadowZone(zone()); }
     MOZ_ALWAYS_INLINE JS::Zone *zoneFromAnyThread() const { return tenuredZoneFromAnyThread(); }
     MOZ_ALWAYS_INLINE JS::shadow::Zone *shadowZoneFromAnyThread() const {
         return JS::shadow::Zone::asShadowZone(zoneFromAnyThread());
     }
 
     static MOZ_ALWAYS_INLINE void readBarrier(T *thing) {
 #ifdef JSGC_INCREMENTAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         JS::shadow::Zone *shadowZone = thing->shadowZoneFromAnyThread();
         if (shadowZone->needsBarrier()) {
             MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
             T *tmp = thing;
             js::gc::MarkUnbarriered<T>(shadowZone->barrierTracer(), &tmp, "read barrier");
             JS_ASSERT(tmp == thing);
         }
 #endif
@@ -215,16 +221,17 @@ class BarrieredCell : public gc::Cell
         return false;
 #endif
     }
 
     static MOZ_ALWAYS_INLINE bool isNullLike(T *thing) { return !thing; }
 
     static MOZ_ALWAYS_INLINE void writeBarrierPre(T *thing) {
 #ifdef JSGC_INCREMENTAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (isNullLike(thing) || !thing->shadowRuntimeFromAnyThread()->needsBarrier())
             return;
 
         JS::shadow::Zone *shadowZone = thing->shadowZoneFromAnyThread();
         if (shadowZone->needsBarrier()) {
             MOZ_ASSERT(!RuntimeFromMainThreadIsHeapMajorCollecting(shadowZone));
             T *tmp = thing;
             js::gc::MarkUnbarriered<T>(shadowZone->barrierTracer(), &tmp, "write barrier");
@@ -312,59 +319,64 @@ struct InternalGCMethods<Value>
     static JS::shadow::Runtime *shadowRuntimeFromMainThread(const Value &v) {
         return reinterpret_cast<JS::shadow::Runtime*>(runtimeFromMainThread(v));
     }
 
     static bool isMarkable(Value v) { return v.isMarkable(); }
 
     static void preBarrier(Value v) {
 #ifdef JSGC_INCREMENTAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (v.isMarkable() && shadowRuntimeFromAnyThread(v)->needsBarrier())
             preBarrier(ZoneOfValueFromAnyThread(v), v);
 #endif
     }
 
     static void preBarrier(Zone *zone, Value v) {
 #ifdef JSGC_INCREMENTAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (v.isString() && StringIsPermanentAtom(v.toString()))
             return;
         JS::shadow::Zone *shadowZone = JS::shadow::Zone::asShadowZone(zone);
         if (shadowZone->needsBarrier()) {
             JS_ASSERT_IF(v.isMarkable(), shadowRuntimeFromMainThread(v)->needsBarrier());
             Value tmp(v);
             js::gc::MarkValueUnbarriered(shadowZone->barrierTracer(), &tmp, "write barrier");
             JS_ASSERT(tmp == v);
         }
 #endif
     }
 
     static void postBarrier(Value *vp) {
 #ifdef JSGC_GENERATIONAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (vp->isObject()) {
             gc::StoreBuffer *sb = reinterpret_cast<gc::Cell *>(&vp->toObject())->storeBuffer();
             if (sb)
                 sb->putValueFromAnyThread(vp);
         }
 #endif
     }
 
     static void postBarrierRelocate(Value *vp) {
 #ifdef JSGC_GENERATIONAL
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         if (vp->isObject()) {
             gc::StoreBuffer *sb = reinterpret_cast<gc::Cell *>(&vp->toObject())->storeBuffer();
             if (sb)
                 sb->putRelocatableValueFromAnyThread(vp);
         }
 #endif
     }
 
     static void postBarrierRemove(Value *vp) {
 #ifdef JSGC_GENERATIONAL
         JS_ASSERT(vp);
         JS_ASSERT(vp->isMarkable());
+        JS_ASSERT(!CurrentThreadIsIonCompiling());
         JSRuntime *rt = static_cast<js::gc::Cell *>(vp->toGCThing())->runtimeFromAnyThread();
         JS::shadow::Runtime *shadowRuntime = JS::shadow::Runtime::asShadowRuntime(rt);
         shadowRuntime->gcStoreBufferPtr()->removeRelocatableValueFromAnyThread(vp);
 #endif
     }
 
     static void readBarrier(const Value &v) { ValueReadBarrier(v); }
 };
--- a/js/src/jit-test/tests/asm.js/testFFI.js
+++ b/js/src/jit-test/tests/asm.js/testFFI.js
@@ -91,8 +91,18 @@ assertEq(f(4,5), 10);
 
 var recurse = function(i,j) { if (i == 0) throw j; f(i-1,j) }
 var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function g(i,j,k) { i=i|0;j=+j;k=k|0; if (!(k|0)) ffi(i|0,j)|0; else g(i, j+1.0, (k-1)|0) } function f(i,j) { i=i|0;j=+j; g(i,j,4) } return f'), null, {ffi:recurse});
 assertThrowsValue(function() { f(0,2.4) }, 2.4+4);
 assertThrowsValue(function() { f(1,2.4) }, 2.4+8);
 assertThrowsValue(function() { f(8,2.4) }, 2.4+36);
 
 assertEq(asmLink(asmCompile('glob', 'imp', USE_ASM + 'var identity=imp.identity; function g(x) { x=+x; return +identity(x) } return g'), null, imp)(13.37), 13.37);
+
+setJitCompilerOption("ion.usecount.trigger", 20);
+function ffiInt(a,b,c,d,e,f,g,h,i,j) { return j+1 }
+var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=i|0; return ffi(i|0,(i+1)|0,(i+2)|0,(i+3)|0,(i+4)|0,(i+5)|0,(i+6)|0,(i+7)|0,(i+8)|0,(i+9)|0)|0 } return f'), null, {ffi:ffiInt});
+for (var i = 0; i < 40; i++)
+    assertEq(f(i), i+10);
+function ffiDouble(a,b,c,d,e,f,g,h,i,j) { return j+1 }
+var f = asmLink(asmCompile('glob', 'imp', USE_ASM + 'var ffi=imp.ffi; function f(i) { i=+i; return +ffi(i,i+1.0,i+2.0,i+3.0,i+4.0,i+5.0,i+6.0,i+7.0,i+8.0,i+9.0) } return f'), null, {ffi:ffiDouble});
+for (var i = 0; i < 40; i++)
+    assertEq(f(i), i+10);
--- a/js/src/jit-test/tests/basic/testDontClobberScannerError.js
+++ b/js/src/jit-test/tests/basic/testDontClobberScannerError.js
@@ -1,9 +1,9 @@
 var caught = false;
 try {
-    Function("a, `", "");
+    Function("a, @", "");
 } catch(e) {
     assertEq(e.toString().search("SyntaxError: malformed formal parameter") == -1, true);
     assertEq(e.toString().search("SyntaxError: illegal character")          == -1, false);
     caught = true;
 }
 assertEq(caught, true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/bug1015498.js
@@ -0,0 +1,91 @@
+setJitCompilerOption("baseline.usecount.trigger", 10);
+setJitCompilerOption("ion.usecount.trigger", 20);
+
+function myFloor(x) {
+    if(x >= 0)
+        return x - Math.abs(x % 1);
+    else
+        return x - Math.abs(1 + (x % 1));
+}
+
+function floorRangeTest(x) {
+    if(10 < x) {
+        if(x < 100) {
+            assertEq(Math.floor(x), myFloor(x));
+        }
+    }
+
+    if(-100 < x) {
+        if(x < -10) {
+            assertEq(Math.floor(x), myFloor(x));
+        }
+    }
+
+    if (-(4294967296 - 1) < x) {
+        if(x < 10) {
+            assertEq(Math.floor(x), myFloor(x));
+        }
+    }
+
+    if (-10 < x) {
+        if(x < 4294967296) {
+            assertEq(Math.floor(x), myFloor(x));
+        }
+    }
+
+    if (-2147483648 < x) {
+        if(x < 10) {
+            assertEq(Math.floor(x), myFloor(x));
+        }
+    }
+
+    if ((-2147483648 -1) < x) {
+        if(x < 10) {
+            assertEq(Math.floor(x), myFloor(x));
+        }
+    }
+
+    if (10 < x) {
+        if(x < 2147483648) {
+            assertEq(Math.floor(x), myFloor(x));
+        }
+    }
+
+    if (10 < x) {
+        if(x < 2147483649) {
+            assertEq(Math.floor(x), myFloor(x));
+        }
+    }
+
+    if (Math.pow(2,31) < x) {
+        if(x < Math.pow(2,33)) {
+            assertEq(Math.floor(x), myFloor(x));
+        }
+    }
+}
+
+var a = [Math.pow(2,31), Math.pow(2,33), -4294967296.4, 214748364.2, -50.4, 50.4];
+
+for(var i = 0; i < 10; i++) {
+  for (var j = 0; j < a.length; j++) {
+      floorRangeTest(a[j]);
+  }
+}
+
+for (var j = 0; j < 30; j++) {
+    (function() {
+        Math.floor(1.5);
+    })()
+}
+
+for (var j = 0; j < 30; j++) {
+    (function() {
+        Math.floor(-1.5);
+    })()
+}
+
+for (var j = 0; j < 30; j++) {
+    (function() {
+        Math.floor(-127.5);
+    })()
+}
--- a/js/src/jit-test/tests/ion/dce-with-rinstructions.js
+++ b/js/src/jit-test/tests/ion/dce-with-rinstructions.js
@@ -139,24 +139,16 @@ function radd_float(i) {
     var t = Math.fround(1/3);
     var fi = Math.fround(i);
     var x = Math.fround(Math.fround(Math.fround(Math.fround(t + fi) + t) + fi) + t);
     if (uceFault_add_float(i) || uceFault_add_float(i))
         assertEq(x, 199); /* != 199.00000002980232 (when computed with double additions) */
     return i;
 }
 
-var uceFault_add_string = eval(uneval(uceFault).replace('uceFault', 'uceFault_add_string'));
-function radd_string(i) {
-    var x = "s" + i;
-    if (uceFault_add_string(i) || uceFault_add_string(i))
-        assertEq(x, "s99");
-    return i;
-}
-
 var uceFault_add_object = eval(uneval(uceFault).replace('uceFault', 'uceFault_add_object'));
 function radd_object(i) {
     var t = i;
     var o = { valueOf: function () { return t; } };
     var x = o + i; /* computed with t == i, not 1000 */
     t = 1000;
     if (uceFault_add_object(i) || uceFault_add_object(i))
         assertEq(x, 198);
@@ -264,44 +256,61 @@ function rmod_object(i) {
     var o = { valueOf: function() { return t; } };
     var x = o % 98; /* computed with t == i, not 1000 */
     t = 1000;
     if(uceFault_mod_object(i) || uceFault_mod_object(i))
         assertEq(x, 1); /* 99 % 98 = 1 */
     return i;
 }
 
+var uceFault_concat_string = eval(uneval(uceFault).replace('uceFault', 'uceFault_concat_string'));
+function rconcat_string(i) {
+    var x = "s" + i.toString();
+    if (uceFault_concat_string(i) || uceFault_concat_string(i))
+        assertEq(x, "s99");
+    return i;
+}
+
+var uceFault_concat_number = eval(uneval(uceFault).replace('uceFault', 'uceFault_concat_number'));
+function rconcat_number(i) {
+    var x = "s" + i;
+    if (uceFault_concat_number(i) || uceFault_concat_number(i))
+        assertEq(x, "s99");
+    return i;
+}
+
 for (i = 0; i < 100; i++) {
     rbitnot_number(i);
     rbitnot_object(i);
     rbitor_number(i);
     rbitor_object(i);
     rbitxor_number(i);
     rbitxor_object(i);
     rlsh_number(i);
     rlsh_object(i);
     rrsh_number(i);
     rrsh_object(i);
     rursh_number(i);
     rursh_object(i);
     radd_number(i);
     radd_float(i);
-    radd_string(i);
     radd_object(i);
     rsub_number(i);
     rsub_float(i);
     rsub_object(i);
     rmul_number(i);
     rmul_float(i);
     rmul_object(i);
     rdiv_number(i);
     rdiv_float(i);
     rdiv_object(i);
     rmod_number(i);
     rmod_object(i);
+    rconcat_string(i);
+    rconcat_number(i);
 }
 
 // Test that we can refer multiple time to the same recover instruction, as well
 // as chaining recover instructions.
 
 function alignedAlloc($size, $alignment) {
     var $1 = $size + 4 | 0;
     var $2 = $alignment - 1 | 0;
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -6622,20 +6622,17 @@ GenerateFFIIonExit(ModuleCompiler &m, co
     masm.storePtr(ImmWord(uintptr_t(argc)), Address(StackPointer, argOffset));
     argOffset += sizeof(size_t);
 
     // 4. |this| value
     masm.storeValue(UndefinedValue(), Address(StackPointer, argOffset));
     argOffset += sizeof(Value);
 
     // 5. Fill the arguments
-    unsigned offsetToCallerStackArgs = masm.framePushed();
-#if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
-    offsetToCallerStackArgs += NativeFrameSize;
-#endif
+    unsigned offsetToCallerStackArgs = masm.framePushed() + NativeFrameSize;
     FillArgumentArray(m, exit.sig().args(), argOffset, offsetToCallerStackArgs, scratch);
     argOffset += exit.sig().args().length() * sizeof(Value);
     JS_ASSERT(argOffset == offsetToArgs + argBytes);
 
     // Get the pointer to the ion code
     Label done, oolConvert;
     Label *maybeDebugBreakpoint = nullptr;
 
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -5133,17 +5133,17 @@ bool
 CodeGenerator::emitConcat(LInstruction *lir, Register lhs, Register rhs, Register output)
 {
     OutOfLineCode *ool = oolCallVM(ConcatStringsInfo, lir, (ArgList(), lhs, rhs),
                                    StoreRegisterTo(output));
     if (!ool)
         return false;
 
     ExecutionMode mode = gen->info().executionMode();
-    JitCode *stringConcatStub = gen->compartment->jitCompartment()->stringConcatStub(mode);
+    JitCode *stringConcatStub = gen->compartment->jitCompartment()->stringConcatStubNoBarrier(mode);
     masm.call(stringConcatStub);
     masm.branchTestPtr(Assembler::Zero, output, output, ool->entry());
 
     masm.bind(ool->rejoin());
     return true;
 }
 
 bool
@@ -6800,16 +6800,20 @@ CodeGenerator::link(JSContext *cx, types
 
     // Make sure we don't segv while filling in the code, to avoid deadlocking
     // inside the signal handler.
     cx->runtime()->jitRuntime()->ensureIonCodeAccessible(cx->runtime());
 
     // Implicit interrupts are used only for sequential code. In parallel mode
     // use the normal executable allocator so that we cannot segv during
     // execution off the main thread.
+    //
+    // Also, note that creating the code here during an incremental GC will
+    // trace the code and mark all GC things it refers to. This captures any
+    // read barriers which were skipped while compiling the script off thread.
     Linker linker(masm);
     AutoFlushICache afc("IonLink");
     JitCode *code = (executionMode == SequentialExecution)
                     ? linker.newCodeForIonScript(cx)
                     : linker.newCode<CanGC>(cx, JSC::ION_CODE);
     if (!code) {
         // Use js_free instead of IonScript::Destroy: the cache list and
         // backedge list are still uninitialized.
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -491,24 +491,24 @@ JitCompartment::initialize(JSContext *cx
 
     return true;
 }
 
 bool
 JitCompartment::ensureIonStubsExist(JSContext *cx)
 {
     if (!stringConcatStub_) {
-        stringConcatStub_.set(generateStringConcatStub(cx, SequentialExecution));
+        stringConcatStub_ = generateStringConcatStub(cx, SequentialExecution);
         if (!stringConcatStub_)
             return false;
     }
 
 #ifdef JS_THREADSAFE
     if (!parallelStringConcatStub_) {
-        parallelStringConcatStub_.set(generateStringConcatStub(cx, ParallelExecution));
+        parallelStringConcatStub_ = generateStringConcatStub(cx, ParallelExecution);
         if (!parallelStringConcatStub_)
             return false;
     }
 #endif
 
     return true;
 }
 
@@ -586,23 +586,16 @@ JitRuntime::Mark(JSTracer *trc)
         JitCode *code = i.get<JitCode>();
         MarkJitCodeRoot(trc, &code, "wrapper");
     }
 }
 
 void
 JitCompartment::mark(JSTracer *trc, JSCompartment *compartment)
 {
-    // Cancel any active or pending off thread compilations. Note that the
-    // MIR graph does not hold any nursery pointers, so there's no need to
-    // do this for minor GCs.
-    JS_ASSERT(!trc->runtime()->isHeapMinorCollecting());
-    CancelOffThreadIonCompile(compartment, nullptr);
-    FinishAllOffThreadCompilations(compartment);
-
     // Free temporary OSR buffer.
     trc->runtime()->jitRuntime()->freeOsrTempData();
 
     // Mark scripts with parallel IonScripts if we should preserve them.
     if (activeParallelEntryScripts_) {
         for (ScriptSet::Enum e(*activeParallelEntryScripts_); !e.empty(); e.popFront()) {
             JSScript *script = e.front();
 
@@ -626,34 +619,41 @@ JitCompartment::mark(JSTracer *trc, JSCo
                 MarkScript(trc, const_cast<PreBarrieredScript *>(&e.front()), "par-script");
                 MOZ_ASSERT(script == e.front());
             }
         }
     }
 }
 
 void
-JitCompartment::sweep(FreeOp *fop)
+JitCompartment::sweep(FreeOp *fop, JSCompartment *compartment)
 {
+    // Cancel any active or pending off thread compilations. Note that the
+    // MIR graph does not hold any nursery pointers, so there's no need to
+    // do this for minor GCs.
+    JS_ASSERT(!fop->runtime()->isHeapMinorCollecting());
+    CancelOffThreadIonCompile(compartment, nullptr);
+    FinishAllOffThreadCompilations(compartment);
+
     stubCodes_->sweep(fop);
 
     // If the sweep removed the ICCall_Fallback stub, nullptr the baselineCallReturnAddr_ field.
     if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::Call_Fallback)))
         baselineCallReturnAddr_ = nullptr;
     // Similarly for the ICGetProp_Fallback stub.
     if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::GetProp_Fallback)))
         baselineGetPropReturnAddr_ = nullptr;
     if (!stubCodes_->lookup(static_cast<uint32_t>(ICStub::SetProp_Fallback)))
         baselineSetPropReturnAddr_ = nullptr;
 
-    if (stringConcatStub_ && !IsJitCodeMarked(stringConcatStub_.unsafeGet()))
-        stringConcatStub_.set(nullptr);
-
-    if (parallelStringConcatStub_ && !IsJitCodeMarked(parallelStringConcatStub_.unsafeGet()))
-        parallelStringConcatStub_.set(nullptr);
+    if (stringConcatStub_ && !IsJitCodeMarked(&stringConcatStub_))
+        stringConcatStub_ = nullptr;
+
+    if (parallelStringConcatStub_ && !IsJitCodeMarked(&parallelStringConcatStub_))
+        parallelStringConcatStub_ = nullptr;
 
     if (activeParallelEntryScripts_) {
         for (ScriptSet::Enum e(*activeParallelEntryScripts_); !e.empty(); e.popFront()) {
             JSScript *script = e.front();
             if (!IsScriptMarked(&script))
                 e.removeFront();
             else
                 MOZ_ASSERT(script == e.front());
@@ -1668,16 +1668,19 @@ GenerateCode(MIRGenerator *mir, LIRGraph
     }
 
     return codegen;
 }
 
 CodeGenerator *
 CompileBackEnd(MIRGenerator *mir)
 {
+    // Everything in CompileBackEnd can potentially run on a helper thread.
+    AutoEnterIonCompilation enter;
+
     if (!OptimizeMIR(mir))
         return nullptr;
 
     LIRGraph *lir = GenerateLIR(mir);
     if (!lir)
         return nullptr;
 
     return GenerateCode(mir, lir);
@@ -1750,24 +1753,22 @@ AttachFinishedCompilations(JSContext *cx
 
 static const size_t BUILDER_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12;
 
 static inline bool
 OffThreadCompilationAvailable(JSContext *cx)
 {
 #ifdef JS_THREADSAFE
     // Even if off thread compilation is enabled, compilation must still occur
-    // on the main thread in some cases. Do not compile off thread during an
-    // incremental GC, as this may trip incremental read barriers.
+    // on the main thread in some cases.
     //
     // Require cpuCount > 1 so that Ion compilation jobs and main-thread
     // execution are not competing for the same resources.
     return cx->runtime()->canUseParallelIonCompilation()
-        && HelperThreadState().cpuCount > 1
-        && cx->runtime()->gc.incrementalState == gc::NO_INCREMENTAL;
+        && HelperThreadState().cpuCount > 1;
 #else
     return false;
 #endif
 }
 
 static void
 TrackAllProperties(JSContext *cx, JSObject *obj)
 {
@@ -1984,33 +1985,17 @@ CheckScriptSize(JSContext *cx, JSScript*
         return Method_CantCompile;
     }
 
     uint32_t numLocalsAndArgs = NumLocalsAndArgs(script);
 
     if (script->length() > MAX_MAIN_THREAD_SCRIPT_SIZE ||
         numLocalsAndArgs > MAX_MAIN_THREAD_LOCALS_AND_ARGS)
     {
-#ifdef JS_THREADSAFE
-        size_t cpuCount = HelperThreadState().cpuCount;
-#else
-        size_t cpuCount = 1;
-#endif
-        if (cx->runtime()->canUseParallelIonCompilation() && cpuCount > 1) {
-            // Even if off thread compilation is enabled, there are cases where
-            // compilation must still occur on the main thread. Don't compile
-            // in these cases, but do not forbid compilation so that the script
-            // may be compiled later.
-            if (!OffThreadCompilationAvailable(cx)) {
-                IonSpew(IonSpew_Abort,
-                        "Script too large for main thread, skipping (%u bytes) (%u locals/args)",
-                        script->length(), numLocalsAndArgs);
-                return Method_Skipped;
-            }
-        } else {
+        if (!OffThreadCompilationAvailable(cx)) {
             IonSpew(IonSpew_Abort, "Script too large (%u bytes) (%u locals/args)",
                     script->length(), numLocalsAndArgs);
             return Method_CantCompile;
         }
     }
 
     return Method_Compiled;
 }
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -1955,16 +1955,19 @@ GenerateSetSlot(JSContext *cx, MacroAsse
                        ImmGCPtr(type), &failures);
 
         if (checkTypeset) {
             TypedOrValueRegister valReg = value.reg();
             types::HeapTypeSet *propTypes = type->maybeGetProperty(shape->propid());
             JS_ASSERT(propTypes);
             JS_ASSERT(!propTypes->unknown());
 
+            // guardTypeSet can read from type sets without triggering read barriers.
+            types::TypeSet::readBarrier(propTypes);
+
             Register scratchReg = object;
             masm.push(scratchReg);
 
             masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &barrierFailure);
             masm.pop(object);
         }
     }
 
@@ -2510,16 +2513,19 @@ GenerateAddSlot(JSContext *cx, MacroAsse
     // if a type barrier is required.
     if (checkTypeset) {
         TypedOrValueRegister valReg = value.reg();
         types::TypeObject *type = obj->type();
         types::HeapTypeSet *propTypes = type->maybeGetProperty(obj->lastProperty()->propid());
         JS_ASSERT(propTypes);
         JS_ASSERT(!propTypes->unknown());
 
+        // guardTypeSet can read from type sets without triggering read barriers.
+        types::TypeSet::readBarrier(propTypes);
+
         Register scratchReg = object;
         masm.guardTypeSet(valReg, propTypes, BarrierKind::TypeSet, scratchReg, &failuresPopObject);
         masm.loadPtr(Address(StackPointer, 0), object);
     }
 
     JSObject *proto = obj->getProto();
     Register protoReg = object;
     while (proto) {
--- a/js/src/jit/IonMacroAssembler.cpp
+++ b/js/src/jit/IonMacroAssembler.cpp
@@ -49,24 +49,24 @@ class TypeWrapper {
             return t == t_ || t_ == types::Type::DoubleType();
         return t == t_;
     }
     inline unsigned getObjectCount() const {
         if (t_.isAnyObject() || t_.isUnknown() || !t_.isObject())
             return 0;
         return 1;
     }
-    inline JSObject *getSingleObject(unsigned) const {
+    inline JSObject *getSingleObjectNoBarrier(unsigned) const {
         if (t_.isSingleObject())
-            return t_.singleObject();
+            return t_.singleObjectNoBarrier();
         return nullptr;
     }
-    inline types::TypeObject *getTypeObject(unsigned) const {
+    inline types::TypeObject *getTypeObjectNoBarrier(unsigned) const {
         if (t_.isTypeObject())
-            return t_.typeObject();
+            return t_.typeObjectNoBarrier();
         return nullptr;
     }
 };
 
 } /* anonymous namespace */
 
 template <typename Source, typename TypeSet> void
 MacroAssembler::guardTypeSet(const Source &address, const TypeSet *types, BarrierKind kind,
@@ -139,32 +139,38 @@ template <typename TypeSet> void
 MacroAssembler::guardObjectType(Register obj, const TypeSet *types,
                                 Register scratch, Label *miss)
 {
     JS_ASSERT(!types->unknown());
     JS_ASSERT(!types->hasType(types::Type::AnyObjectType()));
     JS_ASSERT(types->getObjectCount());
     JS_ASSERT(scratch != InvalidReg);
 
+    // Note: this method elides read barriers on values read from type sets, as
+    // this may be called off the main thread during Ion compilation. This is
+    // safe to do as the final JitCode object will be allocated during the
+    // incremental GC (or the compilation canceled before we start sweeping),
+    // see CodeGenerator::link. Other callers should use TypeSet::readBarrier
+    // to trigger the barrier on the contents of type sets passed in here.
     Label matched;
 
     BranchGCPtr lastBranch;
     JS_ASSERT(!lastBranch.isInitialized());
     bool hasTypeObjects = false;
     unsigned count = types->getObjectCount();
     for (unsigned i = 0; i < count; i++) {
-        if (!types->getSingleObject(i)) {
-            hasTypeObjects = hasTypeObjects || types->getTypeObject(i);
+        if (!types->getSingleObjectNoBarrier(i)) {
+            hasTypeObjects = hasTypeObjects || types->getTypeObjectNoBarrier(i);
             continue;
         }
 
         if (lastBranch.isInitialized())
             lastBranch.emit(*this);
 
-        JSObject *object = types->getSingleObject(i);
+        JSObject *object = types->getSingleObjectNoBarrier(i);
         lastBranch = BranchGCPtr(Equal, obj, ImmGCPtr(object), &matched);
     }
 
     if (hasTypeObjects) {
         // We are possibly going to overwrite the obj register. So already
         // emit the branch, since branch depends on previous value of obj
         // register and there is definitely a branch following. So no need
         // to invert the condition.
@@ -172,23 +178,23 @@ MacroAssembler::guardObjectType(Register
             lastBranch.emit(*this);
         lastBranch = BranchGCPtr();
 
         // Note: Some platforms give the same register for obj and scratch.
         // Make sure when writing to scratch, the obj register isn't used anymore!
         loadPtr(Address(obj, JSObject::offsetOfType()), scratch);
 
         for (unsigned i = 0; i < count; i++) {
-            if (!types->getTypeObject(i))
+            if (!types->getTypeObjectNoBarrier(i))
                 continue;
 
             if (lastBranch.isInitialized())
                 lastBranch.emit(*this);
 
-            types::TypeObject *object = types->getTypeObject(i);
+            types::TypeObject *object = types->getTypeObjectNoBarrier(i);
             lastBranch = BranchGCPtr(Equal, scratch, ImmGCPtr(object), &matched);
         }
     }
 
     if (!lastBranch.isInitialized()) {
         jump(miss);
         return;
     }
--- a/js/src/jit/JitCompartment.h
+++ b/js/src/jit/JitCompartment.h
@@ -400,20 +400,21 @@ class JitCompartment
     // Keep track of offset into various baseline stubs' code at return
     // point from called script.
     void *baselineCallReturnAddr_;
     void *baselineGetPropReturnAddr_;
     void *baselineSetPropReturnAddr_;
 
     // Stub to concatenate two strings inline. Note that it can't be
     // stored in JitRuntime because masm.newGCString bakes in zone-specific
-    // pointers. This has to be a weak pointer to avoid keeping the whole
-    // compartment alive.
-    ReadBarrieredJitCode stringConcatStub_;
-    ReadBarrieredJitCode parallelStringConcatStub_;
+    // pointers. These are weak pointers, but are not declared as ReadBarriered
+    // since they are only read from during Ion compilation, which may occur
+    // off thread and whose barriers are captured during CodeGenerator::link.
+    JitCode *stringConcatStub_;
+    JitCode *parallelStringConcatStub_;
 
     // Set of JSScripts invoked by ForkJoin (i.e. the entry script). These
     // scripts are marked if their respective parallel IonScripts' age is less
     // than a certain amount. See IonScript::parallelAge_.
     typedef HashSet<PreBarrieredScript> ScriptSet;
     ScriptSet *activeParallelEntryScripts_;
 
     JitCode *generateStringConcatStub(JSContext *cx, ExecutionMode mode);
@@ -469,19 +470,19 @@ class JitCompartment
     ~JitCompartment();
 
     bool initialize(JSContext *cx);
 
     // Initialize code stubs only used by Ion, not Baseline.
     bool ensureIonStubsExist(JSContext *cx);
 
     void mark(JSTracer *trc, JSCompartment *compartment);
-    void sweep(FreeOp *fop);
+    void sweep(FreeOp *fop, JSCompartment *compartment);
 
-    JitCode *stringConcatStub(ExecutionMode mode) const {
+    JitCode *stringConcatStubNoBarrier(ExecutionMode mode) const {
         switch (mode) {
           case SequentialExecution: return stringConcatStub_;
           case ParallelExecution:   return parallelStringConcatStub_;
           default:                  MOZ_ASSUME_UNREACHABLE("No such execution mode");
         }
     }
 };
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4636,16 +4636,22 @@ class MConcat
         return this;
     }
     bool congruentTo(const MDefinition *ins) const {
         return congruentIfOperandsEqual(ins);
     }
     AliasSet getAliasSet() const {
         return AliasSet::None();
     }
+
+    bool writeRecoverData(CompactBufferWriter &writer) const;
+    bool canRecoverOnBailout() const {
+        return true;
+    }
+
 };
 
 class MConcatPar
   : public MTernaryInstruction
 {
     MConcatPar(MDefinition *cx, MDefinition *left, MDefinition *right)
       : MTernaryInstruction(cx, left, right)
     {
--- a/js/src/jit/RangeAnalysis.cpp
+++ b/js/src/jit/RangeAnalysis.cpp
@@ -551,17 +551,17 @@ ExponentImpliedByDouble(double d)
     return uint16_t(Max(int_fast16_t(0), ExponentComponent(d)));
 }
 
 void
 Range::setDouble(double l, double h)
 {
     // Infer lower_, upper_, hasInt32LowerBound_, and hasInt32UpperBound_.
     if (l >= INT32_MIN && l <= INT32_MAX) {
-        lower_ = int32_t(floor(l));
+        lower_ = int32_t(::floor(l));
         hasInt32LowerBound_ = true;
     } else {
         lower_ = INT32_MIN;
         hasInt32LowerBound_ = false;
     }
     if (h >= INT32_MIN && h <= INT32_MAX) {
         upper_ = int32_t(ceil(h));
         hasInt32UpperBound_ = true;
@@ -949,16 +949,41 @@ Range::max(TempAllocator &alloc, const R
     return new(alloc) Range(Max(lhs->lower_, rhs->lower_),
                             lhs->hasInt32LowerBound_ || rhs->hasInt32LowerBound_,
                             Max(lhs->upper_, rhs->upper_),
                             lhs->hasInt32UpperBound_ && rhs->hasInt32UpperBound_,
                             lhs->canHaveFractionalPart_ || rhs->canHaveFractionalPart_,
                             Max(lhs->max_exponent_, rhs->max_exponent_));
 }
 
+Range *
+Range::floor(TempAllocator &alloc, const Range *op)
+{
+    Range *copy = new(alloc) Range(*op);
+    // Decrement lower bound of copy range if op have a factional part and lower
+    // bound is Int32 defined. Also we avoid to decrement when op have a
+    // fractional part but lower_ >= JSVAL_INT_MAX.
+    if (op->canHaveFractionalPart() && op->hasInt32LowerBound())
+        copy->setLowerInit(int64_t(copy->lower_) - 1);
+
+    // Also refine max_exponent_ because floor may have decremented int value
+    // If we've got int32 defined bounds, just deduce it using defined bounds.
+    // But, if we don't have those, value's max_exponent_ may have changed.
+    // Because we're looking to maintain an over estimation, if we can,
+    // we increment it.
+    if(copy->hasInt32Bounds())
+        copy->max_exponent_ = copy->exponentImpliedByInt32Bounds();
+    else if(copy->max_exponent_ < MaxFiniteExponent)
+        copy->max_exponent_++;
+
+    copy->canHaveFractionalPart_ = false;
+    copy->assertInvariants();
+    return copy;
+}
+
 bool
 Range::negativeZeroMul(const Range *lhs, const Range *rhs)
 {
     // The result can only be negative zero if both sides are finite and they
     // have differing signs.
     return (lhs->canBeFiniteNegative() && rhs->canBeFiniteNonNegative()) ||
            (rhs->canBeFiniteNegative() && lhs->canBeFiniteNonNegative());
 }
@@ -1174,19 +1199,17 @@ MAbs::computeRange(TempAllocator &alloc)
         next->wrapAroundToInt32();
     setRange(next);
 }
 
 void
 MFloor::computeRange(TempAllocator &alloc)
 {
     Range other(getOperand(0));
-    Range *copy = new(alloc) Range(other);
-    copy->resetFractionalPart();
-    setRange(copy);
+    setRange(Range::floor(alloc, &other));
 }
 
 void
 MMinMax::computeRange(TempAllocator &alloc)
 {
     if (specialization_ != MIRType_Int32 && specialization_ != MIRType_Double)
         return;
 
--- a/js/src/jit/RangeAnalysis.h
+++ b/js/src/jit/RangeAnalysis.h
@@ -409,16 +409,17 @@ class Range : public TempObject {
     static Range *rsh(TempAllocator &alloc, const Range *lhs, int32_t c);
     static Range *ursh(TempAllocator &alloc, const Range *lhs, int32_t c);
     static Range *lsh(TempAllocator &alloc, const Range *lhs, const Range *rhs);
     static Range *rsh(TempAllocator &alloc, const Range *lhs, const Range *rhs);
     static Range *ursh(TempAllocator &alloc, const Range *lhs, const Range *rhs);
     static Range *abs(TempAllocator &alloc, const Range *op);
     static Range *min(TempAllocator &alloc, const Range *lhs, const Range *rhs);
     static Range *max(TempAllocator &alloc, const Range *lhs, const Range *rhs);
+    static Range *floor(TempAllocator &alloc, const Range *op);
 
     static bool negativeZeroMul(const Range *lhs, const Range *rhs);
 
     bool isUnknownInt32() const {
         return isInt32() && lower() == INT32_MIN && upper() == INT32_MAX;
     }
 
     bool isUnknown() const {
@@ -582,19 +583,14 @@ class Range : public TempObject {
     }
 
     void setSymbolicLower(SymbolicBound *bound) {
         symbolicLower_ = bound;
     }
     void setSymbolicUpper(SymbolicBound *bound) {
         symbolicUpper_ = bound;
     }
-
-    void resetFractionalPart() {
-        canHaveFractionalPart_ = false;
-        optimize();
-    }
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_RangeAnalysis_h */
--- a/js/src/jit/Recover.cpp
+++ b/js/src/jit/Recover.cpp
@@ -360,16 +360,42 @@ RSub::recover(JSContext *cx, SnapshotIte
     if (isFloatOperation_ && !RoundFloat32(cx, result, &result))
         return false;
 
     iter.storeInstructionResult(result);
     return true;
 }
 
 bool
+MConcat::writeRecoverData(CompactBufferWriter &writer) const
+{
+    MOZ_ASSERT(canRecoverOnBailout());
+    writer.writeUnsigned(uint32_t(RInstruction::Recover_Concat));
+    return true;
+}
+
+RConcat::RConcat(CompactBufferReader &reader)
+{}
+
+bool
+RConcat::recover(JSContext *cx, SnapshotIterator &iter) const
+{
+    RootedValue lhs(cx, iter.read());
+    RootedValue rhs(cx, iter.read());
+    RootedValue result(cx);
+
+    MOZ_ASSERT(!lhs.isObject() && !rhs.isObject());
+    if (!js::AddValues(cx, &lhs, &rhs, &result))
+        return false;
+
+    iter.storeInstructionResult(result);
+    return true;
+}
+
+bool
 MMul::writeRecoverData(CompactBufferWriter &writer) const
 {
     MOZ_ASSERT(canRecoverOnBailout());
     writer.writeUnsigned(uint32_t(RInstruction::Recover_Mul));
     writer.writeByte(specialization_ == MIRType_Float32);
     return true;
 }
 
--- a/js/src/jit/Recover.h
+++ b/js/src/jit/Recover.h
@@ -24,16 +24,17 @@ namespace jit {
     _(Lsh)                                      \
     _(Rsh)                                      \
     _(Ursh)                                     \
     _(Add)                                      \
     _(Sub)                                      \
     _(Mul)                                      \
     _(Div)                                      \
     _(Mod)                                      \
+    _(Concat)                                   \
     _(NewObject)                                \
     _(NewDerivedTypedObject)
 
 class RResumePoint;
 class SnapshotIterator;
 
 class RInstruction
 {
@@ -237,16 +238,28 @@ class RDiv MOZ_FINAL : public RInstructi
 
     virtual uint32_t numOperands() const {
         return 2;
     }
 
     bool recover(JSContext *cx, SnapshotIterator &iter) const;
 };
 
+class RConcat MOZ_FINAL : public RInstruction
+{
+  public:
+    RINSTRUCTION_HEADER_(Concat)
+
+    virtual uint32_t numOperands() const {
+        return 2;
+    }
+
+    bool recover(JSContext *cx, SnapshotIterator &iter) const;
+};
+
 class RNewObject MOZ_FINAL : public RInstruction
 {
   private:
     bool templateObjectIsClassPrototype_;
 
   public:
     RINSTRUCTION_HEADER_(NewObject)
 
--- a/js/src/jscompartment.cpp
+++ b/js/src/jscompartment.cpp
@@ -607,17 +607,17 @@ JSCompartment::sweep(FreeOp *fop, bool r
         if (selfHostingScriptSource &&
             IsObjectAboutToBeFinalized((JSObject **) selfHostingScriptSource.unsafeGet()))
         {
             selfHostingScriptSource.set(nullptr);
         }
 
 #ifdef JS_ION
         if (jitCompartment_)
-            jitCompartment_->sweep(fop);
+            jitCompartment_->sweep(fop, this);
 #endif
 
         /*
          * JIT code increments activeUseCount for any RegExpShared used by jit
          * code for the lifetime of the JIT script. Thus, we must perform
          * sweeping after clearing jit code.
          */
         regExps.sweep(rt);
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -2140,22 +2140,27 @@ bool
 GCRuntime::triggerGC(JS::gcreason::Reason reason)
 {
     /* Wait till end of parallel section to trigger GC. */
     if (InParallelSection()) {
         ForkJoinContext::current()->requestGC(reason);
         return true;
     }
 
+    /*
+     * Don't trigger GCs if this is being called off the main thread from
+     * onTooMuchMalloc().
+     */
+    if (!CurrentThreadCanAccessRuntime(rt))
+        return false;
+
     /* Don't trigger GCs when allocating under the interrupt callback lock. */
     if (rt->currentThreadOwnsInterruptLock())
         return false;
 
-    JS_ASSERT(CurrentThreadCanAccessRuntime(rt));
-
     /* GC is already running. */
     if (rt->isHeapCollecting())
         return false;
 
     JS::PrepareForFullGC(rt);
     requestInterrupt(reason);
     return true;
 }
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -609,16 +609,32 @@ TypeSet::print()
         for (unsigned i = 0; i < count; i++) {
             TypeObjectKey *object = getObject(i);
             if (object)
                 fprintf(stderr, " %s", TypeString(Type::ObjectType(object)));
         }
     }
 }
 
+/* static */ void
+TypeSet::readBarrier(const TypeSet *types)
+{
+    if (types->unknownObject())
+        return;
+
+    for (unsigned i = 0; i < types->getObjectCount(); i++) {
+        if (TypeObjectKey *object = types->getObject(i)) {
+            if (object->isSingleObject())
+                (void) object->asSingleObject();
+            else
+                (void) object->asTypeObject();
+        }
+    }
+}
+
 bool
 TypeSet::clone(LifoAlloc *alloc, TemporaryTypeSet *result) const
 {
     JS_ASSERT(result->empty());
 
     unsigned objectCount = baseObjectCount();
     unsigned capacity = (objectCount >= 2) ? HashSetCapacity(objectCount) : 0;
 
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -272,24 +272,26 @@ class Type
 
     /* Accessors for JSObject types */
 
     bool isSingleObject() const {
         return isObject() && !!(data & 1);
     }
 
     inline JSObject *singleObject() const;
+    inline JSObject *singleObjectNoBarrier() const;
 
     /* Accessors for TypeObject types */
 
     bool isTypeObject() const {
         return isObject() && !(data & 1);
     }
 
     inline TypeObject *typeObject() const;
+    inline TypeObject *typeObjectNoBarrier() const;
 
     bool operator == (Type o) const { return data == o.data; }
     bool operator != (Type o) const { return data != o.data; }
 
     static inline Type UndefinedType() { return Type(JSVAL_TYPE_UNDEFINED); }
     static inline Type NullType()      { return Type(JSVAL_TYPE_NULL); }
     static inline Type BooleanType()   { return Type(JSVAL_TYPE_BOOLEAN); }
     static inline Type Int32Type()     { return Type(JSVAL_TYPE_INT32); }
@@ -573,16 +575,18 @@ class TypeSet
      * Iterate through the objects in this set. getObjectCount overapproximates
      * in the hash case (see SET_ARRAY_SIZE in jsinferinlines.h), and getObject
      * may return nullptr.
      */
     inline unsigned getObjectCount() const;
     inline TypeObjectKey *getObject(unsigned i) const;
     inline JSObject *getSingleObject(unsigned i) const;
     inline TypeObject *getTypeObject(unsigned i) const;
+    inline JSObject *getSingleObjectNoBarrier(unsigned i) const;
+    inline TypeObject *getTypeObjectNoBarrier(unsigned i) const;
 
     /* The Class of an object in this set. */
     inline const Class *getObjectClass(unsigned i) const;
 
     bool canSetDefinite(unsigned slot) {
         // Note: the cast is required to work around an MSVC issue.
         return (slot + 1) <= (unsigned(TYPE_FLAG_DEFINITE_MASK) >> TYPE_FLAG_DEFINITE_SHIFT);
     }
@@ -612,16 +616,19 @@ class TypeSet
 
     // Clone a type set into an arbitrary allocator.
     TemporaryTypeSet *clone(LifoAlloc *alloc) const;
     bool clone(LifoAlloc *alloc, TemporaryTypeSet *result) const;
 
     // Create a new TemporaryTypeSet where undefined and/or null has been filtered out.
     TemporaryTypeSet *filter(LifoAlloc *alloc, bool filterUndefined, bool filterNull) const;
 
+    // Trigger a read barrier on all the contents of a type set.
+    static void readBarrier(const TypeSet *types);
+
   protected:
     uint32_t baseObjectCount() const {
         return (flags & TYPE_FLAG_OBJECT_COUNT_MASK) >> TYPE_FLAG_OBJECT_COUNT_SHIFT;
     }
     inline void setBaseObjectCount(uint32_t count);
 
     void clearObjects();
 };
@@ -1355,24 +1362,21 @@ struct TypeObjectKey
 
     bool isTypeObject() {
         return (uintptr_t(this) & 1) == 0;
     }
     bool isSingleObject() {
         return (uintptr_t(this) & 1) != 0;
     }
 
-    TypeObject *asTypeObject() {
-        JS_ASSERT(isTypeObject());
-        return (TypeObject *) this;
-    }
-    JSObject *asSingleObject() {
-        JS_ASSERT(isSingleObject());
-        return (JSObject *) (uintptr_t(this) & ~1);
-    }
+    inline TypeObject *asTypeObject();
+    inline JSObject *asSingleObject();
+
+    inline TypeObject *asTypeObjectNoBarrier();
+    inline JSObject *asSingleObjectNoBarrier();
 
     const Class *clasp();
     TaggedProto proto();
     bool hasTenuredProto();
     JSObject *singleton();
     TypeNewScript *newScript();
 
     bool unknownProperties();
--- a/js/src/jsinferinlines.h
+++ b/js/src/jsinferinlines.h
@@ -71,16 +71,46 @@ RecompileInfo::shouldSweep(TypeZone &typ
     outputIndex = output->sweepIndex();
     return false;
 }
 
 /////////////////////////////////////////////////////////////////////
 // Types
 /////////////////////////////////////////////////////////////////////
 
+inline TypeObject *
+TypeObjectKey::asTypeObjectNoBarrier()
+{
+    JS_ASSERT(isTypeObject());
+    return (TypeObject *) this;
+}
+
+inline JSObject *
+TypeObjectKey::asSingleObjectNoBarrier()
+{
+    JS_ASSERT(isSingleObject());
+    return (JSObject *) (uintptr_t(this) & ~1);
+}
+
+inline TypeObject *
+TypeObjectKey::asTypeObject()
+{
+    TypeObject *res = asTypeObjectNoBarrier();
+    TypeObject::readBarrier(res);
+    return res;
+}
+
+inline JSObject *
+TypeObjectKey::asSingleObject()
+{
+    JSObject *res = asSingleObjectNoBarrier();
+    JSObject::readBarrier(res);
+    return res;
+}
+
 /* static */ inline Type
 Type::ObjectType(JSObject *obj)
 {
     if (obj->hasSingletonType())
         return Type(uintptr_t(obj) | 1);
     return Type(uintptr_t(obj->type()));
 }
 
@@ -978,37 +1008,41 @@ HashSetLookup(U **values, unsigned count
 
     return nullptr;
 }
 
 inline TypeObjectKey *
 Type::objectKey() const
 {
     JS_ASSERT(isObject());
-    if (isTypeObject())
-        TypeObject::readBarrier((TypeObject *) data);
-    else
-        JSObject::readBarrier((JSObject *) (data ^ 1));
     return (TypeObjectKey *) data;
 }
 
 inline JSObject *
 Type::singleObject() const
 {
-    JS_ASSERT(isSingleObject());
-    JSObject::readBarrier((JSObject *) (data ^ 1));
-    return (JSObject *) (data ^ 1);
+    return objectKey()->asSingleObject();
 }
 
 inline TypeObject *
 Type::typeObject() const
 {
-    JS_ASSERT(isTypeObject());
-    TypeObject::readBarrier((TypeObject *) data);
-    return (TypeObject *) data;
+    return objectKey()->asTypeObject();
+}
+
+inline JSObject *
+Type::singleObjectNoBarrier() const
+{
+    return objectKey()->asSingleObjectNoBarrier();
+}
+
+inline TypeObject *
+Type::typeObjectNoBarrier() const
+{
+    return objectKey()->asTypeObjectNoBarrier();
 }
 
 inline bool
 TypeSet::hasType(Type type) const
 {
     if (unknown())
         return true;
 
@@ -1104,16 +1138,30 @@ TypeSet::getSingleObject(unsigned i) con
 
 inline TypeObject *
 TypeSet::getTypeObject(unsigned i) const
 {
     TypeObjectKey *key = getObject(i);
     return (key && key->isTypeObject()) ? key->asTypeObject() : nullptr;
 }
 
+inline JSObject *
+TypeSet::getSingleObjectNoBarrier(unsigned i) const
+{
+    TypeObjectKey *key = getObject(i);
+    return (key && key->isSingleObject()) ? key->asSingleObjectNoBarrier() : nullptr;
+}
+
+inline TypeObject *
+TypeSet::getTypeObjectNoBarrier(unsigned i) const
+{
+    TypeObjectKey *key = getObject(i);
+    return (key && key->isTypeObject()) ? key->asTypeObjectNoBarrier() : nullptr;
+}
+
 inline const Class *
 TypeSet::getObjectClass(unsigned i) const
 {
     if (JSObject *object = getSingleObject(i))
         return object->getClass();
     if (TypeObject *object = getTypeObject(i))
         return object->clasp();
     return nullptr;
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -2449,19 +2449,26 @@ Proxy::set(JSContext *cx, HandleObject p
     Rooted<PropertyDescriptor> desc(cx);
     if (!Proxy::getPropertyDescriptor(cx, proxy, id, &desc))
         return false;
     if (desc.object() && desc.setter() && desc.setter() != JS_StrictPropertyStub)
         return CallSetter(cx, receiver, id, desc.setter(), desc.attributes(), strict, vp);
 
     // Ok. Either there was no pre-existing property, or it was a value prop
     // that we're going to shadow. Make a property descriptor and define it.
+    //
+    // Note that for pre-existing own value properties, we inherit the existing
+    // attributes, since we're really just changing the value and not trying to
+    // reconfigure the property.
     Rooted<PropertyDescriptor> newDesc(cx);
+    if (desc.object() == proxy)
+        newDesc.setAttributes(desc.attributes());
+    else
+        newDesc.setAttributes(JSPROP_ENUMERATE);
     newDesc.value().set(vp);
-    newDesc.setAttributes(JSPROP_ENUMERATE);
     return handler->defineProperty(cx, receiver, id, &newDesc);
 }
 
 bool
 Proxy::keys(JSContext *cx, HandleObject proxy, AutoIdVector &props)
 {
     JS_CHECK_RECURSION(cx, return false);
     BaseProxyHandler *handler = proxy->as<ProxyObject>().handler();
--- a/js/src/jsversion.h
+++ b/js/src/jsversion.h
@@ -20,16 +20,19 @@
 #define JS_HAS_FUN_EXPR_STMT    1       /* has function expression statement */
 #define JS_HAS_NO_SUCH_METHOD   1       /* has o.__noSuchMethod__ handler */
 #define JS_HAS_FOR_EACH_IN      1       /* has for each (lhs in iterable) */
 #define JS_HAS_GENERATORS       1       /* has yield in generator function */
 #define JS_HAS_BLOCK_SCOPE      1       /* has block scope via let/arraycomp */
 #define JS_HAS_DESTRUCTURING    2       /* has [a,b] = ... or {p:a,q:b} = ... */
 #define JS_HAS_GENERATOR_EXPRS  1       /* has (expr for (lhs in iterable)) */
 #define JS_HAS_EXPR_CLOSURES    1       /* has function (formals) listexpr */
+#ifdef NIGHTLY_BUILD
+#define JS_HAS_TEMPLATE_STRINGS 1       /* has template string support */
+#endif
 
 /* Support for JS_NewGlobalObject. */
 #define JS_HAS_NEW_GLOBAL_OBJECT        1
 
 /* Support for JS_MakeSystemObject. */
 #define JS_HAS_MAKE_SYSTEM_OBJECT       1
 
 /* Feature-test macro for evolving destructuring support. */
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TemplateStrings/browser.js
@@ -0,0 +1,2 @@
+
+
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TemplateStrings/noSubstTests.js
@@ -0,0 +1,142 @@
+// 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/.
+
+// This test case is weird in the sense the actual work happens at the eval
+// at the end. If template strings are not enabled, the test cases would throw
+// a syntax error and we'd have failure reported. To avoid that, the entire
+// test case is commented out and is part of a function. We use toString to
+// get the string version, obtain the actual lines to run, and then use eval to
+// do the actual evaluation.
+
+function testCaseFn() {
+/*
+function syntaxError (script) {
+    try {
+        Function(script);
+    } catch (e) {
+        if (e.name === "SyntaxError") {
+            return;
+        }
+    }
+    throw "Expected syntax error: " + script;
+}
+
+// TEST BEGIN
+
+
+// unterminated quasi literal
+syntaxError("`");
+syntaxError("`$");
+syntaxError("`${");
+syntaxError("`${}");
+syntaxError("`${1}");
+syntaxError("`${1 + 2}");
+
+
+// character escape sequence (single escape character)
+assertEq("\'", `\'`);
+assertEq("\"", `\"`);
+assertEq("\\", `\\`);
+assertEq("\b", `\b`);
+assertEq("\f", `\f`);
+assertEq("\n", `\n`);
+assertEq("\r", `\r`);
+assertEq("\t", `\t`);
+assertEq("\v", `\v`);
+assertEq("\r\n", `\r\n`);
+
+
+assertEq("\0", eval("`\\" + String.fromCharCode(0) + "`"));
+assertEq("$", `\$`);
+assertEq(".", `\.`);
+assertEq("A", `\A`);
+assertEq("a", `\a`);
+
+
+// digit escape sequence
+assertEq("\0", `\0`);
+syntaxError("`\\1`");
+syntaxError("`\\2`");
+syntaxError("`\\3`");
+syntaxError("`\\4`");
+syntaxError("`\\5`");
+syntaxError("`\\6`");
+syntaxError("`\\7`");
+syntaxError("`\\01`");
+syntaxError("`\\001`");
+syntaxError("`\\00`");
+
+// hex escape sequence
+syntaxError("`\\x`");
+syntaxError("`\\x0`");
+syntaxError("`\\x0Z`");
+syntaxError("`\\xZ`");
+
+assertEq("\0", `\x00`);
+assertEq("$", `\x24`);
+assertEq(".", `\x2E`);
+assertEq("A", `\x41`);
+assertEq("a", `\x61`);
+assertEq("AB", `\x41B`);
+assertEq(String.fromCharCode(0xFF), `\xFF`);
+
+
+// unicode escape sequence
+
+assertEq("\0", `\u0000`);
+assertEq("$", `\u0024`);
+assertEq(".", `\u002E`);
+assertEq("A", `\u0041`);
+assertEq("a", `\u0061`);
+assertEq("AB", `\u0041B`);
+assertEq(String.fromCharCode(0xFFFF), `\uFFFF`);
+
+
+// line continuation
+assertEq("", eval("`\\\n`"));
+assertEq("", eval("`\\\r`"));
+assertEq("", eval("`\\\u2028`"));
+assertEq("", eval("`\\\u2029`"));
+assertEq("\u2028", eval("`\u2028`"));
+assertEq("\u2029", eval("`\u2029`"));
+
+assertEq("a\nb", eval("`a\rb`"))
+assertEq("a\nb", eval("`a\r\nb`"))
+
+
+// source character
+for (var i = 0; i < 0xFF; ++i) {
+    var c = String.fromCharCode(i);
+    if (c == "`" || c == "\\" || c == "\r") continue;
+    assertEq(c, eval("`" + c + "`"));
+}
+
+assertEq("", ``);
+assertEq("`", `\``);
+assertEq("$", `$`);
+assertEq("$$", `$$`);
+assertEq("$$}", `$$}`);
+
+// multi-line
+assertEq(`hey
+there`, "hey\nthere");
+
+// differences between strings and template strings
+syntaxError("var obj = { `illegal`: 1}");
+
+// test for JSON.parse
+assertThrowsInstanceOf(() => JSON.parse('[1, `false`]'), SyntaxError);
+
+syntaxError('({get `name`() { return 10; }});');
+*/
+/*End func*/}
+
+var str = testCaseFn.toString().replace("/*","").replace("*/","");
+str = str.replace("function testCaseFn() {\n", "").replace("/*End func*/}","");
+var hasTemplateStrings = false;
+try { eval("``"); hasTemplateStrings = true; } catch (exc) { }
+if (hasTemplateStrings)
+    eval(str);
+
+reportCompare(0, 0, "ok");
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TemplateStrings/shell.js
@@ -0,0 +1,1 @@
+
--- a/js/src/tests/js1_8_5/extensions/reflect-parse.js
+++ b/js/src/tests/js1_8_5/extensions/reflect-parse.js
@@ -48,16 +48,17 @@ function dotExpr(obj, id) Pattern({ type
 function memExpr(obj, id) Pattern({ type: "MemberExpression", computed: true, object: obj, property: id })
 function forStmt(init, test, update, body) Pattern({ type: "ForStatement", init: init, test: test, update: update, body: body })
 function forOfStmt(lhs, rhs, body) Pattern({ type: "ForOfStatement", left: lhs, right: rhs, body: body })
 function forInStmt(lhs, rhs, body) Pattern({ type: "ForInStatement", left: lhs, right: rhs, body: body, each: false })
 function forEachInStmt(lhs, rhs, body) Pattern({ type: "ForInStatement", left: lhs, right: rhs, body: body, each: true })
 function breakStmt(lab) Pattern({ type: "BreakStatement", label: lab })
 function continueStmt(lab) Pattern({ type: "ContinueStatement", label: lab })
 function blockStmt(body) Pattern({ type: "BlockStatement", body: body })
+function literal(val) Pattern({ type: "Literal",  value: val })
 var emptyStmt = Pattern({ type: "EmptyStatement" })
 function ifStmt(test, cons, alt) Pattern({ type: "IfStatement", test: test, alternate: alt, consequent: cons })
 function labStmt(lab, stmt) Pattern({ type: "LabeledStatement", label: lab, body: stmt })
 function withStmt(obj, stmt) Pattern({ type: "WithStatement", object: obj, body: stmt })
 function whileStmt(test, stmt) Pattern({ type: "WhileStatement", test: test, body: stmt })
 function doStmt(stmt, test) Pattern({ type: "DoWhileStatement", test: test, body: stmt })
 function switchStmt(disc, cases) Pattern({ type: "SwitchStatement", discriminant: disc, cases: cases })
 function caseClause(test, stmts) Pattern({ type: "SwitchCase", test: test, consequent: stmts })
@@ -132,16 +133,20 @@ function assertLocalExpr(src, patt) {
 function assertLocalDecl(src, patt) {
     localPatt(patt).assert(Reflect.parse(localSrc(src)));
 }
 
 function assertGlobalStmt(src, patt, builder) {
     program([patt]).assert(Reflect.parse(src, {builder: builder}));
 }
 
+function assertStringExpr(src, patt) {
+    program([exprStmt(patt)]).assert(Reflect.parse(src));
+}
+
 function assertGlobalExpr(src, patt, builder) {
     program([exprStmt(patt)]).assert(Reflect.parse(src, {builder: builder}));
     //assertStmt(src, exprStmt(patt));
 }
 
 function assertGlobalDecl(src, patt) {
     program([patt]).assert(Reflect.parse(src));
 }
@@ -390,16 +395,23 @@ assertStmt("if (foo) throw 42; else true
 assertStmt("if (foo) { throw 1; throw 2; throw 3; }",
            ifStmt(ident("foo"),
                   blockStmt([throwStmt(lit(1)), throwStmt(lit(2)), throwStmt(lit(3))]),
                   null));
 assertStmt("if (foo) { throw 1; throw 2; throw 3; } else true;",
            ifStmt(ident("foo"),
                   blockStmt([throwStmt(lit(1)), throwStmt(lit(2)), throwStmt(lit(3))]),
                   exprStmt(lit(true))));
+var hasTemplateStrings = false;  try { eval("``"); hasTemplateStrings = true; } catch (exc) { }
+if (hasTemplateStrings == true) {
+    assertStringExpr("`hey there`", literal("hey there"));
+    assertStringExpr("`hey\nthere`", literal("hey\nthere"));
+}
+assertStringExpr("\"hey there\"", literal("hey there"));
+
 assertStmt("foo: for(;;) break foo;", labStmt(ident("foo"), forStmt(null, null, null, breakStmt(ident("foo")))));
 assertStmt("foo: for(;;) continue foo;", labStmt(ident("foo"), forStmt(null, null, null, continueStmt(ident("foo")))));
 assertStmt("with (obj) { }", withStmt(ident("obj"), blockStmt([])));
 assertStmt("with (obj) { obj; }", withStmt(ident("obj"), blockStmt([exprStmt(ident("obj"))])));
 assertStmt("while (foo) { }", whileStmt(ident("foo"), blockStmt([])));
 assertStmt("while (foo) { foo; }", whileStmt(ident("foo"), blockStmt([exprStmt(ident("foo"))])));
 assertStmt("do { } while (foo);", doStmt(blockStmt([]), ident("foo")));
 assertStmt("do { foo; } while (foo)", doStmt(blockStmt([exprStmt(ident("foo"))]), ident("foo")));
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -82,16 +82,19 @@ PerThreadData::PerThreadData(JSRuntime *
     asmJSActivationStack_(nullptr),
     autoFlushICache_(nullptr),
 #if defined(JS_ARM_SIMULATOR) || defined(JS_MIPS_SIMULATOR)
     simulator_(nullptr),
     simulatorStackLimit_(0),
 #endif
     dtoaState(nullptr),
     suppressGC(0),
+#ifdef DEBUG
+    ionCompiling(false),
+#endif
     activeCompilations(0)
 {}
 
 PerThreadData::~PerThreadData()
 {
     if (dtoaState)
         js_DestroyDtoaState(dtoaState);
 
@@ -705,18 +708,16 @@ void
 JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes)
 {
     gc.updateMallocCounter(zone, nbytes);
 }
 
 JS_FRIEND_API(void)
 JSRuntime::onTooMuchMalloc()
 {
-    if (!CurrentThreadCanAccessRuntime(this))
-        return;
     gc.onTooMuchMalloc();
 }
 
 JS_FRIEND_API(void *)
 JSRuntime::onOutOfMemory(void *p, size_t nbytes)
 {
     return onOutOfMemory(p, nbytes, nullptr);
 }
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -574,16 +574,21 @@ class PerThreadData : public PerThreadDa
      * to suppress GC when reporting an OOM (see js_ReportOutOfMemory) and in
      * debugging facilities that cannot tolerate a GC and would rather OOM
      * immediately, such as utilities exposed to GDB. Setting this flag is
      * extremely dangerous and should only be used when in an OOM situation or
      * in non-exposed debugging facilities.
      */
     int32_t suppressGC;
 
+#ifdef DEBUG
+    // Whether this thread is actively Ion compiling.
+    bool ionCompiling;
+#endif
+
     // Number of active bytecode compilation on this thread.
     unsigned activeCompilations;
 
     explicit PerThreadData(JSRuntime *runtime);
     ~PerThreadData();
 
     bool init();
 
@@ -1658,15 +1663,41 @@ class RuntimeAllocPolicy
     void *calloc_(size_t bytes) { return runtime->calloc_(bytes); }
     void *realloc_(void *p, size_t bytes) { return runtime->realloc_(p, bytes); }
     void free_(void *p) { js_free(p); }
     void reportAllocOverflow() const {}
 };
 
 extern const JSSecurityCallbacks NullSecurityCallbacks;
 
+// Debugging RAII class which marks the current thread as performing an Ion
+// compilation, for use by CurrentThreadCan{Read,Write}CompilationData
+class AutoEnterIonCompilation
+{
+  public:
+    AutoEnterIonCompilation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
+        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+
+#if defined(DEBUG) && defined(JS_THREADSAFE)
+        PerThreadData *pt = js::TlsPerThreadData.get();
+        JS_ASSERT(!pt->ionCompiling);
+        pt->ionCompiling = true;
+#endif
+    }
+
+    ~AutoEnterIonCompilation() {
+#if defined(DEBUG) && defined(JS_THREADSAFE)
+        PerThreadData *pt = js::TlsPerThreadData.get();
+        JS_ASSERT(pt->ionCompiling);
+        pt->ionCompiling = false;
+#endif
+    }
+
+    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+};
+
 } /* namespace js */
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #endif
 
 #endif /* vm_Runtime_h */
--- a/js/xpconnect/src/event_impl_gen.py
+++ b/js/xpconnect/src/event_impl_gen.py
@@ -178,17 +178,17 @@ def print_class_declaration(eventname, i
 
     for a in attributes:
         """xpidl methods take care of string member variables!"""
         firstCapName = firstCap(a.name)
         cleanNativeType = a.realtype.nativeType('in').strip('* ')
         if a.realtype.nativeType('in').count("nsAString"):
             continue
         elif a.realtype.nativeType('in').count("nsIVariant"):
-            fd.write("  JS::Value Get%s(JSContext* aCx, ErrorResult& aRv);\n\n" % firstCapName);
+            fd.write("  void Get%s(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv);\n\n" % firstCapName);
         elif a.realtype.nativeType('in').endswith('*'):
             fd.write("  already_AddRefed<%s> Get%s()\n" % (xpidl_to_native(cleanNativeType, conf), firstCapName))
             fd.write("  {\n");
             fd.write("    nsCOMPtr<%s> %s = do_QueryInterface(m%s);\n" % (xpidl_to_canonical(cleanNativeType, conf), a.name, firstCapName))
             fd.write("    return %s.forget().downcast<%s>();\n" % (a.name, xpidl_to_native(cleanNativeType, conf)))
             fd.write("  }\n\n");
         else:
             fd.write("  %s %s()\n" % (cleanNativeType, firstCapName))
@@ -300,25 +300,25 @@ def writeAttributeGetter(fd, classname, 
         fd.write("  a%s = m%s;\n" % (firstCap(a.name), firstCap(a.name)))
     elif a.realtype.nativeType('in').count("nsACString"):
         fd.write("  a%s = m%s;\n" % (firstCap(a.name), firstCap(a.name)))
     else:
         fd.write("  *a%s = %s();\n" % (firstCap(a.name), firstCap(a.name)))
     fd.write("  return NS_OK;\n");
     fd.write("}\n\n");
     if a.realtype.nativeType('in').count("nsIVariant"):
-        fd.write("JS::Value\n")
-        fd.write("%s::Get%s(JSContext* aCx, ErrorResult& aRv)\n" % (classname, firstCap(a.name)))
+        fd.write("void\n")
+        fd.write("%s::Get%s(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval, ErrorResult& aRv)\n" % (classname, firstCap(a.name)))
         fd.write("{\n")
-        fd.write("  JS::Rooted<JS::Value> retVal(aCx, JS::NullValue());\n");
         fd.write("  nsresult rv = NS_ERROR_UNEXPECTED;\n")
-        fd.write("  if (m%s && !XPCVariant::VariantDataToJS(m%s, &rv, &retVal)) {\n" % (firstCap(a.name), firstCap(a.name)))
+        fd.write("  if (!m%s) {\n" % firstCap(a.name))
+        fd.write("    aRetval.setNull();\n")
+        fd.write("  } else if (!XPCVariant::VariantDataToJS(m%s, &rv, aRetval)) {\n" % (firstCap(a.name)))
         fd.write("    aRv.Throw(NS_ERROR_FAILURE);\n")
         fd.write("  }\n")
-        fd.write("  return retVal;\n");
         fd.write("}\n\n")
 
 def writeAttributeParams(fd, a):
     if a.realtype.nativeType('in').endswith('*'):
         fd.write(", %s* a%s" % (a.realtype.nativeType('in').strip('* '), firstCap(a.name)))
     elif a.realtype.nativeType('in').count("nsAString"):
         fd.write(", const nsAString& a%s" % firstCap(a.name))
     elif a.realtype.nativeType('in').count("nsACString"):
--- a/js/xpconnect/tests/chrome/test_xrayToJS.xul
+++ b/js/xpconnect/tests/chrome/test_xrayToJS.xul
@@ -110,16 +110,18 @@ https://bugzilla.mozilla.org/show_bug.cg
     is(global(iwin.eval(toEval)), iwin, "eval creates objects in the correct global");
     is(iwin.eval(toEval).b.foo, 'bar', "eval-ed object looks right");
     is(Cu.waiveXrays(iwin.eval(toEval)).f(), Cu.waiveXrays(iwin), "evaled function works right");
 
     testDate();
 
     testObject();
 
+    testArray();
+
     // We could also test DataView and Iterator here for completeness, but it's
     // more trouble than it's worth.
 
 
     SimpleTest.finish();
   }
 
   // Maintain a static list of the properties that are available on each standard
@@ -140,16 +142,22 @@ https://bugzilla.mozilla.org/show_bug.cg
     "toLocaleDateString", "toLocaleTimeString", "toDateString", "toTimeString",
     "toISOString", "toJSON", "toSource", "toString", "valueOf", "constructor",
     "toGMTString"];
   gPrototypeProperties['Object'] = /* __proto__ is intentionally excluded here, because
                                       the JS engine filters it out of getOwnPropertyNames */
     ["constructor", "toSource", "toString", "toLocaleString", "valueOf", "watch",
      "unwatch", "hasOwnProperty", "isPrototypeOf", "propertyIsEnumerable",
      "__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__"];
+  gPrototypeProperties['Array'] =
+    ["length", "toSource", "toString", "toLocaleString", "join", "reverse", "sort", "push",
+      "pop", "shift", "unshift", "splice", "concat", "slice", "lastIndexOf", "indexOf",
+      "forEach", "map", "reduce", "reduceRight", "filter", "some", "every", "mapPar",
+      "reducePar", "scanPar", "scatterPar", "filterPar", "find", "findIndex", "copyWithin",
+      "fill", "@@iterator", "entries", "keys", "constructor"];
 
   function filterOut(array, props) {
     return array.filter(p => props.indexOf(p) == -1);
   }
 
   function testXray(classname, xray, xray2, propsToSkip) {
     propsToSkip = propsToSkip || [];
     let xrayProto = Object.getPrototypeOf(xray);
@@ -220,24 +228,80 @@ https://bugzilla.mozilla.org/show_bug.cg
                    primitiveProp: 42, objectProp: { foo: 2 }, \
                    xoProp: top.location, hasOwnProperty: 10, \
                    get getterProp() { return 2; }, \
                    set setterProp(x) { }, \
                    get getterSetterProp() { return 3; }, \
                    set getterSetterProp(x) { }, \
                    callableProp: function() { }, \
                    nonXrayableProp: new WeakMap() })');
+    testTrickyObject(trickyObject);
+
+  }
+
+function testArray() {
+    // The |length| property is generally very weird, especially with respect
+    // to its behavior on the prototype. Array.prototype is actually an Array
+    // instance, and therefore has a vestigial .length. But we don't want to
+    // show that over Xrays, and generally want .length to just appear as an
+    // |own| data property. So we add it to the ignore list here, and check it
+    // separately.
+    let propsToSkip = ['length'];
+    testXray('Array', new iwin.Array(20), new iwin.Array(), propsToSkip);
+
+    var trickyArray =
+      iwin.eval("var trickyArray = []; \
+                 trickyArray.primitiveProp = 42; \
+                 trickyArray.objectProp = { foo: 2 }; \
+                 trickyArray.xoProp = top.location; \
+                 trickyArray.hasOwnProperty = 10; \
+                 Object.defineProperty(trickyArray, 'getterProp', { get: function() { return 2; }}); \
+                 Object.defineProperty(trickyArray, 'setterProp', { set: function(x) {}}); \
+                 Object.defineProperty(trickyArray, 'getterSetterProp', { get: function() { return 3; }, set: function(x) {}}); \
+                 trickyArray.callableProp = function() {}; \
+                 trickyArray.nonXrayableProp = new WeakMap(); \
+                 trickyArray;");
+
+    // Test indexed access.
+    trickyArray.wrappedJSObject[9] = "some indexed property";
+    is(trickyArray[9], "some indexed property", "indexed properties work correctly over Xrays");
+    is(trickyArray.length, 10, "Length works correctly over Xrays");
+    checkThrows(function() { "use strict"; delete trickyArray.length; }, /config/, "Can't delete non-configurable 'length' property");
+    delete trickyArray[9];
+    is(trickyArray[9], undefined, "Delete works correctly over Xrays");
+    is(trickyArray.wrappedJSObject[9], undefined, "Delete works correctly over Xrays (viewed via waiver)");
+    is(trickyArray.length, 10, "length doesn't change");
+    trickyArray[11] = "some other indexed property";
+    is(trickyArray.length, 12, "length now changes");
+    is(trickyArray.wrappedJSObject[11], "some other indexed property");
+    trickyArray.length = 0;
+    is(trickyArray.length, 0, "Setting length works over Xray");
+    is(trickyArray[11], undefined, "Setting length truncates over Xray");
+    Object.defineProperty(trickyArray, 'length', { configurable: false, enumerable: false, writable: false, value: 0 });
+    trickyArray[1] = "hi";
+    is(trickyArray.length, 0, "Length remains non-writable");
+    is(trickyArray[1], undefined, "Frozen length forbids new properties");
+
+    testTrickyObject(trickyArray);
+}
+
+// Parts of this function are kind of specific to testing Object, but we factor
+// it out so that we can re-use the trickyObject stuff on Arrays.
+function testTrickyObject(trickyObject) {
 
     // Make sure it looks right under the hood.
     is(trickyObject.wrappedJSObject.getterProp, 2, "Underlying object has getter");
     is(Cu.unwaiveXrays(trickyObject.wrappedJSObject.xoProp), top.location, "Underlying object has xo property");
 
     // Test getOwnPropertyNames.
+    var expectedNames = ['objectProp', 'primitiveProp'];
+    if (trickyObject instanceof iwin.Array)
+      expectedNames.push('length');
     is(Object.getOwnPropertyNames(trickyObject).sort().toSource(),
-       ['objectProp', 'primitiveProp'].toSource(), "getOwnPropertyNames should be filtered correctly");
+       expectedNames.sort().toSource(), "getOwnPropertyNames should be filtered correctly");
 
     // Test iteration and in-place modification. Beware of 'expando', which is the property
     // we placed on the xray proto.
     var propCount = 0;
     for (let prop in trickyObject) {
       if (prop == 'primitiveProp')
         trickyObject[prop] = trickyObject[prop] - 10;
       if (prop != 'expando')
--- a/js/xpconnect/wrappers/WrapperFactory.cpp
+++ b/js/xpconnect/wrappers/WrapperFactory.cpp
@@ -117,24 +117,26 @@ WrapperFactory::DoubleWrap(JSContext *cx
         JSAutoCompartment ac(cx, obj);
         return WaiveXray(cx, obj);
     }
     return obj;
 }
 
 // In general, we're trying to deprecate COWs incrementally as we introduce
 // Xrays to the corresponding object types. But switching off COWs for Object
-// instances would be too tumultuous at present, so we punt on that for later.
+// and Array instances would be too tumultuous at present, so we punt on that
+// for later.
 static bool
 ForceCOWBehavior(JSObject *obj)
 {
-    if (IdentifyStandardInstanceOrPrototype(obj) == JSProto_Object) {
+    JSProtoKey key = IdentifyStandardInstanceOrPrototype(obj);
+    if (key == JSProto_Object || key == JSProto_Array) {
         MOZ_ASSERT(GetXrayType(obj) == XrayForJSObject,
-                   "We should use XrayWrappers for standard ES Object instances "
-                   "modulo this hack");
+                   "We should use XrayWrappers for standard ES Object and Array "
+                   "instances modulo this hack");
         return true;
     }
     return false;
 }
 
 JSObject *
 WrapperFactory::PrepareForWrapping(JSContext *cx, HandleObject scope,
                                    HandleObject objArg, unsigned flags)
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -41,16 +41,17 @@ using namespace XrayUtils;
 
 // Whitelist for the standard ES classes we can Xray to.
 static bool
 IsJSXraySupported(JSProtoKey key)
 {
     switch (key) {
       case JSProto_Date:
       case JSProto_Object:
+      case JSProto_Array:
         return true;
       default:
         return false;
     }
 }
 
 XrayType
 GetXrayType(JSObject *obj)
@@ -467,17 +468,17 @@ bool JSXrayTraits::getOwnPropertyFromTar
             // Note - We're going add Xrays for Arrays/TypedArrays soon in
             // bug 987163, so we don't want to cause unnecessary compat churn
             // by making xrayedObj.arrayProp stop working temporarily, and then
             // start working again. At the same time, this is an important check,
             // and this patch wouldn't be as useful without it. So we just
             // forcibly override the behavior here for Arrays until bug 987163
             // lands.
             JSProtoKey key = IdentifyStandardInstanceOrPrototype(propObj);
-            if (key != JSProto_Array && key != JSProto_Uint8ClampedArray &&
+            if (key != JSProto_Uint8ClampedArray &&
                 key != JSProto_Int8Array && key != JSProto_Uint8Array &&
                 key != JSProto_Int16Array && key != JSProto_Uint16Array &&
                 key != JSProto_Int32Array && key != JSProto_Uint32Array &&
                 key != JSProto_Float32Array && key != JSProto_Float64Array)
             {
                 return SilentFailure(cx, id, "Value not Xrayable");
             }
         }
@@ -514,20 +515,28 @@ JSXrayTraits::resolveOwnProperty(JSConte
     // Call the common code.
     bool ok = XrayTraits::resolveOwnProperty(cx, jsWrapper, wrapper, holder,
                                              id, desc);
     if (!ok || desc.object())
         return ok;
 
     RootedObject target(cx, getTargetObject(wrapper));
     if (!isPrototype(holder)) {
-        // For object instances, we expose some properties from the underlying
-        // object, but only after filtering them carefully.
+        // For Object and Array instances, we expose some properties from the
+        // underlying object, but only after filtering them carefully.
+        //
+        // Note that, as far as JS observables go, Arrays are just Objects with
+        // a different prototype and a magic (own, non-configurable) |.length| that
+        // serves as a non-tight upper bound on |own| indexed properties. So while
+        // it's tempting to try to impose some sort of structure on what Arrays
+        // "should" look like over Xrays, the underlying object is squishy enough
+        // that it makes sense to just treat them like Objects for Xray purposes.
         switch (getProtoKey(holder)) {
           case JSProto_Object:
+          case JSProto_Array:
             {
                 JSAutoCompartment ac(cx, target);
                 if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, desc))
                     return false;
             }
             return JS_WrapPropertyDescriptor(cx, desc);
 
           default:
@@ -664,18 +673,20 @@ JSXrayTraits::resolveOwnProperty(JSConte
 bool
 JSXrayTraits::delete_(JSContext *cx, HandleObject wrapper, HandleId id, bool *bp)
 {
     RootedObject holder(cx, ensureHolder(cx, wrapper));
 
     // If we're using Object Xrays, we allow callers to attempt to delete any
     // property from the underlying object that they are able to resolve. Note
     // that this deleting may fail if the property is non-configurable.
-    bool isObjectInstance = getProtoKey(holder) == JSProto_Object && !isPrototype(holder);
-    if (isObjectInstance) {
+    JSProtoKey key = getProtoKey(holder);
+    bool isObjectOrArrayInstance = (key == JSProto_Object || key == JSProto_Array) &&
+                                   !isPrototype(holder);
+    if (isObjectOrArrayInstance) {
         RootedObject target(cx, getTargetObject(wrapper));
         JSAutoCompartment ac(cx, target);
         Rooted<JSPropertyDescriptor> desc(cx);
         if (!getOwnPropertyFromTargetIfSafe(cx, target, wrapper, id, &desc))
             return false;
         if (desc.object())
             return JS_DeletePropertyById2(cx, target, id, bp);
     }
@@ -690,44 +701,46 @@ JSXrayTraits::defineProperty(JSContext *
                                bool *defined)
 {
     *defined = false;
     RootedObject holder(cx, ensureHolder(cx, wrapper));
     if (!holder)
         return false;
 
 
-    // Object instances are special. For that case, we forward property
+    // Object and Array instances are special. For those cases, we forward property
     // definitions to the underlying object if the following conditions are met:
     // * The property being defined is a value-prop.
     // * The property being defined is either a primitive or subsumed by the target.
     // * As seen from the Xray, any existing property that we would overwrite is an
     //   |own| value-prop.
     //
-    // To avoid confusion, we disallow expandos on Object instances, and
+    // To avoid confusion, we disallow expandos on Object and Array instances, and
     // therefore raise an exception here if the above conditions aren't met.
-    bool isObjectInstance = getProtoKey(holder) == JSProto_Object && !isPrototype(holder);
-    if (isObjectInstance) {
+    JSProtoKey key = getProtoKey(holder);
+    bool isObjectOrArrayInstance = (key == JSProto_Object || key == JSProto_Array) &&
+                                   !isPrototype(holder);
+    if (isObjectOrArrayInstance) {
         RootedObject target(cx, getTargetObject(wrapper));
         if (desc.hasGetterOrSetter()) {
-            JS_ReportError(cx, "Not allowed to define accessor property on [Object] XrayWrapper");
+            JS_ReportError(cx, "Not allowed to define accessor property on [Object] or [Array] XrayWrapper");
             return false;
         }
         if (desc.value().isObject() &&
             !AccessCheck::subsumes(target, js::UncheckedUnwrap(&desc.value().toObject())))
         {
-            JS_ReportError(cx, "Not allowed to define cross-origin object as property on [Object] XrayWrapper");
+            JS_ReportError(cx, "Not allowed to define cross-origin object as property on [Object] or [Array] XrayWrapper");
             return false;
         }
         if (existingDesc.hasGetterOrSetter()) {
-            JS_ReportError(cx, "Not allowed to overwrite accessor property on [Object] XrayWrapper");
+            JS_ReportError(cx, "Not allowed to overwrite accessor property on [Object] or [Array] XrayWrapper");
             return false;
         }
         if (existingDesc.object() && existingDesc.object() != wrapper) {
-            JS_ReportError(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] XrayWrapper");
+            JS_ReportError(cx, "Not allowed to shadow non-own Xray-resolved property on [Object] or [Array] XrayWrapper");
             return false;
         }
 
         JSAutoCompartment ac(cx, target);
         if (!JS_WrapPropertyDescriptor(cx, desc) ||
             !JS_DefinePropertyById(cx, target, id, desc.value(), desc.attributes(),
                                    JS_PropertyStub, JS_StrictPropertyStub))
         {
@@ -745,20 +758,21 @@ JSXrayTraits::enumerateNames(JSContext *
                              AutoIdVector &props)
 {
     RootedObject target(cx, getTargetObject(wrapper));
     RootedObject holder(cx, ensureHolder(cx, wrapper));
     if (!holder)
         return false;
 
     if (!isPrototype(holder)) {
-        // For object instances, we expose some properties from the underlying
+        // For Object and Array instances, we expose some properties from the underlying
         // object, but only after filtering them carefully.
         switch (getProtoKey(holder)) {
           case JSProto_Object:
+          case JSProto_Array:
             MOZ_ASSERT(props.empty());
             {
                 JSAutoCompartment ac(cx, target);
                 AutoIdVector targetProps(cx);
                 if (!js::GetPropertyNames(cx, target, flags | JSITER_OWNONLY, &targetProps))
                     return false;
                 // Loop over the properties, and only pass along the ones that
                 // we determine to be safe.
@@ -2309,18 +2323,32 @@ XrayWrapper<Base, Traits>::definePropert
         return false;
 
     // Note that the check here is intended to differentiate between own and
     // non-own properties, since the above lookup is not limited to own
     // properties. At present, this may not always do the right thing because
     // we often lie (sloppily) about where we found properties and set
     // desc.object() to |wrapper|. Once we fully fix our Xray prototype semantics,
     // this should work as intended.
-    if (existing_desc.object() == wrapper && existing_desc.isPermanent())
-        return true; // silently ignore attempt to overwrite native property
+    if (existing_desc.object() == wrapper && existing_desc.isPermanent()) {
+        // We have a non-configurable property. See if the caller is trying to
+        // re-configure it in any way other than making it non-writable.
+        if (existing_desc.hasGetterOrSetterObject() || desc.hasGetterOrSetterObject() ||
+            existing_desc.isEnumerable() != desc.isEnumerable() ||
+            (existing_desc.isReadonly() && !desc.isReadonly()))
+        {
+