merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 20 Jul 2015 11:36:42 +0200
changeset 253652 5df788c56ae77e1a2e906535c5ef26837c22f5a2
parent 253580 481da0cbe1e311c60bd820aca9a2b26b132821c8 (current diff)
parent 253651 fda440fa9aa160ec24feb8734da473700a79ea91 (diff)
child 253653 ad35e6706f9b203e45b4cf228c1385fcccd547da
child 253678 2d52988a5e8389863f20aa33b04f6d4499563ffe
child 253725 ed1bcfdd4a7c07224cc8f9d153488489c688936d
push id29072
push usercbook@mozilla.com
push dateMon, 20 Jul 2015 09:36:58 +0000
treeherdermozilla-central@5df788c56ae7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone42.0a1
first release with
nightly linux32
5df788c56ae7 / 42.0a1 / 20150720030213 / files
nightly linux64
5df788c56ae7 / 42.0a1 / 20150720030213 / files
nightly mac
5df788c56ae7 / 42.0a1 / 20150720030213 / files
nightly win32
5df788c56ae7 / 42.0a1 / 20150720030213 / files
nightly win64
5df788c56ae7 / 42.0a1 / 20150720030213 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
docshell/base/nsDocShell.cpp
dom/base/Console.cpp
dom/events/EventListenerManager.cpp
layout/base/nsPresShell.cpp
netwerk/base/nsIRedirectHistory.idl
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1178850 requires clobber for Android JNI header changes
+Bug 1123386 requires clobber for switching to use a new compiler
--- a/browser/config/tooltool-manifests/linux32/clang.manifest
+++ b/browser/config/tooltool-manifests/linux32/clang.manifest
@@ -1,12 +1,12 @@
 [
 {
-"clang_version": "r183744"
+"clang_version": "r241406"
 }, 
 {
-"size": 70206124, 
-"digest": "a6b8046bd9485f9387dcb1c14b8d442822f02b1caa61b653e8b6cfd96906deadfb4b29809f2cd2b71f919b321d97dd2ebec6020c15f6d485f1641c0f710a762f", 
+"size": 100307285, 
+"digest": "4d147d0072a928945fc1e938f39a5d0a9d3c676399c09e092c8750b2f973cdbbebda8d94d4d05805fae74a5c49c54263dc22b8b443c23c9a0ae830a261d3cf30", 
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2",
 "unpack": true,
 }
 ]
--- a/browser/config/tooltool-manifests/linux64/clang.manifest
+++ b/browser/config/tooltool-manifests/linux64/clang.manifest
@@ -1,12 +1,12 @@
 [
 {
-"clang_version": "r183744"
+"clang_version": "r241406"
 }, 
 {
-"size": 70350828, 
-"digest": "6cd04e8ec44c6fef159349c22bd0476891e4a2d46479f9586283eaf3305e42f79c720d40dfec0e78d8899c1651189b12e285de60862ffd0612b0dac7a0c336c6", 
+"size": 100307285, 
+"digest": "4d147d0072a928945fc1e938f39a5d0a9d3c676399c09e092c8750b2f973cdbbebda8d94d4d05805fae74a5c49c54263dc22b8b443c23c9a0ae830a261d3cf30", 
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2",
 "unpack": true
 }
 ]
--- a/browser/config/tooltool-manifests/macosx64/clang.manifest
+++ b/browser/config/tooltool-manifests/macosx64/clang.manifest
@@ -1,15 +1,15 @@
 [
 {
-"clang_version": "r183744"
+"clang_version": "r241406"
 }, 
 {
-"size": 59602619, 
-"digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b", 
+"size": 86465808,
+"digest": "947eaaf11ac8cbe12e11b48c8b052721e018d31fb8ce20f8bf14b117b6623c56513b1422d8d9c8011bc0b0b985ef74d8f181e7200c6d7a05d79a1bce0d75ddee",
 "algorithm": "sha512", 
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/caps/nsScriptSecurityManager.cpp
+++ b/caps/nsScriptSecurityManager.cpp
@@ -331,16 +331,36 @@ nsScriptSecurityManager::GetChannelResul
             prin.forget(aPrincipal);
             return NS_OK;
         }
 
         if (loadInfo->GetForceInheritPrincipal()) {
             NS_ADDREF(*aPrincipal = loadInfo->TriggeringPrincipal());
             return NS_OK;
         }
+
+        nsSecurityFlags securityFlags = loadInfo->GetSecurityMode();
+        if (securityFlags == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS ||
+            securityFlags == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS ||
+            securityFlags == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+
+            nsCOMPtr<nsIURI> uri;
+            nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(uri));
+            NS_ENSURE_SUCCESS(rv, rv);
+            nsCOMPtr<nsIPrincipal> triggeringPrincipal = loadInfo->TriggeringPrincipal();
+            bool inheritForAboutBlank = loadInfo->GetAboutBlankInherits();
+
+            if (nsContentUtils::ChannelShouldInheritPrincipal(triggeringPrincipal,
+                                                               uri,
+                                                               inheritForAboutBlank,
+                                                               false)) {
+                triggeringPrincipal.forget(aPrincipal);
+                return NS_OK;
+            }
+        }
     }
     return GetChannelURIPrincipal(aChannel, aPrincipal);
 }
 
 /* The principal of the URI that this channel is loading. This is never
  * affected by things like sandboxed loads, or loads where we forcefully
  * inherit the principal.  Think of this as the principal of the server
  * which this channel is loading from.  Most callers should use
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -9710,17 +9710,18 @@ nsDocShell::InternalLoad(nsIURI* aURI,
 
       if (targetDocShell == rootShell) {
         isTargetTopLevelDocShell = true;
       }
     }
   }
   if (IsFrame() && !isNewDocShell && !isTargetTopLevelDocShell) {
     NS_ASSERTION(requestingElement, "A frame but no DOM element!?");
-    contentType = nsIContentPolicy::TYPE_SUBDOCUMENT;
+    contentType = requestingElement->IsHTMLElement(nsGkAtoms::iframe) ?
+      nsIContentPolicy::TYPE_INTERNAL_IFRAME : nsIContentPolicy::TYPE_INTERNAL_FRAME;
   } else {
     contentType = nsIContentPolicy::TYPE_DOCUMENT;
   }
 
   nsISupports* context = requestingElement;
   if (!context) {
     context = ToSupports(mScriptGlobal);
   }
@@ -9831,23 +9832,19 @@ nsDocShell::InternalLoad(nsIURI* aURI,
       NS_ENSURE_TRUE(win, NS_ERROR_NOT_AVAILABLE);
 
       nsDependentString name(aWindowTarget);
       nsCOMPtr<nsIDOMWindow> newWin;
       nsAutoCString spec;
       if (aURI) {
         aURI->GetSpec(spec);
       }
-      nsAutoString features;
-      if (mInPrivateBrowsing) {
-        features.AssignLiteral("private");
-      }
       rv = win->OpenNoNavigate(NS_ConvertUTF8toUTF16(spec),
                                name,  // window name
-                               features,
+                               EmptyString(), // Features
                                getter_AddRefs(newWin));
 
       // In some cases the Open call doesn't actually result in a new
       // 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();
--- a/docshell/test/navigation/browser.ini
+++ b/docshell/test/navigation/browser.ini
@@ -4,8 +4,9 @@ support-files =
   bug343515_pg2.html
   bug343515_pg3.html
   bug343515_pg3_1.html
   bug343515_pg3_1_1.html
   bug343515_pg3_2.html
 
 [browser_bug343515.js]
 skip-if = e10s # Bug ?????? - test directly touches content (tries to QI the content window)
+[browser_test-content-chromeflags.js]
new file mode 100644
--- /dev/null
+++ b/docshell/test/navigation/browser_test-content-chromeflags.js
@@ -0,0 +1,45 @@
+const TEST_PAGE = `data:text/html,<html><body><a href="about:blank" target="_blank">Test</a></body></html>`;
+const CHROME_ALL = Ci.nsIWebBrowserChrome.CHROME_ALL;
+const CHROME_REMOTE_WINDOW = Ci.nsIWebBrowserChrome.CHROME_REMOTE_WINDOW;
+
+/**
+ * Tests that when we open new browser windows from content they
+ * get the full browser chrome.
+ */
+add_task(function* () {
+  // Make sure that the window.open call will open a new
+  // window instead of a new tab.
+  yield new Promise(resolve => {
+    SpecialPowers.pushPrefEnv({
+      "set": [
+        ["browser.link.open_newwindow", 2],
+      ]
+    }, resolve);
+  });
+
+  yield BrowserTestUtils.withNewTab({
+    gBrowser,
+    url: TEST_PAGE
+  }, function*(browser) {
+    let openedPromise = BrowserTestUtils.waitForNewWindow();
+    BrowserTestUtils.synthesizeMouse("a", 0, 0, {}, browser);
+    let win = yield openedPromise;
+
+    let chromeFlags = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIWebNavigation)
+                         .QueryInterface(Ci.nsIDocShellTreeItem)
+                         .treeOwner
+                         .QueryInterface(Ci.nsIInterfaceRequestor)
+                         .getInterface(Ci.nsIXULWindow)
+                         .chromeFlags;
+
+    // In the multi-process case, the new window will have the
+    // CHROME_REMOTE_WINDOW flag set.
+    const EXPECTED = gMultiProcessBrowser ? CHROME_ALL | CHROME_REMOTE_WINDOW
+                                          : CHROME_ALL;
+
+    is(chromeFlags, EXPECTED, "Window should have opened with all chrome");
+
+    BrowserTestUtils.closeWindow(win);
+  });
+});
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -516,33 +516,30 @@ AudioChannelService::Observe(nsISupports
     }
 
     RemoveChildStatus(childID);
   }
 
   return NS_OK;
 }
 
-struct RefreshAgentsVolumeData
-{
-  explicit RefreshAgentsVolumeData(nsPIDOMWindow* aWindow)
-    : mWindow(aWindow)
-  {}
-
-  nsPIDOMWindow* mWindow;
-  nsTArray<nsRefPtr<AudioChannelAgent>> mAgents;
-};
-
 void
 AudioChannelService::RefreshAgentsVolume(nsPIDOMWindow* aWindow)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
-  AudioChannelWindow* winData = GetWindowData(aWindow->WindowID());
+  nsCOMPtr<nsIDOMWindow> topWindow;
+  aWindow->GetScriptableTop(getter_AddRefs(topWindow));
+  nsCOMPtr<nsPIDOMWindow> pTopWindow = do_QueryInterface(topWindow);
+  if (!pTopWindow) {
+    return;
+  }
+
+  AudioChannelWindow* winData = GetWindowData(pTopWindow->WindowID());
   if (!winData) {
     return;
   }
 
   nsTObserverArray<AudioChannelAgent*>::ForwardIterator
     iter(winData->mAgents);
   while (iter.HasMore()) {
     iter.GetNext()->WindowVolumeChanged();
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/ConsoleBinding.h"
 
 #include "mozilla/dom/BlobBinding.h"
 #include "mozilla/dom/Exceptions.h"
 #include "mozilla/dom/File.h"
+#include "mozilla/dom/StructuredCloneHelper.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "mozilla/Maybe.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsDocument.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsGlobalWindow.h"
 #include "nsJSUtils.h"
 #include "nsPerformance.h"
@@ -69,97 +70,16 @@ ConsoleStructuredCloneData
 
 /**
  * Console API in workers uses the Structured Clone Algorithm to move any value
  * from the worker thread to the main-thread. Some object cannot be moved and,
  * in these cases, we convert them to strings.
  * It's not the best, but at least we are able to show something.
  */
 
-// This method is called by the Structured Clone Algorithm when some data has
-// to be read.
-static JSObject*
-ConsoleStructuredCloneCallbacksRead(JSContext* aCx,
-                                    JSStructuredCloneReader* /* unused */,
-                                    uint32_t aTag, uint32_t aIndex,
-                                    void* aClosure)
-{
-  AssertIsOnMainThread();
-  ConsoleStructuredCloneData* data =
-    static_cast<ConsoleStructuredCloneData*>(aClosure);
-  MOZ_ASSERT(data);
-
-  if (aTag == CONSOLE_TAG_BLOB) {
-    MOZ_ASSERT(data->mBlobs.Length() > aIndex);
-
-    JS::Rooted<JS::Value> val(aCx);
-    {
-      nsRefPtr<Blob> blob =
-        Blob::Create(data->mParent, data->mBlobs.ElementAt(aIndex));
-      if (!ToJSValue(aCx, blob, &val)) {
-        return nullptr;
-      }
-    }
-
-    return &val.toObject();
-  }
-
-  MOZ_CRASH("No other tags are supported.");
-  return nullptr;
-}
-
-// This method is called by the Structured Clone Algorithm when some data has
-// to be written.
-static bool
-ConsoleStructuredCloneCallbacksWrite(JSContext* aCx,
-                                     JSStructuredCloneWriter* aWriter,
-                                     JS::Handle<JSObject*> aObj,
-                                     void* aClosure)
-{
-  ConsoleStructuredCloneData* data =
-    static_cast<ConsoleStructuredCloneData*>(aClosure);
-  MOZ_ASSERT(data);
-
-  nsRefPtr<Blob> blob;
-  if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
-      blob->Impl()->MayBeClonedToOtherThreads()) {
-    if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB, data->mBlobs.Length())) {
-      return false;
-    }
-
-    data->mBlobs.AppendElement(blob->Impl());
-    return true;
-  }
-
-  JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
-  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
-  if (!jsString) {
-    return false;
-  }
-
-  if (!JS_WriteString(aWriter, jsString)) {
-    return false;
-  }
-
-  return true;
-}
-
-static void
-ConsoleStructuredCloneCallbacksError(JSContext* /* aCx */,
-                                     uint32_t /* aErrorId */)
-{
-  NS_WARNING("Failed to clone data for the Console API in workers.");
-}
-
-static const JSStructuredCloneCallbacks gConsoleCallbacks = {
-  ConsoleStructuredCloneCallbacksRead,
-  ConsoleStructuredCloneCallbacksWrite,
-  ConsoleStructuredCloneCallbacksError
-};
-
 class ConsoleCallData final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ConsoleCallData)
 
   ConsoleCallData()
     : mMethodName(Console::MethodLog)
     , mPrivate(false)
@@ -269,16 +189,17 @@ public:
   }
 
 private:
   JSContext* mCx;
 };
 
 class ConsoleRunnable : public nsRunnable
                       , public WorkerFeature
+                      , public StructuredCloneHelperInternal
 {
 public:
   explicit ConsoleRunnable(Console* aConsole)
     : mWorkerPrivate(GetCurrentThreadWorkerPrivate())
     , mConsole(aConsole)
   {
     MOZ_ASSERT(mWorkerPrivate);
   }
@@ -426,20 +347,77 @@ private:
 protected:
   virtual bool
   PreDispatch(JSContext* aCx) = 0;
 
   virtual void
   RunConsole(JSContext* aCx, nsPIDOMWindow* aOuterWindow,
              nsPIDOMWindow* aInnerWindow) = 0;
 
+  virtual JSObject* ReadCallback(JSContext* aCx,
+                                 JSStructuredCloneReader* aReader,
+                                 uint32_t aTag,
+                                 uint32_t aIndex) override
+  {
+    AssertIsOnMainThread();
+
+    if (aTag == CONSOLE_TAG_BLOB) {
+      MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex);
+
+      JS::Rooted<JS::Value> val(aCx);
+      {
+        nsRefPtr<Blob> blob =
+          Blob::Create(mClonedData.mParent, mClonedData.mBlobs.ElementAt(aIndex));
+        if (!ToJSValue(aCx, blob, &val)) {
+          return nullptr;
+        }
+      }
+
+      return &val.toObject();
+    }
+
+    MOZ_CRASH("No other tags are supported.");
+    return nullptr;
+  }
+
+  virtual bool WriteCallback(JSContext* aCx,
+                             JSStructuredCloneWriter* aWriter,
+                             JS::Handle<JSObject*> aObj) override
+  {
+    nsRefPtr<Blob> blob;
+    if (NS_SUCCEEDED(UNWRAP_OBJECT(Blob, aObj, blob)) &&
+        blob->Impl()->MayBeClonedToOtherThreads()) {
+      if (!JS_WriteUint32Pair(aWriter, CONSOLE_TAG_BLOB,
+                              mClonedData.mBlobs.Length())) {
+        return false;
+      }
+
+      mClonedData.mBlobs.AppendElement(blob->Impl());
+      return true;
+    }
+
+    JS::Rooted<JS::Value> value(aCx, JS::ObjectOrNullValue(aObj));
+    JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
+    if (!jsString) {
+      return false;
+    }
+
+    if (!JS_WriteString(aWriter, jsString)) {
+      return false;
+    }
+
+    return true;
+  }
+
   WorkerPrivate* mWorkerPrivate;
 
   // This must be released on the worker thread.
   nsRefPtr<Console> mConsole;
+
+  ConsoleStructuredCloneData mClonedData;
 };
 
 // This runnable appends a CallData object into the Console queue running on
 // the main-thread.
 class ConsoleCallDataRunnable final : public ConsoleRunnable
 {
 public:
   ConsoleCallDataRunnable(Console* aConsole,
@@ -494,17 +472,17 @@ private:
       arg = mCallData->mArguments[i];
       if (!JS_DefineElement(aCx, arguments, i, arg, JSPROP_ENUMERATE)) {
         return false;
       }
     }
 
     JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
 
-    if (!mArguments.write(aCx, value, &gConsoleCallbacks, &mData)) {
+    if (!Write(aCx, value)) {
       return false;
     }
 
     mCallData->CleanupJSObjects();
     return true;
   }
 
   void
@@ -532,32 +510,32 @@ private:
       } else {
         id = NS_LITERAL_STRING("Worker");
       }
 
       mCallData->SetIDs(frame.mFilename, id);
     }
 
     // Now we could have the correct window (if we are not window-less).
-    mData.mParent = aInnerWindow;
+    mClonedData.mParent = aInnerWindow;
 
     ProcessCallData(aCx);
     mCallData->CleanupJSObjects();
 
-    mData.mParent = nullptr;
+    mClonedData.mParent = nullptr;
   }
 
 private:
   void
   ProcessCallData(JSContext* aCx)
   {
     ClearException ce(aCx);
 
     JS::Rooted<JS::Value> argumentsValue(aCx);
-    if (!mArguments.read(aCx, &argumentsValue, &gConsoleCallbacks, &mData)) {
+    if (!Read(aCx, &argumentsValue)) {
       return;
     }
 
     MOZ_ASSERT(argumentsValue.isObject());
     JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
     MOZ_ASSERT(JS_IsArrayObject(aCx, argumentsObj));
 
     uint32_t length;
@@ -577,19 +555,16 @@ private:
 
     MOZ_ASSERT(mCallData->mArguments.Length() == length);
 
     mCallData->mGlobal = JS::CurrentGlobalOrNull(aCx);
     mConsole->ProcessCallData(mCallData);
   }
 
   nsRefPtr<ConsoleCallData> mCallData;
-
-  JSAutoStructuredCloneBuffer mArguments;
-  ConsoleStructuredCloneData mData;
 };
 
 // This runnable calls ProfileMethod() on the console on the main-thread.
 class ConsoleProfileRunnable final : public ConsoleRunnable
 {
 public:
   ConsoleProfileRunnable(Console* aConsole, const nsAString& aAction,
                          const Sequence<JS::Value>& aArguments)
@@ -624,36 +599,36 @@ private:
       arg = mArguments[i];
       if (!JS_DefineElement(aCx, arguments, i, arg, JSPROP_ENUMERATE)) {
         return false;
       }
     }
 
     JS::Rooted<JS::Value> value(aCx, JS::ObjectValue(*arguments));
 
-    if (!mBuffer.write(aCx, value, &gConsoleCallbacks, &mData)) {
+    if (!Write(aCx, value)) {
       return false;
     }
 
     mArguments.Clear();
     return true;
   }
 
   void
   RunConsole(JSContext* aCx, nsPIDOMWindow* aOuterWindow,
              nsPIDOMWindow* aInnerWindow) override
   {
     ClearException ce(aCx);
 
     // Now we could have the correct window (if we are not window-less).
-    mData.mParent = aInnerWindow;
+    mClonedData.mParent = aInnerWindow;
 
     JS::Rooted<JS::Value> argumentsValue(aCx);
-    bool ok = mBuffer.read(aCx, &argumentsValue, &gConsoleCallbacks, &mData);
-    mData.mParent = nullptr;
+    bool ok = Read(aCx, &argumentsValue);
+    mClonedData.mParent = nullptr;
 
     if (!ok) {
       return;
     }
 
     MOZ_ASSERT(argumentsValue.isObject());
     JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
     MOZ_ASSERT(JS_IsArrayObject(aCx, argumentsObj));
@@ -677,19 +652,16 @@ private:
       }
     }
 
     mConsole->ProfileMethod(aCx, mAction, arguments);
   }
 
   nsString mAction;
   Sequence<JS::Value> mArguments;
-
-  JSAutoStructuredCloneBuffer mBuffer;
-  ConsoleStructuredCloneData mData;
 };
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(Console)
 
 // We don't need to traverse/unlink mStorage and mSanbox because they are not
 // CCed objects and they are only used on the main thread, even when this
 // Console object is used on workers.
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -1054,20 +1054,22 @@ Element::CreateShadowRoot(ErrorResult& a
     if (shell) {
       shell->CreateFramesFor(destroyedFramesFor);
     }
   }
 
   return shadowRoot.forget();
 }
 
-NS_IMPL_CYCLE_COLLECTION(DestinationInsertionPointList, mParent, mDestinationPoints)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DestinationInsertionPointList, mParent,
+                                      mDestinationPoints)
 
 NS_INTERFACE_TABLE_HEAD(DestinationInsertionPointList)
-  NS_INTERFACE_TABLE(DestinationInsertionPointList, nsINodeList)
+  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
+  NS_INTERFACE_TABLE(DestinationInsertionPointList, nsINodeList, nsIDOMNodeList)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(DestinationInsertionPointList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DestinationInsertionPointList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DestinationInsertionPointList)
 
 DestinationInsertionPointList::DestinationInsertionPointList(Element* aElement)
   : mParent(aElement)
@@ -1673,17 +1675,17 @@ Element::UnbindFromTree(bool aDeep, bool
     if (IsFullScreenAncestor()) {
       // The element being removed is an ancestor of the full-screen element,
       // exit full-screen state.
       nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                       NS_LITERAL_CSTRING("DOM"), OwnerDoc(),
                                       nsContentUtils::eDOM_PROPERTIES,
                                       "RemovedFullScreenElement");
       // Fully exit full-screen.
-      nsIDocument::ExitFullscreen(OwnerDoc(), /* async */ false);
+      nsIDocument::ExitFullscreenInDocTree(OwnerDoc());
     }
     if (HasPointerLock()) {
       nsIDocument::UnlockPointer();
     }
     if (GetParent()) {
       nsINode* p = mParent;
       mParent = nullptr;
       NS_RELEASE(p);
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -1296,17 +1296,17 @@ private:
 };
 
 class DestinationInsertionPointList : public nsINodeList
 {
 public:
   explicit DestinationInsertionPointList(Element* aElement);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(DestinationInsertionPointList)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DestinationInsertionPointList)
 
   // nsIDOMNodeList
   NS_DECL_NSIDOMNODELIST
 
   // nsINodeList
   virtual nsIContent* Item(uint32_t aIndex) override;
   virtual int32_t IndexOf(nsIContent* aContent) override;
   virtual nsINode* GetParentObject() override { return mParent; }
new file mode 100644
--- /dev/null
+++ b/dom/base/StructuredCloneHelper.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 "StructuredCloneHelper.h"
+
+namespace mozilla {
+namespace dom {
+
+namespace {
+
+JSObject*
+StructuredCloneCallbacksRead(JSContext* aCx,
+                             JSStructuredCloneReader* aReader,
+                             uint32_t aTag, uint32_t aIndex,
+                             void* aClosure)
+{
+  StructuredCloneHelperInternal* helper =
+    static_cast<StructuredCloneHelperInternal*>(aClosure);
+  MOZ_ASSERT(helper);
+  return helper->ReadCallback(aCx, aReader, aTag, aIndex);
+}
+
+bool
+StructuredCloneCallbacksWrite(JSContext* aCx,
+                              JSStructuredCloneWriter* aWriter,
+                              JS::Handle<JSObject*> aObj,
+                              void* aClosure)
+{
+  StructuredCloneHelperInternal* helper =
+    static_cast<StructuredCloneHelperInternal*>(aClosure);
+  MOZ_ASSERT(helper);
+  return helper->WriteCallback(aCx, aWriter, aObj);
+}
+
+bool
+StructuredCloneCallbacksReadTransfer(JSContext* aCx,
+                                     JSStructuredCloneReader* aReader,
+                                     uint32_t aTag,
+                                     void* aContent,
+                                     uint64_t aExtraData,
+                                     void* aClosure,
+                                     JS::MutableHandleObject aReturnObject)
+{
+  StructuredCloneHelperInternal* helper =
+    static_cast<StructuredCloneHelperInternal*>(aClosure);
+  MOZ_ASSERT(helper);
+  return helper->ReadTransferCallback(aCx, aReader, aTag, aContent,
+                                      aExtraData, aReturnObject);
+}
+
+bool
+StructuredCloneCallbacksWriteTransfer(JSContext* aCx,
+                                      JS::Handle<JSObject*> aObj,
+                                      void* aClosure,
+                                      // Output:
+                                      uint32_t* aTag,
+                                      JS::TransferableOwnership* aOwnership,
+                                      void** aContent,
+                                      uint64_t* aExtraData)
+{
+  StructuredCloneHelperInternal* helper =
+    static_cast<StructuredCloneHelperInternal*>(aClosure);
+  MOZ_ASSERT(helper);
+  return helper->WriteTransferCallback(aCx, aObj, aTag, aOwnership, aContent,
+                                       aExtraData);
+}
+
+void
+StructuredCloneCallbacksFreeTransfer(uint32_t aTag,
+                                     JS::TransferableOwnership aOwnership,
+                                     void* aContent,
+                                     uint64_t aExtraData,
+                                     void* aClosure)
+{
+  StructuredCloneHelperInternal* helper =
+    static_cast<StructuredCloneHelperInternal*>(aClosure);
+  MOZ_ASSERT(helper);
+  return helper->FreeTransferCallback(aTag, aOwnership, aContent, aExtraData);
+}
+
+void
+StructuredCloneCallbacksError(JSContext* aCx,
+                              uint32_t aErrorId)
+{
+  NS_WARNING("Failed to clone data for the Console API in workers.");
+}
+
+const JSStructuredCloneCallbacks gCallbacks = {
+  StructuredCloneCallbacksRead,
+  StructuredCloneCallbacksWrite,
+  StructuredCloneCallbacksError,
+  StructuredCloneCallbacksReadTransfer,
+  StructuredCloneCallbacksWriteTransfer,
+  StructuredCloneCallbacksFreeTransfer
+};
+
+} // anonymous namespace
+
+bool
+StructuredCloneHelperInternal::Write(JSContext* aCx,
+                                     JS::Handle<JS::Value> aValue)
+{
+  MOZ_ASSERT(!mBuffer, "Double Write is not allowed");
+
+  mBuffer = new JSAutoStructuredCloneBuffer(&gCallbacks, this);
+  return mBuffer->write(aCx, aValue, &gCallbacks, this);
+}
+
+bool
+StructuredCloneHelperInternal::Read(JSContext* aCx,
+                                    JS::MutableHandle<JS::Value> aValue)
+{
+  MOZ_ASSERT(mBuffer, "Read() without Write() is not allowed.");
+
+  bool ok = mBuffer->read(aCx, aValue, &gCallbacks, this);
+  mBuffer = nullptr;
+  return ok;
+}
+
+bool
+StructuredCloneHelperInternal::ReadTransferCallback(JSContext* aCx,
+                                                    JSStructuredCloneReader* aReader,
+                                                    uint32_t aTag,
+                                                    void* aContent,
+                                                    uint64_t aExtraData,
+                                                    JS::MutableHandleObject aReturnObject)
+{
+  MOZ_CRASH("Nothing to read.");
+  return false;
+}
+
+
+bool
+StructuredCloneHelperInternal::WriteTransferCallback(JSContext* aCx,
+                                                     JS::Handle<JSObject*> aObj,
+                                                     uint32_t* aTag,
+                                                     JS::TransferableOwnership* aOwnership,
+                                                     void** aContent,
+                                                     uint64_t* aExtraData)
+{
+  // No transfers are supported by default.
+  return false;
+}
+
+void
+StructuredCloneHelperInternal::FreeTransferCallback(uint32_t aTag,
+                                                    JS::TransferableOwnership aOwnership,
+                                                    void* aContent,
+                                                    uint64_t aExtraData)
+{
+  MOZ_CRASH("Nothing to free.");
+}
+
+} // dom namespace
+} // mozilla namespace
new file mode 100644
--- /dev/null
+++ b/dom/base/StructuredCloneHelper.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_StructuredCloneHelper_h
+#define mozilla_dom_StructuredCloneHelper_h
+
+#include "js/StructuredClone.h"
+#include "nsAutoPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+class StructuredCloneHelperInternal
+{
+public:
+  // These methods should be implemented in order to clone data.
+  // Read more documentation in js/public/StructuredClone.h.
+
+  virtual JSObject* ReadCallback(JSContext* aCx,
+                                 JSStructuredCloneReader* aReader,
+                                 uint32_t aTag,
+                                 uint32_t aIndex) = 0;
+
+  virtual bool WriteCallback(JSContext* aCx,
+                             JSStructuredCloneWriter* aWriter,
+                             JS::Handle<JSObject*> aObj) = 0;
+
+  // If these 3 methods are not implement, transfering objects will not be
+  // allowed.
+
+  virtual bool
+  ReadTransferCallback(JSContext* aCx,
+                       JSStructuredCloneReader* aReader,
+                       uint32_t aTag,
+                       void* aContent,
+                       uint64_t aExtraData,
+                       JS::MutableHandleObject aReturnObject);
+
+  virtual bool
+  WriteTransferCallback(JSContext* aCx,
+                        JS::Handle<JSObject*> aObj,
+                        // Output:
+                        uint32_t* aTag,
+                        JS::TransferableOwnership* aOwnership,
+                        void** aContent,
+                        uint64_t* aExtraData);
+
+  virtual void
+  FreeTransferCallback(uint32_t aTag,
+                       JS::TransferableOwnership aOwnership,
+                       void* aContent,
+                       uint64_t aExtraData);
+
+  // These methods are what you should use.
+
+  bool Write(JSContext* aCx,
+             JS::Handle<JS::Value> aValue);
+
+  bool Write(JSContext* aCx,
+             JS::Handle<JS::Value> aValue,
+             JS::Handle<JS::Value> aTransfer);
+
+  bool Read(JSContext* aCx,
+            JS::MutableHandle<JS::Value> aValue);
+
+protected:
+  nsAutoPtr<JSAutoStructuredCloneBuffer> mBuffer;
+};
+
+} // dom namespace
+} // mozilla namespace
+
+#endif // mozilla_dom_StructuredCloneHelper_h
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -190,16 +190,17 @@ EXPORTS.mozilla.dom += [
     'PerformanceMeasure.h',
     'PerformanceResourceTiming.h',
     'ProcessGlobal.h',
     'ResponsiveImageSelector.h',
     'SameProcessMessageQueue.h',
     'ScreenOrientation.h',
     'ScriptSettings.h',
     'ShadowRoot.h',
+    'StructuredCloneHelper.h',
     'StructuredCloneTags.h',
     'StyleSheetList.h',
     'SubtleCrypto.h',
     'Text.h',
     'TreeWalker.h',
     'URL.h',
     'URLSearchParams.h',
     'WebSocket.h',
@@ -327,16 +328,17 @@ UNIFIED_SOURCES += [
     'PerformanceMeasure.cpp',
     'PerformanceResourceTiming.cpp',
     'PostMessageEvent.cpp',
     'ProcessGlobal.cpp',
     'ResponsiveImageSelector.cpp',
     'SameProcessMessageQueue.cpp',
     'ScriptSettings.cpp',
     'ShadowRoot.cpp',
+    'StructuredCloneHelper.cpp',
     'StyleSheetList.cpp',
     'SubtleCrypto.cpp',
     'Text.cpp',
     'TextInputProcessor.cpp',
     'ThirdPartyUtil.cpp',
     'TreeWalker.cpp',
     'URL.cpp',
     'URLSearchParams.cpp',
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -897,27 +897,27 @@ nsDOMClassInfo::AddProperty(nsIXPConnect
 {
   NS_WARNING("nsDOMClassInfo::AddProperty Don't call me!");
 
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
 nsDOMClassInfo::GetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                            JSObject *obj, jsid id, jsval *vp,
+                            JSObject *obj, jsid id, JS::Value *vp,
                             bool *_retval)
 {
   NS_WARNING("nsDOMClassInfo::GetProperty Don't call me!");
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMClassInfo::SetProperty(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                            JSObject *obj, jsid id, jsval *vp,
+                            JSObject *obj, jsid id, JS::Value *vp,
                             bool *_retval)
 {
   NS_WARNING("nsDOMClassInfo::SetProperty Don't call me!");
 
   return NS_ERROR_UNEXPECTED;
 }
 
 NS_IMETHODIMP
@@ -1334,17 +1334,17 @@ public:
 
   nsresult PreCreate(JSContext *cx, JSObject *globalObj, JSObject **parentObj);
 
   nsresult Construct(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
                      JS::Handle<JSObject*> obj, const JS::CallArgs &args,
                      bool *_retval);
 
   nsresult HasInstance(nsIXPConnectWrappedNative *wrapper, JSContext *cx,
-                       JS::Handle<JSObject*> obj, const jsval &val, bool *bp,
+                       JS::Handle<JSObject*> obj, const JS::Value &val, bool *bp,
                        bool *_retval);
 
   nsresult ResolveInterfaceConstants(JSContext *cx, JS::Handle<JSObject*> obj);
 
 private:
   const nsGlobalNameStruct *GetNameStruct()
   {
     if (!mClassName) {
@@ -1494,17 +1494,17 @@ nsDOMConstructor::Construct(nsIXPConnect
   }
 
   return BaseStubConstructor(mWeakOwner, name_struct, cx, obj, args);
 }
 
 nsresult
 nsDOMConstructor::HasInstance(nsIXPConnectWrappedNative *wrapper,
                               JSContext * cx, JS::Handle<JSObject*> obj,
-                              const jsval &v, bool *bp, bool *_retval)
+                              const JS::Value &v, bool *bp, bool *_retval)
 
 {
   // No need to look these up in the hash.
   *bp = false;
   if (v.isPrimitive()) {
     return NS_OK;
   }
 
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -3207,17 +3207,17 @@ nsDOMWindowUtils::HandleFullscreenReques
 nsresult
 nsDOMWindowUtils::ExitFullscreen()
 {
   MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
 
   nsCOMPtr<nsIDocument> doc = GetDocument();
   NS_ENSURE_STATE(doc);
 
-  nsIDocument::ExitFullscreen(doc, /* async */ false);
+  nsIDocument::ExitFullscreenInDocTree(doc);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMWindowUtils::SelectAtPoint(float aX, float aY, uint32_t aSelectBehavior,
                                 bool *_retval)
 {
   *_retval = false;
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -9353,17 +9353,17 @@ nsDocument::OnPageHide(bool aPersisted,
     // doctree branch. This ensures that if the user navigates while in
     // fullscreen mode we don't leave its still visible ancestor documents
     // in fullscreen mode. So exit fullscreen in the document's fullscreen
     // root document, as this will exit fullscreen in all the root's
     // descendant documents. Note that documents are removed from the
     // doctree by the time OnPageHide() is called, so we must store a
     // reference to the root (in nsDocument::mFullscreenRoot) since we can't
     // just traverse the doctree to get the root.
-    nsIDocument::ExitFullscreen(this, /* async */ false);
+    nsIDocument::ExitFullscreenInDocTree(this);
 
     // Since the document is removed from the doctree before OnPageHide() is
     // called, ExitFullscreen() can't traverse from the root down to *this*
     // document, so we must manually call CleanupFullscreenState() below too.
     // Note that CleanupFullscreenState() clears nsDocument::mFullscreenRoot,
     // so we *must* call it after ExitFullscreen(), not before.
     // OnPageHide() is called in every hidden (i.e. descendant) document,
     // so calling CleanupFullscreenState() here will ensure all hidden
@@ -11117,41 +11117,53 @@ private:
 };
 
 static void
 SetWindowFullScreen(nsIDocument* aDoc, bool aValue, gfx::VRHMDInfo *aVRHMD = nullptr)
 {
   nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue, aVRHMD));
 }
 
-class nsCallExitFullscreen : public nsRunnable {
+static void
+AskWindowToExitFullscreen(nsIDocument* aDoc)
+{
+  if (XRE_GetProcessType() == GeckoProcessType_Content) {
+    nsContentUtils::DispatchEventOnlyToChrome(
+      aDoc, ToSupports(aDoc), NS_LITERAL_STRING("MozDOMFullscreen:Exit"),
+      /* Bubbles */ true, /* Cancelable */ false,
+      /* DefaultAction */ nullptr);
+  } else {
+    SetWindowFullScreen(aDoc, false);
+  }
+}
+
+class nsCallExitFullscreen : public nsRunnable
+{
 public:
   explicit nsCallExitFullscreen(nsIDocument* aDoc)
     : mDoc(aDoc) {}
-  NS_IMETHOD Run()
+
+  NS_IMETHOD Run() override final
   {
-    nsDocument::ExitFullscreen(mDoc);
+    if (!mDoc) {
+      FullscreenRoots::ForEach(&AskWindowToExitFullscreen);
+    } else {
+      AskWindowToExitFullscreen(mDoc);
+    }
     return NS_OK;
   }
+
 private:
   nsCOMPtr<nsIDocument> mDoc;
 };
 
-/* static */
-void
-nsIDocument::ExitFullscreen(nsIDocument* aDoc, bool aRunAsync)
-{
-  if (aDoc && !aDoc->IsFullScreenDoc()) {
-    return;
-  }
-  if (aRunAsync) {
-    NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc));
-    return;
-  }
-  nsDocument::ExitFullscreen(aDoc);
+/* static */ void
+nsIDocument::AsyncExitFullscreen(nsIDocument* aDoc)
+{
+  NS_DispatchToCurrentThread(new nsCallExitFullscreen(aDoc));
 }
 
 static bool
 CountFullscreenSubDocuments(nsIDocument* aDoc, void* aData)
 {
   if (aDoc->IsFullScreenDoc()) {
     uint32_t* count = static_cast<uint32_t*>(aData);
     (*count)++;
@@ -11188,26 +11200,26 @@ ResetFullScreen(nsIDocument* aDocument, 
     NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen");
     nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData);
     changed->AppendElement(aDocument);
     aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
   }
   return true;
 }
 
-static void
-ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
+/* static */ void
+nsIDocument::ExitFullscreenInDocTree(nsIDocument* aMaybeNotARootDoc)
 {
   MOZ_ASSERT(aMaybeNotARootDoc);
+
+  // Unlock the pointer
+  UnlockPointer();
+
   nsCOMPtr<nsIDocument> root = aMaybeNotARootDoc->GetFullscreenRoot();
-  NS_ASSERTION(root, "Should have root when in fullscreen!");
-  if (!root) {
-    return;
-  }
-  if (!root->IsFullScreenDoc()) {
+  if (!root || !root->IsFullScreenDoc()) {
     // If a document was detached before exiting from fullscreen, it is
     // possible that the root had left fullscreen state. In this case,
     // we would not get anything from the ResetFullScreen() call. Root's
     // not being a fullscreen doc also means the widget should have
     // exited fullscreen state. It means even if we do not return here,
     // we would actually do nothing below except crashing ourselves via
     // dispatching the "MozDOMFullscreen:Exited" event to an nonexistent
     // document.
@@ -11241,34 +11253,16 @@ ExitFullscreenInDocTree(nsIDocument* aMa
     changed.LastElement(), ToSupports(changed.LastElement()),
     NS_LITERAL_STRING("MozDOMFullscreen:Exited"),
     /* Bubbles */ true, /* Cancelable */ false, /* DefaultAction */ nullptr);
   // Move the top-level window out of fullscreen mode.
   FullscreenRoots::Remove(root);
   SetWindowFullScreen(root, false);
 }
 
-/* static */
-void
-nsDocument::ExitFullscreen(nsIDocument* aDoc)
-{
-  // Unlock the pointer
-  UnlockPointer();
-
-  if (aDoc)  {
-    ExitFullscreenInDocTree(aDoc);
-    return;
-  }
-
-  // Clear fullscreen stacks in all fullscreen roots' descendant documents.
-  FullscreenRoots::ForEach(&ExitFullscreenInDocTree);
-  NS_ASSERTION(FullscreenRoots::IsEmpty(),
-      "Should have exited all fullscreen roots from fullscreen");
-}
-
 bool
 GetFullscreenLeaf(nsIDocument* aDoc, void* aData)
 {
   if (aDoc->IsFullscreenLeaf()) {
     nsIDocument** result = static_cast<nsIDocument**>(aData);
     *result = aDoc;
     return false;
   } else if (aDoc->IsFullScreenDoc()) {
@@ -11313,24 +11307,17 @@ nsDocument::RestorePreviousFullScreenSta
     if (static_cast<nsDocument*>(doc)->mFullScreenStack.Length() > 1) {
       exitingFullscreen = false;
       break;
     }
   }
   if (exitingFullscreen) {
     // If we are fully exiting fullscreen, don't touch anything here,
     // just wait for the window to get out from fullscreen first.
-    if (XRE_GetProcessType() == GeckoProcessType_Content) {
-      nsContentUtils::DispatchEventOnlyToChrome(
-        this, ToSupports(this), NS_LITERAL_STRING("MozDOMFullscreen:Exit"),
-        /* Bubbles */ true, /* Cancelable */ false,
-        /* DefaultAction */ nullptr);
-    } else {
-      SetWindowFullScreen(this, false);
-    }
+    AskWindowToExitFullscreen(this);
     return;
   }
 
   // If fullscreen mode is updated the pointer should be unlocked
   UnlockPointer();
 
   nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
 
--- a/dom/base/nsDocument.h
+++ b/dom/base/nsDocument.h
@@ -1237,18 +1237,16 @@ public:
   // Returns strong references to mBlockedTrackingNodes. (nsIDocument.h)
   //
   // This array contains nodes that have been blocked to prevent
   // user tracking. They most likely have had their nsIChannel
   // canceled by the URL classifier (Safebrowsing).
   //
   already_AddRefed<nsSimpleContentList> BlockedTrackingNodes() const;
 
-  static void ExitFullscreen(nsIDocument* aDoc);
-
   // Do the "fullscreen element ready check" from the fullscreen spec.
   // It returns true if the given element is allowed to go into fullscreen.
   bool FullscreenElementReadyCheck(Element* aElement, bool aWasCallerChrome);
 
   // This is called asynchronously by nsIDocument::AsyncRequestFullScreen()
   // to move this document into full-screen mode if allowed.
   void RequestFullScreen(mozilla::UniquePtr<FullscreenRequest>&& aRequest);
 
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -1244,17 +1244,17 @@ nsFocusManager::SetFocusInner(nsIContent
   if (contentToFocus &&
       nsContentUtils::GetRootDocument(contentToFocus->OwnerDoc())->IsFullScreenDoc() &&
       nsContentUtils::HasPluginWithUncontrolledEventDispatch(contentToFocus)) {
     nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                     NS_LITERAL_CSTRING("DOM"),
                                     contentToFocus->OwnerDoc(),
                                     nsContentUtils::eDOM_PROPERTIES,
                                     "FocusedWindowedPluginWhileFullScreen");
-    nsIDocument::ExitFullscreen(contentToFocus->OwnerDoc(), /* async */ true);
+    nsIDocument::AsyncExitFullscreen(contentToFocus->OwnerDoc());
   }
 #endif
 
   // if the FLAG_NOSWITCHFRAME flag is used, only allow the focus to be
   // shifted away from the current element if the new shell to focus is
   // the same or an ancestor shell of the currently focused shell.
   bool allowFrameSwitch = !(aFlags & FLAG_NOSWITCHFRAME) ||
                             IsSameOrAncestor(newWindow, mFocusedWindow);
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -6303,21 +6303,19 @@ nsGlobalWindow::SetFullScreen(bool aFull
 
 void
 FinishDOMFullscreenChange(nsIDocument* aDoc, bool aInDOMFullscreen)
 {
   if (aInDOMFullscreen) {
     // Ask the document to handle any pending DOM fullscreen change.
     nsIDocument::HandlePendingFullscreenRequests(aDoc);
   } else {
-    // Force exit from DOM full-screen mode. This is so that if we're in
-    // DOM full-screen mode and the user exits full-screen mode with
-    // the browser full-screen mode toggle keyboard-shortcut, we'll detect
-    // that and leave DOM API full-screen mode too.
-    nsIDocument::ExitFullscreen(aDoc, /* async */ false);
+    // If the window is leaving fullscreen state, also ask the document
+    // to exit from DOM Fullscreen.
+    nsIDocument::ExitFullscreenInDocTree(aDoc);
   }
 }
 
 struct FullscreenTransitionDuration
 {
   // The unit of the durations is millisecond
   uint16_t mFadeIn = 0;
   uint16_t mFadeOut = 0;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -1160,34 +1160,39 @@ public:
    * Sets whether this document is approved for fullscreen mode.
    * Documents aren't approved for fullscreen until chrome has sent a
    * "fullscreen-approved" notification with a subject which is a pointer
    * to the approved document.
    */
   virtual void SetApprovedForFullscreen(bool aIsApproved) = 0;
 
   /**
-   * Exits documents out of DOM fullscreen mode.
+   * Synchronously cleans up the fullscreen state on the given document.
    *
-   * If aDocument is null, all fullscreen documents in all browser windows
-   * exit fullscreen.
-   *
-   * If aDocument is non null, all documents from aDocument's fullscreen root
-   * to the fullscreen leaf exit fullscreen.
+   * Calling this without performing fullscreen transition could lead
+   * to undesired effect (the transition happens after document state
+   * flips), hence it should only be called either by nsGlobalWindow
+   * when we have performed the transition, or when it is necessary to
+   * clean up the state immediately. Otherwise, AsyncExitFullscreen()
+   * should be called instead.
    *
-   * Note that the fullscreen leaf is the bottom-most document which is
-   * fullscreen, it may have non-fullscreen child documents. The fullscreen
-   * root is normally the chrome document.
+   * aDocument must not be null.
+   */
+  static void ExitFullscreenInDocTree(nsIDocument* aDocument);
+
+  /**
+   * Ask the document to exit fullscreen state asynchronously.
    *
-   * If aRunAsync is true, fullscreen is executed asynchronously.
+   * Different from ExitFullscreenInDocTree(), this allows the window
+   * to perform fullscreen transition first if any.
    *
-   * Note if aDocument is not fullscreen this function has no effect, even if
-   * aDocument has fullscreen ancestors.
+   * If aDocument is null, it will exit fullscreen from all documents
+   * in all windows.
    */
-  static void ExitFullscreen(nsIDocument* aDocument, bool aRunAsync);
+  static void AsyncExitFullscreen(nsIDocument* aDocument);
 
   /**
    * Handles one single fullscreen request, updates `aHandled` if the request
    * is handled, and returns whether this request should be removed from the
    * request queue.
    */
   static bool HandlePendingFullscreenRequest(const FullscreenRequest& aRequest,
                                              nsIDocShellTreeItem* aRootShell,
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -1507,16 +1507,25 @@ nsImageLoadingContent::TrackImage(imgIRe
     return;
 
   MOZ_ASSERT(aImage == mCurrentRequest || aImage == mPendingRequest,
              "Why haven't we heard of this request?");
 
   nsIDocument* doc = GetOurCurrentDoc();
   if (doc && (mFrameCreateCalled || GetOurPrimaryFrame()) &&
       (mVisibleCount > 0)) {
+
+    if (mVisibleCount == 1) {
+      // Since we're becoming visible, request a decode.
+      nsImageFrame* f = do_QueryFrame(GetOurPrimaryFrame());
+      if (f) {
+        f->MaybeDecodeForPredictedSize();
+      }
+    }
+
     if (aImage == mCurrentRequest && !(mCurrentRequestFlags & REQUEST_IS_TRACKED)) {
       mCurrentRequestFlags |= REQUEST_IS_TRACKED;
       doc->AddImage(mCurrentRequest);
     }
     if (aImage == mPendingRequest && !(mPendingRequestFlags & REQUEST_IS_TRACKED)) {
       mPendingRequestFlags |= REQUEST_IS_TRACKED;
       doc->AddImage(mPendingRequest);
     }
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1480,18 +1480,20 @@ nsObjectLoadingContent::CheckLoadPolicy(
   }
 
   nsCOMPtr<nsIContent> thisContent =
     do_QueryInterface(static_cast<nsIImageLoadingContent*>(this));
   NS_ASSERTION(thisContent, "Must be an instance of content");
 
   nsIDocument* doc = thisContent->OwnerDoc();
 
+  nsContentPolicyType contentPolicyType = GetContentPolicyType();
+
   *aContentPolicy = nsIContentPolicy::ACCEPT;
-  nsresult rv = NS_CheckContentLoadPolicy(nsIContentPolicy::TYPE_OBJECT,
+  nsresult rv = NS_CheckContentLoadPolicy(contentPolicyType,
                                           mURI,
                                           doc->NodePrincipal(),
                                           thisContent,
                                           mContentType,
                                           nullptr, //extra
                                           aContentPolicy,
                                           nsContentUtils::GetContentPolicy(),
                                           nsContentUtils::GetSecurityManager());
@@ -1527,17 +1529,17 @@ nsObjectLoadingContent::CheckProcessPoli
   switch (mType) {
     case eType_Image:
       objectType = nsIContentPolicy::TYPE_IMAGE;
       break;
     case eType_Document:
       objectType = nsIContentPolicy::TYPE_DOCUMENT;
       break;
     case eType_Plugin:
-      objectType = nsIContentPolicy::TYPE_OBJECT;
+      objectType = GetContentPolicyType();
       break;
     default:
       NS_NOTREACHED("Calling checkProcessPolicy with a unloadable type");
       return false;
   }
 
   *aContentPolicy = nsIContentPolicy::ACCEPT;
   nsresult rv =
@@ -2480,21 +2482,23 @@ nsObjectLoadingContent::OpenChannel()
   nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
   if (inherit) {
     securityFlags |= nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
   }
   if (isSandBoxed) {
     securityFlags |= nsILoadInfo::SEC_SANDBOXED;
   }
 
+  nsContentPolicyType contentPolicyType = GetContentPolicyType();
+
   rv = NS_NewChannel(getter_AddRefs(chan),
                      mURI,
                      thisContent,
                      securityFlags,
-                     nsIContentPolicy::TYPE_OBJECT,
+                     contentPolicyType,
                      group, // aLoadGroup
                      shim,  // aCallbacks
                      nsIChannel::LOAD_CALL_CONTENT_SNIFFERS |
                      nsIChannel::LOAD_CLASSIFY_URI);
 
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Referrer
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -12,16 +12,17 @@
 
 #ifndef NSOBJECTLOADINGCONTENT_H_
 #define NSOBJECTLOADINGCONTENT_H_
 
 #include "mozilla/Attributes.h"
 #include "nsImageLoadingContent.h"
 #include "nsIStreamListener.h"
 #include "nsIChannelEventSink.h"
+#include "nsIContentPolicy.h"
 #include "nsIObjectLoadingContent.h"
 #include "nsIRunnable.h"
 #include "nsIThreadInternal.h"
 #include "nsIFrame.h"
 #include "nsIFrameLoader.h"
 
 class nsAsyncInstantiateEvent;
 class nsStopPluginRunnable;
@@ -327,16 +328,21 @@ class nsObjectLoadingContent : public ns
                       bool aForcedReentry = false);
 
     nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                         nsIContent* aBindingParent,
                         bool aCompileEventHandler);
     void UnbindFromTree(bool aDeep = true,
                         bool aNullParent = true);
 
+    /**
+     * Return the content policy type used for loading the element.
+     */
+    virtual nsContentPolicyType GetContentPolicyType() const = 0;
+
   private:
 
     // Object parameter changes returned by UpdateObjectParameters
     enum ParameterUpdateFlags {
       eParamNoChange           = 0,
       // Parameters that potentially affect the channel changed
       // - mOriginalURI, mOriginalContentType
       eParamChannelChanged     = 1u << 0,
--- a/dom/browser-element/BrowserElementParent.js
+++ b/dom/browser-element/BrowserElementParent.js
@@ -31,32 +31,20 @@ function getIntPref(prefName, def) {
   try {
     return Services.prefs.getIntPref(prefName);
   }
   catch(err) {
     return def;
   }
 }
 
-function visibilityChangeHandler(e) {
-  // The visibilitychange event's target is the document.
-  let win = e.target.defaultView;
-
-  if (!win._browserElementParents) {
-    return;
-  }
-
-  let beps = Cu.nondeterministicGetWeakMapKeys(win._browserElementParents);
-  if (beps.length == 0) {
-    win.removeEventListener('visibilitychange', visibilityChangeHandler);
-    return;
-  }
-
-  for (let i = 0; i < beps.length; i++) {
-    beps[i]._ownerVisibilityChange();
+function handleWindowEvent(e) {
+  if (this._browserElementParents) {
+    let beps = Cu.nondeterministicGetWeakMapKeys(this._browserElementParents);
+    beps.forEach(bep => bep._handleOwnerEvent(e));
   }
 }
 
 function defineNoReturnMethod(fn) {
   return function method() {
     if (!this._domRequestReady) {
       // Remote browser haven't been created, we just queue the API call.
       let args = Array.slice(arguments);
@@ -114,33 +102,32 @@ BrowserElementParent.prototype = {
     //
     // To accomplish this, we register just one listener on the window, and have
     // it reference a WeakMap whose keys are all the BrowserElementParent objects
     // on the window.  Then when the listener fires, we iterate over the
     // WeakMap's keys (which we can do, because we're chrome) to notify the
     // BrowserElementParents.
     if (!this._window._browserElementParents) {
       this._window._browserElementParents = new WeakMap();
-      this._window.addEventListener('visibilitychange',
-                                    visibilityChangeHandler,
-                                    /* useCapture = */ false,
-                                    /* wantsUntrusted = */ false);
+      let handler = handleWindowEvent.bind(this._window);
+      let windowEvents = ['visibilitychange', 'mozfullscreenchange'];
+      let els = Cc["@mozilla.org/eventlistenerservice;1"]
+                  .getService(Ci.nsIEventListenerService);
+      for (let event of windowEvents) {
+        els.addSystemEventListener(this._window, event, handler,
+                                   /* useCapture = */ true);
+      }
     }
 
     this._window._browserElementParents.set(this, null);
 
     // Insert ourself into the prompt service.
     BrowserElementPromptService.mapFrameToBrowserElementParent(this._frameElement, this);
     this._setupMessageListener();
     this._registerAppManifest();
-
-    let els = Cc["@mozilla.org/eventlistenerservice;1"]
-                .getService(Ci.nsIEventListenerService);
-    els.addSystemEventListener(this._window.document, "mozfullscreenchange",
-                               this._fullscreenChange.bind(this), true);
   },
 
   _runPendingAPICall: function() {
     if (!this._pendingAPICalls) {
       return;
     }
     for (let i = 0; i < this._pendingAPICalls.length; i++) {
       try {
@@ -1051,24 +1038,29 @@ BrowserElementParent.prototype = {
     Services.obs.notifyObservers(
       this._frameElement, "fullscreen-origin-change", data.json.originNoSuffix);
   },
 
   _exitDomFullscreen: function(data) {
     this._windowUtils.remoteFrameFullscreenReverted();
   },
 
-  _fullscreenChange: function(evt) {
-    if (this._isAlive() && evt.target == this._window.document) {
-      if (!this._window.document.mozFullScreen) {
-        this._sendAsyncMsg("exit-fullscreen");
-      } else if (this._pendingDOMFullscreen) {
-        this._pendingDOMFullscreen = false;
-        this._sendAsyncMsg("entered-fullscreen");
-      }
+  _handleOwnerEvent: function(evt) {
+    switch (evt.type) {
+      case 'visibilitychange':
+        this._ownerVisibilityChange();
+        break;
+      case 'mozfullscreenchange':
+        if (!this._window.document.mozFullScreen) {
+          this._sendAsyncMsg('exit-fullscreen');
+        } else if (this._pendingDOMFullscreen) {
+          this._pendingDOMFullscreen = false;
+          this._sendAsyncMsg('entered-fullscreen');
+        }
+        break;
     }
   },
 
   _fireFatalError: function() {
     let evt = this._createEvent('error', {type: 'fatal'},
                                 /* cancelable = */ false);
     this._frameElement.dispatchEvent(evt);
   },
--- a/dom/canvas/WebGL2ContextFramebuffers.cpp
+++ b/dom/canvas/WebGL2ContextFramebuffers.cpp
@@ -493,41 +493,48 @@ WebGL2Context::InvalidateSubFramebuffer(
 }
 
 void
 WebGL2Context::ReadBuffer(GLenum mode)
 {
     if (IsContextLost())
         return;
 
-    MakeContextCurrent();
+    const bool isColorAttachment = (mode >= LOCAL_GL_COLOR_ATTACHMENT0 &&
+                                    mode <= LastColorAttachment());
+
+    if (mode != LOCAL_GL_NONE && mode != LOCAL_GL_BACK && !isColorAttachment) {
+        ErrorInvalidEnum("readBuffer: `mode` must be one of NONE, BACK, or "
+                         "COLOR_ATTACHMENTi. Was %s",
+                         EnumName(mode));
+        return;
+    }
 
     if (mBoundReadFramebuffer) {
-        bool isColorAttachment = (mode >= LOCAL_GL_COLOR_ATTACHMENT0 &&
-                                  mode <= LastColorAttachment());
         if (mode != LOCAL_GL_NONE &&
             !isColorAttachment)
         {
-            ErrorInvalidEnumInfo("readBuffer: If READ_FRAMEBUFFER is non-null,"
-                                 " `mode` must be COLOR_ATTACHMENTN or NONE."
-                                 " Was:", mode);
+            ErrorInvalidOperation("readBuffer: If READ_FRAMEBUFFER is non-null, `mode` "
+                                  "must be COLOR_ATTACHMENTi or NONE. Was %s",
+                                  EnumName(mode));
             return;
         }
 
+        MakeContextCurrent();
         gl->fReadBuffer(mode);
         return;
     }
 
     // Operating on the default framebuffer.
-
     if (mode != LOCAL_GL_NONE &&
         mode != LOCAL_GL_BACK)
     {
-        ErrorInvalidEnumInfo("readBuffer: If READ_FRAMEBUFFER is null, `mode`"
-                             " must be BACK or NONE. Was:", mode);
+        ErrorInvalidOperation("readBuffer: If READ_FRAMEBUFFER is null, `mode`"
+                              " must be BACK or NONE. Was %s",
+                              EnumName(mode));
         return;
     }
 
     gl->Screen()->SetReadBuffer(mode);
 }
 
 void
 WebGL2Context::RenderbufferStorageMultisample(GLenum target, GLsizei samples,
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -1398,20 +1398,18 @@ protected:
     nsTArray<WebGLRefPtr<WebGLTexture> > mBound3DTextures;
     nsTArray<WebGLRefPtr<WebGLSampler> > mBoundSamplers;
 
     void ResolveTexturesForDraw() const;
 
     WebGLRefPtr<WebGLProgram> mCurrentProgram;
     RefPtr<const webgl::LinkedProgramInfo> mActiveProgramLinkInfo;
 
-    uint32_t mMaxFramebufferColorAttachments;
-
     GLenum LastColorAttachment() const {
-        return LOCAL_GL_COLOR_ATTACHMENT0 + mMaxFramebufferColorAttachments - 1;
+        return LOCAL_GL_COLOR_ATTACHMENT0 + mGLMaxColorAttachments - 1;
     }
 
     bool ValidateFramebufferTarget(GLenum target, const char* const info);
 
     WebGLRefPtr<WebGLFramebuffer> mBoundDrawFramebuffer;
     WebGLRefPtr<WebGLFramebuffer> mBoundReadFramebuffer;
     WebGLRefPtr<WebGLRenderbuffer> mBoundRenderbuffer;
     WebGLRefPtr<WebGLTransformFeedback> mBoundTransformFeedback;
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -396,25 +396,18 @@ WebGLContext::ValidateFramebufferAttachm
 
     if (attachment == LOCAL_GL_DEPTH_ATTACHMENT ||
         attachment == LOCAL_GL_STENCIL_ATTACHMENT ||
         attachment == LOCAL_GL_DEPTH_STENCIL_ATTACHMENT)
     {
         return true;
     }
 
-    GLenum colorAttachCount = 1;
-    if (IsExtensionEnabled(WebGLExtensionID::WEBGL_draw_buffers))
-        colorAttachCount = mGLMaxColorAttachments;
-
-    if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 &&
-        attachment < GLenum(LOCAL_GL_COLOR_ATTACHMENT0 + colorAttachCount))
-    {
+    if (attachment >= LOCAL_GL_COLOR_ATTACHMENT0 && attachment <= LastColorAttachment())
         return true;
-    }
 
     ErrorInvalidEnum("%s: attachment: invalid enum value 0x%x.", funcName,
                      attachment);
     return false;
 }
 
 /**
  * Return true if pname is valid for GetSamplerParameter calls.
@@ -1880,19 +1873,16 @@ WebGLContext::InitAndValidateGL()
             } else {
                 mGLMaxVaryingVectors = 16;
                 // 16 = 64/4, and 64 is the min value for
                 // maxVertexOutputComponents in the OpenGL 3.2 spec.
             }
         }
     }
 
-    // Always 1 for GLES2
-    mMaxFramebufferColorAttachments = 1;
-
     if (gl->IsCompatibilityProfile()) {
         // gl_PointSize is always available in ES2 GLSL, but has to be
         // specifically enabled on desktop GLSL.
         gl->fEnable(LOCAL_GL_VERTEX_PROGRAM_POINT_SIZE);
 
         /* gl_PointCoord is always available in ES2 GLSL and in newer desktop
          * GLSL versions, but apparently not in OpenGL 2 and apparently not (due
          * to a driver bug) on certain NVIDIA setups. See:
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -393,17 +393,18 @@ EventListenerManager::AddEventListenerIn
     }
   }
 
   if (aTypeAtom && mTarget) {
     mTarget->EventListenerAdded(aTypeAtom);
   }
 
   if (mIsMainThreadELM && mTarget) {
-    EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
+    EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
+                                                              aTypeAtom);
   }
 }
 
 bool
 EventListenerManager::IsDeviceType(uint32_t aType)
 {
   switch (aType) {
     case NS_DEVICE_ORIENTATION:
@@ -513,17 +514,18 @@ EventListenerManager::RemoveEventListene
         mListeners.RemoveElementAt(i);
         --count;
         mNoListenerForEvent = NS_EVENT_NULL;
         mNoListenerForEventAtom = nullptr;
         if (mTarget && aUserType) {
           mTarget->EventListenerRemoved(aUserType);
         }
         if (mIsMainThreadELM && mTarget) {
-          EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
+          EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
+                                                                    aUserType);
         }
 
         if (!deviceType
 #ifdef MOZ_B2G
             && !timeChangeEvent && !networkEvent
 #endif // MOZ_B2G
             ) {
           return;
@@ -649,17 +651,17 @@ EventListenerManager::SetEventHandlerInt
     bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler;
     // Possibly the same listener, but update still the context and scope.
     jsEventHandler->SetHandler(aTypedHandler);
     if (mTarget && !same && aName) {
       mTarget->EventListenerRemoved(aName);
       mTarget->EventListenerAdded(aName);
     }
     if (mIsMainThreadELM && mTarget) {
-      EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
+      EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, aName);
     }
   }
 
   // Set flag to indicate possible need for compilation later
   listener->mHandlerIsString = !aTypedHandler.HasEventHandler();
   if (aPermitUntrustedEvents) {
     listener->mFlags.mAllowUntrustedEvents = true;
   }
@@ -779,17 +781,17 @@ EventListenerManager::RemoveEventHandler
   if (listener) {
     mListeners.RemoveElementAt(uint32_t(listener - &mListeners.ElementAt(0)));
     mNoListenerForEvent = NS_EVENT_NULL;
     mNoListenerForEventAtom = nullptr;
     if (mTarget && aName) {
       mTarget->EventListenerRemoved(aName);
     }
     if (mIsMainThreadELM && mTarget) {
-      EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
+      EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, aName);
     }
   }
 }
 
 nsresult
 EventListenerManager::CompileEventHandlerInternal(Listener* aListener,
                                                   const nsAString* aBody,
                                                   Element* aElement)
--- a/dom/events/EventListenerService.cpp
+++ b/dom/events/EventListenerService.cpp
@@ -19,16 +19,54 @@
 #include "nsArray.h"
 #include "nsThreadUtils.h"
 
 namespace mozilla {
 
 using namespace dom;
 
 /******************************************************************************
+ * mozilla::EventListenerChange
+ ******************************************************************************/
+
+NS_IMPL_ISUPPORTS(EventListenerChange, nsIEventListenerChange)
+
+EventListenerChange::~EventListenerChange()
+{
+}
+
+EventListenerChange::EventListenerChange(dom::EventTarget* aTarget) :
+  mTarget(aTarget)
+{
+  mChangedListenerNames = nsArrayBase::Create();
+}
+
+void
+EventListenerChange::AddChangedListenerName(nsIAtom* aEventName)
+{
+  mChangedListenerNames->AppendElement(aEventName, false);
+}
+
+NS_IMETHODIMP
+EventListenerChange::GetTarget(nsIDOMEventTarget** aTarget)
+{
+  NS_ENSURE_ARG_POINTER(aTarget);
+  NS_ADDREF(*aTarget = mTarget);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+EventListenerChange::GetChangedListenerNames(nsIArray** aEventNames)
+{
+  NS_ENSURE_ARG_POINTER(aEventNames);
+  NS_ADDREF(*aEventNames = mChangedListenerNames);
+  return NS_OK;
+}
+
+/******************************************************************************
  * mozilla::EventListenerInfo
  ******************************************************************************/
 
 NS_IMPL_CYCLE_COLLECTION(EventListenerInfo, mListener)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventListenerInfo)
   NS_INTERFACE_MAP_ENTRY(nsIEventListenerInfo)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
@@ -318,34 +356,38 @@ EventListenerService::AddListenerChangeL
 NS_IMETHODIMP
 EventListenerService::RemoveListenerChangeListener(nsIListenerChangeListener* aListener)
 {
   mChangeListeners.RemoveElement(aListener);
   return NS_OK;
 };
 
 void
-EventListenerService::NotifyAboutMainThreadListenerChangeInternal(dom::EventTarget* aTarget)
+EventListenerService::NotifyAboutMainThreadListenerChangeInternal(dom::EventTarget* aTarget,
+                                                                  nsIAtom* aName)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (mChangeListeners.IsEmpty()) {
     return;
   }
 
   if (!mPendingListenerChanges) {
     mPendingListenerChanges = nsArrayBase::Create();
     nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this,
       &EventListenerService::NotifyPendingChanges);
     NS_DispatchToCurrentThread(runnable);
   }
 
-  if (!mPendingListenerChangesSet.Get(aTarget)) {
-    mPendingListenerChanges->AppendElement(aTarget, false);
-    mPendingListenerChangesSet.Put(aTarget, true);
+  nsRefPtr<EventListenerChange> changes = mPendingListenerChangesSet.Get(aTarget);
+  if (!changes) {
+    changes = new EventListenerChange(aTarget);
+    mPendingListenerChanges->AppendElement(changes, false);
+    mPendingListenerChangesSet.Put(aTarget, changes);
   }
+  changes->AddChangedListenerName(aName);
 }
 
 void
 EventListenerService::NotifyPendingChanges()
 {
   nsCOMPtr<nsIMutableArray> changes;
   mPendingListenerChanges.swap(changes);
   mPendingListenerChangesSet.Clear();
--- a/dom/events/EventListenerService.h
+++ b/dom/events/EventListenerService.h
@@ -11,27 +11,45 @@
 #include "mozilla/Attributes.h"
 #include "nsAutoPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsIDOMEventListener.h"
 #include "nsIEventListenerService.h"
 #include "nsString.h"
 #include "nsTObserverArray.h"
 #include "nsDataHashtable.h"
+#include "nsGkAtoms.h"
 
 class nsIMutableArray;
 
 namespace mozilla {
 namespace dom {
 class EventTarget;
 } // namespace dom
 
 template<typename T>
 class Maybe;
 
+class EventListenerChange final : public nsIEventListenerChange
+{
+public:
+  explicit EventListenerChange(dom::EventTarget* aTarget);
+
+  void AddChangedListenerName(nsIAtom* aEventName);
+
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIEVENTLISTENERCHANGE
+
+protected:
+  virtual ~EventListenerChange();
+  nsCOMPtr<dom::EventTarget> mTarget;
+  nsCOMPtr<nsIMutableArray> mChangedListenerNames;
+
+};
+
 class EventListenerInfo final : public nsIEventListenerInfo
 {
 public:
   EventListenerInfo(const nsAString& aType,
                     already_AddRefed<nsIDOMEventListener> aListener,
                     bool aCapturing,
                     bool aAllowsUntrusted,
                     bool aInSystemEventGroup)
@@ -65,28 +83,30 @@ protected:
 class EventListenerService final : public nsIEventListenerService
 {
   ~EventListenerService();
 public:
   EventListenerService();
   NS_DECL_ISUPPORTS
   NS_DECL_NSIEVENTLISTENERSERVICE
 
-  static void NotifyAboutMainThreadListenerChange(dom::EventTarget* aTarget)
+  static void NotifyAboutMainThreadListenerChange(dom::EventTarget* aTarget,
+                                                  nsIAtom* aName)
   {
     if (sInstance) {
-      sInstance->NotifyAboutMainThreadListenerChangeInternal(aTarget);
+      sInstance->NotifyAboutMainThreadListenerChangeInternal(aTarget, aName);
     }
   }
 
   void NotifyPendingChanges();
 private:
-  void NotifyAboutMainThreadListenerChangeInternal(dom::EventTarget* aTarget);
+  void NotifyAboutMainThreadListenerChangeInternal(dom::EventTarget* aTarget,
+                                                   nsIAtom* aName);
   nsTObserverArray<nsCOMPtr<nsIListenerChangeListener>> mChangeListeners;
   nsCOMPtr<nsIMutableArray> mPendingListenerChanges;
-  nsDataHashtable<nsISupportsHashKey, bool> mPendingListenerChangesSet;
+  nsDataHashtable<nsISupportsHashKey, nsRefPtr<EventListenerChange>> mPendingListenerChangesSet;
 
   static EventListenerService* sInstance;
 };
 
 } // namespace mozilla
 
 #endif // mozilla_EventListenerService_h_
--- a/dom/events/nsIEventListenerService.idl
+++ b/dom/events/nsIEventListenerService.idl
@@ -4,20 +4,31 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIDOMEventListener;
 interface nsIDOMEventTarget;
 interface nsIArray;
 
-[scriptable, function, uuid(8d5b5a6b-dec0-473d-86c4-591801dfaac1)]
+/**
+ * Contains an event target along with an array of nsIAtom in form "oneventname"
+ * representing changed event listener names.
+ */
+[scriptable, uuid(07222b02-da12-4cf4-b2f7-761da007a8d8)]
+interface nsIEventListenerChange : nsISupports
+{
+  readonly attribute nsIDOMEventTarget target;
+  readonly attribute nsIArray changedListenerNames;
+};
+
+[scriptable, function, uuid(aa7c95f6-d3b5-44b3-9597-1d9f19b9c5f2)]
 interface nsIListenerChangeListener : nsISupports
 {
-  void listenersChanged(in nsIArray aEventTargets);
+  void listenersChanged(in nsIArray aEventListenerChanges);
 };
 
 /**
  * An instance of this interface describes how an event listener
  * was added to an event target.
  */
 [scriptable, uuid(11ba5fd7-8db2-4b1a-9f67-342cfa11afad)]
 interface nsIEventListenerInfo : nsISupports
--- a/dom/events/test/test_bug524674.xul
+++ b/dom/events/test/test_bug524674.xul
@@ -17,52 +17,74 @@ https://bugzilla.mozilla.org/show_bug.cg
   <!-- test code goes here -->
   <script type="application/javascript">
   <![CDATA[
   /** Test for Bug 524674 **/
 
   var els = Components.classes["@mozilla.org/eventlistenerservice;1"]
                       .getService(Components.interfaces.nsIEventListenerService);
 
+  const Ci = Components.interfaces;
+
   function dummyListener() {}
 
   var runningTest = null;
   var d = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
   var xhr = new XMLHttpRequest();
 
   // Test also double removals and such.
   var tests = [
     function() {
       els.addListenerChangeListener(changeListener);
       d.addEventListener("foo", dummyListener);
       d.addEventListener("foo", dummyListener);
       xhr.addEventListener("foo", dummyListener);
-      tests[0] = [d, xhr];
+      tests[0] = [{target: d, listeners: ["onfoo"]},
+                  {target: xhr, listeners: ["onfoo"]}];
+    },
+    function() {
+      d.addEventListener("bar", dummyListener);
+      d.addEventListener("baz", dummyListener);
+      xhr.addEventListener("bar", dummyListener);
+      xhr.addEventListener("baz", dummyListener);
+      tests[0] = [{target: d, listeners: ["onbaz", "onbar"]},
+                  {target: xhr, listeners: ["onbaz", "onbar"]}];
     },
     function() {
       d.onclick = dummyListener;
       d.onclick = dummyListener;
       xhr.onload = dummyListener;
-      tests[0] = [d, xhr];
+      tests[0] = [{target: d, listeners: ["onclick"]},
+                  {target: xhr, listeners: ["onload"]}];
     },
     function() {
       d.onclick = function() {};
-      tests[0] = [d];
+      tests[0] = [{target: d, listeners: ["onclick"]}];
     },
     function() {
       d.removeEventListener("foo", dummyListener);
       d.removeEventListener("foo", dummyListener);
       xhr.removeEventListener("foo", dummyListener);
-      tests[0] = [d, xhr];
+      tests[0] = [{target: d, listeners: ["onfoo"]},
+                  {target: xhr, listeners: ["onfoo"]}];
+    },
+    function() {
+      d.removeEventListener("bar", dummyListener);
+      d.removeEventListener("baz", dummyListener);
+      xhr.removeEventListener("bar", dummyListener);
+      xhr.removeEventListener("baz", dummyListener);
+      tests[0] = [{target: d, listeners: ["onbar", "onbaz"]},
+                  {target: xhr, listeners: ["onbar", "onbaz"]}];
     },
     function() {
       d.onclick = null;
       d.onclick = null;
       xhr.onload = null;
-      tests[0] = [d, xhr];
+      tests[0] = [{target: d, listeners: ["onclick"]},
+                  {target: xhr, listeners: ["onload"]}];
     },
     function() {
       els.removeListenerChangeListener(changeListener);
       // Check that once we've removed the change listener, it isn't called anymore.
       d.addEventListener("foo", dummyListener);
       xhr.addEventListener("foo", dummyListener);
       SimpleTest.executeSoon(function() {
         SimpleTest.finish();
@@ -71,39 +93,53 @@ https://bugzilla.mozilla.org/show_bug.cg
   ];
 
   SimpleTest.executeSoon(tests[0]);
 
   function changeListener(array) {
     if (typeof tests[0] == "function") {
       return;
     }
-    var expectedEventTargets = tests[0];
-    var e = array.enumerate();
+    var expectedEventChanges = tests[0];
+    var eventChanges = array.enumerate();
     var i = 0;
-    while (e.hasMoreElements()) {
+    while (eventChanges.hasMoreElements() && i < expectedEventChanges.length) {
       var current;
       try {
-        current = e.getNext();
+        current = eventChanges.getNext().QueryInterface(Ci.nsIEventListenerChange);
+        var expected = expectedEventChanges[i];
+
+        if (current.target == expected.target) {
+          // expected.target.listeners should be a subset of
+          // current.changedListenerNames if all expected listener changes were
+          // sent. We may get random other event listener changes here too, not
+          // just the one from the test.
+          is(current.target, expected.target, current.target + " = " + expected.target);
+
+          var eNames = current.changedListenerNames.enumerate();
+          var listeners = [];
+          while (eNames.hasMoreElements()) {
+            var listenerName = eNames.getNext().QueryInterface(Ci.nsIAtom).toString();
+            listeners.push(listenerName);
+          }
+          var matchAll = expected.listeners.every(function(val)
+                                                { return listeners.indexOf(val) >= 0; });
+          if (!matchAll)
+            return;
+          ++i;
+        }
       } catch(ex) {
         continue;
       }
-      var expected = expectedEventTargets[i];
-      if (current == expected) {
-        is(current, expected, current + " = " + expected);
-        // We may get random other event listener changes here too, not just the one from the
-        // test.
-        ++i
-      }
     }
-    if (expectedEventTargets.length != i) {
+    if (expectedEventChanges.length != i) {
       return;
     }
 
-    is(expectedEventTargets.length, i, "Should have got notification for all the changes.");
+    is(expectedEventChanges.length, i, "Should have got notification for all the changes.");
     tests.shift();
 
     ok(tests.length);
     SimpleTest.executeSoon(tests[0]);
   }
 
   SimpleTest.waitForExplicitFinish();
   ]]>
--- a/dom/fetch/InternalRequest.cpp
+++ b/dom/fetch/InternalRequest.cpp
@@ -123,25 +123,31 @@ InternalRequest::MapContentPolicyTypeToR
     context = RequestContext::Sharedworker;
     break;
   case nsIContentPolicy::TYPE_IMAGE:
     context = RequestContext::Image;
     break;
   case nsIContentPolicy::TYPE_STYLESHEET:
     context = RequestContext::Style;
     break;
-  case nsIContentPolicy::TYPE_OBJECT:
+  case nsIContentPolicy::TYPE_INTERNAL_OBJECT:
     context = RequestContext::Object;
     break;
+  case nsIContentPolicy::TYPE_INTERNAL_EMBED:
+    context = RequestContext::Embed;
+    break;
   case nsIContentPolicy::TYPE_DOCUMENT:
     context = RequestContext::Internal;
     break;
-  case nsIContentPolicy::TYPE_SUBDOCUMENT:
+  case nsIContentPolicy::TYPE_INTERNAL_IFRAME:
     context = RequestContext::Iframe;
     break;
+  case nsIContentPolicy::TYPE_INTERNAL_FRAME:
+    context = RequestContext::Frame;
+    break;
   case nsIContentPolicy::TYPE_REFRESH:
     context = RequestContext::Internal;
     break;
   case nsIContentPolicy::TYPE_XBL:
     context = RequestContext::Internal;
     break;
   case nsIContentPolicy::TYPE_PING:
     context = RequestContext::Ping;
--- a/dom/fetch/InternalRequest.h
+++ b/dom/fetch/InternalRequest.h
@@ -29,32 +29,32 @@ namespace dom {
  * below for examples).
  *
  * RequestContext    | nsContentPolicyType
  * ------------------+--------------------
  * audio             | TYPE_INTERNAL_AUDIO
  * beacon            | TYPE_BEACON
  * cspreport         | TYPE_CSP_REPORT
  * download          |
- * embed             | TYPE_OBJECT
+ * embed             | TYPE_INTERNAL_EMBED
  * eventsource       |
  * favicon           |
  * fetch             | TYPE_FETCH
  * font              | TYPE_FONT
  * form              |
- * frame             | TYPE_SUBDOCUMENT
+ * frame             | TYPE_INTERNAL_FRAME
  * hyperlink         |
- * iframe            | TYPE_SUBDOCUMENT
+ * iframe            | TYPE_INTERNAL_IFRAME
  * image             | TYPE_IMAGE
  * imageset          | TYPE_IMAGESET
  * import            | Not supported by Gecko
  * internal          | TYPE_DOCUMENT, TYPE_XBL, TYPE_OTHER
  * location          |
  * manifest          | TYPE_WEB_MANIFEST
- * object            | TYPE_OBJECT
+ * object            | TYPE_INTERNAL_OBJECT
  * ping              | TYPE_PING
  * plugin            | TYPE_OBJECT_SUBREQUEST
  * prefetch          |
  * script            | TYPE_INTERNAL_SCRIPT
  * sharedworker      | TYPE_INTERNAL_SHARED_WORKER
  * subresource       | Not supported by Gecko
  * style             | TYPE_STYLESHEET
  * track             | TYPE_INTERNAL_TRACK
@@ -62,25 +62,23 @@ namespace dom {
  * worker            | TYPE_INTERNAL_WORKER
  * xmlhttprequest    | TYPE_XMLHTTPREQUEST
  * xslt              | TYPE_XSLT
  *
  * TODO: Figure out if TYPE_REFRESH maps to anything useful
  * TODO: Figure out if TYPE_DTD maps to anything useful
  * TODO: Split TYPE_XMLHTTPREQUEST and TYPE_DATAREQUEST for EventSource
  * TODO: Figure out if TYPE_WEBSOCKET maps to anything useful
- * TODO: Differentiate between frame and iframe
  * TODO: Add a content type for prefetch
  * TODO: Use the content type for manifest when it becomes available
  * TODO: Add a content type for location
  * TODO: Add a content type for hyperlink
  * TODO: Add a content type for form
  * TODO: Add a content type for favicon
  * TODO: Add a content type for download
- * TODO: Split TYPE_OBJECT into TYPE_EMBED and TYPE_OBJECT
  */
 
 class Request;
 
 #define kFETCH_CLIENT_REFERRER_STR "about:client"
 
 class InternalRequest final
 {
--- a/dom/html/HTMLContentElement.cpp
+++ b/dom/html/HTMLContentElement.cpp
@@ -292,20 +292,22 @@ HTMLContentElement::Match(nsIContent* aC
 
 already_AddRefed<DistributedContentList>
 HTMLContentElement::GetDistributedNodes()
 {
   nsRefPtr<DistributedContentList> list = new DistributedContentList(this);
   return list.forget();
 }
 
-NS_IMPL_CYCLE_COLLECTION(DistributedContentList, mParent, mDistributedNodes)
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DistributedContentList, mParent,
+                                      mDistributedNodes)
 
 NS_INTERFACE_TABLE_HEAD(DistributedContentList)
-  NS_INTERFACE_TABLE(DistributedContentList, nsINodeList)
+  NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
+  NS_INTERFACE_TABLE(DistributedContentList, nsINodeList, nsIDOMNodeList)
   NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(DistributedContentList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DistributedContentList)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DistributedContentList)
 
 DistributedContentList::DistributedContentList(HTMLContentElement* aHostElement)
   : mParent(aHostElement)
--- a/dom/html/HTMLContentElement.h
+++ b/dom/html/HTMLContentElement.h
@@ -97,17 +97,17 @@ protected:
 };
 
 class DistributedContentList : public nsINodeList
 {
 public:
   explicit DistributedContentList(HTMLContentElement* aHostElement);
 
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(DistributedContentList)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DistributedContentList)
 
   // nsIDOMNodeList
   NS_DECL_NSIDOMNODELIST
 
   // nsINodeList
   virtual nsIContent* Item(uint32_t aIndex) override;
   virtual int32_t IndexOf(nsIContent* aContent) override;
   virtual nsINode* GetParentObject() override { return mParent; }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1167,35 +1167,16 @@ nsresult HTMLMediaElement::LoadResource(
   }
 
   // Check if media is allowed for the docshell.
   nsCOMPtr<nsIDocShell> docShell = OwnerDoc()->GetDocShell();
   if (docShell && !docShell->GetAllowMedia()) {
     return NS_ERROR_FAILURE;
   }
 
-  MOZ_ASSERT(IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
-  nsContentPolicyType contentPolicyType = IsHTMLElement(nsGkAtoms::audio) ?
-    nsIContentPolicy::TYPE_INTERNAL_AUDIO : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
-
-  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
-  nsresult rv = NS_CheckContentLoadPolicy(contentPolicyType,
-                                          mLoadingSrc,
-                                          NodePrincipal(),
-                                          static_cast<Element*>(this),
-                                          EmptyCString(), // mime type
-                                          nullptr, // extra
-                                          &shouldLoad,
-                                          nsContentUtils::GetContentPolicy(),
-                                          nsContentUtils::GetSecurityManager());
-  NS_ENSURE_SUCCESS(rv, rv);
-  if (NS_CP_REJECTED(shouldLoad)) {
-    return NS_ERROR_FAILURE;
-  }
-
   // Set the media element's CORS mode only when loading a resource
   mCORSMode = AttrValueToCORSMode(GetParsedAttr(nsGkAtoms::crossorigin));
 
 #ifdef MOZ_EME
   if (mMediaKeys &&
       !IsMediaStreamURI(mLoadingSrc) &&
       Preferences::GetBool("media.eme.mse-only", true)) {
     return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
@@ -1207,17 +1188,17 @@ nsresult HTMLMediaElement::LoadResource(
     // Clone it.
     nsresult rv = InitializeDecoderAsClone(other->mDecoder);
     if (NS_SUCCEEDED(rv))
       return rv;
   }
 
   if (IsMediaStreamURI(mLoadingSrc)) {
     nsRefPtr<DOMMediaStream> stream;
-    rv = NS_GetStreamForMediaStreamURI(mLoadingSrc, getter_AddRefs(stream));
+    nsresult rv = NS_GetStreamForMediaStreamURI(mLoadingSrc, getter_AddRefs(stream));
     if (NS_FAILED(rv)) {
       nsAutoString spec;
       GetCurrentSrc(spec);
       const char16_t* params[] = { spec.get() };
       ReportLoadError("MediaLoadInvalidURI", params, ArrayLength(params));
       return rv;
     }
     SetupSrcMediaStreamPlayback(stream);
@@ -1235,81 +1216,67 @@ nsresult HTMLMediaElement::LoadResource(
     nsRefPtr<MediaResource> resource =
       MediaSourceDecoder::CreateResource(mMediaSource->GetPrincipal());
     if (IsAutoplayEnabled()) {
       mJoinLatency.Start();
     }
     return FinishDecoderSetup(decoder, resource, nullptr, nullptr);
   }
 
-  nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
-  if (nsContentUtils::ChannelShouldInheritPrincipal(NodePrincipal(),
-                                                    mLoadingSrc,
-                                                    false, // aInheritForAboutBlank
-                                                    false // aForceInherit
-                                                    )) {
-    securityFlags = nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
-  }
+  // determine what security checks need to be performed in AsyncOpen2().
+  nsSecurityFlags securityFlags =
+    ShouldCheckAllowOrigin() ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS :
+                               nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
+
+  if (GetCORSMode() == CORS_USE_CREDENTIALS) {
+    securityFlags |= nsILoadInfo::SEC_REQUIRE_CORS_WITH_CREDENTIALS;
+  }
+
+  MOZ_ASSERT(IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
+  nsContentPolicyType contentPolicyType = IsHTMLElement(nsGkAtoms::audio) ?
+    nsIContentPolicy::TYPE_INTERNAL_AUDIO : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
 
   nsCOMPtr<nsILoadGroup> loadGroup = GetDocumentLoadGroup();
   nsCOMPtr<nsIChannel> channel;
-  rv = NS_NewChannel(getter_AddRefs(channel),
-                     mLoadingSrc,
-                     static_cast<Element*>(this),
-                     securityFlags,
-                     contentPolicyType,
-                     loadGroup,
-                     nullptr,   // aCallbacks
-                     nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
-                     nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE |
-                     nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
+  nsresult rv = NS_NewChannel(getter_AddRefs(channel),
+                              mLoadingSrc,
+                              static_cast<Element*>(this),
+                              securityFlags,
+                              contentPolicyType,
+                              loadGroup,
+                              nullptr,   // aCallbacks
+                              nsICachingChannel::LOAD_BYPASS_LOCAL_CACHE_IF_BUSY |
+                              nsIChannel::LOAD_MEDIA_SNIFFER_OVERRIDES_CONTENT_TYPE |
+                              nsIChannel::LOAD_CALL_CONTENT_SNIFFERS);
 
   NS_ENSURE_SUCCESS(rv,rv);
 
   // The listener holds a strong reference to us.  This creates a
   // reference cycle, once we've set mChannel, which is manually broken
   // in the listener's OnStartRequest method after it is finished with
   // the element. The cycle will also be broken if we get a shutdown
   // notification before OnStartRequest fires.  Necko guarantees that
   // OnStartRequest will eventually fire if we don't shut down first.
   nsRefPtr<MediaLoadListener> loadListener = new MediaLoadListener(this);
 
   channel->SetNotificationCallbacks(loadListener);
 
-  nsCOMPtr<nsIStreamListener> listener;
-  if (ShouldCheckAllowOrigin()) {
-    nsRefPtr<nsCORSListenerProxy> corsListener =
-      new nsCORSListenerProxy(loadListener,
-                              NodePrincipal(),
-                              GetCORSMode() == CORS_USE_CREDENTIALS);
-    rv = corsListener->Init(channel, DataURIHandling::Allow);
-    NS_ENSURE_SUCCESS(rv, rv);
-    listener = corsListener;
-  } else {
-    rv = nsContentUtils::GetSecurityManager()->
-           CheckLoadURIWithPrincipal(NodePrincipal(),
-                                     mLoadingSrc,
-                                     nsIScriptSecurityManager::STANDARD);
-    listener = loadListener;
-    NS_ENSURE_SUCCESS(rv, rv);
-  }
-
   nsCOMPtr<nsIHttpChannel> hc = do_QueryInterface(channel);
   if (hc) {
     // Use a byte range request from the start of the resource.
     // This enables us to detect if the stream supports byte range
     // requests, and therefore seeking, early.
     hc->SetRequestHeader(NS_LITERAL_CSTRING("Range"),
                          NS_LITERAL_CSTRING("bytes=0-"),
                          false);
 
     SetRequestHeaders(hc);
   }
 
-  rv = channel->AsyncOpen(listener, nullptr);
+  rv = channel->AsyncOpen2(loadListener);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Else the channel must be open and starting to download. If it encounters
   // a non-catastrophic failure, it will set a new task to continue loading
   // another candidate.  It's safe to set it as mChannel now.
   mChannel = channel;
 
   // loadListener will be unregistered either on shutdown or when
--- a/dom/html/HTMLObjectElement.h
+++ b/dom/html/HTMLObjectElement.h
@@ -241,17 +241,22 @@ private:
    */
   void StartObjectLoad(bool aNotify);
 
   /**
    * Returns if the element is currently focusable regardless of it's tabindex
    * value. This is used to know the default tabindex value.
    */
   bool IsFocusableForTabIndex();
-  
+
+  nsContentPolicyType GetContentPolicyType() const override
+  {
+    return nsIContentPolicy::TYPE_INTERNAL_OBJECT;
+  }
+
   virtual void GetItemValueText(DOMString& text) override;
   virtual void SetItemValueText(const nsAString& text) override;
 
   virtual ~HTMLObjectElement();
 
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
--- a/dom/html/HTMLSharedObjectElement.cpp
+++ b/dom/html/HTMLSharedObjectElement.cpp
@@ -382,10 +382,23 @@ HTMLSharedObjectElement::WrapNode(JSCont
   if (!obj) {
     return nullptr;
   }
   JS::Rooted<JSObject*> rootedObj(aCx, obj);
   SetupProtoChain(aCx, rootedObj);
   return rootedObj;
 }
 
+nsContentPolicyType
+HTMLSharedObjectElement::GetContentPolicyType() const
+{
+  if (mNodeInfo->Equals(nsGkAtoms::applet)) {
+    // We use TYPE_INTERNAL_OBJECT for applet too, since it is not exposed
+    // through RequestContext yet.
+    return nsIContentPolicy::TYPE_INTERNAL_OBJECT;
+  } else {
+    MOZ_ASSERT(mNodeInfo->Equals(nsGkAtoms::embed));
+    return nsIContentPolicy::TYPE_INTERNAL_EMBED;
+  }
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/html/HTMLSharedObjectElement.h
+++ b/dom/html/HTMLSharedObjectElement.h
@@ -202,16 +202,18 @@ private:
 
   nsIAtom *URIAttrName() const
   {
     return mNodeInfo->Equals(nsGkAtoms::applet) ?
            nsGkAtoms::code :
            nsGkAtoms::src;
   }
 
+  nsContentPolicyType GetContentPolicyType() const override;
+
   // mIsDoneAddingChildren is only really used for <applet>.  This boolean is
   // always true for <embed>, per the documentation in nsIContent.h.
   bool mIsDoneAddingChildren;
 
   virtual void GetItemValueText(DOMString& text) override;
   virtual void SetItemValueText(const nsAString& text) override;
 
   virtual JSObject* WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto) override;
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -158,17 +158,17 @@ parent:
 
     sync CreateWindow(PBrowser aNewTab,
                       uint32_t aChromeFlags,
                       bool aCalledFromJS,
                       bool aPositionSpecified,
                       bool aSizeSpecified,
                       nsString aURI,
                       nsString aName,
-                      nsString aFeatures,
+                      nsCString aFeatures,
                       nsString aBaseURI)
       returns (nsresult rv,
                bool windowOpened,
                FrameScriptInfo[] frameScripts,
                nsCString urlToLoad);
 
     sync SyncMessage(nsString aMessage, ClonedMessageData aData,
                      CpowEntry[] aCpows, Principal aPrincipal)
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1592,26 +1592,22 @@ TabChild::ProvideWindowCommon(nsIDOMWind
     if (!baseURI) {
       NS_ERROR("nsIDocument didn't return a base URI");
       return NS_ERROR_FAILURE;
     }
 
     nsAutoCString baseURIString;
     baseURI->GetSpec(baseURIString);
 
-    // We can assume that if content is requesting to open a window from a remote
-    // tab, then we want to enforce that the new window is also a remote tab.
-    features.AppendLiteral(",remote");
-
     nsresult rv;
 
     if (!SendCreateWindow(newChild,
                           aChromeFlags, aCalledFromJS, aPositionSpecified,
                           aSizeSpecified, url,
-                          name, NS_ConvertUTF8toUTF16(features),
+                          name, features,
                           NS_ConvertUTF8toUTF16(baseURIString),
                           &rv,
                           aWindowIsNew,
                           &frameScripts,
                           &urlToLoad)) {
       return NS_ERROR_NOT_AVAILABLE;
     }
 
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -607,26 +607,33 @@ FindMostRecentOpenWindow()
 bool
 TabParent::RecvCreateWindow(PBrowserParent* aNewTab,
                             const uint32_t& aChromeFlags,
                             const bool& aCalledFromJS,
                             const bool& aPositionSpecified,
                             const bool& aSizeSpecified,
                             const nsString& aURI,
                             const nsString& aName,
-                            const nsString& aFeatures,
+                            const nsCString& aFeatures,
                             const nsString& aBaseURI,
                             nsresult* aResult,
                             bool* aWindowIsNew,
                             InfallibleTArray<FrameScriptInfo>* aFrameScripts,
                             nsCString* aURLToLoad)
 {
   // We always expect to open a new window here. If we don't, it's an error.
   *aWindowIsNew = true;
 
+  // The content process should never be in charge of computing whether or
+  // not a window should be private or remote - the parent will do that.
+  MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW));
+  MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW));
+  MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME));
+  MOZ_ASSERT(!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW));
+
   if (NS_WARN_IF(IsBrowserOrApp()))
     return false;
 
   nsCOMPtr<nsPIWindowWatcher> pwwatch =
     do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);
 
   if (NS_WARN_IF(NS_FAILED(*aResult)))
     return true;
@@ -730,19 +737,21 @@ TabParent::RecvCreateWindow(PBrowserPare
 
     finalURI->GetSpec(finalURIString);
   }
 
   nsCOMPtr<nsIDOMWindow> window;
 
   AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
 
+  const char* features = aFeatures.Length() ? aFeatures.get() : nullptr;
+
   *aResult = pwwatch->OpenWindow2(parent, finalURIString.get(),
                                   NS_ConvertUTF16toUTF8(aName).get(),
-                                  NS_ConvertUTF16toUTF8(aFeatures).get(), aCalledFromJS,
+                                  features, aCalledFromJS,
                                   false, false, this, nullptr, getter_AddRefs(window));
 
   if (NS_WARN_IF(NS_FAILED(*aResult)))
     return true;
 
   *aResult = NS_ERROR_FAILURE;
 
   nsCOMPtr<nsPIDOMWindow> pwindow = do_QueryInterface(window);
@@ -3131,17 +3140,19 @@ public:
   NS_IMETHOD GetSecurityInfo(nsISupports**) NO_IMPL
   NS_IMETHOD GetContentType(nsACString&) NO_IMPL
   NS_IMETHOD SetContentType(const nsACString&) NO_IMPL
   NS_IMETHOD GetContentCharset(nsACString&) NO_IMPL
   NS_IMETHOD SetContentCharset(const nsACString&) NO_IMPL
   NS_IMETHOD GetContentLength(int64_t*) NO_IMPL
   NS_IMETHOD SetContentLength(int64_t) NO_IMPL
   NS_IMETHOD Open(nsIInputStream**) NO_IMPL
+  NS_IMETHOD Open2(nsIInputStream**) NO_IMPL
   NS_IMETHOD AsyncOpen(nsIStreamListener*, nsISupports*) NO_IMPL
+  NS_IMETHOD AsyncOpen2(nsIStreamListener*) NO_IMPL
   NS_IMETHOD GetContentDisposition(uint32_t*) NO_IMPL
   NS_IMETHOD SetContentDisposition(uint32_t) NO_IMPL
   NS_IMETHOD GetContentDispositionFilename(nsAString&) NO_IMPL
   NS_IMETHOD SetContentDispositionFilename(const nsAString&) NO_IMPL
   NS_IMETHOD GetContentDispositionHeader(nsACString&) NO_IMPL
   NS_IMETHOD OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo) override;
   NS_IMETHOD OnAuthCancelled(nsISupports *aContext, bool userCancel) override;
   NS_IMETHOD GetInterface(const nsIID & uuid, void **result) override
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -137,17 +137,17 @@ public:
                                             bool* aOutWindowOpened) override;
     virtual bool RecvCreateWindow(PBrowserParent* aOpener,
                                   const uint32_t& aChromeFlags,
                                   const bool& aCalledFromJS,
                                   const bool& aPositionSpecified,
                                   const bool& aSizeSpecified,
                                   const nsString& aURI,
                                   const nsString& aName,
-                                  const nsString& aFeatures,
+                                  const nsCString& aFeatures,
                                   const nsString& aBaseURI,
                                   nsresult* aResult,
                                   bool* aWindowIsNew,
                                   InfallibleTArray<FrameScriptInfo>* aFrameScripts,
                                   nsCString* aURLToLoad) override;
     virtual bool RecvSyncMessage(const nsString& aMessage,
                                  const ClonedMessageData& aData,
                                  InfallibleTArray<CpowEntry>&& aCpows,
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -45,16 +45,17 @@
 #include "nsILoadInfo.h"
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIWritablePropertyBag2.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsSandboxFlags.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsILoadInfo.h"
+#include "nsContentSecurityManager.h"
 
 #include "mozilla/ipc/URIUtils.h"
 
 using mozilla::dom::AutoEntryScript;
 
 static NS_DEFINE_CID(kJSURICID, NS_JSURI_CID);
 
 class nsJSThunk : public nsIInputStream
@@ -548,16 +549,25 @@ nsJSChannel::Open(nsIInputStream **aResu
                                            mExecutionPolicy,
                                            mOriginalInnerWindow);
     NS_ENSURE_SUCCESS(rv, rv);
 
     return mStreamChannel->Open(aResult);
 }
 
 NS_IMETHODIMP
+nsJSChannel::Open2(nsIInputStream** aStream)
+{
+    nsCOMPtr<nsIStreamListener> listener;
+    nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+    NS_ENSURE_SUCCESS(rv, rv);
+    return Open(aStream);
+}
+
+NS_IMETHODIMP
 nsJSChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
 {
     NS_ENSURE_ARG(aListener);
 
     // First make sure that we have a usable inner window; we'll want to make
     // sure that we execute against that inner and no other.
     nsIScriptGlobalObject* global = GetGlobalObject(this);
     if (!global) {
@@ -658,16 +668,25 @@ nsJSChannel::AsyncOpen(nsIStreamListener
     if (NS_FAILED(rv)) {
         loadGroup->RemoveRequest(this, nullptr, rv);
         mIsActive = false;
         CleanupStrongRefs();
     }
     return rv;
 }
 
+NS_IMETHODIMP
+nsJSChannel::AsyncOpen2(nsIStreamListener *aListener)
+{
+  nsCOMPtr<nsIStreamListener> listener = aListener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return AsyncOpen(listener, nullptr);
+}
+
 void
 nsJSChannel::EvaluateScript()
 {
     // Synchronously execute the script...
     // mIsActive is used to indicate the the request is 'busy' during the
     // the script evaluation phase.  This means that IsPending() will 
     // indicate the the request is busy while the script is executing...
 
--- a/dom/media/gstreamer/GStreamerFormatHelper.cpp
+++ b/dom/media/gstreamer/GStreamerFormatHelper.cpp
@@ -253,18 +253,19 @@ static gboolean FactoryFilter(GstPluginF
 {
   if (!GST_IS_ELEMENT_FACTORY(aFeature)) {
     return FALSE;
   }
 
   const gchar *className =
     gst_element_factory_get_klass(GST_ELEMENT_FACTORY_CAST(aFeature));
 
-  if (!strstr(className, "Decoder") && !strstr(className, "Demux") &&
-      !strstr(className, "Parser")) {
+  // NB: We skip filtering parsers here, because adding them to
+  // the list can give false decoder positives to canPlayType().
+  if (!strstr(className, "Decoder") && !strstr(className, "Demux")) {
     return FALSE;
   }
 
   return
     gst_plugin_feature_get_rank(aFeature) >= GST_RANK_MARGINAL &&
     !GStreamerFormatHelper::IsPluginFeatureBlacklisted(aFeature);
 }
 
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -293,17 +293,21 @@ TrackBuffer::AppendDataToCurrentResource
   mCurrentDecoder->SetRealMediaDuration(mCurrentDecoder->GetRealMediaDuration() + aDuration);
 
   return appendOffset;
 }
 
 nsRefPtr<TrackBuffer::BufferedRangesUpdatedPromise>
 TrackBuffer::UpdateBufferedRanges(Interval<int64_t> aByteRange, bool aNotifyParent)
 {
-  if (aByteRange.Length()) {
+  if (!mParentDecoder) {
+    return BufferedRangesUpdatedPromise::CreateAndResolve(true, __func__);
+  }
+
+  if (mCurrentDecoder && aByteRange.Length()) {
     mCurrentDecoder->GetReader()->NotifyDataArrived(aByteRange);
   }
 
   // Recalculate and cache our new buffered range.
   {
     ReentrantMonitorAutoEnter mon(mParentDecoder->GetReentrantMonitor());
     TimeIntervals buffered;
 
--- a/dom/media/platforms/wmf/WMFDecoderModule.cpp
+++ b/dom/media/platforms/wmf/WMFDecoderModule.cpp
@@ -14,21 +14,23 @@
 #include "WMFMediaDataDecoder.h"
 #include "nsIWindowsRegKey.h"
 #include "nsComponentManagerUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIGfxInfo.h"
 #include "GfxDriverInfo.h"
 #include "gfxWindowsPlatform.h"
 #include "MediaInfo.h"
+#include "prsystem.h"
 
 namespace mozilla {
 
 static bool sIsWMFEnabled = false;
 static bool sDXVAEnabled = false;
+static int  sNumDecoderThreads = -1;
 
 WMFDecoderModule::WMFDecoderModule()
   : mWMFInitialized(false)
 {
 }
 
 WMFDecoderModule::~WMFDecoderModule()
 {
@@ -39,23 +41,50 @@ WMFDecoderModule::~WMFDecoderModule()
 }
 
 void
 WMFDecoderModule::DisableHardwareAcceleration()
 {
   sDXVAEnabled = false;
 }
 
+static void
+SetNumOfDecoderThreads()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Preferences can only be read on main thread");
+  int32_t numCores = PR_GetNumberOfProcessors();
+
+  // If we have more than 4 cores, let the decoder decide how many threads.
+  // On an 8 core machine, WMF chooses 4 decoder threads
+  const int WMF_DECODER_DEFAULT = -1;
+  int32_t prefThreadCount = Preferences::GetInt("media.wmf.decoder.thread-count", -1);
+  if (prefThreadCount != WMF_DECODER_DEFAULT) {
+    sNumDecoderThreads = std::max(prefThreadCount, 1);
+  } else if (numCores > 4) {
+    sNumDecoderThreads = WMF_DECODER_DEFAULT;
+  } else {
+    sNumDecoderThreads = std::max(numCores - 1, 1);
+  }
+}
+
 /* static */
 void
 WMFDecoderModule::Init()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Must be on main thread.");
   sIsWMFEnabled = Preferences::GetBool("media.windows-media-foundation.enabled", false);
   sDXVAEnabled = gfxPlatform::GetPlatform()->CanUseHardwareVideoDecoding();
+  SetNumOfDecoderThreads();
+}
+
+/* static */
+int
+WMFDecoderModule::GetNumDecoderThreads()
+{
+  return sNumDecoderThreads;
 }
 
 nsresult
 WMFDecoderModule::Startup()
 {
   if (sIsWMFEnabled) {
     mWMFInitialized = SUCCEEDED(wmf::MFStartup());
   }
--- a/dom/media/platforms/wmf/WMFDecoderModule.h
+++ b/dom/media/platforms/wmf/WMFDecoderModule.h
@@ -43,16 +43,19 @@ public:
   // on the system to play various codecs. Windows Vista doesn't have the
   // H.264/AAC decoders if the "Platform Update Supplement for Windows Vista"
   // is not installed.
   static bool HasAAC();
   static bool HasH264();
 
   // Called on main thread.
   static void Init();
+
+  // Called from any thread, must call init first
+  static int GetNumDecoderThreads();
 private:
   bool ShouldUseDXVA(const VideoInfo& aConfig) const;
   bool mWMFInitialized;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.cpp
@@ -193,23 +193,25 @@ WMFVideoMFTManager::InitInternal(bool aF
   mUseHwAccel = false; // default value; changed if D3D setup succeeds.
   bool useDxva = InitializeDXVA(aForceD3D9);
 
   RefPtr<MFTDecoder> decoder(new MFTDecoder());
 
   HRESULT hr = decoder->Create(GetMFTGUID());
   NS_ENSURE_TRUE(SUCCEEDED(hr), nullptr);
 
-  if (useDxva) {
-    RefPtr<IMFAttributes> attr(decoder->GetAttributes());
+  RefPtr<IMFAttributes> attr(decoder->GetAttributes());
+  UINT32 aware = 0;
+  if (attr) {
+      attr->GetUINT32(MF_SA_D3D_AWARE, &aware);
+      attr->SetUINT32(CODECAPI_AVDecNumWorkerThreads,
+                      WMFDecoderModule::GetNumDecoderThreads());
+  }
 
-    UINT32 aware = 0;
-    if (attr) {
-      attr->GetUINT32(MF_SA_D3D_AWARE, &aware);
-    }
+  if (useDxva) {
     if (aware) {
       // TODO: Test if I need this anywhere... Maybe on Vista?
       //hr = attr->SetUINT32(CODECAPI_AVDecVideoAcceleration_H264, TRUE);
       //NS_ENSURE_TRUE(SUCCEEDED(hr), hr);
       MOZ_ASSERT(mDXVA2Manager);
       ULONG_PTR manager = ULONG_PTR(mDXVA2Manager->GetDXVADeviceManager());
       hr = decoder->SendMFTMessage(MFT_MESSAGE_SET_D3D_MANAGER, manager);
       if (SUCCEEDED(hr)) {
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -2,27 +2,30 @@
 # vim: set filetype=python:
 # 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/.
 
 TEST_DIRS += ['test']
 
 EXPORTS.mozilla.dom += [
+    'nsContentSecurityManager.h',
     'nsCSPContext.h',
     'nsCSPService.h',
     'nsCSPUtils.h',
     'nsMixedContentBlocker.h',
 ]
 
 EXPORTS += [
+    'nsContentSecurityManager.h',
     'nsCORSListenerProxy.h'
 ]
 
 UNIFIED_SOURCES += [
+    'nsContentSecurityManager.cpp',
     'nsCORSListenerProxy.cpp',
     'nsCSPContext.cpp',
     'nsCSPParser.cpp',
     'nsCSPService.cpp',
     'nsCSPUtils.cpp',
     'nsMixedContentBlocker.cpp',
 ]
 
new file mode 100644
--- /dev/null
+++ b/dom/security/nsContentSecurityManager.cpp
@@ -0,0 +1,263 @@
+#include "nsContentSecurityManager.h"
+#include "nsIChannel.h"
+#include "nsIStreamListener.h"
+#include "nsILoadInfo.h"
+#include "nsContentUtils.h"
+#include "nsCORSListenerProxy.h"
+#include "nsIStreamListener.h"
+
+#include "mozilla/dom/Element.h"
+
+nsresult
+ValidateSecurityFlags(nsILoadInfo* aLoadInfo)
+{
+  nsSecurityFlags securityMode = aLoadInfo->GetSecurityMode();
+
+  if (securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS &&
+      securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED &&
+      securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS &&
+      securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
+      securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+    MOZ_ASSERT(false, "need one securityflag from nsILoadInfo to perform security checks");
+    return NS_ERROR_FAILURE;
+  }
+
+  // make sure that cors-with-credentials is only used in combination with CORS.
+  if (aLoadInfo->GetRequireCorsWithCredentials() &&
+      securityMode != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+    MOZ_ASSERT(false, "can not use cors-with-credentials without cors");
+    return NS_ERROR_FAILURE;
+  }
+  // all good, found the right security flags
+  return NS_OK;
+}
+
+nsresult
+DoSOPChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
+{
+  nsSecurityFlags securityMode = aLoadInfo->GetSecurityMode();
+
+  // if none of the REQUIRE_SAME_ORIGIN flags are set, then SOP does not apply
+  if ((securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS) &&
+      (securityMode != nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED)) {
+    return NS_OK;
+  }
+
+  nsIPrincipal* loadingPrincipal = aLoadInfo->LoadingPrincipal();
+  bool sameOriginDataInherits =
+    securityMode == nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_INHERITS;
+  return loadingPrincipal->CheckMayLoad(aURI,
+                                        true, // report to console
+                                        sameOriginDataInherits);
+}
+
+nsresult
+DoCheckLoadURIChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
+{
+  nsresult rv = NS_OK;
+  nsSecurityFlags securityMode = aLoadInfo->GetSecurityMode();
+  // Please note that checkLoadURIWithPrincipal should only be enforced for
+  // cross origin requests. If the flag SEC_REQUIRE_CORS_DATA_INHERITS is set
+  // within the loadInfo, then then CheckLoadURIWithPrincipal is performed
+  // within nsCorsListenerProxy
+  if ((securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS) &&
+      (securityMode != nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL)) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIPrincipal> loadingPrincipal = aLoadInfo->LoadingPrincipal();
+  // XXX: @arg nsIScriptSecurityManager::STANDARD
+  // lets use STANDARD for now and evaluate on a callsite basis, see also:
+  // http://mxr.mozilla.org/mozilla-central/source/caps/nsIScriptSecurityManager.idl#62
+  rv = nsContentUtils::GetSecurityManager()->
+    CheckLoadURIWithPrincipal(loadingPrincipal,
+                              aURI,
+                              nsIScriptSecurityManager::STANDARD);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // If the loadingPrincipal and the triggeringPrincipal are different, then make
+  // sure the triggeringPrincipal is allowed to access that URI.
+  nsCOMPtr<nsIPrincipal> triggeringPrincipal = aLoadInfo->TriggeringPrincipal();
+  if (loadingPrincipal != triggeringPrincipal) {
+    rv = nsContentUtils::GetSecurityManager()->
+           CheckLoadURIWithPrincipal(triggeringPrincipal,
+                                     aURI,
+                                     nsIScriptSecurityManager::STANDARD);
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+  return NS_OK;
+}
+
+nsresult
+DoCORSChecks(nsIChannel* aChannel, nsILoadInfo* aLoadInfo,
+             nsCOMPtr<nsIStreamListener>& aInAndOutListener)
+{
+  if (aLoadInfo->GetSecurityMode() != nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
+    return NS_OK;
+  }
+
+  nsIPrincipal* loadingPrincipal = aLoadInfo->LoadingPrincipal();
+  nsRefPtr<nsCORSListenerProxy> corsListener =
+    new nsCORSListenerProxy(aInAndOutListener,
+                            loadingPrincipal,
+                            aLoadInfo->GetRequireCorsWithCredentials());
+  // XXX: @arg: DataURIHandling::Allow
+  // lets use  DataURIHandling::Allow for now and then decide on callsite basis. see also:
+  // http://mxr.mozilla.org/mozilla-central/source/dom/security/nsCORSListenerProxy.h#33
+  nsresult rv = corsListener->Init(aChannel, DataURIHandling::Allow);
+  NS_ENSURE_SUCCESS(rv, rv);
+  aInAndOutListener = corsListener;
+  return NS_OK;
+}
+
+nsresult
+DoContentSecurityChecks(nsIURI* aURI, nsILoadInfo* aLoadInfo)
+{
+  nsContentPolicyType contentPolicyType = aLoadInfo->GetContentPolicyType();
+  nsCString mimeTypeGuess;
+  nsCOMPtr<nsISupports> requestingContext = nullptr;
+
+  switch(contentPolicyType) {
+    case nsIContentPolicy::TYPE_OTHER:
+    case nsIContentPolicy::TYPE_SCRIPT:
+    case nsIContentPolicy::TYPE_IMAGE:
+    case nsIContentPolicy::TYPE_STYLESHEET:
+    case nsIContentPolicy::TYPE_OBJECT:
+    case nsIContentPolicy::TYPE_DOCUMENT:
+    case nsIContentPolicy::TYPE_SUBDOCUMENT:
+    case nsIContentPolicy::TYPE_REFRESH:
+    case nsIContentPolicy::TYPE_XBL:
+    case nsIContentPolicy::TYPE_PING:
+    case nsIContentPolicy::TYPE_XMLHTTPREQUEST:
+    // alias nsIContentPolicy::TYPE_DATAREQUEST:
+    case nsIContentPolicy::TYPE_OBJECT_SUBREQUEST:
+    case nsIContentPolicy::TYPE_DTD:
+    case nsIContentPolicy::TYPE_FONT:
+      MOZ_ASSERT(false, "contentPolicyType not supported yet");
+      break;
+
+    case nsIContentPolicy::TYPE_MEDIA:
+      mimeTypeGuess = EmptyCString();
+      requestingContext = aLoadInfo->LoadingNode();
+#ifdef DEBUG
+      {
+        nsCOMPtr<mozilla::dom::Element> element = do_QueryInterface(requestingContext);
+        NS_ASSERTION(element != nullptr,
+                     "type_media requires requestingContext of type Element");
+      }
+#endif
+      break;
+
+    case nsIContentPolicy::TYPE_WEBSOCKET:
+    case nsIContentPolicy::TYPE_CSP_REPORT:
+    case nsIContentPolicy::TYPE_XSLT:
+    case nsIContentPolicy::TYPE_BEACON:
+    case nsIContentPolicy::TYPE_FETCH:
+    case nsIContentPolicy::TYPE_IMAGESET:
+      MOZ_ASSERT(false, "contentPolicyType not supported yet");
+      break;
+
+    default:
+      // nsIContentPolicy::TYPE_INVALID
+      MOZ_ASSERT(false, "can not perform security check without a valid contentType");
+  }
+
+  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
+  nsresult rv = NS_CheckContentLoadPolicy(contentPolicyType,
+                                          aURI,
+                                          aLoadInfo->LoadingPrincipal(),
+                                          requestingContext,
+                                          mimeTypeGuess,
+                                          nullptr,        //extra,
+                                          &shouldLoad,
+                                          nsContentUtils::GetContentPolicy(),
+                                          nsContentUtils::GetSecurityManager());
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_CP_REJECTED(shouldLoad)) {
+    return NS_ERROR_CONTENT_BLOCKED;
+  }
+  return NS_OK;
+}
+
+/*
+ * Based on the security flags provided in the loadInfo of the channel,
+ * doContentSecurityCheck() performs the following content security checks
+ * before opening the channel:
+ *
+ * (1) Same Origin Policy Check (if applicable)
+ * (2) Allow Cross Origin but perform sanity checks whether a principal
+ *     is allowed to access the following URL.
+ * (3) Perform CORS check (if applicable)
+ * (4) ContentPolicy checks (Content-Security-Policy, Mixed Content, ...)
+ *
+ * @param aChannel
+ *    The channel to perform the security checks on.
+ * @param aInAndOutListener
+ *    The streamListener that is passed to channel->AsyncOpen2() that is now potentially
+ *    wrappend within nsCORSListenerProxy() and becomes the corsListener that now needs
+ *    to be set as new streamListener on the channel.
+ */
+nsresult
+nsContentSecurityManager::doContentSecurityCheck(nsIChannel* aChannel,
+                                                 nsCOMPtr<nsIStreamListener>& aInAndOutListener)
+{
+  NS_ENSURE_ARG(aChannel);
+  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->GetLoadInfo();
+
+  if (!loadInfo) {
+    MOZ_ASSERT(false, "channel needs to have loadInfo to perform security checks");
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  // make sure that only one of the five security flags is set in the loadinfo
+  // e.g. do not require same origin and allow cross origin at the same time
+  nsresult rv = ValidateSecurityFlags(loadInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // lets store the initialSecurityCheckDone flag which indicates whether the channel
+  // was initialy evaluated by the contentSecurityManager. Once the inital
+  // asyncOpen() of the channel went through the contentSecurityManager then
+  // redirects do not have perform all the security checks, e.g. no reason
+  // to setup CORS again.
+  bool initialSecurityCheckDone = loadInfo->GetInitialSecurityCheckDone();
+
+  // now lets set the initalSecurityFlag for subsequent calls
+  rv = loadInfo->SetInitialSecurityCheckDone(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // since aChannel was openend using asyncOpen2() we have to make sure
+  // that redirects of that channel also get openend using asyncOpen2()
+  // please note that some implementations of ::AsyncOpen2 might already
+  // have set that flag to true (e.g. nsViewSourceChannel) in which case
+  // we just set the flag again.
+  rv = loadInfo->SetEnforceSecurity(true);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsCOMPtr<nsIURI> finalChannelURI;
+  rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(finalChannelURI));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Perform Same Origin Policy check
+  rv = DoSOPChecks(finalChannelURI, loadInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // if dealing with a redirected channel then we only enforce SOP
+  // and can return at this point.
+  if (initialSecurityCheckDone) {
+    return NS_OK;
+  }
+
+  rv = DoCheckLoadURIChecks(finalChannelURI, loadInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Check if CORS needs to be set up
+  rv = DoCORSChecks(aChannel, loadInfo, aInAndOutListener);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // Perform all ContentPolicy checks (MixedContent, CSP, ...)
+  rv = DoContentSecurityChecks(finalChannelURI, loadInfo);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // all security checks passed - lets allow the load
+  return NS_OK;
+}
new file mode 100644
--- /dev/null
+++ b/dom/security/nsContentSecurityManager.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* 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 nsContentSecurityManager_h___
+#define nsContentSecurityManager_h___
+
+#include "nsIChannel.h"
+class nsIStreamListener;
+
+class nsContentSecurityManager
+{
+private:
+  nsContentSecurityManager() {}
+  virtual ~nsContentSecurityManager() {}
+
+public:
+  static nsresult doContentSecurityCheck(nsIChannel* aChannel,
+                                         nsCOMPtr<nsIStreamListener>& aInAndOutListener);
+};
+
+#endif /* nsContentSecurityManager_h___ */
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -430,18 +430,25 @@ PushMessageData::PushMessageData(const n
   : mData(aData)
 {
 }
 
 PushMessageData::~PushMessageData()
 {
 }
 
-NS_IMPL_ISUPPORTS0(PushMessageData);
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(PushMessageData);
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessageData)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PushMessageData)
 
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushMessageData)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
 
 void
 PushMessageData::Json(JSContext* cx, JS::MutableHandle<JSObject*> aRetval)
 {
   //todo bug 1149195.  Don't be lazy.
    NS_ABORT();
 }
 
--- a/dom/workers/ServiceWorkerEvents.h
+++ b/dom/workers/ServiceWorkerEvents.h
@@ -158,17 +158,18 @@ public:
 #ifndef MOZ_SIMPLEPUSH
 
 class PushMessageData final : public nsISupports,
                               public nsWrapperCache
 {
   nsString mData;
 
 public:
-  NS_DECL_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushMessageData)
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     return mozilla::dom::PushMessageDataBinding_workers::Wrap(aCx, this, aGivenProto);
   }
 
   nsISupports* GetParentObject() const {
     return nullptr;
--- a/dom/workers/Workers.h
+++ b/dom/workers/Workers.h
@@ -335,17 +335,17 @@ public:
 
   // Generically useful function for running a bit of C++ code on the worker
   // thread.
   bool
   PostTask(WorkerTask* aTask);
 };
 
 WorkerCrossThreadDispatcher*
-GetWorkerCrossThreadDispatcher(JSContext* aCx, jsval aWorker);
+GetWorkerCrossThreadDispatcher(JSContext* aCx, JS::Value aWorker);
 
 // Random unique constant to facilitate JSPrincipal debugging
 const uint32_t kJSPrincipalsDebugToken = 0x7e2df9d2;
 
 namespace exceptions {
 
 // Implemented in Exceptions.cpp
 void
--- a/dom/workers/test/serviceworkers/fetch/context/context_test.js
+++ b/dom/workers/test/serviceworkers/fetch/context/context_test.js
@@ -41,18 +41,17 @@ self.addEventListener("fetch", function(
     respondToServiceWorker(event, "object");
   } else if (event.request.url.indexOf("font") >= 0) {
     respondToServiceWorker(event, "font");
   } else if (event.request.url.indexOf("iframe") >= 0) {
     if (event.request.context == "iframe") {
       event.respondWith(fetch("context_test.js"));
     }
   } else if (event.request.url.indexOf("frame") >= 0) {
-    // FIXME: Bug 1148044: This should be "frame".
-    if (event.request.context == "iframe") {
+    if (event.request.context == "frame") {
       event.respondWith(fetch("context_test.js"));
     }
   } else if (event.request.url.indexOf("newwindow") >= 0) {
     respondToServiceWorker(event, "newwindow");
   } else if (event.request.url.indexOf("ping") >= 0) {
     respondToServiceWorker(event, "ping");
   } else if (event.request.url.indexOf("plugin") >= 0) {
     respondToServiceWorker(event, "plugin");
--- a/dom/workers/test/serviceworkers/fetch/context/index.html
+++ b/dom/workers/test/serviceworkers/fetch/context/index.html
@@ -123,18 +123,17 @@
     });
 
     return new Promise(function(resolve, reject) {
       var embed = document.createElement("embed");
       embed.src = "embed";
       document.documentElement.appendChild(embed);
       navigator.serviceWorker.addEventListener("message", function onMessage(e) {
         if (e.data.data == "embed") {
-          // FIXME: Bug 1148030: This should be "embed".
-          is(e.data.context, "object", "Expected the object context on an embed");
+          is(e.data.context, "embed", "Expected the object context on an embed");
           navigator.serviceWorker.removeEventListener("message", onMessage);
           resolve();
         }
       }, false);
     });
   }
 
   function testObject() {
--- a/embedding/components/windowwatcher/nsWindowWatcher.cpp
+++ b/embedding/components/windowwatcher/nsWindowWatcher.cpp
@@ -557,17 +557,18 @@ nsWindowWatcher::OpenWindowInternal(nsID
   }
 
   // Make sure we call CalculateChromeFlags() *before* we push the
   // callee context onto the context stack so that
   // CalculateChromeFlags() sees the actual caller when doing its
   // security checks.
   chromeFlags = CalculateChromeFlags(aParent, features.get(), featuresSpecified,
                                      aDialog, uriToLoadIsChrome,
-                                     hasChromeParent, openedFromRemoteTab);
+                                     hasChromeParent, aCalledFromJS,
+                                     openedFromRemoteTab);
 
   // If we are opening a window from a remote browser, the resulting window
   // should also be remote.
   MOZ_ASSERT_IF(openedFromRemoteTab,
                 chromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
 
   // If we're not called through our JS version of the API, and we got
   // our internal modal option, treat the window we're opening as a
@@ -1461,18 +1462,18 @@ nsWindowWatcher::URIfromURL(const char* 
   }
 
   // build and return the absolute URI
   return NS_NewURI(aURI, aURL, baseURI);
 }
 
 #define NS_CALCULATE_CHROME_FLAG_FOR(feature, flag)                            \
   prefBranch->GetBoolPref(feature, &forceEnable);                              \
-  if (forceEnable && !(aDialog && isCallerChrome) &&                           \
-      !(isCallerChrome && aHasChromeParent) && !aChromeURL) {                  \
+  if (forceEnable && !(aDialog && !openedFromContentScript) &&                 \
+      !(!openedFromContentScript && aHasChromeParent) && !aChromeURL) {                  \
     chromeFlags |= flag;                                                       \
   } else {                                                                     \
     chromeFlags |=                                                             \
       WinHasOption(aFeatures, feature, 0, &presenceFlag) ? flag : 0;           \
   }
 
 /**
  * Calculate the chrome bitmask from a string list of features.
@@ -1486,69 +1487,75 @@ nsWindowWatcher::URIfromURL(const char* 
 // static
 uint32_t
 nsWindowWatcher::CalculateChromeFlags(nsIDOMWindow* aParent,
                                       const char* aFeatures,
                                       bool aFeaturesSpecified,
                                       bool aDialog,
                                       bool aChromeURL,
                                       bool aHasChromeParent,
+                                      bool aCalledFromJS,
                                       bool aOpenedFromRemoteTab)
 {
+  const bool inContentProcess = XRE_IsContentProcess();
   uint32_t chromeFlags = 0;
-  bool isCallerChrome =
-    nsContentUtils::IsCallerChrome() && !aOpenedFromRemoteTab;
 
-  bool onlyPrivateFlag = aFeaturesSpecified && aFeatures && isCallerChrome &&
-    nsCRT::strcasecmp(aFeatures, "private") == 0;
-  if (!aFeaturesSpecified || !aFeatures || onlyPrivateFlag) {
+  if (!aFeaturesSpecified || !aFeatures) {
     chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
     if (aDialog) {
       chromeFlags |= nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
                      nsIWebBrowserChrome::CHROME_OPENAS_CHROME;
     }
-    if (onlyPrivateFlag) {
-      chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
+
+    if (inContentProcess) {
+      return chromeFlags;
     }
-    return chromeFlags;
+  } else {
+    chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
   }
 
+  bool openedFromContentScript =
+    aOpenedFromRemoteTab ? aCalledFromJS
+                         : !nsContentUtils::IsCallerChrome();
+
   /* This function has become complicated since browser windows and
      dialogs diverged. The difference is, browser windows assume all
      chrome not explicitly mentioned is off, if the features string
      is not null. Exceptions are some OS border chrome new with Mozilla.
      Dialogs interpret a (mostly) empty features string to mean
      "OS's choice," and also support an "all" flag explicitly disallowed
      in the standards-compliant window.(normal)open. */
 
   bool presenceFlag = false;
-
-  chromeFlags = nsIWebBrowserChrome::CHROME_WINDOW_BORDERS;
   if (aDialog && WinHasOption(aFeatures, "all", 0, &presenceFlag)) {
     chromeFlags = nsIWebBrowserChrome::CHROME_ALL;
   }
 
   /* Next, allow explicitly named options to override the initial settings */
 
-  // Determine whether the window is a private browsing window
-  if (isCallerChrome) {
+  if (!inContentProcess && !openedFromContentScript) {
+    // Determine whether the window is a private browsing window
     chromeFlags |= WinHasOption(aFeatures, "private", 0, &presenceFlag) ?
       nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW : 0;
     chromeFlags |= WinHasOption(aFeatures, "non-private", 0, &presenceFlag) ?
       nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW : 0;
   }
 
-  // Determine whether the window should have remote tabs.
-  if (isCallerChrome || aOpenedFromRemoteTab) {
-    bool remote;
-    if (BrowserTabsRemoteAutostart()) {
-      remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag);
-    } else {
-      remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag);
+  if (!inContentProcess) {
+    // Determine whether the window should have remote tabs.
+    bool remote = BrowserTabsRemoteAutostart();
+
+    if (!openedFromContentScript) {
+      if (remote) {
+        remote = !WinHasOption(aFeatures, "non-remote", 0, &presenceFlag);
+      } else {
+        remote = WinHasOption(aFeatures, "remote", 0, &presenceFlag);
+      }
     }
+
     if (remote) {
       chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
     }
   }
 
   nsresult rv;
 
   nsCOMPtr<nsIPrefBranch> prefBranch;
@@ -1639,17 +1646,17 @@ nsWindowWatcher::CalculateChromeFlags(ns
   nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
   branch->GetBoolPref("dom.disable_window_open_dialog_feature",
                       &disableDialogFeature);
 
   bool isFullScreen = false;
   if (aParent) {
     aParent->GetFullScreen(&isFullScreen);
   }
-  if (isFullScreen && !isCallerChrome) {
+  if (isFullScreen && openedFromContentScript) {
     // If the parent window is in fullscreen & the caller context is content,
     // dialog feature is disabled. (see bug 803675)
     disableDialogFeature = true;
   }
 
   if (!disableDialogFeature) {
     chromeFlags |= WinHasOption(aFeatures, "dialog", 0, nullptr) ?
       nsIWebBrowserChrome::CHROME_OPENAS_DIALOG : 0;
@@ -1666,17 +1673,17 @@ nsWindowWatcher::CalculateChromeFlags(ns
     }
   }
 
   /* missing
      chromeFlags->copy_history
    */
 
   // Check security state for use in determing window dimensions
-  if (!isCallerChrome || !aHasChromeParent) {
+  if (openedFromContentScript || !aHasChromeParent) {
     // If priv check fails (or if we're called from chrome, but the
     // parent is not a chrome window), set all elements to minimum
     // reqs., else leave them alone.
     chromeFlags |= nsIWebBrowserChrome::CHROME_TITLEBAR;
     chromeFlags |= nsIWebBrowserChrome::CHROME_WINDOW_CLOSE;
     chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_LOWERED;
     chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_RAISED;
     chromeFlags &= ~nsIWebBrowserChrome::CHROME_WINDOW_POPUP;
@@ -2265,19 +2272,26 @@ nsWindowWatcher::GetWindowOpenLocation(n
       // in the current window with no features (see bug 803675)
       restrictionPref = 0;
     }
 
     if (restrictionPref == 1) {
       return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
     }
 
-    if (restrictionPref == 2 &&
-        // Only continue if there are no size/position features and no special
-        // chrome flags.
-        (aChromeFlags != nsIWebBrowserChrome::CHROME_ALL ||
-         aPositionSpecified || aSizeSpecified)) {
-      return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+    if (restrictionPref == 2) {
+      // Only continue if there are no size/position features and no special
+      // chrome flags - with the exception of the remoteness and private flags,
+      // which might have been automatically flipped by Gecko.
+      int32_t uiChromeFlags = aChromeFlags;
+      uiChromeFlags &= ~(nsIWebBrowserChrome::CHROME_REMOTE_WINDOW |
+                         nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
+                         nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
+                         nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
+      if (uiChromeFlags != nsIWebBrowserChrome::CHROME_ALL ||
+          aPositionSpecified || aSizeSpecified) {
+        return nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+      }
     }
   }
 
   return containerPref;
 }
--- a/embedding/components/windowwatcher/nsWindowWatcher.h
+++ b/embedding/components/windowwatcher/nsWindowWatcher.h
@@ -91,16 +91,17 @@ protected:
                              nsIURI** aURI);
 
   static uint32_t CalculateChromeFlags(nsIDOMWindow* aParent,
                                        const char* aFeatures,
                                        bool aFeaturesSpecified,
                                        bool aDialog,
                                        bool aChromeURL,
                                        bool aHasChromeParent,
+                                       bool aCalledFromJS,
                                        bool aOpenedFromRemoteTab);
   static int32_t WinHasOption(const char* aOptions, const char* aName,
                               int32_t aDefault, bool* aPresenceFlag);
   /* Compute the right SizeSpec based on aFeatures */
   static void CalcSizeSpec(const char* aFeatures, SizeSpec& aResult);
   static nsresult ReadyOpenedDocShellItem(nsIDocShellTreeItem* aOpenedItem,
                                           nsIDOMWindow* aParent,
                                           bool aWindowIsNew,
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -335,102 +335,105 @@ UpgradeHostToOriginAndInsert(const nsACS
                            aExpireType, aExpireTime, aModificationTime);
     return NS_OK;
   }
 
   // The user may use this host at non-standard ports or protocols, we can use their history
   // to guess what ports and protocols we want to add permissions for.
   // We find every URI which they have visited with this host (or a subdomain of this host),
   // and try to add it as a principal.
+  bool foundHistory = false;
+
   nsCOMPtr<nsINavHistoryService> histSrv = do_GetService(NS_NAVHISTORYSERVICE_CONTRACTID);
 
-  nsCOMPtr<nsINavHistoryQuery> histQuery;
-  rv = histSrv->GetNewQuery(getter_AddRefs(histQuery));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // We want to only find history items for this particular host, and subdomains
-  rv = histQuery->SetDomain(aHost);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = histQuery->SetDomainIsHost(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsINavHistoryQueryOptions> histQueryOpts;
-  rv = histSrv->GetNewQueryOptions(getter_AddRefs(histQueryOpts));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // We want to get the URIs for every item in the user's history with the given host
-  rv = histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // We only search history, because searching both bookmarks and history
-  // is not supported, and history tends to be more comprehensive.
-  rv = histQueryOpts->SetQueryType(nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // We include hidden URIs (such as those visited via iFrames) as they may have permissions too
-  rv = histQueryOpts->SetIncludeHidden(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsINavHistoryResult> histResult;
-  rv = histSrv->ExecuteQuery(histQuery, histQueryOpts, getter_AddRefs(histResult));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  nsCOMPtr<nsINavHistoryContainerResultNode> histResultContainer;
-  rv = histResult->GetRoot(getter_AddRefs(histResultContainer));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = histResultContainer->SetContainerOpen(true);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  uint32_t childCount = 0;
-  rv = histResultContainer->GetChildCount(&childCount);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  bool foundHistory = false;
-  for (uint32_t i = 0; i < childCount; i++) {
-    nsCOMPtr<nsINavHistoryResultNode> child;
-    histResultContainer->GetChild(i, getter_AddRefs(child));
-    if (NS_FAILED(rv)) continue;
-
-    uint32_t type;
-    rv = child->GetType(&type);
-    if (NS_FAILED(rv) || type != nsINavHistoryResultNode::RESULT_TYPE_URI) {
-      NS_WARNING("Unexpected non-RESULT_TYPE_URI node in "
-                 "UpgradeHostToOriginAndInsert()");
-      continue;
+  if (histSrv) {
+    nsCOMPtr<nsINavHistoryQuery> histQuery;
+    rv = histSrv->GetNewQuery(getter_AddRefs(histQuery));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // We want to only find history items for this particular host, and subdomains
+    rv = histQuery->SetDomain(aHost);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = histQuery->SetDomainIsHost(false);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsINavHistoryQueryOptions> histQueryOpts;
+    rv = histSrv->GetNewQueryOptions(getter_AddRefs(histQueryOpts));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // We want to get the URIs for every item in the user's history with the given host
+    rv = histQueryOpts->SetResultType(nsINavHistoryQueryOptions::RESULTS_AS_URI);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // We only search history, because searching both bookmarks and history
+    // is not supported, and history tends to be more comprehensive.
+    rv = histQueryOpts->SetQueryType(nsINavHistoryQueryOptions::QUERY_TYPE_HISTORY);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    // We include hidden URIs (such as those visited via iFrames) as they may have permissions too
+    rv = histQueryOpts->SetIncludeHidden(true);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsINavHistoryResult> histResult;
+    rv = histSrv->ExecuteQuery(histQuery, histQueryOpts, getter_AddRefs(histResult));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    nsCOMPtr<nsINavHistoryContainerResultNode> histResultContainer;
+    rv = histResult->GetRoot(getter_AddRefs(histResultContainer));
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    rv = histResultContainer->SetContainerOpen(true);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    uint32_t childCount = 0;
+    rv = histResultContainer->GetChildCount(&childCount);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    for (uint32_t i = 0; i < childCount; i++) {
+      nsCOMPtr<nsINavHistoryResultNode> child;
+      histResultContainer->GetChild(i, getter_AddRefs(child));
+      if (NS_FAILED(rv)) continue;
+
+      uint32_t type;
+      rv = child->GetType(&type);
+      if (NS_FAILED(rv) || type != nsINavHistoryResultNode::RESULT_TYPE_URI) {
+        NS_WARNING("Unexpected non-RESULT_TYPE_URI node in "
+                   "UpgradeHostToOriginAndInsert()");
+        continue;
+      }
+
+      nsAutoCString uriSpec;
+      rv = child->GetUri(uriSpec);
+      if (NS_FAILED(rv)) continue;
+
+      nsCOMPtr<nsIURI> uri;
+      rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
+      if (NS_FAILED(rv)) continue;
+
+      // Use the provided host - this URI may be for a subdomain, rather than the host we care about.
+      rv = uri->SetHost(aHost);
+      if (NS_FAILED(rv)) continue;
+
+      // We now have a URI which we can make a nsIPrincipal out of
+      nsCOMPtr<nsIPrincipal> principal;
+      rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal));
+      if (NS_FAILED(rv)) continue;
+
+      // Insert it! (The backend should be able to deal with us inserting the same origin repeatedly)
+      foundHistory = true;
+      rv = aHelper->Insert(principal, aType, aPermission,
+                           aExpireType, aExpireTime, aModificationTime);
+      NS_WARN_IF(NS_FAILED(rv));
     }
 
-    nsAutoCString uriSpec;
-    rv = child->GetUri(uriSpec);
-    if (NS_FAILED(rv)) continue;
-
-    nsCOMPtr<nsIURI> uri;
-    rv = NS_NewURI(getter_AddRefs(uri), uriSpec);
-    if (NS_FAILED(rv)) continue;
-
-    // Use the provided host - this URI may be for a subdomain, rather than the host we care about.
-    rv = uri->SetHost(aHost);
-    if (NS_FAILED(rv)) continue;
-
-    // We now have a URI which we can make a nsIPrincipal out of
-    nsCOMPtr<nsIPrincipal> principal;
-    rv = GetPrincipal(uri, aAppId, aIsInBrowserElement, getter_AddRefs(principal));
-    if (NS_FAILED(rv)) continue;
-
-    // Insert it! (The backend should be able to deal with us inserting the same origin repeatedly)
-    foundHistory = true;
-    rv = aHelper->Insert(principal, aType, aPermission,
-                         aExpireType, aExpireTime, aModificationTime);
-    NS_WARN_IF(NS_FAILED(rv));
+    rv = histResultContainer->SetContainerOpen(false);
+    NS_ENSURE_SUCCESS(rv, rv);
   }
 
-  rv = histResultContainer->SetContainerOpen(false);
-  NS_ENSURE_SUCCESS(rv, rv);
-
   // If we didn't find any origins for this host in the poermissions database,
   // we can insert the default http:// and https:// permissions into the database.
   // This has a relatively high liklihood of applying the permission to the correct
   // origin.
   if (!foundHistory) {
     rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_CSTRING("http://") + aHost);
     if (NS_SUCCEEDED(rv)) {
       nsCOMPtr<nsIPrincipal> principal;
--- a/gfx/gl/GLDefs.h
+++ b/gfx/gl/GLDefs.h
@@ -39,16 +39,22 @@
 // EGL_KHR_image_base (not supplied by EGL_KHR_image!)
 #define LOCAL_EGL_IMAGE_PRESERVED                       0x30D2
 
 // AMD_compressed_ATC_texture
 #define LOCAL_GL_ATC_RGB                                0x8C92
 #define LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA                0x8C93
 #define LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA            0x87EE
 
+// EGL_ANDROID_image_crop
+#define LOCAL_EGL_IMAGE_CROP_LEFT_ANDROID               0x3148
+#define LOCAL_EGL_IMAGE_CROP_TOP_ANDROID                0x3149
+#define LOCAL_EGL_IMAGE_CROP_RIGHT_ANDROID              0x314A
+#define LOCAL_EGL_IMAGE_CROP_BOTTOM_ANDROID             0x314B
+
 // Others
 #define LOCAL_EGL_PRESERVED_RESOURCES                   0x3030
 #define LOCAL_EGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_EXT 0x3138
 
 #define LOCAL_GL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
 #define LOCAL_GL_CONTEXT_LOST                           0x9242
 #define LOCAL_GL_CONTEXT_FLAGS_ARB                      0x2094
 #define LOCAL_GL_CONTEXT_CORE_PROFILE_BIT_ARB           0x00000001
--- a/gfx/gl/GLLibraryEGL.cpp
+++ b/gfx/gl/GLLibraryEGL.cpp
@@ -31,16 +31,17 @@ static const char *sEGLExtensionNames[] 
     "EGL_KHR_image_pixmap",
     "EGL_KHR_gl_texture_2D_image",
     "EGL_KHR_lock_surface",
     "EGL_ANGLE_surface_d3d_texture_2d_share_handle",
     "EGL_EXT_create_context_robustness",
     "EGL_KHR_image",
     "EGL_KHR_fence_sync",
     "EGL_ANDROID_native_fence_sync",
+    "EGL_ANDROID_image_crop",
     "ANGLE_platform_angle",
     "ANGLE_platform_angle_d3d"
 };
 
 #if defined(ANDROID)
 
 static PRLibrary* LoadApitraceLibrary()
 {
--- a/gfx/gl/GLLibraryEGL.h
+++ b/gfx/gl/GLLibraryEGL.h
@@ -126,16 +126,17 @@ public:
         KHR_image_pixmap,
         KHR_gl_texture_2D_image,
         KHR_lock_surface,
         ANGLE_surface_d3d_texture_2d_share_handle,
         EXT_create_context_robustness,
         KHR_image,
         KHR_fence_sync,
         ANDROID_native_fence_sync,
+        EGL_ANDROID_image_crop,
         ANGLE_platform_angle,
         ANGLE_platform_angle_d3d,
         Extensions_Max
     };
 
     bool IsExtensionSupported(EGLExtensions aKnownExtension) const {
         return mAvailableExtensions[aKnownExtension];
     }
--- a/gfx/layers/composite/ImageHost.cpp
+++ b/gfx/layers/composite/ImageHost.cpp
@@ -90,16 +90,19 @@ ImageHost::UseTextureHost(const nsTArray
   }
   // Recycle any leftover mTextureSources and call PrepareTextureSource on all
   // images.
   for (auto& img : newImages) {
     if (!img.mTextureSource && !mImages.IsEmpty()) {
       img.mTextureSource = mImages.LastElement().mTextureSource;
       mImages.RemoveElementAt(mImages.Length() - 1);
     }
+    // SetCropRect() affects only on a specific platform.
+    // If it is not implemented, it does nothing.
+    img.mFrontBuffer->SetCropRect(img.mPictureRect);
     img.mFrontBuffer->Updated();
     img.mFrontBuffer->PrepareTextureSource(img.mTextureSource);
   }
   mImages.SwapElements(newImages);
 }
 
 void
 ImageHost::RemoveTextureHost(TextureHost* aTexture)
--- a/gfx/layers/composite/TextureHost.h
+++ b/gfx/layers/composite/TextureHost.h
@@ -413,16 +413,21 @@ public:
    *
    * This is important to ensure the correctness of the deallocation protocol.
    */
   virtual void ForgetSharedData() {}
 
   virtual gfx::IntSize GetSize() const = 0;
 
   /**
+   * Should be overridden if TextureHost supports crop rect.
+   */
+  virtual void SetCropRect(nsIntRect aCropRect) {}
+
+  /**
    * Debug facility.
    * XXX - cool kids use Moz2D. See bug 882113.
    */
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() = 0;
 
   /**
    * XXX - Flags should only be set at creation time, this will be removed.
    */
--- a/gfx/layers/ipc/LayersSurfaces.ipdlh
+++ b/gfx/layers/ipc/LayersSurfaces.ipdlh
@@ -79,22 +79,16 @@ struct EGLImageDescriptor {
   uintptr_t image; // `EGLImage` is a `void*`.
   uintptr_t fence;
   IntSize size;
   bool hasAlpha;
 };
 
 struct NewSurfaceDescriptorGralloc {
   MaybeMagicGrallocBufferHandle buffer;
-  /**
-   * android::GraphicBuffer has a size information. But there are cases
-   * that GraphicBuffer's size and actual video's size are different.
-   * Extra size member is necessary. See Bug 850566.
-   */
-  IntSize size;
   bool isOpaque;
 };
 
 /**
  * Used for shmem-backed YCbCr and (flavors of) RGBA textures
  */
 struct SurfaceDescriptorShmem {
   Shmem data;
--- a/gfx/layers/opengl/EGLImageHelpers.cpp
+++ b/gfx/layers/opengl/EGLImageHelpers.cpp
@@ -10,27 +10,42 @@
 
 namespace mozilla
 {
 namespace layers {
 
 using namespace gl;
 
 EGLImage
-EGLImageCreateFromNativeBuffer(GLContext* aGL, void* aBuffer)
+EGLImageCreateFromNativeBuffer(GLContext* aGL, void* aBuffer, const gfx::IntSize& aCropSize)
 {
     EGLint attrs[] = {
         LOCAL_EGL_IMAGE_PRESERVED, LOCAL_EGL_TRUE,
-        LOCAL_EGL_NONE, LOCAL_EGL_NONE
+        LOCAL_EGL_NONE, LOCAL_EGL_NONE,
     };
 
+    EGLint cropAttrs[] = {
+        LOCAL_EGL_IMAGE_PRESERVED, LOCAL_EGL_TRUE,
+        LOCAL_EGL_IMAGE_CROP_LEFT_ANDROID, 0,
+        LOCAL_EGL_IMAGE_CROP_TOP_ANDROID, 0,
+        LOCAL_EGL_IMAGE_CROP_RIGHT_ANDROID, aCropSize.width,
+        LOCAL_EGL_IMAGE_CROP_BOTTOM_ANDROID, aCropSize.height,
+        LOCAL_EGL_NONE, LOCAL_EGL_NONE,
+    };
+
+    bool hasCropRect = (aCropSize.width != 0 && aCropSize.height != 0);
+    EGLint* usedAttrs = attrs;
+    if (hasCropRect && sEGLLibrary.IsExtensionSupported(GLLibraryEGL::EGL_ANDROID_image_crop)) {
+        usedAttrs = cropAttrs;
+    }
+
     return sEGLLibrary.fCreateImage(sEGLLibrary.Display(),
                                      EGL_NO_CONTEXT,
                                      LOCAL_EGL_NATIVE_BUFFER_ANDROID,
-                                     aBuffer, attrs);
+                                     aBuffer, usedAttrs);
 }
 
 void
 EGLImageDestroy(GLContext* aGL, EGLImage aImage)
 {
     sEGLLibrary.fDestroyImage(sEGLLibrary.Display(), aImage);
 }
 
--- a/gfx/layers/opengl/EGLImageHelpers.h
+++ b/gfx/layers/opengl/EGLImageHelpers.h
@@ -2,24 +2,26 @@
 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
 /* 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 EGLIMAGEHELPERS_H_
 #define EGLIMAGEHELPERS_H_
 
+#include "mozilla/gfx/Point.h"
+
 typedef void* EGLImage;
 
 namespace mozilla {
 namespace gl {
     class GLContext;
 }
 
 namespace layers {
 
-EGLImage EGLImageCreateFromNativeBuffer(gl::GLContext* aGL, void* aBuffer);
+EGLImage EGLImageCreateFromNativeBuffer(gl::GLContext* aGL, void* aBuffer, const gfx::IntSize& aCropSize);
 void EGLImageDestroy(gl::GLContext* aGL, EGLImage aImage);
 
 } // namespace layers
 } // namespace mozilla
 
 #endif // EGLIMAGEHELPERS_H_
--- a/gfx/layers/opengl/GrallocTextureClient.cpp
+++ b/gfx/layers/opengl/GrallocTextureClient.cpp
@@ -66,17 +66,17 @@ GrallocTextureClientOGL::CreateSimilar(T
 bool
 GrallocTextureClientOGL::ToSurfaceDescriptor(SurfaceDescriptor& aOutDescriptor)
 {
   MOZ_ASSERT(IsValid());
   if (!IsAllocated()) {
     return false;
   }
 
-  aOutDescriptor = NewSurfaceDescriptorGralloc(mGrallocHandle, mSize, mIsOpaque);
+  aOutDescriptor = NewSurfaceDescriptorGralloc(mGrallocHandle, mIsOpaque);
   return true;
 }
 
 void
 GrallocTextureClientOGL::SetRemoveFromCompositableWaiter(AsyncTransactionWaiter* aWaiter)
 {
   mRemoveFromCompositableWaiter = aWaiter;
 }
--- a/gfx/layers/opengl/GrallocTextureClient.h
+++ b/gfx/layers/opengl/GrallocTextureClient.h
@@ -134,23 +134,16 @@ protected:
   /**
    * Points to a mapped gralloc buffer between calls to lock and unlock.
    * Should be null outside of the lock-unlock pair.
    */
   uint8_t* mMappedBuffer;
 
   RefPtr<gfx::DrawTarget> mDrawTarget;
 
-  /**
-   * android::GraphicBuffer has a size information. But there are cases
-   * that GraphicBuffer's size and actual video's size are different.
-   * Extra size member is necessary. See Bug 850566.
-   */
-  gfx::IntSize mSize;
-
   android::MediaBuffer* mMediaBuffer;
 
   bool mIsOpaque;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/opengl/GrallocTextureHost.cpp
+++ b/gfx/layers/opengl/GrallocTextureHost.cpp
@@ -98,29 +98,30 @@ TextureTargetForAndroidPixelFormat(andro
   }
 }
 
 GrallocTextureHostOGL::GrallocTextureHostOGL(TextureFlags aFlags,
                                              const NewSurfaceDescriptorGralloc& aDescriptor)
   : TextureHost(aFlags)
   , mGrallocHandle(aDescriptor)
   , mSize(0, 0)
-  , mDescriptorSize(aDescriptor.size())
+  , mCropSize(0, 0)
   , mFormat(gfx::SurfaceFormat::UNKNOWN)
   , mEGLImage(EGL_NO_IMAGE)
   , mIsOpaque(aDescriptor.isOpaque())
 {
   android::GraphicBuffer* graphicBuffer = GetGraphicBufferFromDesc(mGrallocHandle).get();
   MOZ_ASSERT(graphicBuffer);
 
   if (graphicBuffer) {
     mFormat =
       SurfaceFormatForAndroidPixelFormat(graphicBuffer->getPixelFormat(),
                                          aFlags & TextureFlags::RB_SWAPPED);
     mSize = gfx::IntSize(graphicBuffer->getWidth(), graphicBuffer->getHeight());
+    mCropSize = mSize;
   } else {
     printf_stderr("gralloc buffer is nullptr");
   }
 }
 
 GrallocTextureHostOGL::~GrallocTextureHostOGL()
 {
   DestroyEGLImage();
@@ -217,17 +218,17 @@ GrallocTextureHostOGL::GetRenderState()
     }
     if (mFlags & TextureFlags::ORIGIN_BOTTOM_LEFT) {
       flags |= LayerRenderStateFlags::ORIGIN_BOTTOM_LEFT;
     }
     if (mFlags & TextureFlags::RB_SWAPPED) {
       flags |= LayerRenderStateFlags::FORMAT_RB_SWAP;
     }
     return LayerRenderState(graphicBuffer,
-                            mDescriptorSize,
+                            mCropSize,
                             flags,
                             this);
   }
 
   return LayerRenderState();
 }
 
 already_AddRefed<gfx::DataSourceSurface>
@@ -339,18 +340,22 @@ GrallocTextureHostOGL::PrepareTextureSou
 
   gl::GLContext* gl = GetGLContext();
   if (!gl || !gl->MakeCurrent()) {
     mGLTextureSource = nullptr;
     return;
   }
 
   if (mEGLImage == EGL_NO_IMAGE) {
+    gfx::IntSize cropSize(0, 0);
+    if (mCropSize != mSize) {
+      cropSize = mCropSize;
+    }
     // Should only happen the first time.
-    mEGLImage = EGLImageCreateFromNativeBuffer(gl, graphicBuffer->getNativeBuffer());
+    mEGLImage = EGLImageCreateFromNativeBuffer(gl, graphicBuffer->getNativeBuffer(), cropSize);
   }
 
   GLenum textureTarget = GetTextureTarget(gl, graphicBuffer->getPixelFormat());
 
   GLTextureSource* glSource = aTextureSource.get() ?
     aTextureSource->AsSourceOGL()->AsGLTextureSource() : nullptr;
 
   bool shouldCreateTextureSource = !glSource  || !glSource->IsValid()
@@ -409,16 +414,33 @@ GrallocTextureHostOGL::WaitAcquireFenceH
                                               0,
                                               400000000 /*400 usec*/);
   if (status != LOCAL_EGL_CONDITION_SATISFIED) {
     NS_ERROR("failed to wait native fence sync");
   }
   MOZ_ALWAYS_TRUE( sEGLLibrary.fDestroySync(EGL_DISPLAY(), sync) );
 }
 
+void
+GrallocTextureHostOGL::SetCropRect(nsIntRect aCropRect)
+{
+  MOZ_ASSERT(aCropRect.TopLeft() == IntPoint(0, 0));
+  MOZ_ASSERT(!aCropRect.IsEmpty());
+  MOZ_ASSERT(aCropRect.width <= mSize.width);
+  MOZ_ASSERT(aCropRect.height <= mSize.height);
+
+  gfx::IntSize cropSize(aCropRect.width, aCropRect.height);
+  if (mCropSize == cropSize) {
+    return;
+  }
+
+  mCropSize = cropSize;
+  mGLTextureSource = nullptr;
+}
+
 bool
 GrallocTextureHostOGL::BindTextureSource(CompositableTextureSourceRef& aTextureSource)
 {
   // This happens at composition time.
 
   // If mGLTextureSource is null it means PrepareTextureSource failed.
   if (!mGLTextureSource) {
     return false;
--- a/gfx/layers/opengl/GrallocTextureHost.h
+++ b/gfx/layers/opengl/GrallocTextureHost.h
@@ -33,47 +33,49 @@ public:
   virtual void DeallocateSharedData() override;
 
   virtual void ForgetSharedData() override;
 
   virtual void DeallocateDeviceData() override;
 
   virtual gfx::SurfaceFormat GetFormat() const;
 
-  virtual gfx::IntSize GetSize() const override { return mDescriptorSize; }
+  virtual gfx::IntSize GetSize() const override { return mCropSize; }
 
   virtual LayerRenderState GetRenderState() override;
 
   virtual void PrepareTextureSource(CompositableTextureSourceRef& aTextureSource) override;
 
   virtual bool BindTextureSource(CompositableTextureSourceRef& aTextureSource) override;
 
   virtual void UnbindTextureSource() override;
 
   virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override;
 
   virtual void WaitAcquireFenceHandleSyncComplete() override;
 
+  virtual void SetCropRect(nsIntRect aCropRect) override;
+
   bool IsValid() const;
 
   virtual const char* Name() override { return "GrallocTextureHostOGL"; }
 
   gl::GLContext* GetGLContext() const { return mCompositor ? mCompositor->gl() : nullptr; }
 
 private:
   void DestroyEGLImage();
 
   NewSurfaceDescriptorGralloc mGrallocHandle;
   RefPtr<GLTextureSource> mGLTextureSource;
   RefPtr<CompositorOGL> mCompositor;
   // Size reported by the GraphicBuffer
   gfx::IntSize mSize;
   // Size reported by TextureClient, can be different in some cases (video?),
   // used by LayerRenderState.
-  gfx::IntSize mDescriptorSize;
+  gfx::IntSize mCropSize;
   gfx::SurfaceFormat mFormat;
   EGLImage mEGLImage;
   bool mIsOpaque;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/image/FrameAnimator.cpp
+++ b/image/FrameAnimator.cpp
@@ -268,18 +268,17 @@ FrameAnimator::GetFirstFrameRefreshArea(
 
 LookupResult
 FrameAnimator::GetCompositedFrame(uint32_t aFrameNum)
 {
   MOZ_ASSERT(aFrameNum != 0, "First frame is never composited");
 
   // If we have a composited version of this frame, return that.
   if (mLastCompositedFrameIndex == int32_t(aFrameNum)) {
-    return LookupResult(mCompositingFrame->DrawableRef(),
-                        /* aIsExactMatch = */ true);
+    return LookupResult(mCompositingFrame->DrawableRef(), MatchType::EXACT);
   }
 
   // Otherwise return the raw frame. DoBlend is required to ensure that we only
   // hit this case if the frame is not paletted and doesn't require compositing.
   LookupResult result =
     SurfaceCache::Lookup(ImageKey(mImage),
                          RasterSurfaceKey(mSize,
                                           0,  // Default decode flags.
--- a/image/LookupResult.h
+++ b/image/LookupResult.h
@@ -13,57 +13,78 @@
 
 #include "mozilla/Attributes.h"
 #include "mozilla/Move.h"
 #include "imgFrame.h"
 
 namespace mozilla {
 namespace image {
 
+enum class MatchType : uint8_t
+{
+  NOT_FOUND,  // No matching surface and no placeholder.
+  PENDING,    // Found a matching placeholder, but no surface.
+  EXACT,      // Found a surface that matches exactly.
+  SUBSTITUTE_BECAUSE_NOT_FOUND,  // No exact match, but found a similar one.
+  SUBSTITUTE_BECAUSE_PENDING     // Found a similar surface and a placeholder
+                                 // for an exact match.
+};
+
 /**
  * LookupResult is the return type of SurfaceCache's Lookup*() functions. It
  * combines a surface with relevant metadata tracked by SurfaceCache.
  */
 class MOZ_STACK_CLASS LookupResult
 {
 public:
-  LookupResult()
-    : mIsExactMatch(false)
-  { }
+  explicit LookupResult(MatchType aMatchType)
+    : mMatchType(aMatchType)
+  {
+    MOZ_ASSERT(mMatchType == MatchType::NOT_FOUND ||
+               mMatchType == MatchType::PENDING,
+               "Only NOT_FOUND or PENDING make sense with no surface");
+  }
 
   LookupResult(LookupResult&& aOther)
     : mDrawableRef(Move(aOther.mDrawableRef))
-    , mIsExactMatch(aOther.mIsExactMatch)
+    , mMatchType(aOther.mMatchType)
   { }
 
-  LookupResult(DrawableFrameRef&& aDrawableRef, bool aIsExactMatch)
+  LookupResult(DrawableFrameRef&& aDrawableRef, MatchType aMatchType)
     : mDrawableRef(Move(aDrawableRef))
-    , mIsExactMatch(aIsExactMatch)
-  { }
+    , mMatchType(aMatchType)
+  {
+    MOZ_ASSERT(!mDrawableRef || !(mMatchType == MatchType::NOT_FOUND ||
+                                  mMatchType == MatchType::PENDING),
+               "Only NOT_FOUND or PENDING make sense with no surface");
+    MOZ_ASSERT(mDrawableRef || mMatchType == MatchType::NOT_FOUND ||
+                               mMatchType == MatchType::PENDING,
+               "NOT_FOUND or PENDING do not make sense with a surface");
+  }
 
   LookupResult& operator=(LookupResult&& aOther)
   {
     MOZ_ASSERT(&aOther != this, "Self-move-assignment is not supported");
     mDrawableRef = Move(aOther.mDrawableRef);
-    mIsExactMatch = aOther.mIsExactMatch;
+    mMatchType = aOther.mMatchType;
     return *this;
   }
 
   DrawableFrameRef& DrawableRef() { return mDrawableRef; }
   const DrawableFrameRef& DrawableRef() const { return mDrawableRef; }
 
   /// @return true if this LookupResult contains a surface.
   explicit operator bool() const { return bool(mDrawableRef); }
 
-  /// @return true if the surface is an exact match for the Lookup*() arguments.
-  bool IsExactMatch() const { return mIsExactMatch; }
+  /// @return what kind of match this is (exact, substitute, etc.)
+  MatchType Type() const { return mMatchType; }
 
 private:
   LookupResult(const LookupResult&) = delete;
 
   DrawableFrameRef mDrawableRef;
-  bool mIsExactMatch;
+  MatchType mMatchType;
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_LookupResult_h
--- a/image/RasterImage.cpp
+++ b/image/RasterImage.cpp
@@ -521,18 +521,22 @@ RasterImage::LookupFrame(uint32_t aFrame
 
   LookupResult result = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
 
   if (!result && !mHasSize) {
     // We can't request a decode without knowing our intrinsic size. Give up.
     return DrawableFrameRef();
   }
 
-  if (!result || !result.IsExactMatch()) {
-    // The OS threw this frame away. We need to redecode if we can.
+  if (result.Type() == MatchType::NOT_FOUND ||
+      result.Type() == MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND ||
+      ((aFlags & FLAG_SYNC_DECODE) && !result)) {
+    // We don't have a copy of this frame, and there's no decoder working on
+    // one. (Or we're sync decoding and the existing decoder hasn't even started
+    // yet.) Trigger decoding so it'll be available next time.
     MOZ_ASSERT(!mAnim, "Animated frames should be locked");
 
     Decode(Some(requestedSize), aFlags);
 
     // If we can sync decode, we should already have the frame.
     if (aFlags & FLAG_SYNC_DECODE) {
       result = LookupFrameInternal(aFrameNum, requestedSize, aFlags);
     }
@@ -1494,16 +1498,29 @@ RasterImage::CreateDecoder(const Maybe<I
   }
 
   decoder->Init();
 
   if (NS_FAILED(decoder->GetDecoderError())) {
     return nullptr;
   }
 
+  if (aSize) {
+    // Add a placeholder for the first frame to the SurfaceCache so we won't
+    // trigger any more decoders with the same parameters.
+    InsertOutcome outcome =
+      SurfaceCache::InsertPlaceholder(ImageKey(this),
+                                      RasterSurfaceKey(*aSize,
+                                                       decoder->GetDecodeFlags(),
+                                                       /* aFrameNum = */ 0));
+    if (outcome != InsertOutcome::SUCCESS) {
+      return nullptr;
+    }
+  }
+
   if (!aSize) {
     Telemetry::GetHistogramById(
       Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
     mDecodeCount++;
     Telemetry::GetHistogramById(
       Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
 
     if (mDecodeCount > sMaxDecodeCount) {
--- a/image/SurfaceCache.cpp
+++ b/image/SurfaceCache.cpp
@@ -11,16 +11,17 @@
 
 #include <algorithm>
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Move.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/Pair.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/StaticPtr.h"
 #include "nsIMemoryReporter.h"
 #include "gfx2DGlue.h"
 #include "gfxPattern.h"  // Workaround for flaw in bug 921753 part 2.
 #include "gfxPlatform.h"
 #include "gfxPrefs.h"
 #include "imgFrame.h"
@@ -62,16 +63,20 @@ static StaticRefPtr<SurfaceCacheImpl> sI
 /**
  * Cost models the cost of storing a surface in the cache. Right now, this is
  * simply an estimate of the size of the surface in bytes, but in the future it
  * may be worth taking into account the cost of rematerializing the surface as
  * well.
  */
 typedef size_t Cost;
 
+// Placeholders do not have surfaces, but need to be given a trivial cost for
+// our invariants to hold.
+static const Cost sPlaceholderCost = 1;
+
 static Cost
 ComputeCost(const IntSize& aSize, uint32_t aBytesPerPixel)
 {
   MOZ_ASSERT(aBytesPerPixel == 1 || aBytesPerPixel == 4);
   return aSize.width * aSize.height * aBytesPerPixel;
 }
 
 /**
@@ -132,45 +137,61 @@ public:
                 const SurfaceKey&  aSurfaceKey,
                 const Lifetime     aLifetime)
     : mSurface(aSurface)
     , mCost(aCost)
     , mImageKey(aImageKey)
     , mSurfaceKey(aSurfaceKey)
     , mLifetime(aLifetime)
   {
-    MOZ_ASSERT(mSurface, "Must have a valid surface");
+    MOZ_ASSERT(!IsPlaceholder() ||
+               (mCost == sPlaceholderCost && mLifetime == Lifetime::Transient),
+               "Placeholder should have trivial cost and transient lifetime");
     MOZ_ASSERT(mImageKey, "Must have a valid image key");
   }
 
   DrawableFrameRef DrawableRef() const
   {
+    if (MOZ_UNLIKELY(IsPlaceholder())) {
+      MOZ_ASSERT_UNREACHABLE("Shouldn't call DrawableRef() on a placeholder");
+      return DrawableFrameRef();
+    }
+
     return mSurface->DrawableRef();
   }
 
   void SetLocked(bool aLocked)
   {
+    if (IsPlaceholder()) {
+      return;  // Can't lock a placeholder.
+    }
+
     if (aLocked && mLifetime == Lifetime::Persistent) {
       // This may fail, and that's OK. We make no guarantees about whether
       // locking is successful if you call SurfaceCache::LockImage() after
       // SurfaceCache::Insert().
       mDrawableRef = mSurface->DrawableRef();
     } else {
       mDrawableRef.reset();
     }
   }
 
+  bool IsPlaceholder() const { return !bool(mSurface); }
   bool IsLocked() const { return bool(mDrawableRef); }
 
   ImageKey GetImageKey() const { return mImageKey; }
   SurfaceKey GetSurfaceKey() const { return mSurfaceKey; }
   CostEntry GetCostEntry() { return image::CostEntry(this, mCost); }
   nsExpirationState* GetExpirationState() { return &mExpirationState; }
   Lifetime GetLifetime() const { return mLifetime; }
-  bool IsDecoded() const { return mSurface->IsImageComplete(); }
+
+  bool IsDecoded() const
+  {
+    return !IsPlaceholder() && mSurface->IsImageComplete();
+  }
 
   // A helper type used by SurfaceCacheImpl::CollectSizeOfSurfaces.
   struct MOZ_STACK_CLASS SurfaceMemoryReport
   {
     SurfaceMemoryReport(nsTArray<SurfaceMemoryCounter>& aCounters,
                         MallocSizeOf                    aMallocSizeOf)
       : mCounters(aCounters)
       , mMallocSizeOf(aMallocSizeOf)
@@ -257,31 +278,57 @@ public:
 
   already_AddRefed<CachedSurface> Lookup(const SurfaceKey& aSurfaceKey)
   {
     nsRefPtr<CachedSurface> surface;
     mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface));
     return surface.forget();
   }
 
-  already_AddRefed<CachedSurface>
+  MOZ_WARN_UNUSED_RESULT  // See bug 1185044.
+  Pair<already_AddRefed<CachedSurface>, MatchType>
   LookupBestMatch(const SurfaceKey&      aSurfaceKey,
                   const Maybe<uint32_t>& aAlternateFlags)
   {
-    // Try for a perfect match first.
-    nsRefPtr<CachedSurface> surface;
-    mSurfaces.Get(aSurfaceKey, getter_AddRefs(surface));
-    if (surface && surface->IsDecoded()) {
-      return surface.forget();
+    // Try for an exact match first.
+    nsRefPtr<CachedSurface> exactMatch;
+    mSurfaces.Get(aSurfaceKey, getter_AddRefs(exactMatch));
+    if (exactMatch && exactMatch->IsDecoded()) {
+      return MakePair(exactMatch.forget(), MatchType::EXACT);
     }
 
     // There's no perfect match, so find the best match we can.
     MatchContext matchContext(aSurfaceKey, aAlternateFlags);
     ForEach(TryToImproveMatch, &matchContext);
-    return matchContext.mBestMatch.forget();
+
+    MatchType matchType;
+    if (matchContext.mBestMatch) {
+      if (!exactMatch) {
+        // No exact match, but we found a substitute.
+        matchType = MatchType::SUBSTITUTE_BECAUSE_NOT_FOUND;
+      } else if (exactMatch != matchContext.mBestMatch) {
+        // The exact match is still decoding, but we found a substitute.
+        matchType = MatchType::SUBSTITUTE_BECAUSE_PENDING;
+      } else {
+        // The exact match is still decoding, but it's the best we've got.
+        matchType = MatchType::EXACT;
+      }
+    } else {
+      if (exactMatch) {
+        // We found an "exact match", but since TryToImproveMatch didn't return
+        // it, it must have been a placeholder.
+        MOZ_ASSERT(exactMatch->IsPlaceholder());
+        matchType = MatchType::PENDING;
+      } else {
+        // We couldn't find an exact match *or* a substitute.
+        matchType = MatchType::NOT_FOUND;
+      }
+    }
+
+    return MakePair(matchContext.mBestMatch.forget(), matchType);
   }
 
   void ForEach(SurfaceTable::EnumReadFunction aFunction, void* aData)
   {
     mSurfaces.EnumerateRead(aFunction, aData);
   }
 
   void SetLocked(bool aLocked) { mLocked = aLocked; }
@@ -303,16 +350,21 @@ private:
 
   static PLDHashOperator TryToImproveMatch(const SurfaceKey& aSurfaceKey,
                                            CachedSurface*    aSurface,
                                            void*             aContext)
   {
     auto context = static_cast<MatchContext*>(aContext);
     const SurfaceKey& idealKey = context->mIdealKey;
 
+    // We never match a placeholder.
+    if (aSurface->IsPlaceholder()) {
+      return PL_DHASH_NEXT;
+    }
+
     // Matching the animation time and SVG context is required.
     if (aSurfaceKey.AnimationTime() != idealKey.AnimationTime() ||
         aSurfaceKey.SVGContext() != idealKey.SVGContext()) {
       return PL_DHASH_NEXT;
     }
 
     // Matching the flags is required, but we can match the alternate flags as
     // well if some were provided.
@@ -420,20 +472,31 @@ public:
 
   InsertOutcome Insert(imgFrame*         aSurface,
                        const Cost        aCost,
                        const ImageKey    aImageKey,
                        const SurfaceKey& aSurfaceKey,
                        Lifetime          aLifetime)
   {
     // If this is a duplicate surface, refuse to replace the original.
-    if (MOZ_UNLIKELY(Lookup(aImageKey, aSurfaceKey, /* aMarkUsed = */ false))) {
+    // XXX(seth): Calling Lookup() and then RemoveSurface() does the lookup
+    // twice. We'll make this more efficient in bug 1185137.
+    LookupResult result = Lookup(aImageKey, aSurfaceKey, /* aMarkUsed = */ false);
+    if (MOZ_UNLIKELY(result)) {
       return InsertOutcome::FAILURE_ALREADY_PRESENT;
     }
 
+    if (result.Type() == MatchType::PENDING) {
+      RemoveSurface(aImageKey, aSurfaceKey);
+    }
+
+    MOZ_ASSERT(result.Type() == MatchType::NOT_FOUND ||
+               result.Type() == MatchType::PENDING,
+               "A LookupResult with no surface should be NOT_FOUND or PENDING");
+
     // If this is bigger than we can hold after discarding everything we can,
     // refuse to cache it.
     if (MOZ_UNLIKELY(!CanHoldAfterDiscarding(aCost))) {
       mOverflowCount++;
       return InsertOutcome::FAILURE;
     }
 
     // Remove elements in order of cost until we can fit this in the cache. Note
@@ -453,16 +516,17 @@ public:
     }
 
     nsRefPtr<CachedSurface> surface =
       new CachedSurface(aSurface, aCost, aImageKey, aSurfaceKey, aLifetime);
 
     // We require that locking succeed if the image is locked and the surface is
     // persistent; the caller may need to know this to handle errors correctly.
     if (cache->IsLocked() && aLifetime == Lifetime::Persistent) {
+      MOZ_ASSERT(!surface->IsPlaceholder(), "Placeholders should be transient");
       surface->SetLocked(true);
       if (!surface->IsLocked()) {
         return InsertOutcome::FAILURE;
       }
     }
 
     // Insert.
     MOZ_ASSERT(aCost <= mAvailableCost, "Inserting despite too large a cost");
@@ -545,86 +609,99 @@ public:
   }
 
   LookupResult Lookup(const ImageKey    aImageKey,
                       const SurfaceKey& aSurfaceKey,
                       bool aMarkUsed = true)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
-      return LookupResult();  // No cached surfaces for this image.
+      // No cached surfaces for this image.
+      return LookupResult(MatchType::NOT_FOUND);
     }
 
     nsRefPtr<CachedSurface> surface = cache->Lookup(aSurfaceKey);
     if (!surface) {
-      return LookupResult();  // Lookup in the per-image cache missed.
+      // Lookup in the per-image cache missed.
+      return LookupResult(MatchType::NOT_FOUND);
+    }
+
+    if (surface->IsPlaceholder()) {
+      return LookupResult(MatchType::PENDING);
     }
 
     DrawableFrameRef ref = surface->DrawableRef();
     if (!ref) {
       // The surface was released by the operating system. Remove the cache
       // entry as well.
       Remove(surface);
-      return LookupResult();
+      return LookupResult(MatchType::NOT_FOUND);
     }
 
     if (aMarkUsed) {
       MarkUsed(surface, cache);
     }
 
-    return LookupResult(Move(ref), /* aIsExactMatch = */ true);
+    MOZ_ASSERT(surface->GetSurfaceKey() == aSurfaceKey,
+               "Lookup() not returning an exact match?");
+    return LookupResult(Move(ref), MatchType::EXACT);
   }
 
   LookupResult LookupBestMatch(const ImageKey         aImageKey,
                                const SurfaceKey&      aSurfaceKey,
                                const Maybe<uint32_t>& aAlternateFlags)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
-      return LookupResult();  // No cached surfaces for this image.
+      // No cached surfaces for this image.
+      return LookupResult(MatchType::NOT_FOUND);
     }
 
     // Repeatedly look up the best match, trying again if the resulting surface
     // has been freed by the operating system, until we can either lock a
     // surface for drawing or there are no matching surfaces left.
     // XXX(seth): This is O(N^2), but N is expected to be very small. If we
     // encounter a performance problem here we can revisit this.
 
     nsRefPtr<CachedSurface> surface;
     DrawableFrameRef ref;
+    MatchType matchType = MatchType::NOT_FOUND;
     while (true) {
-      surface = cache->LookupBestMatch(aSurfaceKey, aAlternateFlags);
+      // XXX(seth): This code is begging for std::tie. See bug 1184385.
+      Pair<already_AddRefed<CachedSurface>, MatchType> lookupResult =
+        cache->LookupBestMatch(aSurfaceKey, aAlternateFlags);
+      surface = lookupResult.first();
+      matchType = lookupResult.second();
+
       if (!surface) {
-        return LookupResult();  // Lookup in the per-image cache missed.
+        return LookupResult(matchType);  // Lookup in the per-image cache missed.
       }
 
       ref = surface->DrawableRef();
       if (ref) {
         break;
       }
 
       // The surface was released by the operating system. Remove the cache
       // entry as well.
       Remove(surface);
     }
 
-    SurfaceKey key = surface->GetSurfaceKey();
-    const bool isExactMatch = key.Size() == aSurfaceKey.Size();
-
-    MOZ_ASSERT(isExactMatch ==
-      (key == aSurfaceKey ||
-         (aAlternateFlags && key == aSurfaceKey.WithNewFlags(*aAlternateFlags))),
+    MOZ_ASSERT((matchType == MatchType::EXACT) ==
+      (surface->GetSurfaceKey() == aSurfaceKey ||
+         (aAlternateFlags &&
+          surface->GetSurfaceKey() ==
+            aSurfaceKey.WithNewFlags(*aAlternateFlags))),
       "Result differs in a way other than size or alternate flags");
 
-
-    if (isExactMatch) {
+    if (matchType == MatchType::EXACT) {
       MarkUsed(surface, cache);
     }
 
-    return LookupResult(Move(ref), isExactMatch);
+    return LookupResult(Move(ref), matchType);
   }
 
   void RemoveSurface(const ImageKey    aImageKey,
                      const SurfaceKey& aSurfaceKey)
   {
     nsRefPtr<ImageSurfaceCache> cache = GetImageCache(aImageKey);
     if (!cache) {
       return;  // No cached surfaces for this image.
@@ -987,17 +1064,17 @@ SurfaceCache::Shutdown()
 }
 
 /* static */ LookupResult
 SurfaceCache::Lookup(const ImageKey         aImageKey,
                      const SurfaceKey&      aSurfaceKey,
                      const Maybe<uint32_t>& aAlternateFlags /* = Nothing() */)
 {
   if (!sInstance) {
-    return LookupResult();
+    return LookupResult(MatchType::NOT_FOUND);
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
 
   LookupResult result = sInstance->Lookup(aImageKey, aSurfaceKey);
   if (!result && aAlternateFlags) {
     result = sInstance->Lookup(aImageKey,
                                aSurfaceKey.WithNewFlags(*aAlternateFlags));
@@ -1008,17 +1085,17 @@ SurfaceCache::Lookup(const ImageKey     
 
 /* static */ LookupResult
 SurfaceCache::LookupBestMatch(const ImageKey         aImageKey,
                               const SurfaceKey&      aSurfaceKey,
                               const Maybe<uint32_t>& aAlternateFlags
                                 /* = Nothing() */)
 {
   if (!sInstance) {
-    return LookupResult();
+    return LookupResult(MatchType::NOT_FOUND);
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
   return sInstance->LookupBestMatch(aImageKey, aSurfaceKey, aAlternateFlags);
 }
 
 /* static */ InsertOutcome
 SurfaceCache::Insert(imgFrame*         aSurface,
@@ -1035,16 +1112,29 @@ SurfaceCache::Insert(imgFrame*         a
     MOZ_CRASH("Don't pass null surfaces to SurfaceCache::Insert");
   }
 
   MutexAutoLock lock(sInstance->GetMutex());
   Cost cost = ComputeCost(aSurface->GetSize(), aSurface->GetBytesPerPixel());
   return sInstance->Insert(aSurface, cost, aImageKey, aSurfaceKey, aLifetime);
 }
 
+/* static */ InsertOutcome
+SurfaceCache::InsertPlaceholder(const ImageKey    aImageKey,
+                                const SurfaceKey& aSurfaceKey)
+{
+  if (!sInstance) {
+    return InsertOutcome::FAILURE;
+  }
+
+  MutexAutoLock lock(sInstance->GetMutex());
+  return sInstance->Insert(nullptr, sPlaceholderCost, aImageKey, aSurfaceKey,
+                           Lifetime::Transient);
+}
+
 /* static */ bool
 SurfaceCache::CanHold(const IntSize& aSize, uint32_t aBytesPerPixel /* = 4 */)
 {
   if (!sInstance) {
     return false;
   }
 
   Cost cost = ComputeCost(aSize, aBytesPerPixel);
--- a/image/SurfaceCache.h
+++ b/image/SurfaceCache.h
@@ -225,16 +225,17 @@ struct SurfaceCache
   static LookupResult LookupBestMatch(const ImageKey    aImageKey,
                                       const SurfaceKey& aSurfaceKey,
                                       const Maybe<uint32_t>& aAlternateFlags
                                         = Nothing());
 
   /**
    * Insert a surface into the cache. If a surface with the same ImageKey and
    * SurfaceKey is already in the cache, Insert returns FAILURE_ALREADY_PRESENT.
+   * If a matching placeholder is already present, the placeholder is removed.
    *
    * Each surface in the cache has a lifetime, either Transient or Persistent.
    * Transient surfaces can expire from the cache at any time. Persistent
    * surfaces, on the other hand, will never expire as long as they remain
    * locked, but if they become unlocked, can expire just like transient
    * surfaces. When it is first inserted, a persistent surface is locked if its
    * associated image is locked. When that image is later unlocked, the surface
    * becomes unlocked too. To become locked again at that point, two things must
@@ -276,16 +277,43 @@ struct SurfaceCache
    *           SurfaceKey already exists in the cache.
    */
   static InsertOutcome Insert(imgFrame*         aSurface,
                               const ImageKey    aImageKey,
                               const SurfaceKey& aSurfaceKey,
                               Lifetime          aLifetime);
 
   /**
+   * Insert a placeholder for a surface into the cache. If a surface with the
+   * same ImageKey and SurfaceKey is already in the cache, InsertPlaceholder()
+   * returns FAILURE_ALREADY_PRESENT.
+   *
+   * Placeholders exist to allow lazy allocation of surfaces. The Lookup*()
+   * methods will report whether a placeholder for an exactly matching surface
+   * existed by returning a MatchType of PENDING or SUBSTITUTE_BECAUSE_PENDING,
+   * but they will never return a placeholder directly. (They couldn't, since
+   * placeholders don't have an associated surface.)
+   *
+   * Once inserted, placeholders can be removed using RemoveSurface() or
+   * RemoveImage(), just like a surface.  They're automatically removed when a
+   * real surface that matches the placeholder is inserted with Insert().
+   *
+   * @param aImageKey    Key data identifying which image the placeholder
+   *                     belongs to.
+   * @param aSurfaceKey  Key data which uniquely identifies the surface the
+   *                     placeholder stands in for.
+   * @return SUCCESS if the placeholder was inserted successfully.
+   *         FAILURE if the placeholder could not be inserted for some reason.
+   *         FAILURE_ALREADY_PRESENT if a surface with the same ImageKey and
+   *           SurfaceKey already exists in the cache.
+   */
+  static InsertOutcome InsertPlaceholder(const ImageKey    aImageKey,
+                                         const SurfaceKey& aSurfaceKey);
+
+  /**
    * Checks if a surface of a given size could possibly be stored in the cache.
    * If CanHold() returns false, Insert() will always fail to insert the
    * surface, but the inverse is not true: Insert() may take more information
    * into account than just image size when deciding whether to cache the
    * surface, so Insert() may still fail even if CanHold() returns true.
    *
    * Use CanHold() to avoid the need to create a temporary surface when we know
    * for sure the cache can't hold it.
@@ -357,34 +385,35 @@ struct SurfaceCache
    * If the image is unlocked, this has no effect.
    *
    * @param aImageKey    The image which should have its existing surfaces
    *                     unlocked.
    */
   static void UnlockSurfaces(const ImageKey aImageKey);
 
   /**
-   * Removes a surface from the cache, if it's present. If it's not present,
-   * RemoveSurface() has no effect.
+   * Removes a surface or placeholder from the cache, if it's present. If it's
+   * not present, RemoveSurface() has no effect.
    *
    * Use this function to remove individual surfaces that have become invalid.
    * Prefer RemoveImage() or DiscardAll() when they're applicable, as they have
    * much better performance than calling this function repeatedly.
    *
    * @param aImageKey    Key data identifying which image the surface belongs
                          to.
    * @param aSurfaceKey  Key data which uniquely identifies the requested
                          surface.
    */
   static void RemoveSurface(const ImageKey    aImageKey,
                             const SurfaceKey& aSurfaceKey);
 
   /**
-   * Removes all cached surfaces associated with the given image from the cache.
-   * If the image is locked, it is automatically unlocked.
+   * Removes all cached surfaces and placeholders associated with the given
+   * image from the cache.  If the image is locked, it is automatically
+   * unlocked.
    *
    * This MUST be called, at a minimum, when an Image which could be storing
    * surfaces in the surface cache is destroyed. If another image were allocated
    * at the same address it could result in subtle, difficult-to-reproduce bugs.
    *
    * @param aImageKey  The image which should be removed from the cache.
    */
   static void RemoveImage(const ImageKey aImageKey);
--- a/image/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -20,16 +20,17 @@
 #include "nsIOutputStream.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsILocalFileMac.h"
 #include "nsIFileURL.h"
 #include "nsTArray.h"
 #include "nsObjCExceptions.h"
 #include "nsProxyRelease.h"
+#include "nsContentSecurityManager.h"
 
 #include <Cocoa/Cocoa.h>
 
 // nsIconChannel methods
 nsIconChannel::nsIconChannel()
 {
 }
 
@@ -172,16 +173,25 @@ nsIconChannel::GetURI(nsIURI** aURI)
 }
 
 NS_IMETHODIMP
 nsIconChannel::Open(nsIInputStream** _retval)
 {
   return MakeInputStream(_retval, false);
 }
 
+NS_IMETHODIMP
+nsIconChannel::Open2(nsIInputStream** aStream)
+{
+  nsCOMPtr<nsIStreamListener> listener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return Open(aStream);
+}
+
 nsresult
 nsIconChannel::ExtractIconInfoFromUrl(nsIFile** aLocalFile,
                                                uint32_t* aDesiredImageSize,
                                                nsACString& aContentType,
                                                nsACString& aFileExtension)
 {
   nsresult rv = NS_OK;
   nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
@@ -231,16 +241,25 @@ nsIconChannel::AsyncOpen(nsIStreamListen
     if (mLoadGroup) {
       mLoadGroup->AddRequest(this, nullptr);
     }
   }
 
   return rv;
 }
 
+NS_IMETHODIMP
+nsIconChannel::AsyncOpen2(nsIStreamListener* aListener)
+{
+  nsCOMPtr<nsIStreamListener> listener = aListener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return AsyncOpen(listener, nullptr);
+}
+
 nsresult
 nsIconChannel::MakeInputStream(nsIInputStream** _retval,
                                         bool aNonBlocking)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   nsXPIDLCString contentType;
   nsAutoCString fileExt;
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -21,16 +21,17 @@
 #include "nsIPipe.h"
 #include "nsNetCID.h"
 #include "nsIFile.h"
 #include "nsIFileURL.h"
 #include "nsIMIMEService.h"
 #include "nsCExternalHandlerService.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsProxyRelease.h"
+#include "nsContentSecurityManager.h"
 
 #ifdef _WIN32_WINNT
 #undef _WIN32_WINNT
 #endif
 #define _WIN32_WINNT 0x0600
 
 // we need windows.h to read out registry information...
 #include <windows.h>
@@ -193,16 +194,25 @@ nsIconChannel::GetURI(nsIURI** aURI)
 }
 
 NS_IMETHODIMP
 nsIconChannel::Open(nsIInputStream** _retval)
 {
   return MakeInputStream(_retval, false);
 }
 
+NS_IMETHODIMP
+nsIconChannel::Open2(nsIInputStream** aStream)
+{
+  nsCOMPtr<nsIStreamListener> listener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return Open(aStream);
+}
+
 nsresult
 nsIconChannel::ExtractIconInfoFromUrl(nsIFile** aLocalFile,
                         uint32_t* aDesiredImageSize, nsCString& aContentType,
                         nsCString& aFileExtension)
 {
   nsresult rv = NS_OK;
   nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -248,16 +258,25 @@ nsIconChannel::AsyncOpen(nsIStreamListen
     // Add ourself to the load group, if available
     if (mLoadGroup) {
       mLoadGroup->AddRequest(this, nullptr);
     }
   }
   return rv;
 }
 
+NS_IMETHODIMP
+nsIconChannel::AsyncOpen2(nsIStreamListener* aListener)
+{
+  nsCOMPtr<nsIStreamListener> listener = aListener;
+  nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+  NS_ENSURE_SUCCESS(rv, rv);
+  return AsyncOpen(listener, nullptr);
+}
+
 static DWORD
 GetSpecialFolderIcon(nsIFile* aFile, int aFolder,
                                   SHFILEINFOW* aSFI, UINT aInfoFlags)
 {
   DWORD shellResult = 0;
 
   if (!aFile) {
     return shellResult;
--- a/ipc/glue/BackgroundUtils.cpp
+++ b/ipc/glue/BackgroundUtils.cpp
@@ -11,16 +11,17 @@
 #include "nsPrincipal.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "mozilla/LoadInfo.h"
 #include "nsNullPrincipal.h"
 #include "nsServiceManagerUtils.h"
 #include "nsString.h"
+#include "nsTArray.h"
 
 namespace mozilla {
 namespace net {
 class OptionalLoadInfoArgs;
 }
 
 using namespace mozilla::net;
 
@@ -239,26 +240,35 @@ LoadInfoToLoadInfoArgs(nsILoadInfo *aLoa
   rv = PrincipalToPrincipalInfo(aLoadInfo->LoadingPrincipal(),
                                 &requestingPrincipalInfo);
   NS_ENSURE_SUCCESS(rv, rv);
 
   PrincipalInfo triggeringPrincipalInfo;
   rv = PrincipalToPrincipalInfo(aLoadInfo->TriggeringPrincipal(),
                                 &triggeringPrincipalInfo);
 
+  nsTArray<PrincipalInfo> redirectChain;
+  for (const nsCOMPtr<nsIPrincipal>& principal : aLoadInfo->RedirectChain()) {
+    rv = PrincipalToPrincipalInfo(principal, redirectChain.AppendElement());
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
   *aOptionalLoadInfoArgs =
     LoadInfoArgs(
       requestingPrincipalInfo,
       triggeringPrincipalInfo,
       aLoadInfo->GetSecurityFlags(),
       aLoadInfo->GetContentPolicyType(),
       aLoadInfo->GetUpgradeInsecureRequests(),
       aLoadInfo->GetInnerWindowID(),
       aLoadInfo->GetOuterWindowID(),
-      aLoadInfo->GetParentOuterWindowID());
+      aLoadInfo->GetParentOuterWindowID(),
+      aLoadInfo->GetEnforceSecurity(),
+      aLoadInfo->GetInitialSecurityCheckDone(),
+      redirectChain);
 
   return NS_OK;
 }
 
 nsresult
 LoadInfoArgsToLoadInfo(const OptionalLoadInfoArgs& aOptionalLoadInfoArgs,
                        nsILoadInfo** outLoadInfo)
 {
@@ -273,24 +283,35 @@ LoadInfoArgsToLoadInfo(const OptionalLoa
   nsresult rv = NS_OK;
   nsCOMPtr<nsIPrincipal> requestingPrincipal =
     PrincipalInfoToPrincipal(loadInfoArgs.requestingPrincipalInfo(), &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   nsCOMPtr<nsIPrincipal> triggeringPrincipal =
     PrincipalInfoToPrincipal(loadInfoArgs.triggeringPrincipalInfo(), &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsTArray<nsCOMPtr<nsIPrincipal>> redirectChain;
+  for (const PrincipalInfo& principalInfo : loadInfoArgs.redirectChain()) {
+    nsCOMPtr<nsIPrincipal> redirectedPrincipal =
+      PrincipalInfoToPrincipal(principalInfo, &rv);
+    NS_ENSURE_SUCCESS(rv, rv);
+    redirectChain.AppendElement(redirectedPrincipal.forget());
+  }
+
   nsCOMPtr<nsILoadInfo> loadInfo =
     new mozilla::LoadInfo(requestingPrincipal,
                           triggeringPrincipal,
                           loadInfoArgs.securityFlags(),
                           loadInfoArgs.contentPolicyType(),
                           loadInfoArgs.upgradeInsecureRequests(),
                           loadInfoArgs.innerWindowID(),
                           loadInfoArgs.outerWindowID(),
-                          loadInfoArgs.parentOuterWindowID());
+                          loadInfoArgs.parentOuterWindowID(),
+                          loadInfoArgs.enforceSecurity(),
+                          loadInfoArgs.initialSecurityCheckDone(),
+                          redirectChain);
 
    loadInfo.forget(outLoadInfo);
    return NS_OK;
 }
 
 } // namespace ipc
 } // namespace mozilla
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -504,16 +504,79 @@ class MOZ_STACK_CLASS MutableHandle : pu
     T* ptr;
 };
 
 } /* namespace JS */
 
 namespace js {
 
 /*
+ * InternalHandle is a handle to an internal pointer into a gcthing. Use
+ * InternalHandle when you have a pointer to a direct field of a gcthing, or
+ * when you need a parameter type for something that *may* be a pointer to a
+ * direct field of a gcthing.
+ */
+template <typename T>
+class InternalHandle {};
+
+template <typename T>
+class InternalHandle<T*>
+{
+    void * const* holder;
+    size_t offset;
+
+  public:
+    /*
+     * Create an InternalHandle using a Handle to the gcthing containing the
+     * field in question, and a pointer to the field.
+     */
+    template<typename H>
+    InternalHandle(const JS::Handle<H>& handle, T* field)
+      : holder((void**)handle.address()), offset(uintptr_t(field) - uintptr_t(handle.get()))
+    {}
+
+    /*
+     * Create an InternalHandle to a field within a Rooted<>.
+     */
+    template<typename R>
+    InternalHandle(const JS::Rooted<R>& root, T* field)
+      : holder((void**)root.address()), offset(uintptr_t(field) - uintptr_t(root.get()))
+    {}
+
+    InternalHandle(const InternalHandle<T*>& other)
+      : holder(other.holder), offset(other.offset) {}
+
+    T* get() const { return reinterpret_cast<T*>(uintptr_t(*holder) + offset); }
+
+    const T& operator*() const { return *get(); }
+    T* operator->() const { return get(); }
+
+    static InternalHandle<T*> fromMarkedLocation(T* fieldPtr) {
+        return InternalHandle(fieldPtr);
+    }
+
+  private:
+    /*
+     * Create an InternalHandle to something that is not a pointer to a
+     * gcthing, and so does not need to be rooted in the first place. Use these
+     * InternalHandles to pass pointers into functions that also need to accept
+     * regular InternalHandles to gcthing fields.
+     *
+     * Make this private to prevent accidental misuse; this is only for
+     * fromMarkedLocation().
+     */
+    explicit InternalHandle(T* field)
+      : holder(&js::ConstNullValue),
+        offset(uintptr_t(field))
+    {}
+
+    void operator=(InternalHandle<T*> other) = delete;
+};
+
+/*
  * By default, things should use the inheritance hierarchy to find their
  * ThingRootKind. Some pointer types are explicitly set in jspubtd.h so that
  * Rooted<T> may be used without the class definition being available.
  */
 template <typename T>
 struct RootKind
 {
     static ThingRootKind rootKind() { return T::rootKind(); }
--- a/js/public/Value.h
+++ b/js/public/Value.h
@@ -1935,23 +1935,16 @@ struct LayoutAlignmentTester { char c; j
 static_assert(sizeof(LayoutAlignmentTester) == 16,
               "jsval_layout must be 16-byte-aligned");
 
 } // namespace detail
 #endif /* JS_DEBUG */
 
 } // namespace JS
 
-/*
- * JS::Value and jsval are the same type; jsval is the old name, kept around
- * for backwards compatibility along with all the JSVAL_* operations below.
- * jsval_layout is an implementation detail and should not be used externally.
- */
-typedef JS::Value jsval;
-
 static_assert(sizeof(jsval_layout) == sizeof(JS::Value),
               "jsval_layout and JS::Value must have identical layouts");
 
 /************************************************************************/
 
 namespace JS {
 
 extern JS_PUBLIC_DATA(const HandleValue) NullHandleValue;
--- a/js/src/builtin/AtomicsObject.h
+++ b/js/src/builtin/AtomicsObject.h
@@ -12,17 +12,17 @@
 
 namespace js {
 
 class AtomicsObject : public JSObject
 {
   public:
     static const Class class_;
     static JSObject* initClass(JSContext* cx, Handle<GlobalObject*> global);
-    static bool toString(JSContext* cx, unsigned int argc, jsval* vp);
+    static bool toString(JSContext* cx, unsigned int argc, Value* vp);
 
     // Defined return values for futexWait.
     // The error values must be negative because APIs such as futexWaitOrRequeue
     // return a value that is either the number of tasks woken or an error code.
     enum FutexWaitResult : int32_t {
         FutexOK = 0,
         FutexNotequal = -1,
         FutexTimedout = -2
--- a/js/src/builtin/Profilers.cpp
+++ b/js/src/builtin/Profilers.cpp
@@ -224,17 +224,17 @@ struct RequiredStringArg {
         return (void*) mBytes;
     }
     ~RequiredStringArg() {
         js_free(mBytes);
     }
 };
 
 static bool
-StartProfiling(JSContext* cx, unsigned argc, jsval* vp)
+StartProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         args.rval().setBoolean(JS_StartProfiling(nullptr, getpid()));
         return true;
     }
 
     RequiredStringArg profileName(cx, args, 0, "startProfiling");
@@ -251,66 +251,66 @@ StartProfiling(JSContext* cx, unsigned a
         return false;
     }
     pid_t pid = static_cast<pid_t>(args[1].toInt32());
     args.rval().setBoolean(JS_StartProfiling(profileName.mBytes, pid));
     return true;
 }
 
 static bool
-StopProfiling(JSContext* cx, unsigned argc, jsval* vp)
+StopProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         args.rval().setBoolean(JS_StopProfiling(nullptr));
         return true;
     }
 
     RequiredStringArg profileName(cx, args, 0, "stopProfiling");
     if (!profileName)
         return false;
     args.rval().setBoolean(JS_StopProfiling(profileName.mBytes));
     return true;
 }
 
 static bool
-PauseProfilers(JSContext* cx, unsigned argc, jsval* vp)
+PauseProfilers(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         args.rval().setBoolean(JS_PauseProfilers(nullptr));
         return true;
     }
 
     RequiredStringArg profileName(cx, args, 0, "pauseProfiling");
     if (!profileName)
         return false;
     args.rval().setBoolean(JS_PauseProfilers(profileName.mBytes));
     return true;
 }
 
 static bool
-ResumeProfilers(JSContext* cx, unsigned argc, jsval* vp)
+ResumeProfilers(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         args.rval().setBoolean(JS_ResumeProfilers(nullptr));
         return true;
     }
 
     RequiredStringArg profileName(cx, args, 0, "resumeProfiling");
     if (!profileName)
         return false;
     args.rval().setBoolean(JS_ResumeProfilers(profileName.mBytes));
     return true;
 }
 
 /* Usage: DumpProfile([filename[, profileName]]) */
 static bool
-DumpProfile(JSContext* cx, unsigned argc, jsval* vp)
+DumpProfile(JSContext* cx, unsigned argc, Value* vp)
 {
     bool ret;
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         ret = JS_DumpProfile(nullptr, nullptr);
     } else {
         RequiredStringArg filename(cx, args, 0, "dumpProfile");
         if (!filename)
@@ -327,62 +327,62 @@ DumpProfile(JSContext* cx, unsigned argc
         }
     }
 
     args.rval().setBoolean(ret);
     return true;
 }
 
 static bool
-GetMaxGCPauseSinceClear(JSContext* cx, unsigned argc, jsval* vp)
+GetMaxGCPauseSinceClear(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setNumber(uint32_t(cx->runtime()->gc.stats.getMaxGCPauseSinceClear()));
     return true;
 }
 
 static bool
-ClearMaxGCPauseAccumulator(JSContext* cx, unsigned argc, jsval* vp)
+ClearMaxGCPauseAccumulator(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setNumber(uint32_t(cx->runtime()->gc.stats.clearMaxGCPauseAccumulator()));
     return true;
 }
 
 #if defined(MOZ_SHARK) || defined(MOZ_INSTRUMENTS)
 
 static bool
-IgnoreAndReturnTrue(JSContext* cx, unsigned argc, jsval* vp)
+IgnoreAndReturnTrue(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(true);
     return true;
 }
 
 #endif
 
 #ifdef MOZ_CALLGRIND
 static bool
-StartCallgrind(JSContext* cx, unsigned argc, jsval* vp)
+StartCallgrind(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(js_StartCallgrind());
     return true;
 }
 
 static bool
-StopCallgrind(JSContext* cx, unsigned argc, jsval* vp)
+StopCallgrind(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(js_StopCallgrind());
     return true;
 }
 
 static bool
-DumpCallgrind(JSContext* cx, unsigned argc, jsval* vp)
+DumpCallgrind(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         args.rval().setBoolean(js_DumpCallgrind(nullptr));
         return true;
     }
 
     RequiredStringArg outFile(cx, args, 0, "dumpCallgrind");
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -3616,17 +3616,17 @@ ASTSerializer::functionBody(ParseNode* p
         if (!sourceElement(next, &child) || !elts.append(child))
             return false;
     }
 
     return builder.blockStatement(elts, pos, dst);
 }
 
 static bool
-reflect_parse(JSContext* cx, uint32_t argc, jsval* vp)
+reflect_parse(JSContext* cx, uint32_t argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
                              "Reflect.parse", "0", "s");
         return false;
     }
--- a/js/src/builtin/SIMD.cpp
+++ b/js/src/builtin/SIMD.cpp
@@ -411,21 +411,22 @@ CreateAndBindSimdClass(JSContext* cx, Ha
     return typeDescr;
 }
 
 template <typename T>
 static bool
 FillLanes(JSContext* cx, Handle<TypedObject*> result, const CallArgs& args)
 {
     typedef typename T::Elem Elem;
+    InternalHandle<Elem*> mem(result, reinterpret_cast<Elem*>(result->typedMem()));
     Elem tmp;
     for (unsigned i = 0; i < T::lanes; i++) {
         if (!T::toType(cx, args.get(i), &tmp))
             return false;
-        reinterpret_cast<Elem*>(result->typedMem())[i] = tmp;
+        mem.get()[i] = tmp;
     }
     args.rval().setObject(*result);
     return true;
 }
 
 bool
 SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
 {
--- a/js/src/builtin/SIMD.h
+++ b/js/src/builtin/SIMD.h
@@ -356,17 +356,17 @@
 
 namespace js {
 
 class SIMDObject : public JSObject
 {
   public:
     static const Class class_;
     static JSObject* initClass(JSContext* cx, Handle<GlobalObject*> global);
-    static bool toString(JSContext* cx, unsigned int argc, jsval* vp);
+    static bool toString(JSContext* cx, unsigned int argc, Value* vp);
 };
 
 // These classes exist for use with templates below.
 
 struct Float32x4 {
     typedef float Elem;
     static const unsigned lanes = 4;
     static const SimdTypeDescr::Type type = SimdTypeDescr::Float32x4;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -44,17 +44,17 @@ using mozilla::ArrayLength;
 using mozilla::Move;
 using mozilla::UniquePtr;
 
 // If fuzzingSafe is set, remove functionality that could cause problems with
 // fuzzers. Set this via the environment variable MOZ_FUZZING_SAFE.
 static bool fuzzingSafe = false;
 
 static bool
-GetBuildConfiguration(JSContext* cx, unsigned argc, jsval* vp)
+GetBuildConfiguration(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject info(cx, JS_NewPlainObject(cx));
     if (!info)
         return false;
 
     if (!JS_SetProperty(cx, info, "rooting-analysis", FalseHandleValue))
         return false;
@@ -224,17 +224,17 @@ GetBuildConfiguration(JSContext* cx, uns
     if (!JS_SetProperty(cx, info, "pointer-byte-size", value))
         return false;
 
     args.rval().setObject(*info);
     return true;
 }
 
 static bool
-GC(JSContext* cx, unsigned argc, jsval* vp)
+GC(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /*
      * If the first argument is 'compartment', we collect any compartments
      * previously scheduled for GC via schedulegc. If the first argument is an
      * object, we collect the object's compartment (and any other compartments
      * scheduled for GC). Otherwise, we collect all compartments.
@@ -280,17 +280,17 @@ GC(JSContext* cx, unsigned argc, jsval* 
     JSString* str = JS_NewStringCopyZ(cx, buf);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
-MinorGC(JSContext* cx, unsigned argc, jsval* vp)
+MinorGC(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.get(0) == BooleanValue(true))
         cx->runtime()->gc.storeBuffer.setAboutToOverflow();
 
     cx->minorGC(JS::gcreason::API);
     args.rval().setUndefined();
     return true;
@@ -454,17 +454,17 @@ IsRelazifiableFunction(JSContext* cx, un
     }
 
     JSFunction* fun = &args[0].toObject().as<JSFunction>();
     args.rval().setBoolean(fun->hasScript() && fun->nonLazyScript()->isRelazifiable());
     return true;
 }
 
 static bool
-InternalConst(JSContext* cx, unsigned argc, jsval* vp)
+InternalConst(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() == 0) {
         JS_ReportError(cx, "the function takes exactly one argument");
         return false;
     }
 
     JSString* str = ToString(cx, args[0]);
@@ -479,17 +479,17 @@ InternalConst(JSContext* cx, unsigned ar
     } else {
         JS_ReportError(cx, "unknown const name");
         return false;
     }
     return true;
 }
 
 static bool
-GCPreserveCode(JSContext* cx, unsigned argc, jsval* vp)
+GCPreserveCode(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 0) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Wrong number of arguments");
         return false;
     }
@@ -580,47 +580,47 @@ SelectForGC(JSContext* cx, unsigned argc
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-VerifyPreBarriers(JSContext* cx, unsigned argc, jsval* vp)
+VerifyPreBarriers(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() > 0) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Too many arguments");
         return false;
     }
 
     gc::VerifyBarriers(cx->runtime(), gc::PreBarrierVerifier);
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-VerifyPostBarriers(JSContext* cx, unsigned argc, jsval* vp)
+VerifyPostBarriers(JSContext* cx, unsigned argc, Value* vp)
 {
     // This is a no-op since the post barrier verifier was removed.
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length()) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Too many arguments");
         return false;
     }
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-GCState(JSContext* cx, unsigned argc, jsval* vp)
+GCState(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 0) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Too many arguments");
         return false;
     }
@@ -641,17 +641,17 @@ GCState(JSContext* cx, unsigned argc, js
     JSString* str = JS_NewStringCopyZ(cx, state);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
-DeterministicGC(JSContext* cx, unsigned argc, jsval* vp)
+DeterministicGC(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Wrong number of arguments");
         return false;
     }
@@ -745,49 +745,49 @@ AbortGC(JSContext* cx, unsigned argc, Va
     }
 
     cx->runtime()->gc.abortGC();
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-ValidateGC(JSContext* cx, unsigned argc, jsval* vp)
+ValidateGC(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Wrong number of arguments");
         return false;
     }
 
     cx->runtime()->gc.setValidate(ToBoolean(args[0]));
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-FullCompartmentChecks(JSContext* cx, unsigned argc, jsval* vp)
+FullCompartmentChecks(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Wrong number of arguments");
         return false;
     }
 
     cx->runtime()->gc.setFullCompartmentChecks(ToBoolean(args[0]));
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc, jsval* vp)
+NondeterministicGetWeakMapKeys(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Wrong number of arguments");
         return false;
     }
@@ -825,17 +825,17 @@ class HasChildTracer : public JS::Callba
     HasChildTracer(JSRuntime* rt, HandleValue child)
       : JS::CallbackTracer(rt, TraceWeakMapKeysValues), child_(rt, child), found_(false)
     {}
 
     bool found() const { return found_; }
 };
 
 static bool
-HasChild(JSContext* cx, unsigned argc, jsval* vp)
+HasChild(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedValue parent(cx, args.get(0));
     RootedValue child(cx, args.get(1));
 
     if (!parent.isMarkable() || !child.isMarkable()) {
         args.rval().setBoolean(false);
         return true;
@@ -843,40 +843,40 @@ HasChild(JSContext* cx, unsigned argc, j
 
     HasChildTracer trc(cx->runtime(), child);
     TraceChildren(&trc, parent.toGCThing(), parent.traceKind());
     args.rval().setBoolean(trc.found());
     return true;
 }
 
 static bool
-SetSavedStacksRNGState(JSContext* cx, unsigned argc, jsval* vp)
+SetSavedStacksRNGState(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "setSavedStacksRNGState", 1))
         return false;
 
     int32_t seed;
     if (!ToInt32(cx, args[0], &seed))
         return false;
 
     cx->compartment()->savedStacks().setRNGState((seed ^ RNG_MULTIPLIER) & RNG_MASK);
     return true;
 }
 
 static bool
-GetSavedFrameCount(JSContext* cx, unsigned argc, jsval* vp)
+GetSavedFrameCount(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setNumber(cx->compartment()->savedStacks().count());
     return true;
 }
 
 static bool
-SaveStack(JSContext* cx, unsigned argc, jsval* vp)
+SaveStack(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     unsigned maxFrameCount = 0;
     if (args.length() >= 1) {
         double d;
         if (!ToNumber(cx, args[0], &d))
             return false;
@@ -913,17 +913,17 @@ SaveStack(JSContext* cx, unsigned argc, 
     if (stack && !cx->compartment()->wrap(cx, &stack))
         return false;
 
     args.rval().setObjectOrNull(stack);
     return true;
 }
 
 static bool
-CallFunctionWithAsyncStack(JSContext* cx, unsigned argc, jsval* vp)
+CallFunctionWithAsyncStack(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 3) {
         JS_ReportError(cx, "The function takes exactly three arguments.");
         return false;
     }
     if (!args[0].isObject() || !IsCallable(args[0])) {
@@ -944,32 +944,32 @@ CallFunctionWithAsyncStack(JSContext* cx
     RootedString asyncCause(cx, args[2].toString());
 
     JS::AutoSetAsyncStackForNewCalls sas(cx, stack, asyncCause);
     return Call(cx, UndefinedHandleValue, function,
                 JS::HandleValueArray::empty(), args.rval());
 }
 
 static bool
-EnableTrackAllocations(JSContext* cx, unsigned argc, jsval* vp)
+EnableTrackAllocations(JSContext* cx, unsigned argc, Value* vp)
 {
     SetObjectMetadataCallback(cx, SavedStacksMetadataCallback);
     return true;
 }
 
 static bool
-DisableTrackAllocations(JSContext* cx, unsigned argc, jsval* vp)
+DisableTrackAllocations(JSContext* cx, unsigned argc, Value* vp)
 {
     SetObjectMetadataCallback(cx, nullptr);
     return true;
 }
 
 #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT)
 static bool
-OOMAfterAllocations(JSContext* cx, unsigned argc, jsval* vp)
+OOMAfterAllocations(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1) {
         JS_ReportError(cx, "count argument required");
         return false;
     }
 
     uint32_t count;
@@ -977,17 +977,17 @@ OOMAfterAllocations(JSContext* cx, unsig
         return false;
 
     OOM_maxAllocations = OOM_counter + count;
     OOM_failAlways = true;
     return true;
 }
 
 static bool
-OOMAtAllocation(JSContext* cx, unsigned argc, jsval* vp)
+OOMAtAllocation(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1) {
         JS_ReportError(cx, "count argument required");
         return false;
     }
 
     uint32_t count;
@@ -995,45 +995,45 @@ OOMAtAllocation(JSContext* cx, unsigned 
         return false;
 
     OOM_maxAllocations = OOM_counter + count;
     OOM_failAlways = false;
     return true;
 }
 
 static bool
-ResetOOMFailure(JSContext* cx, unsigned argc, jsval* vp)
+ResetOOMFailure(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setBoolean(OOM_counter >= OOM_maxAllocations);
     OOM_maxAllocations = UINT32_MAX;
     return true;
 }
 #endif
 
 static const js::Class FakePromiseClass = {
     "Promise", JSCLASS_IS_ANONYMOUS
 };
 
 static bool
-MakeFakePromise(JSContext* cx, unsigned argc, jsval* vp)
+MakeFakePromise(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedObject obj(cx, NewObjectWithGivenProto(cx, &FakePromiseClass, nullptr));
     if (!obj)
         return false;
 
     JS::dbg::onNewPromise(cx, obj);
     args.rval().setObject(*obj);
     return true;
 }
 
 static bool
-SettleFakePromise(JSContext* cx, unsigned argc, jsval* vp)
+SettleFakePromise(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "settleFakePromise", 1))
         return false;
     if (!args[0].isObject() || args[0].toObject().getClass() != &FakePromiseClass) {
         JS_ReportError(cx, "first argument must be a (fake) Promise object");
         return false;
     }
@@ -1060,30 +1060,30 @@ static const JSClass FinalizeCounterClas
     nullptr, /* enumerate */
     nullptr, /* resolve */
     nullptr, /* mayResolve */
     nullptr, /* convert */
     finalize_counter_finalize
 };
 
 static bool
-MakeFinalizeObserver(JSContext* cx, unsigned argc, jsval* vp)
+MakeFinalizeObserver(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JSObject* obj = JS_NewObjectWithGivenProto(cx, &FinalizeCounterClass, nullptr);
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
     return true;
 }
 
 static bool
-FinalizeCount(JSContext* cx, unsigned argc, jsval* vp)
+FinalizeCount(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setInt32(finalizeCount);
     return true;
 }
 
 static bool
 DumpHeap(JSContext* cx, unsigned argc, Value* vp)
@@ -1139,34 +1139,34 @@ DumpHeap(JSContext* cx, unsigned argc, V
     if (dumpFile)
         fclose(dumpFile);
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-Terminate(JSContext* cx, unsigned arg, jsval* vp)
+Terminate(JSContext* cx, unsigned arg, Value* vp)
 {
 #ifdef JS_MORE_DETERMINISTIC
     // Print a message to stderr in more-deterministic builds to help jsfunfuzz
     // find uncatchable-exception bugs.
     fprintf(stderr, "terminate called\n");
 #endif
 
     JS_ClearPendingException(cx);
     return false;
 }
 
 #define SPS_PROFILING_STACK_MAX_SIZE 1000
 static ProfileEntry SPS_PROFILING_STACK[SPS_PROFILING_STACK_MAX_SIZE];
 static uint32_t SPS_PROFILING_STACK_SIZE = 0;
 
 static bool
-EnableSPSProfiling(JSContext* cx, unsigned argc, jsval* vp)
+EnableSPSProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Disable before re-enabling; see the assertion in |SPSProfiler::setProfilingStack|.
     if (cx->runtime()->spsProfiler.installed())
         cx->runtime()->spsProfiler.enable(false);
 
     SetRuntimeProfilingStack(cx->runtime(), SPS_PROFILING_STACK, &SPS_PROFILING_STACK_SIZE,
@@ -1174,17 +1174,17 @@ EnableSPSProfiling(JSContext* cx, unsign
     cx->runtime()->spsProfiler.enableSlowAssertions(false);
     cx->runtime()->spsProfiler.enable(true);
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-EnableSPSProfilingWithSlowAssertions(JSContext* cx, unsigned argc, jsval* vp)
+EnableSPSProfilingWithSlowAssertions(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setUndefined();
 
     if (cx->runtime()->spsProfiler.enabled()) {
         // If profiling already enabled with slow assertions disabled,
         // this is a no-op.
         if (cx->runtime()->spsProfiler.slowAssertionsEnabled())
@@ -1203,27 +1203,27 @@ EnableSPSProfilingWithSlowAssertions(JSC
                              SPS_PROFILING_STACK_MAX_SIZE);
     cx->runtime()->spsProfiler.enableSlowAssertions(true);
     cx->runtime()->spsProfiler.enable(true);
 
     return true;
 }
 
 static bool
-DisableSPSProfiling(JSContext* cx, unsigned argc, jsval* vp)
+DisableSPSProfiling(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (cx->runtime()->spsProfiler.installed())
         cx->runtime()->spsProfiler.enable(false);
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-ReadSPSProfilingStack(JSContext* cx, unsigned argc, jsval* vp)
+ReadSPSProfilingStack(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setUndefined();
 
     // Return boolean 'false' if profiler is not enabled.
     if (!cx->runtime()->spsProfiler.enabled()) {
         args.rval().setBoolean(false);
         return true;
@@ -1299,28 +1299,28 @@ ReadSPSProfilingStack(JSContext* cx, uns
             return false;
     }
 
     args.rval().setObject(*stack);
     return true;
 }
 
 static bool
-EnableOsiPointRegisterChecks(JSContext*, unsigned argc, jsval* vp)
+EnableOsiPointRegisterChecks(JSContext*, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 #ifdef CHECK_OSIPOINT_REGISTERS
     jit::js_JitOptions.checkOsiPointRegisters = true;
 #endif
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-DisplayName(JSContext* cx, unsigned argc, jsval* vp)
+DisplayName(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.get(0).isObject() || !args[0].toObject().is<JSFunction>()) {
         RootedObject arg(cx, &args.callee());
         ReportUsageError(cx, arg, "Must have one function argument");
         return false;
     }
 
@@ -1371,52 +1371,52 @@ ShellObjectMetadataCallback(JSContext* c
             stackIndex++;
         }
     }
 
     return obj;
 }
 
 static bool
-SetObjectMetadataCallback(JSContext* cx, unsigned argc, jsval* vp)
+SetObjectMetadataCallback(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool enabled = args.length() ? ToBoolean(args[0]) : false;
     SetObjectMetadataCallback(cx, enabled ? ShellObjectMetadataCallback : nullptr);
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-GetObjectMetadata(JSContext* cx, unsigned argc, jsval* vp)
+GetObjectMetadata(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1 || !args[0].isObject()) {
         JS_ReportError(cx, "Argument must be an object");
         return false;
     }
 
     args.rval().setObjectOrNull(GetObjectMetadata(&args[0].toObject()));
     return true;
 }
 
 bool
-js::testingFunc_bailout(JSContext* cx, unsigned argc, jsval* vp)
+js::testingFunc_bailout(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // NOP when not in IonMonkey
     args.rval().setUndefined();
     return true;
 }
 
 bool
-js::testingFunc_inJit(JSContext* cx, unsigned argc, jsval* vp)
+js::testingFunc_inJit(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!jit::IsBaselineEnabled(cx)) {
         JSString* error = JS_NewStringCopyZ(cx, "Baseline is disabled.");
         if(!error)
             return false;
 
@@ -1434,17 +1434,17 @@ js::testingFunc_inJit(JSContext* cx, uns
         return true;
     }
 
     args.rval().setBoolean(cx->currentlyRunningInJit());
     return true;
 }
 
 bool
-js::testingFunc_inIon(JSContext* cx, unsigned argc, jsval* vp)
+js::testingFunc_inIon(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!jit::IsIonEnabled(cx)) {
         JSString* error = JS_NewStringCopyZ(cx, "Ion is disabled.");
         if (!error)
             return false;
 
@@ -1471,55 +1471,55 @@ js::testingFunc_inIon(JSContext* cx, uns
         }
     }
 
     args.rval().setBoolean(iter.isIon());
     return true;
 }
 
 bool
-js::testingFunc_assertFloat32(JSContext* cx, unsigned argc, jsval* vp)
+js::testingFunc_assertFloat32(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 2) {
         JS_ReportError(cx, "Expects only 2 arguments");
         return false;
     }
 
     // NOP when not in IonMonkey
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-TestingFunc_assertJitStackInvariants(JSContext* cx, unsigned argc, jsval* vp)
+TestingFunc_assertJitStackInvariants(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     jit::AssertJitStackInvariants(cx);
     args.rval().setUndefined();
     return true;
 }
 
 bool
-js::testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc, jsval* vp)
+js::testingFunc_assertRecoveredOnBailout(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 2) {
         JS_ReportError(cx, "Expects only 2 arguments");
         return false;
     }
 
     // NOP when not in IonMonkey
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-SetJitCompilerOption(JSContext* cx, unsigned argc, jsval* vp)
+SetJitCompilerOption(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject callee(cx, &args.callee());
 
     if (args.length() != 2) {
         ReportUsageError(cx, callee, "Wrong number of arguments.");
         return false;
     }
@@ -1568,17 +1568,17 @@ SetJitCompilerOption(JSContext* cx, unsi
 
     JS_SetGlobalJitCompilerOption(cx->runtime(), opt, uint32_t(number));
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-GetJitCompilerOptions(JSContext* cx, unsigned argc, jsval* vp)
+GetJitCompilerOptions(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject info(cx, JS_NewPlainObject(cx));
     if (!info)
         return false;
 
     RootedValue value(cx);
 
@@ -1593,17 +1593,17 @@ GetJitCompilerOptions(JSContext* cx, uns
 #undef JIT_COMPILER_MATCH
 
     args.rval().setObject(*info);
 
     return true;
 }
 
 static bool
-SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, jsval* vp)
+SetIonCheckGraphCoherency(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     jit::js_JitOptions.checkGraphConsistency = ToBoolean(args.get(0));
     args.rval().setUndefined();
     return true;
 }
 
 class CloneBufferObject : public NativeObject {
@@ -1757,34 +1757,34 @@ const Class CloneBufferObject::class_ = 
 };
 
 const JSPropertySpec CloneBufferObject::props_[] = {
     JS_PSGS("clonebuffer", getCloneBuffer, setCloneBuffer, 0),
     JS_PS_END
 };
 
 static bool
-Serialize(JSContext* cx, unsigned argc, jsval* vp)
+Serialize(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JSAutoStructuredCloneBuffer clonebuf;
     if (!clonebuf.write(cx, args.get(0), args.get(1)))
         return false;
 
     RootedObject obj(cx, CloneBufferObject::Create(cx, &clonebuf));
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
     return true;
 }
 
 static bool
-Deserialize(JSContext* cx, unsigned argc, jsval* vp)
+Deserialize(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1 || !args[0].isObject()) {
         JS_ReportError(cx, "deserialize requires a single clonebuffer argument");
         return false;
     }
 
@@ -1815,17 +1815,17 @@ Deserialize(JSContext* cx, unsigned argc
 
     if (hasTransferable)
         obj->discard();
 
     return true;
 }
 
 static bool
-Neuter(JSContext* cx, unsigned argc, jsval* vp)
+Neuter(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 2) {
         JS_ReportError(cx, "wrong number of arguments to neuter()");
         return false;
     }
 
@@ -1857,83 +1857,83 @@ Neuter(JSContext* cx, unsigned argc, jsv
     if (!JS_NeuterArrayBuffer(cx, obj, changeData))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-HelperThreadCount(JSContext* cx, unsigned argc, jsval* vp)
+HelperThreadCount(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 #ifdef JS_MORE_DETERMINISTIC
     // Always return 0 to get consistent output with and without --no-threads.
     args.rval().setInt32(0);
 #else
     if (CanUseExtraThreads())
         args.rval().setInt32(HelperThreadState().threadCount);
     else
         args.rval().setInt32(0);
 #endif
     return true;
 }
 
 static bool
-TimesAccessed(JSContext* cx, unsigned argc, jsval* vp)
+TimesAccessed(JSContext* cx, unsigned argc, Value* vp)
 {
     static int32_t accessed = 0;
     CallArgs args = CallArgsFromVp(argc, vp);
     args.rval().setInt32(++accessed);
     return true;
 }
 
 #ifdef JS_TRACE_LOGGING
 static bool
-EnableTraceLogger(JSContext* cx, unsigned argc, jsval* vp)
+EnableTraceLogger(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
     if (!TraceLoggerEnable(logger, cx))
         return false;
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-DisableTraceLogger(JSContext* cx, unsigned argc, jsval* vp)
+DisableTraceLogger(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
     args.rval().setBoolean(TraceLoggerDisable(logger));
 
     return true;
 }
 #endif
 
 #ifdef DEBUG
 static bool
-DumpObject(JSContext* cx, unsigned argc, jsval* vp)
+DumpObject(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     RootedObject obj(cx, ToObject(cx, args.get(0)));
     if (!obj)
         return false;
 
     DumpObject(obj);
 
     args.rval().setUndefined();
     return true;
 }
 #endif
 
 #ifdef NIGHTLY_BUILD
 static bool
-ObjectAddress(JSContext* cx, unsigned argc, jsval* vp)
+ObjectAddress(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1) {
         RootedObject callee(cx, &args.callee());
         ReportUsageError(cx, callee, "Wrong number of arguments");
         return false;
     }
     if (!args[0].isObject()) {
@@ -1956,26 +1956,26 @@ ObjectAddress(JSContext* cx, unsigned ar
     args.rval().setString(str);
 #endif
 
     return true;
 }
 #endif
 
 static bool
-DumpBacktrace(JSContext* cx, unsigned argc, jsval* vp)
+DumpBacktrace(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     DumpBacktrace(cx);
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-GetBacktrace(JSContext* cx, unsigned argc, jsval* vp)
+GetBacktrace(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool showArgs = false;
     bool showLocals = false;
     bool showThisProps = false;
 
     if (args.length() > 1) {
@@ -2009,27 +2009,27 @@ GetBacktrace(JSContext* cx, unsigned arg
         return false;
     JS_smprintf_free(buf);
 
     args.rval().setString(str);
     return true;
 }
 
 static bool
-ReportOutOfMemory(JSContext* cx, unsigned argc, jsval* vp)
+ReportOutOfMemory(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JS_ReportOutOfMemory(cx);
     cx->clearPendingException();
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-ReportLargeAllocationFailure(JSContext* cx, unsigned argc, jsval* vp)
+ReportLargeAllocationFailure(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     void* buf = cx->runtime()->onOutOfMemoryCanGC(AllocFunction::Malloc, JSRuntime::LARGE_ALLOCATION);
     js_free(buf);
     args.rval().setUndefined();
     return true;
 }
 
@@ -2142,17 +2142,17 @@ struct FindPathHandler {
     // - The last node, nodes[n-1], is the start node.
     AutoValueVector& nodes;
     Vector<EdgeName>& edges;
 };
 
 } // namespace heaptools
 
 static bool
-FindPath(JSContext* cx, unsigned argc, jsval* vp)
+FindPath(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (argc < 2) {
         JS_ReportErrorNumber(cx, GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "findPath", "1", "");
         return false;
     }
 
@@ -2242,17 +2242,17 @@ FindPath(JSContext* cx, unsigned argc, j
         result->setDenseElement(length - i - 1, ObjectValue(*obj));
     }
 
     args.rval().setObject(*result);
     return true;
 }
 
 static bool
-EvalReturningScope(JSContext* cx, unsigned argc, jsval* vp)
+EvalReturningScope(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (!args.requireAtLeast(cx, "evalReturningScope", 1))
         return false;
 
     RootedString str(cx, ToString(cx, args[0]));
     if (!str)
         return false;
@@ -2490,17 +2490,17 @@ GetConstructorName(JSContext* cx, unsign
         args.rval().setString(name);
     } else {
         args.rval().setNull();
     }
     return true;
 }
 
 static bool
-AllocationMarker(JSContext* cx, unsigned argc, jsval* vp)
+AllocationMarker(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     static const JSClass cls = { "AllocationMarker" };
 
     RootedObject obj(cx, JS_NewObject(cx, &cls));
     if (!obj)
         return false;
@@ -2552,17 +2552,17 @@ minorGC(JSRuntime* rt, JSGCStatus status
 // Process global, should really be runtime-local. Also, the final one of these
 // is currently leaked, since they are only deleted when changing.
 MajorGC* prevMajorGC = nullptr;
 MinorGC* prevMinorGC = nullptr;
 
 } /* namespace gcCallback */
 
 static bool
-SetGCCallback(JSContext* cx, unsigned argc, jsval* vp)
+SetGCCallback(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() != 1) {
         JS_ReportError(cx, "Wrong number of arguments");
         return false;
     }
 
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -216,114 +216,114 @@ struct Property
   static bool
   Fun(JSContext* cx, unsigned argc, JS::Value* vp)
   {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     return JS::CallNonGenericMethod<Test, Impl>(cx, args);
   }
 };
 
-static bool ConstructAbstract(JSContext* cx, unsigned argc, jsval* vp);
+static bool ConstructAbstract(JSContext* cx, unsigned argc, Value* vp);
 
 namespace CType {
-  static bool ConstructData(JSContext* cx, unsigned argc, jsval* vp);
+  static bool ConstructData(JSContext* cx, unsigned argc, Value* vp);
   static bool ConstructBasic(JSContext* cx, HandleObject obj, const CallArgs& args);
 
   static void Trace(JSTracer* trc, JSObject* obj);
   static void Finalize(JSFreeOp* fop, JSObject* obj);
 
   bool IsCType(HandleValue v);
   bool IsCTypeOrProto(HandleValue v);
 
   bool PrototypeGetter(JSContext* cx, JS::CallArgs args);
   bool NameGetter(JSContext* cx, JS::CallArgs args);
   bool SizeGetter(JSContext* cx, JS::CallArgs args);
   bool PtrGetter(JSContext* cx, JS::CallArgs args);
 
-  static bool CreateArray(JSContext* cx, unsigned argc, jsval* vp);
-  static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
-  static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
+  static bool CreateArray(JSContext* cx, unsigned argc, Value* vp);
+  static bool ToString(JSContext* cx, unsigned argc, Value* vp);
+  static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
   static bool HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp);
 
 
   /*
    * Get the global "ctypes" object.
    *
    * |obj| must be a CType object.
    *
    * This function never returns nullptr.
    */
   static JSObject* GetGlobalCTypes(JSContext* cx, JSObject* obj);
 
 } // namespace CType
 
 namespace ABI {
   bool IsABI(JSObject* obj);
-  static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
+  static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
 } // namespace ABI
 
 namespace PointerType {
-  static bool Create(JSContext* cx, unsigned argc, jsval* vp);
+  static bool Create(JSContext* cx, unsigned argc, Value* vp);
   static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
 
   bool IsPointerType(HandleValue v);
   bool IsPointer(HandleValue v);
 
   bool TargetTypeGetter(JSContext* cx, JS::CallArgs args);
   bool ContentsGetter(JSContext* cx, JS::CallArgs args);
   bool ContentsSetter(JSContext* cx, JS::CallArgs args);
 
-  static bool IsNull(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Increment(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Decrement(JSContext* cx, unsigned argc, jsval* vp);
+  static bool IsNull(JSContext* cx, unsigned argc, Value* vp);
+  static bool Increment(JSContext* cx, unsigned argc, Value* vp);
+  static bool Decrement(JSContext* cx, unsigned argc, Value* vp);
   // The following is not an instance function, since we don't want to expose arbitrary
   // pointer arithmetic at this moment.
   static bool OffsetBy(JSContext* cx, const CallArgs& args, int offset);
 } // namespace PointerType
 
 namespace ArrayType {
   bool IsArrayType(HandleValue v);
   bool IsArrayOrArrayType(HandleValue v);
 
-  static bool Create(JSContext* cx, unsigned argc, jsval* vp);
+  static bool Create(JSContext* cx, unsigned argc, Value* vp);
   static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
 
   bool ElementTypeGetter(JSContext* cx, JS::CallArgs args);
   bool LengthGetter(JSContext* cx, JS::CallArgs args);
 
   static bool Getter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp);
   static bool Setter(JSContext* cx, HandleObject obj, HandleId idval, MutableHandleValue vp,
                      ObjectOpResult& result);
-  static bool AddressOfElement(JSContext* cx, unsigned argc, jsval* vp);
+  static bool AddressOfElement(JSContext* cx, unsigned argc, Value* vp);
 } // namespace ArrayType
 
 namespace StructType {
   bool IsStruct(HandleValue v);
 
-  static bool Create(JSContext* cx, unsigned argc, jsval* vp);
+  static bool Create(JSContext* cx, unsigned argc, Value* vp);
   static bool ConstructData(JSContext* cx, HandleObject obj, const CallArgs& args);
 
   bool FieldsArrayGetter(JSContext* cx, JS::CallArgs args);
 
   enum {
     SLOT_FIELDNAME
   };
 
   static bool FieldGetter(JSContext* cx, unsigned argc, Value* vp);
   static bool FieldSetter(JSContext* cx, unsigned argc, Value* vp);
-  static bool AddressOfField(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Define(JSContext* cx, unsigned argc, jsval* vp);
+  static bool AddressOfField(JSContext* cx, unsigned argc, Value* vp);
+  static bool Define(JSContext* cx, unsigned argc, Value* vp);
 } // namespace StructType
 
 namespace FunctionType {
-  static bool Create(JSContext* cx, unsigned argc, jsval* vp);
+  static bool Create(JSContext* cx, unsigned argc, Value* vp);
   static bool ConstructData(JSContext* cx, HandleObject typeObj,
-    HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, jsval errVal);
-
-  static bool Call(JSContext* cx, unsigned argc, jsval* vp);
+    HandleObject dataObj, HandleObject fnObj, HandleObject thisObj, Value errVal);
+
+  static bool Call(JSContext* cx, unsigned argc, Value* vp);
 
   bool IsFunctionType(HandleValue v);
 
   bool ArgTypesGetter(JSContext* cx, JS::CallArgs args);
   bool ReturnTypeGetter(JSContext* cx, JS::CallArgs args);
   bool ABIGetter(JSContext* cx, JS::CallArgs args);
   bool IsVariadicGetter(JSContext* cx, JS::CallArgs args);
 } // namespace FunctionType
@@ -350,20 +350,20 @@ namespace CClosure {
 } // namespace CClosure
 
 namespace CData {
   static void Finalize(JSFreeOp* fop, JSObject* obj);
 
   bool ValueGetter(JSContext* cx, JS::CallArgs args);
   bool ValueSetter(JSContext* cx, JS::CallArgs args);
 
-  static bool Address(JSContext* cx, unsigned argc, jsval* vp);
-  static bool ReadString(JSContext* cx, unsigned argc, jsval* vp);
-  static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp);
-  static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
+  static bool Address(JSContext* cx, unsigned argc, Value* vp);
+  static bool ReadString(JSContext* cx, unsigned argc, Value* vp);
+  static bool ReadStringReplaceMalformed(JSContext* cx, unsigned argc, Value* vp);
+  static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
   static JSString* GetSourceString(JSContext* cx, HandleObject typeObj,
                                    void* data);
 
   bool ErrnoGetter(JSContext* cx, JS::CallArgs args);
 
 #if defined(XP_WIN)
   bool LastErrorGetter(JSContext* cx, JS::CallArgs args);
 #endif // defined(XP_WIN)
@@ -377,17 +377,17 @@ namespace CDataFinalizer {
    *
    * JavaScript signature:
    * function(CData, CData):   CDataFinalizer
    *          value  finalizer finalizable
    *
    * Where |finalizer| is a one-argument function taking a value
    * with the same type as |value|.
    */
-  static bool Construct(JSContext* cx, unsigned argc, jsval* vp);
+  static bool Construct(JSContext* cx, unsigned argc, Value* vp);
 
   /*
    * Private data held by |CDataFinalizer|.
    *
    * See also |enum CDataFinalizerSlot| for the slots of
    * |CDataFinalizer|.
    *
    * Note: the private data may be nullptr, if |dispose|, |forget| or the
@@ -423,20 +423,20 @@ namespace CDataFinalizer {
      */
     void* rvalue;
   };
 
   /*
    * Methods of instances of |CDataFinalizer|
    */
   namespace Methods {
-    static bool Dispose(JSContext* cx, unsigned argc, jsval* vp);
-    static bool Forget(JSContext* cx, unsigned argc, jsval* vp);
-    static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
-    static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
+    static bool Dispose(JSContext* cx, unsigned argc, Value* vp);
+    static bool Forget(JSContext* cx, unsigned argc, Value* vp);
+    static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
+    static bool ToString(JSContext* cx, unsigned argc, Value* vp);
   } // namespace Methods
 
   /*
    * Utility functions
    *
    * @return true if |obj| is a CDataFinalizer, false otherwise.
    */
   static bool IsCDataFinalizer(JSObject* obj);
@@ -468,19 +468,19 @@ namespace CDataFinalizer {
   static JSObject* GetCType(JSContext* cx, JSObject* obj);
 
   /*
    * Perform finalization of a |CDataFinalizer|
    */
   static void Finalize(JSFreeOp* fop, JSObject* obj);
 
   /*
-   * Return the jsval contained by this finalizer.
+   * Return the Value contained by this finalizer.
    *
-   * Note that the jsval is actually not recorded, but converted back from C.
+   * Note that the Value is actually not recorded, but converted back from C.
    */
   static bool GetValue(JSContext* cx, JSObject* obj, MutableHandleValue result);
 
   static JSObject* GetCData(JSContext* cx, JSObject* obj);
 } // namespace CDataFinalizer
 
 
 // Int64Base provides functions common to Int64 and UInt64.
@@ -495,37 +495,37 @@ namespace Int64Base {
 
   bool ToSource(JSContext* cx, JSObject* obj, const CallArgs& args,
                 bool isUnsigned);
 
   static void Finalize(JSFreeOp* fop, JSObject* obj);
 } // namespace Int64Base
 
 namespace Int64 {
-  static bool Construct(JSContext* cx, unsigned argc, jsval* vp);
-
-  static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
-  static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
-
-  static bool Compare(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Lo(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Hi(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Join(JSContext* cx, unsigned argc, jsval* vp);
+  static bool Construct(JSContext* cx, unsigned argc, Value* vp);
+
+  static bool ToString(JSContext* cx, unsigned argc, Value* vp);
+  static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
+
+  static bool Compare(JSContext* cx, unsigned argc, Value* vp);
+  static bool Lo(JSContext* cx, unsigned argc, Value* vp);
+  static bool Hi(JSContext* cx, unsigned argc, Value* vp);
+  static bool Join(JSContext* cx, unsigned argc, Value* vp);
 } // namespace Int64
 
 namespace UInt64 {
-  static bool Construct(JSContext* cx, unsigned argc, jsval* vp);
-
-  static bool ToString(JSContext* cx, unsigned argc, jsval* vp);
-  static bool ToSource(JSContext* cx, unsigned argc, jsval* vp);
-
-  static bool Compare(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Lo(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Hi(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Join(JSContext* cx, unsigned argc, jsval* vp);
+  static bool Construct(JSContext* cx, unsigned argc, Value* vp);
+
+  static bool ToString(JSContext* cx, unsigned argc, Value* vp);
+  static bool ToSource(JSContext* cx, unsigned argc, Value* vp);
+
+  static bool Compare(JSContext* cx, unsigned argc, Value* vp);
+  static bool Lo(JSContext* cx, unsigned argc, Value* vp);
+  static bool Hi(JSContext* cx, unsigned argc, Value* vp);
+  static bool Join(JSContext* cx, unsigned argc, Value* vp);
 } // namespace UInt64
 
 /*******************************************************************************
 ** JSClass definitions and initialization functions
 *******************************************************************************/
 
 // Class representing the 'ctypes' object itself. This exists to contain the
 // JSCTypesCallbacks set of function pointers.
@@ -867,17 +867,17 @@ Align(size_t val, size_t align)
 static ABICode
 GetABICode(JSObject* obj)
 {
   // make sure we have an object representing a CABI class,
   // and extract the enumerated class type from the reserved slot.
   if (JS_GetClass(obj) != &sCABIClass)
     return INVALID_ABI;
 
-  jsval result = JS_GetReservedSlot(obj, SLOT_ABICODE);
+  Value result = JS_GetReservedSlot(obj, SLOT_ABICODE);
   return ABICode(result.toInt32());
 }
 
 static const JSErrorFormatString ErrorFormatString[CTYPESERR_LIMIT] = {
 #define MSG_DEF(name, count, exception, format) \
   { format, count, exception } ,
 #include "ctypes/ctypes.msg"
 #undef MSG_DEF
@@ -996,17 +996,17 @@ BuildCStyleFunctionTypeSource(JSContext*
 }
 
 static void
 BuildFunctionTypeSource(JSContext* cx, HandleObject funObj, AutoString& source)
 {
   MOZ_ASSERT(CData::IsCData(funObj) || CType::IsCType(funObj));
 
   if (CData::IsCData(funObj)) {
-    jsval slot = JS_GetReservedSlot(funObj, SLOT_REFERENT);
+    Value slot = JS_GetReservedSlot(funObj, SLOT_REFERENT);
     if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
       slot = JS_GetReservedSlot(funObj, SLOT_FUNNAME);
       MOZ_ASSERT(!slot.isUndefined());
       RootedObject typeObj(cx, CData::GetCType(funObj));
       RootedObject baseTypeObj(cx, PointerType::GetBaseType(typeObj));
       RootedString nameStr(cx, slot.toString());
       BuildCStyleFunctionTypeSource(cx, baseTypeObj, nameStr, 0, source);
       return;
@@ -1917,17 +1917,17 @@ IsCTypesGlobal(HandleValue v)
 }
 
 // Get the JSCTypesCallbacks struct from the 'ctypes' object 'obj'.
 const JSCTypesCallbacks*
 GetCallbacks(JSObject* obj)
 {
   MOZ_ASSERT(IsCTypesGlobal(obj));
 
-  jsval result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
+  Value result = JS_GetReservedSlot(obj, SLOT_CALLBACKS);
   if (result.isUndefined())
     return nullptr;
 
   return static_cast<const JSCTypesCallbacks*>(result.toPrivate());
 }
 
 // Utility function to access a property of an object as an object
 // returns false and sets the error if the property does not exist
@@ -2017,17 +2017,17 @@ namespace js {
 
 JS_FRIEND_API(size_t)
 SizeOfDataIfCDataObject(mozilla::MallocSizeOf mallocSizeOf, JSObject* obj)
 {
     if (!CData::IsCData(obj))
         return 0;
 
     size_t n = 0;
-    jsval slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS);
+    Value slot = JS_GetReservedSlot(obj, ctypes::SLOT_OWNS);
     if (!slot.isUndefined()) {
         bool owns = slot.toBoolean();
         slot = JS_GetReservedSlot(obj, ctypes::SLOT_DATA);
         if (!slot.isUndefined()) {
             char** buffer = static_cast<char**>(slot.toPrivate());
             n += mallocSizeOf(buffer);
             if (owns)
                 n += mallocSizeOf(*buffer);
@@ -2207,17 +2207,17 @@ template<class Type>
 static MOZ_ALWAYS_INLINE bool IsNegative(Type i)
 {
   return IsNegativeImpl<Type, NumericLimits<Type>::is_signed>::Test(i);
 }
 
 // Implicitly convert val to bool, allowing bool, int, and double
 // arguments numerically equal to 0 or 1.
 static bool
-jsvalToBool(JSContext* cx, jsval val, bool* result)
+jsvalToBool(JSContext* cx, Value val, bool* result)
 {
   if (val.isBoolean()) {
     *result = val.toBoolean();
     return true;
   }
   if (val.isInt32()) {
     int32_t i = val.toInt32();
     *result = i != 0;
@@ -2233,17 +2233,17 @@ jsvalToBool(JSContext* cx, jsval val, bo
   return false;
 }
 
 // Implicitly convert val to IntegerType, allowing bool, int, double,
 // Int64, UInt64, and CData integer types 't' where all values of 't' are
 // representable by IntegerType.
 template<class IntegerType>
 static bool
-jsvalToInteger(JSContext* cx, jsval val, IntegerType* result)
+jsvalToInteger(JSContext* cx, Value val, IntegerType* result)
 {
   JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
 
   if (val.isInt32()) {
     // Make sure the integer fits in the alotted precision, and has the right
     // sign.
     int32_t i = val.toInt32();
     return ConvertExact(i, result);
@@ -2323,17 +2323,17 @@ jsvalToInteger(JSContext* cx, jsval val,
   return false;
 }
 
 // Implicitly convert val to FloatType, allowing int, double,
 // Int64, UInt64, and CData numeric types 't' where all values of 't' are
 // representable by FloatType.
 template<class FloatType>
 static bool
-jsvalToFloat(JSContext* cx, jsval val, FloatType* result)
+jsvalToFloat(JSContext* cx, Value val, FloatType* result)
 {
   JS_STATIC_ASSERT(!NumericLimits<FloatType>::is_exact);
 
   // The following casts may silently throw away some bits, but there's
   // no good way around it. Sternly requiring that the 64-bit double
   // argument be exactly representable as a 32-bit float is
   // unrealistic: it would allow 1/2 to pass but not 1/3.
   if (val.isInt32()) {
@@ -2449,17 +2449,17 @@ StringToInteger(JSContext* cx, JSString*
 }
 
 // Implicitly convert val to IntegerType, allowing int, double,
 // Int64, UInt64, and optionally a decimal or hexadecimal string argument.
 // (This is common code shared by jsvalToSize and the Int64/UInt64 constructors.)
 template<class IntegerType>
 static bool
 jsvalToBigInteger(JSContext* cx,
-                  jsval val,
+                  Value val,
                   bool allowString,
                   IntegerType* result)
 {
   JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
 
   if (val.isInt32()) {
     // Make sure the integer fits in the alotted precision, and has the right
     // sign.
@@ -2505,17 +2505,17 @@ jsvalToBigInteger(JSContext* cx,
 
   }
   return false;
 }
 
 // Implicitly convert val to a size value, where the size value is represented
 // by size_t but must also fit in a double.
 static bool
-jsvalToSize(JSContext* cx, jsval val, bool allowString, size_t* result)
+jsvalToSize(JSContext* cx, Value val, bool allowString, size_t* result)
 {
   if (!jsvalToBigInteger(cx, val, allowString, result))
     return false;
 
   // Also check that the result fits in a double.
   return Convert<size_t>(double(*result)) == *result;
 }
 
@@ -2554,34 +2554,34 @@ jsidToSize(JSContext* cx, jsid val, bool
 {
   if (!jsidToBigInteger(cx, val, allowString, result))
     return false;
 
   // Also check that the result fits in a double.
   return Convert<size_t>(double(*result)) == *result;
 }
 
-// Implicitly convert a size value to a jsval, ensuring that the size_t value
+// Implicitly convert a size value to a Value, ensuring that the size_t value
 // fits in a double.
 static bool
 SizeTojsval(JSContext* cx, size_t size, MutableHandleValue result)
 {
   if (Convert<size_t>(double(size)) != size) {
     JS_ReportError(cx, "size overflow");
     return false;
   }
 
   result.setNumber(double(size));
   return true;
 }
 
 // Forcefully convert val to IntegerType when explicitly requested.
 template<class IntegerType>
 static bool
-jsvalToIntegerExplicit(jsval val, IntegerType* result)
+jsvalToIntegerExplicit(Value val, IntegerType* result)
 {
   JS_STATIC_ASSERT(NumericLimits<IntegerType>::is_exact);
 
   if (val.isDouble()) {
     // Convert -Inf, Inf, and NaN to 0; otherwise, convert by C-style cast.
     double d = val.toDouble();
     *result = mozilla::IsFinite(d) ? IntegerType(d) : 0;
     return true;
@@ -2600,17 +2600,17 @@ jsvalToIntegerExplicit(jsval val, Intege
       return true;
     }
   }
   return false;
 }
 
 // Forcefully convert val to a pointer value when explicitly requested.
 static bool
-jsvalToPtrExplicit(JSContext* cx, jsval val, uintptr_t* result)
+jsvalToPtrExplicit(JSContext* cx, Value val, uintptr_t* result)
 {
   if (val.isInt32()) {
     // int32_t always fits in intptr_t. If the integer is negative, cast through
     // an intptr_t intermediate to sign-extend.
     int32_t i = val.toInt32();
     *result = i < 0 ? uintptr_t(intptr_t(i)) : uintptr_t(i);
     return true;
   }
@@ -2853,17 +2853,17 @@ bool CanConvertTypedArrayItemTo(JSObject
     break;
   default:
     return false;
   }
 
   return elementTypeCode == baseTypeCode;
 }
 
-// Implicitly convert jsval 'val' to a C binary representation of CType
+// Implicitly convert Value 'val' to a C binary representation of CType
 // 'targetType', storing the result in 'buffer'. Adequate space must be
 // provided in 'buffer' by the caller. This function generally does minimal
 // coercion between types. There are two cases in which this function is used:
 // 1) The target buffer is internal to a CData object; we simply write data
 //    into it.
 // 2) We are converting an argument for an ffi call, in which case 'convType'
 //    will be 'ConversionType::Argument'. This allows us to handle a special
 //    case: if necessary, we can autoconvert a JS string primitive to a
@@ -3315,17 +3315,17 @@ ImplicitConvert(JSContext* cx,
   case TYPE_void_t:
   case TYPE_function:
     MOZ_CRASH("invalid type");
   }
 
   return true;
 }
 
-// Convert jsval 'val' to a C binary representation of CType 'targetType',
+// Convert Value 'val' to a C binary representation of CType 'targetType',
 // storing the result in 'buffer'. This function is more forceful than
 // ImplicitConvert.
 static bool
 ExplicitConvert(JSContext* cx, HandleValue val, HandleObject targetType,
                 void* buffer, ConversionType convType)
 {
   // If ImplicitConvert succeeds, use that result.
   if (ImplicitConvert(cx, val, targetType, buffer, convType, nullptr))
@@ -3817,31 +3817,31 @@ BuildDataSource(JSContext* cx,
 
 /*******************************************************************************
 ** JSAPI callback function implementations
 *******************************************************************************/
 
 bool
 ConstructAbstract(JSContext* cx,
                   unsigned argc,
-                  jsval* vp)
+                  Value* vp)
 {
   // Calling an abstract base class constructor is disallowed.
   JS_ReportError(cx, "cannot construct from abstract type");
   return false;
 }
 
 /*******************************************************************************
 ** CType implementation
 *******************************************************************************/
 
 bool
 CType::ConstructData(JSContext* cx,
                      unsigned argc,
-                     jsval* vp)
+                     Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   // get the callee object...
   RootedObject obj(cx, &args.callee());
   if (!CType::IsCType(obj)) {
     JS_ReportError(cx, "not a CType");
     return false;
   }
@@ -3893,18 +3893,18 @@ CType::ConstructBasic(JSContext* cx,
 }
 
 JSObject*
 CType::Create(JSContext* cx,
               HandleObject typeProto,
               HandleObject dataProto,
               TypeCode type,
               JSString* name_,
-              jsval size_,
-              jsval align_,
+              Value size_,
+              Value align_,
               ffi_type* ffiType)
 {
   RootedString name(cx, name_);
   RootedValue size(cx, size_);
   RootedValue align(cx, align_);
 
   // Create a CType object with the properties and slots common to all CTypes.
   // Each type object 't' has:
@@ -3965,18 +3965,18 @@ CType::Create(JSContext* cx,
 JSObject*
 CType::DefineBuiltin(JSContext* cx,
                      HandleObject ctypesObj,
                      const char* propName,
                      JSObject* typeProto_,
                      JSObject* dataProto_,
                      const char* name,
                      TypeCode type,
-                     jsval size_,
-                     jsval align_,
+                     Value size_,
+                     Value align_,
                      ffi_type* ffiType)
 {
   RootedObject typeProto(cx, typeProto_);
   RootedObject dataProto(cx, dataProto_);
   RootedValue size(cx, size_);
   RootedValue align(cx, align_);
 
   RootedString nameStr(cx, JS_NewStringCopyZ(cx, name));
@@ -3995,17 +3995,17 @@ CType::DefineBuiltin(JSContext* cx,
 
   return typeObj;
 }
 
 void
 CType::Finalize(JSFreeOp* fop, JSObject* obj)
 {
   // Make sure our TypeCode slot is legit. If it's not, bail.
-  jsval slot = JS_GetReservedSlot(obj, SLOT_TYPECODE);
+  Value slot = JS_GetReservedSlot(obj, SLOT_TYPECODE);
   if (slot.isUndefined())
     return;
 
   // The contents of our slots depends on what kind of type we are.
   switch (TypeCode(slot.toInt32())) {
   case TYPE_function: {
     // Free the FunctionInfo.
     slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
@@ -4052,17 +4052,17 @@ TraceFieldInfoHash(JSTracer* trc, FieldI
     JS_CallObjectTracer(trc, &e.front().value().mType, "fieldType");
   }
 }
 
 void
 CType::Trace(JSTracer* trc, JSObject* obj)
 {
   // Make sure our TypeCode slot is legit. If it's not, bail.
-  jsval slot = obj->as<NativeObject>().getSlot(SLOT_TYPECODE);
+  Value slot = obj->as<NativeObject>().getSlot(SLOT_TYPECODE);
   if (slot.isUndefined())
     return;
 
   // The contents of our slots depends on what kind of type we are.
   switch (TypeCode(slot.toInt32())) {
   case TYPE_struct: {
     slot = obj->as<NativeObject>().getReservedSlot(SLOT_FIELDINFO);
     if (slot.isUndefined())
@@ -4107,17 +4107,17 @@ CType::IsCTypeProto(JSObject* obj)
   return JS_GetClass(obj) == &sCTypeProtoClass;
 }
 
 TypeCode
 CType::GetTypeCode(JSObject* typeObj)
 {
   MOZ_ASSERT(IsCType(typeObj));
 
-  jsval result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE);
+  Value result = JS_GetReservedSlot(typeObj, SLOT_TYPECODE);
   return TypeCode(result.toInt32());
 }
 
 bool
 CType::TypesEqual(JSObject* t1, JSObject* t2)
 {
   MOZ_ASSERT(IsCType(t1) && IsCType(t2));
 
@@ -4185,17 +4185,17 @@ CType::TypesEqual(JSObject* t1, JSObject
   }
 }
 
 bool
 CType::GetSafeSize(JSObject* obj, size_t* result)
 {
   MOZ_ASSERT(CType::IsCType(obj));
 
-  jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
+  Value size = JS_GetReservedSlot(obj, SLOT_SIZE);
 
   // The "size" property can be an int, a double, or JS::UndefinedValue()
   // (for arrays of undefined length), and must always fit in a size_t.
   if (size.isInt32()) {
     *result = size.toInt32();
     return true;
   }
   if (size.isDouble()) {
@@ -4207,17 +4207,17 @@ CType::GetSafeSize(JSObject* obj, size_t
   return false;
 }
 
 size_t
 CType::GetSize(JSObject* obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
 
-  jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
+  Value size = JS_GetReservedSlot(obj, SLOT_SIZE);
 
   MOZ_ASSERT(!size.isUndefined());
 
   // The "size" property can be an int, a double, or JS::UndefinedValue()
   // (for arrays of undefined length), and must always fit in a size_t.
   // For callers who know it can never be JS::UndefinedValue(), return a size_t
   // directly.
   if (size.isInt32())
@@ -4225,39 +4225,39 @@ CType::GetSize(JSObject* obj)
   return Convert<size_t>(size.toDouble());
 }
 
 bool
 CType::IsSizeDefined(JSObject* obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
 
-  jsval size = JS_GetReservedSlot(obj, SLOT_SIZE);
+  Value size = JS_GetReservedSlot(obj, SLOT_SIZE);
 
   // The "size" property can be an int, a double, or JS::UndefinedValue()
   // (for arrays of undefined length), and must always fit in a size_t.
   MOZ_ASSERT(size.isInt32() || size.isDouble() || size.isUndefined());
   return !size.isUndefined();
 }
 
 size_t
 CType::GetAlignment(JSObject* obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
 
-  jsval slot = JS_GetReservedSlot(obj, SLOT_ALIGN);
+  Value slot = JS_GetReservedSlot(obj, SLOT_ALIGN);
   return static_cast<size_t>(slot.toInt32());
 }
 
 ffi_type*
 CType::GetFFIType(JSContext* cx, JSObject* obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
 
-  jsval slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
+  Value slot = JS_GetReservedSlot(obj, SLOT_FFITYPE);
 
   if (!slot.isUndefined()) {
     return static_cast<ffi_type*>(slot.toPrivate());
   }
 
   UniquePtrFFIType result;
   switch (CType::GetTypeCode(obj)) {
   case TYPE_array:
@@ -4278,40 +4278,40 @@ CType::GetFFIType(JSContext* cx, JSObjec
   return result.release();
 }
 
 JSString*
 CType::GetName(JSContext* cx, HandleObject obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
 
-  jsval string = JS_GetReservedSlot(obj, SLOT_NAME);
+  Value string = JS_GetReservedSlot(obj, SLOT_NAME);
   if (!string.isUndefined())
     return string.toString();
 
   // Build the type name lazily.
   JSString* name = BuildTypeName(cx, obj);
   if (!name)
     return nullptr;
   JS_SetReservedSlot(obj, SLOT_NAME, StringValue(name));
   return name;
 }
 
 JSObject*
 CType::GetProtoFromCtor(JSObject* obj, CTypeProtoSlot slot)
 {
   // Get ctypes.{Pointer,Array,Struct}Type.prototype from a reserved slot
   // on the type constructor.
-  jsval protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO);
+  Value protoslot = js::GetFunctionNativeReserved(obj, SLOT_FN_CTORPROTO);
   JSObject* proto = &protoslot.toObject();
   MOZ_ASSERT(proto);
   MOZ_ASSERT(CType::IsCTypeProto(proto));
 
   // Get the desired prototype.
-  jsval result = JS_GetReservedSlot(proto, slot);
+  Value result = JS_GetReservedSlot(proto, slot);
   return &result.toObject();
 }
 
 JSObject*
 CType::GetProtoFromType(JSContext* cx, JSObject* objArg, CTypeProtoSlot slot)
 {
   MOZ_ASSERT(IsCType(objArg));
   RootedObject obj(cx, objArg);
@@ -4319,17 +4319,17 @@ CType::GetProtoFromType(JSContext* cx, J
   // Get the prototype of the type object.
   RootedObject proto(cx);
   if (!JS_GetPrototype(cx, obj, &proto))
     return nullptr;
   MOZ_ASSERT(proto);
   MOZ_ASSERT(CType::IsCTypeProto(proto));
 
   // Get the requested ctypes.{Pointer,Array,Struct,Function}Type.prototype.
-  jsval result = JS_GetReservedSlot(proto, slot);
+  Value result = JS_GetReservedSlot(proto, slot);
   MOZ_ASSERT(result.isObject());
   return &result.toObject();
 }
 
 bool
 CType::IsCTypeOrProto(HandleValue v)
 {
   if (!v.isObject())
@@ -4384,17 +4384,17 @@ CType::PtrGetter(JSContext* cx, JS::Call
   if (!pointerType)
     return false;
 
   args.rval().setObject(*pointerType);
   return true;
 }
 
 bool
-CType::CreateArray(JSContext* cx, unsigned argc, jsval* vp)
+CType::CreateArray(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject baseType(cx, JS_THIS_OBJECT(cx, vp));
   if (!baseType)
     return false;
   if (!CType::IsCType(baseType)) {
     JS_ReportError(cx, "not a CType");
     return false;
@@ -4416,17 +4416,17 @@ CType::CreateArray(JSContext* cx, unsign
   if (!result)
     return false;
 
   args.rval().setObject(*result);
   return true;
 }
 
 bool
-CType::ToString(JSContext* cx, unsigned argc, jsval* vp)
+CType::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj)) {
     JS_ReportError(cx, "not a CType");
     return false;
@@ -4447,17 +4447,17 @@ CType::ToString(JSContext* cx, unsigned 
   if (!result)
     return false;
 
   args.rval().setString(result);
   return true;
 }
 
 bool
-CType::ToSource(JSContext* cx, unsigned argc, jsval* vp)
+CType::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!CType::IsCType(obj) && !CType::IsCTypeProto(obj))
   {
     JS_ReportError(cx, "not a CType");
@@ -4481,17 +4481,17 @@ CType::ToSource(JSContext* cx, unsigned 
   return true;
 }
 
 bool
 CType::HasInstance(JSContext* cx, HandleObject obj, MutableHandleValue v, bool* bp)
 {
   MOZ_ASSERT(CType::IsCType(obj));
 
-  jsval slot = JS_GetReservedSlot(obj, SLOT_PROTO);
+  Value slot = JS_GetReservedSlot(obj, SLOT_PROTO);
   JS::Rooted<JSObject*> prototype(cx, &slot.toObject());
   MOZ_ASSERT(prototype);
   MOZ_ASSERT(CData::IsCDataProto(prototype));
 
   *bp = false;
   if (v.isPrimitive())
     return true;
 
@@ -4516,33 +4516,33 @@ CType::GetGlobalCTypes(JSContext* cx, JS
 
   RootedObject obj(cx, objArg);
   RootedObject objTypeProto(cx);
   if (!JS_GetPrototype(cx, obj, &objTypeProto))
     return nullptr;
   MOZ_ASSERT(objTypeProto);
   MOZ_ASSERT(CType::IsCTypeProto(objTypeProto));
 
-  jsval valCTypes = JS_GetReservedSlot(objTypeProto, SLOT_CTYPES);
+  Value valCTypes = JS_GetReservedSlot(objTypeProto, SLOT_CTYPES);
   MOZ_ASSERT(valCTypes.isObject());
   return &valCTypes.toObject();
 }
 
 /*******************************************************************************
 ** ABI implementation
 *******************************************************************************/
 
 bool
 ABI::IsABI(JSObject* obj)
 {
   return JS_GetClass(obj) == &sCABIClass;
 }
 
 bool
-ABI::ToSource(JSContext* cx, unsigned argc, jsval* vp)
+ABI::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "ABI.prototype.toSource", "no", "s");
   }
 
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
@@ -4575,25 +4575,25 @@ ABI::ToSource(JSContext* cx, unsigned ar
 }
 
 
 /*******************************************************************************
 ** PointerType implementation
 *******************************************************************************/
 
 bool
-PointerType::Create(JSContext* cx, unsigned argc, jsval* vp)
+PointerType::Create(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   // Construct and return a new PointerType object.
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "PointerType", "one", "");
   }
 
-  jsval arg = args[0];
+  Value arg = args[0];
   RootedObject obj(cx);
   if (arg.isPrimitive() || !CType::IsCType(obj = &arg.toObject())) {
     return ArgumentTypeMismatch(cx, "", "PointerType", "a CType");
   }
 
   JSObject* result = CreateInternal(cx, obj);
   if (!result)
     return false;
@@ -4601,17 +4601,17 @@ PointerType::Create(JSContext* cx, unsig
   args.rval().setObject(*result);
   return true;
 }
 
 JSObject*
 PointerType::CreateInternal(JSContext* cx, HandleObject baseType)
 {
   // check if we have a cached PointerType on our base CType.
-  jsval slot = JS_GetReservedSlot(baseType, SLOT_PTR);
+  Value slot = JS_GetReservedSlot(baseType, SLOT_PTR);
   if (!slot.isUndefined())
     return &slot.toObject();
 
   // Get ctypes.PointerType.prototype and the common prototype for CData objects
   // of this type, or ctypes.FunctionType.prototype for function pointers.
   CTypeProtoSlot slotId = CType::GetTypeCode(baseType) == TYPE_function ?
     SLOT_FUNCTIONDATAPROTO : SLOT_POINTERDATAPROTO;
   RootedObject dataProto(cx, CType::GetProtoFromType(cx, baseType, slotId));
@@ -4719,17 +4719,17 @@ PointerType::ConstructData(JSContext* cx
   return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal);
 }
 
 JSObject*
 PointerType::GetBaseType(JSObject* obj)
 {
   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_pointer);
 
-  jsval type = JS_GetReservedSlot(obj, SLOT_TARGET_T);
+  Value type = JS_GetReservedSlot(obj, SLOT_TARGET_T);
   MOZ_ASSERT(!type.isNull());
   return &type.toObject();
 }
 
 bool
 PointerType::IsPointerType(HandleValue v)
 {
   if (!v.isObject())
@@ -4752,17 +4752,17 @@ PointerType::TargetTypeGetter(JSContext*
 {
   RootedObject obj(cx, &args.thisv().toObject());
   args.rval().set(JS_GetReservedSlot(obj, SLOT_TARGET_T));
   MOZ_ASSERT(args.rval().isObject());
   return true;
 }
 
 bool
-PointerType::IsNull(JSContext* cx, unsigned argc, jsval* vp)
+PointerType::IsNull(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!CData::IsCData(obj)) {
     JS_ReportError(cx, "not a CData");
     return false;
@@ -4812,24 +4812,24 @@ PointerType::OffsetBy(JSContext* cx, con
   if (!result)
     return false;
 
   args.rval().setObject(*result);
   return true;
 }
 
 bool
-PointerType::Increment(JSContext* cx, unsigned argc, jsval* vp)
+PointerType::Increment(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   return OffsetBy(cx, args, 1);
 }
 
 bool
-PointerType::Decrement(JSContext* cx, unsigned argc, jsval* vp)
+PointerType::Decrement(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   return OffsetBy(cx, args, -1);
 }
 
 bool
 PointerType::ContentsGetter(JSContext* cx, JS::CallArgs args)
 {
@@ -4875,17 +4875,17 @@ PointerType::ContentsSetter(JSContext* c
                          ConversionType::Setter, nullptr);
 }
 
 /*******************************************************************************
 ** ArrayType implementation
 *******************************************************************************/
 
 bool
-ArrayType::Create(JSContext* cx, unsigned argc, jsval* vp)
+ArrayType::Create(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   // Construct and return a new ArrayType object.
   if (args.length() < 1 || args.length() > 2) {
     return ArgumentLengthError(cx, "ArrayType", "one or two", "s");
   }
 
   if (args[0].isPrimitive() || !CType::IsCType(&args[0].toObject())) {
@@ -5068,28 +5068,28 @@ ArrayType::ConstructData(JSContext* cx,
 }
 
 JSObject*
 ArrayType::GetBaseType(JSObject* obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
 
-  jsval type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T);
+  Value type = JS_GetReservedSlot(obj, SLOT_ELEMENT_T);
   MOZ_ASSERT(!type.isNull());
   return &type.toObject();
 }
 
 bool
 ArrayType::GetSafeLength(JSObject* obj, size_t* result)
 {
   MOZ_ASSERT(CType::IsCType(obj));
   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
 
-  jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
+  Value length = JS_GetReservedSlot(obj, SLOT_LENGTH);
 
   // The "length" property can be an int, a double, or JS::UndefinedValue()
   // (for arrays of undefined length), and must always fit in a size_t.
   if (length.isInt32()) {
     *result = length.toInt32();
     return true;
   }
   if (length.isDouble()) {
@@ -5102,17 +5102,17 @@ ArrayType::GetSafeLength(JSObject* obj, 
 }
 
 size_t
 ArrayType::GetLength(JSObject* obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_array);
 
-  jsval length = JS_GetReservedSlot(obj, SLOT_LENGTH);
+  Value length = JS_GetReservedSlot(obj, SLOT_LENGTH);
 
   MOZ_ASSERT(!length.isUndefined());
 
   // The "length" property can be an int, a double, or JS::UndefinedValue()
   // (for arrays of undefined length), and must always fit in a size_t.
   // For callers who know it can never be JS::UndefinedValue(), return a size_t
   // directly.
   if (length.isInt32())
@@ -5285,17 +5285,17 @@ ArrayType::Setter(JSContext* cx, HandleO
   char* data = static_cast<char*>(CData::GetData(obj)) + elementSize * index;
   if (!ImplicitConvert(cx, vp, baseType, data, ConversionType::Setter,
                        nullptr, nullptr, 0, typeObj, index))
     return false;
   return result.succeed();
 }
 
 bool
-ArrayType::AddressOfElement(JSContext* cx, unsigned argc, jsval* vp)
+ArrayType::AddressOfElement(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!CData::IsCData(obj)) {
     JS_ReportError(cx, "not a CData");
     return false;
@@ -5342,17 +5342,17 @@ ArrayType::AddressOfElement(JSContext* c
 
 /*******************************************************************************
 ** StructType implementation
 *******************************************************************************/
 
 // For a struct field descriptor 'val' of the form { name : type }, extract
 // 'name' and 'type'.
 static JSFlatString*
-ExtractStructField(JSContext* cx, jsval val, MutableHandleObject typeObj)
+ExtractStructField(JSContext* cx, Value val, MutableHandleObject typeObj)
 {
   if (val.isPrimitive()) {
     JS_ReportError(cx, "struct field descriptors require a valid name and type");
     return nullptr;
   }
 
   RootedObject obj(cx, &val.toObject());
   AutoIdArray props(cx, JS_Enumerate(cx, obj));
@@ -5418,26 +5418,26 @@ AddFieldToArray(JSContext* cx,
          typeObj,
          JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
     return false;
 
   return JS_FreezeObject(cx, fieldObj);
 }
 
 bool
-StructType::Create(JSContext* cx, unsigned argc, jsval* vp)
+StructType::Create(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Construct and return a new StructType object.
   if (args.length() < 1 || args.length() > 2) {
     return ArgumentLengthError(cx, "StructType", "one or two", "s");
   }
 
-  jsval name = args[0];
+  Value name = args[0];
   if (!name.isString()) {
     return ArgumentTypeMismatch(cx, "first ", "StructType", "a string");
   }
 
   // Get ctypes.StructType.prototype from the ctypes.StructType constructor.
   RootedObject typeProto(cx, CType::GetProtoFromCtor(&args.callee(), SLOT_STRUCTPROTO));
 
   // Create a simple StructType with no defined fields. The result will be
@@ -5712,17 +5712,17 @@ StructType::BuildFFIType(JSContext* cx, 
   ffiType->size = structSize;
   ffiType->alignment = structAlign;
 #endif
 
   return Move(ffiType);
 }
 
 bool
-StructType::Define(JSContext* cx, unsigned argc, jsval* vp)
+StructType::Define(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!CType::IsCType(obj) ||
       CType::GetTypeCode(obj) != TYPE_struct) {
     JS_ReportError(cx, "not a StructType");
@@ -5733,17 +5733,17 @@ StructType::Define(JSContext* cx, unsign
     JS_ReportError(cx, "StructType has already been defined");
     return false;
   }
 
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "StructType.prototype.define", "one", "");
   }
 
-  jsval arg = args[0];
+  Value arg = args[0];
   if (arg.isPrimitive()) {
     return ArgumentTypeMismatch(cx, "", "StructType.prototype.define",
                                 "an array");
   }
   RootedObject arr(cx, arg.toObjectOrNull());
   if (!JS_IsArrayObject(cx, arr)) {
     return ArgumentTypeMismatch(cx, "", "StructType.prototype.define",
                                 "an array");
@@ -5833,17 +5833,17 @@ StructType::ConstructData(JSContext* cx,
 }
 
 const FieldInfoHash*
 StructType::GetFieldInfo(JSObject* obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_struct);
 
-  jsval slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
+  Value slot = JS_GetReservedSlot(obj, SLOT_FIELDINFO);
   MOZ_ASSERT(!slot.isUndefined() && slot.toPrivate());
 
   return static_cast<const FieldInfoHash*>(slot.toPrivate());
 }
 
 const FieldInfo*
 StructType::LookupField(JSContext* cx, JSObject* obj, JSFlatString* name)
 {
@@ -6002,17 +6002,17 @@ StructType::FieldSetter(JSContext* cx, u
   args.rval().setUndefined();
 
   char* data = static_cast<char*>(CData::GetData(obj)) + field->mOffset;
   return ImplicitConvert(cx, args.get(0), field->mType, data, ConversionType::Setter, nullptr,
                          nullptr, 0, typeObj, field->mIndex);
 }
 
 bool
-StructType::AddressOfField(JSContext* cx, unsigned argc, jsval* vp)
+StructType::AddressOfField(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!CData::IsCData(obj)) {
     JS_ReportError(cx, "not a CData");
     return false;
@@ -6083,17 +6083,17 @@ struct AutoValue
       memset(mData, 0, size);
     return mData != nullptr;
   }
 
   void* mData;
 };
 
 static bool
-GetABI(JSContext* cx, jsval abiType, ffi_abi* result)
+GetABI(JSContext* cx, Value abiType, ffi_abi* result)
 {
   if (abiType.isPrimitive())
     return false;
 
   ABICode abi = GetABICode(abiType.toObjectOrNull());
 
   // determine the ABI from the subset of those available on the
   // given platform. ABI_DEFAULT specifies the default
@@ -6115,17 +6115,17 @@ GetABI(JSContext* cx, jsval abiType, ffi
 #endif
   case INVALID_ABI:
     break;
   }
   return false;
 }
 
 static JSObject*
-PrepareType(JSContext* cx, jsval type)
+PrepareType(JSContext* cx, Value type)
 {
   if (type.isPrimitive() || !CType::IsCType(type.toObjectOrNull())) {
     JS_ReportError(cx, "not a ctypes type");
     return nullptr;
   }
 
   JSObject* result = type.toObjectOrNull();
   TypeCode typeCode = CType::GetTypeCode(result);
@@ -6151,17 +6151,17 @@ PrepareType(JSContext* cx, jsval type)
 
   // libffi cannot pass types of zero size by value.
   MOZ_ASSERT(CType::GetSize(result) != 0);
 
   return result;
 }
 
 static JSObject*
-PrepareReturnType(JSContext* cx, jsval type)
+PrepareReturnType(JSContext* cx, Value type)
 {
   if (type.isPrimitive() || !CType::IsCType(type.toObjectOrNull())) {
     JS_ReportError(cx, "not a ctypes type");
     return nullptr;
   }
 
   JSObject* result = type.toObjectOrNull();
   TypeCode typeCode = CType::GetTypeCode(result);
@@ -6179,17 +6179,17 @@ PrepareReturnType(JSContext* cx, jsval t
 
   // libffi cannot pass types of zero size by value.
   MOZ_ASSERT(typeCode == TYPE_void_t || CType::GetSize(result) != 0);
 
   return result;
 }
 
 static MOZ_ALWAYS_INLINE bool
-IsEllipsis(JSContext* cx, jsval v, bool* isEllipsis)
+IsEllipsis(JSContext* cx, Value v, bool* isEllipsis)
 {
   *isEllipsis = false;
   if (!v.isString())
     return true;
   JSString* str = v.toString();
   if (str->length() != 3)
     return true;
   JSLinearString* linear = str->ensureLinear(cx);
@@ -6360,29 +6360,29 @@ CreateFunctionInfo(JSContext* cx,
 
   if (!PrepareCIF(cx, fninfo))
     return false;
 
   return true;
 }
 
 bool
-FunctionType::Create(JSContext* cx, unsigned argc, jsval* vp)
+FunctionType::Create(JSContext* cx, unsigned argc, Value* vp)
 {
   // Construct and return a new FunctionType object.
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() < 2 || args.length() > 3) {
     return ArgumentLengthError(cx, "FunctionType", "two or three", "s");
   }
 
   AutoValueVector argTypes(cx);
   RootedObject arrayObj(cx, nullptr);
 
   if (args.length() == 3) {
-    // Prepare an array of jsvals for the arguments.
+    // Prepare an array of Values for the arguments.
     if (args[2].isObject())
       arrayObj = &args[2].toObject();
     if (!arrayObj || !JS_IsArrayObject(cx, arrayObj)) {
       return ArgumentTypeMismatch(cx, "third ", "FunctionType", "an array");
     }
 
     uint32_t len;
     ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
@@ -6446,17 +6446,17 @@ FunctionType::CreateInternal(JSContext* 
 // Regular function pointers are constructed directly in
 // PointerType::ConstructData().
 bool
 FunctionType::ConstructData(JSContext* cx,
                             HandleObject typeObj,
                             HandleObject dataObj,
                             HandleObject fnObj,
                             HandleObject thisObj,
-                            jsval errVal)
+                            Value errVal)
 {
   MOZ_ASSERT(CType::GetTypeCode(typeObj) == TYPE_function);
 
   PRFuncPtr* data = static_cast<PRFuncPtr*>(CData::GetData(dataObj));
 
   FunctionInfo* fninfo = FunctionType::GetFunctionInfo(typeObj);
   if (fninfo->mIsVariadic) {
     JS_ReportError(cx, "Can't declare a variadic callback function");
@@ -6517,17 +6517,17 @@ ConvertArgument(JSContext* cx,
   }
 
   return true;
 }
 
 bool
 FunctionType::Call(JSContext* cx,
                    unsigned argc,
-                   jsval* vp)
+                   Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   // get the callee object...
   RootedObject obj(cx, &args.callee());
   if (!CData::IsCData(obj)) {
     JS_ReportError(cx, "not a CData");
     return false;
   }
@@ -6549,17 +6549,17 @@ FunctionType::Call(JSContext* cx,
 
   if ((!fninfo->mIsVariadic && args.length() != argcFixed) ||
       (fninfo->mIsVariadic && args.length() < argcFixed)) {
     JS_ReportError(cx, "Number of arguments does not match declaration");
     return false;
   }
 
   // Check if we have a Library object. If we do, make sure it's open.
-  jsval slot = JS_GetReservedSlot(obj, SLOT_REFERENT);
+  Value slot = JS_GetReservedSlot(obj, SLOT_REFERENT);
   if (!slot.isUndefined() && Library::IsLibrary(&slot.toObject())) {
     PRLibrary* library = Library::GetLibrary(&slot.toObject());
     if (!library) {
       JS_ReportError(cx, "library is not open");
       return false;
     }
   }
 
@@ -6686,17 +6686,17 @@ FunctionType::Call(JSContext* cx,
 }
 
 FunctionInfo*
 FunctionType::GetFunctionInfo(JSObject* obj)
 {
   MOZ_ASSERT(CType::IsCType(obj));
   MOZ_ASSERT(CType::GetTypeCode(obj) == TYPE_function);
 
-  jsval slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
+  Value slot = JS_GetReservedSlot(obj, SLOT_FNINFO);
   MOZ_ASSERT(!slot.isUndefined() && slot.toPrivate());
 
   return static_cast<FunctionInfo*>(slot.toPrivate());
 }
 
 bool
 FunctionType::IsFunctionType(HandleValue v)
 {
@@ -6769,17 +6769,17 @@ FunctionType::IsVariadicGetter(JSContext
 ** CClosure implementation
 *******************************************************************************/
 
 JSObject*
 CClosure::Create(JSContext* cx,
                  HandleObject typeObj,
                  HandleObject fnObj,
                  HandleObject thisObj,
-                 jsval errVal_,
+                 Value errVal_,
                  PRFuncPtr* fnptr)
 {
   RootedValue errVal(cx, errVal_);
   MOZ_ASSERT(fnObj);
 
   RootedObject result(cx, JS_NewObject(cx, &sCClosureClass));
   if (!result)
     return nullptr;
@@ -6863,17 +6863,17 @@ CClosure::Create(JSContext* cx,
   *fnptr = reinterpret_cast<PRFuncPtr>(reinterpret_cast<uintptr_t>(code));
   return result;
 }
 
 void
 CClosure::Trace(JSTracer* trc, JSObject* obj)
 {
   // Make sure our ClosureInfo slot is legit. If it's not, bail.
-  jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
+  Value slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
   if (slot.isUndefined())
     return;
 
   ClosureInfo* cinfo = static_cast<ClosureInfo*>(slot.toPrivate());
 
   // Identify our objects to the tracer. (There's no need to identify
   // 'closureObj', since that's us.)
   JS_CallObjectTracer(trc, &cinfo->typeObj, "typeObj");
@@ -6881,17 +6881,17 @@ CClosure::Trace(JSTracer* trc, JSObject*
   if (cinfo->thisObj)
     JS_CallObjectTracer(trc, &cinfo->thisObj, "thisObj");
 }
 
 void
 CClosure::Finalize(JSFreeOp* fop, JSObject* obj)
 {
   // Make sure our ClosureInfo slot is legit. If it's not, bail.
-  jsval slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
+  Value slot = JS_GetReservedSlot(obj, SLOT_CLOSUREINFO);
   if (slot.isUndefined())
     return;
 
   ClosureInfo* cinfo = static_cast<ClosureInfo*>(slot.toPrivate());
   FreeOp::get(fop)->delete_(cinfo);
 }
 
 void
@@ -7064,17 +7064,17 @@ CData::Create(JSContext* cx,
 {
   MOZ_ASSERT(typeObj);
   MOZ_ASSERT(CType::IsCType(typeObj));
   MOZ_ASSERT(CType::IsSizeDefined(typeObj));
   MOZ_ASSERT(ownResult || source);
   MOZ_ASSERT_IF(refObj && CData::IsCData(refObj), !ownResult);
 
   // Get the 'prototype' property from the type.
-  jsval slot = JS_GetReservedSlot(typeObj, SLOT_PROTO);
+  Value slot = JS_GetReservedSlot(typeObj, SLOT_PROTO);
   MOZ_ASSERT(slot.isObject());
 
   RootedObject proto(cx, &slot.toObject());
 
   RootedObject dataObj(cx, JS_NewObjectWithGivenProto(cx, &sCDataClass, proto));
   if (!dataObj)
     return nullptr;
 
@@ -7121,17 +7121,17 @@ CData::Create(JSContext* cx,
 
   return dataObj;
 }
 
 void
 CData::Finalize(JSFreeOp* fop, JSObject* obj)
 {
   // Delete our buffer, and the data it contains if we own it.
-  jsval slot = JS_GetReservedSlot(obj, SLOT_OWNS);
+  Value slot = JS_GetReservedSlot(obj, SLOT_OWNS);
   if (slot.isUndefined())
     return;
 
   bool owns = slot.toBoolean();
 
   slot = JS_GetReservedSlot(obj, SLOT_DATA);
   if (slot.isUndefined())
     return;
@@ -7142,28 +7142,28 @@ CData::Finalize(JSFreeOp* fop, JSObject*
   FreeOp::get(fop)->delete_(buffer);
 }
 
 JSObject*
 CData::GetCType(JSObject* dataObj)
 {
   MOZ_ASSERT(CData::IsCData(dataObj));
 
-  jsval slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE);
+  Value slot = JS_GetReservedSlot(dataObj, SLOT_CTYPE);
   JSObject* typeObj = slot.toObjectOrNull();
   MOZ_ASSERT(CType::IsCType(typeObj));
   return typeObj;
 }
 
 void*
 CData::GetData(JSObject* dataObj)
 {
   MOZ_ASSERT(CData::IsCData(dataObj));
 
-  jsval slot = JS_GetReservedSlot(dataObj, SLOT_DATA);
+  Value slot = JS_GetReservedSlot(dataObj, SLOT_DATA);
 
   void** buffer = static_cast<void**>(slot.toPrivate());
   MOZ_ASSERT(buffer);
   MOZ_ASSERT(*buffer);
   return *buffer;
 }
 
 bool
@@ -7199,17 +7199,17 @@ CData::ValueSetter(JSContext* cx, JS::Ca
 {
   RootedObject obj(cx, &args.thisv().toObject());
   args.rval().setUndefined();
   return ImplicitConvert(cx, args.get(0), GetCType(obj), GetData(obj),
                          ConversionType::Setter, nullptr);
 }
 
 bool
-CData::Address(JSContext* cx, unsigned argc, jsval* vp)
+CData::Address(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CData.prototype.address", "no", "s");
   }
 
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
@@ -7233,17 +7233,17 @@ CData::Address(JSContext* cx, unsigned a
 
   // Manually set the pointer inside the object, so we skip the conversion step.
   void** data = static_cast<void**>(GetData(result));
   *data = GetData(obj);
   return true;
 }
 
 bool
-CData::Cast(JSContext* cx, unsigned argc, jsval* vp)
+CData::Cast(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 2) {
     return ArgumentLengthError(cx, "ctypes.cast", "two", "s");
   }
 
   if (args[0].isPrimitive() || !CData::IsCData(&args[0].toObject())) {
     return ArgumentTypeMismatch(cx, "first ", "ctypes.cast", "a CData");
@@ -7271,17 +7271,17 @@ CData::Cast(JSContext* cx, unsigned argc
   if (!result)
     return false;
 
   args.rval().setObject(*result);
   return true;
 }
 
 bool
-CData::GetRuntime(JSContext* cx, unsigned argc, jsval* vp)
+CData::GetRuntime(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "ctypes.getRuntime", "one", "");
   }
 
   if (args[0].isPrimitive() || !CType::IsCType(&args[0].toObject())) {
     return ArgumentTypeMismatch(cx, "", "ctypes.getRuntime", "a CType");
@@ -7302,17 +7302,17 @@ CData::GetRuntime(JSContext* cx, unsigne
 
   args.rval().setObject(*result);
   return true;
 }
 
 typedef JS::TwoByteCharsZ (*InflateUTF8Method)(JSContext*, const JS::UTF8Chars, size_t*);
 
 static bool
-ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc, jsval* vp)
+ReadStringCommon(JSContext* cx, InflateUTF8Method inflateUTF8, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     if (inflateUTF8 == JS::UTF8CharsToNewTwoByteCharsZ) {
       return ArgumentLengthError(cx, "CData.prototype.readString", "no", "s");
     }
     return ArgumentLengthError(cx, "CData.prototype.readStringReplaceMalformed",
                                "no", "s");
@@ -7389,23 +7389,23 @@ ReadStringCommon(JSContext* cx, InflateU
   if (!result)
     return false;
 
   args.rval().setString(result);
   return true;
 }
 
 bool
-CData::ReadString(JSContext* cx, unsigned argc, jsval* vp)
+CData::ReadString(JSContext* cx, unsigned argc, Value* vp)
 {
   return ReadStringCommon(cx, JS::UTF8CharsToNewTwoByteCharsZ, argc, vp);
 }
 
 bool
-CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc, jsval* vp)
+CData::ReadStringReplaceMalformed(JSContext* cx, unsigned argc, Value* vp)
 {
   return ReadStringCommon(cx, JS::LossyUTF8CharsToNewTwoByteCharsZ, argc, vp);
 }
 
 JSString*
 CData::GetSourceString(JSContext* cx, HandleObject typeObj, void* data)
 {
   // Walk the types, building up the toSource() string.
@@ -7421,17 +7421,17 @@ CData::GetSourceString(JSContext* cx, Ha
     return nullptr;
 
   AppendString(source, ")");
 
   return NewUCString(cx, source);
 }
 
 bool
-CData::ToSource(JSContext* cx, unsigned argc, jsval* vp)
+CData::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CData.prototype.toSource", "no", "s");
   }
 
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
@@ -7470,17 +7470,17 @@ bool
 CData::LastErrorGetter(JSContext* cx, JS::CallArgs args)
 {
   args.rval().set(JS_GetReservedSlot(&args.thisv().toObject(), SLOT_LASTERROR));
   return true;
 }
 #endif // defined(XP_WIN)
 
 bool
-CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc, jsval* vp)
+CDataFinalizer::Methods::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject objThis(cx, JS_THIS_OBJECT(cx, vp));
   if (!objThis)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
     JS_ReportError(cx, "not a CDataFinalizer");
     return false;
@@ -7502,17 +7502,17 @@ CDataFinalizer::Methods::ToSource(JSCont
     AutoString source;
     AppendString(source, "ctypes.CDataFinalizer(");
     JSString* srcValue = CData::GetSourceString(cx, objType, p->cargs);
     if (!srcValue) {
       return false;
     }
     AppendString(source, srcValue);
     AppendString(source, ", ");
-    jsval valCodePtrType = JS_GetReservedSlot(objThis,
+    Value valCodePtrType = JS_GetReservedSlot(objThis,
                                               SLOT_DATAFINALIZER_CODETYPE);
     if (valCodePtrType.isPrimitive()) {
       return false;
     }
 
     RootedObject typeObj(cx, valCodePtrType.toObjectOrNull());
     JSString* srcDispose = CData::GetSourceString(cx, typeObj, &(p->code));
     if (!srcDispose) {
@@ -7529,17 +7529,17 @@ CDataFinalizer::Methods::ToSource(JSCont
     return false;
   }
 
   args.rval().setString(strMessage);
   return true;
 }
 
 bool
-CDataFinalizer::Methods::ToString(JSContext* cx, unsigned argc, jsval* vp)
+CDataFinalizer::Methods::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* objThis = JS_THIS_OBJECT(cx, vp);
   if (!objThis)
     return false;
   if (!CDataFinalizer::IsCDataFinalizer(objThis)) {
     JS_ReportError(cx, "not a CDataFinalizer");
     return false;
@@ -7573,17 +7573,17 @@ CDataFinalizer::IsCDataFinalizer(JSObjec
 }
 
 
 JSObject*
 CDataFinalizer::GetCType(JSContext* cx, JSObject* obj)
 {
   MOZ_ASSERT(IsCDataFinalizer(obj));
 
-  jsval valData = JS_GetReservedSlot(obj,
+  Value valData = JS_GetReservedSlot(obj,
                                      SLOT_DATAFINALIZER_VALTYPE);
   if (valData.isUndefined()) {
     return nullptr;
   }
 
   return valData.toObjectOrNull();
 }
 
@@ -7635,17 +7635,17 @@ CDataFinalizer::GetValue(JSContext* cx, 
  *
  * This function attaches strong references to the following values:
  * - the CType of |value|
  *
  * Note: This function takes advantage of the fact that non-variadic
  * CData functions are initialized during creation.
  */
 bool
-CDataFinalizer::Construct(JSContext* cx, unsigned argc, jsval* vp)
+CDataFinalizer::Construct(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject objSelf(cx, &args.callee());
   RootedObject objProto(cx);
   if (!GetObjectProperty(cx, objSelf, "prototype", &objProto)) {
     JS_ReportError(cx, "CDataFinalizer.prototype does not exist");
     return false;
   }
@@ -7863,17 +7863,17 @@ CDataFinalizer::CallFinalizer(CDataFinal
  * Preconditions: |this| must be a |CDataFinalizer|.
  * The function fails if |this| has gone through |Forget|/|Dispose|
  * or |Finalize|.
  *
  * Does not call the finalizer. Cleans up the Private memory and releases all
  * strong references.
  */
 bool
-CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, jsval* vp)
+CDataFinalizer::Methods::Forget(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CDataFinalizer.prototype.forget", "no",
                                "s");
   }
 
   JS::Rooted<JSObject*> obj(cx, args.thisv().toObjectOrNull());
@@ -7911,17 +7911,17 @@ CDataFinalizer::Methods::Forget(JSContex
  * Preconditions: |this| must be a |CDataFinalizer|.
  * The function fails if |this| has gone through |Forget|/|Dispose|
  * or |Finalize|.
  *
  * Calls the finalizer, cleans up the Private memory and releases all
  * strong references.
  */
 bool
-CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, jsval* vp)
+CDataFinalizer::Methods::Dispose(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 0) {
     return ArgumentLengthError(cx, "CDataFinalizer.prototype.dispose", "no",
                                "s");
   }
 
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
@@ -7935,24 +7935,24 @@ CDataFinalizer::Methods::Dispose(JSConte
   CDataFinalizer::Private* p = (CDataFinalizer::Private*)
     JS_GetPrivate(obj);
 
   if (!p) {
     JS_ReportError(cx, "dispose called on an empty CDataFinalizer.");
     return false;
   }
 
-  jsval valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
+  Value valType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_VALTYPE);
   MOZ_ASSERT(valType.isObject());
 
   JSObject* objCTypes = CType::GetGlobalCTypes(cx, &valType.toObject());
   if (!objCTypes)
     return false;
 
-  jsval valCodePtrType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE);
+  Value valCodePtrType = JS_GetReservedSlot(obj, SLOT_DATAFINALIZER_CODETYPE);
   MOZ_ASSERT(valCodePtrType.isObject());
   JSObject* objCodePtrType = &valCodePtrType.toObject();
 
   JSObject* objCodeType = PointerType::GetBaseType(objCodePtrType);
   MOZ_ASSERT(objCodeType);
   MOZ_ASSERT(CType::GetTypeCode(objCodeType) == TYPE_function);
 
   RootedObject resultType(cx, FunctionType::GetFunctionInfo(objCodeType)->mReturnType);
@@ -8067,28 +8067,28 @@ Int64Base::Construct(JSContext* cx,
     return nullptr;
 
   return result;
 }
 
 void
 Int64Base::Finalize(JSFreeOp* fop, JSObject* obj)
 {
-  jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
+  Value slot = JS_GetReservedSlot(obj, SLOT_INT64);
   if (slot.isUndefined())
     return;
 
   FreeOp::get(fop)->delete_(static_cast<uint64_t*>(slot.toPrivate()));
 }
 
 uint64_t
 Int64Base::GetInt(JSObject* obj) {
   MOZ_ASSERT(Int64::IsInt64(obj) || UInt64::IsUInt64(obj));
 
-  jsval slot = JS_GetReservedSlot(obj, SLOT_INT64);
+  Value slot = JS_GetReservedSlot(obj, SLOT_INT64);
   return *static_cast<uint64_t*>(slot.toPrivate());
 }
 
 bool
 Int64Base::ToString(JSContext* cx,
                     JSObject* obj,
                     const CallArgs& args,
                     bool isUnsigned)
@@ -8099,17 +8099,17 @@ Int64Base::ToString(JSContext* cx,
                                  "at most one", "");
     }
     return ArgumentLengthError(cx, "Int64.prototype.toString",
                                "at most one", "");
   }
 
   int radix = 10;
   if (args.length() == 1) {
-    jsval arg = args[0];
+    Value arg = args[0];
     if (arg.isInt32())
       radix = arg.toInt32();
     if (!arg.isInt32() || radix < 2 || radix > 36) {
       if (isUnsigned) {
         return ArgumentRangeMismatch(cx, "", "UInt64.prototype.toString", "an integer at least 2 and no greater than 36");
       }
       return ArgumentRangeMismatch(cx, "", "Int64.prototype.toString", "an integer at least 2 and no greater than 36");
     }
@@ -8160,17 +8160,17 @@ Int64Base::ToSource(JSContext* cx,
 
   args.rval().setString(result);
   return true;
 }
 
 bool
 Int64::Construct(JSContext* cx,
                  unsigned argc,
-                 jsval* vp)
+                 Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Construct and return a new Int64 object.
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "Int64 constructor", "one", "");
   }
 
@@ -8196,47 +8196,47 @@ Int64::Construct(JSContext* cx,
 
 bool
 Int64::IsInt64(JSObject* obj)
 {
   return JS_GetClass(obj) == &sInt64Class;
 }
 
 bool
-Int64::ToString(JSContext* cx, unsigned argc, jsval* vp)
+Int64::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!Int64::IsInt64(obj)) {
     JS_ReportError(cx, "not an Int64");
     return false;
   }
 
   return Int64Base::ToString(cx, obj, args, false);
 }
 
 bool
-Int64::ToSource(JSContext* cx, unsigned argc, jsval* vp)
+Int64::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!Int64::IsInt64(obj)) {
     JS_ReportError(cx, "not an Int64");
     return false;
   }
 
   return Int64Base::ToSource(cx, obj, args, false);
 }
 
 bool
-Int64::Compare(JSContext* cx, unsigned argc, jsval* vp)
+Int64::Compare(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 2) {
     return ArgumentLengthError(cx, "Int64.compare", "two", "s");
   }
   if (args[0].isPrimitive() || !Int64::IsInt64(&args[0].toObject())) {
     return ArgumentTypeMismatch(cx, "first ", "Int64.compare", "a Int64");
   }
@@ -8260,17 +8260,17 @@ Int64::Compare(JSContext* cx, unsigned a
   return true;
 }
 
 #define LO_MASK ((uint64_t(1) << 32) - 1)
 #define INT64_LO(i) ((i) & LO_MASK)
 #define INT64_HI(i) ((i) >> 32)
 
 bool
-Int64::Lo(JSContext* cx, unsigned argc, jsval* vp)
+Int64::Lo(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "Int64.lo", "one", "");
   }
   if (args[0].isPrimitive() || !Int64::IsInt64(&args[0].toObject())) {
     return ArgumentTypeMismatch(cx, "", "Int64.lo", "a Int64");
   }
@@ -8279,17 +8279,17 @@ Int64::Lo(JSContext* cx, unsigned argc, 
   int64_t u = Int64Base::GetInt(obj);
   double d = uint32_t(INT64_LO(u));
 
   args.rval().setNumber(d);
   return true;
 }
 
 bool
-Int64::Hi(JSContext* cx, unsigned argc, jsval* vp)
+Int64::Hi(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "Int64.hi", "one", "");
   }
   if (args[0].isPrimitive() || !Int64::IsInt64(&args[0].toObject())) {
     return ArgumentTypeMismatch(cx, "", "Int64.hi", "a Int64");
   }
@@ -8298,17 +8298,17 @@ Int64::Hi(JSContext* cx, unsigned argc, 
   int64_t u = Int64Base::GetInt(obj);
   double d = int32_t(INT64_HI(u));
 
   args.rval().setDouble(d);
   return true;
 }
 
 bool
-Int64::Join(JSContext* cx, unsigned argc, jsval* vp)
+Int64::Join(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 2) {
     return ArgumentLengthError(cx, "Int64.join", "two", "s");
   }
 
   int32_t hi;
   uint32_t lo;
@@ -8317,32 +8317,32 @@ Int64::Join(JSContext* cx, unsigned argc
   if (!jsvalToInteger(cx, args[1], &lo))
     return ArgumentConvError(cx, args[1], "Int64.join", 1);
 
   int64_t i = (int64_t(hi) << 32) + int64_t(lo);
 
   // Get Int64.prototype from the function's reserved slot.
   JSObject* callee = &args.callee();
 
-  jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
+  Value slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
   RootedObject proto(cx, &slot.toObject());
   MOZ_ASSERT(JS_GetClass(proto) == &sInt64ProtoClass);
 
   JSObject* result = Int64Base::Construct(cx, proto, i, false);
   if (!result)
     return false;
 
   args.rval().setObject(*result);
   return true;
 }
 
 bool
 UInt64::Construct(JSContext* cx,
                   unsigned argc,
-                  jsval* vp)
+                  Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
 
   // Construct and return a new UInt64 object.
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "UInt64 constructor", "one", "");
   }
 
@@ -8368,47 +8368,47 @@ UInt64::Construct(JSContext* cx,
 
 bool
 UInt64::IsUInt64(JSObject* obj)
 {
   return JS_GetClass(obj) == &sUInt64Class;
 }
 
 bool
-UInt64::ToString(JSContext* cx, unsigned argc, jsval* vp)
+UInt64::ToString(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!UInt64::IsUInt64(obj)) {
     JS_ReportError(cx, "not a UInt64");
     return false;
   }
 
   return Int64Base::ToString(cx, obj, args, true);
 }
 
 bool
-UInt64::ToSource(JSContext* cx, unsigned argc, jsval* vp)
+UInt64::ToSource(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!UInt64::IsUInt64(obj)) {
     JS_ReportError(cx, "not a UInt64");
     return false;
   }
 
   return Int64Base::ToSource(cx, obj, args, true);
 }
 
 bool
-UInt64::Compare(JSContext* cx, unsigned argc, jsval* vp)
+UInt64::Compare(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 2) {
     return ArgumentLengthError(cx, "UInt64.compare", "two", "s");
   }
   if (args[0].isPrimitive() || !UInt64::IsUInt64(&args[0].toObject())) {
     return ArgumentTypeMismatch(cx, "first ", "UInt64.compare", "a UInt64");
   }
@@ -8428,17 +8428,17 @@ UInt64::Compare(JSContext* cx, unsigned 
     args.rval().setInt32(-1);
   else
     args.rval().setInt32(1);
 
   return true;
 }
 
 bool
-UInt64::Lo(JSContext* cx, unsigned argc, jsval* vp)
+UInt64::Lo(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "UInt64.lo", "one", "");
   }
   if (args[0].isPrimitive() || !UInt64::IsUInt64(&args[0].toObject())) {
     return ArgumentTypeMismatch(cx, "", "UInt64.lo", "a UInt64");
   }
@@ -8447,17 +8447,17 @@ UInt64::Lo(JSContext* cx, unsigned argc,
   uint64_t u = Int64Base::GetInt(obj);
   double d = uint32_t(INT64_LO(u));
 
   args.rval().setDouble(d);
   return true;
 }
 
 bool
-UInt64::Hi(JSContext* cx, unsigned argc, jsval* vp)
+UInt64::Hi(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 1) {
     return ArgumentLengthError(cx, "UInt64.hi", "one", "");
   }
   if (args[0].isPrimitive() || !UInt64::IsUInt64(&args[0].toObject())) {
     return ArgumentTypeMismatch(cx, "", "UInt64.hi", "a UInt64");
   }
@@ -8466,17 +8466,17 @@ UInt64::Hi(JSContext* cx, unsigned argc,
   uint64_t u = Int64Base::GetInt(obj);
   double d = uint32_t(INT64_HI(u));
 
   args.rval().setDouble(d);
   return true;
 }
 
 bool
-UInt64::Join(JSContext* cx, unsigned argc, jsval* vp)
+UInt64::Join(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 2) {
     return ArgumentLengthError(cx, "UInt64.join", "two", "s");
   }
 
   uint32_t hi;
   uint32_t lo;
@@ -8485,17 +8485,17 @@ UInt64::Join(JSContext* cx, unsigned arg
   if (!jsvalToInteger(cx, args[1], &lo))
     return ArgumentConvError(cx, args[1], "UInt64.join", 1);
 
   uint64_t u = (uint64_t(hi) << 32) + uint64_t(lo);
 
   // Get UInt64.prototype from the function's reserved slot.
   JSObject* callee = &args.callee();
 
-  jsval slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
+  Value slot = js::GetFunctionNativeReserved(callee, SLOT_FN_INT64PROTO);
   RootedObject proto(cx, &slot.toObject());
   MOZ_ASSERT(JS_GetClass(proto) == &sUInt64ProtoClass);
 
   JSObject* result = Int64Base::Construct(cx, proto, u, true);
   if (!result)
     return false;
 
   args.rval().setObject(*result);
--- a/js/src/ctypes/CTypes.h
+++ b/js/src/ctypes/CTypes.h
@@ -289,17 +289,17 @@ TraceFieldInfoHash(JSTracer* trc, FieldI
 struct FunctionInfo
 {
   // Initialized in NewFunctionInfo when !mIsVariadic, but only later, in
   // FunctionType::Call, when mIsVariadic. Not always consistent with
   // mFFITypes, due to lazy initialization when mIsVariadic.
   ffi_cif mCIF;
 
   // Calling convention of the function. Convert to ffi_abi using GetABI
-  // and OBJECT_TO_JSVAL. Stored as a JSObject* for ease of tracing.
+  // and ObjectValue. Stored as a JSObject* for ease of tracing.
   JS::Heap<JSObject*> mABI;
 
   // The CType of the value returned by the function.
   JS::Heap<JSObject*> mReturnType;
 
   // A fixed array of known parameter types, excluding any variadic
   // parameters (if mIsVariadic).
   Array<JS::Heap<JSObject*> > mArgTypes;
@@ -346,18 +346,18 @@ bool IsCTypesGlobal(JSObject* obj);
 const JSCTypesCallbacks* GetCallbacks(JSObject* obj);
 
 /*******************************************************************************
 ** JSClass reserved slot definitions
 *******************************************************************************/
 
 enum CTypesGlobalSlot {
   SLOT_CALLBACKS = 0, // pointer to JSCTypesCallbacks struct
-  SLOT_ERRNO = 1,     // jsval for latest |errno|
-  SLOT_LASTERROR = 2, // jsval for latest |GetLastError|, used only with Windows
+  SLOT_ERRNO = 1,     // Value for latest |errno|
+  SLOT_LASTERROR = 2, // Value for latest |GetLastError|, used only with Windows
   CTYPESGLOBAL_SLOTS
 };
 
 enum CABISlot {
   SLOT_ABICODE = 0, // ABICode of the CABI object
   CABI_SLOTS
 };
 
@@ -397,17 +397,17 @@ enum CTypeSlot {
   SLOT_ARGS_T    = 8, // (FunctionTypes only) 'argTypes' property (cached)
   CTYPE_SLOTS
 };
 
 enum CDataSlot {
   SLOT_CTYPE    = 0, // CType object representing the underlying type
   SLOT_REFERENT = 1, // JSObject this object must keep alive, if any
   SLOT_DATA     = 2, // pointer to a buffer containing the binary data
-  SLOT_OWNS     = 3, // JSVAL_TRUE if this CData owns its own buffer
+  SLOT_OWNS     = 3, // TrueValue() if this CData owns its own buffer
   SLOT_FUNNAME  = 4, // JSString representing the function name
   CDATA_SLOTS
 };
 
 enum CClosureSlot {
   SLOT_CLOSUREINFO = 0, // ClosureInfo struct
   CCLOSURE_SLOTS
 };
@@ -438,21 +438,21 @@ enum Int64FunctionSlot {
 };
 
 /*******************************************************************************
 ** Object API definitions
 *******************************************************************************/
 
 namespace CType {
   JSObject* Create(JSContext* cx, HandleObject typeProto, HandleObject dataProto,
-    TypeCode type, JSString* name, jsval size, jsval align, ffi_type* ffiType);
+    TypeCode type, JSString* name, Value size, Value align, ffi_type* ffiType);
 
   JSObject* DefineBuiltin(JSContext* cx, HandleObject ctypesObj, const char* propName,
     JSObject* typeProto, JSObject* dataProto, const char* name, TypeCode type,
-    jsval size, jsval align, ffi_type* ffiType);
+    Value size, Value align, ffi_type* ffiType);
 
   bool IsCType(JSObject* obj);
   bool IsCTypeProto(JSObject* obj);
   TypeCode GetTypeCode(JSObject* typeObj);
   bool TypesEqual(JSObject* t1, JSObject* t2);
   size_t GetSize(JSObject* obj);
   bool GetSafeSize(JSObject* obj, size_t* result);
   bool IsSizeDefined(JSObject* obj);
@@ -500,33 +500,33 @@ namespace FunctionType {
 
   FunctionInfo* GetFunctionInfo(JSObject* obj);
   void BuildSymbolName(JSString* name, JSObject* typeObj,
     AutoCString& result);
 } // namespace FunctionType
 
 namespace CClosure {
   JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject fnObj,
-    HandleObject thisObj, jsval errVal, PRFuncPtr* fnptr);
+    HandleObject thisObj, Value errVal, PRFuncPtr* fnptr);
 } // namespace CClosure
 
 namespace CData {
   JSObject* Create(JSContext* cx, HandleObject typeObj, HandleObject refObj,
     void* data, bool ownResult);
 
   JSObject* GetCType(JSObject* dataObj);
   void* GetData(JSObject* dataObj);
   bool IsCData(JSObject* obj);
   bool IsCData(HandleValue v);
   bool IsCDataProto(JSObject* obj);
 
   // Attached by JSAPI as the function 'ctypes.cast'
-  bool Cast(JSContext* cx, unsigned argc, jsval* vp);
+  bool Cast(JSContext* cx, unsigned argc, Value* vp);
   // Attached by JSAPI as the function 'ctypes.getRuntime'
-  bool GetRuntime(JSContext* cx, unsigned argc, jsval* vp);
+  bool GetRuntime(JSContext* cx, unsigned argc, Value* vp);
 } // namespace CData
 
 namespace Int64 {
   bool IsInt64(JSObject* obj);
 } // namespace Int64
 
 namespace UInt64 {
   bool IsUInt64(JSObject* obj);
--- a/js/src/ctypes/Library.cpp
+++ b/js/src/ctypes/Library.cpp
@@ -17,18 +17,18 @@ namespace ctypes {
 /*******************************************************************************
 ** JSAPI function prototypes
 *******************************************************************************/
 
 namespace Library
 {
   static void Finalize(JSFreeOp* fop, JSObject* obj);
 
-  static bool Close(JSContext* cx, unsigned argc, jsval* vp);
-  static bool Declare(JSContext* cx, unsigned argc, jsval* vp);
+  static bool Close(JSContext* cx, unsigned argc, Value* vp);
+  static bool Declare(JSContext* cx, unsigned argc, Value* vp);
 } // namespace Library
 
 /*******************************************************************************
 ** JSObject implementation
 *******************************************************************************/
 
 typedef Rooted<JSFlatString*>    RootedFlatString;
 
@@ -44,17 +44,17 @@ static const JSClass sLibraryClass = {
 
 static const JSFunctionSpec sLibraryFunctions[] = {
   JS_FN("close",   Library::Close,   0, CTYPESFN_FLAGS),
   JS_FN("declare", Library::Declare, 0, CTYPESFN_FLAGS),
   JS_FS_END
 };
 
 bool
-Library::Name(JSContext* cx, unsigned argc, jsval* vp)
+Library::Name(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   if (args.length() != 1) {
     JS_ReportError(cx, "libraryName takes one argument");
     return false;
   }
 
   Value arg = args[0];
@@ -76,17 +76,17 @@ Library::Name(JSContext* cx, unsigned ar
   if (!result)
     return false;
 
   args.rval().setString(result);
   return true;
 }
 
 JSObject*
-Library::Create(JSContext* cx, jsval path_, const JSCTypesCallbacks* callbacks)
+Library::Create(JSContext* cx, Value path_, const JSCTypesCallbacks* callbacks)
 {
   RootedValue path(cx, path_);
   RootedObject libraryObj(cx, JS_NewObject(cx, &sLibraryClass));
   if (!libraryObj)
     return nullptr;
 
   // initialize the library
   JS_SetReservedSlot(libraryObj, SLOT_LIBRARY, PrivateValue(nullptr));
@@ -177,17 +177,17 @@ Library::IsLibrary(JSObject* obj)
   return JS_GetClass(obj) == &sLibraryClass;
 }
 
 PRLibrary*
 Library::GetLibrary(JSObject* obj)
 {
   MOZ_ASSERT(IsLibrary(obj));
 
-  jsval slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
+  Value slot = JS_GetReservedSlot(obj, SLOT_LIBRARY);
   return static_cast<PRLibrary*>(slot.toPrivate());
 }
 
 static void
 UnloadLibrary(JSObject* obj)
 {
   PRLibrary* library = Library::GetLibrary(obj);
   if (library)
@@ -196,17 +196,17 @@ UnloadLibrary(JSObject* obj)
 
 void
 Library::Finalize(JSFreeOp* fop, JSObject* obj)
 {
   UnloadLibrary(obj);
 }
 
 bool
-Library::Open(JSContext* cx, unsigned argc, jsval* vp)
+Library::Open(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* ctypesObj = JS_THIS_OBJECT(cx, vp);
   if (!ctypesObj)
     return false;
   if (!IsCTypesGlobal(ctypesObj)) {
     JS_ReportError(cx, "not a ctypes object");
     return false;
@@ -221,17 +221,17 @@ Library::Open(JSContext* cx, unsigned ar
   if (!library)
     return false;
 
   args.rval().setObject(*library);
   return true;
 }
 
 bool
-Library::Close(JSContext* cx, unsigned argc, jsval* vp)
+Library::Close(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   JSObject* obj = JS_THIS_OBJECT(cx, vp);
   if (!obj)
     return false;
   if (!IsLibrary(obj)) {
     JS_ReportError(cx, "not a library");
     return false;
@@ -246,17 +246,17 @@ Library::Close(JSContext* cx, unsigned a
   UnloadLibrary(obj);
   JS_SetReservedSlot(obj, SLOT_LIBRARY, PrivateValue(nullptr));
 
   args.rval().setUndefined();
   return true;
 }
 
 bool
-Library::Declare(JSContext* cx, unsigned argc, jsval* vp)
+Library::Declare(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
   RootedObject obj(cx, JS_THIS_OBJECT(cx, vp));
   if (!obj)
     return false;
   if (!IsLibrary(obj)) {
     JS_ReportError(cx, "not a library");
     return false;
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -443,23 +443,22 @@ frontend::CompileScript(ExclusiveContext
      */
     if (!bce.emit1(JSOP_RETRVAL))
         return nullptr;
 
     // Global/eval script bindings are always empty (all names are added to the
     // scope dynamically via JSOP_DEFFUN/VAR).  They may have block-scoped
     // locals, however, which are allocated to the fixed part of the stack
     // frame.
-    Rooted<Bindings> bindings(cx, script->bindings);
-    if (!Bindings::initWithTemporaryStorage(cx, &bindings, 0, 0, 0,
+    InternalHandle<Bindings*> bindings(script, &script->bindings);
+    if (!Bindings::initWithTemporaryStorage(cx, bindings, 0, 0, 0,
                                             pc->blockScopeDepth, 0, 0, nullptr))
     {
         return nullptr;
     }
-    script->bindings = bindings;
 
     if (!JSScript::fullyInitFromEmitter(cx, script, &bce))
         return nullptr;
 
     // Note that this marking must happen before we tell Debugger
     // about the new script, in case Debugger delazifies the script's
     // inner functions.
     if (options.forEval)
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -3409,17 +3409,18 @@ BytecodeEmitter::emitFunctionScript(Pars
     else
         fun->setScript(script);
 
     if (funbox->argumentsHasLocalBinding()) {
         MOZ_ASSERT(offset() == 0);  /* See JSScript::argumentsBytecode. */
         switchToPrologue();
         if (!emit1(JSOP_ARGUMENTS))
             return false;
-        BindingIter bi = Bindings::argumentsBinding(cx, script);
+        InternalBindingsHandle bindings(script, &script->bindings);
+        BindingIter bi = Bindings::argumentsBinding(cx, bindings);
         if (script->bindingIsAliased(bi)) {
             ScopeCoordinate sc;
             sc.setHops(0);
             sc.setSlot(0);  // initialize to silence GCC warning
             JS_ALWAYS_TRUE(lookupAliasedNameSlot(cx->names().arguments, &sc));
             if (!emitAliasedVarOp(JSOP_SETALIASEDVAR, sc, DontCheckLexical))
                 return false;
         } else {
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -366,17 +366,17 @@ AppendPackedBindings(const ParseContext<
             ++*numUnaliased;
     }
 }
 
 template <typename ParseHandler>
 bool
 ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
                                                      LifoAlloc& alloc,
-                                                     MutableHandle<Bindings> bindings) const
+                                                     InternalHandle<Bindings*> bindings) const
 {
     MOZ_ASSERT(sc->isFunctionBox());
     MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
     MOZ_ASSERT(vars_.length() + bodyLevelLexicals_.length() < LOCALNO_LIMIT);
 
     /*
      * Avoid pathological edge cases by explicitly limiting the total number of
      * bindings to what will fit in a uint32_t.
@@ -842,20 +842,20 @@ Parser<FullParseHandler>::standaloneFunc
         for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
             Definition* dn = r.front().value().get<FullParseHandler>();
             MOZ_ASSERT(dn->isPlaceholder());
 
             handler.deoptimizeUsesWithin(dn, fn->pn_pos);
         }
     }
 
-    Rooted<Bindings> bindings(context, funbox->bindings);
-    if (!funpc.generateFunctionBindings(context, tokenStream, alloc, &bindings))
-        return null();
-    funbox->bindings = bindings;
+    InternalHandle<Bindings*> funboxBindings =
+        InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
+    if (!funpc.generateFunctionBindings(context, tokenStream, alloc, funboxBindings))
+        return null();
 
     return fn;
 }
 
 template <>
 bool
 Parser<FullParseHandler>::checkFunctionArguments()
 {
@@ -1465,22 +1465,19 @@ Parser<FullParseHandler>::leaveFunction(
                 outer_dn->pn_dflags |= dn->pn_dflags & ~PND_PLACEHOLDER;
             }
 
             /* Mark the outer dn as escaping. */
             outer_dn->pn_dflags |= PND_CLOSED;
         }
     }
 
-    Rooted<Bindings> bindings(context, funbox->bindings);
-    if (!pc->generateFunctionBindings(context, tokenStream, alloc, &bindings))
-        return false;
-    funbox->bindings = bindings;
-
-    return true;
+    InternalHandle<Bindings*> bindings =
+        InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
+    return pc->generateFunctionBindings(context, tokenStream, alloc, bindings);
 }
 
 template <>
 bool
 Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler>* outerpc,
                                           FunctionSyntaxKind kind)
 {
     outerpc->blockidGen = pc->blockidGen;
@@ -2490,20 +2487,20 @@ Parser<FullParseHandler>::standaloneLazy
     if (fun->isNamedLambda()) {
         if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) {
             Definition* dn = p.value().get<FullParseHandler>();
             if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
                 return nullptr;
         }
     }
 
-    Rooted<Bindings> bindings(context, funbox->bindings);
-    if (!pc->generateFunctionBindings(context, tokenStream, alloc, &bindings))
-        return null();
-    funbox->bindings = bindings;
+    InternalHandle<Bindings*> bindings =
+        InternalHandle<Bindings*>::fromMarkedLocation(&funbox->bindings);
+    if (!pc->generateFunctionBindings(context, tokenStream, alloc, bindings))
+        return null();
 
     if (!FoldConstants(context, &pn, this))
         return null();
 
     return pn;
 }
 
 template <typename ParseHandler>
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -208,17 +208,17 @@ struct ParseContext : public GenericPars
      *  - Bindings provide the initial js::Shape to use when creating a dynamic
      *    scope object (js::CallObject) for the function. This shape is used
      *    during dynamic name lookup.
      *  - Sometimes a script's bindings are accessed at runtime to retrieve the
      *    contents of the lexical scope (e.g., from the debugger).
      */
     bool generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
                                   LifoAlloc& alloc,
-                                  MutableHandle<Bindings> bindings) const;
+                                  InternalHandle<Bindings*> bindings) const;
 
   private:
     ParseContext**  parserPC;     /* this points to the Parser's active pc
                                        and holds either |this| or one of
                                        |this|'s descendents */
 
     // Value for parserPC to restore at the end. Use 'parent' instead for
     // information about the parse chain, this may be nullptr if
--- a/js/src/gc/Barrier.h
+++ b/js/src/gc/Barrier.h
@@ -232,16 +232,18 @@ void MarkIdForBarrier(JSTracer* trc, jsi
 template <typename T>
 struct InternalGCMethods {};
 
 template <typename T>
 struct InternalGCMethods<T*>
 {
     static bool isMarkable(T* v) { return v != nullptr; }
 
+    static bool isMarkableTaggedPointer(T* v) { return !IsNullTaggedPointer(v); }
+
     static void preBarrier(T* v) { T::writeBarrierPre(v); }
 
     static void postBarrier(T** vp, T* prev, T* next) { T::writeBarrierPost(vp, prev, next); }
 
     static void readBarrier(T* v) { T::readBarrier(v); }
 };
 
 template <typename S> struct PreBarrierFunctor : VoidDefaultAdaptor<S> {
@@ -251,16 +253,17 @@ template <typename S> struct PreBarrierF
 template <typename S> struct ReadBarrierFunctor : public VoidDefaultAdaptor<S> {
     template <typename T> void operator()(T* t);
 };
 
 template <>
 struct InternalGCMethods<Value>
 {
     static bool isMarkable(Value v) { return v.isMarkable(); }
+    static bool isMarkableTaggedPointer(Value v) { return isMarkable(v); }
 
     static void preBarrier(Value v) {
         DispatchValueTyped(PreBarrierFunctor<Value>(), v);
     }
 
     static void postBarrier(Value* vp, const Value& prev, const Value& next) {
         MOZ_ASSERT(!CurrentThreadIsIonCompiling());
         MOZ_ASSERT(vp);
@@ -286,16 +289,17 @@ struct InternalGCMethods<Value>
         DispatchValueTyped(ReadBarrierFunctor<Value>(), v);
     }
 };
 
 template <>
 struct InternalGCMethods<jsid>
 {
     static bool isMarkable(jsid id) { return JSID_IS_STRING(id) || JSID_IS_SYMBOL(id); }
+    static bool isMarkableTaggedPointer(jsid id) { return isMarkable(id); }
 
     static void preBarrier(jsid id) { DispatchIdTyped(PreBarrierFunctor<jsid>(), id); }
     static void postBarrier(jsid* idp, jsid prev, jsid next) {}
 };
 
 template <typename T>
 class BarrieredBaseMixins {};
 
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -446,16 +446,25 @@ void
 js::TraceRoot(JSTracer* trc, T* thingp, const char* name)
 {
     AssertRootMarkingPhase(trc);
     DispatchToTracer(trc, ConvertToBase(thingp), name);
 }
 
 template <typename T>
 void
+js::TraceNullableRoot(JSTracer* trc, T* thingp, const char* name)
+{
+    AssertRootMarkingPhase(trc);
+    if (InternalGCMethods<T>::isMarkableTaggedPointer(*thingp))
+        DispatchToTracer(trc, ConvertToBase(thingp), name);
+}
+
+template <typename T>
+void
 js::TraceRange(JSTracer* trc, size_t len, BarrieredBase<T>* vec, const char* name)
 {
     JS::AutoTracingIndex index(trc);
     for (auto i : MakeRange(len)) {
         if (InternalGCMethods<T>::isMarkable(vec[i].get()))
             DispatchToTracer(trc, ConvertToBase(vec[i].unsafeGet()), name);
         ++index;
     }
@@ -474,16 +483,17 @@ js::TraceRootRange(JSTracer* trc, size_t
     }
 }
 
 // Instantiate a copy of the Tracing templates for each derived type.
 #define INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS(type) \
     template void js::TraceEdge<type>(JSTracer*, BarrieredBase<type>*, const char*); \
     template void js::TraceManuallyBarrieredEdge<type>(JSTracer*, type*, const char*); \
     template void js::TraceRoot<type>(JSTracer*, type*, const char*); \
+    template void js::TraceNullableRoot<type>(JSTracer*, type*, const char*); \
     template void js::TraceRange<type>(JSTracer*, size_t, BarrieredBase<type>*, const char*); \
     template void js::TraceRootRange<type>(JSTracer*, size_t, type*, const char*);
 FOR_EACH_GC_POINTER_TYPE(INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS)
 #undef INSTANTIATE_ALL_VALID_TRACE_FUNCTIONS
 
 template <typename T>
 void
 js::TraceManuallyBarrieredCrossCompartmentEdge(JSTracer* trc, JSObject* src, T* dst,
--- a/js/src/gc/RootMarking.cpp
+++ b/js/src/gc/RootMarking.cpp
@@ -40,71 +40,59 @@ typedef RootedValueMap::Range RootRange;
 typedef RootedValueMap::Entry RootEntry;
 typedef RootedValueMap::Enum RootEnum;
 
 // Note: the following two functions cannot be static as long as we are using
 // GCC 4.4, since it requires template function parameters to have external
 // linkage.
 
 void
+MarkBindingsRoot(JSTracer* trc, Bindings* bindings, const char* name)
+{
+    bindings->trace(trc);
+}
+
+void
 MarkPropertyDescriptorRoot(JSTracer* trc, JSPropertyDescriptor* pd, const char* name)
 {
     pd->trace(trc);
 }
 
-template <class T>
-static inline bool
-IgnoreExactRoot(T* thingp)
-{
-    return false;
-}
+template <typename T>
+using TraceFunction = void (*)(JSTracer* trc, T* ref, const char* name);
 
-template <class T>
-inline bool
-IgnoreExactRoot(T** thingp)
-{
-    return IsNullTaggedPointer(*thingp);
-}
-
-template <>
-inline bool
-IgnoreExactRoot(JSObject** thingp)
-{
-    return IsNullTaggedPointer(*thingp) || *thingp == TaggedProto::LazyProto;
-}
-
-template <class T, void MarkFunc(JSTracer* trc, T* ref, const char* name), class Source>
+template <class T, TraceFunction<T> TraceFn = TraceNullableRoot, class Source>
 static inline void
 MarkExactStackRootList(JSTracer* trc, Source* s, const char* name)
 {
     Rooted<T>* rooter = s->roots.template gcRooters<T>();
     while (rooter) {
         T* addr = rooter->address();
-        if (!IgnoreExactRoot(addr))
-            MarkFunc(trc, addr, name);
+        TraceFn(trc, addr, name);
         rooter = rooter->previous();
     }
 }
 
 template<class T>
 static void
 MarkExactStackRootsAcrossTypes(T context, JSTracer* trc)
 {
-    MarkExactStackRootList<JSObject*, TraceRoot>(trc, context, "exact-object");
-    MarkExactStackRootList<Shape*, TraceRoot>(trc, context, "exact-shape");
-    MarkExactStackRootList<BaseShape*, TraceRoot>(trc, context, "exact-baseshape");
-    MarkExactStackRootList<ObjectGroup*, TraceRoot>(
+    MarkExactStackRootList<JSObject*>(trc, context, "exact-object");
+    MarkExactStackRootList<Shape*>(trc, context, "exact-shape");
+    MarkExactStackRootList<BaseShape*>(trc, context, "exact-baseshape");
+    MarkExactStackRootList<ObjectGroup*>(
         trc, context, "exact-objectgroup");
-    MarkExactStackRootList<JSString*, TraceRoot>(trc, context, "exact-string");
-    MarkExactStackRootList<JS::Symbol*, TraceRoot>(trc, context, "exact-symbol");
-    MarkExactStackRootList<jit::JitCode*, TraceRoot>(trc, context, "exact-jitcode");
-    MarkExactStackRootList<JSScript*, TraceRoot>(trc, context, "exact-script");
-    MarkExactStackRootList<LazyScript*, TraceRoot>(trc, context, "exact-lazy-script");
-    MarkExactStackRootList<jsid, TraceRoot>(trc, context, "exact-id");
-    MarkExactStackRootList<Value, TraceRoot>(trc, context, "exact-value");
+    MarkExactStackRootList<JSString*>(trc, context, "exact-string");
+    MarkExactStackRootList<JS::Symbol*>(trc, context, "exact-symbol");
+    MarkExactStackRootList<jit::JitCode*>(trc, context, "exact-jitcode");
+    MarkExactStackRootList<JSScript*>(trc, context, "exact-script");
+    MarkExactStackRootList<LazyScript*>(trc, context, "exact-lazy-script");
+    MarkExactStackRootList<jsid>(trc, context, "exact-id");
+    MarkExactStackRootList<Value>(trc, context, "exact-value");
+    MarkExactStackRootList<Bindings, MarkBindingsRoot>(trc, context, "Bindings");
     MarkExactStackRootList<JSPropertyDescriptor, MarkPropertyDescriptorRoot>(
         trc, context, "JSPropertyDescriptor");
     MarkExactStackRootList<JS::StaticTraceable,
                            js::DispatchWrapper<JS::StaticTraceable>::TraceWrapped>(
         trc, context, "StaticTraceable");
 }
 
 static void
@@ -329,51 +317,39 @@ namespace gc {
 template<typename T>
 struct PersistentRootedMarker
 {
     typedef PersistentRooted<T> Element;
     typedef mozilla::LinkedList<Element> List;
     typedef void (*MarkFunc)(JSTracer* trc, T* ref, const char* name);
 
     static void
-    markChainIfNotNull(JSTracer* trc, List& list, const char* name)
-    {
-        for (Element* r = list.getFirst(); r; r = r->getNext()) {
-            if (r->get())
-                TraceRoot(trc, r->address(), name);
-        }
-    }
-
-    static void
     markChain(JSTracer* trc, List& list, const char* name)
     {
         for (Element* r = list.getFirst(); r; r = r->getNext())
-            TraceRoot(trc, r->address(), name);
+            TraceNullableRoot(trc, r->address(), name);
     }
 };
 
 } // namespace gc
 } // namespace js
 
 void
 js::gc::MarkPersistentRootedChains(JSTracer* trc)
 {
     JSRuntime* rt = trc->runtime();
 
-    // Mark the PersistentRooted chains of types that may be null.
-    PersistentRootedMarker<JSFunction*>::markChainIfNotNull(trc, rt->functionPersistentRooteds,
-                                                            "PersistentRooted<JSFunction*>");
-    PersistentRootedMarker<JSObject*>::markChainIfNotNull(trc, rt->objectPersistentRooteds,
-                                                          "PersistentRooted<JSObject*>");
-    PersistentRootedMarker<JSScript*>::markChainIfNotNull(trc, rt->scriptPersistentRooteds,
-                                                          "PersistentRooted<JSScript*>");
-    PersistentRootedMarker<JSString*>::markChainIfNotNull(trc, rt->stringPersistentRooteds,
-                                                          "PersistentRooted<JSString*>");
-
-    // Mark the PersistentRooted chains of types that are never null.
+    PersistentRootedMarker<JSFunction*>::markChain(trc, rt->functionPersistentRooteds,
+                                                   "PersistentRooted<JSFunction*>");
+    PersistentRootedMarker<JSObject*>::markChain(trc, rt->objectPersistentRooteds,
+                                                 "PersistentRooted<JSObject*>");
+    PersistentRootedMarker<JSScript*>::markChain(trc, rt->scriptPersistentRooteds,
+                                                 "PersistentRooted<JSScript*>");
+    PersistentRootedMarker<JSString*>::markChain(trc, rt->stringPersistentRooteds,
+                                                 "PersistentRooted<JSString*>");
     PersistentRootedMarker<jsid>::markChain(trc, rt->idPersistentRooteds,
                                             "PersistentRooted<jsid>");
     PersistentRootedMarker<Value>::markChain(trc, rt->valuePersistentRooteds,
                                              "PersistentRooted<Value>");
 }
 
 void
 js::gc::GCRuntime::markRuntime(JSTracer* trc,
--- a/js/src/gc/Tracer.h
+++ b/js/src/gc/Tracer.h
@@ -57,16 +57,22 @@ TraceEdge(JSTracer* trc, BarrieredBase<T
 
 // Trace through a "root" edge. These edges are the initial edges in the object
 // graph traversal. Root edges are asserted to only be traversed in the initial
 // phase of a GC.
 template <typename T>
 void
 TraceRoot(JSTracer* trc, T* thingp, const char* name);
 
+// Idential to TraceRoot, except that this variant will not crash if |*thingp|
+// is null.
+template <typename T>
+void
+TraceNullableRoot(JSTracer* trc, T* thingp, const char* name);
+
 // Like TraceEdge, but for edges that do not use one of the automatic barrier
 // classes and, thus, must be treated specially for moving GC. This method is
 // separate from TraceEdge to make accidental use of such edges more obvious.
 template <typename T>
 void
 TraceManuallyBarrieredEdge(JSTracer* trc, T* thingp, const char* name);
 
 // Trace all edges contained in the given array.
--- a/js/src/gdb/tests/test-Root.cpp
+++ b/js/src/gdb/tests/test-Root.cpp
@@ -23,17 +23,17 @@ void callee(JS::Handle<JSObject*> obj, J
 
 FRAGMENT(Root, handle) {
   JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
   callee(global, &global);
   (void) global;
 }
 
 FRAGMENT(Root, HeapSlot) {
-  JS::Rooted<jsval> plinth(cx, JS::StringValue(JS_NewStringCopyZ(cx, "plinth")));
+  JS::Rooted<JS::Value> plinth(cx, JS::StringValue(JS_NewStringCopyZ(cx, "plinth")));
   JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, JS::HandleValueArray(plinth)));
 
   breakpoint();
 
   (void) plinth;
   (void) array;
 }
 
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -3309,16 +3309,44 @@ BaselineCompiler::emit_JSOP_TOID()
         return false;
 
     masm.bind(&done);
     frame.pop(); // Pop index.
     frame.push(R0);
     return true;
 }
 
+typedef JSString* (*ToStringFn)(JSContext*, HandleValue);
+static const VMFunction ToStringInfo = FunctionInfo<ToStringFn>(ToStringSlow);
+
+bool
+BaselineCompiler::emit_JSOP_TOSTRING()
+{
+    // Keep top stack value in R0.
+    frame.popRegsAndSync(1);
+
+    // Inline path for string.
+    Label done;
+    masm.branchTestString(Assembler::Equal, R0, &done);
+
+    prepareVMCall();
+
+    pushArg(R0);
+
+    // Call ToStringSlow which doesn't handle string inputs.
+    if (!callVM(ToStringInfo))
+        return false;
+
+    masm.tagValue(JSVAL_TYPE_STRING, ReturnReg, R0);
+
+    masm.bind(&done);
+    frame.push(R0);
+    return true;
+}
+
 bool
 BaselineCompiler::emit_JSOP_TABLESWITCH()
 {
     frame.popRegsAndSync(1);
 
     // Call IC.
     ICTableSwitch::Compiler compiler(cx, pc);
     return emitOpIC(compiler.getStub(&stubSpace_));
--- a/js/src/jit/BaselineCompiler.h
+++ b/js/src/jit/BaselineCompiler.h
@@ -178,16 +178,17 @@ namespace jit {
     _(JSOP_FRESHENBLOCKSCOPE)  \
     _(JSOP_DEBUGLEAVEBLOCK)    \
     _(JSOP_EXCEPTION)          \
     _(JSOP_DEBUGGER)           \
     _(JSOP_ARGUMENTS)          \
     _(JSOP_RUNONCE)            \
     _(JSOP_REST)               \
     _(JSOP_TOID)               \
+    _(JSOP_TOSTRING)           \
     _(JSOP_TABLESWITCH)        \
     _(JSOP_ITER)               \
     _(JSOP_MOREITER)           \
     _(JSOP_ISNOITER)           \
     _(JSOP_ENDITER)            \
     _(JSOP_GENERATOR)          \
     _(JSOP_INITIALYIELD)       \
     _(JSOP_YIELD)              \
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -19,17 +19,17 @@
 
 using mozilla::DebugOnly;
 
 namespace js {
 namespace jit {
 
 static Register CallReg = ip;
 static const int defaultShift = 3;
-JS_STATIC_ASSERT(1 << defaultShift == sizeof(jsval));
+JS_STATIC_ASSERT(1 << defaultShift == sizeof(JS::Value));
 
 // MacroAssemblerARM is inheriting form Assembler defined in
 // Assembler-arm.{h,cpp}
 class MacroAssemblerARM : public Assembler
 {
   protected:
     // On ARM, some instructions require a second scratch register. This
     // register defaults to lr, since it's non-allocatable (as it can be
--- a/js/src/jit/mips/MacroAssembler-mips.h
+++ b/js/src/jit/mips/MacroAssembler-mips.h
@@ -58,17 +58,17 @@ struct ImmType : public ImmTag
     { }
 };
 
 static const ValueOperand JSReturnOperand = ValueOperand(JSReturnReg_Type, JSReturnReg_Data);
 static const ValueOperand softfpReturnOperand = ValueOperand(v1, v0);
 
 static Register CallReg = t9;
 static const int defaultShift = 3;
-static_assert(1 << defaultShift == sizeof(jsval), "The defaultShift is wrong");
+static_assert(1 << defaultShift == sizeof(JS::Value), "The defaultShift is wrong");
 
 class MacroAssemblerMIPS : public Assembler
 {
   public:
     // higher level tag testing code
     Operand ToPayload(Operand base);
     Address ToPayload(Address base) {
         return ToPayload(Operand(base)).toAddress();
--- a/js/src/jsapi-tests/testChromeBuffer.cpp
+++ b/js/src/jsapi-tests/testChromeBuffer.cpp
@@ -25,17 +25,17 @@ static const JSClass global_class = {
     nullptr,
     JS_GlobalObjectTraceHook
 };
 
 static JS::PersistentRootedObject trusted_glob;
 static JS::PersistentRootedObject trusted_fun;
 
 static bool
-CallTrusted(JSContext* cx, unsigned argc, jsval* vp)
+CallTrusted(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
     if (!JS_SaveFrameChain(cx))
         return false;
 
     bool ok = false;
     {
--- a/js/src/jsapi-tests/testClassGetter.cpp
+++ b/js/src/jsapi-tests/testClassGetter.cpp
@@ -14,38 +14,38 @@ static int called_test_prop_get;
 
 static bool test_prop_get( JSContext* cx, JS::HandleObject obj, JS::HandleId id, JS::MutableHandleValue vp )
 {
     called_test_prop_get++;
     return true;
 }
 
 static bool
-PTest(JSContext* cx, unsigned argc, jsval* vp);
+PTest(JSContext* cx, unsigned argc, JS::Value* vp);
 
 static const JSClass ptestClass = {
     "PTest",
     JSCLASS_HAS_PRIVATE,
     nullptr, // addProperty
     nullptr, // delProperty
     test_prop_get,
     nullptr // setProperty
 };
 
 static bool
-PTest(JSContext* cx, unsigned argc, jsval* vp)
+PTest(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     JSObject* obj = JS_NewObjectForConstructor(cx, &ptestClass, args);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
-static bool test_fn(JSContext* cx, unsigned argc, jsval* vp)
+static bool test_fn(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     called_test_fn++;
     return true;
 }
 
 static const JSFunctionSpec ptestFunctions[] = {
     JS_FS( "test_fn", test_fn, 0, 0 ),
     JS_FS_END
--- a/js/src/jsapi-tests/testContexts.cpp
+++ b/js/src/jsapi-tests/testContexts.cpp
@@ -8,17 +8,17 @@
 
 BEGIN_TEST(testContexts_IsRunning)
     {
         CHECK(JS_DefineFunction(cx, global, "chk", chk, 0, 0));
         EXEC("for (var i = 0; i < 9; i++) chk();");
         return true;
     }
 
-    static bool chk(JSContext* cx, unsigned argc, jsval* vp)
+    static bool chk(JSContext* cx, unsigned argc, JS::Value* vp)
     {
         JSRuntime* rt = JS_GetRuntime(cx);
         JSContext* acx = JS_NewContext(rt, 8192);
         if (!acx) {
             JS_ReportOutOfMemory(cx);
             return false;
         }
 
--- a/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
+++ b/js/src/jsapi-tests/testDefineGetterSetterNonEnumerable.cpp
@@ -3,17 +3,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 "jsapi-tests/tests.h"
 
 static bool
-NativeGetterSetter(JSContext* cx, unsigned argc, jsval* vp)
+NativeGetterSetter(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     return true;
 }
 
 BEGIN_TEST(testDefineGetterSetterNonEnumerable)
 {
     static const char PROPERTY_NAME[] = "foo";
 
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -3,17 +3,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 "jsapi-tests/tests.h"
 
 static bool
-constructHook(JSContext* cx, unsigned argc, jsval* vp)
+constructHook(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = CallArgsFromVp(argc, vp);
 
     // Check that arguments were passed properly from JS_New.
 
     JS::RootedObject obj(cx, JS_NewPlainObject(cx));
     if (!obj) {
         JS_ReportError(cx, "test failed, could not construct object");
--- a/js/src/jsapi-tests/testObjectEmulatingUndefined.cpp
+++ b/js/src/jsapi-tests/testObjectEmulatingUndefined.cpp
@@ -5,17 +5,17 @@
 #include "jsapi-tests/tests.h"
 
 static const JSClass ObjectEmulatingUndefinedClass = {
     "ObjectEmulatingUndefined",
     JSCLASS_EMULATES_UNDEFINED
 };
 
 static bool
-ObjectEmulatingUndefinedConstructor(JSContext* cx, unsigned argc, jsval* vp)
+ObjectEmulatingUndefinedConstructor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     JSObject* obj = JS_NewObjectForConstructor(cx, &ObjectEmulatingUndefinedClass, args);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
--- a/js/src/jsapi-tests/testOps.cpp
+++ b/js/src/jsapi-tests/testOps.cpp
@@ -22,17 +22,17 @@ my_convert(JSContext* context, JS::Handl
 static const JSClass myClass = {
     "MyClass",
     0,
     nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, my_convert
 };
 
 static bool
-createMyObject(JSContext* context, unsigned argc, jsval* vp)
+createMyObject(JSContext* context, unsigned argc, JS::Value* vp)
 {
     JS_BeginRequest(context);
 
     //JS_GC(context); //<- if we make GC here, all is ok
 
     JSObject* myObject = JS_NewObject(context, &myClass);
     *vp = JS::ObjectOrNullValue(myObject);
 
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -322,17 +322,17 @@ ReportJSONError(JSContext* cx, const cha
         p->expectedErrorCount++;
     else
         p->unexpectedErrorCount++;
 }
 
 END_TEST(testParseJSON_error)
 
 static bool
-Censor(JSContext* cx, unsigned argc, jsval* vp)
+Censor(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     MOZ_RELEASE_ASSERT(args.length() == 2);
     MOZ_RELEASE_ASSERT(args[0].isString());
     args.rval().setNull();
     return true;
 }
 
--- a/js/src/jsapi-tests/testProfileStrings.cpp
+++ b/js/src/jsapi-tests/testProfileStrings.cpp
@@ -25,46 +25,46 @@ reset(JSContext* cx)
     js::EnableRuntimeProfilingStack(cx->runtime(), true);
 }
 
 static const JSClass ptestClass = {
     "Prof", 0
 };
 
 static bool
-test_fn(JSContext* cx, unsigned argc, jsval* vp)
+test_fn(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     max_stack = psize;
     return true;
 }
 
 static bool
-test_fn2(JSContext* cx, unsigned argc, jsval* vp)
+test_fn2(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::RootedValue r(cx);
     JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
     return JS_CallFunctionName(cx, global, "d", JS::HandleValueArray::empty(), &r);
 }
 
 static bool
-enable(JSContext* cx, unsigned argc, jsval* vp)
+enable(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     js::EnableRuntimeProfilingStack(cx->runtime(), true);
     return true;
 }
 
 static bool
-disable(JSContext* cx, unsigned argc, jsval* vp)
+disable(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     js::EnableRuntimeProfilingStack(cx->runtime(), false);
     return true;
 }
 
 static bool
-Prof(JSContext* cx, unsigned argc, jsval* vp)
+Prof(JSContext* cx, unsigned argc, JS::Value* vp)
 {
     JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
     JSObject* obj = JS_NewObjectForConstructor(cx, &ptestClass, args);
     if (!obj)
         return false;
     args.rval().setObject(*obj);
     return true;
 }
--- a/js/src/jsapi-tests/tests.h
+++ b/js/src/jsapi-tests/tests.h
@@ -177,17 +177,17 @@ class JSAPITest
     }
 
 #define CHECK_NULL(actual) \
     do { \
         if (!checkNull(actual, #actual, __FILE__, __LINE__)) \
             return false; \
     } while (false)
 
-    bool checkSame(jsval actualArg, jsval expectedArg,
+    bool checkSame(JS::Value actualArg, JS::Value expectedArg,
                    const char* actualExpr, const char* expectedExpr,
                    const char* filename, int lineno) {
         bool same;
         JS::RootedValue actual(cx, actualArg), expected(cx, expectedArg);
         return (JS_SameValue(cx, actual, expected, &same) && same) ||
                fail(JSAPITestString("CHECK_SAME failed: expected JS_SameValue(cx, ") +
                     actualExpr + ", " + expectedExpr + "), got !JS_SameValue(cx, " +
                     jsvalToSource(actual) + ", " + jsvalToSource(expected) + ")", filename, lineno);
@@ -233,17 +233,17 @@ class JSAPITest
             nullptr, nullptr, nullptr, nullptr,
             JS_GlobalObjectTraceHook
         };
         return &c;
     }
 
   protected:
     static bool
-    print(JSContext* cx, unsigned argc, jsval* vp)
+    print(JSContext* cx, unsigned argc, JS::Value* vp)
     {
         JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
 
         for (unsigned i = 0; i < args.length(); i++) {
             JSString* str = JS::ToString(cx, args[i]);
             if (!str)
                 return false;
             char* bytes = JS_EncodeString(cx, str);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -261,35 +261,35 @@ JS::ObjectOpResult::failNoIndexedSetter(
 }
 
 JS_PUBLIC_API(int64_t)
 JS_Now()
 {
     return PRMJ_Now();
 }
 
-JS_PUBLIC_API(jsval)
+JS_PUBLIC_API(Value)
 JS_GetNaNValue(JSContext* cx)
 {
     return cx->runtime()->NaNValue;
 }
 
-JS_PUBLIC_API(jsval)
+JS_PUBLIC_API(Value)
 JS_GetNegativeInfinityValue(JSContext* cx)
 {
     return cx->runtime()->negativeInfinityValue;
 }
 
-JS_PUBLIC_API(jsval)
+JS_PUBLIC_API(Value)
 JS_GetPositiveInfinityValue(JSContext* cx)
 {
     return cx->runtime()->positiveInfinityValue;
 }
 
-JS_PUBLIC_API(jsval)
+JS_PUBLIC_API(Value)
 JS_GetEmptyStringValue(JSContext* cx)
 {
     return StringValue(cx->runtime()->emptyString);
 }
 
 JS_PUBLIC_API(JSString*)
 JS_GetEmptyString(JSRuntime* rt)
 {
@@ -1430,18 +1430,18 @@ JS::CurrentGlobalOrNull(JSContext* cx)
 {
     AssertHeapIsIdleOrIterating(cx);
     CHECK_REQUEST(cx);
     if (!cx->compartment())
         return nullptr;
     return cx->global();
 }
 
-JS_PUBLIC_API(jsval)
-JS_ComputeThis(JSContext* cx, jsval* vp)
+JS_PUBLIC_API(Value)
+JS_ComputeThis(JSContext* cx, Value* vp)
 {
     AssertHeapIsIdle(cx);
     assertSameCompartment(cx, JSValueArray(vp, 2));
     CallReceiver call = CallReceiverFromVp(vp);
     if (!BoxNonStrictThis(cx, call))
         return NullValue();
     return call.thisv();
 }
@@ -3139,17 +3139,17 @@ JS_Enumerate(JSContext* cx, HandleObject
 
     AutoIdVector props(cx);
     JSIdArray* ida;
     if (!GetPropertyKeys(cx, obj, JSITER_OWNONLY, &props) || !VectorToIdArray(cx, props, &ida))
         return nullptr;
     return ida;
 }
 
-JS_PUBLIC_API(jsval)
+JS_PUBLIC_API(Value)
 JS_GetReservedSlot(JSObject* obj, uint32_t index)
 {
     return obj->as<NativeObject>().getReservedSlot(index);
 }
 
 JS_PUBLIC_API(void)
 JS_SetReservedSlot(JSObject* obj, uint32_t index, Value value)
 {
@@ -3523,17 +3523,17 @@ GenericNativeMethodDispatcher(JSContext*
     }
 
     /*
      * Copy all actual (argc) arguments down over our |this| parameter, vp[1],
      * which is almost always the class constructor object, e.g. Array.  Then
      * call the corresponding prototype native method with our first argument
      * passed as |this|.
      */
-    memmove(vp + 1, vp + 2, argc * sizeof(jsval));
+    memmove(vp + 1, vp + 2, argc * sizeof(Value));
 
     /* Clear the last parameter in case too few arguments were passed. */
     vp[2 + --argc].setUndefined();
 
     return fs->call.op(cx, argc, vp);
 }
 
 JS_PUBLIC_API(bool)
@@ -4318,29 +4318,29 @@ JS_DecompileFunctionBody(JSContext* cx, 
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, fun);
     return FunctionToString(cx, fun, true, !(indent & JS_DONT_PRETTY_PRINT));
 }
 
 MOZ_NEVER_INLINE static bool
-ExecuteScript(JSContext* cx, HandleObject scope, HandleScript script, jsval* rval)
+ExecuteScript(JSContext* cx, HandleObject scope, HandleScript script, Value* rval)
 {
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, scope, script);
     MOZ_ASSERT_IF(!scope->is<GlobalObject>(), script->hasNonSyntacticScope());
     AutoLastFrameCheck lfc(cx);
     return Execute(cx, script, *scope, rval);
 }
 
 static bool
-ExecuteScript(JSContext* cx, AutoObjectVector& scopeChain, HandleScript scriptArg, jsval* rval)
+ExecuteScript(JSContext* cx, AutoObjectVector& scopeChain, HandleScript scriptArg, Value* rval)
 {
     RootedObject dynamicScope(cx);
     Rooted<ScopeObject*> staticScope(cx);
     if (!CreateNonSyntacticScopeChain(cx, scopeChain, &dynamicScope, &staticScope))
         return false;
 
     RootedScript script(cx, scriptArg);
     if (!script->hasNonSyntacticScope() && !dynamicScope->is<GlobalObject>()) {
@@ -5808,17 +5808,17 @@ JS_ErrorFromException(JSContext* cx, Han
 JS_PUBLIC_API(bool)
 JS_ThrowStopIteration(JSContext* cx)
 {
     AssertHeapIsIdle(cx);
     return ThrowStopIteration(cx);
 }
 
 JS_PUBLIC_API(bool)
-JS_IsStopIteration(jsval v)
+JS_IsStopIteration(Value v)
 {
     return v.isObject() && v.toObject().is<StopIterationObject>();
 }
 
 JS_PUBLIC_API(intptr_t)
 JS_GetCurrentThread()
 {
     return reinterpret_cast<intptr_t>(PR_GetCurrentThread());
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -729,17 +729,17 @@ typedef void
  *
  * Returns an opaque key.
  */
 typedef void*
 (* JSCurrentPerfGroupCallback)(JSContext*);
 
 /************************************************************************/
 
-static MOZ_ALWAYS_INLINE jsval
+static MOZ_ALWAYS_INLINE JS::Value
 JS_NumberValue(double d)
 {
     int32_t i;
     d = JS::CanonicalizeNaN(d);
     if (mozilla::NumberIsInt32(d, &i))
         return JS::Int32Value(i);
     return JS::DoubleValue(d);
 }
@@ -931,27 +931,27 @@ class MOZ_STACK_CLASS SourceBufferHolder
  */
 extern JS_PUBLIC_API(bool)
 JS_CallOnce(JSCallOnceType* once, JSInitCallback func);
 
 /* Microseconds since the epoch, midnight, January 1, 1970 UTC. */
 extern JS_PUBLIC_API(int64_t)
 JS_Now(void);
 
-/* Don't want to export data, so provide accessors for non-inline jsvals. */
-extern JS_PUBLIC_API(jsval)
+/* Don't want to export data, so provide accessors for non-inline Values. */
+extern JS_PUBLIC_API(JS::Value)
 JS_GetNaNValue(JSContext* cx);
 
-extern JS_PUBLIC_API(jsval)
+extern JS_PUBLIC_API(JS::Value)
 JS_GetNegativeInfinityValue(JSContext* cx);
 
-extern JS_PUBLIC_API(jsval)
+extern JS_PUBLIC_API(JS::Value)
 JS_GetPositiveInfinityValue(JSContext* cx);
 
-extern JS_PUBLIC_API(jsval)
+extern JS_PUBLIC_API(JS::Value)
 JS_GetEmptyStringValue(JSContext* cx);
 
 extern JS_PUBLIC_API(JSString*)
 JS_GetEmptyString(JSRuntime* rt);
 
 extern JS_PUBLIC_API(bool)
 JS_ValueToObject(JSContext* cx, JS::HandleValue v, JS::MutableHandleObject objp);
 
@@ -3187,21 +3187,21 @@ JS_CreateMappedArrayBufferContents(int f
  * release the resource used by the object.
  */
 extern JS_PUBLIC_API(void)
 JS_ReleaseMappedArrayBufferContents(void* contents, size_t length);
 
 extern JS_PUBLIC_API(JSIdArray*)
 JS_Enumerate(JSContext* cx, JS::HandleObject obj);
 
-extern JS_PUBLIC_API(jsval)
+extern JS_PUBLIC_API(JS::Value)
 JS_GetReservedSlot(JSObject* obj, uint32_t index);
 
 extern JS_PUBLIC_API(void)
-JS_SetReservedSlot(JSObject* obj, uint32_t index, jsval v);
+JS_SetReservedSlot(JSObject* obj, uint32_t index, JS::Value v);
 
 /************************************************************************/
 
 /*
  * Functions and scripts.
  */
 extern JS_PUBLIC_API(JSFunction*)
 JS_NewFunction(JSContext* cx, JSNative call, unsigned nargs, unsigned flags,
@@ -4921,17 +4921,17 @@ ExceptionStackOrNull(JSContext* cx, JS::
 
 /*
  * Throws a StopIteration exception on cx.
  */
 extern JS_PUBLIC_API(bool)
 JS_ThrowStopIteration(JSContext* cx);
 
 extern JS_PUBLIC_API(bool)
-JS_IsStopIteration(jsval v);
+JS_IsStopIteration(JS::Value v);
 
 extern JS_PUBLIC_API(intptr_t)
 JS_GetCurrentThread();
 
 /*
  * A JS runtime always has an "owner thread". The owner thread is set when the
  * runtime is created (to the current thread) and practically all entry points
  * into the JS engine check that a runtime (or anything contained in the
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -1301,20 +1301,22 @@ class MOZ_STACK_CLASS AutoStableStringCh
     bool ownsChars_;
 
   public:
     explicit AutoStableStringChars(JSContext* cx)
       : s_(cx), state_(Uninitialized), ownsChars_(false)
     {}
     ~AutoStableStringChars();
 
-    bool init(JSContext* cx, JSString* s) MOZ_WARN_UNUSED_RESULT;
+    MOZ_WARN_UNUSED_RESULT
+    bool init(JSContext* cx, JSString* s);
 
     /* Like init(), but Latin1 chars are inflated to TwoByte. */
-    bool initTwoByte(JSContext* cx, JSString* s) MOZ_WARN_UNUSED_RESULT;
+    MOZ_WARN_UNUSED_RESULT
+    bool initTwoByte(JSContext* cx, JSString* s);
 
     bool isLatin1() const { return state_ == Latin1; }
     bool isTwoByte() const { return state_ == TwoByte; }
 
     const char16_t* twoByteChars() const {
         MOZ_ASSERT(state_ == TwoByte);
         return twoByteChars_;
     }
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -664,20 +664,20 @@ IsConstructor(const Value& v)
 {
     return v.isObject() && v.toObject().isConstructor();
 }
 
 } /* namespace js */
 
 class JSValueArray {
   public:
-    const jsval* array;
+    const js::Value* array;
     size_t length;
 
-    JSValueArray(const jsval* v, size_t c) : array(v), length(c) {}
+    JSValueArray(const js::Value* v, size_t c) : array(v), length(c) {}
 };
 
 class ValueArray {
   public:
     js::Value* array;
     size_t length;
 
     ValueArray(js::Value* v, size_t c) : array(v), length(c) {}
--- a/js/src/jspubtd.h
+++ b/js/src/jspubtd.h
@@ -320,16 +320,17 @@ enum ThingRootKind
     THING_ROOT_OBJECT_GROUP,
     THING_ROOT_STRING,
     THING_ROOT_SYMBOL,
     THING_ROOT_JIT_CODE,
     THING_ROOT_SCRIPT,
     THING_ROOT_LAZY_SCRIPT,
     THING_ROOT_ID,
     THING_ROOT_VALUE,
+    THING_ROOT_BINDINGS,
     THING_ROOT_PROPERTY_DESCRIPTOR,
     THING_ROOT_PROP_DESC,
     THING_ROOT_STATIC_TRACEABLE,
     THING_ROOT_LIMIT
 };
 
 template <typename T>
 struct RootKind;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -56,56 +56,55 @@ using namespace js;
 using namespace js::gc;
 using namespace js::frontend;
 
 using mozilla::PodCopy;
 using mozilla::PodZero;
 using mozilla::RotateLeft;
 
 /* static */ BindingIter
-Bindings::argumentsBinding(ExclusiveContext* cx, HandleScript script)
+Bindings::argumentsBinding(ExclusiveContext* cx, InternalBindingsHandle bindings)
 {
     HandlePropertyName arguments = cx->names().arguments;
-    BindingIter bi(script);
+    BindingIter bi(bindings);
     while (bi->name() != arguments)
         bi++;
     return bi;
 }
 
 bool
-Bindings::initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle<Bindings> self,
+Bindings::initWithTemporaryStorage(ExclusiveContext* cx, InternalBindingsHandle self,
                                    uint32_t numArgs, uint32_t numVars,
                                    uint32_t numBodyLevelLexicals, uint32_t numBlockScoped,
                                    uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals,
-                                   const Binding* bindingArray)
+                                   Binding* bindingArray)
 {
-    MOZ_ASSERT(!self.callObjShape());
-    MOZ_ASSERT(self.bindingArrayUsingTemporaryStorage());
-    MOZ_ASSERT(!self.bindingArray());
+    MOZ_ASSERT(!self->callObjShape_);
+    MOZ_ASSERT(self->bindingArrayAndFlag_ == TEMPORARY_STORAGE_BIT);
     MOZ_ASSERT(!(uintptr_t(bindingArray) & TEMPORARY_STORAGE_BIT));
     MOZ_ASSERT(numArgs <= ARGC_LIMIT);
     MOZ_ASSERT(numVars <= LOCALNO_LIMIT);
     MOZ_ASSERT(numBlockScoped <= LOCALNO_LIMIT);
     MOZ_ASSERT(numBodyLevelLexicals <= LOCALNO_LIMIT);
     mozilla::DebugOnly<uint64_t> totalSlots = uint64_t(numVars) +
                                               uint64_t(numBodyLevelLexicals) +
                                               uint64_t(numBlockScoped);
     MOZ_ASSERT(totalSlots <= LOCALNO_LIMIT);
     MOZ_ASSERT(UINT32_MAX - numArgs >= totalSlots);
 
     MOZ_ASSERT(numUnaliasedVars <= numVars);
     MOZ_ASSERT(numUnaliasedBodyLevelLexicals <= numBodyLevelLexicals);
 
-    self.setBindingArray(bindingArray, TEMPORARY_STORAGE_BIT);
-    self.setNumArgs(numArgs);
-    self.setNumVars(numVars);
-    self.setNumBodyLevelLexicals(numBodyLevelLexicals);
-    self.setNumBlockScoped(numBlockScoped);
-    self.setNumUnaliasedVars(numUnaliasedVars);
-    self.setNumUnaliasedBodyLevelLexicals(numUnaliasedBodyLevelLexicals);
+    self->bindingArrayAndFlag_ = uintptr_t(bindingArray) | TEMPORARY_STORAGE_BIT;
+    self->numArgs_ = numArgs;
+    self->numVars_ = numVars;
+    self->numBodyLevelLexicals_ = numBodyLevelLexicals;
+    self->numBlockScoped_ = numBlockScoped;
+    self->numUnaliasedVars_ = numUnaliasedVars;
+    self->numUnaliasedBodyLevelLexicals_ = numUnaliasedBodyLevelLexicals;
 
     // Get the initial shape to use when creating CallObjects for this script.
     // After creation, a CallObject's shape may change completely (via direct eval() or
     // other operations that mutate the lexical scope). However, since the
     // lexical bindings added to the initial shape are permanent and the
     // allocKind/nfixed of a CallObject cannot change, one may assume that the
     // slot location (whether in the fixed or dynamic slots) of a variable is
     // the same as in the initial shape. (This is assumed by the interpreter and
@@ -130,17 +129,17 @@ Bindings::initWithTemporaryStorage(Exclu
                 bi.localIndex() >= numVars)
             {
                 aliasedBodyLevelLexicalBegin = nslots;
             }
 
             nslots++;
         }
     }
-    self.setAliasedBodyLevelLexicalBegin(aliasedBodyLevelLexicalBegin);
+    self->aliasedBodyLevelLexicalBegin_ = aliasedBodyLevelLexicalBegin;
 
     // Put as many of nslots inline into the object header as possible.
     uint32_t nfixed = gc::GetGCKindSlots(gc::GetGCObjectKind(nslots));
 
     // Start with the empty shape and then append one shape per aliased binding.
     RootedShape shape(cx,
         EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr),
                                     nfixed, BaseShape::QUALIFIED_VAROBJ | BaseShape::DELEGATE));
@@ -186,17 +185,17 @@ Bindings::initWithTemporaryStorage(Exclu
             return false;
 
         MOZ_ASSERT(slot < nslots);
         slot++;
     }
     MOZ_ASSERT(slot == nslots);
 
     MOZ_ASSERT(!shape->inDictionary());
-    self.setCallObjShape(shape);
+    self->callObjShape_.init(shape);
     return true;
 }
 
 bool
 Bindings::initTrivial(ExclusiveContext* cx)
 {
     Shape* shape = EmptyShape::getInitialShape(cx, &CallObject::class_, TaggedProto(nullptr),
                                                CallObject::RESERVED_SLOTS,
@@ -214,22 +213,22 @@ Bindings::switchToScriptStorage(Binding*
     MOZ_ASSERT(!(uintptr_t(newBindingArray) & TEMPORARY_STORAGE_BIT));
 
     if (count() > 0)
         PodCopy(newBindingArray, bindingArray(), count());
     bindingArrayAndFlag_ = uintptr_t(newBindingArray);
     return reinterpret_cast<uint8_t*>(newBindingArray + count());
 }
 
-/* static */ bool
-Bindings::clone(JSContext* cx, MutableHandle<Bindings> self,
+bool
+Bindings::clone(JSContext* cx, InternalBindingsHandle self,
                 uint8_t* dstScriptData, HandleScript srcScript)
 {
     /* The clone has the same bindingArray_ offset as 'src'. */
-    Handle<Bindings> src = Handle<Bindings>::fromMarkedLocation(&srcScript->bindings);
+    Bindings& src = srcScript->bindings;
     ptrdiff_t off = (uint8_t*)src.bindingArray() - srcScript->data;
     MOZ_ASSERT(off >= 0);
     MOZ_ASSERT(size_t(off) <= srcScript->dataSize());
     Binding* dstPackedBindings = (Binding*)(dstScriptData + off);
 
     /*
      * Since atoms are shareable throughout the runtime, we can simply copy
      * the source's bindingArray directly.
@@ -239,20 +238,26 @@ Bindings::clone(JSContext* cx, MutableHa
                                   src.numBlockScoped(),
                                   src.numUnaliasedVars(),
                                   src.numUnaliasedBodyLevelLexicals(),
                                   src.bindingArray()))
     {
         return false;
     }
 
-    self.switchToScriptStorage(dstPackedBindings);
+    self->switchToScriptStorage(dstPackedBindings);
     return true;
 }
 
+/* static */ Bindings
+GCMethods<Bindings>::initial()
+{
+    return Bindings();
+}
+
 template<XDRMode mode>
 static bool
 XDRScriptBindings(XDRState<mode>* xdr, LifoAllocScope& las, uint16_t numArgs, uint32_t numVars,
                   uint16_t numBodyLevelLexicals, uint16_t numBlockScoped,
                   uint32_t numUnaliasedVars, uint16_t numUnaliasedBodyLevelLexicals,
                   HandleScript script)
 {
     JSContext* cx = xdr->cx();
@@ -292,25 +297,24 @@ XDRScriptBindings(XDRState<mode>* xdr, L
 
             PropertyName* name = atoms[i].toString()->asAtom().asPropertyName();
             Binding::Kind kind = Binding::Kind(u8 >> 1);
             bool aliased = bool(u8 & 1);
 
             bindingArray[i] = Binding(name, kind, aliased);
         }
 
-        Rooted<Bindings> bindings(cx, script->bindings);
-        if (!Bindings::initWithTemporaryStorage(cx, &bindings, numArgs, numVars,
+        InternalBindingsHandle bindings(script, &script->bindings);
+        if (!Bindings::initWithTemporaryStorage(cx, bindings, numArgs, numVars,
                                                 numBodyLevelLexicals, numBlockScoped,
                                                 numUnaliasedVars, numUnaliasedBodyLevelLexicals,
                                                 bindingArray))
         {
             return false;
         }
-        script->bindings = bindings;
     }
 
     return true;
 }
 
 bool
 Bindings::bindingIsAliased(uint32_t bindingIndex)
 {
@@ -2355,17 +2359,17 @@ js::FreeScriptData(JSRuntime* rt)
  * Consts           consts()->vector      consts()->length
  * Objects          objects()->vector     objects()->length
  * Regexps          regexps()->vector     regexps()->length
  * Try notes        trynotes()->vector    trynotes()->length
  * Scope notes      blockScopes()->vector blockScopes()->length
  *
  * IMPORTANT: This layout has two key properties.
  * - It ensures that everything has sufficient alignment; in particular, the
- *   consts() elements need jsval alignment.
+ *   consts() elements need Value alignment.
  * - It ensures there are no gaps between elements, which saves space and makes
  *   manual layout easy. In particular, in the second part, arrays with larger
  *   elements precede arrays with smaller elements.
  *
  * SharedScriptData::data contains data that can be shared within a
  * runtime. These items' layout is manually controlled to make it easier to
  * manage both during (temporary) allocation and during matching against
  * existing entries in the runtime. As the jsbytecode has to come first to
@@ -2378,30 +2382,30 @@ js::FreeScriptData(JSRuntime* rt)
  * jsbytecode       code                  length
  * jsscrnote        notes()               numNotes()
  * Atoms            atoms                 natoms
  *
  * The following static assertions check JSScript::data's alignment properties.
  */
 
 #define KEEPS_JSVAL_ALIGNMENT(T) \
-    (JS_ALIGNMENT_OF(jsval) % JS_ALIGNMENT_OF(T) == 0 && \
-     sizeof(T) % sizeof(jsval) == 0)
+    (JS_ALIGNMENT_OF(JS::Value) % JS_ALIGNMENT_OF(T) == 0 && \
+     sizeof(T) % sizeof(JS::Value) == 0)
 
 #define HAS_JSVAL_ALIGNMENT(T) \
-    (JS_ALIGNMENT_OF(jsval) == JS_ALIGNMENT_OF(T) && \
-     sizeof(T) == sizeof(jsval))
+    (JS_ALIGNMENT_OF(JS::Value) == JS_ALIGNMENT_OF(T) && \
+     sizeof(T) == sizeof(JS::Value))
 
 #define NO_PADDING_BETWEEN_ENTRIES(T1, T2) \
     (JS_ALIGNMENT_OF(T1) % JS_ALIGNMENT_OF(T2) == 0)
 
 /*
  * These assertions ensure that there is no padding between the array headers,
  * and also that the consts() elements (which follow immediately afterward) are
- * jsval-aligned.  (There is an assumption that |data| itself is jsval-aligned;
+ * Value-aligned.  (There is an assumption that |data| itself is Value-aligned;
  * we check this below).
  */
 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ConstArray));
 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(ObjectArray));       /* there are two of these */
 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(TryNoteArray));
 JS_STATIC_ASSERT(KEEPS_JSVAL_ALIGNMENT(BlockScopeArray));
 
 /* These assertions ensure there is no padding required between array elements. */
@@ -2556,17 +2560,17 @@ JSScript::partiallyInit(ExclusiveContext
 
     YieldOffsetArray* yieldOffsets = nullptr;
     if (nyieldoffsets != 0) {
         yieldOffsets = reinterpret_cast<YieldOffsetArray*>(cursor);
         cursor += sizeof(YieldOffsetArray);
     }
 
     if (nconsts != 0) {
-        MOZ_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(jsval) == 0);
+        MOZ_ASSERT(reinterpret_cast<uintptr_t>(cursor) % sizeof(JS::Value) == 0);
         script->consts()->length = nconsts;
         script->consts()->vector = (HeapValue*)cursor;
         cursor += nconsts * sizeof(script->consts()->vector[0]);
     }
 
     if (nobjects != 0) {
         script->objects()->length = nobjects;
         script->objects()->vector = (HeapPtrObject*)cursor;
@@ -3116,17 +3120,19 @@ js::detail::CopyScript(JSContext* cx, Ha
     size_t size = src->dataSize();
     uint8_t* data = AllocScriptData(cx->zone(), size);
     if (size && !data)
         return false;
 
     /* Bindings */
 
     Rooted<Bindings> bindings(cx);
-    if (!Bindings::clone(cx, &bindings, data, src))
+    InternalHandle<Bindings*> bindingsHandle =
+        InternalHandle<Bindings*>::fromMarkedLocation(bindings.address());
+    if (!Bindings::clone(cx, bindingsHandle, data, src))
         return false;
 
     /* Objects */
 
     AutoObjectVector objects(cx);
     if (nobjects != 0) {
         HeapPtrObject* vector = src->objects()->vector;
         for (unsigned i = 0; i < nobjects; i++) {
@@ -3701,17 +3707,18 @@ void
 js::SetFrameArgumentsObject(JSContext* cx, AbstractFramePtr frame,
                             HandleScript script, JSObject* argsobj)
 {
     /*
      * Replace any optimized arguments in the frame with an explicit arguments
      * object. Note that 'arguments' may have already been overwritten.
      */
 
-    BindingIter bi = Bindings::argumentsBinding(cx, script);
+    InternalBindingsHandle bindings(script, &script->bindings);
+    BindingIter bi = Bindings::argumentsBinding(cx, bindings);
 
     if (script->bindingIsAliased(bi)) {
         /*
          * Scan the script to find the slot in the call object that 'arguments'
          * is assigned to.
          */
         jsbytecode* pc = script->code();
         while (*pc != JSOP_ARGUMENTS)
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -195,28 +195,29 @@ class Binding
 
     bool aliased() const {
         return bool(bits_ & ALIASED_BIT);
     }
 };
 
 JS_STATIC_ASSERT(sizeof(Binding) == sizeof(uintptr_t));
 
+class Bindings;
+typedef InternalHandle<Bindings*> InternalBindingsHandle;
+
 /*
  * Formal parameters and local variables are stored in a shape tree
  * path encapsulated within this class.  This class represents bindings for
  * both function and top-level scripts (the latter is needed to track names in
  * strict mode eval code, to give such code its own lexical environment).
  */
-class Bindings : public JS::StaticTraceable
+class Bindings
 {
     friend class BindingIter;
     friend class AliasedFormalIter;
-    template <typename Outer> friend class BindingsOperations;
-    template <typename Outer> friend class MutableBindingsOperations;
 
     RelocatablePtrShape callObjShape_;
     uintptr_t bindingArrayAndFlag_;
     uint16_t numArgs_;
     uint16_t numBlockScoped_;
     uint16_t numBodyLevelLexicals_;
     uint16_t aliasedBodyLevelLexicalBegin_;
     uint16_t numUnaliasedBodyLevelLexicals_;
@@ -244,38 +245,30 @@ class Bindings : public JS::StaticTracea
     }
 
   public:
 
     Binding* bindingArray() const {
         return reinterpret_cast<Binding*>(bindingArrayAndFlag_ & ~TEMPORARY_STORAGE_BIT);
     }
 
-    Bindings()
-      : callObjShape_(nullptr), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT),
-        numArgs_(0), numBlockScoped_(0),
-        numBodyLevelLexicals_(0), numUnaliasedBodyLevelLexicals_(0),
-        numVars_(0), numUnaliasedVars_(0)
-    {}
+    inline Bindings();
 
     /*
      * Initialize a Bindings with a pointer into temporary storage.
      * bindingArray must have length numArgs + numVars +
      * numBodyLevelLexicals. Before the temporary storage is release,
      * switchToScriptStorage must be called, providing a pointer into the
      * Binding array stored in script->data.
      */
-    static bool initWithTemporaryStorage(ExclusiveContext* cx, MutableHandle<Bindings> self,
-                                         uint32_t numArgs,
-                                         uint32_t numVars,
-                                         uint32_t numBodyLevelLexicals,
-                                         uint32_t numBlockScoped,
-                                         uint32_t numUnaliasedVars,
-                                         uint32_t numUnaliasedBodyLevelLexicals,
-                                         const Binding* bindingArray);
+    static bool initWithTemporaryStorage(ExclusiveContext* cx, InternalBindingsHandle self,
+                                         uint32_t numArgs, uint32_t numVars,
+                                         uint32_t numBodyLevelLexicals, uint32_t numBlockScoped,
+                                         uint32_t numUnaliasedVars, uint32_t numUnaliasedBodyLevelLexicals,
+                                         Binding* bindingArray);
 
     // Initialize a trivial Bindings with no slots and an empty callObjShape.
     bool initTrivial(ExclusiveContext* cx);
 
     // CompileScript parses and compiles one statement at a time, but the result
     // is one Script object.  There will be no vars or bindings, because those
     // go on the global, but there may be block-scoped locals, and the number of
     // block-scoped locals may increase as we parse more expressions.  This
@@ -294,17 +287,17 @@ class Bindings : public JS::StaticTracea
     }
 
     uint8_t* switchToScriptStorage(Binding* newStorage);
 
     /*
      * Clone srcScript's bindings (as part of js::CloneScript). dstScriptData
      * is the pointer to what will eventually be dstScript->data.
      */
-    static bool clone(JSContext* cx, MutableHandle<Bindings> self, uint8_t* dstScriptData,
+    static bool clone(JSContext* cx, InternalBindingsHandle self, uint8_t* dstScriptData,
                       HandleScript srcScript);
 
     uint32_t numArgs() const { return numArgs_; }
     uint32_t numVars() const { return numVars_; }
     uint32_t numBodyLevelLexicals() const { return numBodyLevelLexicals_; }
     uint32_t numBlockScoped() const { return numBlockScoped_; }
     uint32_t numBodyLevelLocals() const { return numVars_ + numBodyLevelLexicals_; }
     uint32_t numUnaliasedBodyLevelLocals() const { return numUnaliasedVars_ + numUnaliasedBodyLevelLexicals_; }
@@ -319,148 +312,39 @@ class Bindings : public JS::StaticTracea
 
     // Return the size of the bindingArray.
     uint32_t count() const { return numArgs() + numVars() + numBodyLevelLexicals(); }
 
     /* Return the initial shape of call objects created for this scope. */
     Shape* callObjShape() const { return callObjShape_; }
 
     /* Convenience method to get the var index of 'arguments'. */
-    static BindingIter argumentsBinding(ExclusiveContext* cx, HandleScript script);
+    static BindingIter argumentsBinding(ExclusiveContext* cx, InternalBindingsHandle);
 
     /* Return whether the binding at bindingIndex is aliased. */
     bool bindingIsAliased(uint32_t bindingIndex);
 
     /* Return whether this scope has any aliased bindings. */
     bool hasAnyAliasedBindings() const {
         if (!callObjShape_)
             return false;
 
         return !callObjShape_->isEmptyShape();
     }
 
     Binding* begin() const { return bindingArray(); }
     Binding* end() const { return bindingArray() + count(); }
 
-    static void trace(Bindings* self, JSTracer* trc) { self->trace(trc); }
+    static js::ThingRootKind rootKind() { return js::THING_ROOT_BINDINGS; }
     void trace(JSTracer* trc);
 };
 
-template <class Outer>
-class BindingsOperations
-{
-    const Bindings& bindings() const { return static_cast<const Outer*>(this)->extract(); }
-
-  public:
-    // Direct data access to the underlying bindings.
-    const RelocatablePtrShape& callObjShape() const {
-        return bindings().callObjShape_;
-    }
-    uint16_t numArgs() const {
-        return bindings().numArgs_;
-    }
-    uint16_t numBlockScoped() const {
-        return bindings().numBlockScoped_;
-    }
-    uint16_t numBodyLevelLexicals() const {
-        return bindings().numBodyLevelLexicals_;
-    }
-    uint16_t aliasedBodyLevelLexicalBegin() const {
-        return bindings().aliasedBodyLevelLexicalBegin_;
-    }
-    uint16_t numUnaliasedBodyLevelLexicals() const {
-        return bindings().numUnaliasedBodyLevelLexicals_;
-    }
-    uint32_t numVars() const {
-        return bindings().numVars_;
-    }
-    uint32_t numUnaliasedVars() const {
-        return bindings().numUnaliasedVars_;
-    }
-
-    // Binding array access.
-    bool bindingArrayUsingTemporaryStorage() const {
-        return bindings().bindingArrayUsingTemporaryStorage();
-    }
-    const Binding* bindingArray() const {
-        return bindings().bindingArray();
-    }
-    uint32_t count() const {
-        return bindings().count();
-    }
-
-    // Helpers.
-    uint32_t numBodyLevelLocals() const {
-        return numVars() + numBodyLevelLexicals();
-    }
-    uint32_t numUnaliasedBodyLevelLocals() const {
-        return numUnaliasedVars() + numUnaliasedBodyLevelLexicals();
-    }
-    uint32_t numAliasedBodyLevelLocals() const {
-        return numBodyLevelLocals() - numUnaliasedBodyLevelLocals();
-    }
-    uint32_t numLocals() const {
-        return numVars() + numBodyLevelLexicals() + numBlockScoped();
-    }
-    uint32_t numFixedLocals() const {
-        return numUnaliasedVars() + numUnaliasedBodyLevelLexicals() + numBlockScoped();
-    }
-    uint32_t lexicalBegin() const {
-        return numArgs() + numVars();
-    }
-};
-
-template <class Outer>
-class MutableBindingsOperations : public BindingsOperations<Outer>
-{
-    Bindings& bindings() { return static_cast<Outer*>(this)->extractMutable(); }
-
-  public:
-    void setCallObjShape(HandleShape shape) { bindings().callObjShape_ = shape; }
-    void setBindingArray(const Binding* bindingArray, uintptr_t temporaryBit) {
-        bindings().bindingArrayAndFlag_ = uintptr_t(bindingArray) | temporaryBit;
-    }
-    void setNumArgs(uint16_t num) { bindings().numArgs_ = num; }
-    void setNumVars(uint32_t num) { bindings().numVars_ = num; }
-    void setNumBodyLevelLexicals(uint16_t num) { bindings().numBodyLevelLexicals_ = num; }
-    void setNumBlockScoped(uint16_t num) { bindings().numBlockScoped_ = num; }
-    void setNumUnaliasedVars(uint32_t num) { bindings().numUnaliasedVars_ = num; }
-    void setNumUnaliasedBodyLevelLexicals(uint16_t num) {
-        bindings().numUnaliasedBodyLevelLexicals_ = num;
-    }
-    void setAliasedBodyLevelLexicalBegin(uint16_t offset) {
-        bindings().aliasedBodyLevelLexicalBegin_ = offset;
-    }
-    uint8_t* switchToScriptStorage(Binding* permanentStorage) {
-        return bindings().switchToScriptStorage(permanentStorage);
-    }
-};
-
 template <>
-class HandleBase<Bindings> : public BindingsOperations<JS::Handle<Bindings>>
-{
-    friend class BindingsOperations<JS::Handle<Bindings>>;
-    const Bindings& extract() const {
-        return static_cast<const JS::Handle<Bindings>*>(this)->get();
-    }
-};
-
-template <>
-class MutableHandleBase<Bindings>
-  : public MutableBindingsOperations<JS::MutableHandle<Bindings>>
-{
-    friend class BindingsOperations<JS::MutableHandle<Bindings>>;
-    const Bindings& extract() const {
-        return static_cast<const JS::MutableHandle<Bindings>*>(this)->get();
-    }
-
-    friend class MutableBindingsOperations<JS::MutableHandle<Bindings>>;
-    Bindings& extractMutable() {
-        return static_cast<JS::MutableHandle<Bindings>*>(this)->get();
-    }
+struct GCMethods<Bindings> {
+    static Bindings initial();
 };
 
 class ScriptCounts
 {
     friend class ::JSScript;
     friend struct ScriptAndCounts;
 
     /*
@@ -1893,34 +1777,30 @@ namespace js {
 /*
  * Iterator over a script's bindings (formals and variables).
  * The order of iteration is:
  *  - first, formal arguments, from index 0 to numArgs
  *  - next, variables, from index 0 to numLocals
  */
 class BindingIter
 {
-    Handle<Bindings> bindings_;
+    const InternalBindingsHandle bindings_;
     uint32_t i_;
     uint32_t unaliasedLocal_;
 
     friend class ::JSScript;
     friend class Bindings;
 
   public:
-    explicit BindingIter(Handle<Bindings> bindings)
-      : bindings_(bindings), i_(0), unaliasedLocal_(0)
-    {}
-
+    explicit BindingIter(const InternalBindingsHandle& bindings)
+      : bindings_(bindings), i_(0), unaliasedLocal_(0) {}
     explicit BindingIter(const HandleScript& script)
-      : bindings_(Handle<Bindings>::fromMarkedLocation(&script->bindings)),
-        i_(0), unaliasedLocal_(0)
-    {}
+      : bindings_(script, &script->bindings), i_(0), unaliasedLocal_(0) {}
 
-    bool done() const { return i_ == bindings_.count(); }
+    bool done() const { return i_ == bindings_->count(); }
     explicit operator bool() const { return !done(); }
     BindingIter& operator++() { (*this)++; return *this; }
 
     void operator++(int) {
         MOZ_ASSERT(!done());
         const Binding& binding = **this;
         if (binding.kind() != Binding::ARGUMENT && !binding.aliased())
             unaliasedLocal_++;
@@ -1928,47 +1808,47 @@ class BindingIter
     }
 
     // Stack slots are assigned to arguments (aliased and unaliased) and
     // unaliased locals. frameIndex() returns the slot index. It's invalid to
     // call this method when the iterator is stopped on an aliased local, as it
     // has no stack slot.
     uint32_t frameIndex() const {
         MOZ_ASSERT(!done());
-        if (i_ < bindings_.numArgs())
+        if (i_ < bindings_->numArgs())
             return i_;
         MOZ_ASSERT(!(*this)->aliased());
         return unaliasedLocal_;
     }
 
     // If the current binding is an argument, argIndex() returns its index.
     // It returns the same value as frameIndex(), as slots are allocated for
     // both unaliased and aliased arguments.
     uint32_t argIndex() const {
         MOZ_ASSERT(!done());
-        MOZ_ASSERT(i_ < bindings_.numArgs());
+        MOZ_ASSERT(i_ < bindings_->numArgs());
         return i_;
     }
     uint32_t argOrLocalIndex() const {
         MOZ_ASSERT(!done());
-        return i_ < bindings_.numArgs() ? i_ : i_ - bindings_.numArgs();
+        return i_ < bindings_->numArgs() ? i_ : i_ - bindings_->numArgs();
     }
     uint32_t localIndex() const {
         MOZ_ASSERT(!done());
-        MOZ_ASSERT(i_ >= bindings_.numArgs());
-        return i_ - bindings_.numArgs();
+        MOZ_ASSERT(i_ >= bindings_->numArgs());
+        return i_ - bindings_->numArgs();
     }
     bool isBodyLevelLexical() const {
         MOZ_ASSERT(!done());
         const Binding& binding = **this;
         return binding.kind() != Binding::ARGUMENT;
     }
 
-    const Binding& operator*() const { MOZ_ASSERT(!done()); return bindings_.bindingArray()[i_]; }
-    const Binding* operator->() const { MOZ_ASSERT(!done()); return &bindings_.bindingArray()[i_]; }
+    const Binding& operator*() const { MOZ_ASSERT(!done()); return bindings_->bindingArray()[i_]; }
+    const Binding* operator->() const { MOZ_ASSERT(!done()); return &bindings_->bindingArray()[i_]; }
 };
 
 /*
  * Iterator over the aliased formal bindings in ascending index order. This can
  * be veiwed as a filtering of BindingIter with predicate
  *   bi->aliased() && bi->kind() == Binding::ARGUMENT
  */
 class AliasedFormalIter
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -16,16 +16,24 @@
 
 #include "jscompartmentinlines.h"
 
 #include "vm/Shape-inl.h"
 
 namespace js {
 
 inline
+Bindings::Bindings()
+    : callObjShape_(nullptr), bindingArrayAndFlag_(TEMPORARY_STORAGE_BIT),
+      numArgs_(0), numBlockScoped_(0),
+      numBodyLevelLexicals_(0), numUnaliasedBodyLevelLexicals_(0),
+      numVars_(0), numUnaliasedVars_(0)
+{}
+
+inline
 AliasedFormalIter::AliasedFormalIter(JSScript* script)
   : begin_(script->bindingArray()),
     p_(begin_),
     end_(begin_ + (script->funHasAnyAliasedFormal() ? script->numArgs() : 0)),
     slot_(CallObject::RESERVED_SLOTS)
 {
     settle();
 }
--- a/js/src/perf/jsperf.cpp
+++ b/js/src/perf/jsperf.cpp
@@ -44,56 +44,56 @@ GETTER(context_switches)
 GETTER(cpu_migrations)
 GETTER(eventsMeasured)
 
 #undef GETTER
 
 // Calls
 
 static bool
-pm_start(JSContext* cx, unsigned argc, jsval* vp)
+pm_start(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     PerfMeasurement* p = GetPM(cx, args.thisv(), "start");
     if (!p)
         return false;
 
     p->start();
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-pm_stop(JSContext* cx, unsigned argc, jsval* vp)
+pm_stop(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     PerfMeasurement* p = GetPM(cx, args.thisv(), "stop");
     if (!p)
         return false;
 
     p->stop();
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-pm_reset(JSContext* cx, unsigned argc, jsval* vp)
+pm_reset(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     PerfMeasurement* p = GetPM(cx, args.thisv(), "reset");
     if (!p)
         return false;
 
     p->reset();
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-pm_canMeasureSomething(JSContext* cx, unsigned argc, jsval* vp)
+pm_canMeasureSomething(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     PerfMeasurement* p = GetPM(cx, args.thisv(), "canMeasureSomething");
     if (!p)
         return false;
 
     args.rval().setBoolean(p->canMeasureSomething());
     return true;
@@ -153,29 +153,29 @@ static const struct pm_const {
     CONSTANT(CPU_MIGRATIONS),
     CONSTANT(ALL),
     CONSTANT(NUM_MEASURABLE_EVENTS),
     { 0, PerfMeasurement::EventMask(0) }
 };
 
 #undef CONSTANT
 
-static bool pm_construct(JSContext* cx, unsigned argc, jsval* vp);
+static bool pm_construct(JSContext* cx, unsigned argc, Value* vp);
 static void pm_finalize(JSFreeOp* fop, JSObject* obj);
 
 static const JSClass pm_class = {
     "PerfMeasurement", JSCLASS_HAS_PRIVATE,
     nullptr, nullptr, nullptr, nullptr, nullptr,
     nullptr, nullptr, nullptr, pm_finalize
 };
 
 // Constructor and destructor
 
 static bool
-pm_construct(JSContext* cx, unsigned argc, jsval* vp)
+pm_construct(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     uint32_t mask;
     if (!args.hasDefined(0)) {
         ReportMissingArg(cx, args.calleev(), 0);
         return false;
     }
@@ -262,17 +262,17 @@ RegisterPerfMeasurement(JSContext* cx, H
         !JS_FreezeObject(cx, ctor)) {
         return 0;
     }
 
     return prototype;
 }
 
 PerfMeasurement*
-ExtractPerfMeasurement(jsval wrapper)
+ExtractPerfMeasurement(Value wrapper)
 {
     if (wrapper.isPrimitive())
         return 0;
 
     // This is what JS_GetInstancePrivate does internally.  We can't
     // call JS_anything from here, because we don't have a JSContext.
     JSObject* obj = wrapper.toObjectOrNull();
     if (obj->getClass() != js::Valueify(&pm_class))
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -96,16 +96,16 @@ class ScriptedDirectProxyHandler : publi
     static const int IS_CALLABLE    = 1 << 0;
     static const int IS_CONSTRUCTOR = 1 << 1;
     // The "function extended" slot index in which the revocation object is stored. Per spec, this
     // is to be cleared during the first revocation.
     static const int REVOKE_SLOT = 0;
 };
 
 bool
-proxy(JSContext* cx, unsigned argc, jsval* vp);
+proxy(JSContext* cx, unsigned argc, Value* vp);
 
 bool
-proxy_revocable(JSContext* cx, unsigned argc, jsval* vp);
+proxy_revocable(JSContext* cx, unsigned argc, Value* vp);
 
 } /* namespace js */
 
 #endif /* proxy_ScriptedDirectProxyHandler_h */
--- a/js/src/shell/OSObject.cpp
+++ b/js/src/shell/OSObject.cpp
@@ -146,17 +146,17 @@ FileAsTypedArray(JSContext* cx, const ch
                 obj = nullptr;
             }
         }
     }
     return obj;
 }
 
 static bool
-ReadFile(JSContext* cx, unsigned argc, jsval* vp, bool scriptRelative)
+ReadFile(JSContext* cx, unsigned argc, Value* vp, bool scriptRelative)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() < 1 || args.length() > 2) {
         JS_ReportErrorNumber(cx, js::shell::my_GetErrorMessage, nullptr,
                              args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
                              "snarf");
         return false;
@@ -194,23 +194,23 @@ ReadFile(JSContext* cx, unsigned argc, j
 
     if (!(str = FileAsString(cx, filename.ptr())))
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
-osfile_readFile(JSContext* cx, unsigned argc, jsval* vp)
+osfile_readFile(JSContext* cx, unsigned argc, Value* vp)
 {
     return ReadFile(cx, argc, vp, false);
 }
 
 static bool
-osfile_readRelativeToScript(JSContext* cx, unsigned argc, jsval* vp)
+osfile_readRelativeToScript(JSContext* cx, unsigned argc, Value* vp)
 {
     return ReadFile(cx, argc, vp, true);
 }
 
 static bool
 Redirect(JSContext* cx, FILE* fp, HandleString relFilename)
 {
     RootedString filename(cx, ResolvePath(cx, relFilename, RootRelative));
@@ -222,17 +222,17 @@ Redirect(JSContext* cx, FILE* fp, Handle
     if (freopen(filenameABS.ptr(), "wb", fp) == nullptr) {
         JS_ReportError(cx, "cannot redirect to %s: %s", filenameABS.ptr(), strerror(errno));
         return false;
     }
     return true;
 }
 
 static bool
-osfile_redirect(JSContext* cx, unsigned argc, jsval* vp)
+osfile_redirect(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() < 1 || args.length() > 2) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "redirect");
         return false;
     }
 
@@ -366,17 +366,17 @@ ReportSysError(JSContext* cx, const char
     snprintf(final, nbytes, "%s: %s", prefix, errstr);
 #endif
 
     JS_ReportError(cx, final);
     js_free(final);
 }
 
 static bool
-os_system(JSContext* cx, unsigned argc, jsval* vp)
+os_system(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
         JS_ReportError(cx, "os.system requires 1 argument");
         return false;
     }
 
@@ -395,17 +395,17 @@ os_system(JSContext* cx, unsigned argc, 
     }
 
     args.rval().setInt32(result);
     return true;
 }
 
 #ifndef XP_WIN
 static bool
-os_spawn(JSContext* cx, unsigned argc, jsval* vp)
+os_spawn(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() == 0) {
         JS_ReportError(cx, "os.spawn requires 1 argument");
         return false;
     }
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -576,17 +576,17 @@ Process(JSContext* cx, const char* filen
         RunFile(cx, filename, file, compileOnly);
     } else {
         // It's an interactive filehandle; drop into read-eval-print loop.
         ReadEvalPrintLoop(cx, file, gOutFile, compileOnly);
     }
 }
 
 static bool
-Version(JSContext* cx, unsigned argc, jsval* vp)
+Version(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     JSVersion origVersion = JS_GetVersion(cx);
     if (args.length() == 0 || args[0].isUndefined()) {
         /* Get version. */
         args.rval().setInt32(origVersion);
     } else {
         /* Set version. */
@@ -686,17 +686,17 @@ CreateMappedArrayBuffer(JSContext* cx, u
     if (!obj)
         return false;
 
     args.rval().setObject(*obj);
     return true;
 }
 
 static bool
-Options(JSContext* cx, unsigned argc, jsval* vp)
+Options(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     JS::RuntimeOptions oldRuntimeOptions = JS::RuntimeOptionsRef(cx);
     for (unsigned i = 0; i < args.length(); i++) {
         JSString* str = JS::ToString(cx, args[i]);
         if (!str)
             return false;
@@ -745,17 +745,17 @@ Options(JSContext* cx, unsigned argc, js
     free(names);
     if (!str)
         return false;
     args.rval().setString(str);
     return true;
 }
 
 static bool
-LoadScript(JSContext* cx, unsigned argc, jsval* vp, bool scriptRelative)
+LoadScript(JSContext* cx, unsigned argc, Value* vp, bool scriptRelative)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     RootedString str(cx);
     for (unsigned i = 0; i < args.length(); i++) {
         str = JS::ToString(cx, args[i]);
         if (!str) {
             JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "load");
@@ -784,23 +784,23 @@ LoadScript(JSContext* cx, unsigned argc,
         }
     }
 
     args.rval().setUndefined();
     return true;
 }
 
 static bool
-Load(JSContext* cx, unsigned argc, jsval* vp)
+Load(JSContext* cx, unsigned argc, Value* vp)
 {
     return LoadScript(cx, argc, vp, false);
 }
 
 static bool
-LoadScriptRelativeToScript(JSContext* cx, unsigned argc, jsval* vp)
+LoadScriptRelativeToScript(JSContext* cx, unsigned argc, Value* vp)
 {
     return LoadScript(cx, argc, vp, true);
 }
 
 // Populate |options| with the options given by |opts|'s properties. If we
 // need to convert a filename to a C string, let fileNameBytes own the
 // bytes.
 static bool
@@ -1026,17 +1026,17 @@ class AutoSaveFrameChain
 
     ~AutoSaveFrameChain() {
         if (saved_)
             JS_RestoreFrameChain(cx_);
     }
 };
 
 static bool
-Evaluate(JSContext* cx, unsigned argc, jsval* vp)
+Evaluate(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() < 1 || args.length() > 2) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr,
                              args.length() < 1 ? JSSMSG_NOT_ENOUGH_ARGS : JSSMSG_TOO_MANY_ARGS,
                              "evaluate");
         return false;
@@ -1324,17 +1324,17 @@ js::shell::FileAsString(JSContext* cx, c
 }
 
 /*
  * Function to run scripts and return compilation + execution time. Semantics
  * are closely modelled after the equivalent function in WebKit, as this is used
  * to produce benchmark timings by SunSpider.
  */
 static bool
-Run(JSContext* cx, unsigned argc, jsval* vp)
+Run(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     if (args.length() != 1) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS, "run");
         return false;
     }
 
     RootedString str(cx, JS::ToString(cx, args[0]));