merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 23 Sep 2015 12:28:10 +0200
changeset 263986 abe43c30d78d7546ed7c622c5cf62d265709cdfd
parent 263985 569434e69a44250c0554fae609bf3d4483c9a29b (current diff)
parent 263908 59f692be81f842c4d85091e080c483280437d2a5 (diff)
child 263987 88067e5193f024cddba80453677ca209d10a15fe
child 264067 8e8f1c36d994f07a904ce79bb096b85268c2345d
child 264080 abac231f245e808658e2df368719d561ddc4d45e
push id65484
push usercbook@mozilla.com
push dateWed, 23 Sep 2015 10:47:13 +0000
treeherdermozilla-inbound@88067e5193f0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/components/extensions/test/browser/browser_extensions_simple.js
ipc/chromium/src/third_party/libevent-avoid-empty-sighandler.patch
ipc/chromium/src/third_party/libevent-dont-use-issetugid-on-android.patch
ipc/chromium/src/third_party/libevent-use-non-deprecated-syscalls.patch
ipc/chromium/src/third_party/libevent/mac-arc4random-buf.patch
ipc/chromium/src/third_party/libevent/openbsd-no-arc4random_addrandom.patch
mobile/android/config/tooltool-manifests/android/releng.manifest
testing/taskcluster/scripts/misc/repackage-jdk.sh
testing/web-platform/meta/dom/nodes/DOMImplementation-createHTMLDocument.html.ini
testing/web-platform/meta/domparsing/DOMParser-parseFromString-html.html.ini
testing/web-platform/mozilla/meta/service-workers/service-worker/getregistrations.sub.html.ini
testing/web-platform/mozilla/tests/service-workers/service-worker/getregistrations.sub.html
toolkit/components/extensions/test/mochitest/file_contentscript_page1.html
toolkit/components/extensions/test/mochitest/test_extension_contentscript.html
toolkit/components/extensions/test/mochitest/test_extension_webrequest.html
toolkit/components/extensions/test/mochitest/test_simple_extensions.html
--- a/accessible/atk/AccessibleWrap.cpp
+++ b/accessible/atk/AccessibleWrap.cpp
@@ -822,17 +822,25 @@ getParentCB(AtkObject *aAtkObj)
 gint
 getChildCountCB(AtkObject *aAtkObj)
 {
   if (AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj)) {
     if (nsAccUtils::MustPrune(accWrap)) {
       return 0;
     }
 
-    return static_cast<gint>(accWrap->EmbeddedChildCount());
+    uint32_t count = accWrap->EmbeddedChildCount();
+    if (count) {
+      return static_cast<gint>(count);
+    }
+
+    OuterDocAccessible* outerDoc = accWrap->AsOuterDoc();
+    if (outerDoc && outerDoc->RemoteChildDoc()) {
+      return 1;
+    }
   }
 
   ProxyAccessible* proxy = GetProxy(aAtkObj);
   if (proxy && !proxy->MustPruneChildren()) {
     return proxy->EmbeddedChildCount();
   }
 
   return 0;
--- a/accessible/generic/DocAccessible.cpp
+++ b/accessible/generic/DocAccessible.cpp
@@ -1369,25 +1369,25 @@ DocAccessible::ProcessInvalidationList()
       AutoTreeMutation mut(oldParent);
       oldParent->RemoveChild(child);
 
       MaybeNotifyOfValueChange(oldParent);
       FireDelayedEvent(reorderEvent);
     }
 
     {
+      AutoTreeMutation mut(owner);
+      owner->AppendChild(child);
+
       nsRefPtr<AccReorderEvent> reorderEvent = new AccReorderEvent(owner);
       nsRefPtr<AccMutationEvent> showEvent =
         new AccShowEvent(child, child->GetContent());
       FireDelayedEvent(showEvent);
       reorderEvent->AddSubMutationEvent(showEvent);
 
-      AutoTreeMutation mut(owner);
-      owner->AppendChild(child);
-
       MaybeNotifyOfValueChange(owner);
       FireDelayedEvent(reorderEvent);
     }
 
     child->SetRepositioned(true);
   }
 
   mARIAOwnsInvalidationList.Clear();
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -1913,17 +1913,17 @@ DocAccessibleChild::RecvExtents(const ui
                                 int32_t* aWidth,
                                 int32_t* aHeight)
 {
   *aX = 0;
   *aY = 0;
   *aWidth = 0;
   *aHeight = 0;
   Accessible* acc = IdToAccessible(aID);
-  if (acc && !acc->IsDefunct() && !nsAccUtils::MustPrune(acc)) {
+  if (acc && !acc->IsDefunct()) {
     nsIntRect screenRect = acc->Bounds();
     if (!screenRect.IsEmpty()) {
       if (aNeedsScreenCoords) {
         nsIntPoint winCoords =
           nsCoreUtils::GetScreenCoordsForWindow(acc->GetNode());
         screenRect.x -= winCoords.x;
         screenRect.y -= winCoords.y;
       }
--- a/devtools/shared/Loader.jsm
+++ b/devtools/shared/Loader.jsm
@@ -28,16 +28,17 @@ this.EXPORTED_SYMBOLS = ["DevToolsLoader
  * Providers are different strategies for loading the devtools.
  */
 
 var loaderModules = {
   "Services": Object.create(Services),
   "toolkit/loader": Loader,
   PromiseDebugging,
   ThreadSafeChromeUtils,
+  HeapSnapshot,
 };
 XPCOMUtils.defineLazyGetter(loaderModules, "Debugger", () => {
   // addDebuggerToGlobal only allows adding the Debugger object to a global. The
   // this object is not guaranteed to be a global (in particular on B2G, due to
   // compartment sharing), so add the Debugger object to a sandbox instead.
   let sandbox = Cu.Sandbox(CC('@mozilla.org/systemprincipal;1', 'nsIPrincipal')());
   Cu.evalInSandbox(
     "Components.utils.import('resource://gre/modules/jsdebugger.jsm');" +
--- a/devtools/shared/heapsnapshot/DeserializedNode.cpp
+++ b/devtools/shared/heapsnapshot/DeserializedNode.cpp
@@ -95,18 +95,18 @@ Concrete<DeserializedNode>::typeName() c
 Node::Size
 Concrete<DeserializedNode>::size(mozilla::MallocSizeOf mallocSizeof) const
 {
   return get().size;
 }
 
 class DeserializedEdgeRange : public EdgeRange
 {
-  SimpleEdgeVector edges;
-  size_t           i;
+  EdgeVector edges;
+  size_t     i;
 
   void settle() {
     front_ = i < edges.length() ? &edges[i] : nullptr;
   }
 
 public:
   explicit DeserializedEdgeRange(JSContext* cx)
     : edges(cx)
@@ -127,17 +127,17 @@ public:
       char16_t* name = nullptr;
       if (edgep->name) {
         name = NS_strdup(edgep->name);
         if (!name)
           return false;
       }
 
       auto referent = node.getEdgeReferent(*edgep);
-      edges.infallibleAppend(mozilla::Move(SimpleEdge(name, referent)));
+      edges.infallibleAppend(mozilla::Move(Edge(name, referent)));
     }
 
     settle();
     return true;
   }
 
   void popFront() override
   {
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/FileDescriptorOutputStream.cpp
@@ -0,0 +1,85 @@
+/* -*-  Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/devtools/FileDescriptorOutputStream.h"
+#include "private/pprio.h"
+
+namespace mozilla {
+namespace devtools {
+
+/* static */ already_AddRefed<FileDescriptorOutputStream>
+FileDescriptorOutputStream::Create(const ipc::FileDescriptor& fileDescriptor)
+{
+  if (NS_WARN_IF(!fileDescriptor.IsValid()))
+    return nullptr;
+
+  PRFileDesc* prfd = PR_ImportFile(PROsfd(fileDescriptor.PlatformHandle()));
+  if (NS_WARN_IF(!prfd))
+    return nullptr;
+
+  nsRefPtr<FileDescriptorOutputStream> stream = new FileDescriptorOutputStream(prfd);
+  return stream.forget();
+}
+
+NS_IMPL_ISUPPORTS(FileDescriptorOutputStream, nsIOutputStream);
+
+NS_IMETHODIMP
+FileDescriptorOutputStream::Close()
+{
+  // Repeatedly closing is idempotent.
+  if (!fd)
+    return NS_OK;
+
+  if (PR_Close(fd) != PR_SUCCESS)
+    return NS_ERROR_FAILURE;
+  fd = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileDescriptorOutputStream::Write(const char* buf, uint32_t count, uint32_t* retval)
+{
+  if (NS_WARN_IF(!fd))
+    return NS_ERROR_FAILURE;
+
+  auto written = PR_Write(fd, buf, count);
+  if (written < 0)
+    return NS_ERROR_FAILURE;
+  *retval = written;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+FileDescriptorOutputStream::Flush()
+{
+  if (NS_WARN_IF(!fd))
+    return NS_ERROR_FAILURE;
+
+  return PR_Sync(fd) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+FileDescriptorOutputStream::WriteFrom(nsIInputStream* fromStream, uint32_t count,
+                                      uint32_t* retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+FileDescriptorOutputStream::WriteSegments(nsReadSegmentFun reader, void* closure,
+                                          uint32_t count, uint32_t* retval)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+FileDescriptorOutputStream::IsNonBlocking(bool* retval)
+{
+  *retval = false;
+  return NS_OK;
+}
+
+} // namespace devtools
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/FileDescriptorOutputStream.h
@@ -0,0 +1,41 @@
+/* -*-  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_devtools_FileDescriptorOutputStream_h
+#define mozilla_devtools_FileDescriptorOutputStream_h
+
+#include <prio.h>
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "nsIOutputStream.h"
+
+namespace mozilla {
+namespace devtools {
+
+class FileDescriptorOutputStream final : public nsIOutputStream
+{
+private:
+  PRFileDesc* fd;
+
+public:
+  static already_AddRefed<FileDescriptorOutputStream>
+    Create(const ipc::FileDescriptor& fileDescriptor);
+
+private:
+  explicit FileDescriptorOutputStream(PRFileDesc* prfd)
+    : fd(prfd)
+  { }
+
+  virtual ~FileDescriptorOutputStream() { }
+
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIOUTPUTSTREAM
+};
+
+} // namespace devtools
+} // namespace mozilla
+
+#endif // mozilla_devtools_FileDescriptorOutputStream_h
--- a/devtools/shared/heapsnapshot/HeapSnapshot.cpp
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.cpp
@@ -12,23 +12,24 @@
 #include "js/Debug.h"
 #include "js/TypeDecls.h"
 #include "js/UbiNodeCensus.h"
 #include "js/UbiNodeTraverse.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/devtools/AutoMemMap.h"
 #include "mozilla/devtools/CoreDump.pb.h"
 #include "mozilla/devtools/DeserializedNode.h"
+#include "mozilla/devtools/FileDescriptorOutputStream.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/devtools/ZeroCopyNSIOutputStream.h"
 #include "mozilla/dom/ChromeUtils.h"
+#include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/HeapSnapshotBinding.h"
 #include "mozilla/RangedPtr.h"
 #include "mozilla/Telemetry.h"
-#include "mozilla/TimeStamp.h"
-#include "mozilla/UniquePtr.h"
 
 #include "jsapi.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsCRTGlue.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsIFile.h"
 #include "nsIOutputStream.h"
 #include "nsISupportsImpl.h"
@@ -805,35 +806,28 @@ WriteHeapGraph(JSContext* cx,
   if (ok) {
     outNodeCount = handler.nodeCount;
     outEdgeCount = handler.edgeCount;
   }
 
   return ok;
 }
 
-} // namespace devtools
-
-namespace dom {
-
-using namespace JS;
-using namespace devtools;
-
 static unsigned long
 msSinceProcessCreation(const TimeStamp& now)
 {
   bool ignored;
   auto duration = now - TimeStamp::ProcessCreation(ignored);
   return (unsigned long) duration.ToMilliseconds();
 }
 
-// Creates the `$TEMP_DIR/XXXXXX-XXX.fxsnapshot` core dump file that heap
-// snapshots are serialized into.
-static already_AddRefed<nsIFile>
-createUniqueCoreDumpFile(ErrorResult& rv, const TimeStamp& now, nsAString& outFilePath)
+/* static */ already_AddRefed<nsIFile>
+HeapSnapshot::CreateUniqueCoreDumpFile(ErrorResult& rv,
+                                       const TimeStamp& now,
+                                       nsAString& outFilePath)
 {
   nsCOMPtr<nsIFile> file;
   rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(file));
   if (NS_WARN_IF(rv.Failed()))
     return nullptr;
 
   auto ms = msSinceProcessCreation(now);
   rv = file->AppendNative(nsPrintfCString("%lu.fxsnapshot", ms));
@@ -846,38 +840,117 @@ createUniqueCoreDumpFile(ErrorResult& rv
 
   rv = file->GetPath(outFilePath);
   if (NS_WARN_IF(rv.Failed()))
     return nullptr;
 
   return file.forget();
 }
 
+// Deletion policy for cleaning up PHeapSnapshotTempFileHelperChild pointers.
+class DeleteHeapSnapshotTempFileHelperChild
+{
+public:
+  MOZ_CONSTEXPR DeleteHeapSnapshotTempFileHelperChild() { }
+
+  void operator()(PHeapSnapshotTempFileHelperChild* ptr) const {
+    NS_WARN_IF(!HeapSnapshotTempFileHelperChild::Send__delete__(ptr));
+  }
+};
+
+// A UniquePtr alias to automatically manage PHeapSnapshotTempFileHelperChild
+// pointers.
+using UniqueHeapSnapshotTempFileHelperChild = UniquePtr<PHeapSnapshotTempFileHelperChild,
+                                                        DeleteHeapSnapshotTempFileHelperChild>;
+
+// Get an nsIOutputStream that we can write the heap snapshot to. In non-e10s
+// and in the e10s parent process, open a file directly and create an output
+// stream for it. In e10s child processes, we are sandboxed without access to
+// the filesystem. Use IPDL to request a file descriptor from the parent
+// process.
+static already_AddRefed<nsIOutputStream>
+getCoreDumpOutputStream(ErrorResult& rv, TimeStamp& start, nsAString& outFilePath)
+{
+  if (XRE_IsParentProcess()) {
+    // Create the file and open the output stream directly.
+
+    nsCOMPtr<nsIFile> file = HeapSnapshot::CreateUniqueCoreDumpFile(rv,
+                                                                    start,
+                                                                    outFilePath);
+    if (NS_WARN_IF(rv.Failed()))
+      return nullptr;
+
+    nsCOMPtr<nsIOutputStream> outputStream;
+    rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file,
+                                     PR_WRONLY, -1, 0);
+    if (NS_WARN_IF(rv.Failed()))
+      return nullptr;
+
+    return outputStream.forget();
+  } else {
+    // Request a file descriptor from the parent process over IPDL.
+
+    auto cc = ContentChild::GetSingleton();
+    if (!cc) {
+      rv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    UniqueHeapSnapshotTempFileHelperChild helper(
+      cc->SendPHeapSnapshotTempFileHelperConstructor());
+    if (NS_WARN_IF(!helper)) {
+      rv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    OpenHeapSnapshotTempFileResponse response;
+    if (!helper->SendOpenHeapSnapshotTempFile(&response)) {
+      rv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+    if (response.type() == OpenHeapSnapshotTempFileResponse::Tnsresult) {
+      rv.Throw(response.get_nsresult());
+      return nullptr;
+    }
+
+    auto opened = response.get_OpenedFile();
+    outFilePath = opened.path();
+    nsCOMPtr<nsIOutputStream> outputStream =
+      FileDescriptorOutputStream::Create(opened.descriptor());
+    if (NS_WARN_IF(!outputStream)) {
+      rv.Throw(NS_ERROR_UNEXPECTED);
+      return nullptr;
+    }
+
+    return outputStream.forget();
+  }
+}
+
+} // namespace devtools
+
+namespace dom {
+
+using namespace JS;
+using namespace devtools;
+
 /* static */ void
 ThreadSafeChromeUtils::SaveHeapSnapshot(GlobalObject& global,
                                         JSContext* cx,
                                         const HeapSnapshotBoundaries& boundaries,
                                         nsAString& outFilePath,
                                         ErrorResult& rv)
 {
   auto start = TimeStamp::Now();
 
   bool wantNames = true;
   ZoneSet zones;
   uint32_t nodeCount = 0;
   uint32_t edgeCount = 0;
 
-  nsCOMPtr<nsIFile> file = createUniqueCoreDumpFile(rv, start, outFilePath);
-  if (NS_WARN_IF(rv.Failed()))
-    return;
-
-  nsCOMPtr<nsIOutputStream> outputStream;
-  rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file,
-                                   PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE,
-                                   -1, 0);
+  nsCOMPtr<nsIOutputStream> outputStream = getCoreDumpOutputStream(rv, start, outFilePath);
   if (NS_WARN_IF(rv.Failed()))
     return;
 
   ZeroCopyNSIOutputStream zeroCopyStream(outputStream);
   ::google::protobuf::io::GzipOutputStream gzipStream(&zeroCopyStream);
 
   StreamWriter writer(cx, gzipStream, wantNames);
   if (NS_WARN_IF(!writer.init())) {
--- a/devtools/shared/heapsnapshot/HeapSnapshot.h
+++ b/devtools/shared/heapsnapshot/HeapSnapshot.h
@@ -8,16 +8,17 @@
 
 #include "js/HashTable.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/devtools/DeserializedNode.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/RefPtr.h"
+#include "mozilla/TimeStamp.h"
 #include "mozilla/UniquePtr.h"
 
 #include "CoreDump.pb.h"
 #include "nsCOMPtr.h"
 #include "nsCRTGlue.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupports.h"
 #include "nsWrapperCache.h"
@@ -125,16 +126,22 @@ public:
   // core dump. Do NOT take ownership of the buffer, only borrow it for the
   // duration of the call.
   static already_AddRefed<HeapSnapshot> Create(JSContext* cx,
                                                dom::GlobalObject& global,
                                                const uint8_t* buffer,
                                                uint32_t size,
                                                ErrorResult& rv);
 
+  // Creates the `$TEMP_DIR/XXXXXX-XXX.fxsnapshot` core dump file that heap
+  // snapshots are serialized into.
+  static already_AddRefed<nsIFile> CreateUniqueCoreDumpFile(ErrorResult& rv,
+                                                            const TimeStamp& now,
+                                                            nsAString& outFilePath);
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(HeapSnapshot)
   MOZ_DECLARE_REFCOUNTED_TYPENAME(HeapSnapshot)
 
   nsISupports* GetParentObject() const { return mParent; }
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/HeapSnapshotTempFileHelperChild.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et 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 mozilla_devtools_HeapSnapshotTempFileHelperChild_h
+#define mozilla_devtools_HeapSnapshotTempFileHelperChild_h
+
+#include "mozilla/devtools/PHeapSnapshotTempFileHelperChild.h"
+
+namespace mozilla {
+namespace devtools {
+
+class HeapSnapshotTempFileHelperChild : public PHeapSnapshotTempFileHelperChild
+{
+    explicit HeapSnapshotTempFileHelperChild() { }
+
+public:
+    static inline PHeapSnapshotTempFileHelperChild* Create();
+};
+
+/* static */ inline PHeapSnapshotTempFileHelperChild*
+HeapSnapshotTempFileHelperChild::Create()
+{
+    return new HeapSnapshotTempFileHelperChild();
+}
+
+} // namespace devtools
+} // namespace mozilla
+
+#endif // mozilla_devtools_HeapSnapshotTempFileHelperChild_h
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/HeapSnapshotTempFileHelperParent.cpp
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et 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 "mozilla/devtools/HeapSnapshot.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
+#include "mozilla/ErrorResult.h"
+#include "private/pprio.h"
+
+#include "nsIFile.h"
+
+namespace mozilla {
+namespace devtools {
+
+using ipc::FileDescriptor;
+
+static bool
+openFileFailure(ErrorResult& rv,
+                OpenHeapSnapshotTempFileResponse* outResponse)
+{
+    *outResponse = rv.StealNSResult();
+    return true;
+}
+
+bool
+HeapSnapshotTempFileHelperParent::RecvOpenHeapSnapshotTempFile(
+    OpenHeapSnapshotTempFileResponse* outResponse)
+{
+    auto start = TimeStamp::Now();
+    ErrorResult rv;
+    nsAutoString filePath;
+    nsCOMPtr<nsIFile> file = HeapSnapshot::CreateUniqueCoreDumpFile(rv,
+                                                                    start,
+                                                                    filePath);
+    if (NS_WARN_IF(rv.Failed()))
+        return openFileFailure(rv, outResponse);
+
+    PRFileDesc* prfd;
+    rv = file->OpenNSPRFileDesc(PR_WRONLY, 0, &prfd);
+    if (NS_WARN_IF(rv.Failed()))
+        return openFileFailure(rv, outResponse);
+
+    FileDescriptor::PlatformHandleType handle =
+        FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfd));
+    FileDescriptor fd(handle);
+    *outResponse = OpenedFile(filePath, fd);
+    return true;
+}
+
+} // namespace devtools
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/HeapSnapshotTempFileHelperParent.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=8 et 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 mozilla_devtools_HeapSnapshotTempFileHelperParent_h
+#define mozilla_devtools_HeapSnapshotTempFileHelperParent_h
+
+#include "mozilla/devtools/PHeapSnapshotTempFileHelperParent.h"
+
+namespace mozilla {
+namespace devtools {
+
+class HeapSnapshotTempFileHelperParent : public PHeapSnapshotTempFileHelperParent
+{
+    explicit HeapSnapshotTempFileHelperParent() { }
+    void ActorDestroy(ActorDestroyReason why) override { }
+    bool RecvOpenHeapSnapshotTempFile(OpenHeapSnapshotTempFileResponse* outResponse)
+        override;
+
+  public:
+    static inline PHeapSnapshotTempFileHelperParent* Create();
+};
+
+/* static */ inline PHeapSnapshotTempFileHelperParent*
+HeapSnapshotTempFileHelperParent::Create()
+{
+    return new HeapSnapshotTempFileHelperParent();
+}
+
+} // namespace devtools
+} // namespace mozilla
+
+#endif // mozilla_devtools_HeapSnapshotTempFileHelperParent_h
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/PHeapSnapshotTempFileHelper.ipdl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PContent;
+
+namespace mozilla {
+namespace devtools {
+
+struct OpenedFile
+{
+    nsString       path;
+    FileDescriptor descriptor;
+};
+
+union OpenHeapSnapshotTempFileResponse
+{
+    nsresult;
+    OpenedFile;
+};
+
+sync protocol PHeapSnapshotTempFileHelper
+{
+    manager PContent;
+
+parent:
+    sync OpenHeapSnapshotTempFile() returns (OpenHeapSnapshotTempFileResponse response);
+
+    __delete__();
+};
+
+} // namespace devtools
+} // namespace mozilla
--- a/devtools/shared/heapsnapshot/moz.build
+++ b/devtools/shared/heapsnapshot/moz.build
@@ -2,32 +2,44 @@
 # 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/.
 
 if CONFIG['ENABLE_TESTS']:
     DIRS += ['tests/gtest']
 
-XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
-MOCHITEST_CHROME_MANIFESTS += ['tests/mochitest/chrome.ini']
+XPCSHELL_TESTS_MANIFESTS += [ 'tests/unit/xpcshell.ini' ]
+MOCHITEST_MANIFESTS += [ 'tests/mochitest/mochitest.ini' ]
+MOCHITEST_CHROME_MANIFESTS += [ 'tests/mochitest/chrome.ini' ]
 
 EXPORTS.mozilla.devtools += [
     'AutoMemMap.h',
     'CoreDump.pb.h',
     'DeserializedNode.h',
+    'FileDescriptorOutputStream.h',
     'HeapSnapshot.h',
+    'HeapSnapshotTempFileHelperChild.h',
+    'HeapSnapshotTempFileHelperParent.h',
     'ZeroCopyNSIOutputStream.h',
 ]
 
+IPDL_SOURCES += [
+    'PHeapSnapshotTempFileHelper.ipdl',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
 SOURCES += [
     'AutoMemMap.cpp',
     'CoreDump.pb.cc',
     'DeserializedNode.cpp',
+    'FileDescriptorOutputStream.cpp',
     'HeapSnapshot.cpp',
+    'HeapSnapshotTempFileHelperParent.cpp',
     'ZeroCopyNSIOutputStream.cpp',
 ]
 
 # Disable RTTI in google protocol buffer
 DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
 
 FINAL_LIBRARY = 'xul'
 
--- a/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
+++ b/devtools/shared/heapsnapshot/tests/gtest/DevTools.h
@@ -152,20 +152,20 @@ struct DevTools : public ::testing::Test
     body                                        \
   }
 
 
 // Fake JS::ubi::Node implementation
 class MOZ_STACK_CLASS FakeNode
 {
 public:
-  JS::ubi::SimpleEdgeVector edges;
-  JSCompartment*            compartment;
-  JS::Zone*                 zone;
-  size_t                    size;
+  JS::ubi::EdgeVector edges;
+  JSCompartment*      compartment;
+  JS::Zone*           zone;
+  size_t              size;
 
   explicit FakeNode(JSContext* cx)
     : edges(cx),
     compartment(nullptr),
     zone(nullptr),
     size(1)
   { }
 };
@@ -216,17 +216,17 @@ const char16_t Concrete<FakeNode>::concr
 
 void AddEdge(FakeNode& node, FakeNode& referent, const char16_t* edgeName = nullptr) {
   char16_t* ownedEdgeName = nullptr;
   if (edgeName) {
     ownedEdgeName = NS_strdup(edgeName);
     ASSERT_NE(ownedEdgeName, nullptr);
   }
 
-  JS::ubi::SimpleEdge edge(ownedEdgeName, &referent);
+  JS::ubi::Edge edge(ownedEdgeName, &referent);
   ASSERT_TRUE(node.edges.append(mozilla::Move(edge)));
 }
 
 
 // Custom GMock Matchers
 
 // Use the testing namespace to avoid static analysis failures in the gmock
 // matcher classes that get generated from MATCHER_P macros.
--- a/devtools/shared/heapsnapshot/tests/gtest/moz.build
+++ b/devtools/shared/heapsnapshot/tests/gtest/moz.build
@@ -16,12 +16,14 @@ UNIFIED_SOURCES = [
     'DoesCrossZoneBoundaries.cpp',
     'DoesntCrossZoneBoundaries.cpp',
     'SerializesEdgeNames.cpp',
     'SerializesEverythingInHeapGraphOnce.cpp',
     'SerializesTypeNames.cpp',
     'UniqueStringHashPolicy.cpp',
 ]
 
-# XXX: We should fix these warnings.
-ALLOW_COMPILER_WARNINGS = True
+# THE MOCK_METHOD2 macro from gtest triggers this clang warning and it's hard
+# to work around, so we just ignore it.
+if CONFIG['CLANG_CXX']:
+  CXXFLAGS += ['-Wno-error=inconsistent-missing-override']
 
 FINAL_LIBRARY = 'xul-gtest'
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/mochitest/mochitest.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+tags = devtools
+support-files =
+
+[test_saveHeapSnapshot_e10s_01.html]
+
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/mochitest/test_saveHeapSnapshot_e10s_01.html
@@ -0,0 +1,82 @@
+<!DOCTYPE HTML>
+<!--
+Bug 1201597 - Sanity test that we can take a heap snapshot in an e10s child process.
+-->
+<html>
+<head>
+  <title>saveHeapSnapshot in e10s child processes</title>
+  <script type="application/javascript"
+          src="/tests/SimpleTest/SimpleTest.js">
+  </script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+    <script type="application/javascript">
+     window.onerror = function (msg, url, line, col, err) {
+         ok(false, "@" + url + ":" + line + ":" + col + ": " + msg + "\n" + err.stack);
+     };
+
+     SimpleTest.waitForExplicitFinish();
+
+     var childFrameURL = "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+
+     // This function is stringified and loaded in the child process as a frame
+     // script.
+     function childFrameScript() {
+         try {
+             ChromeUtils.saveHeapSnapshot({ runtime: true });
+         } catch (err) {
+             sendAsyncMessage("testSaveHeapSnapshot:error",
+                              { error: err.toString() });
+             return;
+         }
+
+         sendAsyncMessage("testSaveHeapSnapshot:done", {});
+     }
+
+     // Kick everything off on load.
+     window.onload = function () {
+         info("window.onload fired");
+         SpecialPowers.addPermission("browser", true, document);
+         SpecialPowers.pushPrefEnv({
+             "set": [
+                 ["dom.ipc.browser_frames.oop_by_default", true],
+                 ["dom.mozBrowserFramesEnabled", true],
+                 ["browser.pagethumbnails.capturing_disabled", true]
+             ]
+         }, function () {
+             var iframe = document.createElement("iframe");
+             SpecialPowers.wrap(iframe).mozbrowser = true;
+             iframe.id = "iframe";
+             iframe.src = childFrameURL;
+
+
+             iframe.addEventListener("mozbrowserloadend", function onLoadEnd() {
+                 iframe.removeEventListener("mozbrowserloadend", onLoadEnd);
+                 info("iframe done loading");
+
+                 var mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+
+                 function onError(e) {
+                     ok(false, e.data.error);
+                 }
+                 mm.addMessageListener("testSaveHeapSnapshot:error", onError);
+
+                 mm.addMessageListener("testSaveHeapSnapshot:done", function onMsg() {
+                     mm.removeMessageListener("testSaveHeapSnapshot:done", onMsg);
+                     mm.removeMessageListener("testSaveHeapSnapshot:error", onError);
+                     ok(true, "Saved heap snapshot in child process");
+                     SimpleTest.finish();
+                 });
+
+                 info("Loading frame script to save heap snapshot");
+                 mm.loadFrameScript("data:,(" + encodeURI(childFrameScript.toString()) + ")();",
+                                    false);
+             });
+
+             info("Loading iframe");
+             document.body.appendChild(iframe);
+         });
+     };
+    </script>
+</window>
--- a/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
+++ b/devtools/shared/heapsnapshot/tests/unit/head_heapsnapshot.js
@@ -19,17 +19,20 @@ const { Task } = Cu.import("resource://g
 const DevToolsUtils = require("devtools/shared/DevToolsUtils");
 const HeapAnalysesClient =
   require("devtools/shared/heapsnapshot/HeapAnalysesClient");
 const Services = require("Services");
 const { CensusTreeNode } = require("devtools/shared/heapsnapshot/census-tree-node");
 
 // Always log packets when running tests. runxpcshelltests.py will throw
 // the output away anyway, unless you give it the --verbose flag.
-Services.prefs.setBoolPref("devtools.debugger.log", true);
+if (Services.appInfo &&
+    Services.appInfo.processType == Services.appInfo.PROCESS_TYPE_DEFAULT) {
+  Services.prefs.setBoolPref("devtools.debugger.log", true);
+}
 DevToolsUtils.dumpn.wantLogging = true;
 
 const SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"]
   .createInstance(Ci.nsIPrincipal);
 
 function dumpn(msg) {
   dump("HEAPSNAPSHOT-TEST: " + msg + "\n");
 }
new file mode 100644
--- /dev/null
+++ b/devtools/shared/heapsnapshot/tests/unit/test_saveHeapSnapshot_e10s_01.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Test saving a heap snapshot in the sandboxed e10s child process.
+
+function run_test() {
+  run_test_in_child("../unit/test_SaveHeapSnapshot.js");
+}
--- a/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
+++ b/devtools/shared/heapsnapshot/tests/unit/xpcshell.ini
@@ -28,8 +28,9 @@ support-files =
 [test_HeapSnapshot_takeCensus_10.js]
 [test_HeapSnapshot_takeCensus_11.js]
 [test_ReadHeapSnapshot.js]
 [test_ReadHeapSnapshot_with_allocations.js]
 skip-if = os == 'linux' # Bug 1176173
 [test_ReadHeapSnapshot_worker.js]
 skip-if = os == 'linux' # Bug 1176173
 [test_SaveHeapSnapshot.js]
+[test_saveHeapSnapshot_e10s_01.js]
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -13845,16 +13845,17 @@ nsDocShell::MaybeNotifyKeywordSearchLoad
       }
     }
   }
 #endif
 }
 
 NS_IMETHODIMP
 nsDocShell::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate,
+                                      nsContentPolicyType aLoadContentType,
                                       bool* aShouldIntercept)
 {
   *aShouldIntercept = false;
   // Preffed off.
   if (!nsContentUtils::ServiceWorkerInterceptionEnabled()) {
     return NS_OK;
   }
 
@@ -13897,17 +13898,17 @@ nsDocShell::ShouldPrepareForIntercept(ns
           (Preferences::GetInt("network.cookie.cookieBehavior",
                                nsICookieService::BEHAVIOR_ACCEPT) ==
                                nsICookieService::BEHAVIOR_REJECT_FOREIGN)) {
         return NS_OK;
       }
     }
   }
 
-  if (aIsNavigate) {
+  if (aIsNavigate || nsContentUtils::IsWorkerLoad(aLoadContentType)) {
     OriginAttributes attrs(GetAppId(), GetIsInBrowserElement());
     *aShouldIntercept = swm->IsAvailable(attrs, aURI);
     return NS_OK;
   }
 
   nsCOMPtr<nsIDocument> doc = GetDocument();
   if (!doc) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -13977,32 +13978,38 @@ nsDocShell::ChannelIntercepted(nsIInterc
     aChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
     return NS_OK;
   }
 
   bool isNavigation = false;
   nsresult rv = aChannel->GetIsNavigation(&isNavigation);
   NS_ENSURE_SUCCESS(rv, rv);
 
+  nsContentPolicyType loadType;
+  rv = aChannel->GetInternalContentPolicyType(&loadType);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   nsCOMPtr<nsIDocument> doc;
 
-  if (!isNavigation) {
+  bool isSubresourceLoad = !isNavigation &&
+                           !nsContentUtils::IsWorkerLoad(loadType);
+  if (isSubresourceLoad) {
     doc = GetDocument();
     if (!doc) {
       return NS_ERROR_NOT_AVAILABLE;
     }
   }
 
   bool isReload = mLoadType & LOAD_CMD_RELOAD;
 
   OriginAttributes attrs(GetAppId(), GetIsInBrowserElement());
 
   ErrorResult error;
   nsCOMPtr<nsIRunnable> runnable =
-    swm->PrepareFetchEvent(attrs, doc, aChannel, isReload, error);
+    swm->PrepareFetchEvent(attrs, doc, aChannel, isReload, isSubresourceLoad, error);
   if (NS_WARN_IF(error.Failed())) {
     return error.StealNSResult();
   }
 
   MOZ_ASSERT(runnable);
   nsRefPtr<FetchEventDispatcher> dispatcher =
     new FetchEventDispatcher(aChannel, runnable);
   dispatcher.forget(aFetchDispatcher);
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -531,17 +531,16 @@ private:
 
     JS::Rooted<JS::Value> argumentsValue(aCx);
     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;
     if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
       return;
     }
 
     for (uint32_t i = 0; i < length; ++i) {
       JS::Rooted<JS::Value> value(aCx);
@@ -626,17 +625,16 @@ private:
     mClonedData.mParent = nullptr;
 
     if (!ok) {
       return;
     }
 
     MOZ_ASSERT(argumentsValue.isObject());
     JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject());
-    MOZ_ASSERT(JS_IsArrayObject(aCx, argumentsObj));
 
     uint32_t length;
     if (!JS_GetArrayLength(aCx, argumentsObj, &length)) {
       return;
     }
 
     Sequence<JS::Value> arguments;
 
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3253,24 +3253,32 @@ Element::MozRequestFullScreen(JSContext*
 
   auto request = MakeUnique<FullscreenRequest>(this);
   request->mIsCallerChrome = nsContentUtils::IsCallerChrome();
 
   RequestFullscreenOptions fsOptions;
   // We need to check if options is convertible to a dict first before
   // trying to init fsOptions; otherwise Init() would throw, and we want to
   // silently ignore non-dictionary values
-  if (aCx && IsConvertibleToDictionary(aCx, aOptions)) {
-    if (!fsOptions.Init(aCx, aOptions)) {
+  if (aCx) {
+    bool convertible;
+    if (!IsConvertibleToDictionary(aCx, aOptions, &convertible)) {
       aError.Throw(NS_ERROR_FAILURE);
       return;
     }
 
-    if (fsOptions.mVrDisplay) {
-      request->mVRHMDDevice = fsOptions.mVrDisplay->GetHMD();
+    if (convertible) {
+      if (!fsOptions.Init(aCx, aOptions)) {
+        aError.Throw(NS_ERROR_FAILURE);
+        return;
+      }
+
+      if (fsOptions.mVrDisplay) {
+        request->mVRHMDDevice = fsOptions.mVrDisplay->GetHMD();
+      }
     }
   }
 
   OwnerDoc()->AsyncRequestFullScreen(Move(request));
 }
 
 void
 Element::MozRequestPointerLock()
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -2836,18 +2836,21 @@ Navigator::RequestMediaKeySystemAccess(c
       }
       logMsg.AppendLiteral("}");
     }
   }
   logMsg.AppendPrintf("])");
   EME_LOG(logMsg.get());
 
   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
-  nsRefPtr<DetailedPromise> promise = DetailedPromise::Create(go, aRv,
-    NS_LITERAL_CSTRING("navigator.requestMediaKeySystemAccess"));
+  nsRefPtr<DetailedPromise> promise =
+    DetailedPromise::Create(go, aRv,
+      NS_LITERAL_CSTRING("navigator.requestMediaKeySystemAccess"),
+      Telemetry::VIDEO_EME_REQUEST_SUCCESS_LATENCY_MS,
+      Telemetry::VIDEO_EME_REQUEST_FAILURE_LATENCY_MS);
   if (aRv.Failed()) {
     return nullptr;
   }
 
   if (!mMediaKeySystemAccessManager) {
     mMediaKeySystemAccessManager = new MediaKeySystemAccessManager(mWindow);
   }
 
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -8087,16 +8087,24 @@ nsContentUtils::PushEnabled(JSContext* a
   WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(aCx);
   if (!workerPrivate) {
     return false;
   }
 
   return workerPrivate->PushEnabled();
 }
 
+// static
+bool
+nsContentUtils::IsWorkerLoad(nsContentPolicyType aType)
+{
+  return aType == nsIContentPolicy::TYPE_INTERNAL_WORKER ||
+         aType == nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER;
+}
+
 // static, public
 nsContentUtils::StorageAccess
 nsContentUtils::StorageAllowedForWindow(nsPIDOMWindow* aWindow)
 {
   MOZ_ASSERT(aWindow->IsInnerWindow());
 
   nsIDocument* document = aWindow->GetExtantDoc();
   if (document) {
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2476,16 +2476,18 @@ public:
    * https://w3c.github.io/webappsec/specs/referrer-policy/#determine-requests-referrer
    */
   static nsresult SetFetchReferrerURIWithPolicy(nsIPrincipal* aPrincipal,
                                                 nsIDocument* aDoc,
                                                 nsIHttpChannel* aChannel);
 
   static bool PushEnabled(JSContext* aCx, JSObject* aObj);
 
+  static bool IsWorkerLoad(nsContentPolicyType aLoadType);
+
   // The order of these entries matters, as we use std::min for total ordering
   // of permissions. Private Browsing is considered to be more limiting
   // then session scoping
   enum class StorageAccess {
     // Don't allow access to the storage
     eDeny = 0,
     // Allow access to the storage, but only if it is secure to do so in a
     // private browsing context.
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -16,17 +16,16 @@ DEPRECATED_OPERATION(GetAttributeNodeNS)
 DEPRECATED_OPERATION(SetAttributeNodeNS)
 DEPRECATED_OPERATION(RemoveAttributeNode)
 DEPRECATED_OPERATION(CreateAttribute)
 DEPRECATED_OPERATION(CreateAttributeNS)
 DEPRECATED_OPERATION(OwnerElement)
 DEPRECATED_OPERATION(NodeValue)
 DEPRECATED_OPERATION(TextContent)
 DEPRECATED_OPERATION(EnablePrivilege)
-DEPRECATED_OPERATION(InputEncoding)
 DEPRECATED_OPERATION(DOMExceptionCode)
 DEPRECATED_OPERATION(NoExposedProps)
 DEPRECATED_OPERATION(MutationEvent)
 DEPRECATED_OPERATION(Components)
 DEPRECATED_OPERATION(PrefixedVisibilityAPI)
 DEPRECATED_OPERATION(NodeIteratorDetach)
 DEPRECATED_OPERATION(LenientThis)
 DEPRECATED_OPERATION(GetPreventDefault)
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -2622,18 +2622,16 @@ nsDocument::StartDocumentLoad(const char
     CSSLoader()->SetEnabled(false); // Do not load/process styles when loading as data
   } else if (nsCRT::strcmp("external-resource", aCommand) == 0) {
     // Allow CSS, but not scripts
     ScriptLoader()->SetEnabled(false);
   }
 
   mMayStartLayout = false;
 
-  mHaveInputEncoding = true;
-
   if (aReset) {
     Reset(aChannel, aLoadGroup);
   }
 
   nsAutoCString contentType;
   nsCOMPtr<nsIPropertyBag2> bag = do_QueryInterface(aChannel);
   if ((bag && NS_SUCCEEDED(bag->GetPropertyAsACString(
                 NS_LITERAL_STRING("contentType"), contentType))) ||
@@ -7483,29 +7481,18 @@ nsIDocument::SetDir(const nsAString& aDi
     rootElement->SetAttr(kNameSpaceID_None, nsGkAtoms::dir,
                          aDirection, true);
   }
 }
 
 NS_IMETHODIMP
 nsDocument::GetInputEncoding(nsAString& aInputEncoding)
 {
-  nsIDocument::GetInputEncoding(aInputEncoding);
-  return NS_OK;
-}
-
-void
-nsIDocument::GetInputEncoding(nsAString& aInputEncoding) const
-{
-  WarnOnceAbout(eInputEncoding);
-  if (mHaveInputEncoding) {
-    return GetCharacterSet(aInputEncoding);
-  }
-
-  SetDOMStringToNull(aInputEncoding);
+  nsIDocument::GetCharacterSet(aInputEncoding);
+  return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocument::GetMozSyntheticDocument(bool *aSyntheticDocument)
 {
   *aSyntheticDocument = mIsSyntheticDocument;
   return NS_OK;
 }
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2829,20 +2829,16 @@ protected:
   bool mHasTrackingContentBlocked : 1;
 
   // True if a document has loaded Tracking Content
   bool mHasTrackingContentLoaded : 1;
 
   // True if DisallowBFCaching has been called on this document.
   bool mBFCacheDisallowed : 1;
 
-  // If true, we have an input encoding.  If this is false, then the
-  // document was created entirely in memory
-  bool mHaveInputEncoding : 1;
-
   bool mHasHadDefaultView : 1;
 
   // Whether style sheet change events will be dispatched for this document
   bool mStyleSheetChangeEventsEnabled : 1;
 
   // Whether the document was created by a srcdoc iframe.
   bool mIsSrcdocDocument : 1;
 
--- a/dom/base/test/test_XHRSendData.html
+++ b/dom/base/test/test_XHRSendData.html
@@ -31,17 +31,17 @@ xhr.open("GET", "file_XHRSendData_doc.xm
 xhr.send();
 testDoc1 = xhr.responseXML;
 is(testDoc1.inputEncoding, "windows-1252", "wrong encoding");
 
 testDoc2 = document.implementation.createDocument("", "", null);
 testDoc2.appendChild(testDoc2.createComment(" doc 2 "));
 testDoc2.appendChild(testDoc2.createElement("res"));
 testDoc2.documentElement.appendChild(testDoc2.createTextNode("text"));
-is(testDoc2.inputEncoding, null, "wrong encoding");
+is(testDoc2.inputEncoding, "UTF-8", "wrong encoding");
 
 var testData = "blahblahblahblahblahblahblaaaaaaaah. blah.";
 var extensions = [".txt",".png",".jpg",".gif",".xml", "noext"];
 var fileTypes = ["text/plain", "image/png", "image/jpeg", "image/gif", "text/xml", null];
 var testFiles = new Array;
 var testDOMFiles = new Array;
 
 // arraybuffer test objects
--- a/dom/base/test/test_bug431701.html
+++ b/dom/base/test/test_bug431701.html
@@ -58,37 +58,36 @@ function xhrDoc(idx) {
     return xhr.responseXML;
   };
 }
 
 // Each row has the document getter function, then the characterSet,
 // inputEncoding expected for that document.
 
 var tests = [
- [ frameDoc("one"), "windows-1252", "windows-1252" ],
- [ frameDoc("two"), "UTF-8", "UTF-8" ],
- [ frameDoc("three"), "windows-1252", "windows-1252" ],
- [ frameDoc("four"), "UTF-8", "UTF-8" ],
- [ frameDoc("five"), "UTF-8", "UTF-8" ],
- [ frameDoc("six"), "UTF-8", "UTF-8" ],
- [ frameDoc("seven"), "windows-1252", "windows-1252" ],
- [ createDoc, "UTF-8", null ],
- [ xhrDoc(4), "UTF-8", "UTF-8" ],
- [ xhrDoc(5), "UTF-8", "UTF-8" ],
- [ xhrDoc(6), "windows-1252", "windows-1252" ],
+ [ frameDoc("one"), "windows-1252" ],
+ [ frameDoc("two"), "UTF-8" ],
+ [ frameDoc("three"), "windows-1252" ],
+ [ frameDoc("four"), "UTF-8" ],
+ [ frameDoc("five"), "UTF-8" ],
+ [ frameDoc("six"), "UTF-8" ],
+ [ frameDoc("seven"), "windows-1252" ],
+ [ createDoc, "UTF-8" ],
+ [ xhrDoc(4), "UTF-8" ],
+ [ xhrDoc(5), "UTF-8" ],
+ [ xhrDoc(6), "windows-1252" ],
 ];
 
 function doTest(idx) {
-  var [docGetter, expectedCharacterSet,
-       expectedInputEncoding] = tests[idx];
+  var [docGetter, expectedCharacterSet] = tests[idx];
   var doc = docGetter();
 
   // Have to be careful here to catch null vs ""
   is(doc.characterSet, expectedCharacterSet, "Test " + idx + " characterSet");
-  is(doc.inputEncoding, expectedInputEncoding,
+  is(doc.inputEncoding, expectedCharacterSet,
      "Test " + idx + " inputEncoding");
 }
 
 addLoadEvent(function() {
    SpecialPowers.pushPermissions([{'type': 'systemXHR', 'allow': true, 'context': document}], startTest);
 });
 
 function startTest() {
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -249,41 +249,59 @@ template <prototypes::ID PrototypeID, cl
 MOZ_ALWAYS_INLINE nsresult
 UnwrapObject(JSObject* obj, U& value)
 {
   return UnwrapObject<T>(obj, value, PrototypeID,
                          PrototypeTraits<PrototypeID>::Depth);
 }
 
 inline bool
-IsNotDateOrRegExp(JSContext* cx, JS::Handle<JSObject*> obj)
+IsNotDateOrRegExp(JSContext* cx, JS::Handle<JSObject*> obj,
+                  bool* notDateOrRegExp)
 {
   MOZ_ASSERT(obj);
-  return !JS_ObjectIsDate(cx, obj) && !JS_ObjectIsRegExp(cx, obj);
+
+  js::ESClassValue cls;
+  if (!js::GetBuiltinClass(cx, obj, &cls)) {
+    return false;
+  }
+
+  *notDateOrRegExp = cls != js::ESClass_Date && cls != js::ESClass_RegExp;
+  return true;
 }
 
 MOZ_ALWAYS_INLINE bool
 IsObjectValueConvertibleToDictionary(JSContext* cx,
-                                     JS::Handle<JS::Value> objVal)
+                                     JS::Handle<JS::Value> objVal,
+                                     bool* convertible)
 {
   JS::Rooted<JSObject*> obj(cx, &objVal.toObject());
-  return IsNotDateOrRegExp(cx, obj);
+  return IsNotDateOrRegExp(cx, obj, convertible);
 }
 
 MOZ_ALWAYS_INLINE bool
-IsConvertibleToDictionary(JSContext* cx, JS::Handle<JS::Value> val)
+IsConvertibleToDictionary(JSContext* cx, JS::Handle<JS::Value> val,
+                          bool* convertible)
 {
-  return val.isNullOrUndefined() ||
-    (val.isObject() && IsObjectValueConvertibleToDictionary(cx, val));
+  if (val.isNullOrUndefined()) {
+    *convertible = true;
+    return true;
+  }
+  if (!val.isObject()) {
+    *convertible = false;
+    return true;
+  }
+  return IsObjectValueConvertibleToDictionary(cx, val, convertible);
 }
 
 MOZ_ALWAYS_INLINE bool
-IsConvertibleToCallbackInterface(JSContext* cx, JS::Handle<JSObject*> obj)
+IsConvertibleToCallbackInterface(JSContext* cx, JS::Handle<JSObject*> obj,
+                                 bool* convertible)
 {
-  return IsNotDateOrRegExp(cx, obj);
+  return IsNotDateOrRegExp(cx, obj, convertible);
 }
 
 // The items in the protoAndIfaceCache are indexed by the prototypes::id::ID,
 // constructors::id::ID and namedpropertiesobjects::id::ID enums, in that order.
 // The end of the prototype objects should be the start of the interface
 // objects, and the end of the interface objects should be the start of the
 // named properties objects.
 static_assert((size_t)constructors::id::_ID_Start ==
--- a/dom/bindings/Codegen.py
+++ b/dom/bindings/Codegen.py
@@ -4221,19 +4221,16 @@ def getJSToNativeConversionInfo(type, de
     assert not isDefinitelyObject or not isNullOrUndefined
 
     # If exceptionCode is not set, we'll just rethrow the exception we got.
     # Note that we can't just set failureCode to exceptionCode, because setting
     # failureCode will prevent pending exceptions from being set in cases when
     # they really should be!
     if exceptionCode is None:
         exceptionCode = "return false;\n"
-    # We often want exceptionCode to be indented, since it often appears in an
-    # if body.
-    exceptionCodeIndented = CGIndenter(CGGeneric(exceptionCode))
 
     # Unfortunately, .capitalize() on a string will lowercase things inside the
     # string, which we do not want.
     def firstCap(string):
         return string[0].upper() + string[1:]
 
     # Helper functions for dealing with failures due to the JS value being the
     # wrong type of value
@@ -5172,22 +5169,28 @@ def getJSToNativeConversionInfo(type, de
             undefinedBehavior = "eStringify"
         nullBehavior = treatAs[treatNullAs]
 
         def getConversionCode(varName):
             normalizeCode = ""
             if type.isUSVString():
                 normalizeCode = "NormalizeUSVString(cx, %s);\n" % varName
 
-            conversionCode = (
-                "if (!ConvertJSValueToString(cx, ${val}, %s, %s, %s)) {\n"
-                "%s"
-                "}\n"
-                "%s" % (nullBehavior, undefinedBehavior, varName,
-                        exceptionCodeIndented.define(), normalizeCode))
+            conversionCode = fill("""
+                if (!ConvertJSValueToString(cx, $${val}, ${nullBehavior}, ${undefinedBehavior}, ${varName})) {
+                  $*{exceptionCode}
+                }
+                $*{normalizeCode}
+                """
+                ,
+                nullBehavior=nullBehavior,
+                undefinedBehavior=undefinedBehavior,
+                varName=varName,
+                exceptionCode=exceptionCode,
+                normalizeCode=normalizeCode)
 
             if defaultValue is None:
                 return conversionCode
 
             if isinstance(defaultValue, IDLNullValue):
                 assert(type.nullable())
                 defaultCode = "%s.SetIsVoid(true)" % varName
             else:
@@ -5220,20 +5223,24 @@ def getJSToNativeConversionInfo(type, de
             declType=CGGeneric(declType),
             holderType=holderType)
 
     if type.isByteString():
         assert not isEnforceRange and not isClamp
 
         nullable = toStringBool(type.nullable())
 
-        conversionCode = (
-            "if (!ConvertJSValueToByteString(cx, ${val}, %s, ${declName})) {\n"
-            "%s"
-            "}\n" % (nullable, exceptionCodeIndented.define()))
+        conversionCode = fill("""
+            if (!ConvertJSValueToByteString(cx, $${val}, ${nullable}, $${declName})) {
+              $*{exceptionCode}
+            }
+            """,
+            nullable=nullable,
+            exceptionCode=exceptionCode)
+
         # ByteString arguments cannot have a default value.
         assert defaultValue is None
 
         return JSToNativeConversionInfo(
             conversionCode,
             declType=CGGeneric("nsCString"),
             dealWithOptional=isOptional)
 
@@ -5426,37 +5433,57 @@ def getJSToNativeConversionInfo(type, de
         # default value.
         if (not isNullOrUndefined and not isDefinitelyObject and
             defaultValue is not None):
             assert(isinstance(defaultValue, IDLNullValue))
             val = "(${haveValue}) ? ${val} : JS::NullHandleValue"
         else:
             val = "${val}"
 
+        dictLoc = "${declName}"
+        if type.nullable():
+            dictLoc += ".SetValue()"
+
+        conversionCode = fill("""
+            if (!${dictLoc}.Init(cx, ${val},  "${desc}", $${passedToJSImpl})) {
+              $*{exceptionCode}
+            }
+            """,
+            dictLoc=dictLoc,
+            val=val,
+            desc=firstCap(sourceDescription),
+            exceptionCode=exceptionCode)
+
         if failureCode is not None:
             if isDefinitelyObject:
                 dictionaryTest = "IsObjectValueConvertibleToDictionary"
             else:
                 dictionaryTest = "IsConvertibleToDictionary"
-            # Check that the value we have can in fact be converted to
-            # a dictionary, and return failureCode if not.
-            template = CGIfWrapper(
-                CGGeneric(failureCode),
-                "!%s(cx, ${val})" % dictionaryTest).define() + "\n"
-        else:
-            template = ""
-
-        dictLoc = "${declName}"
-        if type.nullable():
-            dictLoc += ".SetValue()"
-
-        template += ('if (!%s.Init(cx, %s, "%s", ${passedToJSImpl})) {\n'
-                     "%s"
-                     "}\n" % (dictLoc, val, firstCap(sourceDescription),
-                              exceptionCodeIndented.define()))
+
+            template = fill("""
+                { // scope for isConvertible
+                  bool isConvertible;
+                  if (!${testConvertible}(cx, ${val}, &isConvertible)) {
+                    $*{exceptionCode}
+                  }
+                  if (!isConvertible) {
+                    $*{failureCode}
+                  }
+
+                  $*{conversionCode}
+                }
+
+                """,
+                testConvertible=dictionaryTest,
+                val=val,
+                exceptionCode=exceptionCode,
+                failureCode=failureCode,
+                conversionCode=conversionCode)
+        else:
+            template = conversionCode
 
         if type.nullable():
             declType = CGTemplatedType("Nullable", declType)
             template = CGIfElseWrapper("${val}.isNullOrUndefined()",
                                        CGGeneric("${declName}.SetNull();\n"),
                                        CGGeneric(template)).define()
 
         # Dictionary arguments that might contain traceable things need to get
@@ -5494,21 +5521,30 @@ def getJSToNativeConversionInfo(type, de
             notDate = ('ThrowErrorMessage(cx, MSG_NOT_DATE, "%s");\n'
                        "%s" % (firstCap(sourceDescription), exceptionCode))
         else:
             notDate = failureCode
 
         conversion = fill(
             """
             JS::Rooted<JSObject*> possibleDateObject(cx, &$${val}.toObject());
-            if (!JS_ObjectIsDate(cx, possibleDateObject) ||
-                !${dateVal}.SetTimeStamp(cx, possibleDateObject)) {
-              $*{notDate}
-            }
-            """,
+            { // scope for isDate
+              bool isDate;
+              if (!JS_ObjectIsDate(cx, possibleDateObject, &isDate)) {
+                $*{exceptionCode}
+              }
+              if (!isDate) {
+                $*{notDate}
+              }
+              if (!${dateVal}.SetTimeStamp(cx, possibleDateObject)) {
+                $*{exceptionCode}
+              }
+            }
+            """,
+            exceptionCode=exceptionCode,
             dateVal=dateVal,
             notDate=notDate)
 
         conversion = wrapObjectTemplate(conversion, type,
                                         "${declName}.SetNull();\n", notDate)
         return JSToNativeConversionInfo(conversion,
                                         declType=declType,
                                         dealWithOptional=isOptional)
@@ -5528,47 +5564,56 @@ def getJSToNativeConversionInfo(type, de
 
     if type.nullable():
         declType = CGGeneric("Nullable<" + typeName + ">")
         writeLoc = "${declName}.SetValue()"
         readLoc = "${declName}.Value()"
         nullCondition = "${val}.isNullOrUndefined()"
         if defaultValue is not None and isinstance(defaultValue, IDLNullValue):
             nullCondition = "!(${haveValue}) || " + nullCondition
-        template = (
-            "if (%s) {\n"
-            "  ${declName}.SetNull();\n"
-            "} else if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
-            "%s"
-            "}\n" % (nullCondition, typeName, conversionBehavior,
-                     writeLoc, exceptionCodeIndented.define()))
+        template = fill("""
+            if (${nullCondition}) {
+              $${declName}.SetNull();
+            } else if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, &${writeLoc})) {
+              $*{exceptionCode}
+            }
+            """,
+            nullCondition=nullCondition,
+            typeName=typeName,
+            conversionBehavior=conversionBehavior,
+            writeLoc=writeLoc,
+            exceptionCode=exceptionCode)
     else:
         assert(defaultValue is None or
                not isinstance(defaultValue, IDLNullValue))
         writeLoc = "${declName}"
         readLoc = writeLoc
-        template = (
-            "if (!ValueToPrimitive<%s, %s>(cx, ${val}, &%s)) {\n"
-            "%s"
-            "}\n" % (typeName, conversionBehavior, writeLoc,
-                     exceptionCodeIndented.define()))
+        template = fill("""
+            if (!ValueToPrimitive<${typeName}, ${conversionBehavior}>(cx, $${val}, &${writeLoc})) {
+              $*{exceptionCode}
+            }
+            """,
+            typeName=typeName,
+            conversionBehavior=conversionBehavior,
+            writeLoc=writeLoc,
+            exceptionCode=exceptionCode)
         declType = CGGeneric(typeName)
 
     if type.isFloat() and not type.isUnrestricted():
         if lenientFloatCode is not None:
             nonFiniteCode = lenientFloatCode
         else:
             nonFiniteCode = ('ThrowErrorMessage(cx, MSG_NOT_FINITE, "%s");\n'
                              "%s" % (firstCap(sourceDescription), exceptionCode))
+
+        # We're appending to an if-block brace, so strip trailing whitespace
+        # and add an extra space before the else.
         template = template.rstrip()
-        template += fill(
-            """
+        template += fill("""
              else if (!mozilla::IsFinite(${readLoc})) {
-              // Note: mozilla::IsFinite will do the right thing
-              //       when passed a non-finite float too.
               $*{nonFiniteCode}
             }
             """,
             readLoc=readLoc,
             nonFiniteCode=nonFiniteCode)
 
     if (defaultValue is not None and
         # We already handled IDLNullValue, so just deal with the other ones
@@ -11890,25 +11935,30 @@ class CGDictionary(CGThing):
 
         if self.dictionary.parent:
             body += fill(
                 """
                 // Per spec, we init the parent's members first
                 if (!${dictName}::Init(cx, val)) {
                   return false;
                 }
-                MOZ_ASSERT(IsConvertibleToDictionary(cx, val));
 
                 """,
                 dictName=self.makeClassName(self.dictionary.parent))
         else:
             body += dedent(
                 """
-                if (!IsConvertibleToDictionary(cx, val)) {
-                  return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription);
+                { // scope for isConvertible
+                  bool isConvertible;
+                  if (!IsConvertibleToDictionary(cx, val, &isConvertible)) {
+                    return false;
+                  }
+                  if (!isConvertible) {
+                    return ThrowErrorMessage(cx, MSG_NOT_DICTIONARY, sourceDescription);
+                  }
                 }
 
                 """)
 
         memberInits = [self.getMemberConversion(m).define()
                        for m in self.memberInfo]
         if memberInits:
             body += fill(
--- a/dom/bindings/Date.cpp
+++ b/dom/bindings/Date.cpp
@@ -15,20 +15,25 @@
 
 namespace mozilla {
 namespace dom {
 
 bool
 Date::SetTimeStamp(JSContext* aCx, JSObject* aObject)
 {
   JS::Rooted<JSObject*> obj(aCx, aObject);
-  MOZ_ASSERT(JS_ObjectIsDate(aCx, obj));
-  double msecs = js::DateGetMsecSinceEpoch(aCx, obj);
+
+  double msecs;
+  if (!js::DateGetMsecSinceEpoch(aCx, obj, &msecs)) {
+    return false;
+  }
+
   JS::ClippedTime time = JS::TimeClip(msecs);
   MOZ_ASSERT(NumbersAreIdentical(msecs, time.toDouble()));
+
   mMsecSinceEpoch = time;
   return true;
 }
 
 bool
 Date::ToDateObject(JSContext* aCx, JS::MutableHandle<JS::Value> aRval) const
 {
   JSObject* obj = JS::NewDateObject(aCx, mMsecSinceEpoch);
--- a/dom/cache/Cache.cpp
+++ b/dom/cache/Cache.cpp
@@ -112,17 +112,18 @@ public:
 
     // Promise::All() passed an array of fetch() Promises should give us
     // an Array of Response objects.  The following code unwraps these
     // JS values back to an nsTArray<nsRefPtr<Response>>.
 
     nsAutoTArray<nsRefPtr<Response>, 256> responseList;
     responseList.SetCapacity(mRequestList.Length());
 
-    if (NS_WARN_IF(!JS_IsArrayObject(aCx, aValue))) {
+    bool isArray;
+    if (NS_WARN_IF(!JS_IsArrayObject(aCx, aValue, &isArray) || !isArray)) {
       Fail();
       return;
     }
 
     JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
 
     uint32_t length;
     if (NS_WARN_IF(!JS_GetArrayLength(aCx, obj, &length))) {
--- a/dom/canvas/WebGLContextUtils.cpp
+++ b/dom/canvas/WebGLContextUtils.cpp
@@ -494,16 +494,18 @@ WebGLContext::GenerateWarning(const char
         return;
     }
 
     dom::AutoJSAPI api;
     if (!api.Init(mCanvasElement->OwnerDoc()->GetScopeObject())) {
         return;
     }
 
+    api.TakeOwnershipOfErrorReporting();
+
     JSContext* cx = api.cx();
     JS_ReportWarning(cx, "WebGL: %s", buf);
     if (!ShouldGenerateWarnings()) {
         JS_ReportWarning(cx,
                          "WebGL: No further warnings will be reported for this"
                          " WebGL context. (already reported %d warnings)",
                          mAlreadyGeneratedWarnings);
     }
--- a/dom/geolocation/nsGeolocationSettings.cpp
+++ b/dom/geolocation/nsGeolocationSettings.cpp
@@ -317,17 +317,18 @@ nsGeolocationSettings::HandleGeolocation
   nsIGlobalObject* global = xpc::NativeGlobal(obj);
   NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
 
   // the spec requires calling getters when accessing array by index
   AutoEntryScript aes(global, "geolocation.always_precise indexing");
   aes.TakeOwnershipOfErrorReporting();
   JSContext *cx = aes.cx();
 
-  if (!JS_IsArrayObject(cx, obj)) {
+  bool isArray;
+  if (!JS_IsArrayObject(cx, obj, &isArray) || !isArray) {
     return;
   }
 
   uint32_t length;
   if (!JS_GetArrayLength(cx, obj, &length)) {
     return;
   }
 
--- a/dom/imptests/failures/html/dom/test_historical.html.json
+++ b/dom/imptests/failures/html/dom/test_historical.html.json
@@ -1,12 +1,11 @@
 {
   "Historical DOM features must be removed: CDATASection": true,
   "Historical DOM features must be removed: createCDATASection": true,
   "Historical DOM features must be removed: createAttribute": true,
   "Historical DOM features must be removed: createAttributeNS": true,
-  "Historical DOM features must be removed: inputEncoding": true,
   "Historical DOM features must be removed: getAttributeNode": true,
   "Historical DOM features must be removed: getAttributeNodeNS": true,
   "Historical DOM features must be removed: setAttributeNode": true,
   "Historical DOM features must be removed: removeAttributeNode": true,
   "DocumentType member must be nuked: internalSubset": true
 }
--- a/dom/imptests/html/dom/test_historical.html
+++ b/dom/imptests/html/dom/test_historical.html
@@ -36,25 +36,23 @@ function isNukedFromDocument(name) {
     assert_equals(doc[name], undefined)
   }, "Historical DOM features must be removed: " + name)
 }
 var documentNuked = [
   "createCDATASection",
   "createAttribute",
   "createAttributeNS",
   "createEntityReference",
-  "inputEncoding",
   "xmlEncoding",
   "xmlStandalone",
   "xmlVersion",
   "strictErrorChecking",
   "domConfig",
   "normalizeDocument",
   "renameNode",
-  "charset",
   "defaultCharset",
   "height",
   "width"
 ]
 documentNuked.forEach(isNukedFromDocument)
 
 test(function() {
   assert_equals(document.implementation["getFeature"], undefined)
--- a/dom/indexedDB/IDBKeyRange.cpp
+++ b/dom/indexedDB/IDBKeyRange.cpp
@@ -102,18 +102,25 @@ IDBKeyRange::FromJSVal(JSContext* aCx,
 
   if (aVal.isNullOrUndefined()) {
     // undefined and null returns no IDBKeyRange.
     keyRange.forget(aKeyRange);
     return NS_OK;
   }
 
   JS::Rooted<JSObject*> obj(aCx, aVal.isObject() ? &aVal.toObject() : nullptr);
-  if (aVal.isPrimitive() || JS_IsArrayObject(aCx, obj) ||
-      JS_ObjectIsDate(aCx, obj)) {
+  bool isValidKey = aVal.isPrimitive();
+  if (!isValidKey) {
+    js::ESClassValue cls;
+    if (!js::GetBuiltinClass(aCx, obj, &cls)) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    isValidKey = cls == js::ESClass_Array || cls == js::ESClass_Date;
+  }
+  if (isValidKey) {
     // A valid key returns an 'only' IDBKeyRange.
     keyRange = new IDBKeyRange(nullptr, false, false, true);
 
     nsresult rv = GetKeyFromJSVal(aCx, aVal, keyRange->Lower());
     if (NS_FAILED(rv)) {
       return rv;
     }
   }
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -996,17 +996,22 @@ IDBObjectStore::AppendIndexUpdateInfo(
     return NS_OK;
   }
 
   JS::Rooted<JS::Value> val(aCx);
   if (NS_FAILED(aKeyPath.ExtractKeyAsJSVal(aCx, aVal, val.address()))) {
     return NS_OK;
   }
 
-  if (JS_IsArrayObject(aCx, val)) {
+  bool isArray;
+  if (!JS_IsArrayObject(aCx, val, &isArray)) {
+    IDB_REPORT_INTERNAL_ERR();
+    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+  }
+  if (isArray) {
     JS::Rooted<JSObject*> array(aCx, &val.toObject());
     uint32_t arrayLength;
     if (NS_WARN_IF(!JS_GetArrayLength(aCx, array, &arrayLength))) {
       IDB_REPORT_INTERNAL_ERR();
       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
     }
 
     for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -226,17 +226,23 @@ Key::EncodeJSValInternal(JSContext* aCx,
       return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
     }
     EncodeNumber(d, eFloat + aTypeOffset);
     return NS_OK;
   }
 
   if (aVal.isObject()) {
     JS::Rooted<JSObject*> obj(aCx, &aVal.toObject());
-    if (JS_IsArrayObject(aCx, obj)) {
+
+    js::ESClassValue cls;
+    if (!js::GetBuiltinClass(aCx, obj, &cls)) {
+      IDB_REPORT_INTERNAL_ERR();
+      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+    }
+    if (cls == js::ESClass_Array) {
       aTypeOffset += eMaxType;
 
       if (aTypeOffset == eMaxType * kMaxArrayCollapse) {
         mBuffer.Append(aTypeOffset);
         aTypeOffset = 0;
       }
       NS_ASSERTION((aTypeOffset % eMaxType) == 0 &&
                    aTypeOffset < (eMaxType * kMaxArrayCollapse),
@@ -264,21 +270,31 @@ Key::EncodeJSValInternal(JSContext* aCx,
         aTypeOffset = 0;
       }
 
       mBuffer.Append(eTerminator + aTypeOffset);
 
       return NS_OK;
     }
 
-    if (JS_ObjectIsDate(aCx, obj)) {
-      if (!js::DateIsValid(aCx, obj))  {
+    if (cls == js::ESClass_Date) {
+      bool valid;
+      if (!js::DateIsValid(aCx, obj, &valid)) {
+        IDB_REPORT_INTERNAL_ERR();
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
+      if (!valid)  {
         return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
       }
-      EncodeNumber(js::DateGetMsecSinceEpoch(aCx, obj), eDate + aTypeOffset);
+      double t;
+      if (!js::DateGetMsecSinceEpoch(aCx, obj, &t)) {
+        IDB_REPORT_INTERNAL_ERR();
+        return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+      }
+      EncodeNumber(t, eDate + aTypeOffset);
       return NS_OK;
     }
   }
 
   return NS_ERROR_DOM_INDEXEDDB_DATA_ERR;
 }
 
 // static
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -18,16 +18,17 @@
 #include "CrashReporterChild.h"
 #include "GeckoProfiler.h"
 #include "TabChild.h"
 
 #include "mozilla/Attributes.h"
 #include "mozilla/LookAndFeel.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/ProcessHangMonitorIPC.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
 #include "mozilla/docshell/OfflineCacheUpdateChild.h"
 #include "mozilla/dom/ContentBridgeChild.h"
 #include "mozilla/dom/ContentBridgeParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/ExternalHelperAppChild.h"
 #include "mozilla/dom/PCrashReporterChild.h"
@@ -1448,16 +1449,30 @@ ContentChild::AllocPHalChild()
 
 bool
 ContentChild::DeallocPHalChild(PHalChild* aHal)
 {
     delete aHal;
     return true;
 }
 
+devtools::PHeapSnapshotTempFileHelperChild*
+ContentChild::AllocPHeapSnapshotTempFileHelperChild()
+{
+    return devtools::HeapSnapshotTempFileHelperChild::Create();
+}
+
+bool
+ContentChild::DeallocPHeapSnapshotTempFileHelperChild(
+    devtools::PHeapSnapshotTempFileHelperChild* aHeapSnapshotHelper)
+{
+    delete aHeapSnapshotHelper;
+    return true;
+}
+
 PIccChild*
 ContentChild::SendPIccConstructor(PIccChild* aActor,
                                   const uint32_t& aServiceId)
 {
     // Add an extra ref for IPDL. Will be released in
     // ContentChild::DeallocPIccChild().
     static_cast<IccChild*>(aActor)->AddRef();
     return PContentChild::SendPIccConstructor(aActor, aServiceId);
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -162,16 +162,19 @@ public:
     AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id,
                              const uint32_t& processType) override;
     virtual bool
     DeallocPCrashReporterChild(PCrashReporterChild*) override;
 
     virtual PHalChild* AllocPHalChild() override;
     virtual bool DeallocPHalChild(PHalChild*) override;
 
+    virtual PHeapSnapshotTempFileHelperChild* AllocPHeapSnapshotTempFileHelperChild() override;
+    virtual bool DeallocPHeapSnapshotTempFileHelperChild(PHeapSnapshotTempFileHelperChild*) override;
+
     PIccChild*
     SendPIccConstructor(PIccChild* aActor, const uint32_t& aServiceId);
     virtual PIccChild*
     AllocPIccChild(const uint32_t& aClientId) override;
     virtual bool
     DeallocPIccChild(PIccChild* aActor) override;
 
     virtual PMemoryReportRequestChild*
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -28,16 +28,17 @@
 #include "AudioChannelService.h"
 #include "BlobParent.h"
 #include "CrashReporterParent.h"
 #include "GMPServiceParent.h"
 #include "IHistory.h"
 #include "imgIContainer.h"
 #include "mozIApplication.h"
 #include "mozilla/ClearOnShutdown.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 #include "mozilla/docshell/OfflineCacheUpdateParent.h"
 #include "mozilla/dom/DataStoreService.h"
 #include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/DOMStorageIPC.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/ExternalHelperAppParent.h"
 #include "mozilla/dom/FileSystemRequestParent.h"
@@ -3604,16 +3605,30 @@ ContentParent::AllocPHalParent()
 
 bool
 ContentParent::DeallocPHalParent(hal_sandbox::PHalParent* aHal)
 {
     delete aHal;
     return true;
 }
 
+devtools::PHeapSnapshotTempFileHelperParent*
+ContentParent::AllocPHeapSnapshotTempFileHelperParent()
+{
+    return devtools::HeapSnapshotTempFileHelperParent::Create();
+}
+
+bool
+ContentParent::DeallocPHeapSnapshotTempFileHelperParent(
+    devtools::PHeapSnapshotTempFileHelperParent* aHeapSnapshotHelper)
+{
+    delete aHeapSnapshotHelper;
+    return true;
+}
+
 PIccParent*
 ContentParent::AllocPIccParent(const uint32_t& aServiceId)
 {
     if (!AssertAppProcessPermission(this, "mobileconnection")) {
         return nullptr;
     }
     IccParent* parent = new IccParent(aServiceId);
     // We release this ref in DeallocPIccParent().
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -343,16 +343,19 @@ public:
                               bool* aSuccess) override;
     virtual bool DeallocPScreenManagerParent(PScreenManagerParent* aActor) override;
 
     virtual PHalParent* AllocPHalParent() override;
     virtual bool RecvPHalConstructor(PHalParent* aActor) override {
         return PContentParent::RecvPHalConstructor(aActor);
     }
 
+    virtual PHeapSnapshotTempFileHelperParent*
+    AllocPHeapSnapshotTempFileHelperParent() override;
+
     virtual PStorageParent* AllocPStorageParent() override;
     virtual bool RecvPStorageConstructor(PStorageParent* aActor) override {
         return PContentParent::RecvPStorageConstructor(aActor);
     }
 
     virtual PJavaScriptParent*
     AllocPJavaScriptParent() override;
     virtual bool
@@ -625,16 +628,18 @@ private:
 
     virtual bool RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI,
                                  const uint32_t& aFlags, bool* aIsSecureURI) override;
 
     virtual bool RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive) override;
 
     virtual bool DeallocPHalParent(PHalParent*) override;
 
+    virtual bool DeallocPHeapSnapshotTempFileHelperParent(PHeapSnapshotTempFileHelperParent*) override;
+
     virtual PIccParent* AllocPIccParent(const uint32_t& aServiceId) override;
     virtual bool DeallocPIccParent(PIccParent* aActor) override;
 
     virtual PMemoryReportRequestParent*
     AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
                                     const bool &aAnonymize,
                                     const bool &aMinimizeMemoryUsage,
                                     const MaybeFileDesc &aDMDFile) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -16,16 +16,17 @@ include protocol PCycleCollectWithLogs;
 include protocol PCrashReporter;
 include protocol PPSMContentDownloader;
 include protocol PExternalHelperApp;
 include protocol PDeviceStorageRequest;
 include protocol PFileDescriptorSet;
 include protocol PFMRadio;
 include protocol PFileSystemRequest;
 include protocol PHal;
+include protocol PHeapSnapshotTempFileHelper;
 include protocol PIcc;
 include protocol PProcessHangMonitor;
 include protocol PImageBridge;
 include protocol PMedia;
 include protocol PMemoryReportRequest;
 include protocol PMobileConnection;
 include protocol PNecko;
 // FIXME This is pretty ridiculous, but we have to keep the order of the
@@ -427,16 +428,17 @@ prio(normal upto urgent) sync protocol P
     manages PCycleCollectWithLogs;
     manages PDeviceStorageRequest;
     manages PFileSystemRequest;
     manages PPSMContentDownloader;
     manages PExternalHelperApp;
     manages PFileDescriptorSet;
     manages PFMRadio;
     manages PHal;
+    manages PHeapSnapshotTempFileHelper;
     manages PIcc;
     manages PMedia;
     manages PMemoryReportRequest;
     manages PMobileConnection;
     manages PNecko;
     manages POfflineCacheUpdate;
     manages PPrinting;
     manages PScreenManager;
@@ -753,16 +755,18 @@ parent:
 
     async AccumulateMixedContentHSTS(URIParams uri, bool active);
 
     sync GetLookAndFeelCache()
         returns (LookAndFeelInt[] lookAndFeelIntCache);
 
     prio(urgent) async PHal();
 
+    async PHeapSnapshotTempFileHelper();
+
     PIcc(uint32_t serviceId);
 
     PMobileConnection(uint32_t clientId);
 
     PNecko();
 
     PPrinting();
 
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -59,17 +59,16 @@ CreateAttributeWarning=Use of document.c
 CreateAttributeNSWarning=Use of document.createAttributeNS() is deprecated. Use element.setAttributeNS() instead.
 OwnerElementWarning=Use of attributes' ownerElement attribute is deprecated.
 NodeValueWarning=Use of attributes' nodeValue attribute is deprecated. Use value instead.
 TextContentWarning=Use of attributes' textContent attribute is deprecated. Use value instead.
 EnablePrivilegeWarning=Use of enablePrivilege is deprecated.  Please use code that runs with the system principal (e.g. an extension) instead.
 nsIJSONDecodeDeprecatedWarning=nsIJSON.decode is deprecated.  Please use JSON.parse instead.
 nsIJSONEncodeDeprecatedWarning=nsIJSON.encode is deprecated.  Please use JSON.stringify instead.
 nsIDOMWindowInternalWarning=Use of nsIDOMWindowInternal is deprecated. Use nsIDOMWindow instead.
-InputEncodingWarning=Use of inputEncoding is deprecated.
 FullScreenDeniedDisabled=Request for full-screen was denied because full-screen API is disabled by user preference.
 FullScreenDeniedFocusedPlugin=Request for full-screen was denied because a windowed plugin is focused.
 FullScreenDeniedHidden=Request for full-screen was denied because the document is no longer visible.
 FullScreenDeniedIframeNotAllowed=Request for full-screen was denied because at least one of the document's containing iframes does not have an "allowfullscreen" attribute.
 FullScreenDeniedNotInputDriven=Request for full-screen was denied because Element.mozRequestFullScreen() was not called from inside a short running user-generated event handler.
 FullScreenDeniedNotInDocument=Request for full-screen was denied because requesting element is no longer in its document.
 FullScreenDeniedMovedDocument=Request for full-screen was denied because requesting element has moved document.
 FullScreenDeniedLostWindow=Request for full-screen was denied because we no longer have a window.
--- a/dom/media/eme/DetailedPromise.cpp
+++ b/dom/media/eme/DetailedPromise.cpp
@@ -11,32 +11,44 @@
 namespace mozilla {
 namespace dom {
 
 DetailedPromise::DetailedPromise(nsIGlobalObject* aGlobal,
                                  const nsACString& aName)
   : Promise(aGlobal)
   , mName(aName)
   , mResponded(false)
+  , mStartTime(TimeStamp::Now())
 {
 }
 
+DetailedPromise::DetailedPromise(nsIGlobalObject* aGlobal,
+                                 const nsACString& aName,
+                                 Telemetry::ID aSuccessLatencyProbe,
+                                 Telemetry::ID aFailureLatencyProbe)
+  : DetailedPromise(aGlobal, aName)
+{
+  mSuccessLatencyProbe.Construct(aSuccessLatencyProbe);
+  mFailureLatencyProbe.Construct(aFailureLatencyProbe);
+}
+
 DetailedPromise::~DetailedPromise()
 {
   MOZ_ASSERT(mResponded == IsPending());
+  MaybeReportTelemetry(Failed);
 }
 
 void
 DetailedPromise::MaybeReject(nsresult aArg, const nsACString& aReason)
 {
   nsPrintfCString msg("%s promise rejected 0x%x '%s'", mName.get(), aArg,
                       PromiseFlatCString(aReason).get());
   EME_LOG(msg.get());
 
-  mResponded = true;
+  MaybeReportTelemetry(Failed);
 
   LogToBrowserConsole(NS_ConvertUTF8toUTF16(msg));
 
   nsRefPtr<DOMException> exception =
     DOMException::Create(aArg, aReason);
   Promise::MaybeRejectBrokenly(exception);
 }
 
@@ -51,10 +63,40 @@ DetailedPromise::Create(nsIGlobalObject*
                         ErrorResult& aRv,
                         const nsACString& aName)
 {
   nsRefPtr<DetailedPromise> promise = new DetailedPromise(aGlobal, aName);
   promise->CreateWrapper(nullptr, aRv);
   return aRv.Failed() ? nullptr : promise.forget();
 }
 
+/* static */ already_AddRefed<DetailedPromise>
+DetailedPromise::Create(nsIGlobalObject* aGlobal,
+                        ErrorResult& aRv,
+                        const nsACString& aName,
+                        Telemetry::ID aSuccessLatencyProbe,
+                        Telemetry::ID aFailureLatencyProbe)
+{
+  nsRefPtr<DetailedPromise> promise = new DetailedPromise(aGlobal, aName, aSuccessLatencyProbe, aFailureLatencyProbe);
+  promise->CreateWrapper(nullptr, aRv);
+  return aRv.Failed() ? nullptr : promise.forget();
+}
+
+void
+DetailedPromise::MaybeReportTelemetry(Status aStatus)
+{
+  if (mResponded) {
+    return;
+  }
+  mResponded = true;
+  if (!mSuccessLatencyProbe.WasPassed() || !mFailureLatencyProbe.WasPassed()) {
+    return;
+  }
+  uint32_t latency = (TimeStamp::Now() - mStartTime).ToMilliseconds();
+  EME_LOG("%s %s latency %ums reported via telemetry", mName.get(),
+          ((aStatus == Succeeded) ? "succcess" : "failure"), latency);
+  Telemetry::ID tid = (aStatus == Succeeded) ? mSuccessLatencyProbe.Value()
+                                             : mFailureLatencyProbe.Value();
+  Telemetry::Accumulate(tid, latency);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/eme/DetailedPromise.h
+++ b/dom/media/eme/DetailedPromise.h
@@ -3,50 +3,71 @@
 /* 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 __DetailedPromise_h__
 #define __DetailedPromise_h__
 
 #include "mozilla/dom/Promise.h"
+#include "mozilla/Telemetry.h"
 #include "EMEUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 /*
  * This is pretty horrible; bug 1160445.
  * Extend Promise to add custom DOMException messages on rejection.
  * Get rid of this once we've ironed out EME errors in the wild.
  */
 class DetailedPromise : public Promise
 {
 public:
   static already_AddRefed<DetailedPromise>
-  Create(nsIGlobalObject* aGlobal, ErrorResult& aRv, const nsACString& aName);
+  Create(nsIGlobalObject* aGlobal,
+         ErrorResult& aRv,
+         const nsACString& aName);
+
+  static already_AddRefed<DetailedPromise>
+  Create(nsIGlobalObject* aGlobal, ErrorResult& aRv,
+         const nsACString& aName,
+         Telemetry::ID aSuccessLatencyProbe,
+         Telemetry::ID aFailureLatencyProbe);
 
   template <typename T>
   void MaybeResolve(const T& aArg)
   {
     EME_LOG("%s promise resolved", mName.get());
-    mResponded = true;
+    MaybeReportTelemetry(Succeeded);
     Promise::MaybeResolve<T>(aArg);
   }
 
   void MaybeReject(nsresult aArg) = delete;
   void MaybeReject(nsresult aArg, const nsACString& aReason);
 
   void MaybeReject(ErrorResult& aArg) = delete;
   void MaybeReject(ErrorResult&, const nsACString& aReason);
 
 private:
-  explicit DetailedPromise(nsIGlobalObject* aGlobal, const nsACString& aName);
+  explicit DetailedPromise(nsIGlobalObject* aGlobal,
+                           const nsACString& aName);
+
+  explicit DetailedPromise(nsIGlobalObject* aGlobal,
+                           const nsACString& aName,
+                           Telemetry::ID aSuccessLatencyProbe,
+                           Telemetry::ID aFailureLatencyProbe);
   virtual ~DetailedPromise();
 
+  enum Status { Succeeded, Failed };
+  void MaybeReportTelemetry(Status aStatus);
+
   nsCString mName;
   bool mResponded;
+  TimeStamp mStartTime;
+  Optional<Telemetry::ID> mSuccessLatencyProbe;
+  Optional<Telemetry::ID> mFailureLatencyProbe;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // __DetailedPromise_h__
--- a/dom/media/platforms/agnostic/BlankDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/BlankDecoderModule.cpp
@@ -29,21 +29,21 @@ public:
                         TrackInfo::TrackType aType)
     : mCreator(aCreator)
     , mTaskQueue(aTaskQueue)
     , mCallback(aCallback)
     , mType(aType)
   {
   }
 
-  virtual nsRefPtr<InitPromise> Init() override {
+  nsRefPtr<InitPromise> Init() override {
     return InitPromise::CreateAndResolve(mType, __func__);
   }
 
-  virtual nsresult Shutdown() override {
+  nsresult Shutdown() override {
     return NS_OK;
   }
 
   class OutputEvent : public nsRunnable {
   public:
     OutputEvent(MediaRawData* aSample,
                 MediaDataDecoderCallback* aCallback,
                 BlankMediaDataCreator* aCreator)
@@ -62,32 +62,32 @@ public:
       return NS_OK;
     }
   private:
     nsRefPtr<MediaRawData> mSample;
     BlankMediaDataCreator* mCreator;
     MediaDataDecoderCallback* mCallback;
   };
 
-  virtual nsresult Input(MediaRawData* aSample) override
+  nsresult Input(MediaRawData* aSample) override
   {
     // The MediaDataDecoder must delete the sample when we're finished
     // with it, so the OutputEvent stores it in an nsAutoPtr and deletes
     // it once it's run.
     RefPtr<nsIRunnable> r(new OutputEvent(aSample, mCallback, mCreator));
     mTaskQueue->Dispatch(r.forget());
     return NS_OK;
   }
 
-  virtual nsresult Flush() override {
+  nsresult Flush() override {
     mTaskQueue->Flush();
     return NS_OK;
   }
 
-  virtual nsresult Drain() override {
+  nsresult Drain() override {
     mCallback->DrainComplete();
     return NS_OK;
   }
 
 private:
   nsAutoPtr<BlankMediaDataCreator> mCreator;
   RefPtr<FlushableTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
@@ -208,60 +208,60 @@ private:
   uint32_t mChannelCount;
   uint32_t mSampleRate;
 };
 
 class BlankDecoderModule : public PlatformDecoderModule {
 public:
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override {
     BlankVideoDataCreator* creator = new BlankVideoDataCreator(
       aConfig.mDisplay.width, aConfig.mDisplay.height, aImageContainer);
     nsRefPtr<MediaDataDecoder> decoder =
       new BlankMediaDataDecoder<BlankVideoDataCreator>(creator,
                                                        aVideoTaskQueue,
                                                        aCallback,
                                                        TrackInfo::kVideoTrack);
     return decoder.forget();
   }
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override {
     BlankAudioDataCreator* creator = new BlankAudioDataCreator(
       aConfig.mChannels, aConfig.mRate);
 
     nsRefPtr<MediaDataDecoder> decoder =
       new BlankMediaDataDecoder<BlankAudioDataCreator>(creator,
                                                        aAudioTaskQueue,
                                                        aCallback,
                                                        TrackInfo::kAudioTrack);
     return decoder.forget();
   }
 
-  virtual bool
+  bool
   SupportsMimeType(const nsACString& aMimeType) override
   {
     return true;
   }
 
-  virtual bool
+  bool
   SupportsSharedDecoders(const VideoInfo& aConfig) const override {
     return false;
   }
 
-  virtual ConversionRequired
+  ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override
   {
     return kNeedNone;
   }
 
 };
 
 class AgnosticDecoderModule : public BlankDecoderModule {
--- a/dom/media/platforms/agnostic/eme/EMEAudioDecoder.h
+++ b/dom/media/platforms/agnostic/eme/EMEAudioDecoder.h
@@ -13,32 +13,32 @@
 namespace mozilla {
 
 class EMEAudioCallbackAdapter : public AudioCallbackAdapter {
 public:
   explicit EMEAudioCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback)
    : AudioCallbackAdapter(aCallback)
   {}
 
-  virtual void Error(GMPErr aErr) override;
+  void Error(GMPErr aErr) override;
 };
 
 class EMEAudioDecoder : public GMPAudioDecoder {
 public:
   EMEAudioDecoder(CDMProxy* aProxy,
                   const AudioInfo& aConfig,
                   TaskQueue* aTaskQueue,
                   MediaDataDecoderCallbackProxy* aCallback)
    : GMPAudioDecoder(aConfig, aTaskQueue, aCallback, new EMEAudioCallbackAdapter(aCallback))
    , mProxy(aProxy)
   {
   }
 
 private:
-  virtual void InitTags(nsTArray<nsCString>& aTags) override;
-  virtual nsCString GetNodeId() override;
+  void InitTags(nsTArray<nsCString>& aTags) override;
+  nsCString GetNodeId() override;
 
   nsRefPtr<CDMProxy> mProxy;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.cpp
@@ -40,22 +40,22 @@ public:
     , mCallback(aCallback)
     , mTaskQueue(aDecodeTaskQueue)
     , mProxy(aProxy)
     , mSamplesWaitingForKey(new SamplesWaitingForKey(this, mTaskQueue, mProxy))
     , mIsShutdown(false)
   {
   }
 
-  virtual nsRefPtr<InitPromise> Init() override {
+  nsRefPtr<InitPromise> Init() override {
     MOZ_ASSERT(!mIsShutdown);
     return mDecoder->Init();
   }
 
-  virtual nsresult Input(MediaRawData* aSample) override {
+  nsresult Input(MediaRawData* aSample) override {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
     MOZ_ASSERT(!mIsShutdown);
     if (mSamplesWaitingForKey->WaitIfKeyNotUsable(aSample)) {
       return NS_OK;
     }
 
     nsAutoPtr<MediaRawDataWriter> writer(aSample->CreateWriter());
     mProxy->GetSessionIdsForKeyId(aSample->mCrypto.mKeyId,
@@ -99,36 +99,36 @@ public:
       }
     } else {
       MOZ_ASSERT(!mIsShutdown);
       nsresult rv = mDecoder->Input(aDecrypted.mSample);
       unused << NS_WARN_IF(NS_FAILED(rv));
     }
   }
 
-  virtual nsresult Flush() override {
+  nsresult Flush() override {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
     MOZ_ASSERT(!mIsShutdown);
     mDecrypts.Enumerate(&DropDecryptPromises, nullptr);
     nsresult rv = mDecoder->Flush();
     unused << NS_WARN_IF(NS_FAILED(rv));
     mSamplesWaitingForKey->Flush();
     return rv;
   }
 
-  virtual nsresult Drain() override {
+  nsresult Drain() override {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
     MOZ_ASSERT(!mIsShutdown);
     mDecrypts.Enumerate(&DropDecryptPromises, nullptr);
     nsresult rv = mDecoder->Drain();
     unused << NS_WARN_IF(NS_FAILED(rv));
     return rv;
   }
 
-  virtual nsresult Shutdown() override {
+  nsresult Shutdown() override {
     MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
     MOZ_ASSERT(!mIsShutdown);
     mIsShutdown = true;
     nsresult rv = mDecoder->Shutdown();
     unused << NS_WARN_IF(NS_FAILED(rv));
     mSamplesWaitingForKey->BreakCycles();
     mSamplesWaitingForKey = nullptr;
     mDecoder = nullptr;
@@ -152,18 +152,18 @@ class EMEMediaDataDecoderProxy : public 
 public:
   EMEMediaDataDecoderProxy(nsIThread* aProxyThread, MediaDataDecoderCallback* aCallback, CDMProxy* aProxy, FlushableTaskQueue* aTaskQueue)
    : MediaDataDecoderProxy(aProxyThread, aCallback)
    , mSamplesWaitingForKey(new SamplesWaitingForKey(this, aTaskQueue, aProxy))
    , mProxy(aProxy)
   {
   }
 
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Shutdown() override;
+  nsresult Input(MediaRawData* aSample) override;
+  nsresult Shutdown() override;
 
 private:
   nsRefPtr<SamplesWaitingForKey> mSamplesWaitingForKey;
   nsRefPtr<CDMProxy> mProxy;
 };
 
 nsresult
 EMEMediaDataDecoderProxy::Input(MediaRawData* aSample)
--- a/dom/media/platforms/agnostic/eme/EMEDecoderModule.h
+++ b/dom/media/platforms/agnostic/eme/EMEDecoderModule.h
@@ -21,30 +21,30 @@ public:
   EMEDecoderModule(CDMProxy* aProxy,
                    PlatformDecoderModule* aPDM,
                    bool aCDMDecodesAudio,
                    bool aCDMDecodesVideo);
 
   virtual ~EMEDecoderModule();
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                     layers::LayersBackend aLayersBackend,
                     layers::ImageContainer* aImageContainer,
                     FlushableTaskQueue* aVideoTaskQueue,
                     MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual ConversionRequired
+  ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 
 private:
   nsRefPtr<CDMProxy> mProxy;
   // Will be null if CDM has decoding capability.
   nsRefPtr<PlatformDecoderModule> mPDM;
   // We run the PDM on its own task queue.
   nsRefPtr<TaskQueue> mTaskQueue;
--- a/dom/media/platforms/agnostic/eme/EMEVideoDecoder.h
+++ b/dom/media/platforms/agnostic/eme/EMEVideoDecoder.h
@@ -18,17 +18,17 @@ class TaskQueue;
 class EMEVideoCallbackAdapter : public VideoCallbackAdapter {
 public:
   EMEVideoCallbackAdapter(MediaDataDecoderCallbackProxy* aCallback,
                           VideoInfo aVideoInfo,
                           layers::ImageContainer* aImageContainer)
    : VideoCallbackAdapter(aCallback, aVideoInfo, aImageContainer)
   {}
 
-  virtual void Error(GMPErr aErr) override;
+  void Error(GMPErr aErr) override;
 };
 
 class EMEVideoDecoder : public GMPVideoDecoder {
 public:
   EMEVideoDecoder(CDMProxy* aProxy,
                   const VideoInfo& aConfig,
                   layers::LayersBackend aLayersBackend,
                   layers::ImageContainer* aImageContainer,
@@ -43,18 +43,18 @@ public:
                                                  VideoInfo(aConfig.mDisplay.width,
                                                            aConfig.mDisplay.height),
                                                  aImageContainer))
    , mProxy(aProxy)
   {
   }
 
 private:
-  virtual void InitTags(nsTArray<nsCString>& aTags) override;
-  virtual nsCString GetNodeId() override;
-  virtual GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(MediaRawData* aSample) override;
+  void InitTags(nsTArray<nsCString>& aTags) override;
+  nsCString GetNodeId() override;
+  GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(MediaRawData* aSample) override;
 
   nsRefPtr<CDMProxy> mProxy;
 };
 
 } // namespace mozilla
 
 #endif // EMEVideoDecoder_h_
--- a/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPAudioDecoder.h
@@ -20,22 +20,22 @@ public:
    : mCallback(aCallback)
    , mLastStreamOffset(0)
    , mAudioFrameSum(0)
    , mAudioFrameOffset(0)
    , mMustRecaptureAudioPosition(true)
   {}
 
   // GMPAudioDecoderCallbackProxy
-  virtual void Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp, uint32_t aChannels, uint32_t aRate) override;
-  virtual void InputDataExhausted() override;
-  virtual void DrainComplete() override;
-  virtual void ResetComplete() override;
-  virtual void Error(GMPErr aErr) override;
-  virtual void Terminated() override;
+  void Decoded(const nsTArray<int16_t>& aPCM, uint64_t aTimeStamp, uint32_t aChannels, uint32_t aRate) override;
+  void InputDataExhausted() override;
+  void DrainComplete() override;
+  void ResetComplete() override;
+  void Error(GMPErr aErr) override;
+  void Terminated() override;
 
   void SetLastStreamOffset(int64_t aStreamOffset) {
     mLastStreamOffset = aStreamOffset;
   }
 
 private:
   MediaDataDecoderCallbackProxy* mCallback;
   int64_t mLastStreamOffset;
@@ -64,21 +64,21 @@ public:
                   MediaDataDecoderCallbackProxy* aCallback)
    : mConfig(aConfig)
    , mCallback(aCallback)
    , mGMP(nullptr)
    , mAdapter(new AudioCallbackAdapter(aCallback))
   {
   }
 
-  virtual nsRefPtr<InitPromise> Init() override;
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override;
-  virtual nsresult Shutdown() override;
+  nsRefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
 
 private:
   class GMPInitDoneRunnable : public nsRunnable
   {
@@ -118,17 +118,17 @@ private:
   public:
     GMPInitDoneCallback(GMPAudioDecoder* aDecoder,
                         GMPInitDoneRunnable* aGMPInitDone)
       : mDecoder(aDecoder)
       , mGMPInitDone(aGMPInitDone)
     {
     }
 
-    virtual void Done(GMPAudioDecoderProxy* aGMP)
+    void Done(GMPAudioDecoderProxy* aGMP) override
     {
       if (aGMP) {
         mDecoder->GMPInitDone(aGMP);
       }
       mGMPInitDone->Dispatch();
     }
 
   private:
--- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
+++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
@@ -13,28 +13,28 @@ namespace mozilla {
 
 class GMPDecoderModule : public PlatformDecoderModule {
 public:
   GMPDecoderModule();
 
   virtual ~GMPDecoderModule();
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual ConversionRequired
+  ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 };
 
 } // namespace mozilla
 
 #endif // GMPDecoderModule_h_
--- a/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
+++ b/dom/media/platforms/agnostic/gmp/GMPVideoDecoder.h
@@ -23,24 +23,24 @@ public:
                        layers::ImageContainer* aImageContainer)
    : mCallback(aCallback)
    , mLastStreamOffset(0)
    , mVideoInfo(aVideoInfo)
    , mImageContainer(aImageContainer)
   {}
 
   // GMPVideoDecoderCallbackProxy
-  virtual void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
-  virtual void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) override;
-  virtual void ReceivedDecodedFrame(const uint64_t aPictureId) override;
-  virtual void InputDataExhausted() override;
-  virtual void DrainComplete() override;
-  virtual void ResetComplete() override;
-  virtual void Error(GMPErr aErr) override;
-  virtual void Terminated() override;
+  void Decoded(GMPVideoi420Frame* aDecodedFrame) override;
+  void ReceivedDecodedReferenceFrame(const uint64_t aPictureId) override;
+  void ReceivedDecodedFrame(const uint64_t aPictureId) override;
+  void InputDataExhausted() override;
+  void DrainComplete() override;
+  void ResetComplete() override;
+  void Error(GMPErr aErr) override;
+  void Terminated() override;
 
   void SetLastStreamOffset(int64_t aStreamOffset) {
     mLastStreamOffset = aStreamOffset;
   }
 
 private:
   MediaDataDecoderCallbackProxy* mCallback;
   int64_t mLastStreamOffset;
@@ -79,21 +79,21 @@ public:
    , mAdapter(new VideoCallbackAdapter(aCallback,
                                        VideoInfo(aConfig.mDisplay.width,
                                                  aConfig.mDisplay.height),
                                        aImageContainer))
    , mConvertNALUnitLengths(false)
   {
   }
 
-  virtual nsRefPtr<InitPromise> Init() override;
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override;
-  virtual nsresult Shutdown() override;
+  nsRefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
 
 protected:
   virtual void InitTags(nsTArray<nsCString>& aTags);
   virtual nsCString GetNodeId();
   virtual GMPUniquePtr<GMPVideoEncodedFrame> CreateFrame(MediaRawData* aSample);
 
 private:
   class GMPInitDoneRunnable : public nsRunnable
@@ -134,17 +134,17 @@ private:
   public:
     GMPInitDoneCallback(GMPVideoDecoder* aDecoder,
                         GMPInitDoneRunnable* aGMPInitDone)
       : mDecoder(aDecoder)
       , mGMPInitDone(aGMPInitDone)
     {
     }
 
-    virtual void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost)
+    void Done(GMPVideoDecoderProxy* aGMP, GMPVideoHost* aHost) override
     {
       if (aGMP) {
         mDecoder->GMPInitDone(aGMP, aHost);
       }
       mGMPInitDone->Dispatch();
     }
 
   private:
--- a/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.h
+++ b/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.h
@@ -63,37 +63,37 @@ class MediaDataDecoderProxy;
 class MediaDataDecoderCallbackProxy : public MediaDataDecoderCallback {
 public:
   MediaDataDecoderCallbackProxy(MediaDataDecoderProxy* aProxyDecoder, MediaDataDecoderCallback* aCallback)
    : mProxyDecoder(aProxyDecoder)
    , mProxyCallback(aCallback)
   {
   }
 
-  virtual void Output(MediaData* aData) override {
+  void Output(MediaData* aData) override {
     mProxyCallback->Output(aData);
   }
 
-  virtual void Error() override;
+  void Error() override;
 
-  virtual void InputExhausted() override {
+  void InputExhausted() override {
     mProxyCallback->InputExhausted();
   }
 
-  virtual void DrainComplete() override {
+  void DrainComplete() override {
     mProxyCallback->DrainComplete();
   }
 
-  virtual void ReleaseMediaResources() override {
+  void ReleaseMediaResources() override {
     mProxyCallback->ReleaseMediaResources();
   }
 
-  virtual void FlushComplete();
+  void FlushComplete();
 
-  virtual bool OnReaderTaskQueue() override
+  bool OnReaderTaskQueue() override
   {
     return mProxyCallback->OnReaderTaskQueue();
   }
 
 private:
   MediaDataDecoderProxy* mProxyDecoder;
   MediaDataDecoderCallback* mProxyCallback;
 };
@@ -127,21 +127,21 @@ public:
     MOZ_ASSERT(aProxyDecoder);
     mProxyDecoder = aProxyDecoder;
   }
 
   // These are called from the decoder thread pool.
   // Init and Shutdown run synchronously on the proxy thread, all others are
   // asynchronously and responded to via the MediaDataDecoderCallback.
   // Note: the nsresults returned by the proxied decoder are lost.
-  virtual nsRefPtr<InitPromise> Init() override;
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override;
-  virtual nsresult Shutdown() override;
+  nsRefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
 
   // Called by MediaDataDecoderCallbackProxy.
   void FlushComplete();
 
 private:
   nsRefPtr<InitPromise> InternalInit();
 
 #ifdef DEBUG
--- a/dom/media/platforms/android/AndroidDecoderModule.cpp
+++ b/dom/media/platforms/android/AndroidDecoderModule.cpp
@@ -66,17 +66,17 @@ public:
 
     return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
   }
 
   void Cleanup() override {
     mGLContext = nullptr;
   }
 
-  virtual nsresult Input(MediaRawData* aSample) override {
+  nsresult Input(MediaRawData* aSample) override {
     return MediaCodecDataDecoder::Input(aSample);
   }
 
   bool WantCopy() {
     // Allocating a texture is incredibly slow on PowerVR
     return mGLContext->Vendor() != GLVendor::Imagination;
   }
 
@@ -104,18 +104,18 @@ public:
     EGLImage eglImage = sEGLLibrary.fCreateImage(EGL_DISPLAY(), eglContext,
                                                  LOCAL_EGL_GL_TEXTURE_2D_KHR,
                                                  (EGLClientBuffer)tex, attribs);
     mGLContext->fDeleteTextures(1, &tex);
 
     return eglImage;
   }
 
-  virtual nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat,
-                              const media::TimeUnit& aDuration) override {
+  nsresult PostOutput(BufferInfo::Param aInfo, MediaFormat::Param aFormat,
+                      const media::TimeUnit& aDuration) override {
     if (!EnsureGLContext()) {
       return NS_ERROR_FAILURE;
     }
 
     nsRefPtr<layers::Image> img = mImageContainer->CreateImage(ImageFormat::SURFACE_TEXTURE);
     layers::SurfaceTextureImage::Data data;
     data.mSurfTex = mSurfaceTexture.get();
     data.mSize = mConfig.mDisplay;
--- a/dom/media/platforms/android/AndroidDecoderModule.h
+++ b/dom/media/platforms/android/AndroidDecoderModule.h
@@ -15,53 +15,53 @@
 #include <queue>
 
 namespace mozilla {
 
 typedef std::queue<nsRefPtr<MediaRawData>> SampleQueue;
 
 class AndroidDecoderModule : public PlatformDecoderModule {
 public:
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
 
   AndroidDecoderModule() {}
   virtual ~AndroidDecoderModule() {}
 
-  virtual bool SupportsMimeType(const nsACString& aMimeType) override;
+  bool SupportsMimeType(const nsACString& aMimeType) override;
 
-  virtual ConversionRequired
+  ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 };
 
 class MediaCodecDataDecoder : public MediaDataDecoder {
 public:
 
   MediaCodecDataDecoder(MediaData::Type aType,
                         const nsACString& aMimeType,
                         widget::sdk::MediaFormat::Param aFormat,
                         MediaDataDecoderCallback* aCallback);
 
   virtual ~MediaCodecDataDecoder();
 
-  virtual nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override;
-  virtual nsresult Shutdown() override;
-  virtual nsresult Input(MediaRawData* aSample);
+  nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
+  nsresult Input(MediaRawData* aSample) override;
 
 protected:
   friend class AndroidDecoderModule;
 
   MediaData::Type mType;
 
   nsAutoCString mMimeType;
   widget::sdk::MediaFormat::GlobalRef mFormat;
--- a/dom/media/platforms/apple/AppleATDecoder.h
+++ b/dom/media/platforms/apple/AppleATDecoder.h
@@ -20,21 +20,21 @@ class MediaDataDecoderCallback;
 
 class AppleATDecoder : public MediaDataDecoder {
 public:
   AppleATDecoder(const AudioInfo& aConfig,
                  FlushableTaskQueue* aVideoTaskQueue,
                  MediaDataDecoderCallback* aCallback);
   virtual ~AppleATDecoder();
 
-  virtual nsRefPtr<InitPromise> Init() override;
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override;
-  virtual nsresult Shutdown() override;
+  nsRefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
 
   // Callbacks also need access to the config.
   const AudioInfo& mConfig;
 
   // Use to extract magic cookie for HE-AAC detection.
   nsTArray<uint8_t> mMagicCookie;
   // Will be set to true should an error occurred while attempting to retrieve
   // the magic cookie property.
--- a/dom/media/platforms/apple/AppleDecoderModule.h
+++ b/dom/media/platforms/apple/AppleDecoderModule.h
@@ -11,35 +11,35 @@
 
 namespace mozilla {
 
 class AppleDecoderModule : public PlatformDecoderModule {
 public:
   AppleDecoderModule();
   virtual ~AppleDecoderModule();
 
-  virtual nsresult Startup() override;
+  nsresult Startup() override;
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual bool SupportsMimeType(const nsACString& aMimeType) override;
+  bool SupportsMimeType(const nsACString& aMimeType) override;
 
-  virtual ConversionRequired
+  ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 
   static void Init();
 
 private:
   static bool sInitialized;
   static bool sIsVTAvailable;
   static bool sIsVTHWAvailable;
--- a/dom/media/platforms/apple/AppleVDADecoder.h
+++ b/dom/media/platforms/apple/AppleVDADecoder.h
@@ -66,22 +66,22 @@ public:
     MediaDataDecoderCallback* aCallback,
     layers::ImageContainer* aImageContainer);
 
   AppleVDADecoder(const VideoInfo& aConfig,
                   FlushableTaskQueue* aVideoTaskQueue,
                   MediaDataDecoderCallback* aCallback,
                   layers::ImageContainer* aImageContainer);
   virtual ~AppleVDADecoder();
-  virtual nsRefPtr<InitPromise> Init() override;
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override;
-  virtual nsresult Shutdown() override;
-  virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override
+  nsRefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
+  bool IsHardwareAccelerated(nsACString& aFailureReason) const override
   {
     return true;
   }
 
   // Access from the taskqueue and the decoder's thread.
   // OutputFrame is thread-safe.
   nsresult OutputFrame(CVPixelBufferRef aImage,
                        AppleFrameRef aFrameRef);
--- a/dom/media/platforms/apple/AppleVTDecoder.h
+++ b/dom/media/platforms/apple/AppleVTDecoder.h
@@ -15,19 +15,19 @@ namespace mozilla {
 
 class AppleVTDecoder : public AppleVDADecoder {
 public:
   AppleVTDecoder(const VideoInfo& aConfig,
                  FlushableTaskQueue* aVideoTaskQueue,
                  MediaDataDecoderCallback* aCallback,
                  layers::ImageContainer* aImageContainer);
   virtual ~AppleVTDecoder();
-  virtual nsRefPtr<InitPromise> Init() override;
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override
+  nsRefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  bool IsHardwareAccelerated(nsACString& aFailureReason) const override
   {
     return mIsHardwareAccelerated;
   }
 
 protected:
   void ProcessFlush() override;
   void ProcessDrain() override;
   void ProcessShutdown() override;
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -13,18 +13,17 @@
 #define MAX_CHANNELS 16
 
 namespace mozilla
 {
 
 FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const AudioInfo& aConfig)
-  : FFmpegDataDecoder(aTaskQueue, GetCodecId(aConfig.mMimeType))
-  , mCallback(aCallback)
+  : FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
 {
   MOZ_COUNT_CTOR(FFmpegAudioDecoder);
   // Use a new MediaByteBuffer as the object will be modified during initialization.
   mExtraData = new MediaByteBuffer;
   mExtraData->AppendElements(*aConfig.mCodecSpecificConfig);
 }
 
 nsRefPtr<MediaDataDecoder::InitPromise>
@@ -80,16 +79,17 @@ CopyAndPackAudio(AVFrame* aFrame, uint32
   }
 
   return audio.forget();
 }
 
 void
 FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   AVPacket packet;
   av_init_packet(&packet);
 
   packet.data = const_cast<uint8_t*>(aSample->Data());
   packet.size = aSample->Size();
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg audio decoder failed to allocate frame.");
@@ -155,22 +155,22 @@ nsresult
 FFmpegAudioDecoder<LIBAV_VER>::Input(MediaRawData* aSample)
 {
   nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableMethodWithArg<nsRefPtr<MediaRawData>>(
     this, &FFmpegAudioDecoder::DecodePacket, nsRefPtr<MediaRawData>(aSample)));
   mTaskQueue->Dispatch(runnable.forget());
   return NS_OK;
 }
 
-nsresult
-FFmpegAudioDecoder<LIBAV_VER>::Drain()
+void
+FFmpegAudioDecoder<LIBAV_VER>::ProcessDrain()
 {
-  mTaskQueue->AwaitIdle();
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  ProcessFlush();
   mCallback->DrainComplete();
-  return Flush();
 }
 
 AVCodecID
 FFmpegAudioDecoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
 {
   if (aMimeType.EqualsLiteral("audio/mpeg")) {
     return AV_CODEC_ID_MP3;
   }
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
@@ -20,22 +20,20 @@ template <>
 class FFmpegAudioDecoder<LIBAV_VER> : public FFmpegDataDecoder<LIBAV_VER>
 {
 public:
   FFmpegAudioDecoder(FlushableTaskQueue* aTaskQueue,
                      MediaDataDecoderCallback* aCallback,
                      const AudioInfo& aConfig);
   virtual ~FFmpegAudioDecoder();
 
-  virtual nsRefPtr<InitPromise> Init() override;
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Drain() override;
+  nsRefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  void ProcessDrain() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   void DecodePacket(MediaRawData* aSample);
-
-  MediaDataDecoderCallback* mCallback;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegAACDecoder_h__
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
@@ -8,30 +8,35 @@
 
 #include <string.h>
 #include <unistd.h>
 
 #include "FFmpegLibs.h"
 #include "FFmpegLog.h"
 #include "FFmpegDataDecoder.h"
 #include "prsystem.h"
+#include "FFmpegDecoderModule.h"
 
 namespace mozilla
 {
 
 bool FFmpegDataDecoder<LIBAV_VER>::sFFmpegInitDone = false;
 StaticMutex FFmpegDataDecoder<LIBAV_VER>::sMonitor;
 
 FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue,
+                                                MediaDataDecoderCallback* aCallback,
                                                 AVCodecID aCodecID)
   : mTaskQueue(aTaskQueue)
+  , mCallback(aCallback)
   , mCodecContext(nullptr)
   , mFrame(NULL)
   , mExtraData(nullptr)
   , mCodecID(aCodecID)
+  , mMonitor("FFMpegaDataDecoder")
+  , mIsFlushing(false)
 {
   MOZ_COUNT_CTOR(FFmpegDataDecoder);
 }
 
 FFmpegDataDecoder<LIBAV_VER>::~FFmpegDataDecoder()
 {
   MOZ_COUNT_DTOR(FFmpegDataDecoder);
 }
@@ -81,17 +86,21 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
   if (!(mCodecContext = avcodec_alloc_context3(codec))) {
     NS_WARNING("Couldn't init ffmpeg context");
     return NS_ERROR_FAILURE;
   }
 
   mCodecContext->opaque = this;
 
   // FFmpeg takes this as a suggestion for what format to use for audio samples.
-  mCodecContext->request_sample_fmt = AV_SAMPLE_FMT_FLT;
+  uint32_t major, minor;
+  FFmpegDecoderModule<LIBAV_VER>::GetVersion(major, minor);
+  // LibAV 0.8 produces rubbish float interlaved samples, request 16 bits audio.
+  mCodecContext->request_sample_fmt = major == 53 && minor <= 34 ?
+    AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLT;
 
   // FFmpeg will call back to this to negotiate a video pixel format.
   mCodecContext->get_format = ChoosePixelFormat;
 
   mCodecContext->thread_count = PR_GetNumberOfProcessors();
   mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
   mCodecContext->thread_safe_callbacks = false;
 
@@ -123,46 +132,89 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
     return NS_ERROR_FAILURE;
   }
 
   FFMPEG_LOG("FFmpeg init successful.");
   return NS_OK;
 }
 
 nsresult
-FFmpegDataDecoder<LIBAV_VER>::Flush()
+FFmpegDataDecoder<LIBAV_VER>::Shutdown()
 {
-  mTaskQueue->Flush();
-  avcodec_flush_buffers(mCodecContext);
+  if (mTaskQueue) {
+    nsCOMPtr<nsIRunnable> runnable =
+      NS_NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown);
+    mTaskQueue->Dispatch(runnable.forget());
+  } else {
+    ProcessShutdown();
+  }
   return NS_OK;
 }
 
 nsresult
-FFmpegDataDecoder<LIBAV_VER>::Shutdown()
+FFmpegDataDecoder<LIBAV_VER>::Flush()
+{
+  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
+  mIsFlushing = true;
+  mTaskQueue->Flush();
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessFlush);
+  MonitorAutoLock mon(mMonitor);
+  mTaskQueue->Dispatch(runnable.forget());
+  while (mIsFlushing) {
+    mon.Wait();
+  }
+  return NS_OK;
+}
+
+nsresult
+FFmpegDataDecoder<LIBAV_VER>::Drain()
+{
+  MOZ_ASSERT(mCallback->OnReaderTaskQueue());
+  nsCOMPtr<nsIRunnable> runnable =
+    NS_NewRunnableMethod(this, &FFmpegDataDecoder<LIBAV_VER>::ProcessDrain);
+  mTaskQueue->Dispatch(runnable.forget());
+  return NS_OK;
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::ProcessFlush()
+{
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+  if (mCodecContext) {
+    avcodec_flush_buffers(mCodecContext);
+  }
+  MonitorAutoLock mon(mMonitor);
+  mIsFlushing = false;
+  mon.NotifyAll();
+}
+
+void
+FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown()
 {
   StaticMutexAutoLock mon(sMonitor);
 
   if (sFFmpegInitDone) {
     avcodec_close(mCodecContext);
     av_freep(&mCodecContext);
 #if LIBAVCODEC_VERSION_MAJOR >= 55
     av_frame_free(&mFrame);
 #elif LIBAVCODEC_VERSION_MAJOR == 54
     avcodec_free_frame(&mFrame);
 #else
     delete mFrame;
     mFrame = nullptr;
 #endif
   }
-  return NS_OK;
 }
 
 AVFrame*
 FFmpegDataDecoder<LIBAV_VER>::PrepareFrame()
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 #if LIBAVCODEC_VERSION_MAJOR >= 55
   if (mFrame) {
     av_frame_unref(mFrame);
   } else {
     mFrame = av_frame_alloc();
   }
 #elif LIBAVCODEC_VERSION_MAJOR == 54
   if (mFrame) {
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
@@ -18,37 +18,53 @@ template <int V>
 class FFmpegDataDecoder : public MediaDataDecoder
 {
 };
 
 template <>
 class FFmpegDataDecoder<LIBAV_VER> : public MediaDataDecoder
 {
 public:
-  FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue, AVCodecID aCodecID);
+  FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue,
+                    MediaDataDecoderCallback* aCallback,
+                    AVCodecID aCodecID);
   virtual ~FFmpegDataDecoder();
 
   static bool Link();
 
-  virtual nsRefPtr<InitPromise> Init() override = 0;
-  virtual nsresult Input(MediaRawData* aSample) override = 0;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override = 0;
-  virtual nsresult Shutdown() override;
+  nsRefPtr<InitPromise> Init() override = 0;
+  nsresult Input(MediaRawData* aSample) override = 0;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
 
 protected:
+  // Flush and Drain operation, always run
+  virtual void ProcessFlush();
+  virtual void ProcessDrain() = 0;
+  virtual void ProcessShutdown();
   AVFrame*        PrepareFrame();
   nsresult        InitDecoder();
 
-  FlushableTaskQueue* mTaskQueue;
+  nsRefPtr<FlushableTaskQueue> mTaskQueue;
+  MediaDataDecoderCallback* mCallback;
+
   AVCodecContext* mCodecContext;
   AVFrame*        mFrame;
   nsRefPtr<MediaByteBuffer> mExtraData;
   AVCodecID mCodecID;
 
+  // For wait on mIsFlushing during Shutdown() process.
+  // Protects mReorderQueue.
+  Monitor mMonitor;
+  // Set on reader/decode thread calling Flush() to indicate that output is
+  // not required and so input samples on mTaskQueue need not be processed.
+  // Cleared on mTaskQueue in ProcessDrain().
+  Atomic<bool> mIsFlushing;
+
 private:
   static bool sFFmpegInitDone;
   static StaticMutex sMonitor;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegDataDecoder_h__
--- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
@@ -20,49 +20,58 @@ class FFmpegDecoderModule : public Platf
 public:
   static already_AddRefed<PlatformDecoderModule>
   Create()
   {
     nsRefPtr<PlatformDecoderModule> pdm = new FFmpegDecoderModule();
     return pdm.forget();
   }
 
+  static bool
+  GetVersion(uint32_t& aMajor, uint32_t& aMinor)
+  {
+    uint32_t version = avcodec_version();
+    aMajor = (version >> 16) & 0xff;
+    aMinor = (version >> 8) & 0xff;
+    return true;
+  }
+
   FFmpegDecoderModule() {}
   virtual ~FFmpegDecoderModule() {}
 
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override
   {
     nsRefPtr<MediaDataDecoder> decoder =
       new FFmpegH264Decoder<V>(aVideoTaskQueue, aCallback, aConfig,
                                aImageContainer);
     return decoder.forget();
   }
 
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override
   {
     nsRefPtr<MediaDataDecoder> decoder =
       new FFmpegAudioDecoder<V>(aAudioTaskQueue, aCallback, aConfig);
     return decoder.forget();
   }
 
-  virtual bool SupportsMimeType(const nsACString& aMimeType) override
+  bool SupportsMimeType(const nsACString& aMimeType) override
   {
     return FFmpegAudioDecoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE ||
       FFmpegH264Decoder<V>::GetCodecId(aMimeType) != AV_CODEC_ID_NONE;
   }
 
-  virtual ConversionRequired
+  ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override
   {
     if (aConfig.IsVideo() &&
         (aConfig.mMimeType.EqualsLiteral("video/avc") ||
          aConfig.mMimeType.EqualsLiteral("video/mp4"))) {
       return PlatformDecoderModule::kNeedAVCC;
     } else {
       return kNeedNone;
--- a/dom/media/platforms/ffmpeg/FFmpegFunctionList.h
+++ b/dom/media/platforms/ffmpeg/FFmpegFunctionList.h
@@ -18,16 +18,17 @@ AV_FUNC(avcodec_flush_buffers, 0)
 AV_FUNC(avcodec_alloc_context3, 0)
 AV_FUNC(avcodec_get_edge_width, 0)
 AV_FUNC(avcodec_open2, 0)
 AV_FUNC(av_init_packet, 0)
 AV_FUNC(av_dict_get, 0)
 AV_FUNC(av_parser_init, 0)
 AV_FUNC(av_parser_close, 0)
 AV_FUNC(av_parser_parse2, 0)
+AV_FUNC(avcodec_version, 0)
 
 /* libavutil */
 AV_FUNC(av_image_fill_linesizes, 0)
 AV_FUNC(av_image_fill_pointers, 0)
 AV_FUNC(av_log_set_level, 0)
 AV_FUNC(av_malloc, 0)
 AV_FUNC(av_freep, 0)
 
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
@@ -23,19 +23,20 @@ typedef mozilla::layers::PlanarYCbCrImag
 
 namespace mozilla
 {
 
 FFmpegH264Decoder<LIBAV_VER>::FFmpegH264Decoder(
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const VideoInfo& aConfig,
   ImageContainer* aImageContainer)
-  : FFmpegDataDecoder(aTaskQueue, GetCodecId(aConfig.mMimeType))
-  , mCallback(aCallback)
+  : FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
   , mImageContainer(aImageContainer)
+  , mPictureWidth(aConfig.mImage.width)
+  , mPictureHeight(aConfig.mImage.height)
   , mDisplayWidth(aConfig.mDisplay.width)
   , mDisplayHeight(aConfig.mDisplay.height)
   , mCodecParser(nullptr)
 {
   MOZ_COUNT_CTOR(FFmpegH264Decoder);
   // Use a new MediaByteBuffer as the object will be modified during initialization.
   mExtraData = new MediaByteBuffer;
   mExtraData->AppendElements(*aConfig.mExtraData);
@@ -45,37 +46,42 @@ nsRefPtr<MediaDataDecoder::InitPromise>
 FFmpegH264Decoder<LIBAV_VER>::Init()
 {
   if (NS_FAILED(InitDecoder())) {
     return InitPromise::CreateAndReject(DecoderFailureReason::INIT_ERROR, __func__);
   }
 
   mCodecContext->get_buffer = AllocateBufferCb;
   mCodecContext->release_buffer = ReleaseBufferCb;
+  mCodecContext->width = mPictureWidth;
+  mCodecContext->height = mPictureHeight;
 
   return InitPromise::CreateAndResolve(TrackInfo::kVideoTrack, __func__);
 }
 
 int64_t
 FFmpegH264Decoder<LIBAV_VER>::GetPts(const AVPacket& packet)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 #if LIBAVCODEC_VERSION_MAJOR == 53
   if (mFrame->pkt_pts == 0) {
     return mFrame->pkt_dts;
   } else {
     return mFrame->pkt_pts;
   }
 #else
   return mFrame->pkt_pts;
 #endif
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
   uint8_t* inputData = const_cast<uint8_t*>(aSample->Data());
   size_t inputSize = aSample->Size();
 
   if (inputSize && (mCodecID == AV_CODEC_ID_VP8
 #if LIBAVCODEC_VERSION_MAJOR >= 55
       || mCodecID == AV_CODEC_ID_VP9
 #endif
       )) {
@@ -117,16 +123,18 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
   }
   return DoDecodeFrame(aSample, inputData, inputSize);
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
                                             uint8_t* aData, int aSize)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
   AVPacket packet;
   av_init_packet(&packet);
 
   packet.data = aData;
   packet.size = aSize;
   packet.dts = aSample->mTimecode;
   packet.pts = aSample->mTime;
   packet.flags = aSample->mKeyframe ? AV_PKT_FLAG_KEY : 0;
@@ -203,16 +211,18 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
     return DecodeResult::DECODE_FRAME;
   }
   return DecodeResult::DECODE_NO_FRAME;
 }
 
 void
 FFmpegH264Decoder<LIBAV_VER>::DecodeFrame(MediaRawData* aSample)
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
+
   if (DoDecodeFrame(aSample) != DecodeResult::DECODE_ERROR &&
       mTaskQueue->IsEmpty()) {
     mCallback->InputExhausted();
   }
 }
 
 /* static */ int
 FFmpegH264Decoder<LIBAV_VER>::AllocateBufferCb(AVCodecContext* aCodecContext,
@@ -334,42 +344,25 @@ FFmpegH264Decoder<LIBAV_VER>::Input(Medi
       this, &FFmpegH264Decoder<LIBAV_VER>::DecodeFrame,
       nsRefPtr<MediaRawData>(aSample)));
   mTaskQueue->Dispatch(runnable.forget());
 
   return NS_OK;
 }
 
 void
-FFmpegH264Decoder<LIBAV_VER>::DoDrain()
+FFmpegH264Decoder<LIBAV_VER>::ProcessDrain()
 {
+  MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   nsRefPtr<MediaRawData> empty(new MediaRawData());
   while (DoDecodeFrame(empty) == DecodeResult::DECODE_FRAME) {
   }
   mCallback->DrainComplete();
 }
 
-nsresult
-FFmpegH264Decoder<LIBAV_VER>::Drain()
-{
-  nsCOMPtr<nsIRunnable> runnable(
-    NS_NewRunnableMethod(this, &FFmpegH264Decoder<LIBAV_VER>::DoDrain));
-  mTaskQueue->Dispatch(runnable.forget());
-
-  return NS_OK;
-}
-
-nsresult
-FFmpegH264Decoder<LIBAV_VER>::Flush()
-{
-  nsresult rv = FFmpegDataDecoder::Flush();
-  // Even if the above fails we may as well clear our frame queue.
-  return rv;
-}
-
 FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
 {
   MOZ_COUNT_DTOR(FFmpegH264Decoder);
   if (mCodecParser) {
     av_parser_close(mCodecParser);
     mCodecParser = nullptr;
   }
 }
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.h
@@ -31,20 +31,19 @@ class FFmpegH264Decoder<LIBAV_VER> : pub
 
 public:
   FFmpegH264Decoder(FlushableTaskQueue* aTaskQueue,
                     MediaDataDecoderCallback* aCallback,
                     const VideoInfo& aConfig,
                     ImageContainer* aImageContainer);
   virtual ~FFmpegH264Decoder();
 
-  virtual nsRefPtr<InitPromise> Init() override;
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Drain() override;
-  virtual nsresult Flush() override;
+  nsRefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  void ProcessDrain() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
 
 private:
   void DecodeFrame(MediaRawData* aSample);
   DecodeResult DoDecodeFrame(MediaRawData* aSample);
   DecodeResult DoDecodeFrame(MediaRawData* aSample, uint8_t* aData, int aSize);
   void DoDrain();
   void OutputDelayedFrames();
@@ -57,18 +56,19 @@ private:
    */
   int AllocateYUV420PVideoBuffer(AVCodecContext* aCodecContext,
                                  AVFrame* aFrame);
 
   static int AllocateBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame);
   static void ReleaseBufferCb(AVCodecContext* aCodecContext, AVFrame* aFrame);
   int64_t GetPts(const AVPacket& packet);
 
-  MediaDataDecoderCallback* mCallback;
   nsRefPtr<ImageContainer> mImageContainer;
+  uint32_t mPictureWidth;
+  uint32_t mPictureHeight;
   uint32_t mDisplayWidth;
   uint32_t mDisplayHeight;
   AVCodecParserContext* mCodecParser;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegH264Decoder_h__
--- a/dom/media/platforms/gonk/GonkAudioDecoderManager.h
+++ b/dom/media/platforms/gonk/GonkAudioDecoderManager.h
@@ -21,26 +21,26 @@ namespace mozilla {
 
 class GonkAudioDecoderManager : public GonkDecoderManager {
 typedef android::MediaCodecProxy MediaCodecProxy;
 public:
   GonkAudioDecoderManager(const AudioInfo& aConfig);
 
   virtual ~GonkAudioDecoderManager() override;
 
-  virtual nsRefPtr<InitPromise> Init(MediaDataDecoderCallback* aCallback) override;
+  nsRefPtr<InitPromise> Init(MediaDataDecoderCallback* aCallback) override;
 
-  virtual nsresult Input(MediaRawData* aSample) override;
+  nsresult Input(MediaRawData* aSample) override;
 
-  virtual nsresult Output(int64_t aStreamOffset,
+  nsresult Output(int64_t aStreamOffset,
                           nsRefPtr<MediaData>& aOutput) override;
 
-  virtual nsresult Flush() override;
+  nsresult Flush() override;
 
-  virtual bool HasQueuedSample() override;
+  bool HasQueuedSample() override;
 
 private:
   bool InitMediaCodecProxy(MediaDataDecoderCallback* aCallback);
 
   nsresult CreateAudioData(int64_t aStreamOffset,
                               AudioData** aOutData);
 
   void ReleaseAudioBuffer();
--- a/dom/media/platforms/gonk/GonkDecoderModule.h
+++ b/dom/media/platforms/gonk/GonkDecoderModule.h
@@ -12,33 +12,33 @@
 namespace mozilla {
 
 class GonkDecoderModule : public PlatformDecoderModule {
 public:
   GonkDecoderModule();
   virtual ~GonkDecoderModule();
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      mozilla::layers::LayersBackend aLayersBackend,
                      mozilla::layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
   // Decode thread.
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
   static void Init();
 
-  virtual ConversionRequired
+  ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 
-  virtual bool SupportsMimeType(const nsACString& aMimeType) override;
+  bool SupportsMimeType(const nsACString& aMimeType) override;
 
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/platforms/gonk/GonkMediaDataDecoder.h
+++ b/dom/media/platforms/gonk/GonkMediaDataDecoder.h
@@ -67,25 +67,25 @@ protected:
 class GonkMediaDataDecoder : public MediaDataDecoder {
 public:
   GonkMediaDataDecoder(GonkDecoderManager* aDecoderManager,
                        FlushableTaskQueue* aTaskQueue,
                        MediaDataDecoderCallback* aCallback);
 
   ~GonkMediaDataDecoder();
 
-  virtual nsRefPtr<InitPromise> Init() override;
+  nsRefPtr<InitPromise> Init() override;
 
-  virtual nsresult Input(MediaRawData* aSample) override;
+  nsresult Input(MediaRawData* aSample) override;
 
-  virtual nsresult Flush() override;
+  nsresult Flush() override;
 
-  virtual nsresult Drain() override;
+  nsresult Drain() override;
 
-  virtual nsresult Shutdown() override;
+  nsresult Shutdown() override;
 
 private:
 
   // Called on the task queue. Inserts the sample into the decoder, and
   // extracts output if available, if aSample is null, it means there is
   // no data from source, it will notify the decoder EOS and flush all the
   // decoded frames.
   void ProcessDecode(MediaRawData* aSample);
--- a/dom/media/platforms/gonk/GonkVideoDecoderManager.h
+++ b/dom/media/platforms/gonk/GonkVideoDecoderManager.h
@@ -39,26 +39,26 @@ typedef android::MediaCodecProxy MediaCo
 typedef mozilla::layers::TextureClient TextureClient;
 
 public:
   GonkVideoDecoderManager(mozilla::layers::ImageContainer* aImageContainer,
                           const VideoInfo& aConfig);
 
   virtual ~GonkVideoDecoderManager() override;
 
-  virtual nsRefPtr<InitPromise> Init(MediaDataDecoderCallback* aCallback) override;
+  nsRefPtr<InitPromise> Init(MediaDataDecoderCallback* aCallback) override;
 
-  virtual nsresult Input(MediaRawData* aSample) override;
+  nsresult Input(MediaRawData* aSample) override;
 
-  virtual nsresult Output(int64_t aStreamOffset,
+  nsresult Output(int64_t aStreamOffset,
                           nsRefPtr<MediaData>& aOutput) override;
 
-  virtual nsresult Flush() override;
+  nsresult Flush() override;
 
-  virtual bool HasQueuedSample() override;
+  bool HasQueuedSample() override;
 
   static void RecycleCallback(TextureClient* aClient, void* aClosure);
 
 private:
   struct FrameInfo
   {
     int32_t mWidth = 0;
     int32_t mHeight = 0;
@@ -71,17 +71,17 @@ private:
     int32_t mCropBottom = 0;
   };
   class MessageHandler : public android::AHandler
   {
   public:
     MessageHandler(GonkVideoDecoderManager *aManager);
     ~MessageHandler();
 
-    virtual void onMessageReceived(const android::sp<android::AMessage> &aMessage);
+    void onMessageReceived(const android::sp<android::AMessage> &aMessage) override;
 
   private:
     // Forbidden
     MessageHandler() = delete;
     MessageHandler(const MessageHandler &rhs) = delete;
     const MessageHandler &operator=(const MessageHandler &rhs) = delete;
 
     GonkVideoDecoderManager *mManager;
@@ -89,18 +89,18 @@ private:
   friend class MessageHandler;
 
   class VideoResourceListener : public android::MediaCodecProxy::CodecResourceListener
   {
   public:
     VideoResourceListener(GonkVideoDecoderManager *aManager);
     ~VideoResourceListener();
 
-    virtual void codecReserved() override;
-    virtual void codecCanceled() override;
+    void codecReserved() override;
+    void codecCanceled() override;
 
   private:
     // Forbidden
     VideoResourceListener() = delete;
     VideoResourceListener(const VideoResourceListener &rhs) = delete;
     const VideoResourceListener &operator=(const VideoResourceListener &rhs) = delete;
 
     GonkVideoDecoderManager *mManager;
--- a/dom/media/platforms/wmf/DXVA2Manager.cpp
+++ b/dom/media/platforms/wmf/DXVA2Manager.cpp
@@ -87,17 +87,17 @@ public:
 
   // Copies a region (aRegion) of the video frame stored in aVideoSample
   // into an image which is returned by aOutImage.
   HRESULT CopyToImage(IMFSample* aVideoSample,
                       const nsIntRect& aRegion,
                       ImageContainer* aContainer,
                       Image** aOutImage) override;
 
-  virtual bool SupportsConfig(IMFMediaType* aType, float aFramerate) override;
+  bool SupportsConfig(IMFMediaType* aType, float aFramerate) override;
 
 private:
   nsRefPtr<IDirect3D9Ex> mD3D9;
   nsRefPtr<IDirect3DDevice9Ex> mDevice;
   nsRefPtr<IDirect3DDeviceManager9> mDeviceManager;
   RefPtr<D3D9RecycleAllocator> mTextureClientAllocator;
   nsRefPtr<IDirectXVideoDecoderService> mDecoderService;
   GUID mDecoderGUID;
@@ -476,21 +476,21 @@ public:
 
   // Copies a region (aRegion) of the video frame stored in aVideoSample
   // into an image which is returned by aOutImage.
   HRESULT CopyToImage(IMFSample* aVideoSample,
                       const nsIntRect& aRegion,
                       ImageContainer* aContainer,
                       Image** aOutImage) override;
 
-  virtual HRESULT ConfigureForSize(uint32_t aWidth, uint32_t aHeight) override;
+  HRESULT ConfigureForSize(uint32_t aWidth, uint32_t aHeight) override;
 
-  virtual bool IsD3D11() override { return true; }
+  bool IsD3D11() override { return true; }
 
-  virtual bool SupportsConfig(IMFMediaType* aType, float aFramerate) override;
+  bool SupportsConfig(IMFMediaType* aType, float aFramerate) override;
 
 private:
   HRESULT CreateFormatConverter();
 
   HRESULT CreateOutputSample(RefPtr<IMFSample>& aSample,
                              ID3D11Texture2D* aTexture);
 
   RefPtr<ID3D11Device> mDevice;
--- a/dom/media/platforms/wmf/WMFAudioMFTManager.h
+++ b/dom/media/platforms/wmf/WMFAudioMFTManager.h
@@ -16,27 +16,27 @@ namespace mozilla {
 
 class WMFAudioMFTManager : public MFTManager {
 public:
   WMFAudioMFTManager(const AudioInfo& aConfig);
   ~WMFAudioMFTManager();
 
   bool Init();
 
-  virtual HRESULT Input(MediaRawData* aSample) override;
+  HRESULT Input(MediaRawData* aSample) override;
 
   // Note WMF's AAC decoder sometimes output negatively timestamped samples,
   // presumably they're the preroll samples, and we strip them. We may return
   // a null aOutput in this case.
-  virtual HRESULT Output(int64_t aStreamOffset,
+  HRESULT Output(int64_t aStreamOffset,
                          nsRefPtr<MediaData>& aOutput) override;
 
-  virtual void Shutdown() override;
+  void Shutdown() override;
 
-  virtual TrackInfo::TrackType GetType() override {
+  TrackInfo::TrackType GetType() override {
     return TrackInfo::kAudioTrack;
   }
 
 private:
 
   HRESULT UpdateOutputType();
 
   uint32_t mAudioChannels;
--- a/dom/media/platforms/wmf/WMFDecoderModule.h
+++ b/dom/media/platforms/wmf/WMFDecoderModule.h
@@ -12,33 +12,33 @@
 namespace mozilla {
 
 class WMFDecoderModule : public PlatformDecoderModule {
 public:
   WMFDecoderModule();
   virtual ~WMFDecoderModule();
 
   // Initializes the module, loads required dynamic libraries, etc.
-  virtual nsresult Startup() override;
+  nsresult Startup() override;
 
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
-  virtual already_AddRefed<MediaDataDecoder>
+  already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override;
 
   bool SupportsMimeType(const nsACString& aMimeType) override;
 
-  virtual ConversionRequired
+  ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 
   // Accessors that report whether we have the required MFTs available
   // 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();
--- a/dom/media/platforms/wmf/WMFMediaDataDecoder.h
+++ b/dom/media/platforms/wmf/WMFMediaDataDecoder.h
@@ -64,27 +64,27 @@ protected:
 // type are handled by MFTManager and the MFTDecoder it creates.
 class WMFMediaDataDecoder : public MediaDataDecoder {
 public:
   WMFMediaDataDecoder(MFTManager* aOutputSource,
                       FlushableTaskQueue* aAudioTaskQueue,
                       MediaDataDecoderCallback* aCallback);
   ~WMFMediaDataDecoder();
 
-  virtual nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
+  nsRefPtr<MediaDataDecoder::InitPromise> Init() override;
 
-  virtual nsresult Input(MediaRawData* aSample);
+  nsresult Input(MediaRawData* aSample);
 
-  virtual nsresult Flush() override;
+  nsresult Flush() override;
 
-  virtual nsresult Drain() override;
+  nsresult Drain() override;
 
-  virtual nsresult Shutdown() override;
+  nsresult Shutdown() override;
 
-  virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
+  bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
 
 private:
 
   // Called on the task queue. Inserts the sample into the decoder, and
   // extracts output if available.
   void ProcessDecode(MediaRawData* aSample);
 
   // Called on the task queue. Extracts output if available, and delivers
--- a/dom/media/platforms/wmf/WMFVideoMFTManager.h
+++ b/dom/media/platforms/wmf/WMFVideoMFTManager.h
@@ -22,26 +22,25 @@ public:
   WMFVideoMFTManager(const VideoInfo& aConfig,
                      mozilla::layers::LayersBackend aLayersBackend,
                      mozilla::layers::ImageContainer* aImageContainer,
                      bool aDXVAEnabled);
   ~WMFVideoMFTManager();
 
   bool Init();
 
-  virtual HRESULT Input(MediaRawData* aSample) override;
+  HRESULT Input(MediaRawData* aSample) override;
 
-  virtual HRESULT Output(int64_t aStreamOffset,
-                         nsRefPtr<MediaData>& aOutput) override;
+  HRESULT Output(int64_t aStreamOffset, nsRefPtr<MediaData>& aOutput) override;
 
-  virtual void Shutdown() override;
+  void Shutdown() override;
 
-  virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
+  bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
 
-  virtual TrackInfo::TrackType GetType() override {
+  TrackInfo::TrackType GetType() override {
     return TrackInfo::kVideoTrack;
   }
 
 private:
 
   bool InitializeDXVA(bool aForceD3D9);
 
   bool InitInternal(bool aForceD3D9);
--- a/dom/media/platforms/wrappers/H264Converter.h
+++ b/dom/media/platforms/wrappers/H264Converter.h
@@ -24,22 +24,22 @@ public:
   H264Converter(PlatformDecoderModule* aPDM,
                 const VideoInfo& aConfig,
                 layers::LayersBackend aLayersBackend,
                 layers::ImageContainer* aImageContainer,
                 FlushableTaskQueue* aVideoTaskQueue,
                 MediaDataDecoderCallback* aCallback);
   virtual ~H264Converter();
 
-  virtual nsRefPtr<InitPromise> Init() override;
-  virtual nsresult Input(MediaRawData* aSample) override;
-  virtual nsresult Flush() override;
-  virtual nsresult Drain() override;
-  virtual nsresult Shutdown() override;
-  virtual bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
+  nsRefPtr<InitPromise> Init() override;
+  nsresult Input(MediaRawData* aSample) override;
+  nsresult Flush() override;
+  nsresult Drain() override;
+  nsresult Shutdown() override;
+  bool IsHardwareAccelerated(nsACString& aFailureReason) const override;
 
   // Return true if mimetype is H.264.
   static bool IsH264(const TrackInfo& aConfig);
 
 private:
   // Will create the required MediaDataDecoder if need AVCC and we have a SPS NAL.
   // Returns NS_ERROR_FAILURE if error is permanent and can't be recovered and
   // will set mError accordingly.
--- a/dom/media/webaudio/AudioBufferSourceNode.cpp
+++ b/dom/media/webaudio/AudioBufferSourceNode.cpp
@@ -710,18 +710,24 @@ AudioBufferSourceNode::SendOffsetAndDura
   int32_t offsetSamples = std::max(0, NS_lround(mOffset * rate));
 
   // Don't set parameter unnecessarily
   if (offsetSamples > 0) {
     aStream->SetInt32Parameter(BUFFERSTART, offsetSamples);
   }
 
   if (mDuration != std::numeric_limits<double>::min()) {
-    bufferEnd = std::min(bufferEnd,
-                         offsetSamples + NS_lround(mDuration * rate));
+    MOZ_ASSERT(mDuration >= 0.0); // provided by Start()
+    MOZ_ASSERT(rate >= 0.0f); // provided by AudioBuffer::Create()
+    static_assert(std::numeric_limits<double>::digits >=
+                  std::numeric_limits<decltype(bufferEnd)>::digits,
+                  "bufferEnd should be represented exactly by double");
+    // + 0.5 rounds mDuration to nearest sample when assigned to bufferEnd.
+    bufferEnd = std::min<double>(bufferEnd,
+                                 offsetSamples + mDuration * rate + 0.5);
   }
   aStream->SetInt32Parameter(BUFFEREND, bufferEnd);
 
   MarkActive();
 }
 
 void
 AudioBufferSourceNode::Stop(double aWhen, ErrorResult& aRv)
--- a/dom/media/webspeech/synth/nsSpeechTask.cpp
+++ b/dom/media/webspeech/synth/nsSpeechTask.cpp
@@ -241,18 +241,24 @@ nsSpeechTask::SendAudio(JS::Handle<JS::V
   JS::Rooted<JSObject*> darray(aCx, &aData.toObject());
   JSAutoCompartment ac(aCx, darray);
 
   JS::Rooted<JSObject*> tsrc(aCx, nullptr);
 
   // Allow either Int16Array or plain JS Array
   if (JS_IsInt16Array(darray)) {
     tsrc = darray;
-  } else if (JS_IsArrayObject(aCx, darray)) {
-    tsrc = JS_NewInt16ArrayFromArray(aCx, darray);
+  } else {
+    bool isArray;
+    if (!JS_IsArrayObject(aCx, darray, &isArray)) {
+      return NS_ERROR_UNEXPECTED;
+    }
+    if (isArray) {
+      tsrc = JS_NewInt16ArrayFromArray(aCx, darray);
+    }
   }
 
   if (!tsrc) {
     return NS_ERROR_DOM_TYPE_MISMATCH_ERR;
   }
 
   uint32_t dataLen = JS_GetTypedArrayLength(tsrc);
   nsRefPtr<mozilla::SharedBuffer> samples;
--- a/dom/mobilemessage/MmsMessage.cpp
+++ b/dom/mobilemessage/MmsMessage.cpp
@@ -1,18 +1,17 @@
 /* -*- 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 "MmsMessage.h"
 #include "nsIDOMClassInfo.h"
-#include "jsapi.h" // For OBJECT_TO_JSVAL and JS_NewDateObjectMsec
-#include "jsfriendapi.h" // For js_DateGetMsecSinceEpoch
+#include "jsapi.h" // For JS_IsArrayObject, JS_GetElement, etc.
 #include "nsJSUtils.h"
 #include "nsContentUtils.h"
 #include "nsTArrayHelpers.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/mobilemessage/Constants.h" // For MessageType
 #include "mozilla/dom/mobilemessage/SmsTypes.h"
 #include "mozilla/dom/ScriptSettings.h"
@@ -208,17 +207,21 @@ MmsMessage::Create(int32_t aId,
     return NS_ERROR_INVALID_ARG;
   }
 
   // Set |deliveryInfo|.
   if (!aDeliveryInfo.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
   JS::Rooted<JSObject*> deliveryInfoObj(aCx, &aDeliveryInfo.toObject());
-  if (!JS_IsArrayObject(aCx, deliveryInfoObj)) {
+  bool isArray;
+  if (!JS_IsArrayObject(aCx, deliveryInfoObj, &isArray)) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!isArray) {
     return NS_ERROR_INVALID_ARG;
   }
 
   uint32_t length;
   MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, deliveryInfoObj, &length));
 
   nsTArray<MmsDeliveryInfo> deliveryInfo;
   JS::Rooted<JS::Value> infoJsVal(aCx);
@@ -236,17 +239,20 @@ MmsMessage::Create(int32_t aId,
     deliveryInfo.AppendElement(info);
   }
 
   // Set |receivers|.
   if (!aReceivers.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
   JS::Rooted<JSObject*> receiversObj(aCx, &aReceivers.toObject());
-  if (!JS_IsArrayObject(aCx, receiversObj)) {
+  if (!JS_IsArrayObject(aCx, receiversObj, &isArray)) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!isArray) {
     return NS_ERROR_INVALID_ARG;
   }
 
   MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, receiversObj, &length));
 
   nsTArray<nsString> receivers;
   JS::Rooted<JS::Value> receiverJsVal(aCx);
   for (uint32_t i = 0; i < length; ++i) {
@@ -263,17 +269,20 @@ MmsMessage::Create(int32_t aId,
     receivers.AppendElement(receiverStr);
   }
 
   // Set |attachments|.
   if (!aAttachments.isObject()) {
     return NS_ERROR_INVALID_ARG;
   }
   JS::Rooted<JSObject*> attachmentsObj(aCx, &aAttachments.toObject());
-  if (!JS_IsArrayObject(aCx, attachmentsObj)) {
+  if (!JS_IsArrayObject(aCx, attachmentsObj, &isArray)) {
+    return NS_ERROR_FAILURE;
+  }
+  if (!isArray) {
     return NS_ERROR_INVALID_ARG;
   }
 
   nsTArray<Attachment> attachments;
   MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, attachmentsObj, &length));
 
   JS::Rooted<JS::Value> attachmentJsVal(aCx);
   for (uint32_t i = 0; i < length; ++i) {
--- a/dom/mobilemessage/MobileMessageThread.cpp
+++ b/dom/mobilemessage/MobileMessageThread.cpp
@@ -2,17 +2,16 @@
 /* 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 "MobileMessageThread.h"
 #include "nsIDOMClassInfo.h"
 #include "jsapi.h"           // For OBJECT_TO_JSVAL and JS_NewDateObjectMsec
-#include "jsfriendapi.h"     // For js_DateGetMsecSinceEpoch
 #include "nsJSUtils.h"       // For nsAutoJSString
 #include "nsTArrayHelpers.h" // For nsTArrayToJSArray
 #include "mozilla/dom/mobilemessage/Constants.h" // For MessageType
 
 using namespace mozilla::dom::mobilemessage;
 
 namespace mozilla {
 namespace dom {
@@ -49,17 +48,21 @@ MobileMessageThread::Create(uint64_t aId
 
   // Participants.
   {
     if (!aParticipants.isObject()) {
       return NS_ERROR_INVALID_ARG;
     }
 
     JS::Rooted<JSObject*> obj(aCx, &aParticipants.toObject());
-    if (!JS_IsArrayObject(aCx, obj)) {
+    bool isArray;
+    if (!JS_IsArrayObject(aCx, obj, &isArray)) {
+      return NS_ERROR_FAILURE;
+    }
+    if (!isArray) {
       return NS_ERROR_INVALID_ARG;
     }
 
     uint32_t length;
     MOZ_ALWAYS_TRUE(JS_GetArrayLength(aCx, obj, &length));
     NS_ENSURE_TRUE(length, NS_ERROR_INVALID_ARG);
 
     for (uint32_t i = 0; i < length; ++i) {
--- a/dom/mobilemessage/SmsMessage.cpp
+++ b/dom/mobilemessage/SmsMessage.cpp
@@ -1,18 +1,16 @@
 /* -*- 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 "SmsMessage.h"
 #include "nsIDOMClassInfo.h"
-#include "jsapi.h" // For OBJECT_TO_JSVAL and JS_NewDateObjectMsec
-#include "jsfriendapi.h" // For js_DateGetMsecSinceEpoch
 #include "mozilla/dom/mobilemessage/Constants.h" // For MessageType
 
 using namespace mozilla::dom::mobilemessage;
 
 namespace mozilla {
 namespace dom {
 
 NS_INTERFACE_MAP_BEGIN(SmsMessage)
--- a/dom/tests/mochitest/fetch/reroute.js
+++ b/dom/tests/mochitest/fetch/reroute.js
@@ -1,19 +1,23 @@
 onfetch = function(e) {
   if (e.request.url.indexOf("Referer") >= 0) {
     // Silently rewrite the referrer so the referrer test passes since the
     // document/worker isn't aware of this service worker.
     var url = e.request.url.substring(0, e.request.url.indexOf('?'));
     url += '?headers=' + ({ 'Referer': self.location.href }).toSource();
 
-    e.respondWith(fetch(url, {
-      method: e.request.method,
-      headers: e.request.headers,
-      body: e.request.body,
-      mode: e.request.mode,
-      credentials: e.request.credentials,
-      cache: e.request.cache,
+    e.respondWith(e.request.text().then(function(text) {
+      var body = text === '' ? undefined : text;
+      return fetch(url, {
+        method: e.request.method,
+        headers: e.request.headers,
+        body: body,
+        mode: e.request.mode,
+        credentials: e.request.credentials,
+        redirect: e.request.redirect,
+        cache: e.request.cache,
+      });
     }));
     return;
   }
   e.respondWith(fetch(e.request));
 };
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -135,17 +135,17 @@ var interfaceNamesInGlobalScope =
     "AnalyserNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "Animation", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "AnimationEffectReadOnly", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "AnimationEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "AnimationPlaybackEvent",
+    {name: "AnimationPlaybackEvent", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "AnimationTimeline", release: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Attr",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "Audio",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "AudioBuffer",
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -22,16 +22,20 @@ interface Document : Node {
   [Pure]
   readonly attribute DOMString URL;
   [Pure]
   readonly attribute DOMString documentURI;
   [Pure]
   readonly attribute DOMString compatMode;
   [Pure]
   readonly attribute DOMString characterSet;
+  [Pure,BinaryName="characterSet"]
+  readonly attribute DOMString charset; // legacy alias of .characterSet
+  [Pure,BinaryName="characterSet"]
+  readonly attribute DOMString inputEncoding; // legacy alias of .characterSet
   [Pure]
   readonly attribute DOMString contentType;
 
   [Pure]
   readonly attribute DocumentType? doctype;
   [Pure]
   readonly attribute Element? documentElement;
   [Pure]
@@ -81,18 +85,16 @@ interface Document : Node {
   // These are not in the spec, but leave them for now for backwards compat.
   // So sort of like Gecko extensions
   [NewObject, Throws]
   CDATASection createCDATASection(DOMString data);
   [NewObject, Throws]
   Attr createAttribute(DOMString name);
   [NewObject, Throws]
   Attr createAttributeNS(DOMString? namespace, DOMString name);
-  [Pure]
-  readonly attribute DOMString? inputEncoding;
 };
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-document-object
 partial interface Document {
   [PutForwards=href, Unforgeable] readonly attribute Location? location;
   //(HTML only)         attribute DOMString domain;
   readonly attribute DOMString referrer;
   //(HTML only)         attribute DOMString cookie;
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -3982,37 +3982,32 @@ public:
 
 NS_IMPL_ISUPPORTS_INHERITED(FetchEventRunnable, WorkerRunnable, nsIHttpHeaderVisitor)
 
 already_AddRefed<nsIRunnable>
 ServiceWorkerManager::PrepareFetchEvent(const OriginAttributes& aOriginAttributes,
                                         nsIDocument* aDoc,
                                         nsIInterceptedChannel* aChannel,
                                         bool aIsReload,
+                                        bool aIsSubresourceLoad,
                                         ErrorResult& aRv)
 {
   MOZ_ASSERT(aChannel);
   MOZ_ASSERT(NS_IsMainThread());
 
   nsCOMPtr<nsISupports> serviceWorker;
 
-  bool isNavigation = false;
-  aRv = aChannel->GetIsNavigation(&isNavigation);
-  if (NS_WARN_IF(aRv.Failed())) {
-    return nullptr;
-  }
-
   // if the ServiceWorker script fails to load for some reason, just resume
   // the original channel.
   nsCOMPtr<nsIRunnable> failRunnable =
     NS_NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception);
 
   nsAutoPtr<ServiceWorkerClientInfo> clientInfo;
 
-  if (!isNavigation) {
+  if (aIsSubresourceLoad) {
     MOZ_ASSERT(aDoc);
     aRv = GetDocumentController(aDoc->GetInnerWindow(), failRunnable,
                                 getter_AddRefs(serviceWorker));
     clientInfo = new ServiceWorkerClientInfo(aDoc);
   } else {
     nsCOMPtr<nsIChannel> internalChannel;
     aRv = aChannel->GetChannel(getter_AddRefs(internalChannel));
     if (NS_WARN_IF(aRv.Failed())) {
--- a/dom/workers/ServiceWorkerManager.h
+++ b/dom/workers/ServiceWorkerManager.h
@@ -315,16 +315,17 @@ public:
   bool
   IsControlled(nsIDocument* aDocument, ErrorResult& aRv);
 
   already_AddRefed<nsIRunnable>
   PrepareFetchEvent(const OriginAttributes& aOriginAttributes,
                     nsIDocument* aDoc,
                     nsIInterceptedChannel* aChannel,
                     bool aIsReload,
+                     bool aIsSubresourceLoad,
                     ErrorResult& aRv);
 
   void
   DispatchPreparedFetchEvent(nsIInterceptedChannel* aChannel,
                              nsIRunnable* aPreparedRunnable,
                              ErrorResult& aRv);
 
   void
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -388,18 +388,16 @@ XULDocument::StartDocumentLoad(const cha
     // NOTE: If this ever starts calling nsDocument::StartDocumentLoad
     // we'll possibly need to reset our content type afterwards.
     mStillWalking = true;
     mMayStartLayout = false;
     mDocumentLoadGroup = do_GetWeakReference(aLoadGroup);
 
     mChannel = aChannel;
 
-    mHaveInputEncoding = true;
-
     // Get the URI.  Note that this should match nsDocShell::OnLoadingSite
     nsresult rv =
         NS_GetFinalChannelURI(aChannel, getter_AddRefs(mDocumentURI));
     NS_ENSURE_SUCCESS(rv, rv);
     
     ResetStylesheetsToURI(mDocumentURI);
 
     RetrieveRelevantHeaders(aChannel);
--- a/image/DecodePool.cpp
+++ b/image/DecodePool.cpp
@@ -241,17 +241,17 @@ public:
 
 private:
   ~DecodePoolImpl() { }
 
   Work PopWorkFromQueue(nsTArray<nsRefPtr<Decoder>>& aQueue)
   {
     Work work;
     work.mType = Work::Type::DECODE;
-    work.mDecoder = aQueue.LastElement();
+    work.mDecoder = aQueue.LastElement().forget();
     aQueue.RemoveElementAt(aQueue.Length() - 1);
 
     return work;
   }
 
   nsThreadPoolNaming mThreadNaming;
 
   // mMonitor guards the queues and mShuttingDown.
--- a/ipc/chromium/src/base/message_pump_libevent.cc
+++ b/ipc/chromium/src/base/message_pump_libevent.cc
@@ -13,16 +13,29 @@
 #include "eintr_wrapper.h"
 #include "base/logging.h"
 #include "base/scoped_nsautorelease_pool.h"
 #include "base/time.h"
 #include "nsDependentSubstring.h"
 #include "third_party/libevent/event.h"
 #include "mozilla/UniquePtr.h"
 
+// This macro checks that the _EVENT_SIZEOF_* constants defined in
+// ipc/chromiume/src/third_party/<platform>/event2/event-config.h are correct.
+#define CHECK_EVENT_SIZEOF(TYPE, type) \
+    static_assert(_EVENT_SIZEOF_##TYPE == sizeof(type), \
+    "bad _EVENT_SIZEOF_"#TYPE);
+
+CHECK_EVENT_SIZEOF(LONG,      long);
+CHECK_EVENT_SIZEOF(LONG_LONG, long long);
+CHECK_EVENT_SIZEOF(PTHREAD_T, pthread_t);
+CHECK_EVENT_SIZEOF(SHORT,     short);
+CHECK_EVENT_SIZEOF(SIZE_T,    size_t);
+CHECK_EVENT_SIZEOF(VOID_P,    void*);
+
 // Lifecycle of struct event
 // Libevent uses two main data structures:
 // struct event_base (of which there is one per message pump), and
 // struct event (of which there is roughly one per socket).
 // The socket's struct event is created in
 // MessagePumpLibevent::WatchFileDescriptor(),
 // is owned by the FileDescriptorWatcher, and is destroyed in
 // StopWatchingFileDescriptor().
--- a/ipc/chromium/src/third_party/libevent/README.mozilla
+++ b/ipc/chromium/src/third_party/libevent/README.mozilla
@@ -1,19 +1,40 @@
-This is a clean copy of libevent-2.0.21-stable with the following modifications:
+This is a clean copy of libevent-2.0.21-stable with the following
+modifications.
 
 1. Add the following files:
 
-linux/event2/event-config.h
-mac/event2/event-config.h
-android/event2/event-config.h
+- linux/event2/event-config.h
+- mac/event2/event-config.h
+- bsd/event2/event-config.h
+- android/event2/event-config.h
 
-These files are taken from libevent-2.0.21-stable built on the development environment indicated by the first path component. You have to run "./configure" and "make" to get all of the pre-processing done. The file can then be found in "include/event2/".
+These files are taken from libevent-2.0.21-stable built on the development
+environment indicated by the first path component. You have to run
+"./configure" and "make" to get all of the pre-processing done. The file can
+then be found in "include/event2/".
 
-2. This is ugly, prepare yourself. OS X has a weird problem with how the "TAILQ_END(head)" is used, causing a linking error. Just replace all use of the "TAILQ_END(head)" macro with "NULL".
+You then need to modify the _EVENT_SIZEOF_* constants in the generated linux,
+mac and bsd headers to be appropriate for both 32-bit and 64-bit platforms. Use
+__LP64__ to distinguish the two cases. If you get something wrong the
+CHECK_EVENT_SIZEOF static assertions in message_pump_libevent.cc will fail.
 
-3. Apply "add mac-arc4random-buf.patch", which removes some bad OS X compatibility code. This will allow libevent to compile on all supported versions of OS X.
+2. This is ugly, prepare yourself. OS X has a weird problem with how the
+"TAILQ_END(head)" is used, causing a linking error. Just replace all use of the
+"TAILQ_END(head)" macro with "NULL".
 
-4. Apply "openbsd-no-arc4random_addrandom.patch", which fixes the build on OpenBSD (which doesnt provide arc4random_addrandom anymore, see #931354)
+3. Apply the following patches from
+ipc/chromium/src/third_party/libevent/patches/:
+
+- "mac-arc4random-buf.patch". This removes some bad OS X compatibility code.
+  This will allow libevent to compile on all supported versions of OS X.
 
-5. Apply "libevent-use-non-deprecated-syscalls.patch", which fixes the build on AArch64 architecture (which does not provide deprecated syscalls)
+- "openbsd-no-arc4random_addrandom.patch". This fixes the build on OpenBSD
+  (which doesn't provide arc4random_addrandom anymore, see #931354).
 
-6. Apply "libevent-dont-use-issetugid-on-android.patch'. which fixes the build on Android L preview
+- "use-non-deprecated-syscalls.patch". This fixes the build on AArch64
+  architecture (which does not provide deprecated syscalls).
+
+- "dont-use-issetugid-on-android.patch". This fixes the build on Android L
+  preview.
+
+- "avoid-empty-sighandler.patch". This fixes some OS X crashes.
--- a/ipc/chromium/src/third_party/libevent/bsd/event2/event-config.h
+++ b/ipc/chromium/src/third_party/libevent/bsd/event2/event-config.h
@@ -393,36 +393,61 @@
 
 /* Define to the version of this package. */
 #define _EVENT_PACKAGE_VERSION ""
 
 /* Define to necessary symbol if this constant uses a non-standard name on
    your system. */
 /* #undef _EVENT_PTHREAD_CREATE_JOINABLE */
 
+/* ------------------------------------------------------------------------ */
+/* MOZILLA NOTE: the following constants are hand-modified to be suitable   */
+/* for both 32-bit and 64-bit platforms. See README.mozilla for details.    */
+/* ------------------------------------------------------------------------ */
+
 /* The size of `int', as computed by sizeof. */
 #define _EVENT_SIZEOF_INT 4
 
 /* The size of `long', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_LONG 8
+#else
+#define _EVENT_SIZEOF_LONG 4
+#endif
 
 /* The size of `long long', as computed by sizeof. */
 #define _EVENT_SIZEOF_LONG_LONG 8
 
 /* The size of `pthread_t', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_PTHREAD_T 8
+#else
+#define _EVENT_SIZEOF_PTHREAD_T 4
+#endif
 
 /* The size of `short', as computed by sizeof. */
 #define _EVENT_SIZEOF_SHORT 2
 
 /* The size of `size_t', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_SIZE_T 8
+#else
+#define _EVENT_SIZEOF_SIZE_T 4
+#endif
 
 /* The size of `void *', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_VOID_P 8
+#else
+#define _EVENT_SIZEOF_VOID_P 4
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* END MOZILLA NOTE                                                         */
+/* ------------------------------------------------------------------------ */
 
 /* Define to 1 if you have the ANSI C header files. */
 #define _EVENT_STDC_HEADERS 1
 
 /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
 #define _EVENT_TIME_WITH_SYS_TIME 1
 
 /* Version number of package */
--- a/ipc/chromium/src/third_party/libevent/linux/event2/event-config.h
+++ b/ipc/chromium/src/third_party/libevent/linux/event2/event-config.h
@@ -393,36 +393,61 @@
 
 /* Define to the version of this package. */
 #define _EVENT_PACKAGE_VERSION ""
 
 /* Define to necessary symbol if this constant uses a non-standard name on
    your system. */
 /* #undef _EVENT_PTHREAD_CREATE_JOINABLE */
 
+/* ------------------------------------------------------------------------ */
+/* MOZILLA NOTE: the following constants are hand-modified to be suitable   */
+/* for both 32-bit and 64-bit platforms. See README.mozilla for details.    */
+/* ------------------------------------------------------------------------ */
+
 /* The size of `int', as computed by sizeof. */
 #define _EVENT_SIZEOF_INT 4
 
 /* The size of `long', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_LONG 8
+#else
+#define _EVENT_SIZEOF_LONG 4
+#endif
 
 /* The size of `long long', as computed by sizeof. */
 #define _EVENT_SIZEOF_LONG_LONG 8
 
 /* The size of `pthread_t', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_PTHREAD_T 8
+#else
+#define _EVENT_SIZEOF_PTHREAD_T 4
+#endif
 
 /* The size of `short', as computed by sizeof. */
 #define _EVENT_SIZEOF_SHORT 2
 
 /* The size of `size_t', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_SIZE_T 8
+#else
+#define _EVENT_SIZEOF_SIZE_T 4
+#endif
 
 /* The size of `void *', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_VOID_P 8
+#else
+#define _EVENT_SIZEOF_VOID_P 4
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* END MOZILLA NOTE                                                         */
+/* ------------------------------------------------------------------------ */
 
 /* Define to 1 if you have the ANSI C header files. */
 #define _EVENT_STDC_HEADERS 1
 
 /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
 #define _EVENT_TIME_WITH_SYS_TIME 1
 
 /* Version number of package */
--- a/ipc/chromium/src/third_party/libevent/mac/event2/event-config.h
+++ b/ipc/chromium/src/third_party/libevent/mac/event2/event-config.h
@@ -392,36 +392,61 @@
 
 /* Define to the version of this package. */
 #define _EVENT_PACKAGE_VERSION ""
 
 /* Define to necessary symbol if this constant uses a non-standard name on
    your system. */
 /* #undef _EVENT_PTHREAD_CREATE_JOINABLE */
 
+/* ------------------------------------------------------------------------ */
+/* MOZILLA NOTE: the following constants are hand-modified to be suitable   */
+/* for both 32-bit and 64-bit platforms. See README.mozilla for details.    */
+/* ------------------------------------------------------------------------ */
+
 /* The size of `int', as computed by sizeof. */
 #define _EVENT_SIZEOF_INT 4
 
 /* The size of `long', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_LONG 8
+#else
+#define _EVENT_SIZEOF_LONG 4
+#endif
 
 /* The size of `long long', as computed by sizeof. */
 #define _EVENT_SIZEOF_LONG_LONG 8
 
 /* The size of `pthread_t', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_PTHREAD_T 8
+#else
+#define _EVENT_SIZEOF_PTHREAD_T 4
+#endif
 
 /* The size of `short', as computed by sizeof. */
 #define _EVENT_SIZEOF_SHORT 2
 
 /* The size of `size_t', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_SIZE_T 8
+#else
+#define _EVENT_SIZEOF_SIZE_T 4
+#endif
 
 /* The size of `void *', as computed by sizeof. */
+#ifdef __LP64__
 #define _EVENT_SIZEOF_VOID_P 8
+#else
+#define _EVENT_SIZEOF_VOID_P 4
+#endif
+
+/* ------------------------------------------------------------------------ */
+/* END MOZILLA NOTE                                                         */
+/* ------------------------------------------------------------------------ */
 
 /* Define to 1 if you have the ANSI C header files. */
 #define _EVENT_STDC_HEADERS 1
 
 /* Define to 1 if you can safely include both <sys/time.h> and <time.h>. */
 #define _EVENT_TIME_WITH_SYS_TIME 1
 
 /* Version number of package */
rename from ipc/chromium/src/third_party/libevent-avoid-empty-sighandler.patch
rename to ipc/chromium/src/third_party/libevent/patches/avoid-empty-sighandler.patch
rename from ipc/chromium/src/third_party/libevent-dont-use-issetugid-on-android.patch
rename to ipc/chromium/src/third_party/libevent/patches/dont-use-issetugid-on-android.patch
rename from ipc/chromium/src/third_party/libevent/mac-arc4random-buf.patch
rename to ipc/chromium/src/third_party/libevent/patches/mac-arc4random-buf.patch
rename from ipc/chromium/src/third_party/libevent/openbsd-no-arc4random_addrandom.patch
rename to ipc/chromium/src/third_party/libevent/patches/openbsd-no-arc4random_addrandom.patch
rename from ipc/chromium/src/third_party/libevent-use-non-deprecated-syscalls.patch
rename to ipc/chromium/src/third_party/libevent/patches/use-non-deprecated-syscalls.patch
--- a/js/ipc/JavaScriptBase.h
+++ b/js/ipc/JavaScriptBase.h
@@ -82,19 +82,18 @@ class JavaScriptBase : public WrapperOwn
     bool RecvCallOrConstruct(const uint64_t& objId, InfallibleTArray<JSParam>&& argv,
                              const bool& construct, ReturnStatus* rs, JSVariant* result,
                              nsTArray<JSParam>* outparams) {
         return Answer::RecvCallOrConstruct(ObjectId::deserialize(objId), Move(argv), construct, rs, result, outparams);
     }
     bool RecvHasInstance(const uint64_t& objId, const JSVariant& v, ReturnStatus* rs, bool* bp) {
         return Answer::RecvHasInstance(ObjectId::deserialize(objId), v, rs, bp);
     }
-    bool RecvObjectClassIs(const uint64_t& objId, const uint32_t& classValue,
-                             bool* result) {
-        return Answer::RecvObjectClassIs(ObjectId::deserialize(objId), classValue, result);
+    bool RecvGetBuiltinClass(const uint64_t& objId, ReturnStatus* rs, uint32_t* classValue) {
+        return Answer::RecvGetBuiltinClass(ObjectId::deserialize(objId), rs, classValue);
     }
     bool RecvIsArray(const uint64_t& objId, ReturnStatus* rs, uint32_t* answer) {
         return Answer::RecvIsArray(ObjectId::deserialize(objId), rs, answer);
     }
     bool RecvClassName(const uint64_t& objId, nsCString* result) {
         return Answer::RecvClassName(ObjectId::deserialize(objId), result);
     }
     bool RecvGetPrototype(const uint64_t& objId, ReturnStatus* rs, ObjectOrNullVariant* result) {
@@ -173,19 +172,18 @@ class JavaScriptBase : public WrapperOwn
     bool SendCallOrConstruct(const ObjectId& objId, const nsTArray<JSParam>& argv,
                              const bool& construct, ReturnStatus* rs, JSVariant* result,
                              nsTArray<JSParam>* outparams) {
         return Base::SendCallOrConstruct(objId.serialize(), argv, construct, rs, result, outparams);
     }
     bool SendHasInstance(const ObjectId& objId, const JSVariant& v, ReturnStatus* rs, bool* bp) {
         return Base::SendHasInstance(objId.serialize(), v, rs, bp);
     }
-    bool SendObjectClassIs(const ObjectId& objId, const uint32_t& classValue,
-                           bool* result) {
-        return Base::SendObjectClassIs(objId.serialize(), classValue, result);
+    bool SendGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs, uint32_t* classValue) {
+        return Base::SendGetBuiltinClass(objId.serialize(), rs, classValue);
     }
     bool SendIsArray(const ObjectId& objId, ReturnStatus* rs, uint32_t* answer)
     {
         return Base::SendIsArray(objId.serialize(), rs, answer);
     }
     bool SendClassName(const ObjectId& objId, nsCString* result) {
         return Base::SendClassName(objId.serialize(), result);
     }
--- a/js/ipc/PJavaScript.ipdl
+++ b/js/ipc/PJavaScript.ipdl
@@ -33,17 +33,17 @@ both:
     prio(high) sync Has(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     prio(high) sync HasOwn(uint64_t objId, JSIDVariant id) returns (ReturnStatus rs, bool has);
     prio(high) sync Get(uint64_t objId, JSVariant receiver, JSIDVariant id) returns (ReturnStatus rs, JSVariant result);
     prio(high) sync Set(uint64_t objId, JSIDVariant id, JSVariant value, JSVariant receiver) returns (ReturnStatus rs);
 
     prio(high) sync IsExtensible(uint64_t objId) returns (ReturnStatus rs, bool result);
     prio(high) sync CallOrConstruct(uint64_t objId, JSParam[] argv, bool construct) returns (ReturnStatus rs, JSVariant result, JSParam[] outparams);
     prio(high) sync HasInstance(uint64_t objId, JSVariant v) returns (ReturnStatus rs, bool has);
-    prio(high) sync ObjectClassIs(uint64_t objId, uint32_t classValue) returns (bool result);
+    prio(high) sync GetBuiltinClass(uint64_t objId) returns (ReturnStatus rs, uint32_t classValue);
     prio(high) sync IsArray(uint64_t objId) returns (ReturnStatus rs, uint32_t ans);
     prio(high) sync ClassName(uint64_t objId) returns (nsCString name);
     prio(high) sync GetPrototype(uint64_t objId) returns (ReturnStatus rs, ObjectOrNullVariant result);
     prio(high) sync RegExpToShared(uint64_t objId) returns (ReturnStatus rs, nsString source, uint32_t flags);
 
     prio(high) sync GetPropertyKeys(uint64_t objId, uint32_t flags) returns (ReturnStatus rs, JSIDVariant[] ids);
     prio(high) sync InstanceOf(uint64_t objId, JSIID iid) returns (ReturnStatus rs, bool instanceof);
     prio(high) sync DOMInstanceOf(uint64_t objId, int prototypeID, int depth) returns (ReturnStatus rs, bool instanceof);
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -495,36 +495,39 @@ WrapperAnswer::RecvHasInstance(const Obj
 
     if (!JS_HasInstance(cx, obj, val, bp))
         return fail(jsapi, rs);
 
     return ok(rs);
 }
 
 bool
-WrapperAnswer::RecvObjectClassIs(const ObjectId& objId, const uint32_t& classValue,
-                                 bool* result)
+WrapperAnswer::RecvGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs,
+                                   uint32_t* classValue)
 {
+    *classValue = js::ESClass_Other;
+
     AutoJSAPI jsapi;
     if (NS_WARN_IF(!jsapi.Init(scopeForTargetObjects())))
         return false;
     jsapi.TakeOwnershipOfErrorReporting();
     JSContext* cx = jsapi.cx();
 
     RootedObject obj(cx, findObjectById(cx, objId));
-    if (!obj) {
-        // This is very unfortunate, but we have no choice.
-        *result = false;
-        return true;
-    }
+    if (!obj)
+        return fail(jsapi, rs);
+
+    LOG("%s.getBuiltinClass()", ReceiverObj(objId));
 
-    LOG("%s.objectClassIs()", ReceiverObj(objId));
+    js::ESClassValue cls;
+    if (!js::GetBuiltinClass(cx, obj, &cls))
+        return fail(jsapi, rs);
 
-    *result = js::ObjectClassIs(cx, obj, (js::ESClassValue)classValue);
-    return true;
+    *classValue = cls;
+    return ok(rs);
 }
 
 bool
 WrapperAnswer::RecvIsArray(const ObjectId& objId, ReturnStatus* rs,
                            uint32_t* ans)
 {
     *ans = uint32_t(IsArrayAnswer::NotArray);
 
@@ -605,17 +608,16 @@ WrapperAnswer::RecvRegExpToShared(const 
         return false;
     jsapi.TakeOwnershipOfErrorReporting();
     JSContext* cx = jsapi.cx();
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(jsapi, rs);
 
-    MOZ_RELEASE_ASSERT(JS_ObjectIsRegExp(cx, obj));
     RootedString sourceJSStr(cx, JS_GetRegExpSource(cx, obj));
     if (!sourceJSStr)
         return fail(jsapi, rs);
     nsAutoJSString sourceStr;
     if (!sourceStr.init(cx, sourceJSStr))
         return fail(jsapi, rs);
     source->Assign(sourceStr);
 
--- a/js/ipc/WrapperAnswer.h
+++ b/js/ipc/WrapperAnswer.h
@@ -46,18 +46,18 @@ class WrapperAnswer : public virtual Jav
                  const JSVariant& receiverVar, ReturnStatus* rs);
 
     bool RecvIsExtensible(const ObjectId& objId, ReturnStatus* rs,
                           bool* result);
     bool RecvCallOrConstruct(const ObjectId& objId, InfallibleTArray<JSParam>&& argv,
                              const bool& construct, ReturnStatus* rs, JSVariant* result,
                              nsTArray<JSParam>* outparams);
     bool RecvHasInstance(const ObjectId& objId, const JSVariant& v, ReturnStatus* rs, bool* bp);
-    bool RecvObjectClassIs(const ObjectId& objId, const uint32_t& classValue,
-                           bool* result);
+    bool RecvGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs,
+                             uint32_t* classValue);
     bool RecvIsArray(const ObjectId& objId, ReturnStatus* rs, uint32_t* ans);
     bool RecvClassName(const ObjectId& objId, nsCString* result);
     bool RecvGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result);
     bool RecvRegExpToShared(const ObjectId& objId, ReturnStatus* rs, nsString* source, uint32_t* flags);
 
     bool RecvGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
                              ReturnStatus* rs, nsTArray<JSIDVariant>* ids);
     bool RecvInstanceOf(const ObjectId& objId, const JSIID& iid,
--- a/js/ipc/WrapperOwner.cpp
+++ b/js/ipc/WrapperOwner.cpp
@@ -120,18 +120,18 @@ class CPOWProxyHandler : public BaseProx
 
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const override;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                               AutoIdVector& props) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy,
                              MutableHandleValue v, bool* bp) const override;
-    virtual bool objectClassIs(HandleObject obj, js::ESClassValue classValue,
-                               JSContext* cx) const override;
+    virtual bool getBuiltinClass(JSContext* cx, HandleObject obj,
+                                 js::ESClassValue* classValue) const override;
     virtual bool isArray(JSContext* cx, HandleObject obj,
                          IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override;
     virtual void objectMoved(JSObject* proxy, const JSObject* old) const override;
     virtual bool isCallable(JSObject* obj) const override;
     virtual bool isConstructor(JSObject* obj) const override;
@@ -717,35 +717,37 @@ WrapperOwner::hasInstance(JSContext* cx,
         return ipcfail(cx);
 
     LOG_STACK();
 
     return ok(cx, status);
 }
 
 bool
-CPOWProxyHandler::objectClassIs(HandleObject proxy, js::ESClassValue classValue, JSContext* cx) const
+CPOWProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
+                                  js::ESClassValue* classValue) const
 {
-    FORWARD(objectClassIs, (cx, proxy, classValue));
+    FORWARD(getBuiltinClass, (cx, proxy, classValue));
 }
 
 bool
-WrapperOwner::objectClassIs(JSContext* cx, HandleObject proxy, js::ESClassValue classValue)
+WrapperOwner::getBuiltinClass(JSContext* cx, HandleObject proxy,
+                              js::ESClassValue* classValue)
 {
     ObjectId objId = idOf(proxy);
 
-    // This function is assumed infallible, so we just return false if the IPC
-    // channel fails.
-    bool result;
-    if (!SendObjectClassIs(objId, classValue, &result))
-        return false;
+    uint32_t cls = ESClass_Other;
+    ReturnStatus status;
+    if (!SendGetBuiltinClass(objId, &status, &cls))
+        return ipcfail(cx);
+    *classValue = ESClassValue(cls);
 
     LOG_STACK();
 
-    return result;
+    return ok(cx, status);
 }
 
 bool
 CPOWProxyHandler::isArray(JSContext* cx, HandleObject proxy,
                           IsArrayAnswer* answer) const
 {
     FORWARD(isArray, (cx, proxy, answer));
 }
--- a/js/ipc/WrapperOwner.h
+++ b/js/ipc/WrapperOwner.h
@@ -49,17 +49,17 @@ class WrapperOwner : public virtual Java
 
     // SpiderMonkey extensions.
     bool getPropertyDescriptor(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
                                JS::MutableHandle<JSPropertyDescriptor> desc);
     bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp);
     bool getOwnEnumerablePropertyKeys(JSContext* cx, JS::HandleObject proxy,
                                       JS::AutoIdVector& props);
     bool hasInstance(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleValue v, bool* bp);
-    bool objectClassIs(JSContext* cx, JS::HandleObject obj, js::ESClassValue classValue);
+    bool getBuiltinClass(JSContext* cx, JS::HandleObject proxy, js::ESClassValue* classValue);
     bool isArray(JSContext* cx, JS::HandleObject proxy, JS::IsArrayAnswer* answer);
     const char* className(JSContext* cx, JS::HandleObject proxy);
     bool getPrototype(JSContext* cx, JS::HandleObject proxy, JS::MutableHandleObject protop);
 
     bool regexp_toShared(JSContext* cx, JS::HandleObject proxy, js::RegExpGuard* g);
 
     nsresult instanceOf(JSObject* obj, const nsID* id, bool* bp);
 
@@ -134,18 +134,18 @@ class WrapperOwner : public virtual Java
 
     virtual bool SendIsExtensible(const ObjectId& objId, ReturnStatus* rs,
                                   bool* result) = 0;
     virtual bool SendCallOrConstruct(const ObjectId& objId, const nsTArray<JSParam>& argv,
                                      const bool& construct, ReturnStatus* rs, JSVariant* result,
                                      nsTArray<JSParam>* outparams) = 0;
     virtual bool SendHasInstance(const ObjectId& objId, const JSVariant& v,
                                  ReturnStatus* rs, bool* bp) = 0;
-    virtual bool SendObjectClassIs(const ObjectId& objId, const uint32_t& classValue,
-                                   bool* result) = 0;
+    virtual bool SendGetBuiltinClass(const ObjectId& objId, ReturnStatus* rs,
+                                     uint32_t* classValue) = 0;
     virtual bool SendIsArray(const ObjectId& objId, ReturnStatus* rs,
                              uint32_t* answer) = 0;
     virtual bool SendClassName(const ObjectId& objId, nsCString* result) = 0;
     virtual bool SendGetPrototype(const ObjectId& objId, ReturnStatus* rs, ObjectOrNullVariant* result) = 0;
     virtual bool SendRegExpToShared(const ObjectId& objId, ReturnStatus* rs, nsString* source,
                                     uint32_t* flags) = 0;
 
     virtual bool SendGetPropertyKeys(const ObjectId& objId, const uint32_t& flags,
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -826,31 +826,21 @@ Valueify(const JSClass* c)
 
 /*
  * Enumeration describing possible values of the [[Class]] internal property
  * value of objects.
  */
 enum ESClassValue {
     ESClass_Object, ESClass_Array, ESClass_Number, ESClass_String,
     ESClass_Boolean, ESClass_RegExp, ESClass_ArrayBuffer, ESClass_SharedArrayBuffer,
-    ESClass_Date, ESClass_Set, ESClass_Map
-};
+    ESClass_Date, ESClass_Set, ESClass_Map,
 
-/*
- * Return whether the given object has the given [[Class]] internal property
- * value. Beware, this query says nothing about the js::Class of the JSObject
- * so the caller must not assume anything about obj's representation (e.g., obj
- * may be a proxy).
- */
-inline bool
-ObjectClassIs(JSObject& obj, ESClassValue classValue, JSContext* cx);
-
-/* Just a helper that checks v.isObject before calling ObjectClassIs. */
-inline bool
-IsObjectWithClass(const JS::Value& v, ESClassValue classValue, JSContext* cx);
+    // None of the above.
+    ESClass_Other
+};
 
 /* Fills |vp| with the unboxed value for boxed types, or undefined otherwise. */
 inline bool
 Unbox(JSContext* cx, JS::HandleObject obj, JS::MutableHandleValue vp);
 
 #ifdef DEBUG
 JS_FRIEND_API(bool)
 HasObjectMovedOp(JSObject* obj);
--- a/js/public/Proxy.h
+++ b/js/public/Proxy.h
@@ -317,17 +317,18 @@ class JS_FRIEND_API(BaseProxyHandler)
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const;
     virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp) const;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                               AutoIdVector& props) const;
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp) const;
-    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const;
+    virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy,
+                                 ESClassValue* classValue) const;
     virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const;
     virtual const char* className(JSContext* cx, HandleObject proxy) const;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const;
     virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const;
     virtual void trace(JSTracer* trc, JSObject* proxy) const;
     virtual void finalize(JSFreeOp* fop, JSObject* proxy) const;
@@ -408,18 +409,18 @@ class JS_FRIEND_API(DirectProxyHandler) 
     virtual bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id,
                         bool* bp) const override;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                               AutoIdVector& props) const override;
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
                              bool* bp) const override;
-    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
-                               JSContext* cx) const override;
+    virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy,
+                                 ESClassValue* classValue) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy,
                          JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
                                    unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
                                  RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
--- a/js/public/UbiNode.h
+++ b/js/public/UbiNode.h
@@ -781,48 +781,62 @@ class Node {
         static bool match(const Node& k, const Lookup& l) { return k == l; }
         static void rekey(Node& k, const Node& newKey) { k = newKey; }
     };
 };
 
 
 /*** Edge and EdgeRange ***************************************************************************/
 
-// Edge is the abstract base class representing an outgoing edge of a node.
-// Edges are owned by EdgeRanges, and need not have assignment operators or copy
-// constructors.
-//
-// Each Edge class should inherit from this base class, overriding as
-// appropriate.
+// An outgoing edge to a referent node.
 class Edge {
-  protected:
+  public:
     Edge() : name(nullptr), referent() { }
-    virtual ~Edge() { }
+
+    // Construct an initialized Edge, taking ownership of |name|.
+    Edge(char16_t* name, const Node& referent) {
+        this->name = name;
+        this->referent = referent;
+    }
 
-  public:
+    // Move construction and assignment.
+    Edge(Edge&& rhs) {
+        name = rhs.name;
+        referent = rhs.referent;
+        rhs.name = nullptr;
+    }
+    Edge& operator=(Edge&& rhs) {
+        MOZ_ASSERT(&rhs != this);
+        this->~Edge();
+        new (this) Edge(mozilla::Move(rhs));
+        return *this;
+    }
+
+    ~Edge() {
+        js_free(const_cast<char16_t*>(name));
+    }
+
+    Edge(const Edge&) = delete;
+    Edge& operator=(const Edge&) = delete;
+
     // This edge's name. This may be nullptr, if Node::edges was called with
     // false as the wantNames parameter.
     //
     // The storage is owned by this Edge, and will be freed when this Edge is
     // destructed.
     //
     // (In real life we'll want a better representation for names, to avoid
     // creating tons of strings when the names follow a pattern; and we'll need
     // to think about lifetimes carefully to ensure traversal stays cheap.)
     const char16_t* name;
 
     // This edge's referent.
     Node referent;
-
-  private:
-    Edge(const Edge&) = delete;
-    Edge& operator=(const Edge&) = delete;
 };
 
-
 // EdgeRange is an abstract base class for iterating over a node's outgoing
 // edges. (This is modeled after js::HashTable<K,V>::Range.)
 //
 // Concrete instances of this class need not be as lightweight as Node itself,
 // since they're usually only instantiated while iterating over a particular
 // object's edges. For example, a dumb implementation for JS Cells might use
 // JS::TraceChildren to to get the outgoing edges, and then store them in an
 // array internal to the EdgeRange.
@@ -849,65 +863,32 @@ class EdgeRange {
     virtual void popFront() = 0;
 
   private:
     EdgeRange(const EdgeRange&) = delete;
     EdgeRange& operator=(const EdgeRange&) = delete;
 };
 
 
-// A dumb Edge concrete class. All but the most essential members have the
-// default behavior.
-class SimpleEdge : public Edge {
-    SimpleEdge(SimpleEdge&) = delete;
-    SimpleEdge& operator=(const SimpleEdge&) = delete;
-
-  public:
-    SimpleEdge() : Edge() { }
-
-    // Construct an initialized SimpleEdge, taking ownership of |name|.
-    SimpleEdge(char16_t* name, const Node& referent) {
-        this->name = name;
-        this->referent = referent;
-    }
-    ~SimpleEdge() {
-        js_free(const_cast<char16_t*>(name));
-    }
-
-    // Move construction and assignment.
-    SimpleEdge(SimpleEdge&& rhs) {
-        name = rhs.name;
-        referent = rhs.referent;
-
-        rhs.name = nullptr;
-    }
-    SimpleEdge& operator=(SimpleEdge&& rhs) {
-        MOZ_ASSERT(&rhs != this);
-        this->~SimpleEdge();
-        new(this) SimpleEdge(mozilla::Move(rhs));
-        return *this;
-    }
-};
-
-typedef mozilla::Vector<SimpleEdge, 8, js::TempAllocPolicy> SimpleEdgeVector;
+typedef mozilla::Vector<Edge, 8, js::TempAllocPolicy> EdgeVector;
 
 // An EdgeRange concrete class that holds a pre-existing vector of
-// SimpleEdges. A PreComputedEdgeRange does not take ownership of its
-// SimpleEdgeVector; it is up to the PreComputedEdgeRange's consumer to manage
+// Edges. A PreComputedEdgeRange does not take ownership of its
+// EdgeVector; it is up to the PreComputedEdgeRange's consumer to manage
 // that lifetime.
 class PreComputedEdgeRange : public EdgeRange {
-    SimpleEdgeVector& edges;
-    size_t            i;
+    EdgeVector& edges;
+    size_t      i;
 
     void settle() {
         front_ = i < edges.length() ? &edges[i] : nullptr;
     }
 
   public:
-    explicit PreComputedEdgeRange(JSContext* cx, SimpleEdgeVector& edges)
+    explicit PreComputedEdgeRange(JSContext* cx, EdgeVector& edges)
       : edges(edges),
         i(0)
     {
         settle();
     }
 
     void popFront() override {
         MOZ_ASSERT(!empty());
@@ -946,18 +927,18 @@ class PreComputedEdgeRange : public Edge
 //
 //        ...
 //    }
 class MOZ_STACK_CLASS RootList {
     Maybe<AutoCheckCannotGC>& noGC;
     JSContext*               cx;
 
   public:
-    SimpleEdgeVector edges;
-    bool             wantNames;
+    EdgeVector edges;
+    bool       wantNames;
 
     RootList(JSContext* cx, Maybe<AutoCheckCannotGC>& noGC, bool wantNames = false);
 
     // Find all GC roots.
     bool init();
     // Find only GC roots in the provided set of |Zone|s.
     bool init(ZoneSet& debuggees);
     // Find only GC roots in the given Debugger object's set of debuggee zones.
@@ -1019,20 +1000,32 @@ class TracerConcreteWithCompartment : pu
   public:
     static void construct(void* storage, Referent* ptr) {
         new (storage) TracerConcreteWithCompartment(ptr);
     }
 };
 
 // Define specializations for some commonly-used public JSAPI types.
 // These can use the generic templates above.
-template<> struct Concrete<JS::Symbol> : TracerConcrete<JS::Symbol> { };
+template<>
+struct Concrete<JS::Symbol> : TracerConcrete<JS::Symbol> {
+    Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+  protected:
+    explicit Concrete(JS::Symbol* ptr) : TracerConcrete(ptr) { }
+
+  public:
+    static void construct(void* storage, JS::Symbol* ptr) {
+        new (storage) Concrete(ptr);
+    }
+};
 
 template<> struct Concrete<JSScript> : TracerConcreteWithCompartment<JSScript> {
     CoarseType coarseType() const final { return CoarseType::Script; }
+    Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
 
   protected:
     explicit Concrete(JSScript *ptr) : TracerConcreteWithCompartment<JSScript>(ptr) { }
 
   public:
     static void construct(void *storage, JSScript *ptr) { new (storage) Concrete(ptr); }
 };
 
--- a/js/src/builtin/RegExp.cpp
+++ b/js/src/builtin/RegExp.cpp
@@ -220,17 +220,20 @@ CompileRegExpObject(JSContext* cx, RegEx
     }
 
     RootedValue patternValue(cx, args.get(0));
 
     /*
      * 21.2.3.1 step 5
      * B.2.5.1 step 3.
      */
-    if (IsObjectWithClass(patternValue, ESClass_RegExp, cx)) {
+    ESClassValue cls;
+    if (!GetClassOfValue(cx, patternValue, &cls))
+        return false;
+    if (cls == ESClass_RegExp) {
         /*
          * B.2.5.1 step 3.a.
          */
         if (args.hasDefined(1) && creationMode == CreateForCompile) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NEWREGEXP_FLAGGED);
             return false;
         }
 
@@ -350,17 +353,21 @@ js::IsRegExp(JSContext* cx, HandleValue 
 
     /* Step 4. */
     if (!isRegExp.isUndefined()) {
         *result = ToBoolean(isRegExp);
         return true;
     }
 
     /* Steps 5-6. */
-    *result = IsObjectWithClass(value, ESClass_RegExp, cx);
+    ESClassValue cls;
+    if (!GetClassOfValue(cx, value, &cls))
+        return false;
+
+    *result = cls == ESClass_RegExp;
     return true;
 }
 
 /* ES6 draft rc4 B.2.5.1. */
 MOZ_ALWAYS_INLINE bool
 regexp_compile_impl(JSContext* cx, const CallArgs& args)
 {
     MOZ_ASSERT(IsRegExpObject(args.thisv()));
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -2459,16 +2459,44 @@ ByteSize(JSContext* cx, unsigned argc, V
             args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
         else
             args.rval().setUndefined();
     }
     return true;
 }
 
 static bool
+ByteSizeOfScript(JSContext*cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (!args.requireAtLeast(cx, "byteSizeOfScript", 1))
+        return false;
+    if (!args[0].isObject() || !args[0].toObject().is<JSFunction>()) {
+        JS_ReportError(cx, "Argument must be a Function object");
+        return false;
+    }
+
+    RootedScript script(cx, args[0].toObject().as<JSFunction>().getOrCreateScript(cx));
+    mozilla::MallocSizeOf mallocSizeOf = cx->runtime()->debuggerMallocSizeOf;
+
+    {
+        // We can't tolerate the GC moving things around while we're using a
+        // ubi::Node. Check that nothing we do causes a GC.
+        JS::AutoCheckCannotGC autoCannotGC;
+
+        JS::ubi::Node node = script;
+        if (node)
+            args.rval().setNumber(uint32_t(node.size(mallocSizeOf)));
+        else
+            args.rval().setUndefined();
+    }
+    return true;
+}
+
+static bool
 ImmutablePrototypesEnabled(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     args.rval().setBoolean(JS_ImmutablePrototypesEnabled());
     return true;
 }
 
@@ -3219,16 +3247,20 @@ gc::ZealModeHelpText),
 "    options.locals - show local variables in each frame\n"
 "    options.thisprops - show the properties of the 'this' object of each frame\n"),
 
     JS_FN_HELP("byteSize", ByteSize, 1, 0,
 "byteSize(value)",
 "  Return the size in bytes occupied by |value|, or |undefined| if value\n"
 "  is not allocated in memory.\n"),
 
+    JS_FN_HELP("byteSizeOfScript", ByteSizeOfScript, 1, 0,
+"byteSizeOfScript(f)",
+"  Return the size in bytes occupied by the function |f|'s JSScript.\n"),
+
     JS_FN_HELP("immutablePrototypesEnabled", ImmutablePrototypesEnabled, 0, 0,
 "immutablePrototypesEnabled()",
 "  Returns true if immutable-prototype behavior (triggered by setImmutablePrototype)\n"
 "  is enabled, such that modifying an immutable prototype will fail."),
 
     JS_FN_HELP("setImmutablePrototype", SetImmutablePrototype, 1, 0,
 "setImmutablePrototype(obj)",
 "  Try to make obj's [[Prototype]] immutable, such that subsequent attempts to\n"
--- a/js/src/ctypes/CTypes.cpp
+++ b/js/src/ctypes/CTypes.cpp
@@ -37,16 +37,17 @@
 #include "jsnum.h"
 #include "jsprf.h"
 
 #include "builtin/TypedObject.h"
 #include "ctypes/Library.h"
 #include "gc/Zone.h"
 
 #include "jsatominlines.h"
+#include "jsobjinlines.h"
 
 using namespace std;
 using mozilla::NumericLimits;
 
 using JS::AutoCheckCannotGC;
 
 namespace js {
 namespace ctypes {
@@ -3178,87 +3179,91 @@ ImplicitConvert(JSContext* cx,
           dest[sourceLength] = 0;
 
         break;
       }
       default:
         return ConvError(cx, targetType, val, convType, funObj, argIndex,
                          arrObj, arrIndex);
       }
-
-    } else if (val.isObject() && JS_IsArrayObject(cx, valObj)) {
-      // Convert each element of the array by calling ImplicitConvert.
-      uint32_t sourceLength;
-      if (!JS_GetArrayLength(cx, valObj, &sourceLength) ||
-          targetLength != size_t(sourceLength)) {
-        MOZ_ASSERT(!funObj);
-        return ArrayLengthMismatch(cx, targetLength, targetType,
-                                   size_t(sourceLength), val, convType);
-      }
-
-      // Convert into an intermediate, in case of failure.
-      size_t elementSize = CType::GetSize(baseType);
-      size_t arraySize = elementSize * targetLength;
-      auto intermediate = cx->make_pod_array<char>(arraySize);
-      if (!intermediate) {
-        JS_ReportAllocationOverflow(cx);
+    } else {
+      ESClassValue cls;
+      if (!GetClassOfValue(cx, val, &cls))
         return false;
-      }
-
-      for (uint32_t i = 0; i < sourceLength; ++i) {
-        RootedValue item(cx);
-        if (!JS_GetElement(cx, valObj, i, &item))
+
+      if (cls == ESClass_Array) {
+        // Convert each element of the array by calling ImplicitConvert.
+        uint32_t sourceLength;
+        if (!JS_GetArrayLength(cx, valObj, &sourceLength) ||
+            targetLength != size_t(sourceLength)) {
+          MOZ_ASSERT(!funObj);
+          return ArrayLengthMismatch(cx, targetLength, targetType,
+                                     size_t(sourceLength), val, convType);
+        }
+
+        // Convert into an intermediate, in case of failure.
+        size_t elementSize = CType::GetSize(baseType);
+        size_t arraySize = elementSize * targetLength;
+        auto intermediate = cx->make_pod_array<char>(arraySize);
+        if (!intermediate) {
+          JS_ReportAllocationOverflow(cx);
           return false;
-
-        char* data = intermediate.get() + elementSize * i;
-        if (!ImplicitConvert(cx, item, baseType, data, convType, nullptr,
-                             funObj, argIndex, targetType, i))
-          return false;
-      }
-
-      memcpy(buffer, intermediate.get(), arraySize);
-
-    } else if (val.isObject() && JS_IsArrayBufferObject(valObj)) {
-      // Check that array is consistent with type, then
-      // copy the array.
-      uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj);
-      size_t elementSize = CType::GetSize(baseType);
-      size_t arraySize = elementSize * targetLength;
-      if (arraySize != size_t(sourceLength)) {
-        MOZ_ASSERT(!funObj);
-        return ArrayLengthMismatch(cx, arraySize, targetType,
-                                   size_t(sourceLength), val, convType);
-      }
-      JS::AutoCheckCannotGC nogc;
-      memcpy(buffer, JS_GetArrayBufferData(valObj, nogc), sourceLength);
-      break;
-    } else if (val.isObject() && JS_IsTypedArrayObject(valObj)) {
-      // Check that array is consistent with type, then
-      // copy the array.
-      if(!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
+        }
+
+        RootedValue item(cx);
+        for (uint32_t i = 0; i < sourceLength; ++i) {
+          if (!JS_GetElement(cx, valObj, i, &item))
+            return false;
+
+          char* data = intermediate.get() + elementSize * i;
+          if (!ImplicitConvert(cx, item, baseType, data, convType, nullptr,
+                               funObj, argIndex, targetType, i))
+            return false;
+        }
+
+        memcpy(buffer, intermediate.get(), arraySize);
+      } else if (cls == ESClass_ArrayBuffer) {
+        // Check that array is consistent with type, then
+        // copy the array.
+        uint32_t sourceLength = JS_GetArrayBufferByteLength(valObj);
+        size_t elementSize = CType::GetSize(baseType);
+        size_t arraySize = elementSize * targetLength;
+        if (arraySize != size_t(sourceLength)) {
+          MOZ_ASSERT(!funObj);
+          return ArrayLengthMismatch(cx, arraySize, targetType,
+                                     size_t(sourceLength), val, convType);
+        }
+        JS::AutoCheckCannotGC nogc;
+        memcpy(buffer, JS_GetArrayBufferData(valObj, nogc), sourceLength);
+        break;
+      } else if (JS_IsTypedArrayObject(valObj)) {
+        // Check that array is consistent with type, then
+        // copy the array.
+        if (!CanConvertTypedArrayItemTo(baseType, valObj, cx)) {
+          return ConvError(cx, targetType, val, convType, funObj, argIndex,
+                           arrObj, arrIndex);
+        }
+
+        uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj);
+        size_t elementSize = CType::GetSize(baseType);
+        size_t arraySize = elementSize * targetLength;
+        if (arraySize != size_t(sourceLength)) {
+          MOZ_ASSERT(!funObj);
+          return ArrayLengthMismatch(cx, arraySize, targetType,
+                                     size_t(sourceLength), val, convType);
+        }
+        JS::AutoCheckCannotGC nogc;
+        memcpy(buffer, JS_GetArrayBufferViewData(valObj, nogc), sourceLength);
+        break;
+      } else {
+        // Don't implicitly convert to string. Users can implicitly convert
+        // with `String(x)` or `""+x`.
         return ConvError(cx, targetType, val, convType, funObj, argIndex,
                          arrObj, arrIndex);
       }
-
-      uint32_t sourceLength = JS_GetTypedArrayByteLength(valObj);
-      size_t elementSize = CType::GetSize(baseType);
-      size_t arraySize = elementSize * targetLength;
-      if (arraySize != size_t(sourceLength)) {
-        MOZ_ASSERT(!funObj);
-        return ArrayLengthMismatch(cx, arraySize, targetType,
-                                   size_t(sourceLength), val, convType);
-      }
-      JS::AutoCheckCannotGC nogc;
-      memcpy(buffer, JS_GetArrayBufferViewData(valObj, nogc), sourceLength);
-      break;
-    } else {
-      // Don't implicitly convert to string. Users can implicitly convert
-      // with `String(x)` or `""+x`.
-      return ConvError(cx, targetType, val, convType, funObj, argIndex,
-                       arrObj, arrIndex);
     }
     break;
   }
   case TYPE_struct: {
     if (val.isObject() && !sourceData) {
       // Enumerate the properties of the object; if they match the struct
       // specification, convert the fields.
       Rooted<IdVector> props(cx, IdVector(cx));
@@ -5433,20 +5438,26 @@ StructType::Create(JSContext* cx, unsign
   // have undefined size and alignment and no ffi_type.
   RootedObject result(cx, CType::Create(cx, typeProto, nullptr, TYPE_struct,
                                         name.toString(), JS::UndefinedValue(),
                                         JS::UndefinedValue(), nullptr));
   if (!result)
     return false;
 
   if (args.length() == 2) {
-    RootedObject arr(cx, args[1].isPrimitive() ? nullptr : &args[1].toObject());
-    if (!arr || !JS_IsArrayObject(cx, arr)) {
+    RootedObject arr(cx, args[1].isObject() ? &args[1].toObject() : nullptr);
+    bool isArray;
+    if (!arr) {
+        isArray = false;
+    } else {
+        if (!JS_IsArrayObject(cx, arr, &isArray))
+           return false;
+    }
+    if (!isArray)
       return ArgumentTypeMismatch(cx, "second ", "StructType", "an array");
-    }
 
     // Define the struct fields.
     if (!DefineInternal(cx, result, arr))
       return false;
   }
 
   args.rval().setObject(*result);
   return true;
@@ -5700,27 +5711,36 @@ 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", "");
   }
 
-  Value arg = args[0];
+  HandleValue arg = args[0];
   if (arg.isPrimitive()) {
     return ArgumentTypeMismatch(cx, "", "StructType.prototype.define",
                                 "an array");
   }
-  RootedObject arr(cx, arg.toObjectOrNull());
-  if (!JS_IsArrayObject(cx, arr)) {
+
+  bool isArray;
+  if (!arg.isObject()) {
+    isArray = false;
+  } else {
+    if (!JS_IsArrayObject(cx, arg, &isArray))
+      return false;
+  }
+
+  if (!isArray) {
     return ArgumentTypeMismatch(cx, "", "StructType.prototype.define",
                                 "an array");
   }
 
+  RootedObject arr(cx, &arg.toObject());
   return DefineInternal(cx, obj, arr);
 }
 
 bool
 StructType::ConstructData(JSContext* cx,
                           HandleObject obj,
                           const CallArgs& args)
 {
@@ -5890,17 +5910,16 @@ StructType::FieldsArrayGetter(JSContext*
     if (!fields)
       return false;
     JS_SetReservedSlot(obj, SLOT_FIELDS, ObjectValue(*fields));
 
     args.rval().setObject(*fields);
   }
 
   MOZ_ASSERT(args.rval().isObject());
-  MOZ_ASSERT(JS_IsArrayObject(cx, args.rval()));
   return true;
 }
 
 bool
 StructType::FieldGetter(JSContext* cx, unsigned argc, Value* vp)
 {
   CallArgs args = CallArgsFromVp(argc, vp);
 
@@ -6340,21 +6359,28 @@ FunctionType::Create(JSContext* cx, unsi
     return ArgumentLengthError(cx, "FunctionType", "two or three", "s");
   }
 
   AutoValueVector argTypes(cx);
   RootedObject arrayObj(cx, nullptr);
 
   if (args.length() == 3) {
     // Prepare an array of Values for the arguments.
-    if (args[2].isObject())
-      arrayObj = &args[2].toObject();
-    if (!arrayObj || !JS_IsArrayObject(cx, arrayObj)) {
+    bool isArray;
+    if (!args[2].isObject()) {
+      isArray = false;
+    } else {
+      if (!JS_IsArrayObject(cx, args[2], &isArray))
+        return false;
+    }
+
+    if (!isArray)
       return ArgumentTypeMismatch(cx, "third ", "FunctionType", "an array");
-    }
+
+    arrayObj = &args[2].toObject();
 
     uint32_t len;
     ASSERT_OK(JS_GetArrayLength(cx, arrayObj, &len));
 
     if (!argTypes.resize(len)) {
       JS_ReportOutOfMemory(cx);
       return false;
     }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -49,25 +49,26 @@ using mozilla::Maybe;
 
 using JS::AutoGCRooter;
 
 JSFunction::AutoParseUsingFunctionBox::AutoParseUsingFunctionBox(ExclusiveContext* cx,
                                                                  frontend::FunctionBox* funbox)
   : fun_(cx, funbox->function()),
     oldEnv_(cx, fun_->environment())
 {
+    fun_->unsetEnvironment();
     fun_->setFunctionBox(funbox);
     funbox->computeAllowSyntax(fun_);
     funbox->computeInWith(fun_);
 }
 
 JSFunction::AutoParseUsingFunctionBox::~AutoParseUsingFunctionBox()
 {
     fun_->unsetFunctionBox();
-    fun_->setEnvironment(oldEnv_);
+    fun_->initEnvironment(oldEnv_);
 }
 
 namespace js {
 namespace frontend {
 
 typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
 typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
 typedef Rooted<NestedScopeObject*> RootedNestedScopeObject;
@@ -4861,23 +4862,23 @@ Parser<FullParseHandler>::exportDeclarat
             !tokenStream.currentToken().nameContainsEscape())
         {
             MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
 
             Node moduleSpec = stringLiteral();
             if (!moduleSpec)
                 return null();
 
-            if (!MatchOrInsertSemicolon(tokenStream))
+            if (!MatchOrInsertSemicolon(tokenStream, TokenStream::Operand))
                 return null();
 
             return handler.newExportFromDeclaration(begin, kid, moduleSpec);
-        } else {
-            tokenStream.ungetToken();
-        }
+        }
+
+        tokenStream.ungetToken();
 
         if (!MatchOrInsertSemicolon(tokenStream, TokenStream::Operand))
             return null();
         break;
       }
 
       case TOK_MUL: {
         kid = handler.newList(PNK_EXPORT_SPEC_LIST);
@@ -4889,38 +4890,34 @@ Parser<FullParseHandler>::exportDeclarat
         Node exportSpec = handler.newNullary(PNK_EXPORT_BATCH_SPEC, JSOP_NOP, pos());
         if (!exportSpec)
             return null();
 
         handler.addList(kid, exportSpec);
 
         if (!tokenStream.getToken(&tt))
             return null();
-        if (tt == TOK_NAME && tokenStream.currentName() == context->names().from) {
-            if (!checkUnescapedName())
-                return null();
-
-            MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
-
-            Node moduleSpec = stringLiteral();
-            if (!moduleSpec)
-                return null();
-
-            if (!MatchOrInsertSemicolon(tokenStream))
-                return null();
-
-            return handler.newExportFromDeclaration(begin, kid, moduleSpec);
-        } else {
+        if (tt != TOK_NAME || tokenStream.currentName() != context->names().from) {
             report(ParseError, false, null(), JSMSG_FROM_AFTER_EXPORT_STAR);
             return null();
         }
 
-        if (!MatchOrInsertSemicolon(tokenStream))
-            return null();
-        break;
+        if (!checkUnescapedName())
+            return null();
+
+        MUST_MATCH_TOKEN(TOK_STRING, JSMSG_MODULE_SPEC_AFTER_FROM);
+
+        Node moduleSpec = stringLiteral();
+        if (!moduleSpec)
+            return null();
+
+        if (!MatchOrInsertSemicolon(tokenStream, TokenStream::Operand))
+            return null();
+
+        return handler.newExportFromDeclaration(begin, kid, moduleSpec);
       }
 
       case TOK_FUNCTION:
         kid = functionStmt(YieldIsKeyword, NameRequired);
         if (!kid)
             return null();
 
         if (!addExportName(kid->pn_funbox->function()->atom()))
--- a/js/src/jit-test/lib/census.js
+++ b/js/src/jit-test/lib/census.js
@@ -1,45 +1,55 @@
 // Functions for checking results returned by Debugger.Memory.prototype.takeCensus.
 
 const Census = {};
 
 (function () {
 
-  // Census.walkCensus(subject, name, walker)
+  // Census.walkCensus(subject, name, walker[, ignore])
   //
   // Use |walker| to check |subject|, a census object of the sort returned by
   // Debugger.Memory.prototype.takeCensus: a tree of objects with integers at the
   // leaves. Use |name| as the name for |subject| in diagnostic messages. Return
   // the number of leaves of |subject| we visited.
   //
   // A walker is an object with three methods:
   //
   // - enter(prop): Return the walker we should use to check the property of the
   //   subject census named |prop|. This is for recursing into the subobjects of
   //   the subject.
   //
-  // - done(): Called after we have called 'enter' on every property of the
-  //   subject.
+  // - done(ignore): Called after we have called 'enter' on every property of
+  //   the subject. Passed the |ignore| set of properties.
   //
   // - check(value): Check |value|, a leaf in the subject.
   //
   // Walker methods are expected to simply throw if a node we visit doesn't look
   // right.
-  Census.walkCensus = (subject, name, walker) => walk(subject, name, walker, 0);
-  function walk(subject, name, walker, count) {
+  //
+  // The optional |ignore| parameter allows you to specify a |Set| of property
+  // names which should be ignored. The walker will not traverse such
+  // properties.
+  Census.walkCensus = (subject, name, walker, ignore = new Set()) =>
+    walk(subject, name, walker, ignore, 0);
+
+  function walk(subject, name, walker, ignore, count) {
     if (typeof subject === 'object') {
       print(name);
       for (let prop in subject) {
+        if (ignore.has(prop)) {
+          continue;
+        }
         count = walk(subject[prop],
                      name + "[" + uneval(prop) + "]",
                      walker.enter(prop),
+                     ignore,
                      count);
       }
-      walker.done();
+      walker.done(ignore);
     } else {
       print(name + " = " + uneval(subject));
       walker.check(subject);
       count++;
     }
 
     return count;
   }
@@ -88,17 +98,17 @@ const Census = {};
             unvisited.delete(prop);
             if (prop in basis) {
               return makeWalker(basis[prop]);
             } else {
               return extra(prop);
             }
           },
 
-          done: () => unvisited.forEach(prop => missing(prop, basis[prop])),
+          done: ignore => [...unvisited].filter(p => !ignore.has(p)).forEach(p => missing(p, basis[p])),
           check: expectedObject
         };
       } else {
         return {
           enter: expectedLeaf,
           done: expectedLeaf,
           check: elt => compare(elt, basis)
         };
--- a/js/src/jit-test/tests/debug/Memory-takeCensus-03.js
+++ b/js/src/jit-test/tests/debug/Memory-takeCensus-03.js
@@ -10,17 +10,17 @@ Census.walkCensus(census0, "census0", Ce
 var g1 = newGlobal();
 dbg.addDebuggee(g1);
 var census1 = dbg.memory.takeCensus();
 Census.walkCensus(census1, "census1", Census.assertAllNotLessThan(census0));
 
 var g2 = newGlobal();
 dbg.addDebuggee(g2);
 var census2 = dbg.memory.takeCensus();
-Census.walkCensus(census2, "census2", Census.assertAllNotLessThan(census1));
+Census.walkCensus(census2, "census2", Census.assertAllNotLessThan(census1), new Set(["bytes"]));
 
 dbg.removeDebuggee(g2);
 var census3 = dbg.memory.takeCensus();
-Census.walkCensus(census3, "census3", Census.assertAllEqual(census1));
+Census.walkCensus(census3, "census3", Census.assertAllEqual(census1), new Set(["bytes"]));
 
 dbg.removeDebuggee(g1);
 var census4 = dbg.memory.takeCensus();
 Census.walkCensus(census4, "census4", Census.assertAllEqual(census0));
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-scripts.js
@@ -0,0 +1,46 @@
+// Check JS::ubi::Node::size results for scripts. We don't attempt to check
+// exact sizes in this test (deemed to difficult and non-deterministic), just
+// some sanity checks.
+
+function f1() {
+  return 42;
+}
+
+print("byteSizeOfScript(f1) = " + byteSizeOfScript(f1));
+assertEq(byteSizeOfScript(f1) > 1, true);
+
+function f2(n) {
+  var obj = {
+    x: 1,
+    y: 2,
+    z: 3,
+  };
+
+  if (i % 2 == 0) {
+    for (var i = 0; i < n; i++) {
+      this.x += i;
+      print(uneval(i));
+      obj[i] = i * i;
+      if (i > 10) {
+        f2(i / f1());
+      }
+    }
+  }
+
+  if (i % 3 == 0) {
+    for (var i = 0; i < n; i++) {
+      this.x *= i;
+      print(uneval(i));
+      obj[i] = i * i;
+      if (i > 10) {
+        f2(i / f1());
+      }
+    }
+  }
+
+  return this.x;
+}
+
+print("byteSizeOfScript(f2) = " + byteSizeOfScript(f2));
+assertEq(byteSizeOfScript(f2) > 1, true);
+assertEq(byteSizeOfScript(f2) > byteSizeOfScript(f1), true);
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/heap-analysis/byteSize-of-symbol.js
@@ -0,0 +1,25 @@
+// Check JS::ubi::Node::size results for symbols.
+
+// We actually hard-code specific sizes into this test, even though they're
+// implementation details, because in practice there are only two architecture
+// variants to consider (32-bit and 64-bit), and if these sizes change, that's
+// something SpiderMonkey hackers really want to know; they're supposed to be
+// stable.
+
+// Run this test only if we're using jemalloc. Other malloc implementations
+// exhibit surprising behaviors. For example, 32-bit Fedora builds have
+// non-deterministic allocation sizes.
+var config = getBuildConfiguration();
+if (!config['moz-memory'])
+  quit(0);
+
+const SIZE_OF_SYMBOL = config['pointer-byte-size'] == 4 ? 16 : 24;
+
+// Without a description.
+assertEq(byteSize(Symbol()), SIZE_OF_SYMBOL);
+
+// With a description.
+assertEq(byteSize(Symbol("This is a relatively long description to be passed to "
+                         + "Symbol() but it doesn't matter because it just gets "
+                         + "interned as a JSAtom* anyways.")),
+         SIZE_OF_SYMBOL);
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -103,16 +103,19 @@ class JitCode : public gc::TenuredCell
         return raw() <= addr_u8 && addr_u8 < rawEnd();
     }
     size_t instructionsSize() const {
         return insnSize_;
     }
     size_t bufferSize() const {
         return bufferSize_;
     }
+    size_t headerSize() const {
+        return headerSize_;
+    }
 
     void traceChildren(JSTracer* trc);
     void finalize(FreeOp* fop);
     void fixupAfterMovingGC() {}
     void setInvalidated() {
         invalidated_ = true;
     }
 
@@ -774,16 +777,22 @@ IsMarked(const jit::VMFunction*)
 
 // JS::ubi::Nodes can point to js::jit::JitCode instances; they're js::gc::Cell
 // instances with no associated compartment.
 namespace JS {
 namespace ubi {
 template<>
 struct Concrete<js::jit::JitCode> : TracerConcrete<js::jit::JitCode> {
     CoarseType coarseType() const final { return CoarseType::Script; }
+    Size size(mozilla::MallocSizeOf mallocSizeOf) const override {
+        Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
+        size += get().bufferSize();
+        size += get().headerSize();
+        return size;
+    }
 
   protected:
     explicit Concrete(js::jit::JitCode *ptr) : TracerConcrete<js::jit::JitCode>(ptr) { }
 
   public:
     static void construct(void *storage, js::jit::JitCode *ptr) { new (storage) Concrete(ptr); }
 };
 
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -12422,16 +12422,22 @@ class MTypeBarrier
     bool alwaysBails() const {
         // If mirtype of input doesn't agree with mirtype of barrier,
         // we will definitely bail.
         MIRType type = resultTypeSet()->getKnownMIRType();
         if (type == MIRType_Value)
             return false;
         if (input()->type() == MIRType_Value)
             return false;
+        if (input()->type() == MIRType_ObjectOrNull) {
+            // The ObjectOrNull optimization is only performed when the
+            // barrier's type is MIRType_Null.
+            MOZ_ASSERT(type == MIRType_Null);
+            return false;
+        }
         return input()->type() != type;
     }
 
     ALLOW_CLONE(MTypeBarrier)
 };
 
 // Like MTypeBarrier, guard that the value is in the given type set. This is
 // used before property writes to ensure the value being written is represented
--- a/js/src/jsapi-tests/testNewObject.cpp
+++ b/js/src/jsapi-tests/testNewObject.cpp
@@ -57,41 +57,46 @@ BEGIN_TEST(testNewObject_1)
     static const size_t N = 1000;
     JS::AutoValueVector argv(cx);
     CHECK(argv.resize(N));
 
     JS::RootedValue v(cx);
     EVAL("Array", &v);
     JS::RootedObject Array(cx, v.toObjectOrNull());
 
+    bool isArray;
+
     // With no arguments.
     JS::RootedObject obj(cx, JS_New(cx, Array, JS::HandleValueArray::empty()));
     CHECK(obj);
     JS::RootedValue rt(cx, JS::ObjectValue(*obj));
-    CHECK(JS_IsArrayObject(cx, obj));
+    CHECK(JS_IsArrayObject(cx, obj, &isArray));
+    CHECK(isArray);
     uint32_t len;
     CHECK(JS_GetArrayLength(cx, obj, &len));
     CHECK_EQUAL(len, 0u);
 
     // With one argument.
     argv[0].setInt32(4);
     obj = JS_New(cx, Array, JS::HandleValueArray::subarray(argv, 0, 1));
     CHECK(obj);
     rt = JS::ObjectValue(*obj);
-    CHECK(JS_IsArrayObject(cx, obj));
+    CHECK(JS_IsArrayObject(cx, obj, &isArray));
+    CHECK(isArray);
     CHECK(JS_GetArrayLength(cx, obj, &len));
     CHECK_EQUAL(len, 4u);
 
     // With N arguments.
     for (size_t i = 0; i < N; i++)
         argv[i].setInt32(i);
     obj = JS_New(cx, Array, JS::HandleValueArray::subarray(argv, 0, N));
     CHECK(obj);
     rt = JS::ObjectValue(*obj);
-    CHECK(JS_IsArrayObject(cx, obj));
+    CHECK(JS_IsArrayObject(cx, obj, &isArray));
+    CHECK(isArray);
     CHECK(JS_GetArrayLength(cx, obj, &len));
     CHECK_EQUAL(len, N);
     CHECK(JS_GetElement(cx, obj, N - 1, &v));
     CHECK(v.isInt32(N - 1));
 
     // With JSClass.construct.
     static const JSClass cls = {
         "testNewObject_1",
--- a/js/src/jsapi-tests/testParseJSON.cpp
+++ b/js/src/jsapi-tests/testParseJSON.cpp
@@ -97,43 +97,49 @@ BEGIN_TEST(testParseJSON_success)
     CHECK(TryParse(cx, "\"\\n\"", expected));
     CHECK(TryParse(cx, "\"\\u000A\"", expected));
 
 
     // Arrays
     JS::RootedValue v(cx), v2(cx);
     JS::RootedObject obj(cx);
 
+    bool isArray;
+
     CHECK(Parse(cx, "[]", &v));
     CHECK(v.isObject());
     obj = &v.toObject();
-    CHECK(JS_IsArrayObject(cx, obj));
+    CHECK(JS_IsArrayObject(cx, obj, &isArray));
+    CHECK(isArray);
     CHECK(JS_GetProperty(cx, obj, "length", &v2));
     CHECK(v2.isInt32(0));
 
     CHECK(Parse(cx, "[1]", &v));
     CHECK(v.isObject());
     obj = &v.toObject();
-    CHECK(JS_IsArrayObject(cx, obj));
+    CHECK(JS_IsArrayObject(cx, obj, &isArray));
+    CHECK(isArray);
     CHECK(JS_GetProperty(cx, obj, "0", &v2));
     CHECK(v2.isInt32(1));
     CHECK(JS_GetProperty(cx, obj, "length", &v2));
     CHECK(v2.isInt32(1));
 
 
     // Objects
     CHECK(Parse(cx, "{}", &v));
     CHECK(v.isObject());
     obj = &v.toObject();
-    CHECK(!JS_IsArrayObject(cx, obj));
+    CHECK(JS_IsArrayObject(cx, obj, &isArray));
+    CHECK(!isArray);
 
     CHECK(Parse(cx, "{ \"f\": 17 }", &v));
     CHECK(v.isObject());
     obj = &v.toObject();
-    CHECK(!JS_IsArrayObject(cx, obj));
+    CHECK(JS_IsArrayObject(cx, obj, &isArray));
+    CHECK(!isArray);
     CHECK(JS_GetProperty(cx, obj, "f", &v2));
     CHECK(v2.isInt32(17));
 
     return true;
 }
 
 template<size_t N> static JSFlatString*
 NewString(JSContext* cx, const char16_t (&chars)[N])
--- a/js/src/jsapi-tests/testRegExp.cpp
+++ b/js/src/jsapi-tests/testRegExp.cpp
@@ -3,23 +3,27 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testObjectIsRegExp)
 {
     JS::RootedValue val(cx);
 
+    bool isRegExp;
+
     EVAL("new Object", &val);
     JS::RootedObject obj(cx, val.toObjectOrNull());
-    CHECK(!JS_ObjectIsRegExp(cx, obj));
+    CHECK(JS_ObjectIsRegExp(cx, obj, &isRegExp));
+    CHECK(!isRegExp);
 
     EVAL("/foopy/", &val);
     obj = val.toObjectOrNull();
-    CHECK(JS_ObjectIsRegExp(cx, obj));
+    CHECK(JS_ObjectIsRegExp(cx, obj, &isRegExp));
+    CHECK(isRegExp);
 
     return true;
 }
 END_TEST(testObjectIsRegExp)
 
 BEGIN_TEST(testGetRegExpFlags)
 {
     JS::RootedValue val(cx);
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3207,29 +3207,38 @@ JS_NewArrayObject(JSContext* cx, size_t 
     MOZ_ASSERT(!cx->runtime()->isAtomsCompartment(cx->compartment()));
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
     return NewDenseFullyAllocatedArray(cx, length);
 }
 
 JS_PUBLIC_API(bool)
-JS_IsArrayObject(JSContext* cx, JS::HandleObject obj)
+JS_IsArrayObject(JSContext* cx, JS::HandleObject obj, bool* isArray)
 {
     assertSameCompartment(cx, obj);
-    return ObjectClassIs(obj, ESClass_Array, cx);
-}
-
-JS_PUBLIC_API(bool)
-JS_IsArrayObject(JSContext* cx, JS::HandleValue value)
-{
-    if (!value.isObject())
+
+    ESClassValue cls;
+    if (!GetBuiltinClass(cx, obj, &cls))
         return false;
+
+    *isArray = cls == ESClass_Array;
+    return true;
+}
+
+JS_PUBLIC_API(bool)
+JS_IsArrayObject(JSContext* cx, JS::HandleValue value, bool* isArray)
+{
+    if (!value.isObject()) {
+        *isArray = false;
+        return true;
+    }
+
     RootedObject obj(cx, &value.toObject());
-    return JS_IsArrayObject(cx, obj);
+    return JS_IsArrayObject(cx, obj, isArray);
 }
 
 JS_PUBLIC_API(bool)
 JS_GetArrayLength(JSContext* cx, HandleObject obj, uint32_t* lengthp)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj);
@@ -5493,20 +5502,26 @@ JS_PUBLIC_API(JSObject*)
 JS::NewDateObject(JSContext* cx, JS::ClippedTime time)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     return NewDateObjectMsec(cx, time);
 }
 
 JS_PUBLIC_API(bool)
-JS_ObjectIsDate(JSContext* cx, HandleObject obj)
+JS_ObjectIsDate(JSContext* cx, HandleObject obj, bool* isDate)
 {
     assertSameCompartment(cx, obj);
-    return ObjectClassIs(obj, ESClass_Date, cx);
+
+    ESClassValue cls;
+    if (!GetBuiltinClass(cx, obj, &cls))
+        return false;
+
+    *isDate = cls == ESClass_Date;
+    return true;
 }
 
 JS_PUBLIC_API(void)
 JS_ClearDateCaches(JSContext* cx)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
     cx->runtime()->dateTimeInfo.updateTimeZoneAdjustment();
@@ -5631,20 +5646,26 @@ JS_ExecuteRegExpNoStatics(JSContext* cx,
     if (!input)
         return false;
 
     return ExecuteRegExpLegacy(cx, nullptr, obj->as<RegExpObject>(), input, indexp, test,
                                rval);
 }
 
 JS_PUBLIC_API(bool)
-JS_ObjectIsRegExp(JSContext* cx, HandleObject obj)
+JS_ObjectIsRegExp(JSContext* cx, HandleObject obj, bool* isRegExp)
 {
     assertSameCompartment(cx, obj);
-    return ObjectClassIs(obj, ESClass_RegExp, cx);
+
+    ESClassValue cls;
+    if (!GetBuiltinClass(cx, obj, &cls))
+        return false;
+
+    *isRegExp = cls == ESClass_RegExp;
+    return true;
 }
 
 JS_PUBLIC_API(unsigned)
 JS_GetRegExpFlags(JSContext* cx, HandleObject obj)
 {
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -2951,21 +2951,31 @@ JS_DeleteUCProperty(JSContext* cx, JS::H
                     JS::ObjectOpResult& result);
 
 extern JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, const JS::HandleValueArray& contents);
 
 extern JS_PUBLIC_API(JSObject*)
 JS_NewArrayObject(JSContext* cx, size_t length);
 
-extern JS_PUBLIC_API(bool)
-JS_IsArrayObject(JSContext* cx, JS::HandleValue value);
-
-extern JS_PUBLIC_API(bool)
-JS_IsArrayObject(JSContext* cx, JS::HandleObject obj);
+// Returns true and sets |*isArray| indicating whether |value| is an Array
+// object or a wrapper around one, otherwise returns false on failure.
+//
+// This method returns true with |*isArray == false| when passed a proxy whose
+// target is an Array, or when passed a revoked proxy.
+extern JS_PUBLIC_API(bool)
+JS_IsArrayObject(JSContext* cx, JS::HandleValue value, bool* isArray);
+
+// Returns true and sets |*isArray| indicating whether |obj| is an Array object
+// or a wrapper around one, otherwise returns false on failure.
+//
+// This method returns true with |*isArray == false| when passed a proxy whose
+// target is an Array, or when passed a revoked proxy.
+extern JS_PUBLIC_API(bool)
+JS_IsArrayObject(JSContext* cx, JS::HandleObject obj, bool* isArray);
 
 extern JS_PUBLIC_API(bool)
 JS_GetArrayLength(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t* lengthp);
 
 extern JS_PUBLIC_API(bool)
 JS_SetArrayLength(JSContext* cx, JS::Handle<JSObject*> obj, uint32_t length);
 
 extern JS_PUBLIC_API(bool)
@@ -4717,21 +4727,23 @@ SetForEach(JSContext *cx, HandleObject o
 
 /*
  * Dates.
  */
 
 extern JS_PUBLIC_API(JSObject*)
 JS_NewDateObject(JSContext* cx, int year, int mon, int mday, int hour, int min, int sec);
 
-/*
- * Infallible predicate to test whether obj is a date object.
- */
-extern JS_PUBLIC_API(bool)
-JS_ObjectIsDate(JSContext* cx, JS::HandleObject obj);
+// Returns true and sets |*isDate| indicating whether |obj| is a Date object or
+// a wrapper around one, otherwise returns false on failure.
+//
+// This method returns true with |*isDate == false| when passed a proxy whose
+// target is a Date, or when passed a revoked proxy.
+extern JS_PUBLIC_API(bool)
+JS_ObjectIsDate(JSContext* cx, JS::HandleObject obj, bool* isDate);
 
 /*
  * Clears the cache of calculated local time from each Date object.
  * Call to propagate a system timezone change.
  */
 extern JS_PUBLIC_API(void)
 JS_ClearDateCaches(JSContext* cx);
 
@@ -4772,18 +4784,23 @@ JS_NewRegExpObjectNoStatics(JSContext* c
 
 extern JS_PUBLIC_API(JSObject*)
 JS_NewUCRegExpObjectNoStatics(JSContext* cx, char16_t* chars, size_t length, unsigned flags);
 
 extern JS_PUBLIC_API(bool)
 JS_ExecuteRegExpNoStatics(JSContext* cx, JS::HandleObject reobj, char16_t* chars, size_t length,
                           size_t* indexp, bool test, JS::MutableHandleValue rval);
 
-extern JS_PUBLIC_API(bool)
-JS_ObjectIsRegExp(JSContext* cx, JS::HandleObject obj);
+// Returns true and sets |*isRegExp| indicating whether |obj| is a RegExp
+// object or a wrapper around one, otherwise returns false on failure.
+//
+// This method returns true with |*isRegExp == false| when passed a proxy whose
+// target is a RegExp, or when passed a revoked proxy.
+extern JS_PUBLIC_API(bool)
+JS_ObjectIsRegExp(JSContext* cx, JS::HandleObject obj, bool* isRegExp);
 
 extern JS_PUBLIC_API(unsigned)
 JS_GetRegExpFlags(JSContext* cx, JS::HandleObject obj);
 
 extern JS_PUBLIC_API(JSString*)
 JS_GetRegExpSource(JSContext* cx, JS::HandleObject obj);
 
 /************************************************************************/
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -2874,27 +2874,29 @@ static bool
 date_toString(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     // Step 2.a. (reordered)
     double tv = GenericNaN();
     if (args.thisv().isObject()) {
         // Step 1.
         RootedObject obj(cx, &args.thisv().toObject());
+
         // Step 2.
-        if (ObjectClassIs(obj, ESClass_Date, cx)) {
+        ESClassValue cls;
+        if (!GetBuiltinClass(cx, obj, &cls))
+            return false;
+
+        if (cls == ESClass_Date) {
             // Step 3.a.
             RootedValue unboxed(cx);
             if (!Unbox(cx, obj, &unboxed))
                 return false;
             tv = unboxed.toNumber();
         }
-        // ObjectClassIs can throw for objects from other compartments.
-        if (cx->isExceptionPending())
-            return false;
     }
     // Step 4.
     return date_format(cx, tv, FORMATSPEC_FULL, args.rval());
 }
 
 MOZ_ALWAYS_INLINE bool
 date_valueOf_impl(JSContext* cx, const CallArgs& args)
 {
@@ -3224,42 +3226,46 @@ js::NewDateObject(JSContext* cx, int yea
                   int hour, int min, int sec)
 {
     MOZ_ASSERT(mon < 12);
     double msec_time = MakeDate(MakeDay(year, mon, mday), MakeTime(hour, min, sec, 0.0));
     return NewDateObjectMsec(cx, TimeClip(UTC(msec_time, &cx->runtime()->dateTimeInfo)));
 }
 
 JS_FRIEND_API(bool)
-js::DateIsValid(JSContext* cx, JSObject* objArg)
+js::DateIsValid(JSContext* cx, HandleObject obj, bool* isValid)
 {
-    RootedObject obj(cx, objArg);
-    if (!ObjectClassIs(obj, ESClass_Date, cx))
+    ESClassValue cls;
+    if (!GetBuiltinClass(cx, obj, &cls))
         return false;
 
+    if (cls != ESClass_Date) {
+        *isValid = false;
+        return true;
+    }
+
     RootedValue unboxed(cx);
-    if (!Unbox(cx, obj, &unboxed)) {
-        // This can't actually happen, so we don't force consumers to deal with
-        // a clunky out-param API. Do something sane-ish if it does happen.
-        cx->clearPendingException();
+    if (!Unbox(cx, obj, &unboxed))
         return false;
+
+    *isValid = !IsNaN(unboxed.toNumber());
+    return true;
+}
+
+JS_FRIEND_API(bool)
+js::DateGetMsecSinceEpoch(JSContext* cx, HandleObject obj, double* msecsSinceEpoch)
+{
+    ESClassValue cls;
+    if (!GetBuiltinClass(cx, obj, &cls))
+        return false;
+
+    if (cls != ESClass_Date) {
+        *msecsSinceEpoch = 0;
+        return true;
     }
 
-    return !IsNaN(unboxed.toNumber());
-}
-
-JS_FRIEND_API(double)
-js::DateGetMsecSinceEpoch(JSContext* cx, JSObject* objArg)
-{
-    RootedObject obj(cx, objArg);
-    if (!ObjectClassIs(obj, ESClass_Date, cx))
-        return 0;
-
     RootedValue unboxed(cx);
-    if (!Unbox(cx, obj, &unboxed)) {
-        // This can't actually happen, so we don't force consumers to deal with
-        // a clunky out-param API. Do something sane-ish if it does happen.
-        cx->clearPendingException();
-        return 0;
-    }
-
-    return unboxed.toNumber();
+    if (!Unbox(cx, obj, &unboxed))
+        return false;
+
+    *msecsSinceEpoch = unboxed.toNumber();
+    return true;
 }
--- a/js/src/jsexn.cpp
+++ b/js/src/jsexn.cpp
@@ -1011,19 +1011,24 @@ js::ValueToSourceForError(JSContext* cx,
     if (!str) {
         JS_ClearPendingException(cx);
         return "<<error converting value to string>>";
     }
 
     StringBuffer sb(cx);
     if (val.isObject()) {
         RootedObject valObj(cx, val.toObjectOrNull());
-        if (JS_IsArrayObject(cx, valObj)) {
+        ESClassValue cls;
+        if (!GetBuiltinClass(cx, valObj, &cls)) {
+            JS_ClearPendingException(cx);
+            return "<<error determining class of value>>";
+        }
+        if (cls == ESClass_Array) {
             sb.append("the array ");
-        } else if (JS_IsArrayBufferObject(valObj)) {
+        } else if (cls == ESClass_ArrayBuffer) {
             sb.append("the array buffer ");
         } else if (JS_IsArrayBufferViewObject(valObj)) {
             sb.append("the typed array ");
         } else {
             sb.append("the object ");
         }
     } else if (val.isNumber()) {
         sb.append("the number ");
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -257,19 +257,47 @@ JS_DefineFunctionsWithHelp(JSContext* cx
                 return false;
         }
     }
 
     return true;
 }
 
 JS_FRIEND_API(bool)
-js::ObjectClassIs(JSContext* cx, HandleObject obj, ESClassValue classValue)
+js::GetBuiltinClass(JSContext* cx, HandleObject obj, ESClassValue* classValue)
 {
-    return ObjectClassIs(obj, classValue, cx);
+    if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
+        return Proxy::getBuiltinClass(cx, obj, classValue);
+
+    if (obj->is<PlainObject>() || obj->is<UnboxedPlainObject>())
+        *classValue = ESClass_Object;
+    else if (obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>())
+        *classValue = ESClass_Array;
+    else if (obj->is<NumberObject>())
+        *classValue = ESClass_Number;
+    else if (obj->is<StringObject>())
+        *classValue = ESClass_String;
+    else if (obj->is<BooleanObject>())
+        *classValue = ESClass_Boolean;
+    else if (obj->is<RegExpObject>())
+        *classValue = ESClass_RegExp;
+    else if (obj->is<ArrayBufferObject>())
+        *classValue = ESClass_ArrayBuffer;
+    else if (obj->is<SharedArrayBufferObject>())
+        *classValue = ESClass_SharedArrayBuffer;
+    else if (obj->is<DateObject>())
+        *classValue = ESClass_Date;
+    else if (obj->is<SetObject>())
+        *classValue = ESClass_Set;
+    else if (obj->is<MapObject>())
+        *classValue = ESClass_Map;
+    else
+        *classValue = ESClass_Other;
+
+    return true;
 }
 
 JS_FRIEND_API(const char*)
 js::ObjectClassName(JSContext* cx, HandleObject obj)
 {
     return GetObjectClassName(cx, obj);
 }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -175,17 +175,17 @@ JS_InitializePropertiesFromCompatibleNat
                                                   JS::HandleObject src);
 
 extern JS_FRIEND_API(JSString*)
 JS_BasicObjectToString(JSContext* cx, JS::HandleObject obj);
 
 namespace js {
 
 JS_FRIEND_API(bool)
-ObjectClassIs(JSContext* cx, JS::HandleObject obj, ESClassValue classValue);
+GetBuiltinClass(JSContext* cx, JS::HandleObject obj, ESClassValue* classValue);
 
 JS_FRIEND_API(const char*)
 ObjectClassName(JSContext* cx, JS::HandleObject obj);
 
 JS_FRIEND_API(void)
 ReportOverRecursed(JSContext* maybecx);
 
 JS_FRIEND_API(bool)
@@ -1253,25 +1253,22 @@ DOMProxyShadowsCheck GetDOMProxyShadowsC
 inline bool DOMProxyIsShadowing(DOMProxyShadowsResult result) {
     return result == Shadows ||
            result == ShadowsViaDirectExpando ||
            result == ShadowsViaIndirectExpando;
 }
 
 /* Implemented in jsdate.cpp. */
 
-/*
- * Detect whether the internal date value is NaN.  (Because failure is
- * out-of-band for js_DateGet*)
- */
+/* Detect whether the internal date value is NaN. */
 extern JS_FRIEND_API(bool)
-DateIsValid(JSContext* cx, JSObject* obj);
-
-extern JS_FRIEND_API(double)
-DateGetMsecSinceEpoch(JSContext* cx, JSObject* obj);
+DateIsValid(JSContext* cx, JS::HandleObject obj, bool* isValid);
+
+extern JS_FRIEND_API(bool)
+DateGetMsecSinceEpoch(JSContext* cx, JS::HandleObject obj, double* msecSinceEpoch);
 
 } /* namespace js */
 
 /* Implemented in jscntxt.cpp. */
 
 /*
  * Report an exception, which is currently realized as a printf-style format
  * string and its arguments.
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -314,27 +314,32 @@ class JSFunction : public js::NativeObje
      */
     JSObject* environment() const {
         MOZ_ASSERT(isInterpreted() && !isBeingParsed());
         return u.i.env_;
     }
 
     void setEnvironment(JSObject* obj) {
         MOZ_ASSERT(isInterpreted() && !isBeingParsed());
-        *(js::HeapPtrObject*)&u.i.env_ = obj;
+        *reinterpret_cast<js::HeapPtrObject*>(&u.i.env_) = obj;
     }
 
     void initEnvironment(JSObject* obj) {
         MOZ_ASSERT(isInterpreted() && !isBeingParsed());
-        ((js::HeapPtrObject*)&u.i.env_)->init(obj);
+        reinterpret_cast<js::HeapPtrObject*>(&u.i.env_)->init(obj);
+    }
+
+    void unsetEnvironment() {
+        setEnvironment(nullptr);
     }
 
   private:
     void setFunctionBox(js::frontend::FunctionBox* funbox) {
         MOZ_ASSERT(isInterpreted());
+        MOZ_ASSERT_IF(!isBeingParsed(), !environment());
         flags_ |= BEING_PARSED;
         u.i.funbox_ = funbox;
     }
 
     void unsetFunctionBox() {
         MOZ_ASSERT(isBeingParsed());
         flags_ &= ~BEING_PARSED;
         u.i.funbox_ = nullptr;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -531,17 +531,17 @@ class JSObject : public js::gc::Cell
      * is<BlockObject>, is<NestedScopeObject> and is<ScopeObject>. Each of
      * these has a respective class that derives and adds operations.
      *
      * A class XObject is defined in a vm/XObject{.h, .cpp, -inl.h} file
      * triplet (along with any class YObject that derives XObject).
      *
      * Note that X represents a low-level representation and does not query the
      * [[Class]] property of object defined by the spec (for this, see
-     * js::ObjectClassIs).
+     * js::GetBuiltinClass).
      */
 
     template <class T>
     inline bool is() const { return getClass() == &T::class_; }
 
     template <class T>
     T& as() {
         MOZ_ASSERT(this->is<T>());
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -6,16 +6,17 @@
 
 #ifndef jsobjinlines_h
 #define jsobjinlines_h
 
 #include "jsobj.h"
 
 #include "mozilla/DebugOnly.h"
 
+#include "jsfriendapi.h"
 #include "jsfun.h"
 
 #include "builtin/MapObject.h"
 #include "builtin/TypedObject.h"
 #include "gc/Allocator.h"
 #include "vm/ArrayObject.h"
 #include "vm/DateObject.h"
 #include "vm/NumberObject.h"
@@ -805,46 +806,29 @@ GuessObjectGCKind(size_t numSlots)
 static inline gc::AllocKind
 GuessArrayGCKind(size_t numSlots)
 {
     if (numSlots)
         return gc::GetGCArrayKind(numSlots);
     return gc::AllocKind::OBJECT8;
 }
 
+// Returns ESClass_Other if the value isn't an object, or if the object
+// isn't of one of the enumerated classes.  Otherwise returns the appropriate
+// class.
 inline bool
-ObjectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx)
+GetClassOfValue(JSContext* cx, HandleValue v, ESClassValue* classValue)
 {
-    if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
-        return Proxy::objectClassIs(obj, classValue, cx);
+    if (!v.isObject()) {
+        *classValue = ESClass_Other;
+        return true;
+    }
 
-    switch (classValue) {
-      case ESClass_Object: return obj->is<PlainObject>() || obj->is<UnboxedPlainObject>();
-      case ESClass_Array:
-        return obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>();
-      case ESClass_Number: return obj->is<NumberObject>();
-      case ESClass_String: return obj->is<StringObject>();
-      case ESClass_Boolean: return obj->is<BooleanObject>();
-      case ESClass_RegExp: return obj->is<RegExpObject>();
-      case ESClass_ArrayBuffer: return obj->is<ArrayBufferObject>();
-      case ESClass_SharedArrayBuffer: return obj->is<SharedArrayBufferObject>();
-      case ESClass_Date: return obj->is<DateObject>();
-      case ESClass_Set: return obj->is<SetObject>();
-      case ESClass_Map: return obj->is<MapObject>();
-    }
-    MOZ_CRASH("bad classValue");
-}
-
-inline bool
-IsObjectWithClass(const Value& v, ESClassValue classValue, JSContext* cx)
-{
-    if (!v.isObject())
-        return false;
     RootedObject obj(cx, &v.toObject());
-    return ObjectClassIs(obj, classValue, cx);
+    return GetBuiltinClass(cx, obj, classValue);
 }
 
 inline bool
 Unbox(JSContext* cx, HandleObject obj, MutableHandleValue vp)
 {
     if (MOZ_UNLIKELY(obj->is<ProxyObject>()))
         return Proxy::boxedValue_unbox(cx, obj, vp);
 
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -260,27 +260,32 @@ PreprocessValue(JSContext* cx, HandleObj
         if (!Invoke(cx, args))
             return false;
         vp.set(args.rval());
     }
 
     /* Step 4. */
     if (vp.get().isObject()) {
         RootedObject obj(cx, &vp.get().toObject());
-        if (ObjectClassIs(obj, ESClass_Number, cx)) {
+
+        ESClassValue cls;
+        if (!GetBuiltinClass(cx, obj, &cls))
+            return false;
+
+        if (cls == ESClass_Number) {
             double d;
             if (!ToNumber(cx, vp, &d))
                 return false;
             vp.setNumber(d);
-        } else if (ObjectClassIs(obj, ESClass_String, cx)) {
+        } else if (cls == ESClass_String) {
             JSString* str = ToStringSlow<CanGC>(cx, vp);
             if (!str)
                 return false;
             vp.setString(str);
-        } else if (ObjectClassIs(obj, ESClass_Boolean, cx)) {
+        } else if (cls == ESClass_Boolean) {
             if (!Unbox(cx, obj, vp))
                 return false;
         }
     }
 
     return true;
 }
 
@@ -620,25 +625,32 @@ js::Stringify(JSContext* cx, MutableHand
                     /* Step 4b(iv)(4). */
                     int32_t n;
                     if (v.isNumber() && ValueFitsInInt32(v, &n) && INT_FITS_IN_JSID(n)) {
                         id = INT_TO_JSID(n);
                     } else {
                         if (!ValueToId<CanGC>(cx, v, &id))
                             return false;
                     }
-                } else if (v.isString() ||
-                           IsObjectWithClass(v, ESClass_String, cx) ||
-                           IsObjectWithClass(v, ESClass_Number, cx))
-                {
-                    /* Step 4b(iv)(3), 4b(iv)(5). */
-                    if (!ValueToId<CanGC>(cx, v, &id))
-                        return false;
                 } else {
-                    continue;
+                    bool shouldAdd = v.isString();
+                    if (!shouldAdd) {
+                        ESClassValue cls;
+                        if (!GetClassOfValue(cx, v, &cls))
+                            return false;
+                        shouldAdd = cls == ESClass_String || cls == ESClass_Number;
+                    }
+
+                    if (shouldAdd) {
+                        /* Step 4b(iv)(3), 4b(iv)(5). */
+                        if (!ValueToId<CanGC>(cx, v, &id))
+                            return false;
+                    } else {
+                        continue;
+                    }
                 }
 
                 /* Step 4b(iv)(6). */
                 HashSet<jsid, JsidHasher>::AddPtr p = idSet.lookupForAdd(id);
                 if (!p) {
                     /* Step 4b(iv)(6)(a). */
                     if (!idSet.add(p, id) || !propertyList.append(id))
                         return false;
@@ -647,22 +659,27 @@ js::Stringify(JSContext* cx, MutableHand
         } else {
             replacer = nullptr;
         }
     }
 
     /* Step 5. */
     if (space.isObject()) {
         RootedObject spaceObj(cx, &space.toObject());
-        if (ObjectClassIs(spaceObj, ESClass_Number, cx)) {
+
+        ESClassValue cls;
+        if (!GetBuiltinClass(cx, spaceObj, &cls))
+            return false;
+
+        if (cls == ESClass_Number) {
             double d;
             if (!ToNumber(cx, space, &d))
                 return false;
             space = NumberValue(d);
-        } else if (ObjectClassIs(spaceObj, ESClass_String, cx)) {
+        } else if (cls == ESClass_String) {
             JSString* str = ToStringSlow<CanGC>(cx, space);
             if (!str)
                 return false;
             space = StringValue(str);
         }
     }
 
     StringBuffer gap(cx);
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -4314,8 +4314,36 @@ void
 JSScript::AutoDelazify::dropScript()
 {
     // Don't touch script_ if it's in the self-hosting compartment, see the
     // comment in holdScript.
     if (script_ && !script_->compartment()->isSelfHosting)
         script_->setDoNotRelazify(oldDoNotRelazify_);
     script_ = nullptr;
 }
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<JSScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    Size size = Arena::thingSize(get().asTenured().getAllocKind());
+
+    size += get().sizeOfData(mallocSizeOf);
+    size += get().sizeOfTypeScript(mallocSizeOf);
+
+    size_t baselineSize = 0;
+    size_t baselineStubsSize = 0;
+    jit::AddSizeOfBaselineData(&get(), mallocSizeOf, &baselineSize, &baselineStubsSize);
+    size += baselineSize;
+    size += baselineStubsSize;
+
+    size += jit::SizeOfIonData(&get(), mallocSizeOf);
+
+    MOZ_ASSERT(size > 0);
+    return size;
+}
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<js::LazyScript>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
+    size += get().sizeOfExcludingThis(mallocSizeOf);
+    return size;
+}
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -2439,16 +2439,17 @@ CloneGlobalScript(JSContext* cx, Handle<
 
 // JS::ubi::Nodes can point to js::LazyScripts; they're js::gc::Cell instances
 // with no associated compartment.
 namespace JS {
 namespace ubi {
 template<>
 struct Concrete<js::LazyScript> : TracerConcrete<js::LazyScript> {
     CoarseType coarseType() const final { return CoarseType::Script; }
+    Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
 
   protected:
     explicit Concrete(js::LazyScript *ptr) : TracerConcrete<js::LazyScript>(ptr) { }
 
   public:
     static void construct(void *storage, js::LazyScript *ptr) { new (storage) Concrete(ptr); }
 };
 } // namespace ubi
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2095,18 +2095,24 @@ class MOZ_STACK_CLASS StringRegExpGuard
   public:
     explicit StringRegExpGuard(JSContext* cx)
       : re_(cx), fm(cx), obj_(cx)
     { }
 
     /* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
     bool init(JSContext* cx, const CallArgs& args, bool convertVoid = false)
     {
-        if (args.length() != 0 && IsObjectWithClass(args[0], ESClass_RegExp, cx))
-            return initRegExp(cx, &args[0].toObject());
+        if (args.length() != 0) {
+            ESClassValue cls;
+            if (!GetClassOfValue(cx, args[0], &cls))
+                return false;
+
+            if (cls == ESClass_RegExp)
+                return initRegExp(cx, &args[0].toObject());
+        }
 
         if (convertVoid && !args.hasDefined(0)) {
             fm.pat_ = cx->runtime()->emptyString;
             return true;
         }
 
         JSString* arg = ArgToRootedString(cx, args, 0);
         if (!arg)
@@ -2116,19 +2122,16 @@ class MOZ_STACK_CLASS StringRegExpGuard
         if (!fm.pat_)
             return false;
 
         return true;
     }
 
     bool initRegExp(JSContext* cx, JSObject* regexp) {
         obj_ = regexp;
-
-        MOZ_ASSERT(ObjectClassIs(obj_, ESClass_RegExp, cx));
-
         return RegExpToShared(cx, obj_, &re_);
     }
 
     bool init(JSContext* cx, HandleString pattern) {
         fm.pat_ = AtomizeString(cx, pattern);
         if (!fm.pat_)
             return false;
         return true;
@@ -3856,17 +3859,21 @@ js::str_split(JSContext* cx, unsigned ar
         limit = UINT32_MAX;
     }
 
     /* Step 8. */
     RegExpGuard re(cx);
     RootedLinearString sepstr(cx);
     bool sepDefined = args.hasDefined(0);
     if (sepDefined) {
-        if (IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
+        ESClassValue cls;
+        if (!GetClassOfValue(cx, args[0], &cls))
+            return false;
+
+        if (cls == ESClass_RegExp) {
             RootedObject obj(cx, &args[0].toObject());
             if (!RegExpToShared(cx, obj, &re))
                 return false;
         } else {
             sepstr = ArgToRootedString(cx, args, 0);
             if (!sepstr)
                 return false;
         }
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -200,17 +200,18 @@ class JS_FRIEND_API(OpaqueCrossCompartme
 
     /* SpiderMonkey extensions. */
     virtual bool getPropertyDescriptor(JSContext* cx, HandleObject wrapper, HandleId id,
                                        MutableHandle<JSPropertyDescriptor> desc) const override;
     virtual bool hasOwn(JSContext* cx, HandleObject wrapper, HandleId id,
                         bool* bp) const override;
     virtual bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
                                               AutoIdVector& props) const override;
-    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const override;
+    virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper,
+                                 ESClassValue* classValue) const override;
     virtual bool isArray(JSContext* cx, HandleObject obj,
                          JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject wrapper) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
     virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp) const override;
 
     static const OpaqueCrossCompartmentWrapper singleton;
 };
@@ -242,18 +243,18 @@ class JS_FRIEND_API(SecurityWrapper) : p
     virtual bool preventExtensions(JSContext* cx, HandleObject wrapper,
                                    ObjectOpResult& result) const override;
     virtual bool setPrototype(JSContext* cx, HandleObject proxy, HandleObject proto,
                               ObjectOpResult& result) const override;
     virtual bool setImmutablePrototype(JSContext* cx, HandleObject proxy, bool* succeeded) const override;
 
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
-    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
-                               JSContext* cx) const override;
+    virtual bool getBuiltinClass(JSContext* cx, HandleObject wrapper,
+                                 ESClassValue* classValue) const override;
     virtual bool isArray(JSContext* cx, HandleObject wrapper, JS::IsArrayAnswer* answer) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp) const override;
     virtual bool defaultValue(JSContext* cx, HandleObject wrapper, JSType hint,
                               MutableHandleValue vp) const override;
 
     // Allow isCallable and isConstructor. They used to be class-level, and so could not be guarded
     // against.
--- a/js/src/proxy/BaseProxyHandler.cpp
+++ b/js/src/proxy/BaseProxyHandler.cpp
@@ -314,19 +314,21 @@ BaseProxyHandler::hasInstance(JSContext*
     assertEnteredPolicy(cx, proxy, JSID_VOID, GET);
     RootedValue val(cx, ObjectValue(*proxy.get()));
     ReportValueError(cx, JSMSG_BAD_INSTANCEOF_RHS,
                      JSDVG_SEARCH_STACK, val, nullptr);
     return false;
 }
 
 bool
-BaseProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext* cx) const
+BaseProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
+                                  ESClassValue* classValue) const
 {
-    return false;
+    *classValue = ESClass_Other;
+    return true;
 }
 
 bool
 BaseProxyHandler::isArray(JSContext* cx, HandleObject proxy, IsArrayAnswer* answer) const
 {
     *answer = IsArrayAnswer::NotArray;
     return true;
 }
--- a/js/src/proxy/DeadObjectProxy.cpp
+++ b/js/src/proxy/DeadObjectProxy.cpp
@@ -109,17 +109,18 @@ bool
 DeadObjectProxy::hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
                              bool* bp) const
 {
     ReportDead(cx);
     return false;
 }
 
 bool
-DeadObjectProxy::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const
+DeadObjectProxy::getBuiltinClass(JSContext* cx, HandleObject proxy,
+                                 ESClassValue* classValue) const
 {
     ReportDead(cx);
     return false;
 }
 
 bool
 DeadObjectProxy::isArray(JSContext* cx, HandleObject obj, JS::IsArrayAnswer* answer) const
 {
--- a/js/src/proxy/DeadObjectProxy.h
+++ b/js/src/proxy/DeadObjectProxy.h
@@ -38,18 +38,18 @@ class DeadObjectProxy : public BaseProxy
     virtual bool construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const override;
 
     /* SpiderMonkey extensions. */
     // BaseProxyHandler::getPropertyDescriptor will throw by calling getOwnPropertyDescriptor.
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
                              bool* bp) const override;
-    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
-                               JSContext* cx) const override;
+    virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy,
+                                 ESClassValue* classValue) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g) const override;
     virtual bool defaultValue(JSContext* cx, HandleObject obj, JSType hint,
                               MutableHandleValue vp) const override;
 
     static const char family;
--- a/js/src/proxy/DirectProxyHandler.cpp
+++ b/js/src/proxy/DirectProxyHandler.cpp
@@ -149,21 +149,21 @@ DirectProxyHandler::preventExtensions(JS
 bool
 DirectProxyHandler::isExtensible(JSContext* cx, HandleObject proxy, bool* extensible) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return IsExtensible(cx, target, extensible);
 }
 
 bool
-DirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
-                                  JSContext* cx) const
+DirectProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
+                                    ESClassValue* classValue) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
-    return ObjectClassIs(target, classValue, cx);
+    return GetBuiltinClass(cx, target, classValue);
 }
 
 bool
 DirectProxyHandler::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     return IsArray(cx, target, answer);
 }
--- a/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
+++ b/js/src/proxy/OpaqueCrossCompartmentWrapper.cpp
@@ -146,20 +146,21 @@ OpaqueCrossCompartmentWrapper::hasOwn(JS
 bool
 OpaqueCrossCompartmentWrapper::getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject wrapper,
                                                             AutoIdVector& props) const
 {
     return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, wrapper, props);
 }
 
 bool
-OpaqueCrossCompartmentWrapper::objectClassIs(HandleObject obj, ESClassValue classValue,
-                                             JSContext* cx) const
+OpaqueCrossCompartmentWrapper::getBuiltinClass(JSContext* cx, HandleObject wrapper,
+                                               ESClassValue* classValue) const
 {
-  return false;
+    *classValue = ESClass_Other;
+    return true;
 }
 
 bool
 OpaqueCrossCompartmentWrapper::isArray(JSContext* cx, HandleObject obj,
                                        JS::IsArrayAnswer* answer) const
 {
     *answer = JS::IsArrayAnswer::NotArray;
     return true;
--- a/js/src/proxy/Proxy.cpp
+++ b/js/src/proxy/Proxy.cpp
@@ -450,20 +450,20 @@ Proxy::hasInstance(JSContext* cx, Handle
     *bp = false; // default result if we refuse to perform this action
     AutoEnterPolicy policy(cx, handler, proxy, JSID_VOIDHANDLE, BaseProxyHandler::GET, true);
     if (!policy.allowed())
         return policy.returnValue();
     return proxy->as<ProxyObject>().handler()->hasInstance(cx, proxy, v, bp);
 }
 
 bool
-Proxy::objectClassIs(HandleObject proxy, ESClassValue classValue, JSContext* cx)
+Proxy::getBuiltinClass(JSContext* cx, HandleObject proxy, ESClassValue* classValue)
 {
     JS_CHECK_RECURSION(cx, return false);
-    return proxy->as<ProxyObject>().handler()->objectClassIs(proxy, classValue, cx);
+    return proxy->as<ProxyObject>().handler()->getBuiltinClass(cx, proxy, classValue);
 }
 
 bool
 Proxy::isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer)
 {
     return proxy->as<ProxyObject>().handler()->isArray(cx, proxy, answer);
 }
 
--- a/js/src/proxy/Proxy.h
+++ b/js/src/proxy/Proxy.h
@@ -51,17 +51,17 @@ class Proxy
     static bool getPropertyDescriptor(JSContext* cx, HandleObject proxy, HandleId id,
                                       MutableHandle<JSPropertyDescriptor> desc);
     static bool hasOwn(JSContext* cx, HandleObject proxy, HandleId id, bool* bp);
     static bool getOwnEnumerablePropertyKeys(JSContext* cx, HandleObject proxy,
                                              AutoIdVector& props);
     static bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                            const CallArgs& args);
     static bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v, bool* bp);
-    static bool objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx);
+    static bool getBuiltinClass(JSContext* cx, HandleObject proxy, ESClassValue* classValue);
     static bool isArray(JSContext* cx, HandleObject proxy, JS::IsArrayAnswer* answer);
     static const char* className(JSContext* cx, HandleObject proxy);
     static JSString* fun_toString(JSContext* cx, HandleObject proxy, unsigned indent);
     static bool regexp_toShared(JSContext* cx, HandleObject proxy, RegExpGuard* g);
     static bool boxedValue_unbox(JSContext* cx, HandleObject proxy, MutableHandleValue vp);
     static bool defaultValue(JSContext* cx, HandleObject obj, JSType hint, MutableHandleValue vp);
 
     static bool watch(JSContext* cx, HandleObject proxy, HandleId id, HandleObject callable);
--- a/js/src/proxy/ScriptedDirectProxyHandler.cpp
+++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp
@@ -1097,20 +1097,21 @@ ScriptedDirectProxyHandler::hasInstance(
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED);
         return false;
     }
 
     return HasInstance(cx, target, v, bp);
 }
 
 bool
-ScriptedDirectProxyHandler::objectClassIs(HandleObject proxy, ESClassValue classValue,
-                                          JSContext* cx) const
+ScriptedDirectProxyHandler::getBuiltinClass(JSContext* cx, HandleObject proxy,
+                                            ESClassValue* classValue) const
 {
-    return false;
+    *classValue = ESClass_Other;
+    return true;
 }
 
 bool
 ScriptedDirectProxyHandler::isArray(JSContext* cx, HandleObject proxy,
                                     IsArrayAnswer* answer) const
 {
     RootedObject target(cx, proxy->as<ProxyObject>().target());
     if (target)
--- a/js/src/proxy/ScriptedDirectProxyHandler.h
+++ b/js/src/proxy/ScriptedDirectProxyHandler.h
@@ -65,18 +65,18 @@ class ScriptedDirectProxyHandler : publi
         return BaseProxyHandler::getOwnEnumerablePropertyKeys(cx, proxy, props);
     }
 
     // A scripted proxy should not be treated as generic in most contexts.
     virtual bool nativeCall(JSContext* cx, IsAcceptableThis test, NativeImpl impl,
                             const CallArgs& args) const override;
     virtual bool hasInstance(JSContext* cx, HandleObject proxy, MutableHandleValue v,
                              bool* bp) const override;
-    virtual bool objectClassIs(HandleObject obj, ESClassValue classValue,
-                               JSContext* cx) const override;
+    virtual bool getBuiltinClass(JSContext* cx, HandleObject proxy,
+                                 ESClassValue* classValue) const override;
     virtual bool isArray(JSContext* cx, HandleObject proxy,
                          JS::IsArrayAnswer* answer) const override;
     virtual const char* className(JSContext* cx, HandleObject proxy) const override;
     virtual JSString* fun_toString(JSContext* cx, HandleObject proxy,
                                    unsigned indent) const override;
     virtual bool regexp_toShared(JSContext* cx, HandleObject proxy,
                                  RegExpGuard* g) const override;
     virtual bool boxedValue_unbox(JSContext* cx, HandleObject proxy,
--- a/js/src/proxy/SecurityWrapper.cpp
+++ b/js/src/proxy/SecurityWrapper.cpp
@@ -82,19 +82,21 @@ bool
 SecurityWrapper<Base>::defaultValue(JSContext* cx, HandleObject wrapper,
                                     JSType hint, MutableHandleValue vp) const
 {
     return OrdinaryToPrimitive(cx, wrapper, hint, vp);
 }
 
 template <class Base>
 bool
-SecurityWrapper<Base>::objectClassIs(HandleObject obj, ESClassValue classValue, JSContext* cx) const
+SecurityWrapper<Base>::getBuiltinClass(JSContext* cx, HandleObject wrapper,
+                                       ESClassValue* classValue) const
 {
-    return false;
+    *classValue = ESClass_Other;
+    return true;
 }
 
 template <class Base>
 bool
 SecurityWrapper<Base>::isArray(JSContext* cx, HandleObject obj, JS::IsArrayAnswer* answer) const
 {
     // This should ReportUnwrapDenied(cx), but bug 849730 disagrees.  :-(
     *answer = JS::IsArrayAnswer::NotArray;
--- a/js/src/tests/ecma_6/extensions/keyword-unescaped-requirement-modules.js
+++ b/js/src/tests/ecma_6/extensions/keyword-unescaped-requirement-modules.js
@@ -56,16 +56,42 @@ if (typeof Reflect.parse === "function")
 
   var oneStatementAST =
     Reflect.parse(`export { x } /* no ASI here */
                   from 'foo'`,
                   { target: "module" });
 
   assertEq(oneStatementAST.body.length, 1);
   assertEq(oneStatementAST.body[0].type, "ExportDeclaration");
+
+  twoStatementAST =
+    Reflect.parse(`export { x } from "bar"
+                  /bar/g`,
+                  { target: "module" });
+
+  statements = twoStatementAST.body;
+  assertEq(statements.length, 2,
+           "should have two items in the module, not one ExportDeclaration");
+  assertEq(statements[0].type, "ExportDeclaration");
+  assertEq(statements[1].type, "ExpressionStatement");
+  assertEq(statements[1].expression.type, "Literal");
+  assertEq(statements[1].expression.value.toString(), "/bar/g");
+
+  twoStatementAST =
+    Reflect.parse(`export * from "bar"
+                  /bar/g`,
+                  { target: "module" });
+
+  statements = twoStatementAST.body;
+  assertEq(statements.length, 2,
+           "should have two items in the module, not one ExportDeclaration");
+  assertEq(statements[0].type, "ExportDeclaration");
+  assertEq(statements[1].type, "ExpressionStatement");
+  assertEq(statements[1].expression.type, "Literal");
+  assertEq(statements[1].expression.value.toString(), "/bar/g");
 }
 
 /******************************************************************************/
 
 if (typeof reportCompare === "function")
   reportCompare(true, true);
 
 print("Tests complete");
--- a/js/src/vm/ArrayBufferObject.cpp
+++ b/js/src/vm/ArrayBufferObject.cpp
@@ -349,17 +349,20 @@ ArrayBufferObject::fun_transfer(JSContex
     HandleValue newByteLengthArg = args.get(1);
 
     if (!oldBufferArg.isObject()) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
 
     RootedObject oldBufferObj(cx, &oldBufferArg.toObject());
-    if (!ObjectClassIs(oldBufferObj, ESClass_ArrayBuffer, cx)) {
+    ESClassValue cls;
+    if (!GetBuiltinClass(cx, oldBufferObj, &cls))
+        return false;
+    if (cls != ESClass_ArrayBuffer) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
         return false;
     }
 
     // Beware: oldBuffer can point across compartment boundaries. ArrayBuffer
     // contents are not compartment-specific so this is safe.
     Rooted<ArrayBufferObject*> oldBuffer(cx);
     if (oldBufferObj->is<ArrayBufferObject>()) {
--- a/js/src/vm/RegExpObject.h
+++ b/js/src/vm/RegExpObject.h
@@ -482,23 +482,23 @@ str_replace_regexp_raw(JSContext* cx, Ha
  * Parse regexp flags. Report an error and return false if an invalid
  * sequence of flags is encountered (repeat/invalid flag).
  *
  * N.B. flagStr must be rooted.
  */
 bool
 ParseRegExpFlags(JSContext* cx, JSString* flagStr, RegExpFlag* flagsOut);
 
-/* Assuming ObjectClassIs(obj, ESClass_RegExp), return a RegExpShared for obj. */
+/* Assuming GetBuiltinClass(obj) is ESClass_RegExp, return a RegExpShared for obj. */
 inline bool
 RegExpToShared(JSContext* cx, HandleObject obj, RegExpGuard* g)
 {
     if (obj->is<RegExpObject>())
         return obj->as<RegExpObject>().getShared(cx, g);
-    MOZ_ASSERT(Proxy::objectClassIs(obj, ESClass_RegExp, cx));
+
     return Proxy::regexp_toShared(cx, obj, g);
 }
 
 template<XDRMode mode>
 bool
 XDRScriptRegExpObject(XDRState<mode>* xdr, MutableHandle<RegExpObject*> objp);
 
 extern JSObject*
--- a/js/src/vm/Shape.cpp
+++ b/js/src/vm/Shape.cpp
@@ -1644,8 +1644,28 @@ JSCompartment::fixupInitialShapeTable()
 void
 AutoRooterGetterSetter::Inner::trace(JSTracer* trc)
 {
     if ((attrs & JSPROP_GETTER) && *pgetter)
         TraceRoot(trc, (JSObject**) pgetter, "AutoRooterGetterSetter getter");
     if ((attrs & JSPROP_SETTER) && *psetter)
         TraceRoot(trc, (JSObject**) psetter, "AutoRooterGetterSetter setter");
 }
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<js::Shape>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
+
+    if (get().hasTable())
+        size += get().table().sizeOfIncludingThis(mallocSizeOf);
+
+    if (!get().inDictionary() && get().kids.isHash())
+        size += get().kids.toHash()->sizeOfIncludingThis(mallocSizeOf);
+
+    return size;
+}
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<js::BaseShape>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    return js::gc::Arena::thingSize(get().asTenured().getAllocKind());
+}
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -527,16 +527,17 @@ class Shape : public gc::TenuredCell
     friend class ::JSFunction;
     friend class Bindings;
     friend class NativeObject;
     friend class PropertyTree;
     friend class StaticBlockObject;
     friend class TenuringTracer;
     friend struct StackBaseShape;
     friend struct StackShape;
+    friend struct JS::ubi::Concrete<Shape>;
 
   protected:
     HeapPtrBaseShape    base_;
     PreBarrieredId      propid_;
 
     enum SlotInfo : uint32_t
     {
         /* Number of fixed slots in objects with this shape. */
@@ -1428,14 +1429,33 @@ ReshapeForAllocKind(JSContext* cx, Shape
 #pragma warning(pop)
 #pragma warning(pop)
 #endif
 
 // JS::ubi::Nodes can point to Shapes and BaseShapes; they're js::gc::Cell
 // instances that occupy a compartment.
 namespace JS {
 namespace ubi {
-template<> struct Concrete<js::Shape> : TracerConcreteWithCompartment<js::Shape> { };
-template<> struct Concrete<js::BaseShape> : TracerConcreteWithCompartment<js::BaseShape> { };
+
+template<> struct Concrete<js::Shape> : TracerConcreteWithCompartment<js::Shape> {
+    Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+  protected:
+    explicit Concrete(js::Shape *ptr) : TracerConcreteWithCompartment<js::Shape>(ptr) { }
+
+  public:
+    static void construct(void *storage, js::Shape *ptr) { new (storage) Concrete(ptr); }
+};
+
+template<> struct Concrete<js::BaseShape> : TracerConcreteWithCompartment<js::BaseShape> {
+    Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+  protected:
+    explicit Concrete(js::BaseShape *ptr) : TracerConcreteWithCompartment<js::BaseShape>(ptr) { }
+
+  public:
+    static void construct(void *storage, js::BaseShape *ptr) { new (storage) Concrete(ptr); }
+};
+
 } // namespace ubi
 } // namespace JS
 
 #endif /* vm_Shape_h */
--- a/js/src/vm/SharedArrayObject.cpp
+++ b/js/src/vm/SharedArrayObject.cpp
@@ -1,35 +1,37 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  * vim: set ts=8 sts=4 et sw=4 tw=99:
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "vm/SharedArrayObject.h"
 
+#include "mozilla/Atomics.h"
+
+#include "jsfriendapi.h"
 #include "jsprf.h"
-#include "jsobjinlines.h"
 
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
 #ifndef XP_WIN
 # include <sys/mman.h>
 #endif
 #ifdef MOZ_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
-#include "mozilla/Atomics.h"
-
 #include "asmjs/AsmJSValidate.h"
 #include "vm/TypedArrayCommon.h"
 
+#include "jsobjinlines.h"
+
 using namespace js;
 
 static inline void*
 MapMemory(size_t length, bool commit)
 {
 #ifdef XP_WIN
     int prot = (commit ? MEM_COMMIT : MEM_RESERVE);
     int flags = (commit ? PAGE_READWRITE : PAGE_NOACCESS);
@@ -203,19 +205,24 @@ SharedArrayBufferObject::fun_isView(JSCo
 }
 
 bool
 SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (!args.isConstructing()) {
-        if (args.hasDefined(0) && IsObjectWithClass(args[0], ESClass_SharedArrayBuffer, cx)) {
-            args.rval().set(args[0]);
-            return true;
+        if (args.hasDefined(0)) {
+            ESClassValue cls;
+            if (!GetClassOfValue(cx, args[0], &cls))
+                return false;
+            if (cls == ESClass_SharedArrayBuffer) {
+                args.rval().set(args[0]);
+                return true;
+            }
         }
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_OBJECT);
         return false;
     }
 
     // Bugs 1068458, 1161298: Limit length to 2^31-1.
     uint32_t length;
     bool overflow_unused;
--- a/js/src/vm/SharedTypedArrayObject.cpp
+++ b/js/src/vm/SharedTypedArrayObject.cpp
@@ -406,17 +406,20 @@ class SharedTypedArrayObjectTemplate : p
     static bool fun_copyWithin(JSContext* cx, unsigned argc, Value* vp);
     static bool fun_set(JSContext* cx, unsigned argc, Value* vp);
 
   public:
     static JSObject*
     fromBufferWithProto(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, uint32_t length,
                         HandleObject proto)
     {
-        if (!ObjectClassIs(bufobj, ESClass_SharedArrayBuffer, cx)) {
+        ESClassValue cls;
+        if (!GetBuiltinClass(cx, bufobj, &cls))
+            return nullptr;
+        if (cls != ESClass_SharedArrayBuffer) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_OBJECT);
             return nullptr; // must be SharedArrayBuffer
         }
 
         if (bufobj->is<ProxyObject>()) {
             // Complicated, see TypedArrayObject.cpp for code.  For now, punt.
             JS_ReportError(cx, "Permission denied to access object");
             return nullptr;
--- a/js/src/vm/StructuredClone.cpp
+++ b/js/src/vm/StructuredClone.cpp
@@ -747,17 +747,20 @@ JSStructuredCloneWriter::parseTransferab
     if (transferable.isNull() || transferable.isUndefined())
         return true;
 
     if (!transferable.isObject())
         return reportErrorTransferable(JS_SCERR_TRANSFERABLE);
 
     JSContext* cx = context();
     RootedObject array(cx, &transferable.toObject());
-    if (!JS_IsArrayObject(cx, array))
+    bool isArray;
+    if (!JS_IsArrayObject(cx, array, &isArray))
+        return false;
+    if (!isArray)
         return reportErrorTransferable(JS_SCERR_TRANSFERABLE);
 
     uint32_t length;
     if (!JS_GetArrayLength(cx, array, &length)) {
         return false;
     }
 
     RootedValue v(context());
@@ -959,17 +962,20 @@ JSStructuredCloneWriter::traverseObject(
 
     /* Push obj and count to the stack. */
     if (!objs.append(ObjectValue(*obj)) || !counts.append(properties.length()))
         return false;
 
     checkStack();
 
     /* Write the header for obj. */
-    return out.writePair(ObjectClassIs(obj, ESClass_Array, context()) ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0);
+    ESClassValue cls;
+    if (!GetBuiltinClass(context(), obj, &cls))
+        return false;
+    return out.writePair(cls == ESClass_Array ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0);
 }
 
 bool
 JSStructuredCloneWriter::traverseMap(HandleObject obj)
 {
     AutoValueVector newEntries(context());
     {
         // If there is no wrapper, the compartment munging is a no-op.
@@ -1048,59 +1054,63 @@ JSStructuredCloneWriter::startWrite(Hand
         RootedObject obj(context(), &v.toObject());
 
         bool backref;
         if (!startObject(obj, &backref))
             return false;
         if (backref)
             return true;
 
-        if (ObjectClassIs(obj, ESClass_RegExp, context())) {
+        ESClassValue cls;
+        if (!GetBuiltinClass(context(), obj, &cls))
+            return false;
+
+        if (cls == ESClass_RegExp) {
             RegExpGuard re(context());
             if (!RegExpToShared(context(), obj, &re))
                 return false;
             return out.writePair(SCTAG_REGEXP_OBJECT, re->getFlags()) &&
                    writeString(SCTAG_STRING, re->getSource());
-        } else if (ObjectClassIs(obj, ESClass_Date, context())) {
+        } else if (cls == ESClass_Date) {
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
             return out.writePair(SCTAG_DATE_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
         } else if (JS_IsTypedArrayObject(obj)) {
             return writeTypedArray(obj);
         } else if (JS_IsDataViewObject(obj)) {
             return writeDataView(obj);
         } else if (JS_IsArrayBufferObject(obj) && JS_ArrayBufferHasData(obj)) {
             return writeArrayBuffer(obj);
         } else if (JS_IsSharedTypedArrayObject(obj)) {
             return writeSharedTypedArray(obj);
         } else if (JS_IsSharedArrayBufferObject(obj)) {
             return writeSharedArrayBuffer(obj);
-        } else if (ObjectClassIs(obj, ESClass_Object, context())) {
+        } else if (cls == ESClass_Object) {
             return traverseObject(obj);
-        } else if (ObjectClassIs(obj, ESClass_Array, context())) {
+        } else if (cls == ESClass_Array) {
             return traverseObject(obj);
-        } else if (ObjectClassIs(obj, ESClass_Boolean, context())) {
+        } else if (cls == ESClass_Boolean) {
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
             return out.writePair(SCTAG_BOOLEAN_OBJECT, unboxed.toBoolean());
-        } else if (ObjectClassIs(obj, ESClass_Number, context())) {
+        } else if (cls == ESClass_Number) {
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
             return out.writePair(SCTAG_NUMBER_OBJECT, 0) && out.writeDouble(unboxed.toNumber());
-        } else if (ObjectClassIs(obj, ESClass_String, context())) {
+        } else if (cls == ESClass_String) {
             RootedValue unboxed(context());
             if (!Unbox(context(), obj, &unboxed))
                 return false;
             return writeString(SCTAG_STRING_OBJECT, unboxed.toString());
-        } else if (ObjectClassIs(obj, ESClass_Map, context())) {
+        } else if (cls == ESClass_Map) {
             return traverseMap(obj);
-        } else if (ObjectClassIs(obj, ESClass_Set, context())) {
+        } else if (cls == ESClass_Set) {
             return traverseSet(obj);
         }
 
         if (callbacks && callbacks->write)
             return callbacks->write(context(), this, obj, closure);
         /* else fall through */
     }
 
@@ -1163,17 +1173,21 @@ JSStructuredCloneWriter::transferOwnersh
         uint64_t extraData;
 
 #if DEBUG
         SCInput::getPair(point, &tag, (uint32_t*) &ownership);
         MOZ_ASSERT(tag == SCTAG_TRANSFER_MAP_PENDING_ENTRY);
         MOZ_ASSERT(ownership == JS::SCTAG_TMO_UNFILLED);
 #endif
 
-        if (ObjectClassIs(obj, ESClass_ArrayBuffer, context())) {
+        ESClassValue cls;
+        if (!GetBuiltinClass(context(), obj, &cls))
+            return false;
+
+        if (cls == ESClass_ArrayBuffer) {
             // The current setup of the array buffer inheritance hierarchy doesn't
             // lend itself well to generic manipulation via proxies.
             Rooted<ArrayBufferObject*> arrayBuffer(context(), &CheckedUnwrap(obj)->as<ArrayBufferObject>());
             size_t nbytes = arrayBuffer->byteLength();
 
             // Structured cloning currently only has optimizations for mapped
             // and malloc'd buffers, not asm.js-ified buffers.
             bool hasStealableContents = arrayBuffer->hasStealableContents() &&
@@ -1186,17 +1200,17 @@ JSStructuredCloneWriter::transferOwnersh
 
             content = bufContents.data();
             tag = SCTAG_TRANSFER_MAP_ARRAY_BUFFER;
             if (bufContents.kind() == ArrayBufferObject::MAPPED)
                 ownership = JS::SCTAG_TMO_MAPPED_DATA;
             else
                 ownership = JS::SCTAG_TMO_ALLOC_DATA;
             extraData = nbytes;
-        } else if (ObjectClassIs(obj, ESClass_SharedArrayBuffer, context())) {
+        } else if (cls == ESClass_SharedArrayBuffer) {
             Rooted<SharedArrayBufferObject*> sharedArrayBuffer(context(), &CheckedUnwrap(obj)->as<SharedArrayBufferObject>());
             SharedArrayRawBuffer* rawbuf = sharedArrayBuffer->rawBufferObject();
 
             // Avoids a race condition where the parent thread frees the buffer
             // before the child has accepted the transferable.
             rawbuf->addReference();
 
             tag = SCTAG_TRANSFER_MAP_SHARED_BUFFER;
@@ -1233,25 +1247,29 @@ JSStructuredCloneWriter::write(HandleVal
         RootedObject obj(context(), &objs.back().toObject());
         AutoCompartment ac(context(), obj);
         if (counts.back()) {
             counts.back()--;
             RootedValue key(context(), entries.back());
             entries.popBack();
             checkStack();
 
-            if (ObjectClassIs(obj, ESClass_Map, context())) {
+            ESClassValue cls;
+            if (!GetBuiltinClass(context(), obj, &cls))
+                return false;
+
+            if (cls == ESClass_Map) {
                 counts.back()--;
                 RootedValue val(context(), entries.back());
                 entries.popBack();
                 checkStack();
 
                 if (!startWrite(key) || !startWrite(val))
                     return false;
-            } else if (ObjectClassIs(obj, ESClass_Set, context())) {
+            } else if (cls == ESClass_Set) {
                 if (!startWrite(key))
                     return false;
             } else {
                 RootedId id(context());
                 if (!ValueToId<CanGC>(context(), key, &id))
                   return false;
                 MOZ_ASSERT(JSID_IS_STRING(id) || JSID_IS_INT(id));
 
--- a/js/src/vm/Symbol.cpp
+++ b/js/src/vm/Symbol.cpp
@@ -147,8 +147,18 @@ js::IsSymbolOrSymbolWrapper(Value v)
 }
 
 JS::Symbol*
 js::ToSymbolPrimitive(Value v)
 {
     MOZ_ASSERT(IsSymbolOrSymbolWrapper(v));
     return v.isSymbol() ? v.toSymbol() : v.toObject().as<SymbolObject>().unbox();
 }
+
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<JS::Symbol>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    // If we start allocating symbols in the nursery, we will need to update
+    // this method.
+    MOZ_ASSERT(get().isTenured());
+    return js::gc::Arena::thingSize(get().asTenured().getAllocKind());
+}
--- a/js/src/vm/Symbol.h
+++ b/js/src/vm/Symbol.h
@@ -62,16 +62,20 @@ class Symbol : public js::gc::TenuredCel
     }
     inline void finalize(js::FreeOp*) {}
 
     static MOZ_ALWAYS_INLINE void writeBarrierPre(Symbol* thing) {
         if (thing && !thing->isWellKnownSymbol())
             thing->asTenured().writeBarrierPre(thing);
     }
 
+    size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
+        return mallocSizeOf(this);
+    }
+
 #ifdef DEBUG
     void dump(FILE* fp = stderr);
 #endif
 };
 
 } /* namespace JS */
 
 namespace js {
--- a/js/src/vm/TypeInference.cpp
+++ b/js/src/vm/TypeInference.cpp
@@ -4445,8 +4445,16 @@ TypeScript::printTypes(JSContext* cx, Ha
             types->print();
             fprintf(stderr, "\n");
         }
     }
 
     fprintf(stderr, "\n");
 }
 #endif /* DEBUG */
+
+JS::ubi::Node::Size
+JS::ubi::Concrete<js::ObjectGroup>::size(mozilla::MallocSizeOf mallocSizeOf) const
+{
+    Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
+    size += get().sizeOfExcludingThis(mallocSizeOf);
+    return size;
+}
--- a/js/src/vm/TypeInference.h
+++ b/js/src/vm/TypeInference.h
@@ -1290,13 +1290,24 @@ void
 PrintTypes(JSContext* cx, JSCompartment* comp, bool force);
 
 } /* namespace js */
 
 // JS::ubi::Nodes can point to object groups; they're js::gc::Cell instances
 // with no associated compartment.
 namespace JS {
 namespace ubi {
-template<> struct Concrete<js::ObjectGroup> : TracerConcrete<js::ObjectGroup> { };
+
+template<>
+struct Concrete<js::ObjectGroup> : TracerConcrete<js::ObjectGroup> {
+    Size size(mozilla::MallocSizeOf mallocSizeOf) const override;
+
+  protected:
+    explicit Concrete(js::ObjectGroup *ptr) : TracerConcrete<js::ObjectGroup>(ptr) { }
+
+  public:
+    static void construct(void *storage, js::ObjectGroup *ptr) { new (storage) Concrete(ptr); }
+};
+
 } // namespace ubi
 } // namespace JS
 
 #endif /* vm_TypeInference_h */
--- a/js/src/vm/TypedArrayCommon.h
+++ b/js/src/vm/TypedArrayCommon.h
@@ -8,16 +8,20 @@
 #define vm_TypedArrayCommon_h
 
 /* Utilities and common inline code for TypedArray and SharedTypedArray */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/FloatingPoint.h"
 #include "mozilla/PodOperations.h"
 
+#include "jsarray.h"
+#include "jscntxt.h"
+#include "jsnum.h"
+
 #include "js/Conversions.h"
 #include "js/Value.h"
 
 #include "vm/SharedTypedArrayObject.h"
 #include "vm/TypedArrayObject.h"
 
 namespace js {
 
--- a/js/src/vm/TypedArrayObject.cpp
+++ b/js/src/vm/TypedArrayObject.cpp
@@ -483,19 +483,22 @@ class TypedArrayObjectTemplate : public 
     fromBuffer(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt) {
         return fromBufferWithProto(cx, bufobj, byteOffset, lengthInt, nullptr);
     }
 
     static JSObject*
     fromBufferWithProto(JSContext* cx, HandleObject bufobj, uint32_t byteOffset, int32_t lengthInt,
                         HandleObject proto)
     {
-        if (!ObjectClassIs(bufobj, ESClass_ArrayBuffer, cx)) {
+        ESClassValue cls;
+        if (!GetBuiltinClass(cx, bufobj, &cls))
+            return nullptr;
+        if (cls != ESClass_ArrayBuffer) {
             JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
-            return nullptr; // must be arrayBuffer
+            return nullptr;
         }
 
         MOZ_ASSERT(IsArrayBuffer(bufobj) || bufobj->is<ProxyObject>());
         if (bufobj->is<ProxyObject>()) {
             /*
              * Normally, NonGenericMethodGuard handles the case of transparent
              * wrappers. However, we have a peculiar situation: we want to
              * construct the new typed array in the compartment of the buffer,
--- a/js/src/vm/UbiNode.cpp
+++ b/js/src/vm/UbiNode.cpp
@@ -42,18 +42,17 @@ using JS::HandleValue;
 using JS::Value;
 using JS::ZoneSet;
 using JS::ubi::AtomOrTwoByteChars;
 using JS::ubi::CoarseType;
 using JS::ubi::Concrete;
 using JS::ubi::Edge;
 using JS::ubi::EdgeRange;
 using JS::ubi::Node;
-using JS::ubi::SimpleEdge;
-using JS::ubi::SimpleEdgeVector;
+using JS::ubi::EdgeVector;
 using JS::ubi::StackFrame;
 using JS::ubi::TracerConcrete;
 using JS::ubi::TracerConcreteWithCompartment;
 
 template<typename CharT>
 static size_t
 copyToBuffer(const CharT* src, RangedPtr<char16_t> dest, size_t length)
 {
@@ -197,21 +196,21 @@ Node::exposeToJS() const
     } else {
         v.setUndefined();
     }
 
     return v;
 }
 
 
-// A JS::CallbackTracer subclass that adds a SimpleEdge to a Vector for each
+// A JS::CallbackTracer subclass that adds a Edge to a Vector for each
 // edge on which it is invoked.
-class SimpleEdgeVectorTracer : public JS::CallbackTracer {
-    // The vector to which we add SimpleEdges.
-    SimpleEdgeVector* vec;
+class EdgeVectorTracer : public JS::CallbackTracer {
+    // The vector to which we add Edges.
+    EdgeVector* vec;
 
     // True if we should populate the edge's names.
     bool wantNames;
 
     void onChild(const JS::GCCellPtr& thing) override {
         if (!okay)
             return;
 
@@ -237,54 +236,54 @@ class SimpleEdgeVectorTracer : public JS
             }
 
             size_t i;
             for (i = 0; name[i]; i++)
                 name16[i] = name[i];
             name16[i] = '\0';
         }
 
-        // The simplest code is correct! The temporary SimpleEdge takes
+        // The simplest code is correct! The temporary Edge takes
         // ownership of name; if the append succeeds, the vector element
         // then takes ownership; if the append fails, then the temporary
         // retains it, and its destructor will free it.
-        if (!vec->append(mozilla::Move(SimpleEdge(name16, Node(thing))))) {
+        if (!vec->append(mozilla::Move(Edge(name16, Node(thing))))) {
             okay = false;
             return;
         }
     }
 
   public:
     // True if no errors (OOM, say) have yet occurred.
     bool okay;
 
-    SimpleEdgeVectorTracer(JSContext* cx, SimpleEdgeVector* vec, bool wantNames)
+    EdgeVectorTracer(JSContext* cx, EdgeVector* vec, bool wantNames)
       : JS::CallbackTracer(JS_GetRuntime(cx)),
         vec(vec),
         wantNames(wantNames),
         okay(true)
     { }
 };
 
 
-// An EdgeRange concrete class that simply holds a vector of SimpleEdges,
+// An EdgeRange concrete class that simply holds a vector of Edges,
 // populated by the init method.
 class SimpleEdgeRange : public EdgeRange {
-    SimpleEdgeVector edges;
+    EdgeVector edges;
     size_t i;
 
     void settle() {
         front_ = i < edges.length() ? &edges[i] : nullptr;
     }
 
   public:
     explicit SimpleEdgeRange(JSContext* cx) : edges(cx), i(0) { }
 
     bool init(JSContext* cx, void* thing, JS::TraceKind kind, bool wantNames = true) {
-        SimpleEdgeVectorTracer tracer(cx, &edges, wantNames);
+        EdgeVectorTracer tracer(cx, &edges, wantNames);
         js::TraceChildren(&tracer, thing, kind);
         settle();
         return tracer.okay;
     }
 
     void popFront() override { i++; settle(); }
 };
 
@@ -402,39 +401,39 @@ RootList::RootList(JSContext* cx, Maybe<
     edges(cx),
     wantNames(wantNames)
 { }
 
 
 bool
 RootList::init()
 {
-    SimpleEdgeVectorTracer tracer(cx, &edges, wantNames);
+    EdgeVectorTracer tracer(cx, &edges, wantNames);
     JS_TraceRuntime(&tracer);
     if (!tracer.okay)
         return false;
     noGC.emplace(cx->runtime());
     return true;
 }
 
 bool
 RootList::init(ZoneSet& debuggees)
 {
-    SimpleEdgeVector allRootEdges(cx);
-    SimpleEdgeVectorTracer tracer(cx, &allRootEdges, wantNames);
+    EdgeVector allRootEdges(cx);
+    EdgeVectorTracer tracer(cx, &allRootEdges, wantNames);
 
     JS_TraceRuntime(&tracer);
     if (!tracer.okay)
         return false;
     JS_TraceIncomingCCWs(&tracer, debuggees);
     if (!tracer.okay)
         return false;
 
-    for (SimpleEdgeVector::Range r = allRootEdges.all(); !r.empty(); r.popFront()) {
-        SimpleEdge& edge = r.front();
+    for (EdgeVector::Range r = allRootEdges.all(); !r.empty(); r.popFront()) {
+        Edge& edge = r.front();
         Zone* zone = edge.referent.zone();
         if (zone && !debuggees.has(zone))
             continue;
         if (!edges.append(mozilla::Move(edge)))
             return false;
     }
 
     noGC.emplace(cx->runtime());
@@ -479,17 +478,17 @@ RootList::addRoot(Node node, const char1
 
     UniquePtr<char16_t[], JS::FreePolicy> name;
     if (edgeName) {
         name = DuplicateString(cx, edgeName);
         if (!name)
             return false;
     }
 
-    return edges.append(mozilla::Move(SimpleEdge(name.release(), node)));
+    return edges.append(mozilla::Move(Edge(name.release(), node)));
 }
 
 const char16_t Concrete<RootList>::concreteTypeName[] = MOZ_UTF16("RootList");
 
 UniquePtr<EdgeRange>
 Concrete<RootList>::edges(JSContext* cx, bool wantNames) const {
     MOZ_ASSERT_IF(wantNames, get().wantNames);
     return UniquePtr<EdgeRange>(cx->new_<PreComputedEdgeRange>(cx, get().edges));
--- a/js/xpconnect/loader/mozJSComponentLoader.cpp
+++ b/js/xpconnect/loader/mozJSComponentLoader.cpp
@@ -1180,17 +1180,21 @@ mozJSComponentLoader::ImportInto(const n
         RootedValue symbols(cx);
         RootedObject modObj(cx, mod->obj);
         if (!JS_GetProperty(cx, modObj,
                             "EXPORTED_SYMBOLS", &symbols)) {
             return ReportOnCaller(cxhelper, ERROR_NOT_PRESENT,
                                   PromiseFlatCString(aLocation).get());
         }
 
-        if (!JS_IsArrayObject(cx, symbols)) {
+        bool isArray;
+        if (!JS_IsArrayObject(cx, symbols, &isArray)) {
+            return NS_ERROR_FAILURE;
+        }
+        if (!isArray) {
             return ReportOnCaller(cxhelper, ERROR_NOT_AN_ARRAY,
                                   PromiseFlatCString(aLocation).get());
         }
 
         RootedObject symbolsObj(cx, &symbols.toObject());
 
         // Iterate over symbols array, installing symbols on targetObj:
 
--- a/js/xpconnect/src/Sandbox.cpp
+++ b/js/xpconnect/src/Sandbox.cpp
@@ -858,18 +858,16 @@ xpc::SandboxProxyHandler::enumerate(JSCo
                                     JS::MutableHandle<JSObject*> objp) const
 {
     return BaseProxyHandler::enumerate(cx, proxy, objp);
 }
 
 bool
 xpc::GlobalProperties::Parse(JSContext* cx, JS::HandleObject obj)
 {
-    MOZ_ASSERT(JS_IsArrayObject(cx, obj));
-
     uint32_t length;
     bool ok = JS_GetArrayLength(cx, obj, &length);
     NS_ENSURE_TRUE(ok, false);
     for (uint32_t i = 0; i < length; i++) {
         RootedValue nameValue(cx);
         ok = JS_GetElement(cx, obj, i, &nameValue);
         NS_ENSURE_TRUE(ok, false);
         if (!nameValue.isString()) {
@@ -1243,20 +1241,19 @@ GetPrincipalOrSOP(JSContext* cx, HandleO
  * format or actual objects (see GetPrincipalOrSOP)
  */
 static bool
 GetExpandedPrincipal(JSContext* cx, HandleObject arrayObj, nsIExpandedPrincipal** out)
 {
     MOZ_ASSERT(out);
     uint32_t length;
 
-    if (!JS_IsArrayObject(cx, arrayObj) ||
-        !JS_GetArrayLength(cx, arrayObj, &length) ||
-        !length)
-    {
+    if (!JS_GetArrayLength(cx, arrayObj, &length))
+        return false;
+    if (!length) {
         // We need a whitelist of principals or uri strings to create an
         // expanded principal, if we got an empty array or something else
         // report error.
         JS_ReportError(cx, "Expected an array of URI strings");
         return false;
     }
 
     nsTArray< nsCOMPtr<nsIPrincipal> > allowedDomains(length);
@@ -1476,17 +1473,20 @@ SandboxOptions::ParseGlobalProperties()
         return true;
 
     if (!value.isObject()) {
         JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
         return false;
     }
 
     RootedObject ctors(mCx, &value.toObject());
-    if (!JS_IsArrayObject(mCx, ctors)) {
+    bool isArray;
+    if (!JS_IsArrayObject(mCx, ctors, &isArray))
+        return false;
+    if (!isArray) {
         JS_ReportError(mCx, "Expected an array value for wantGlobalProperties");
         return false;
     }
 
     return globalProperties.Parse(mCx, ctors);
 }
 
 /*
@@ -1572,17 +1572,20 @@ nsXPCComponents_utils_Sandbox::CallOrCon
     nsCOMPtr<nsISupports> prinOrSop;
 
     if (args[0].isString()) {
         RootedString str(cx, args[0].toString());
         ok = ParsePrincipal(cx, str, getter_AddRefs(principal));
         prinOrSop = principal;
     } else if (args[0].isObject()) {
         RootedObject obj(cx, &args[0].toObject());
-        if (JS_IsArrayObject(cx, obj)) {
+        bool isArray;
+        if (!JS_IsArrayObject(cx, obj, &isArray)) {
+            ok = false;
+        } else if (isArray) {
             ok = GetExpandedPrincipal(cx, obj, getter_AddRefs(expanded));
             prinOrSop = expanded;
         } else {
             ok = GetPrincipalOrSOP(cx, obj, getter_AddRefs(prinOrSop));
         }
     } else if (args[0].isNull()) {
         // Null means that we just pass prinOrSop = nullptr, and get an
         // nsNullPrincipal.
--- a/js/xpconnect/src/XPCComponents.cpp
+++ b/js/xpconnect/src/XPCComponents.cpp
@@ -2540,18 +2540,26 @@ nsXPCComponents_Utils::ImportGlobalPrope
     // Don't allow doing this if the global is a Window
     nsGlobalWindow* win;
     if (NS_SUCCEEDED(UNWRAP_OBJECT(Window, global, win))) {
         return NS_ERROR_NOT_AVAILABLE;
     }
 
     GlobalProperties options;
     NS_ENSURE_TRUE(aPropertyList.isObject(), NS_ERROR_INVALID_ARG);
+
     RootedObject propertyList(cx, &aPropertyList.toObject());
-    NS_ENSURE_TRUE(JS_IsArrayObject(cx, propertyList), NS_ERROR_INVALID_ARG);
+    bool isArray;
+    if (NS_WARN_IF(!JS_IsArrayObject(cx, propertyList, &isArray))) {
+        return NS_ERROR_FAILURE;
+    }
+    if (NS_WARN_IF(!isArray)) {
+        return NS_ERROR_INVALID_ARG;
+    }
+
     if (!options.Parse(cx, propertyList) ||
         !options.Define(cx, global))
     {
         return NS_ERROR_FAILURE;
     }
 
     return NS_OK;
 }
--- a/js/xpconnect/src/XPCConvert.cpp
+++ b/js/xpconnect/src/XPCConvert.cpp
@@ -1504,17 +1504,18 @@ XPCConvert::JSArray2Native(void** d, Han
 
     RootedObject jsarray(cx, &s.toObject());
 
     // If this is a typed array, then try a fast conversion with memcpy.
     if (JS_IsTypedArrayObject(jsarray)) {
         return JSTypedArray2Native(d, jsarray, count, type, pErr);
     }
 
-    if (!JS_IsArrayObject(cx, jsarray)) {
+    bool isArray;
+    if (!JS_IsArrayObject(cx, jsarray, &isArray) || !isArray) {
         if (pErr)
             *pErr = NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY;
         return false;
     }
 
     uint32_t len;
     if (!JS_GetArrayLength(cx, jsarray, &len) || len < count) {
         if (pErr)
--- a/js/xpconnect/src/XPCVariant.cpp
+++ b/js/xpconnect/src/XPCVariant.cpp
@@ -186,17 +186,22 @@ XPCArrayHomogenizer::GetTypeForArray(JSC
             break;
         } else if (val.isNull()) {
             type = tNull;
         } else if (val.isString()) {
             type = tStr;
         } else {
             MOZ_ASSERT(val.isObject(), "invalid type of jsval!");
             jsobj = &val.toObject();
-            if (JS_IsArrayObject(cx, jsobj))
+
+            bool isArray;
+            if (!JS_IsArrayObject(cx, jsobj, &isArray))
+                return false;
+
+            if (isArray)
                 type = tArr;
             else if (xpc_JSObjectIsID(cx, jsobj))
                 type = tID;
             else
                 type = tISup;
         }
 
         MOZ_ASSERT(state != tErr, "bad state table!");
@@ -299,17 +304,24 @@ bool XPCVariant::InitializeData(JSContex
     const nsID* id = xpc_JSObjectToID(cx, jsobj);
     if (id)
         return NS_SUCCEEDED(mData.SetFromID(*id));
 
     // Let's see if it is a js array object.
 
     uint32_t len;
 
-    if (JS_IsArrayObject(cx, jsobj) && JS_GetArrayLength(cx, jsobj, &len)) {
+    bool isArray;
+    if (!JS_IsArrayObject(cx, jsobj, &isArray) ||
+        (isArray && !JS_GetArrayLength(cx, jsobj, &len)))
+    {
+        return false;
+    }
+
+    if (isArray) {
         if (!len) {
             // Zero length array
             mData.SetToEmptyArray();
             return true;
         }
 
         nsXPTType type;
         nsID id;
--- a/js/xpconnect/src/XPCWrappedNative.cpp
+++ b/js/xpconnect/src/XPCWrappedNative.cpp
@@ -1494,17 +1494,20 @@ CallMethodHelper::GetArraySizeFromParam(
     // When converting arguments from JS to C++, we pass the array as |maybeArray|,
     // and give ourselves the chance to infer the length. Once we have it, we stick
     // it in the right slot so that we can find it again when cleaning up the params.
     // from the array.
     if (paramIndex >= mArgc && maybeArray.isObject()) {
         MOZ_ASSERT(mMethodInfo->GetParam(paramIndex).IsOptional());
         RootedObject arrayOrNull(mCallContext, maybeArray.isObject() ? &maybeArray.toObject()
                                                                      : nullptr);
-        if (!JS_IsArrayObject(mCallContext, maybeArray) ||
+
+        bool isArray;
+        if (!JS_IsArrayObject(mCallContext, maybeArray, &isArray) ||
+            !isArray ||
             !JS_GetArrayLength(mCallContext, arrayOrNull, &GetDispatchParam(paramIndex)->val.u32))
         {
             return Throw(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY, mCallContext);
         }
     }
 
     *result = GetDispatchParam(paramIndex)->val.u32;
 
--- a/js/xpconnect/src/XPCWrappedNativeScope.cpp
+++ b/js/xpconnect/src/XPCWrappedNativeScope.cpp
@@ -778,23 +778,29 @@ XPCWrappedNativeScope::UpdateInterpositi
     if (!AccessCheck::isChrome(whitelistObj)) {
         JS_ReportError(cx, "Whitelist must be from system scope.");
         return false;
     }
 
     {
         JSAutoCompartment ac(cx, whitelistObj);
 
-        uint32_t length;
-        if (!JS_IsArrayObject(cx, whitelistObj) ||
-            !JS_GetArrayLength(cx, whitelistObj, &length)) {
+        bool isArray;
+        if (!JS_IsArrayObject(cx, whitelistObj, &isArray))
+            return false;
+
+        if (!isArray) {
             JS_ReportError(cx, "Whitelist must be an array.");
             return false;
         }
 
+        uint32_t length;
+        if (!JS_GetArrayLength(cx, whitelistObj, &length))
+            return false;
+
         for (uint32_t i = 0; i < length; i++) {
             RootedValue idval(cx);
             if (!JS_GetElement(cx, whitelistObj, i, &idval))
                 return false;
 
             if (!idval.isString()) {
                 JS_ReportError(cx, "Whitelist must contain strings only.");
                 return false;
--- a/js/xpconnect/wrappers/AccessCheck.cpp
+++ b/js/xpconnect/wrappers/AccessCheck.cpp
@@ -304,17 +304,21 @@ ExposedPropertiesOnly::check(JSContext* 
     if (!JS_HasPropertyById(cx, wrappedObject, exposedPropsId, &found))
         return false;
 
     // If no __exposedProps__ existed, deny access.
     if (!found) {
         // Previously we automatically granted access to indexed properties and
         // .length for Array COWs. We're not doing that anymore, so make sure to
         // let people know what's going on.
-        bool isArray = JS_IsArrayObject(cx, wrappedObject) || JS_IsTypedArrayObject(wrappedObject);
+        bool isArray;
+        if (!JS_IsArrayObject(cx, wrappedObject, &isArray))
+            return false;
+        if (!isArray)
+            isArray = JS_IsTypedArrayObject(wrappedObject);
         bool isIndexedAccessOnArray = isArray && JSID_IS_INT(id) && JSID_TO_INT(id) >= 0;
         bool isLengthAccessOnArray = isArray && JSID_IS_STRING(id) &&
                                      JS_FlatStringEqualsAscii(JSID_TO_FLAT_STRING(id), "length");
         if (isIndexedAccessOnArray || isLengthAccessOnArray) {
             JSAutoCompartment ac2(cx, wrapper);
             ReportWrapperDenial(cx, id, WrapperDenialForCOW,
                                 "Access to elements and length of privileged Array not permitted");
         }
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -175,19 +175,17 @@ SyncViewsAndInvalidateDescendants(nsIFra
     if (aChange & nsChangeHint_SyncFrameView) {
       nsContainerFrame::SyncFrameViewProperties(aFrame->PresContext(),
                                                 aFrame, nullptr, view);
     }
   }
 
   nsIFrame::ChildListIterator lists(aFrame);
   for (; !lists.IsDone(); lists.Next()) {
-    nsFrameList::Enumerator childFrames(lists.CurrentList());
-    for (; !childFrames.AtEnd(); childFrames.Next()) {
-      nsIFrame* child = childFrames.get();
+    for (nsIFrame* child : lists.CurrentList()) {
       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
         // only do frames that don't have placeholders
         if (nsGkAtoms::placeholderFrame == child->GetType()) {
           // do the out-of-flow frame and its continuations
           nsIFrame* outOfFlowFrame =
             nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
           DoApplyRenderingChangeToTree(outOfFlowFrame, aChange);
         } else if (lists.CurrentID() == nsIFrame::kPopupList) {
@@ -632,19 +630,17 @@ RestyleManager::StyleChangeReflow(nsIFra
 void
 RestyleManager::AddSubtreeToOverflowTracker(nsIFrame* aFrame) 
 {
   mOverflowChangedTracker.AddFrame(
       aFrame,
       OverflowChangedTracker::CHILDREN_CHANGED);
   nsIFrame::ChildListIterator lists(aFrame);
   for (; !lists.IsDone(); lists.Next()) {
-    nsFrameList::Enumerator childFrames(lists.CurrentList());
-    for (; !childFrames.AtEnd(); childFrames.Next()) {
-      nsIFrame* child = childFrames.get();
+    for (nsIFrame* child : lists.CurrentList()) {
       AddSubtreeToOverflowTracker(child);
     }
   }
 }
 
 NS_DECLARE_FRAME_PROPERTY(ChangeListProperty, nullptr)
 
 /**
@@ -653,19 +649,17 @@ NS_DECLARE_FRAME_PROPERTY(ChangeListProp
  */
 static bool
 FrameHasPositionedPlaceholderDescendants(nsIFrame* aFrame, uint32_t aPositionMask)
 {
   const nsIFrame::ChildListIDs skip(nsIFrame::kAbsoluteList |
                                     nsIFrame::kFixedList);
   for (nsIFrame::ChildListIterator lists(aFrame); !lists.IsDone(); lists.Next()) {
     if (!skip.Contains(lists.CurrentID())) {
-      for (nsFrameList::Enumerator childFrames(lists.CurrentList());
-           !childFrames.AtEnd(); childFrames.Next()) {
-        nsIFrame* f = childFrames.get();
+      for (nsIFrame* f : lists.CurrentList()) {
         if (f->GetType() == nsGkAtoms::placeholderFrame) {
           nsIFrame* outOfFlow = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
           // If SVG text frames could appear here, they could confuse us since
           // they ignore their position style ... but they can't.
           NS_ASSERTION(!outOfFlow->IsSVGText(),
                        "SVG text frames can't be out of flow");
           if (aPositionMask & (1 << outOfFlow->StyleDisplay()->mPosition)) {
             return true;
@@ -2065,19 +2059,17 @@ static void
 VerifyStyleTree(nsPresContext* aPresContext, nsIFrame* aFrame,
                 nsStyleContext* aParentContext)
 {
   nsStyleContext*  context = aFrame->StyleContext();
   VerifyContextParent(aPresContext, aFrame, context, nullptr);
 
   nsIFrame::ChildListIterator lists(aFrame);
   for (; !lists.IsDone(); lists.Next()) {
-    nsFrameList::Enumerator childFrames(lists.CurrentList());
-    for (; !childFrames.AtEnd(); childFrames.Next()) {
-      nsIFrame* child = childFrames.get();
+    for (nsIFrame* child : lists.CurrentList()) {
       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
         // only do frames that are in flow
         if (nsGkAtoms::placeholderFrame == child->GetType()) {
           // placeholder: first recurse and verify the out of flow frame,
           // then verify the placeholder's context
           nsIFrame* outOfFlowFrame =
             nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
 
@@ -2457,19 +2449,17 @@ RestyleManager::ReparentStyleContext(nsI
         NS_ASSERTION(!(styleChange & nsChangeHint_ReconstructFrame),
                      "Our frame tree is likely to be bogus!");
       }
 
       aFrame->SetStyleContext(newContext);
 
       nsIFrame::ChildListIterator lists(aFrame);
       for (; !lists.IsDone(); lists.Next()) {
-        nsFrameList::Enumerator childFrames(lists.CurrentList());
-        for (; !childFrames.AtEnd(); childFrames.Next()) {
-          nsIFrame* child = childFrames.get();
+        for (nsIFrame* child : lists.CurrentList()) {
           // only do frames that are in flow
           if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
               child != providerChild) {
 #ifdef DEBUG
             if (nsGkAtoms::placeholderFrame == child->GetType()) {
               nsIFrame* outOfFlowFrame =
                 nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
               NS_ASSERTION(outOfFlowFrame, "no out-of-flow frame");
@@ -2878,19 +2868,17 @@ ElementRestyler::ConditionallyRestyleCon
   if (aFrame->GetContent()->HasFlag(mRestyleTracker.RootBit())) {
     aRestyleRoot = aFrame->GetContent()->AsElement();
   }
 
   for (nsIFrame* f = aFrame; f;
        f = GetNextContinuationWithSameStyle(f, f->StyleContext())) {
     nsIFrame::ChildListIterator lists(f);
     for (; !lists.IsDone(); lists.Next()) {
-      nsFrameList::Enumerator childFrames(lists.CurrentList());
-      for (; !childFrames.AtEnd(); childFrames.Next()) {
-        nsIFrame* child = childFrames.get();
+      for (nsIFrame* child : lists.CurrentList()) {
         // Out-of-flows are reached through their placeholders.  Continuations
         // and block-in-inline splits are reached through those chains.
         if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
             !GetPrevContinuationWithSameStyle(child)) {
           // only do frames that are in flow
           if (child->GetType() == nsGkAtoms::placeholderFrame) { // placeholder
             // get out of flow frame and recur there
             nsIFrame* outOfFlowFrame =
@@ -3086,19 +3074,17 @@ ElementRestyler::MustCheckUndisplayedCon
 bool
 ElementRestyler::MoveStyleContextsForContentChildren(
     nsIFrame* aParent,
     nsStyleContext* aOldContext,
     nsTArray<nsStyleContext*>& aContextsToMove)
 {
   nsIFrame::ChildListIterator lists(aParent);
   for (; !lists.IsDone(); lists.Next()) {
-    nsFrameList::Enumerator childFrames(lists.CurrentList());
-    for (; !childFrames.AtEnd(); childFrames.Next()) {
-      nsIFrame* child = childFrames.get();
+    for (nsIFrame* child : lists.CurrentList()) {
       // Bail out if we have out-of-flow frames.
       // FIXME: It might be safe to just continue here instead of bailing out.
       if (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) {
         return false;
       }
       if (GetPrevContinuationWithSameStyle(child)) {
         continue;
       }
@@ -4325,19 +4311,17 @@ ElementRestyler::RestyleChildrenOfDispla
 
     // Then process child frames for content that is a descendant of mContent.
     // XXX perhaps it's better to walk child frames (before reresolving
     // XXX undisplayed contexts above) and mark those that has a stylecontext
     // XXX leading up to mContent's old context? (instead of the
     // XXX ContentIsDescendantOf check below)
     nsIFrame::ChildListIterator lists(aParentFrame);
     for ( ; !lists.IsDone(); lists.Next()) {
-      nsFrameList::Enumerator childFrames(lists.CurrentList());
-      for (; !childFrames.AtEnd(); childFrames.Next()) {
-        nsIFrame* f = childFrames.get();
+      for (nsIFrame* f : lists.CurrentList()) {
         if (nsContentUtils::ContentIsDescendantOf(f->GetContent(), mContent) &&
             !f->GetPrevContinuation()) {
           if (!(f->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
             ComputeStyleChangeFor(f, mChangeList, aMinHint, aRestyleTracker,
                                   aRestyleHint, aRestyleHintData,
                                   mContextsToClear, mSwappedStructOwners);
           }
         }
@@ -4714,19 +4698,17 @@ ElementRestyler::RestyleContentChildren(
   LOG_RESTYLE("RestyleContentChildren");
 
   nsIFrame::ChildListIterator lists(aParent);
   TreeMatchContext::AutoAncestorPusher ancestorPusher(mTreeMatchContext);
   if (!lists.IsDone()) {
     ancestorPusher.PushAncestorAndStyleScope(mContent);
   }
   for (; !lists.IsDone(); lists.Next()) {
-    nsFrameList::Enumerator childFrames(lists.CurrentList());
-    for (; !childFrames.AtEnd(); childFrames.Next()) {
-      nsIFrame* child = childFrames.get();
+    for (nsIFrame* child : lists.CurrentList()) {
       // Out-of-flows are reached through their placeholders.  Continuations
       // and block-in-inline splits are reached through those chains.
       if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW) &&
           !GetPrevContinuationWithSameStyle(child)) {
         // Get the parent of the child frame's content and check if it
         // is a XBL children element. Push the children element as an
         // ancestor here because it does not have a frame and would not
         // otherwise be pushed as an ancestor.
--- a/layout/base/gtest/moz.build
+++ b/layout/base/gtest/moz.build
@@ -4,20 +4,20 @@
 # 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/.
 
 UNIFIED_SOURCES += [
     'TestAccessibleCaretEventHub.cpp',
     'TestAccessibleCaretManager.cpp',
 ]
 
-# XXX: Allow -Winconsistent-missing-override for TestAccessibleCaretManager.cpp
-# that stub virtual methods have 'override' keyword while mocked methods by
-# MOCK_METHOD macro does not. (See 1169974)
-ALLOW_COMPILER_WARNINGS = True
+# THE MOCK_METHOD2 macro from gtest triggers this clang warning and it's hard
+# to work around, so we just ignore it.
+if CONFIG['CLANG_CXX']:
+  CXXFLAGS += ['-Wno-error=inconsistent-missing-override']
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 LOCAL_INCLUDES += [
     '/docshell/base',
     '/layout/base',
     '/layout/style',
 ]
--- a/layout/base/nsCSSFrameConstructor.cpp
+++ b/layout/base/nsCSSFrameConstructor.cpp
@@ -989,24 +989,16 @@ nsFrameConstructorState::nsFrameConstruc
   }
 #endif
   MOZ_COUNT_CTOR(nsFrameConstructorState);
   mFrameState = aPresShell->GetDocument()->GetLayoutHistoryState();
 }
 
 nsFrameConstructorState::~nsFrameConstructorState()
 {
-  // Frame order comparison functions only work properly when the placeholders
-  // have been inserted into the frame tree. So for example if we have a new float
-  // containing the placeholder for a new abs-pos frame, and we process the abs-pos
-  // insertion first, then we won't be able to find the right place to insert in
-  // in the abs-pos list. So put floats in first, because they can contain placeholders
-  // for abs-pos and fixed-pos items whose containing blocks are outside the floats.
-  // Then put abs-pos frames in, because they can contain placeholders for fixed-pos
-  // items whose containing block is outside the abs-pos frames.
   MOZ_COUNT_DTOR(nsFrameConstructorState);
   ProcessFrameInsertions(mFloatedItems, nsIFrame::kFloatList);
   ProcessFrameInsertions(mAbsoluteItems, nsIFrame::kAbsoluteList);
   ProcessFrameInsertions(mFixedItems, nsIFrame::kFixedList);
 #ifdef MOZ_XUL
   ProcessFrameInsertions(mPopupItems, nsIFrame::kPopupList);
 #endif
   for (int32_t i = mGeneratedTextNodesWithInitializer.Count() - 1; i >= 0; --i) {
@@ -1265,16 +1257,21 @@ nsFrameConstructorState::ProcessFrameIns
     // If we're injecting absolutely positioned frames, inject them on the
     // absolute containing block
     if (aChildListID == containingBlock->GetAbsoluteListID()) {
       containingBlock->GetAbsoluteContainingBlock()->
         SetInitialChildList(containingBlock, aChildListID, aFrameItems);
     } else {
       containingBlock->SetInitialChildList(aChildListID, aFrameItems);
     }
+  } else if (aChildListID == nsIFrame::kFixedList ||
+             aChildListID == nsIFrame::kAbsoluteList) {
+    // The order is not important for abs-pos/fixed-pos frame list, just
+    // append the frame items to the list directly.
+    mFrameManager->AppendFrames(containingBlock, aChildListID, aFrameItems);
   } else {
     // Note that whether the frame construction context is doing an append or
     // not is not helpful here, since it could be appending to some frame in
     // the middle of the document, which means we're not necessarily
     // appending to the children of the containing block.
     //
     // We need to make sure the 'append to the end of document' case is fast.
     // So first test the last child of the containing block
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -227,24 +227,25 @@ protected:
     {
       MOZ_ASSERT(aStart < aEnd && aEnd <= kTranslatedMaxLine);
       mStart = aStart;
       mEnd = aEnd;
     }
   };
 
   /**
-   * Return aLine if it's inside the aMin..aMax range (inclusive),
-   * otherwise return kAutoLine.
+   * Return aLine if it's inside the aMin..aMax range (inclusive), otherwise
+   * return kAutoLine.  If the range is empty (aMin == aMax, i.e. there are
+   * no tracks in the grid) then aLine is outside.
    */
   static int32_t
   AutoIfOutside(int32_t aLine, int32_t aMin, int32_t aMax)
   {
     MOZ_ASSERT(aMin <= aMax);
-    if (aLine < aMin || aLine > aMax) {
+    if (aLine < aMin || aLine > aMax || aMin == aMax) {
       return kAutoLine;
     }
     return aLine;
   }
 
   /**
    * A GridArea is the area in the grid for a grid item.
    * The area is represented by two LineRanges, both of which can be auto
--- a/layout/reftests/css-grid/grid-abspos-items-001-ref.html
+++ b/layout/reftests/css-grid/grid-abspos-items-001-ref.html
@@ -157,27 +157,27 @@ span {
 <span class="i abs">i</span>
 </div>
 
 <div class="grid" style="height:7px">
 <span class="j abs">j</span>
 </div>
 
 <div class="grid" style="width:43px; height:53px">
-<span class="abs" style="left:1px; top:3px; height:11px; width:5px;">a</span>
-<span class="abs" style="right:5px; top:3px; height:11px; width:42px;">b</span>
-<span class="abs" style="left:1px; bottom:1px; height:58px; width:5px;">c</span>
-<span class="abs" style="right:5px; bottom:1px; height:58px; width:42px;">d</span>
+<span class="abs" style="width:auto;height:auto; top:3px; left:1px; bottom:1px; right:5px">a</span>
+<span class="abs" style="width:auto;height:auto; top:3px; left:1px; bottom:1px; right:5px">b</span>
+<span class="abs" style="width:auto;height:auto; top:3px; left:1px; bottom:1px; right:5px">c</span>
+<span class="abs" style="width:auto;height:auto; top:3px; left:1px; bottom:1px; right:5px">d</span>
 </div>
 
 <div class="grid" style="width:43px; height:28px; border-width:0;">
-<span class="abs" style="right:48px; top:3px; height:11px; width:12px;"></span>
+<span class="abs" style="right:48px; top:3px; height:22px; width:12px;"></span>
 </div>
 <div class="grid" style="width:43px; height:28px; border-width:0;">
-<span class="abs" style="left:1px; bottom:57px; height:22px; width:5px;"></span>
+<span class="abs" style="left:1px; bottom:57px; height:22px; width:12px;"></span>
 </div>
 <div class="grid" style="width:43px; height:28px; border-width:0;">
 <span class="abs" style="right:48px; bottom:85px; height:22px; width:12px;"></span>
 </div>
 
 </div>
 
 </body>
--- a/layout/reftests/css-grid/grid-abspos-items-002-ref.html
+++ b/layout/reftests/css-grid/grid-abspos-items-002-ref.html
@@ -158,18 +158,18 @@ span {
 <span class="i abs">i</span>
 </div>
 
 <div class="grid" style="height:7px">
 <span class="j abs">j</span>
 </div>
 
 <div class="grid" style="width:43px; height:53px">
-<span class="abs" style="left:1px; top:3px; height:11px; width:5px;">a</span>
-<span class="abs" style="right:5px; top:3px; height:11px; width:42px;">b</span>
-<span class="abs" style="left:1px; bottom:1px; height:58px; width:5px;">c</span>
-<span class="abs" style="right:5px; bottom:1px; height:58px; width:42px;">d</span>
+<span class="abs" style="width:auto;height:auto; top:3px; left:1px; bottom:1px; right:5px">a</span>
+<span class="abs" style="width:auto;height:auto; top:3px; left:1px; bottom:1px; right:5px">b</span>
+<span class="abs" style="width:auto;height:auto; top:3px; left:1px; bottom:1px; right:5px">c</span>
+<span class="abs" style="width:auto;height:auto; top:3px; left:1px; bottom:1px; right:5px">d</span>
 </div>
 
 </div>
 
 </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-abspos-items-011-ref.html
@@ -0,0 +1,54 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: abs pos areas in empty grid</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">
+  <style type="text/css">
+
+div {
+  display: block;
+  position: relative;
+  float: left;
+  width: 20px;
+  height: 20px;
+  background: red;
+}
+
+span {
+  position: absolute;
+  top:0;left:0;bottom:0;right:0;
+  background: lime;
+}
+
+</style>
+</head>
+<body>
+
+There should be no red areas.
+<br clear="all">
+
+<div><span class="cs"></span></div>
+<div><span class="ce"></span></div>
+<div><span class="rs"></span></div>
+<div><span class="rs"></span></div>
+
+<div><span class="cs ce"></span></div>
+<div><span class="cs rs"></span></div>
+<div><span class="cs re"></span></div>
+<div><span class="ce rs"></span></div>
+<div><span class="ce re"></span></div>
+<div><span class="rs re"></span></div>
+
+<div><span class="cs ce rs"></span></div>
+<div><span class="cs ce re"></span></div>
+<div><span class="rs re cs"></span></div>
+<div><span class="rs re ce"></span></div>
+
+<div><span class="cs ce rs re"></span></div>
+
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-abspos-items-011.html
@@ -0,0 +1,61 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: abs pos areas in empty grid</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=">
+  <link rel="help" href="http://dev.w3.org/csswg/css-grid/#abspos-items">
+  <link rel="match" href="grid-abspos-items-011-ref.html">
+  <style type="text/css">
+
+div {
+  display: grid;
+  position: relative;
+  float: left;
+  width: 20px;
+  height: 20px;
+  background: red;
+}
+
+span {
+  position: absolute;
+  top:0;left:0;bottom:0;right:0;
+  background: lime;
+}
+
+.cs { grid-column-start: 1; }
+.ce { grid-column-end: 1; }
+.rs { grid-row-start: 1; }
+.re { grid-row-end: 1; }
+
+</style>
+</head>
+<body>
+
+There should be no red areas.
+<br clear="all">
+
+<div><span class="cs"></span></div>
+<div><span class="ce"></span></div>
+<div><span class="rs"></span></div>
+<div><span class="rs"></span></div>
+
+<div><span class="cs ce"></span></div>
+<div><span class="cs rs"></span></div>
+<div><span class="cs re"></span></div>
+<div><span class="ce rs"></span></div>
+<div><span class="ce re"></span></div>
+<div><span class="rs re"></span></div>
+
+<div><span class="cs ce rs"></span></div>
+<div><span class="cs ce re"></span></div>
+<div><span class="rs re cs"></span></div>
+<div><span class="rs re ce"></span></div>
+
+<div><span class="cs ce rs re"></span></div>
+
+</body>
+</html>
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -16,16 +16,17 @@ fails == grid-whitespace-handling-1b.xht
 == grid-abspos-items-003.html grid-abspos-items-003-ref.html
 == grid-abspos-items-004.html grid-abspos-items-004-ref.html
 == grid-abspos-items-005.html grid-abspos-items-005-ref.html
 == grid-abspos-items-006.html grid-abspos-items-006-ref.html
 == grid-abspos-items-007.html grid-abspos-items-007-ref.html
 == grid-abspos-items-008.html grid-abspos-items-008-ref.html
 == grid-abspos-items-009.html grid-abspos-items-009-ref.html
 == grid-abspos-items-010.html grid-abspos-items-010-ref.html
+== grid-abspos-items-011.html grid-abspos-items-011-ref.html
 == grid-order-abspos-items-001.html grid-order-abspos-items-001-ref.html
 == grid-order-placement-auto-001.html grid-order-placement-auto-001-ref.html
 == grid-order-placement-definite-001.html grid-order-placement-definite-001-ref.html
 skip-if(Android) == grid-placement-definite-implicit-001.html grid-placement-definite-implicit-001-ref.html
 == grid-placement-definite-implicit-002.html grid-placement-definite-implicit-002-ref.html
 skip-if(Android) fuzzy-if(winWidget,1,32) == grid-placement-auto-implicit-001.html grid-placement-auto-implicit-001-ref.html
 == grid-placement-abspos-implicit-001.html grid-placement-abspos-implicit-001-ref.html
 pref(layout.css.vertical-text.enabled,true) == rtl-grid-placement-definite-001.html rtl-grid-placement-definite-001-ref.html
--- a/layout/style/GenerateCSSPropsGenerated.py
+++ b/layout/style/GenerateCSSPropsGenerated.py
@@ -17,42 +17,65 @@ def property_compare(x, y):
     property_order = {"longhand": 0, "logical": 0, "shorthand": 1, "alias": 2}
     return property_order[x["proptype"]] - property_order[y["proptype"]]
 
 properties = sorted(properties, cmp=property_compare)
 
 for i, p in enumerate(properties):
     p["index"] = i
 
+# Record each property's IDL name.
+for p in properties:
+    if "CSS_PROPERTY_INTERNAL" in p["flags"]:
+        p["idlname"] = None
+    else:
+        idl_name = p["prop"]
+        if not idl_name.startswith("Moz"):
+            idl_name = idl_name[0].lower() + idl_name[1:]
+        p["idlname"] = idl_name
+
 def generate_idl_names(properties):
     names = []
     for p in properties:
         if p["proptype"] is "alias":
             continue
-        if "CSS_PROPERTY_INTERNAL" in p["flags"]:
+        if p["idlname"] is None:
             names.append("  nullptr,  // %s" % p["name"])
         else:
-            idl_name = p["prop"]
-            if not idl_name.startswith("Moz"):
-                idl_name = idl_name[0].lower() + idl_name[1:]
-            names.append('  "%s",' % idl_name)
+            names.append('  "%s",' % p["idlname"])
     return "\n".join(names)
 
 def generate_assertions(properties):
     def enum(p):
         if p["proptype"] is "alias":
             return "eCSSPropertyAlias_%s" % p["prop"]
         else:
             return "eCSSProperty_%s" % p["id"]
     msg = ('static_assert(%s == %d, "GenerateCSSPropsGenerated.py did not list '
            'properties in nsCSSProperty order");')
     return "\n".join(map(lambda p: msg % (enum(p), p["index"]), properties))
 
+def generate_idl_name_positions(properties):
+    # Skip aliases.
+    ps = filter(lambda p: p["proptype"] is not "alias", properties)
+
+    # Sort alphabetically by IDL name.
+    ps = sorted(ps, key=lambda p: p["idlname"])
+
+    # Annotate entries with the sorted position.
+    ps = [(p, position) for position, p in enumerate(ps)]
+
+    # Sort back to nsCSSProperty order.
+    ps = sorted(ps, key=lambda (p, position): p["index"])
+
+    return ",\n".join(map(lambda (p, position): "  %d" % position, ps))
+
 cppFile = open(sys.argv[1], "r")
 cppTemplate = cppFile.read()
 cppFile.close()
 
 substitutions = {
   "idl_names": generate_idl_names(properties),
-  "assertions": generate_assertions(properties)
+  "assertions": generate_assertions(properties),
+  "idl_name_positions": generate_idl_name_positions(properties),
 }
 print ("/* THIS IS AN AUTOGENERATED FILE.  DO NOT EDIT */\n\n" +
        string.Template(cppTemplate).substitute(substitutions))
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1206105-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<title>crashtest, bug 1206105</title>
+<style>
+*:nth-child(-n-2147483647) {}
+</style>
+<body>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -116,10 +116,11 @@ pref(layout.css.expensive-style-struct-a
 load 1153693-1.html
 load 1161320-1.html
 pref(dom.animations-api.core.enabled,true) load 1161320-2.html
 load 1161366-1.html
 load 1163446-1.html
 load 1164813-1.html
 load 1167782-1.html
 load 1200568-1.html
+load 1206105-1.html
 load large_border_image_width.html
 load border-image-visited-link.html
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -523,16 +523,31 @@ public:
    */
   static const char* PropertyIDLName(nsCSSProperty aProperty)
   {
     MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT,
                "out of range");
     return kIDLNameTable[aProperty];
   }
 
+private:
+  static const int32_t kIDLNameSortPositionTable[eCSSProperty_COUNT];
+
+public:
+  /**
+   * Returns the position of the specified property in a list of all
+   * properties sorted by their IDL name.
+   */
+  static int32_t PropertyIDLNameSortPosition(nsCSSProperty aProperty)
+  {
+    MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT,
+               "out of range");
+    return kIDLNameSortPositionTable[aProperty];
+  }
+
 public:
 
   static bool IsEnabled(nsCSSProperty aProperty) {
     MOZ_ASSERT(0 <= aProperty && aProperty < eCSSProperty_COUNT_with_aliases,
                "out of range");
     return gPropertyEnabled[aProperty];
   }
 
--- a/layout/style/nsCSSPropsGenerated.inc.in
+++ b/layout/style/nsCSSPropsGenerated.inc.in
@@ -5,9 +5,13 @@
 
 /* processed file that defines CSS property tables that can't be generated
    with the pre-processor, designed to be #included in nsCSSProps.cpp */
 
 const char* const nsCSSProps::kIDLNameTable[eCSSProperty_COUNT] = {
 ${idl_names}
 };
 
+const int32_t nsCSSProps::kIDLNameSortPositionTable[eCSSProperty_COUNT] = {
+${idl_name_positions}
+};
+
 ${assertions}
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -1566,18 +1566,21 @@ nthChildGenericMatches(Element* aElement
   // a * n + b == index.
   if (a == 0) {
     return b == index;
   }
 
   // Integer division in C does truncation (towards 0).  So
   // check that the result is nonnegative, and that there was no
   // truncation.
-  const int32_t n = (index - b) / a;
-  return n >= 0 && (a * n == index - b);
+  const CheckedInt<int32_t> indexMinusB = CheckedInt<int32_t>(index) - b;
+  const CheckedInt<int32_t> n = indexMinusB / a;
+  return n.isValid() &&
+         n.value() >= 0 &&
+         a * n == indexMinusB;
 }
 
 static inline bool
 edgeOfTypeMatches(Element* aElement, TreeMatchContext& aTreeMatchContext,
                   bool checkFirst, bool checkLast)
 {
   nsIContent *parent = aElement->GetParent();
   if (!parent) {
--- a/media/libstagefright/binding/Index.cpp
+++ b/media/libstagefright/binding/Index.cpp
@@ -250,16 +250,17 @@ Index::Index(const nsTArray<Indice>& aIn
     }
     for (size_t i = 0; i < aIndex.Length(); i++) {
       const Indice& indice = aIndex[i];
       Sample sample;
       sample.mByteRange = MediaByteRange(indice.start_offset,
                                          indice.end_offset);
       sample.mCompositionRange = Interval<Microseconds>(indice.start_composition,
                                                         indice.end_composition);
+      sample.mDecodeTime = indice.start_decode;
       sample.mSync = indice.sync;
       // FIXME: Make this infallible after bug 968520 is done.
       MOZ_ALWAYS_TRUE(mIndex.AppendElement(sample, fallible));
     }
   }
 }
 
 Index::~Index() {}
--- a/media/libstagefright/binding/MP4Metadata.cpp
+++ b/media/libstagefright/binding/MP4Metadata.cpp
@@ -76,16 +76,17 @@ ConvertIndex(FallibleTArray<Index::Indic
   }
   for (size_t i = 0; i < aIndex.Length(); i++) {
     Index::Indice indice;
     const stagefright::MediaSource::Indice& s_indice = aIndex[i];
     indice.start_offset = s_indice.start_offset;
     indice.end_offset = s_indice.end_offset;
     indice.start_composition = s_indice.start_composition - aMediaTime;
     indice.end_composition = s_indice.end_composition - aMediaTime;
+    indice.start_decode = s_indice.start_decode;
     indice.sync = s_indice.sync;
     // FIXME: Make this infallible after bug 968520 is done.
     MOZ_ALWAYS_TRUE(aDest.AppendElement(indice, mozilla::fallible));
   }
   return true;
 }
 
 MP4Metadata::MP4Metadata(Stream* aSource)
--- a/media/libstagefright/binding/include/mp4_demuxer/Index.h
+++ b/media/libstagefright/binding/include/mp4_demuxer/Index.h
@@ -46,16 +46,17 @@ public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Index)
 
   struct Indice
   {
     uint64_t start_offset;
     uint64_t end_offset;
     uint64_t start_composition;
     uint64_t end_composition;
+    uint64_t start_decode;
     bool sync;
   };
 
   Index(const nsTArray<Indice>& aIndex,
         Stream* aSource,
         uint32_t aTrackId,
         bool aIsAudio,
         mozilla::Monitor* aMonitor);
--- a/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h
+++ b/media/libstagefright/frameworks/av/include/media/stagefright/MediaSource.h
@@ -111,16 +111,17 @@ struct MediaSource : public virtual RefB
     }
 
     struct Indice
     {
       uint64_t start_offset;
       uint64_t end_offset;
       uint64_t start_composition;
       uint64_t end_composition;
+      uint64_t start_decode;
       bool sync;
     };
 
     virtual nsTArray<Indice> exportIndex() = 0;
 
 protected:
     virtual ~MediaSource();
 
--- a/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/MPEG4Extractor.cpp
@@ -4253,32 +4253,34 @@ nsTArray<MediaSource::Indice> MPEG4Sourc
   }
   for (uint32_t sampleIndex = 0; sampleIndex < mSampleTable->countSamples();
           sampleIndex++) {
       off64_t offset;
       size_t size;
       uint32_t compositionTime;
       uint32_t duration;
       bool isSyncSample;
+      uint32_t decodeTime;
       if (mSampleTable->getMetaDataForSample(sampleIndex, &offset, &size,
                                              &compositionTime, &duration,
-                                             &isSyncSample) != OK) {
+                                             &isSyncSample, &decodeTime) != OK) {
           ALOGE("Unexpected sample table problem");
           continue;
       }
 
       Indice indice;
       indice.start_offset = offset;
       indice.end_offset = offset + size;
       indice.start_composition = (compositionTime * 1000000ll) / mTimescale;
       // end_composition is overwritten everywhere except the last frame, where
       // the presentation duration is equal to the sample duration.
       indice.end_composition =
           (compositionTime * 1000000ll + duration * 1000000ll) / mTimescale;
       indice.sync = isSyncSample;
+      indice.start_decode = (decodeTime * 1000000ll) / mTimescale;
       index.AppendElement(indice);
   }
 
   // Fix up composition durations so we don't end up with any unsightly gaps.
   if (index.Length() != 0) {
       nsTArray<Indice*> composition_order;
       if (!composition_order.SetCapacity(index.Length(), mozilla::fallible)) {
         return index;
--- a/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/frameworks/av/media/libstagefright/SampleTable.cpp
@@ -383,23 +383,23 @@ status_t SampleTable::setCompositionTime
 
     uint8_t header[8];
     if (mDataSource->readAt(
                 data_offset, header, sizeof(header))
             < (ssize_t)sizeof(header)) {
         return ERROR_IO;
     }
 
-    if (U32_AT(header) != 0) {
+    uint32_t numEntries = U32_AT(&header[4]);
+
+    if (U32_AT(header) != 0 && numEntries) {
         // Expected version = 0, flags = 0.
         return ERROR_MALFORMED;
     }
 
-    uint32_t numEntries = U32_AT(&header[4]);
-
     if (data_size != ((uint64_t)numEntries + 1) * 8) {
         return ERROR_MALFORMED;
     }
 
     mNumCompositionTimeDeltaEntries = numEntries;
     mCompositionTimeDeltaEntries = new uint32_t[2 * numEntries];
 
     if (mDataSource->readAt(
--- a/media/libstagefright/gtest/TestParser.cpp
+++ b/media/libstagefright/gtest/TestParser.cpp
@@ -399,8 +399,98 @@ TEST(stagefright_MoofParser, test_case_m
     parser.HasMetadata();
     nsRefPtr<MediaByteBuffer> metadataBuffer = parser.Metadata();
     parser.FirstCompleteMediaSegment();
     parser.FirstCompleteMediaHeader();
 
     buffer->RemoveElementsAt(0, step);
   }
 }
+
+uint8_t media_libstagefright_gtest_video_init_mp4[] = {
+  0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6f, 0x6d,
+  0x00, 0x00, 0x00, 0x01, 0x69, 0x73, 0x6f, 0x6d, 0x61, 0x76, 0x63, 0x31,
+  0x00, 0x00, 0x02, 0xd1, 0x6d, 0x6f, 0x6f, 0x76, 0x00, 0x00, 0x00, 0x6c,
+  0x6d, 0x76, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x49, 0x73, 0xf8,
+  0xc8, 0x4a, 0xc5, 0x7a, 0x00, 0x00, 0x02, 0x58, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x01, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x18,
+  0x69, 0x6f, 0x64, 0x73, 0x00, 0x00, 0x00, 0x00, 0x10, 0x80, 0x80, 0x80,
+  0x07, 0x00, 0x4f, 0xff, 0xff, 0x29, 0x15, 0xff, 0x00, 0x00, 0x02, 0x0d,
+  0x74, 0x72, 0x61, 0x6b, 0x00, 0x00, 0x00, 0x5c, 0x74, 0x6b, 0x68, 0x64,
+  0x00, 0x00, 0x00, 0x01, 0xc8, 0x49, 0x73, 0xf8, 0xc8, 0x49, 0x73, 0xf9,
+  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x40, 0x00, 0x00, 0x00, 0x02, 0x80, 0x00, 0x00, 0x01, 0x68, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0xa9, 0x6d, 0x64, 0x69, 0x61, 0x00, 0x00, 0x00, 0x20,
+  0x6d, 0x64, 0x68, 0x64, 0x00, 0x00, 0x00, 0x00, 0xc8, 0x49, 0x73, 0xf8,
+  0xc8, 0x49, 0x73, 0xf9, 0x00, 0x00, 0x75, 0x30, 0x00, 0x00, 0x00, 0x00,
+  0x55, 0xc4, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x68, 0x64, 0x6c, 0x72,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x64, 0x65,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x47, 0x50, 0x41, 0x43, 0x20, 0x49, 0x53, 0x4f, 0x20, 0x56, 0x69, 0x64,
+  0x65, 0x6f, 0x20, 0x48, 0x61, 0x6e, 0x64, 0x6c, 0x65, 0x72, 0x00, 0x00,
+  0x00, 0x00, 0x01, 0x49, 0x6d, 0x69, 0x6e, 0x66, 0x00, 0x00, 0x00, 0x14,
+  0x76, 0x6d, 0x68, 0x64, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0x64, 0x69, 0x6e, 0x66,
+  0x00, 0x00, 0x00, 0x1c, 0x64, 0x72, 0x65, 0x66, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x0c, 0x75, 0x72, 0x6c, 0x20,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x09, 0x73, 0x74, 0x62, 0x6c,
+  0x00, 0x00, 0x00, 0xad, 0x73, 0x74, 0x73, 0x64, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x9d, 0x61, 0x76, 0x63, 0x31,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x02, 0x80, 0x01, 0x68, 0x00, 0x48, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x18, 0xff, 0xff, 0x00, 0x00, 0x00, 0x33, 0x61, 0x76,
+  0x63, 0x43, 0x01, 0x64, 0x00, 0x1f, 0xff, 0xe1, 0x00, 0x1b, 0x67, 0x64,
+  0x00, 0x1f, 0xac, 0x2c, 0xc5, 0x02, 0x80, 0xbf, 0xe5, 0xc0, 0x44, 0x00,
+  0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0xf2, 0x3c, 0x60, 0xc6,
+  0x58, 0x01, 0x00, 0x05, 0x68, 0xe9, 0x2b, 0x2c, 0x8b, 0x00, 0x00, 0x00,
+  0x14, 0x62, 0x74, 0x72, 0x74, 0x00, 0x01, 0x5a, 0xc2, 0x00, 0x24, 0x74,
+  0x38, 0x00, 0x09, 0x22, 0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x74,
+  0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x10, 0x63, 0x74, 0x74, 0x73, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x73, 0x63, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0x73, 0x74, 0x73,
+  0x7a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x10, 0x73, 0x74, 0x63, 0x6f, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x38, 0x6d, 0x76, 0x65,
+  0x78, 0x00, 0x00, 0x00, 0x10, 0x6d, 0x65, 0x68, 0x64, 0x00, 0x00, 0x00,
+  0x00, 0x00, 0x05, 0x76, 0x18, 0x00, 0x00, 0x00, 0x20, 0x74, 0x72, 0x65,
+  0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+  0x01, 0x00, 0x00, 0x03, 0xe8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+  0x00
+};
+
+const uint32_t media_libstagefright_gtest_video_init_mp4_len = 745;
+
+TEST(stagefright_MP4Metadata, EmptyCTTS)
+{
+  nsRefPtr<MediaByteBuffer> buffer = new MediaByteBuffer(media_libstagefright_gtest_video_init_mp4_len);
+  buffer->AppendElements(media_libstagefright_gtest_video_init_mp4, media_libstagefright_gtest_video_init_mp4_len);
+  nsRefPtr<BufferStream> stream = new BufferStream(buffer);
+
+  EXPECT_TRUE(MP4Metadata::HasCompleteMetadata(stream));
+  nsRefPtr<MediaByteBuffer> metadataBuffer = MP4Metadata::Metadata(stream);
+  EXPECT_TRUE(metadataBuffer);
+
+  MP4Metadata metadata(stream);
+
+  EXPECT_EQ(1u, metadata.GetNumberTracks(TrackInfo::kVideoTrack));
+  mozilla::UniquePtr<mozilla::TrackInfo> track =
+    metadata.GetTrackInfo(TrackInfo::kVideoTrack, 0);
+  EXPECT_TRUE(track != nullptr);
+  // We can seek anywhere in any MPEG4.
+  EXPECT_TRUE(metadata.CanSeek());
+  EXPECT_FALSE(metadata.Crypto().valid);
+}
+
--- a/mobile/android/config/tooltool-manifests/android/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android/releng.manifest
@@ -43,16 +43,16 @@
 "size": 80458572,
 "visibility": "public",
 "unpack": true,
 "digest": "e5101f9dee1e462f6cbd3897ea57eede41d23981825c7b20d91d23ab461875d54d3dfc24999aa58a31e8b01f49fb3140e05ffe5af2957ef1d1afb89fd0dfe1ad",
 "algorithm": "sha512",
 "filename": "gcc.tar.xz"
 },
 {
-"size": 55391032,
+"size": 31013068,
 "visibility": "public",
-"digest": "a15fbba949fbb5039bef717f55370d40050a537a2b3a1ffe5e8b655ae90b54419020783c183bba72ce2610f35e3611e259df09b5844a72bd161b512fb54898fb",
+"digest": "e30a26f98a3448064857491aee1a7a26f98494f86a89113de9be17c37c8181ed60250706fed881ec1f035002fcdaf8b9b4a7d9ae70ce40acff2f1acfbb40f8d9",
 "algorithm": "sha512",
-"unpack": true,
-"filename": "java_home-7u79-2.5.5-0ubuntu0.14.04.2-amd64.tar.xz"
+"filename": "java_home-1.7.0-openjdk-1.7.0.85.x86_64.tar.xz",
+"unpack": true
 }
 ]
--- a/modules/libjar/InterceptedJARChannel.cpp
+++ b/modules/libjar/InterceptedJARChannel.cpp
@@ -31,16 +31,28 @@ InterceptedJARChannel::GetResponseBody(n
 NS_IMETHODIMP
 InterceptedJARChannel::GetIsNavigation(bool* aIsNavigation)
 {
   *aIsNavigation = mIsNavigation;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+InterceptedJARChannel::GetInternalContentPolicyType(nsContentPolicyType* aPolicyType)
+{
+  NS_ENSURE_ARG(aPolicyType);
+  nsCOMPtr<nsILoadInfo> loadInfo;
+  nsresult rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aPolicyType = loadInfo->InternalContentPolicyType();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 InterceptedJARChannel::GetChannel(nsIChannel** aChannel)
 {
   NS_IF_ADDREF(*aChannel = mChannel);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 InterceptedJARChannel::ResetInterception()
--- a/modules/libjar/nsJARChannel.cpp
+++ b/modules/libjar/nsJARChannel.cpp
@@ -874,18 +874,20 @@ nsJARChannel::ShouldIntercept()
 
     nsCOMPtr<nsINetworkInterceptController> controller;
     NS_QueryNotificationCallbacks(mCallbacks, mLoadGroup,
                                   NS_GET_IID(nsINetworkInterceptController),
                                   getter_AddRefs(controller));
     bool shouldIntercept = false;
     if (controller && !mForceNoIntercept) {
       bool isNavigation = mLoadFlags & LOAD_DOCUMENT_URI;
+      nsContentPolicyType type = mLoadInfo->InternalContentPolicyType();
       nsresult rv = controller->ShouldPrepareForIntercept(mAppURI,
                                                           isNavigation,
+                                                          type,
                                                           &shouldIntercept);
       NS_ENSURE_SUCCESS(rv, false);
     }
 
     return shouldIntercept;
 }
 
 void nsJARChannel::ResetInterception()
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -456,20 +456,20 @@ pref("media.getusermedia.playout_delay",
 #endif
 #endif
 
 #if !defined(ANDROID)
 pref("media.getusermedia.screensharing.enabled", true);
 #endif
 
 #ifdef RELEASE_BUILD
-pref("media.getusermedia.screensharing.allowed_domains", "webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,example.com,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com");
+pref("media.getusermedia.screensharing.allowed_domains", "webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,example.com,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org");
 #else
  // temporary value, not intended for release - bug 1049087
-pref("media.getusermedia.screensharing.allowed_domains", "mozilla.github.io,webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,example.com,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com");
+pref("media.getusermedia.screensharing.allowed_domains", "mozilla.github.io,webex.com,*.webex.com,ciscospark.com,*.ciscospark.com,projectsquared.com,*.projectsquared.com,*.room.co,room.co,beta.talky.io,talky.io,*.clearslide.com,appear.in,*.appear.in,tokbox.com,*.tokbox.com,*.sso.francetelecom.fr,*.si.francetelecom.fr,*.sso.infra.ftgroup,*.multimedia-conference.orange-business.com,*.espacecollaboration.orange-business.com,free.gotomeeting.com,g2m.me,*.g2m.me,example.com,*.mypurecloud.com,*.mypurecloud.com.au,spreed.me,*.spreed.me,*.spreed.com,air.mozilla.org");
 #endif
 // OS/X 10.6 and XP have screen/window sharing off by default due to various issues - Caveat emptor
 pref("media.getusermedia.screensharing.allow_on_old_platforms", false);
 
 pref("media.getusermedia.audiocapture.enabled", false);
 
 // TextTrack support
 pref("media.webvtt.enabled", true);
@@ -1317,17 +1317,17 @@ pref("network.http.enablePerElementRefer
 
 // Maximum number of consecutive redirects before aborting.
 pref("network.http.redirection-limit", 20);
 
 // Enable http compression: comment this out in case of problems with 1.1
 // NOTE: support for "compress" has been disabled per bug 196406.
 // NOTE: separate values with comma+space (", "): see bug 576033
 pref("network.http.accept-encoding", "gzip, deflate");
-pref("network.http.accept-encoding.secure", "gzip, deflate");
+pref("network.http.accept-encoding.secure", "gzip, deflate, brotli");
 
 pref("network.http.pipelining"      , false);
 pref("network.http.pipelining.ssl"  , false); // disable pipelining over SSL
 pref("network.http.pipelining.abtest", false);
 pref("network.http.proxy.pipelining", false);
 
 // Max number of requests in the pipeline
 pref("network.http.pipelining.maxrequests" , 32);
--- a/netwerk/base/nsINetworkInterceptController.idl
+++ b/netwerk/base/nsINetworkInterceptController.idl
@@ -1,14 +1,15 @@
 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* 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 "nsISupports.idl"
+#include "nsIContentPolicyBase.idl"
 
 interface nsIChannel;
 interface nsIOutputStream;
 interface nsIURI;
 
 %{C++
 namespace mozilla {
 namespace dom {
@@ -21,17 +22,17 @@ class ChannelInfo;
 
 /**
  * Interface to allow implementors of nsINetworkInterceptController to control the behaviour
  * of intercepted channels without tying implementation details of the interception to
  * the actual channel. nsIInterceptedChannel is expected to be implemented by objects
  * which do not implement nsIChannel.
  */
 
-[scriptable, uuid(40851f73-f799-4a22-a69b-05e0371ee373)]
+[scriptable, uuid(91d430cc-7e04-4df1-a3c0-879fa30f7ab9)]
 interface nsIInterceptedChannel : nsISupports
 {
     /**
      * Instruct a channel that has been intercepted to continue with the original
      * network request.
      */
     void resetInterception();
 
@@ -76,16 +77,22 @@ interface nsIInterceptedChannel : nsISup
      */
     readonly attribute bool isNavigation;
 
     /**
      * This method allows to override the channel info for the channel.
      */
     [noscript]
     void setChannelInfo(in ChannelInfo channelInfo);
+
+    /**
+     * Get the internal load type from the underlying channel.
+     */
+    [noscript]
+    readonly attribute nsContentPolicyType internalContentPolicyType;
 };
 
 /**
  * Interface to allow consumers to dispatch the fetch event asynchronously.
  * Consumers get access to this interface by calling channelIntercepted(),
  * and they can choose to either dispatch() immediately or do that at some
  * later time.
  */
@@ -100,27 +107,27 @@ interface nsIFetchEventDispatcher : nsIS
 };
 
 /**
  * Interface to allow consumers to attach themselves to a channel's
  * notification callbacks/loadgroup and determine if a given channel
  * request should be intercepted before any network request is initiated.
  */
 
-[scriptable, uuid(0a7aa192-0520-4995-88a3-225ed1a0930d)]
+[scriptable, uuid(7157fe12-20e3-45db-b09e-68fdf6d0614f)]
 interface nsINetworkInterceptController : nsISupports
 {
     /**
      * Returns true if a channel should avoid initiating any network
      * requests until specifically instructed to do so.
      *
      * @param aURI the URI being requested by a channel
      * @param aIsNavigate True if the request is for a navigation, false for a fetch.
      */
-    bool shouldPrepareForIntercept(in nsIURI aURI, in bool aIsNavigate);
+    bool shouldPrepareForIntercept(in nsIURI aURI, in bool aIsNavigate, in nsContentPolicyType aType);
 
     /**
      * Notification when a given intercepted channel is prepared to accept a synthesized
      * response via the provided stream.
      *
      * @param aChannel the controlling interface for a channel that has been intercepted
      */
     nsIFetchEventDispatcher channelIntercepted(in nsIInterceptedChannel aChannel);
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -438,16 +438,17 @@ nsresult NS_NewStreamConv(nsStreamConver
 #define INDEX_TO_HTML                "?from=application/http-index-format&to=text/html"
 #define MULTI_MIXED_X                "?from=multipart/x-mixed-replace&to=*/*"
 #define MULTI_MIXED                  "?from=multipart/mixed&to=*/*"
 #define APPLICATION_PACKAGE_CONV     "?from=" APPLICATION_PACKAGE "&to=*/*"
 #define MULTI_BYTERANGES             "?from=multipart/byteranges&to=*/*"
 #define UNKNOWN_CONTENT              "?from=" UNKNOWN_CONTENT_TYPE "&to=*/*"
 #define GZIP_TO_UNCOMPRESSED         "?from=gzip&to=uncompressed"
 #define XGZIP_TO_UNCOMPRESSED        "?from=x-gzip&to=uncompressed"
+#define BROTLI_TO_UNCOMPRESSED       "?from=brotli&to=uncompressed"
 #define COMPRESS_TO_UNCOMPRESSED     "?from=compress&to=uncompressed"
 #define XCOMPRESS_TO_UNCOMPRESSED    "?from=x-compress&to=uncompressed"
 #define DEFLATE_TO_UNCOMPRESSED      "?from=deflate&to=uncompressed"
 #define PLAIN_TO_HTML                "?from=text/plain&to=text/html"
 
 #ifdef BUILD_BINHEX_DECODER
 #define BINHEX_TO_WILD               "?from=application/mac-binhex40&to=*/*"
 #endif
@@ -457,16 +458,17 @@ static const mozilla::Module::CategoryEn
     { NS_ISTREAMCONVERTER_KEY, INDEX_TO_HTML, "" },
     { NS_ISTREAMCONVERTER_KEY, MULTI_MIXED_X, "" },
     { NS_ISTREAMCONVERTER_KEY, MULTI_MIXED, "" },
     { NS_ISTREAMCONVERTER_KEY, APPLICATION_PACKAGE_CONV, "" },
     { NS_ISTREAMCONVERTER_KEY, MULTI_BYTERANGES, "" },
     { NS_ISTREAMCONVERTER_KEY, UNKNOWN_CONTENT, "" },
     { NS_ISTREAMCONVERTER_KEY, GZIP_TO_UNCOMPRESSED, "" },
     { NS_ISTREAMCONVERTER_KEY, XGZIP_TO_UNCOMPRESSED, "" },
+    { NS_ISTREAMCONVERTER_KEY, BROTLI_TO_UNCOMPRESSED, "" },
     { NS_ISTREAMCONVERTER_KEY, COMPRESS_TO_UNCOMPRESSED, "" },
     { NS_ISTREAMCONVERTER_KEY, XCOMPRESS_TO_UNCOMPRESSED, "" },
     { NS_ISTREAMCONVERTER_KEY, DEFLATE_TO_UNCOMPRESSED, "" },
 #ifdef BUILD_BINHEX_DECODER
     { NS_ISTREAMCONVERTER_KEY, BINHEX_TO_WILD, "" },
 #endif
     { NS_ISTREAMCONVERTER_KEY, PLAIN_TO_HTML, "" },
     NS_BINARYDETECTOR_CATEGORYENTRY,
@@ -1045,16 +1047,17 @@ static const mozilla::Module::ContractID
     { NS_ISTREAMCONVERTER_KEY MULTI_BYTERANGES, &kNS_MULTIMIXEDCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY MULTI_MIXED, &kNS_MULTIMIXEDCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY APPLICATION_PACKAGE_CONV, &kNS_MULTIMIXEDCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY UNKNOWN_CONTENT, &kNS_UNKNOWNDECODER_CID },
     { NS_GENERIC_CONTENT_SNIFFER, &kNS_UNKNOWNDECODER_CID },
     { NS_BINARYDETECTOR_CONTRACTID, &kNS_BINARYDETECTOR_CID },
     { NS_ISTREAMCONVERTER_KEY GZIP_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY XGZIP_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
+    { NS_ISTREAMCONVERTER_KEY BROTLI_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY COMPRESS_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY XCOMPRESS_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY DEFLATE_TO_UNCOMPRESSED, &kNS_HTTPCOMPRESSCONVERTER_CID },
     { NS_ISTREAMCONVERTER_KEY PLAIN_TO_HTML, &kNS_NSTXTTOHTMLCONVERTER_CID },
 #ifdef BUILD_BINHEX_DECODER
     { NS_ISTREAMCONVERTER_KEY BINHEX_TO_WILD, &kNS_BINHEXDECODER_CID },
 #endif
     { MOZ_TXTTOHTMLCONV_CONTRACTID, &kMOZITXTTOHTMLCONV_CID },
--- a/netwerk/mime/nsMimeTypes.h
+++ b/netwerk/mime/nsMimeTypes.h
@@ -25,16 +25,17 @@
 #define APPLICATION_MACBINARY               "application/x-macbinary"
 #define APPLICATION_COMPRESS                "application/x-compress"
 #define APPLICATION_COMPRESS2               "application/compress"
 #define APPLICATION_FORTEZZA_CKL            "application/x-fortezza-ckl"
 #define APPLICATION_FORTEZZA_KRL            "application/x-fortezza-krl"
 #define APPLICATION_GZIP                    "application/x-gzip"
 #define APPLICATION_GZIP2                   "application/gzip"
 #define APPLICATION_GZIP3                   "application/x-gunzip"
+#define APPLICATION_BROTLI                  "application/brotli"
 #define APPLICATION_ZIP                     "application/zip"
 #define APPLICATION_HTTP_INDEX_FORMAT       "application/http-index-format"
 #define APPLICATION_ECMASCRIPT              "application/ecmascript"
 #define APPLICATION_JAVASCRIPT              "application/javascript"
 #define APPLICATION_XJAVASCRIPT             "application/x-javascript"
 #define APPLICATION_JSON                    "application/json"
 #define APPLICATION_NETSCAPE_REVOCATION     "application/x-netscape-revocation"
 #define APPLICATION_NS_PROXY_AUTOCONFIG     "application/x-ns-proxy-autoconfig"
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -831,16 +831,27 @@ HttpBaseChannel::DoApplyContentConversio
                                   aCtxt,
                                   getter_AddRefs(converter));
       if (NS_FAILED(rv)) {
         LOG(("Unexpected failure of AsyncConvertData %s\n", val));
         return rv;
       }
 
       LOG(("converter removed '%s' content-encoding\n", val));
+      if (gHttpHandler->IsTelemetryEnabled()) {
+        int mode = 0;
+        if (from.Equals("gzip") || from.Equals("x-gzip")) {
+          mode = 1;
+        } else if (from.Equals("deflate") || from.Equals("x-deflate")) {
+          mode = 2;
+        } else if (from.Equals("brotli")) {
+          mode = 3;
+        }
+        Telemetry::Accumulate(Telemetry::HTTP_CONTENT_ENCODING, mode);
+      }
       nextListener = converter;
     }
     else {
       if (val)
         LOG(("Unknown content encoding '%s', ignoring\n", val));
     }
   }
   *aNewNextListener = nextListener;
@@ -935,16 +946,24 @@ HttpBaseChannel::nsContentEncodings::Get
   if (!haveType) {
     encoding.BeginReading(start);
     if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("deflate"), start, end)) {
       aNextEncoding.AssignLiteral(APPLICATION_ZIP);
       haveType = true;
     }
   }
 
+  if (!haveType) {
+    encoding.BeginReading(start);
+    if (CaseInsensitiveFindInReadable(NS_LITERAL_CSTRING("brotli"), start, end)) {
+      aNextEncoding.AssignLiteral(APPLICATION_BROTLI);
+      haveType = true;
+    }
+  }
+
   // Prepare to fetch the next encoding
   mCurEnd = mCurStart;
   mReady = false;
 
   if (haveType)
     return NS_OK;
 
   NS_WARNING("Unknown encoding type");
@@ -2244,18 +2263,20 @@ HttpBaseChannel::IsNavigation()
 
 bool
 HttpBaseChannel::ShouldIntercept()
 {
   nsCOMPtr<nsINetworkInterceptController> controller;
   GetCallback(controller);
   bool shouldIntercept = false;
   if (controller && !mForceNoIntercept) {
+    nsContentPolicyType type = mLoadInfo->InternalContentPolicyType();
     nsresult rv = controller->ShouldPrepareForIntercept(mURI,
                                                         IsNavigation(),
+                                                        type,
                                                         &shouldIntercept);
     if (NS_FAILED(rv)) {
       return false;
     }
   }
   return shouldIntercept;
 }
 
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -151,17 +151,19 @@ NS_IMPL_ISUPPORTS(HttpChannelParent,
                   nsIRequestObserver,
                   nsIStreamListener,
                   nsIParentChannel,
                   nsIAuthPromptProvider,
                   nsIParentRedirectingChannel,
                   nsINetworkInterceptController)
 
 NS_IMETHODIMP
-HttpChannelParent::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate, bool* aShouldIntercept)
+HttpChannelParent::ShouldPrepareForIntercept(nsIURI* aURI, bool aIsNavigate,
+                                             nsContentPolicyType aType,
+                                             bool* aShouldIntercept)
 {
   *aShouldIntercept = mShouldIntercept;
   return NS_OK;
 }
 
 class HeaderVisitor final : public nsIHttpHeaderVisitor
 {
   nsCOMPtr<nsIInterceptedChannel> mChannel;
--- a/netwerk/protocol/http/InterceptedChannel.cpp
+++ b/netwerk/protocol/http/InterceptedChannel.cpp
@@ -259,16 +259,28 @@ InterceptedChannelChrome::SetChannelInfo
 {
   if (!mChannel) {
     return NS_ERROR_FAILURE;
   }
 
   return aChannelInfo->ResurrectInfoOnChannel(mChannel);
 }
 
+NS_IMETHODIMP
+InterceptedChannelChrome::GetInternalContentPolicyType(nsContentPolicyType* aPolicyType)
+{
+  NS_ENSURE_ARG(aPolicyType);
+  nsCOMPtr<nsILoadInfo> loadInfo;
+  nsresult rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aPolicyType = loadInfo->InternalContentPolicyType();
+  return NS_OK;
+}
+
 InterceptedChannelContent::InterceptedChannelContent(HttpChannelChild* aChannel,
                                                      nsINetworkInterceptController* aController,
                                                      nsIStreamListener* aListener)
 : InterceptedChannelBase(aController, aChannel->IsNavigation())
 , mChannel(aChannel)
 , mStreamListener(aListener)
 {
 }
@@ -368,10 +380,23 @@ InterceptedChannelContent::SetChannelInf
 {
   if (!mChannel) {
     return NS_ERROR_FAILURE;
   }
 
   return aChannelInfo->ResurrectInfoOnChannel(mChannel);
 }
 
+NS_IMETHODIMP
+InterceptedChannelContent::GetInternalContentPolicyType(nsContentPolicyType* aPolicyType)
+{
+  NS_ENSURE_ARG(aPolicyType);
+
+  nsCOMPtr<nsILoadInfo> loadInfo;
+  nsresult rv = mChannel->GetLoadInfo(getter_AddRefs(loadInfo));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  *aPolicyType = loadInfo->InternalContentPolicyType();
+  return NS_OK;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/InterceptedChannel.h
+++ b/netwerk/protocol/http/InterceptedChannel.h
@@ -78,16 +78,17 @@ public:
 
   NS_IMETHOD ResetInterception() override;
   NS_IMETHOD FinishSynthesizedResponse() override;
   NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
   NS_IMETHOD SynthesizeStatus(uint16_t aStatus, const nsACString& aReason) override;
   NS_IMETHOD SynthesizeHeader(const nsACString& aName, const nsACString& aValue) override;
   NS_IMETHOD Cancel(nsresult aStatus) override;
   NS_IMETHOD SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo) override;
+  NS_IMETHOD GetInternalContentPolicyType(nsContentPolicyType *aInternalContentPolicyType) override;
 
   virtual void NotifyController() override;
 };
 
 class InterceptedChannelContent : public InterceptedChannelBase
 {
   // The actual channel being intercepted.
   nsRefPtr<HttpChannelChild> mChannel;
@@ -105,16 +106,17 @@ public:
 
   NS_IMETHOD ResetInterception() override;
   NS_IMETHOD FinishSynthesizedResponse() override;
   NS_IMETHOD GetChannel(nsIChannel** aChannel) override;
   NS_IMETHOD SynthesizeStatus(uint16_t aStatus, const nsACString& aReason) override;
   NS_IMETHOD SynthesizeHeader(const nsACString& aName, const nsACString& aValue) override;
   NS_IMETHOD Cancel(nsresult aStatus) override;
   NS_IMETHOD SetChannelInfo(mozilla::dom::ChannelInfo* aChannelInfo) override;
+  NS_IMETHOD GetInternalContentPolicyType(nsContentPolicyType *aInternalContentPolicyType) override;
 
   virtual void NotifyController() override;
 };
 
 } // namespace net
 } // namespace mozilla
 
 #endif // InterceptedChannel_h
--- a/netwerk/protocol/http/nsHttpHandler.cpp
+++ b/netwerk/protocol/http/nsHttpHandler.cpp
@@ -488,32 +488,34 @@ nsHttpHandler::AddConnectionHeader(nsHtt
 }
 
 bool
 nsHttpHandler::IsAcceptableEncoding(const char *enc, bool isSecure)
 {
     if (!enc)
         return false;
 
-    // HTTP 1.1 allows servers to send x-gzip and x-compress instead
-    // of gzip and compress, for example.  So, we'll always strip off
-    // an "x-" prefix before matching the encoding to one we claim
-    // to accept.
-    if (!PL_strncasecmp(enc, "x-", 2))
-        enc += 2;
-
+    // we used to accept x-foo anytime foo was acceptable, but that's just
+    // continuing bad behavior.. so limit it to known x-* patterns
+    bool rv;
+    if (isSecure) {
+        rv = nsHttp::FindToken(mHttpsAcceptEncodings.get(), enc, HTTP_LWS ",") != nullptr;
+    } else {
+        rv = nsHttp::FindToken(mHttpAcceptEncodings.get(), enc, HTTP_LWS ",") != nullptr;
+    }
     // gzip and deflate are inherently acceptable in modern HTTP - always
     // process them if a stream converter can also be found.
-    if (!PL_strcasecmp(enc, "gzip") || !PL_strcasecmp(enc, "deflate"))
-        return true;
-
-    if (isSecure) {
-        return nsHttp::FindToken(mHttpsAcceptEncodings.get(), enc, HTTP_LWS ",") != nullptr;
+    if (!rv &&
+        (!PL_strcasecmp(enc, "gzip") || !PL_strcasecmp(enc, "deflate") ||
+         !PL_strcasecmp(enc, "x-gzip") || !PL_strcasecmp(enc, "x-deflate"))) {
+        rv = true;
     }
-    return nsHttp::FindToken(mHttpAcceptEncodings.get(), enc, HTTP_LWS ",") != nullptr;
+    LOG(("nsHttpHandler::IsAceptableEncoding %s https=%d %d\n",
+         enc, isSecure, rv));
+    return rv;
 }
 
 nsresult
 nsHttpHandler::GetStreamConverterService(nsIStreamConverterService **result)
 {
     if (!mStreamConvSvc) {
         nsresult rv;
         nsCOMPtr<nsIStreamConverterService> service =
--- a/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
+++ b/netwerk/streamconv/converters/nsHTTPCompressConv.cpp
@@ -9,16 +9,24 @@
 #include "plstr.h"
 #include "nsCOMPtr.h"
 #include "nsError.h"
 #include "nsStreamUtils.h"
 #include "nsStringStream.h"
 #include "nsComponentManagerUtils.h"
 #include "nsThreadUtils.h"
 #include "mozilla/Preferences.h"
+#include "nsIForcePendingChannel.h"
+
+// brotli headers
+#include "state.h"
+#include "decode.h"
+
+extern PRLogModuleInfo *gHttpLog;
+#define LOG(args) MOZ_LOG(gHttpLog, mozilla::LogLevel::Debug, args)
 
 namespace mozilla {
 namespace net {
 
 // nsISupports implementation
 NS_IMPL_ISUPPORTS(nsHTTPCompressConv,
                   nsIStreamConverter,
                   nsIStreamListener,
@@ -34,26 +42,28 @@ nsHTTPCompressConv::nsHTTPCompressConv()
   , mCheckHeaderDone(false)
   , mStreamEnded(false)
   , mStreamInitialized(false)
   , mLen(0)
   , hMode(0)
   , mSkipCount(0)
   , mFlags(0)
 {
+  LOG(("nsHttpCompresssConv %p ctor\n", this));
   if (NS_IsMainThread()) {
     mFailUncleanStops =
       Preferences::GetBool("network.http.enforce-framing.http", false);
   } else {
     mFailUncleanStops = false;
   }
 }
 
 nsHTTPCompressConv::~nsHTTPCompressConv()
 {
+  LOG(("nsHttpCompresssConv %p dtor\n", this));
   if (mInpBuffer) {
     free(mInpBuffer);
   }
 
   if (mOutBuffer) {
     free(mOutBuffer);
   }
 
@@ -73,55 +83,153 @@ nsHTTPCompressConv::AsyncConvertData(con
   if (!PL_strncasecmp(aFromType, HTTP_COMPRESS_TYPE, sizeof(HTTP_COMPRESS_TYPE)-1) ||
       !PL_strncasecmp(aFromType, HTTP_X_COMPRESS_TYPE, sizeof(HTTP_X_COMPRESS_TYPE)-1)) {
     mMode = HTTP_COMPRESS_COMPRESS;
   } else if (!PL_strncasecmp(aFromType, HTTP_GZIP_TYPE, sizeof(HTTP_GZIP_TYPE)-1) ||
              !PL_strncasecmp(aFromType, HTTP_X_GZIP_TYPE, sizeof(HTTP_X_GZIP_TYPE)-1)) {
     mMode = HTTP_COMPRESS_GZIP;
   } else if (!PL_strncasecmp(aFromType, HTTP_DEFLATE_TYPE, sizeof(HTTP_DEFLATE_TYPE)-1)) {
     mMode = HTTP_COMPRESS_DEFLATE;
+  } else if (!PL_strncasecmp(aFromType, HTTP_BROTLI_TYPE, sizeof(HTTP_BROTLI_TYPE)-1)) {
+    mMode = HTTP_COMPRESS_BROTLI;
   }
+  LOG(("nsHttpCompresssConv %p AsyncConvertData %s %s mode %d\n",
+       this, aFromType, aToType, mMode));
 
   // hook ourself up with the receiving listener.
   mListener = aListener;
 
   mAsyncConvContext = aCtxt;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHTTPCompressConv::OnStartRequest(nsIRequest* request, nsISupports *aContext)
 {
+  LOG(("nsHttpCompresssConv %p onstart\n", this));
   return mListener->OnStartRequest(request, aContext);
 }
 
 NS_IMETHODIMP
 nsHTTPCompressConv::OnStopRequest(nsIRequest* request, nsISupports *aContext,
                                   nsresult aStatus)
 {
+  nsresult status = aStatus;
+  LOG(("nsHttpCompresssConv %p onstop %x\n", this, aStatus));
+  
   // Framing integrity is enforced for content-encoding: gzip, but not for
   // content-encoding: deflate. Note that gzip vs deflate is NOT determined
   // by content sniffing but only via header.
-  if (!mStreamEnded && NS_SUCCEEDED(aStatus) &&
+  if (!mStreamEnded && NS_SUCCEEDED(status) &&
       (mFailUncleanStops && (mMode == HTTP_COMPRESS_GZIP)) ) {
     // This is not a clean end of gzip stream: the transfer is incomplete.
-    aStatus = NS_ERROR_NET_PARTIAL_TRANSFER;
+    status = NS_ERROR_NET_PARTIAL_TRANSFER;
+    LOG(("nsHttpCompresssConv %p onstop partial gzip\n", this));
+  }
+  if (NS_SUCCEEDED(status) && mMode == HTTP_COMPRESS_BROTLI) {
+    uint32_t waste;
+    nsCOMPtr<nsIForcePendingChannel> fpChannel = do_QueryInterface(request);
+    bool isPending = false;
+    if (request) {
+      request->IsPending(&isPending);
+    }
+    if (fpChannel && !isPending) {
+      fpChannel->ForcePending(true);
+    }
+    status = BrotliHandler(nullptr, this, nullptr, 0, 0, &waste);
+    LOG(("nsHttpCompresssConv %p onstop brotlihandler rv %x\n", this, status));
+    if (fpChannel && !isPending) {
+      fpChannel->ForcePending(false);
+    }
+  }
+  if (NS_FAILED(status) && status != aStatus) {
+    LOG(("nsHttpCompresssConv %p onstop calling cancel %x\n", this, status));
+    request->Cancel(status);
   }
-  return mListener->OnStopRequest(request, aContext, aStatus);
+  return mListener->OnStopRequest(request, aContext, status);
+}
+
+
+// static
+NS_METHOD
+nsHTTPCompressConv::BrotliHandler(nsIInputStream *stream, void *closure, const char *dataIn,
+                                  uint32_t, uint32_t aAvail, uint32_t *countRead)
+{
+  nsHTTPCompressConv *self = static_cast<nsHTTPCompressConv *>(closure);
+  *countRead = 0;
+
+  const uint32_t kOutSize = 128 * 1024; // just a chunk size, we call in a loop
+  unsigned char outBuffer[kOutSize];
+  unsigned char *outPtr;
+  size_t outSize;
+  size_t avail = aAvail;
+  BrotliResult res;
+
+  do {
+    outSize = kOutSize;
+    outPtr = outBuffer;
+
+    // brotli api is documented in brotli/dec/decode.h
+    LOG(("nsHttpCompresssConv %p brotlihandler decompress %d finish %d\n",
+         self, avail, !stream));
+    res = ::BrotliDecompressBufferStreaming(
+      &avail, reinterpret_cast<const unsigned char **>(&dataIn), stream ? 0 : 1,
+      &outSize, &outPtr, &self->mBrotli->mTotalOut, &self->mBrotli->mState);
+    outSize = kOutSize - outSize;
+    LOG(("nsHttpCompresssConv %p brotlihandler decompress rv=%x out=%d\n",
+         self, res, outSize));
+
+    if (res == BROTLI_RESULT_ERROR) {
+      LOG(("nsHttpCompressConv %p marking invalid encoding", self));
+      self->mBrotli->mStatus = NS_ERROR_INVALID_CONTENT_ENCODING;
+      return self->mBrotli->mStatus;
+    }
+
+    // in 'the current implementation' brotli consumes all input on success
+    MOZ_ASSERT(!avail);
+    if (avail) {
+      LOG(("nsHttpCompressConv %p did not consume all input", self));
+      self->mBrotli->mStatus = NS_ERROR_UNEXPECTED;
+      return self->mBrotli->mStatus;
+    }
+    if (outSize > 0) {
+      nsresult rv = self->do_OnDataAvailable(self->mBrotli->mRequest,
+                                             self->mBrotli->mContext,
+                                             self->mBrotli->mSourceOffset,
+                                             reinterpret_cast<const char *>(outBuffer),
+                                             outSize);
+      LOG(("nsHttpCompressConv %p BrotliHandler ODA rv=%x", self, rv));
+      if (NS_FAILED(rv)) {
+        self->mBrotli->mStatus = rv;
+        return self->mBrotli->mStatus;
+      }
+    }
+
+    if (res == BROTLI_RESULT_SUCCESS ||
+        res == BROTLI_RESULT_NEEDS_MORE_INPUT) {
+      *countRead = aAvail;
+      return NS_OK;
+    }
+    MOZ_ASSERT (res == BROTLI_RESULT_NEEDS_MORE_OUTPUT);
+  } while (res == BROTLI_RESULT_NEEDS_MORE_OUTPUT);
+
+  self->mBrotli->mStatus = NS_ERROR_UNEXPECTED;
+  return self->mBrotli->mStatus;
 }
 
 NS_IMETHODIMP
 nsHTTPCompressConv::OnDataAvailable(nsIRequest* request,
                                     nsISupports *aContext,
                                     nsIInputStream *iStr,
                                     uint64_t aSourceOffset,
                                     uint32_t aCount)
 {
   nsresult rv = NS_ERROR_INVALID_CONTENT_ENCODING;
   uint32_t streamLen = aCount;
+  LOG(("nsHttpCompressConv %p OnDataAvailable %d", this, aCount));
 
   if (streamLen == 0) {
     NS_ERROR("count of zero passed to OnDataAvailable");
     return NS_ERROR_UNEXPECTED;
   }
 
   if (mStreamEnded) {
     // Hmm... this may just indicate that the data stream is done and that
@@ -301,27 +409,47 @@ nsHTTPCompressConv::OnDataAvailable(nsIR
           break;
         } else {
           return NS_ERROR_INVALID_CONTENT_ENCODING;
         }
       } /* for */
     } /* gzip */
     break;
 
+  case HTTP_COMPRESS_BROTLI:
+  {
+    if (!mBrotli) {
+      mBrotli = new BrotliWrapper();
+    }
+
+    mBrotli->mRequest = request;
+    mBrotli->mContext = aContext;
+    mBrotli->mSourceOffset = aSourceOffset;
+
+    uint32_t countRead;
+    rv = iStr->ReadSegments(BrotliHandler, this, streamLen, &countRead);
+    if (NS_SUCCEEDED(rv)) {
+      rv = mBrotli->mStatus;
+    }
+    if (NS_FAILED(rv)) {
+      return rv;
+    }
+  }
+    break;
+
   default:
     rv = mListener->OnDataAvailable(request, aContext, iStr, aSourceOffset, aCount);
     if (NS_FAILED (rv)) {
       return rv;
     }
   } /* switch */
 
   return NS_OK;
 } /* OnDataAvailable */
 
-
 // XXX/ruslan: need to implement this too
 
 NS_IMETHODIMP
 nsHTTPCompressConv::Convert(nsIInputStream *aFromStream,
                             const char *aFromType,
                             const char *aToType,
                             nsISupports *aCtxt,
                             nsIInputStream **_retval)
--- a/netwerk/streamconv/converters/nsHTTPCompressConv.h
+++ b/netwerk/streamconv/converters/nsHTTPCompressConv.h
@@ -7,16 +7,21 @@
 #if !defined (__nsHTTPCompressConv__h__)
 #define	__nsHTTPCompressConv__h__	1
 
 #include "nsIStreamConverter.h"
 #include "nsCOMPtr.h"
 
 #include "zlib.h"
 
+// brotli includes
+#undef assert
+#include "assert.h"
+#include "state.h"
+
 class nsIStringInputStream;
 
 #define NS_HTTPCOMPRESSCONVERTER_CID                    \
   {                                                     \
     /* 66230b2b-17fa-4bd3-abf4-07986151022d */          \
     0x66230b2b,                                         \
       0x17fa,                                           \
       0x4bd3,                                           \
@@ -24,29 +29,54 @@ class nsIStringInputStream;
   }
 
 
 #define	HTTP_DEFLATE_TYPE		"deflate"
 #define	HTTP_GZIP_TYPE	        "gzip"
 #define	HTTP_X_GZIP_TYPE	    "x-gzip"
 #define	HTTP_COMPRESS_TYPE	    "compress"
 #define	HTTP_X_COMPRESS_TYPE	"x-compress"
+#define	HTTP_BROTLI_TYPE        "brotli"
 #define	HTTP_IDENTITY_TYPE	    "identity"
 #define	HTTP_UNCOMPRESSED_TYPE	"uncompressed"
 
 namespace mozilla {
 namespace net {
 
 typedef enum    {
   HTTP_COMPRESS_GZIP,
   HTTP_COMPRESS_DEFLATE,
   HTTP_COMPRESS_COMPRESS,
+  HTTP_COMPRESS_BROTLI,
   HTTP_COMPRESS_IDENTITY
 } CompressMode;
 
+class BrotliWrapper
+{
+public:
+  BrotliWrapper()
+    : mTotalOut(0)
+    , mStatus(NS_OK)
+  {
+    BrotliStateInit(&mState);
+  }
+  ~BrotliWrapper()
+  {
+    BrotliStateCleanup(&mState);
+  }
+
+  BrotliState mState;
+  size_t       mTotalOut;
+  nsresult     mStatus;
+
+  nsIRequest  *mRequest;
+  nsISupports *mContext;
+  uint64_t     mSourceOffset;
+};
+
 class nsHTTPCompressConv : public nsIStreamConverter	{
   public:
   // nsISupports methods
     NS_DECL_THREADSAFE_ISUPPORTS
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
 
   // nsIStreamConverter methods
@@ -61,19 +91,25 @@ private:
     CompressMode        mMode;
 
     unsigned char *mOutBuffer;
     unsigned char *mInpBuffer;
 
     uint32_t	mOutBufferLen;
     uint32_t	mInpBufferLen;
 
+    nsAutoPtr<BrotliWrapper> mBrotli;
+
     nsCOMPtr<nsISupports>   mAsyncConvContext;
     nsCOMPtr<nsIStringInputStream>  mStream;
 
+    static NS_METHOD
+    BrotliHandler(nsIInputStream *stream, void *closure, const char *dataIn,
+                  uint32_t, uint32_t avail, uint32_t *countRead);
+
     nsresult do_OnDataAvailable (nsIRequest *request, nsISupports *aContext,
                                  uint64_t aSourceOffset, const char *buffer,
                                  uint32_t aCount);
 
     bool        mCheckHeaderDone;
     bool        mStreamEnded;
     bool        mStreamInitialized;
     bool        mDummyStreamInitialised;
--- a/netwerk/test/unit/test_content_encoding_gzip.js
+++ b/netwerk/test/unit/test_content_encoding_gzip.js
@@ -19,16 +19,41 @@ var tests = [
      ce: "gzip, gzip",
      body: [
 	 0x1f, 0x8b, 0x08, 0x00, 0x72, 0xa1, 0x31, 0x4f, 0x00, 0x03, 0x93, 0xef, 0xe6, 0xe0, 0x88, 0x5a, 
 	 0x60, 0xe8, 0xcf, 0xc0, 0x5c, 0x52, 0x51, 0xc2, 0xa0, 0x7d, 0xf2, 0x84, 0x4e, 0x18, 0xc3, 0xa2, 
 	 0x49, 0x57, 0x1e, 0x09, 0x39, 0xeb, 0x31, 0xec, 0x54, 0xbe, 0x6e, 0xcd, 0xc7, 0xc0, 0xc0, 0x00, 
 	 0x00, 0x6e, 0x90, 0x7a, 0x85, 0x24, 0x00, 0x00, 0x00],
      datalen: 14 // the data length of the uncompressed document
     },
+
+    {url: "/test/cebrotli1",
+     flags: CL_EXPECT_GZIP,
+     ce: "brotli",
+     body: [0x0B, 0x02, 0x80, 0x74, 0x65, 0x73, 0x74, 0x0A, 0x03],
+
+     datalen: 5 // the data length of the uncompressed document
+    },
+
+    // this is not a brotli document
+    {url: "/test/cebrotli2",
+     flags: CL_EXPECT_GZIP | CL_EXPECT_FAILURE,
+     ce: "brotli",
+     body: [0x0B, 0x0A, 0x09],
+     datalen: 3
+    },
+
+    // this is brotli but should come through as identity due to prefs
+    {url: "/test/cebrotli3",
+     flags: 0,
+     ce: "brotli",
+     body: [0x0B, 0x02, 0x80, 0x74, 0x65, 0x73, 0x74, 0x0A, 0x03],
+
+     datalen: 9
+    },
 ];
 
 function setupChannel(url) {
     var ios = Components.classes["@mozilla.org/network/io-service;1"].
                          getService(Ci.nsIIOService);
     var chan = ios.newChannel2("http://localhost:" +
                                httpserver.identity.primaryPort + url,
                                "",
@@ -37,32 +62,48 @@ function setupChannel(url) {
                                Services.scriptSecurityManager.getSystemPrincipal(),
                                null,      // aTriggeringPrincipal
                                Ci.nsILoadInfo.SEC_NORMAL,
                                Ci.nsIContentPolicy.TYPE_OTHER);
     return chan;
 }
 
 function startIter() {
+    if (tests[index].url === "/test/cebrotli3") {
+      // this test wants to make sure we don't do brotli when not in a-e
+      prefs.setCharPref("network.http.accept-encoding", "gzip, deflate");
+    }
     var channel = setupChannel(tests[index].url);
     channel.asyncOpen(new ChannelListener(completeIter, channel, tests[index].flags), null);
 }
 
 function completeIter(request, data, ctx) {
-    do_check_true(data.length == tests[index].datalen);
+    if (!(tests[index].flags & CL_EXPECT_FAILURE)) {
+	do_check_eq(data.length, tests[index].datalen);
+    }
     if (++index < tests.length) {
 	startIter();
     } else {
         httpserver.stop(do_test_finished);
+	prefs.setCharPref("network.http.accept-encoding", cePref);
     }
 }
 
+var prefs;
+var cePref;
 function run_test() {
+    prefs = Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefBranch);
+    cePref = prefs.getCharPref("network.http.accept-encoding");
+    prefs.setCharPref("network.http.accept-encoding", "gzip, deflate, brotli");
+
     httpserver.registerPathHandler("/test/cegzip1", handler);
     httpserver.registerPathHandler("/test/cegzip2", handler);
+    httpserver.registerPathHandler("/test/cebrotli1", handler);
+    httpserver.registerPathHandler("/test/cebrotli2", handler);
+    httpserver.registerPathHandler("/test/cebrotli3", handler);
     httpserver.start(-1);
 
     startIter();
     do_test_pending();
 }
 
 function handler(metadata, response) {
     response.setStatusLine(metadata.httpVersion, 200, "OK");
--- a/storage/mozStoragePrivateHelpers.cpp
+++ b/storage/mozStoragePrivateHelpers.cpp
@@ -131,20 +131,24 @@ convertJSValToVariant(
     return new IntegerVariant(aValue.isTrue() ? 1 : 0);
 
   if (aValue.isNull())
     return new NullVariant();
 
   if (aValue.isObject()) {
     JS::Rooted<JSObject*> obj(aCtx, &aValue.toObject());
     // We only support Date instances, all others fail.
-    if (!js::DateIsValid(aCtx, obj))
+    bool valid;
+    if (!js::DateIsValid(aCtx, obj, &valid) || !valid)
       return nullptr;
 
-    double msecd = js::DateGetMsecSinceEpoch(aCtx, obj);
+    double msecd;
+    if (!js::DateGetMsecSinceEpoch(aCtx, obj, &msecd))
+      return nullptr;
+
     msecd *= 1000.0;
     int64_t msec = msecd;
 
     return new IntegerVariant(msec);
   }
 
   return nullptr;
 }
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -1517,21 +1517,16 @@ class Mochitest(MochitestUtilsMixin):
         if debugger and not options.slowscript:
             browserEnv["JS_DISABLE_SLOW_SCRIPT_SIGNALS"] = "1"
 
         # For e10s, our tests default to suppressing the "unsafe CPOW usage"
         # warnings that can plague test logs.
         if not options.enableCPOWWarnings:
             browserEnv["DISABLE_UNSAFE_CPOW_WARNINGS"] = "1"
 
-        # Force use of core Xlib events on GTK3 to work around focus bug.
-        # See bug 1170342.
-        if mozinfo.info.get('toolkit') == 'gtk3':
-            browserEnv["GDK_CORE_DEVICE_EVENTS"] = "1"
-
         return browserEnv
 
     def cleanup(self, options):
         """ remove temporary files and profile """
         if hasattr(self, 'manifest') and self.manifest is not None:
             os.remove(self.manifest)
         if hasattr(self, 'profile'):
             del self.profile
new file mode 100755
--- /dev/null
+++ b/testing/taskcluster/scripts/misc/repackage-jdk-centos.sh
@@ -0,0 +1,33 @@
+#! /bin/bash
+
+set -e -x
+
+mkdir artifacts
+cd build
+
+rm -rf root && mkdir root && cd root
+
+# change these variables when updating java version
+mirror_url_base="http://mirror.centos.org/centos/6.7/updates/x86_64/Packages"
+openjdk=java-1.7.0-openjdk-1.7.0.85-2.6.1.3.el6_7.x86_64.rpm
+openjdk_devel=java-1.7.0-openjdk-1.7.0.85-2.6.1.3.el6_7.x86_64.rpm
+jvm_openjdk_dir=java-1.7.0-openjdk-1.7.0.85.x86_64
+
+# grab the rpm and unpack it
+wget ${mirror_url_base}/${openjdk}
+wget ${mirror_url_base}/${openjdk_devel}
+rpm2cpio $openjdk | cpio -ivd
+rpm2cpio $openjdk_devel | cpio -ivd
+
+cd usr/lib/jvm
+mv $jvm_openjdk_dir java_home
+
+# document version this is based on
+echo "Built from ${mirror_url_Base}
+    ${openjdk}
+    ${openjdk_devel}
+
+Run through rpm2cpio | cpio, and /usr/lib/jvm/${jvm_openjdk_dir} renamed to 'java_home'." > java_home/VERSION
+
+# tarball the unpacked rpm and put it in the taskcluster upload artifacts dir
+tar -Jvcf ~/artifacts/java_home-${jvm_openjdk_dir}.tar.xz java_home
deleted file mode 100755
--- a/testing/taskcluster/scripts/misc/repackage-jdk.sh
+++ /dev/null
@@ -1,30 +0,0 @@
-#! /bin/bash
-
-set -e -x
-