Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 18 Aug 2015 10:33:13 -0400
changeset 258184 1fe85780ea7cb156785f85241bf333940642428a
parent 258183 746ac829b15af5cd065d6866de6a7e074ad34a47 (current diff)
parent 258146 acca05b8182e86e12b33c3359cae87d63c7d0c4b (diff)
child 258185 bf540756452992dad2430e684194b7f922f19204
push id63843
push userryanvm@gmail.com
push dateTue, 18 Aug 2015 14:58:06 +0000
treeherdermozilla-inbound@d55e24c983aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.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 inbound to m-c. a=merge
dom/media/MozPromise.h
memory/jemalloc/0001-Dont-overwrite-VERSION-on-a-git-repository.patch
memory/jemalloc/0002-Move-variable-declaration-to-the-top-its-block-for-M.patch
memory/jemalloc/0003-Add-a-isblank-definition-for-MSVC-2013.patch
memory/jemalloc/0004-Implement-stats.bookkeeping.patch
memory/jemalloc/0005-Bug-1121314-Avoid-needing-the-arena-in-chunk_alloc_d.patch
memory/jemalloc/0006-Make-opt.lg_dirty_mult-work-as-documented.patch
memory/jemalloc/0007-Preserve-LastError-when-calling-TlsGetValue.patch
memory/jemalloc/0008-Make-without-export-actually-work.patch
memory/jemalloc/src/bin/pprof
memory/jemalloc/src/include/msvc_compat/C99/inttypes.h
toolkit/components/telemetry/Histograms.json
--- a/accessible/ipc/DocAccessibleParent.cpp
+++ b/accessible/ipc/DocAccessibleParent.cpp
@@ -14,17 +14,17 @@ namespace mozilla {
 namespace a11y {
 
 bool
 DocAccessibleParent::RecvShowEvent(const ShowEventData& aData)
 {
   if (mShutdown)
     return true;
 
-  CheckDocTree();
+  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
 
   if (aData.NewTree().IsEmpty()) {
     NS_ERROR("no children being added");
     return false;
   }
 
   ProxyAccessible* parent = GetAccessible(aData.ID());
 
@@ -45,17 +45,17 @@ DocAccessibleParent::RecvShowEvent(const
   MOZ_ASSERT(consumed == aData.NewTree().Length());
 #ifdef DEBUG
   for (uint32_t i = 0; i < consumed; i++) {
     uint64_t id = aData.NewTree()[i].ID();
     MOZ_ASSERT(mAccessibles.GetEntry(id));
   }
 #endif
 
-  CheckDocTree();
+  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
 
   return true;
 }
 
 uint32_t
 DocAccessibleParent::AddSubtree(ProxyAccessible* aParent,
                                 const nsTArray<a11y::AccessibleData>& aNewTree,
                                 uint32_t aIdx, uint32_t aIdxInParent)
@@ -99,17 +99,17 @@ DocAccessibleParent::AddSubtree(ProxyAcc
 }
 
 bool
 DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID)
 {
   if (mShutdown)
     return true;
 
-  CheckDocTree();
+  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
 
   ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID);
   if (!rootEntry) {
     NS_ERROR("invalid root being removed!");
     return true;
   }
 
   ProxyAccessible* root = rootEntry->mProxy;
@@ -117,17 +117,17 @@ DocAccessibleParent::RecvHideEvent(const
     NS_ERROR("invalid root being removed!");
     return true;
   }
 
   ProxyAccessible* parent = root->Parent();
   parent->RemoveChild(root);
   root->Shutdown();
 
-  CheckDocTree();
+  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
 
   return true;
 }
 
 bool
 DocAccessibleParent::RecvEvent(const uint64_t& aID, const uint32_t& aEventType)
 {
   ProxyAccessible* proxy = GetAccessible(aID);
@@ -191,22 +191,22 @@ bool
 DocAccessibleParent::RecvBindChildDoc(PDocAccessibleParent* aChildDoc, const uint64_t& aID)
 {
   // One document should never directly be the child of another.
   // We should always have at least an outer doc accessible in between.
   MOZ_ASSERT(aID);
   if (!aID)
     return false;
 
-  CheckDocTree();
+  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
 
   auto childDoc = static_cast<DocAccessibleParent*>(aChildDoc);
   bool result = AddChildDoc(childDoc, aID, false);
   MOZ_ASSERT(result);
-  CheckDocTree();
+  MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
   return result;
 }
 
 bool
 DocAccessibleParent::AddChildDoc(DocAccessibleParent* aChildDoc,
                                  uint64_t aParentID, bool aCreating)
 {
   // We do not use GetAccessible here because we want to be sure to not get the
@@ -260,22 +260,26 @@ DocAccessibleParent::Destroy()
   }
   ProxyDestroyed(this);
   if (mParentDoc)
     mParentDoc->RemoveChildDoc(this);
   else if (IsTopLevel())
     GetAccService()->RemoteDocShutdown(this);
 }
 
-void
+bool
 DocAccessibleParent::CheckDocTree() const
 {
   size_t childDocs = mChildDocs.Length();
   for (size_t i = 0; i < childDocs; i++) {
     if (!mChildDocs[i] || mChildDocs[i]->mParentDoc != this)
-      MOZ_CRASH("document tree is broken!");
+      return false;
 
-    mChildDocs[i]->CheckDocTree();
+    if (!mChildDocs[i]->CheckDocTree()) {
+      return false;
+    }
   }
+
+  return true;
 }
 
 } // a11y
 } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h
+++ b/accessible/ipc/DocAccessibleParent.h
@@ -67,17 +67,17 @@ public:
     ParentDoc()->mChildDocs.RemoveElement(this);
     mParentDoc = nullptr;
   }
 
   virtual bool RecvShutdown() override;
   void Destroy();
   virtual void ActorDestroy(ActorDestroyReason aWhy) override
   {
-    CheckDocTree();
+    MOZ_DIAGNOSTIC_ASSERT(CheckDocTree());
     if (!mShutdown)
       Destroy();
   }
 
   /*
    * Return the main processes representation of the parent document (if any)
    * of the document this object represents.
    */
@@ -146,17 +146,17 @@ private:
     enum { ALLOW_MEMMOVE = true };
 
     ProxyAccessible* mProxy;
   };
 
   uint32_t AddSubtree(ProxyAccessible* aParent,
                       const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx,
                       uint32_t aIdxInParent);
-  void CheckDocTree() const;
+  MOZ_WARN_UNUSED_RESULT bool CheckDocTree() const;
 
   nsTArray<DocAccessibleParent*> mChildDocs;
   DocAccessibleParent* mParentDoc;
 
   /*
    * Conceptually this is a map from IDs to proxies, but we store the ID in the
    * proxy object so we can't use a real map.
    */
--- a/browser/base/content/test/general/browser_audioTabIcon.js
+++ b/browser/base/content/test/general/browser_audioTabIcon.js
@@ -65,35 +65,47 @@ function* test_mute_tab(tab, icon, expec
   EventUtils.synthesizeMouseAtCenter(icon, {button: 0});
   leave_icon(icon);
 
   is(gBrowser.selectedTab, activeTab, "Clicking on mute should not change the currently selected tab");
 
   return mutedPromise;
 }
 
+function get_tab_attributes(tab) {
+  const ss = Cc["@mozilla.org/browser/sessionstore;1"].getService(Ci.nsISessionStore);
+  let {attributes} = JSON.parse(ss.getTabState(tab));
+  return attributes;
+}
+
 function* test_playing_icon_on_tab(tab, browser, isPinned) {
   let icon = document.getAnonymousElementByAttribute(tab, "anonid",
                                                      isPinned ? "overlay-icon" : "soundplaying-icon");
 
   yield ContentTask.spawn(browser, {}, function* () {
     let audio = content.document.querySelector("audio");
     audio.play();
   });
 
   yield wait_for_tab_playing_event(tab, true);
 
   yield test_tooltip(icon, "Mute tab");
 
+  ok(!("muted" in get_tab_attributes(tab)), "No muted attribute should be persisted");
+
   yield test_mute_tab(tab, icon, true);
 
+  ok("muted" in get_tab_attributes(tab), "Muted attribute should be persisted");
+
   yield test_tooltip(icon, "Unmute tab");
 
   yield test_mute_tab(tab, icon, false);
 
+  ok(!("muted" in get_tab_attributes(tab)), "No muted attribute should be persisted");
+
   yield test_tooltip(icon, "Mute tab");
 
   yield test_mute_tab(tab, icon, true);
 
   yield ContentTask.spawn(browser, {}, function* () {
     let audio = content.document.querySelector("audio");
     audio.pause();
   });
--- a/browser/components/sessionstore/TabAttributes.jsm
+++ b/browser/components/sessionstore/TabAttributes.jsm
@@ -19,17 +19,17 @@ this.TabAttributes = Object.freeze({
   },
 
   set: function (tab, data = {}) {
     TabAttributesInternal.set(tab, data);
   }
 });
 
 let TabAttributesInternal = {
-  _attrs: new Set(),
+  _attrs: new Set(["muted"]),
 
   // We never want to directly read or write those attributes.
   // 'image' should not be accessed directly but handled by using the
   //         gBrowser.getIcon()/setIcon() methods.
   // 'pending' is used internal by sessionstore and managed accordingly.
   _skipAttrs: new Set(["image", "pending"]),
 
   persist: function (name) {
--- a/config/system-headers
+++ b/config/system-headers
@@ -450,16 +450,17 @@ foundation/base64.h
 foundation/hexdump.h
 #endif
 fp.h
 fpieee.h
 frame/log.h
 frame/req.h
 freetype/freetype.h
 freetype/ftcache.h
+freetype/ftfntfmt.h
 freetype/ftglyph.h
 freetype/ftsynth.h
 freetype/ftoutln.h
 freetype/ttnameid.h
 freetype/tttables.h
 freetype/t1tables.h
 freetype/ftlcdfil.h
 freetype/ftsizes.h
--- a/configure.in
+++ b/configure.in
@@ -2312,16 +2312,17 @@ ia64*-hpux*)
         dnl with the linker doing most of the work in the whole-program
         dnl optimization/PGO case. I think it's probably a compiler bug,
         dnl but we work around it here.
         PROFILE_USE_CFLAGS="-GL -wd4624 -wd4952"
         dnl XXX: should be -LTCG:PGOPTIMIZE, but that fails on libxul.
         dnl Probably also a compiler bug, but what can you do?
         PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE"
         LDFLAGS="$LDFLAGS -DYNAMICBASE"
+        RCFLAGS="-nologo"
         if test "$_CC_MAJOR_VERSION" = "18" -a "$_CC_BUILD_VERSION" = "31101"; then
             dnl Use MaxILKSize as a workaround for LNK1248 in VS2013update4
             dnl See https://connect.microsoft.com/VisualStudio/feedback/details/1044914/fatal-error-lnk1248
             LDFLAGS="$LDFLAGS -MaxILKSize:0x7FF00000"
         fi
         dnl Minimum reqiurement of Gecko is VS2010 or later which supports
         dnl both SSSE3 and SSE4.1.
         HAVE_TOOLCHAIN_SUPPORT_MSSSE3=1
@@ -9128,25 +9129,35 @@ fi
 if test -z "$MOZ_NATIVE_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC3" -o -n "$MOZ_REPLACE_MALLOC"; then
   ac_configure_args="--build=$build --host=$target --enable-stats --with-jemalloc-prefix=je_ --disable-valgrind"
   # We're using memalign for _aligned_malloc in memory/build/mozmemory_wrap.c
   # on Windows, so just export memalign on all platforms.
   ac_configure_args="$ac_configure_args ac_cv_func_memalign=yes"
   if test -n "$MOZ_REPLACE_MALLOC"; then
     # When using replace_malloc, we always want valloc exported from jemalloc.
     ac_configure_args="$ac_configure_args ac_cv_func_valloc=yes"
+    if test "${OS_ARCH}" = Darwin; then
+      # We also need to enable pointer validation on Mac because jemalloc's
+      # zone allocator is not used.
+      ac_configure_args="$ac_configure_args --enable-ivsalloc"
+    fi
   fi
   if test -n "$MOZ_JEMALLOC3"; then
     case "${OS_ARCH}" in
       WINNT|Darwin)
         # We want jemalloc functions to be kept hidden on both Mac and Windows
         # See memory/build/mozmemory_wrap.h for details.
         ac_configure_args="$ac_configure_args --without-export"
         ;;
     esac
+    if test "${OS_ARCH}" = WINNT; then
+      # Lazy lock initialization doesn't play well with lazy linking of
+      # mozglue.dll on Windows XP (leads to startup crash), so disable it.
+      ac_configure_args="$ac_configure_args --disable-lazy-lock"
+    fi
   elif test "${OS_ARCH}" = Darwin; then
     # When building as a replace-malloc lib, disabling the zone allocator
     # forces to use pthread_atfork.
     ac_configure_args="$ac_configure_args --disable-zone-allocator"
   fi
   _MANGLE="malloc posix_memalign aligned_alloc calloc realloc free memalign valloc malloc_usable_size"
   JEMALLOC_WRAPPER=
   if test -z "$MOZ_REPLACE_MALLOC"; then
@@ -9173,30 +9184,37 @@ if test -z "$MOZ_NATIVE_JEMALLOC" -a "$M
   unset CONFIG_FILES
   if test -z "$MOZ_TLS"; then
     ac_configure_args="$ac_configure_args --disable-tls"
   fi
   EXTRA_CFLAGS="$CFLAGS"
   for var in AS CC CXX CPP LD AR RANLIB STRIP CPPFLAGS EXTRA_CFLAGS LDFLAGS; do
     ac_configure_args="$ac_configure_args $var='`eval echo \\${${var}}`'"
   done
-  if test "$CROSS_COMPILE"; then
-    ac_configure_args="$ac_configure_args je_cv_static_page_shift=12"
-  fi
   # Force disable DSS support in jemalloc.
   ac_configure_args="$ac_configure_args ac_cv_func_sbrk=false"
 
   # Make Linux builds munmap freed chunks instead of recycling them.
   ac_configure_args="$ac_configure_args --enable-munmap"
 
+  # Disable cache oblivious behavior that appears to have a performance
+  # impact on Firefox.
+  ac_configure_args="$ac_configure_args --disable-cache-oblivious"
+
   if ! test -e memory/jemalloc; then
     mkdir -p memory/jemalloc
   fi
 
+  # jemalloc's configure runs git to determine the version. But when building
+  # from a gecko git clone, the git commands it uses is going to pick gecko's
+  # information, not jemalloc's, which is useless. So pretend we don't have git
+  # at all. That will make jemalloc's configure pick the in-tree VERSION file.
+  (PATH="$srcdir/memory/jemalloc/helper:$PATH";
   AC_OUTPUT_SUBDIRS(memory/jemalloc/src)
+  ) || exit 1
   ac_configure_args="$_SUBDIR_CONFIG_ARGS"
 fi
 
 # Run freetype configure script
 
 if test "$MOZ_TREE_FREETYPE"; then
    export CFLAGS="$CFLAGS $MOZ_DEBUG_FLAGS -std=c99"
    export CPPFLAGS="$CPPFLAGS $MOZ_DEBUG_FLAGS"
--- a/docshell/base/nsDefaultURIFixup.cpp
+++ b/docshell/base/nsDefaultURIFixup.cpp
@@ -660,17 +660,17 @@ nsDefaultURIFixup::FileURIFixup(const ns
 nsresult
 nsDefaultURIFixup::ConvertFileToStringURI(const nsACString& aIn,
                                           nsCString& aResult)
 {
   bool attemptFixup = false;
 
 #if defined(XP_WIN)
   // Check for \ in the url-string or just a drive (PC)
-  if (kNotFound != aIn.FindChar('\\') ||
+  if (aIn.Contains('\\') ||
       (aIn.Length() == 2 && (aIn.Last() == ':' || aIn.Last() == '|'))) {
     attemptFixup = true;
   }
 #elif defined(XP_UNIX)
   // Check if it starts with / (UNIX)
   if (aIn.First() == '/') {
     attemptFixup = true;
   }
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -369,16 +369,26 @@ Animation::Tick()
 
   if (IsPossiblyOrphanedPendingAnimation()) {
     MOZ_ASSERT(mTimeline && !mTimeline->GetCurrentTime().IsNull(),
                "Orphaned pending animtaions should have an active timeline");
     FinishPendingAt(mTimeline->GetCurrentTime().Value());
   }
 
   UpdateTiming(SeekFlag::NoSeek, SyncNotifyFlag::Async);
+
+  // FIXME: Detect the no-change case and don't request a restyle at all
+  // FIXME: Detect changes to IsPlaying() state and request RestyleType::Layer
+  //        so that layers get updated immediately
+  AnimationCollection* collection = GetCollection();
+  if (collection) {
+    collection->RequestRestyle(CanThrottle() ?
+      AnimationCollection::RestyleType::Throttled :
+      AnimationCollection::RestyleType::Standard);
+  }
 }
 
 void
 Animation::TriggerOnNextTick(const Nullable<TimeDuration>& aReadyTime)
 {
   // Normally we expect the play state to be pending but it's possible that,
   // due to the handling of possibly orphaned animations in Tick(), this
   // animation got started whilst still being in another document's pending
@@ -940,17 +950,17 @@ Animation::FlushStyle() const
   }
 }
 
 void
 Animation::PostUpdate()
 {
   AnimationCollection* collection = GetCollection();
   if (collection) {
-    collection->NotifyAnimationUpdated();
+    collection->RequestRestyle(AnimationCollection::RestyleType::Layer);
   }
 }
 
 void
 Animation::CancelPendingTasks()
 {
   if (mPendingState == PendingState::NotPending) {
     return;
--- a/dom/base/File.cpp
+++ b/dom/base/File.cpp
@@ -1257,17 +1257,17 @@ BlobSet::AppendVoidPtr(const void* aData
 }
 
 nsresult
 BlobSet::AppendString(const nsAString& aString, bool nativeEOL, JSContext* aCx)
 {
   nsCString utf8Str = NS_ConvertUTF16toUTF8(aString);
 
   if (nativeEOL) {
-    if (utf8Str.FindChar('\r') != kNotFound) {
+    if (utf8Str.Contains('\r')) {
       utf8Str.ReplaceSubstring("\r\n", "\n");
       utf8Str.ReplaceSubstring("\r", "\n");
     }
 #ifdef XP_WIN
     utf8Str.ReplaceSubstring("\n", "\r\n");
 #endif
   }
 
--- a/dom/base/PerformanceEntry.h
+++ b/dom/base/PerformanceEntry.h
@@ -10,16 +10,17 @@
 #include "nsDOMNavigationTiming.h"
 #include "nsString.h"
 #include "nsWrapperCache.h"
 
 class nsISupports;
 
 namespace mozilla {
 namespace dom {
+class PerformanceResourceTiming;
 
 // http://www.w3.org/TR/performance-timeline/#performanceentry
 class PerformanceEntry : public nsISupports,
                          public nsWrapperCache
 {
 protected:
   virtual ~PerformanceEntry();
 
@@ -73,16 +74,21 @@ public:
     return 0;
   }
 
   virtual DOMHighResTimeStamp Duration() const
   {
     return 0;
   }
 
+  virtual const PerformanceResourceTiming* ToResourceTiming() const
+  {
+    return nullptr;
+  }
+
 protected:
   nsCOMPtr<nsISupports> mParent;
   nsString mName;
   nsString mEntryType;
 };
 
 } // namespace dom
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/base/PerformanceObserver.cpp
@@ -0,0 +1,154 @@
+/* -*- 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 "PerformanceObserver.h"
+
+#include "mozilla/dom/PerformanceBinding.h"
+#include "mozilla/dom/PerformanceEntryBinding.h"
+#include "mozilla/dom/PerformanceObserverBinding.h"
+#include "mozilla/dom/workers/bindings/Performance.h"
+#include "nsPerformance.h"
+#include "nsPIDOMWindow.h"
+#include "nsQueryObject.h"
+#include "nsString.h"
+#include "PerformanceEntry.h"
+#include "PerformanceObserverEntryList.h"
+#include "WorkerPrivate.h"
+#include "WorkerScope.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::dom::workers;
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceObserver,
+                                      mOwner,
+                                      mPerformance,
+                                      mCallback)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceObserver)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceObserver)
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceObserver)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+PerformanceObserver::PerformanceObserver(nsPIDOMWindow* aOwner,
+                                         PerformanceObserverCallback& aCb)
+  : mOwner(aOwner)
+  , mCallback(&aCb)
+{
+  MOZ_ASSERT(mOwner);
+  mPerformance = aOwner->GetPerformance();
+}
+
+PerformanceObserver::PerformanceObserver(WorkerPrivate* aWorkerPrivate,
+                                         PerformanceObserverCallback& aCb)
+  : mCallback(&aCb)
+{
+  MOZ_ASSERT(aWorkerPrivate);
+  mPerformance = aWorkerPrivate->GlobalScope()->GetPerformance();
+}
+
+PerformanceObserver::~PerformanceObserver()
+{
+}
+
+// static
+already_AddRefed<PerformanceObserver>
+PerformanceObserver::Constructor(const GlobalObject& aGlobal,
+                                 PerformanceObserverCallback& aCb,
+                                 ErrorResult& aRv)
+{
+  if (NS_IsMainThread()) {
+    nsCOMPtr<nsPIDOMWindow> ownerWindow =
+      do_QueryInterface(aGlobal.GetAsSupports());
+    if (!ownerWindow) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+    MOZ_ASSERT(ownerWindow->IsInnerWindow());
+
+    nsRefPtr<PerformanceObserver> observer =
+      new PerformanceObserver(ownerWindow, aCb);
+    return observer.forget();
+  }
+
+  JSContext* cx = aGlobal.Context();
+  WorkerPrivate* workerPrivate = GetWorkerPrivateFromContext(cx);
+  MOZ_ASSERT(workerPrivate);
+
+  nsRefPtr<PerformanceObserver> observer =
+    new PerformanceObserver(workerPrivate, aCb);
+  return observer.forget();
+}
+
+JSObject*
+PerformanceObserver::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return PerformanceObserverBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+PerformanceObserver::Notify(PerformanceEntry* aEntry)
+{
+  MOZ_ASSERT(aEntry);
+
+  nsAutoString entryType;
+  aEntry->GetEntryType(entryType);
+  if (!mEntryTypes.Contains<nsString>(entryType)) {
+    return;
+  }
+
+  nsRefPtr<PerformanceObserverEntryList> list = new PerformanceObserverEntryList(this);
+  list->AppendEntry(aEntry);
+
+  ErrorResult rv;
+  mCallback->Call(this, *list, *this, rv);
+  NS_WARN_IF(rv.Failed());
+}
+
+static nsString sValidTypeNames[7] = {
+  NS_LITERAL_STRING("composite"),
+  NS_LITERAL_STRING("mark"),
+  NS_LITERAL_STRING("measure"),
+  NS_LITERAL_STRING("navigation"),
+  NS_LITERAL_STRING("render"),
+  NS_LITERAL_STRING("resource"),
+  NS_LITERAL_STRING("server")
+};
+
+void
+PerformanceObserver::Observe(const PerformanceObserverInit& aOptions,
+                             ErrorResult& aRv)
+{
+  if (aOptions.mEntryTypes.IsEmpty()) {
+    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+    return;
+  }
+
+  nsTArray<nsString> validEntryTypes;
+
+  for (const nsString& validTypeName : sValidTypeNames) {
+    if (aOptions.mEntryTypes.Contains<nsString>(validTypeName) &&
+        !validEntryTypes.Contains<nsString>(validTypeName)) {
+      validEntryTypes.AppendElement(validTypeName);
+    }
+  }
+
+  if (validEntryTypes.IsEmpty()) {
+    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+    return;
+  }
+
+  mEntryTypes = validEntryTypes;
+
+  mPerformance->AddObserver(this);
+}
+
+void
+PerformanceObserver::Disconnect()
+{
+  mPerformance->RemoveObserver(this);
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/PerformanceObserver.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_PerformanceObserver_h__
+#define mozilla_dom_PerformanceObserver_h__
+
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "mozilla/nsRefPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsWrapperCache.h"
+
+class nsPIDOMWindow;
+class PerformanceBase;
+
+namespace mozilla {
+
+class ErrorResult;
+
+namespace dom {
+
+class GlobalObject;
+class PerformanceEntry;
+class PerformanceObserverCallback;
+struct PerformanceObserverInit;
+namespace workers {
+class WorkerPrivate;
+} // namespace workers
+
+class PerformanceObserver final : public nsISupports,
+                                  public nsWrapperCache
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceObserver)
+
+  static already_AddRefed<PerformanceObserver>
+  Constructor(const GlobalObject& aGlobal,
+              PerformanceObserverCallback& aCb,
+              ErrorResult& aRv);
+
+  PerformanceObserver(nsPIDOMWindow* aOwner,
+                      PerformanceObserverCallback& aCb);
+
+  PerformanceObserver(workers::WorkerPrivate* aWorkerPrivate,
+                      PerformanceObserverCallback& aCb);
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  nsISupports* GetParentObject() const { return mOwner; }
+
+  void Observe(const PerformanceObserverInit& aOptions,
+               mozilla::ErrorResult& aRv);
+
+  void Disconnect();
+
+  void Notify(PerformanceEntry* entry);
+
+private:
+  ~PerformanceObserver();
+
+  nsCOMPtr<nsISupports> mOwner;
+  nsRefPtr<PerformanceObserverCallback> mCallback;
+  nsRefPtr<PerformanceBase> mPerformance;
+  nsTArray<nsString> mEntryTypes;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif
new file mode 100644
--- /dev/null
+++ b/dom/base/PerformanceObserverEntryList.cpp
@@ -0,0 +1,103 @@
+/* -*- 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 "PerformanceObserverEntryList.h"
+
+#include "mozilla/dom/PerformanceObserverEntryListBinding.h"
+#include "nsPerformance.h"
+#include "nsString.h"
+#include "PerformanceResourceTiming.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PerformanceObserverEntryList,
+                                      mOwner,
+                                      mEntries)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(PerformanceObserverEntryList)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(PerformanceObserverEntryList)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceObserverEntryList)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+PerformanceObserverEntryList::~PerformanceObserverEntryList()
+{
+}
+
+JSObject*
+PerformanceObserverEntryList::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+  return PerformanceObserverEntryListBinding::Wrap(aCx, this, aGivenProto);
+}
+
+void
+PerformanceObserverEntryList::GetEntries(
+  const PerformanceEntryFilterOptions& aFilter,
+  nsTArray<nsRefPtr<PerformanceEntry>>& aRetval)
+{
+  aRetval.Clear();
+  for (const nsRefPtr<PerformanceEntry>& entry : mEntries) {
+    if (aFilter.mInitiatorType.WasPassed()) {
+      const PerformanceResourceTiming* resourceEntry =
+        entry->ToResourceTiming();
+      if (!resourceEntry) {
+        continue;
+      }
+      nsAutoString initiatorType;
+      resourceEntry->GetInitiatorType(initiatorType);
+      if (!initiatorType.Equals(aFilter.mInitiatorType.Value())) {
+        continue;
+      }
+    }
+    if (aFilter.mName.WasPassed() &&
+        !entry->GetName().Equals(aFilter.mName.Value())) {
+      continue;
+    }
+    if (aFilter.mEntryType.WasPassed() &&
+        !entry->GetEntryType().Equals(aFilter.mEntryType.Value())) {
+      continue;
+    }
+
+    aRetval.AppendElement(entry);
+  }
+}
+
+void
+PerformanceObserverEntryList::GetEntriesByType(
+  const nsAString& aEntryType,
+  nsTArray<nsRefPtr<PerformanceEntry>>& aRetval)
+{
+  aRetval.Clear();
+  for (const nsRefPtr<PerformanceEntry>& entry : mEntries) {
+    if (entry->GetEntryType().Equals(aEntryType)) {
+      aRetval.AppendElement(entry);
+    }
+  }
+}
+
+void
+PerformanceObserverEntryList::GetEntriesByName(
+  const nsAString& aName,
+  const Optional<nsAString>& aEntryType,
+  nsTArray<nsRefPtr<PerformanceEntry>>& aRetval)
+{
+  aRetval.Clear();
+  for (const nsRefPtr<PerformanceEntry>& entry : mEntries) {
+    if (entry->GetName().Equals(aName)) {
+      aRetval.AppendElement(entry);
+    }
+  }
+}
+
+void
+PerformanceObserverEntryList::AppendEntry(PerformanceEntry* aEntry)
+{
+  mEntries.AppendElement(aEntry);
+}
+
new file mode 100644
--- /dev/null
+++ b/dom/base/PerformanceObserverEntryList.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_PerformanceObserverEntryList_h__
+#define mozilla_dom_PerformanceObserverEntryList_h__
+
+#include "nsCOMPtr.h"
+#include "nsISupports.h"
+#include "nsTArray.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/PerformanceEntryBinding.h"
+
+namespace mozilla {
+namespace dom {
+
+struct PerformanceEntryFilterOptions;
+class PerformanceEntry;
+template<typename T> class Optional;
+
+class PerformanceObserverEntryList final : public nsISupports,
+                                           public nsWrapperCache
+{
+  ~PerformanceObserverEntryList();
+
+public:
+  explicit PerformanceObserverEntryList(nsISupports* aOwner)
+    : mOwner(aOwner)
+  {
+  }
+
+  nsISupports* GetParentObject() const
+  {
+    return mOwner;
+  }
+
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PerformanceObserverEntryList)
+
+  void GetEntries(const PerformanceEntryFilterOptions& aFilter,
+                  nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
+  void GetEntriesByType(const nsAString& aEntryType,
+                        nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
+  void GetEntriesByName(const nsAString& aName,
+                        const Optional<nsAString>& aEntryType,
+                        nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
+
+  void AppendEntry(PerformanceEntry* aEntry);
+
+private:
+  nsCOMPtr<nsISupports> mOwner;
+  nsTArray<nsRefPtr<PerformanceEntry>> mEntries;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+
+#endif
--- a/dom/base/PerformanceResourceTiming.h
+++ b/dom/base/PerformanceResourceTiming.h
@@ -118,16 +118,21 @@ public:
 
   DOMHighResTimeStamp SecureConnectionStart() const
   {
     // This measurement is not available for Navigation Timing either.
     // There is a different bug submitted for it.
     return 0;
   }
 
+  virtual const PerformanceResourceTiming* ToResourceTiming() const override
+  {
+    return this;
+  }
+
 protected:
   virtual ~PerformanceResourceTiming();
 
   nsString mInitiatorType;
   nsRefPtr<nsPerformanceTiming> mTiming;
 };
 
 } // namespace dom
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -184,16 +184,18 @@ EXPORTS.mozilla.dom += [
     'NameSpaceConstants.h',
     'Navigator.h',
     'NodeInfo.h',
     'NodeInfoInlines.h',
     'NodeIterator.h',
     'PerformanceEntry.h',
     'PerformanceMark.h',
     'PerformanceMeasure.h',
+    'PerformanceObserver.h',
+    'PerformanceObserverEntryList.h',
     'PerformanceResourceTiming.h',
     'ProcessGlobal.h',
     'ResponsiveImageSelector.h',
     'SameProcessMessageQueue.h',
     'ScreenOrientation.h',
     'ScriptSettings.h',
     'ShadowRoot.h',
     'StructuredCloneHelper.h',
@@ -323,16 +325,18 @@ UNIFIED_SOURCES += [
     'nsWrapperCache.cpp',
     'nsXHTMLContentSerializer.cpp',
     'nsXMLContentSerializer.cpp',
     'nsXMLHttpRequest.cpp',
     'nsXMLNameSpaceMap.cpp',
     'PerformanceEntry.cpp',
     'PerformanceMark.cpp',
     'PerformanceMeasure.cpp',
+    'PerformanceObserver.cpp',
+    'PerformanceObserverEntryList.cpp',
     'PerformanceResourceTiming.cpp',
     'PostMessageEvent.cpp',
     'ProcessGlobal.cpp',
     'ResponsiveImageSelector.cpp',
     'SameProcessMessageQueue.cpp',
     'ScriptSettings.cpp',
     'ShadowRoot.cpp',
     'StructuredCloneHelper.cpp',
--- a/dom/base/nsAttrValue.cpp
+++ b/dom/base/nsAttrValue.cpp
@@ -24,16 +24,21 @@
 #include "prprf.h"
 #include "nsHTMLCSSStyleSheet.h"
 #include "nsCSSParser.h"
 #include "nsStyledElement.h"
 #include "nsIURI.h"
 #include "nsIDocument.h"
 #include <algorithm>
 
+#ifdef LoadImage
+// Undefine LoadImage to prevent naming conflict with Windows.
+#undef LoadImage
+#endif
+
 using namespace mozilla;
 
 #define MISC_STR_PTR(_cont) \
   reinterpret_cast<void*>((_cont)->mStringBits & NS_ATTRVALUE_POINTERVALUE_MASK)
 
 bool
 MiscContainer::GetString(nsAString& aString) const
 {
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -1724,17 +1724,17 @@ nsContentUtils::GetOfflineAppManifest(ns
     return;
   }
 
   nsAutoString manifestSpec;
   docElement->GetAttr(kNameSpaceID_None, nsGkAtoms::manifest, manifestSpec);
 
   // Manifest URIs can't have fragment identifiers.
   if (manifestSpec.IsEmpty() ||
-      manifestSpec.FindChar('#') != kNotFound) {
+      manifestSpec.Contains('#')) {
     return;
   }
 
   nsContentUtils::NewURIWithDocumentCharset(aURI, manifestSpec,
                                             aDocument,
                                             aDocument->GetDocBaseURI());
 }
 
--- a/dom/base/nsIStyleSheetLinkingElement.h
+++ b/dom/base/nsIStyleSheetLinkingElement.h
@@ -8,18 +8,18 @@
 
 
 #include "nsISupports.h"
 
 class nsICSSLoaderObserver;
 class nsIURI;
 
 #define NS_ISTYLESHEETLINKINGELEMENT_IID          \
-{ 0xe5855604, 0x8a9a, 0x4181, \
- { 0xbe, 0x41, 0xdd, 0xf7, 0x08, 0x70, 0x3f, 0xbe } }
+{ 0xa8b79f3b, 0x9d18, 0x4f9c, \
+  { 0xb1, 0xaa, 0x8c, 0x9b, 0x1b, 0xaa, 0xac, 0xad } }
 
 namespace mozilla {
 class CSSStyleSheet;
 } // namespace mozilla
 
 class nsIStyleSheetLinkingElement : public nsISupports {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(NS_ISTYLESHEETLINKINGELEMENT_IID)
@@ -92,14 +92,22 @@ public:
    * @param aNewBaseURI the new base URI, nullptr to use the default base URI.
    */
   virtual void OverrideBaseURI(nsIURI* aNewBaseURI) = 0;
 
   // This doesn't entirely belong here since they only make sense for
   // some types of linking elements, but it's a better place than
   // anywhere else.
   virtual void SetLineNumber(uint32_t aLineNumber) = 0;
+
+  /**
+   * Get the line number, as previously set by SetLineNumber.
+   *
+   * @return the line number of this element; or 1 if no line number
+   *         was set
+   */
+  virtual uint32_t GetLineNumber() = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIStyleSheetLinkingElement,
                               NS_ISTYLESHEETLINKINGELEMENT_IID)
 
 #endif // nsILinkingElement_h__
--- a/dom/base/nsPerformance.cpp
+++ b/dom/base/nsPerformance.cpp
@@ -13,22 +13,24 @@
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMWindow.h"
 #include "nsILoadInfo.h"
 #include "nsIURI.h"
 #include "nsThreadUtils.h"
 #include "PerformanceEntry.h"
 #include "PerformanceMark.h"
 #include "PerformanceMeasure.h"
+#include "PerformanceObserver.h"
 #include "PerformanceResourceTiming.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/PerformanceBinding.h"
 #include "mozilla/dom/PerformanceEntryEvent.h"
 #include "mozilla/dom/PerformanceTimingBinding.h"
 #include "mozilla/dom/PerformanceNavigationBinding.h"
+#include "mozilla/dom/PerformanceObserverBinding.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/TimeStamp.h"
 #include "js/HeapAPI.h"
 #include "WorkerPrivate.h"
 #include "WorkerRunnable.h"
 
 #ifdef MOZ_WIDGET_GONK
@@ -687,52 +689,75 @@ public:
                "Trying to compare null performance entries");
     return aElem1->StartTime() < aElem2->StartTime();
   }
 };
 
 class PrefEnabledRunnable final : public WorkerMainThreadRunnable
 {
 public:
-  explicit PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate)
+  PrefEnabledRunnable(WorkerPrivate* aWorkerPrivate,
+                      const nsCString& aPrefName)
     : WorkerMainThreadRunnable(aWorkerPrivate)
     , mEnabled(false)
+    , mPrefName(aPrefName)
   { }
 
   bool MainThreadRun() override
   {
     MOZ_ASSERT(NS_IsMainThread());
-    mEnabled = Preferences::GetBool("dom.enable_user_timing", false);
+    mEnabled = Preferences::GetBool(mPrefName.get(), false);
     return true;
   }
 
   bool IsEnabled() const
   {
     return mEnabled;
   }
 
 private:
   bool mEnabled;
+  nsCString mPrefName;
 };
 
 } // namespace
 
 /* static */ bool
 nsPerformance::IsEnabled(JSContext* aCx, JSObject* aGlobal)
 {
   if (NS_IsMainThread()) {
     return Preferences::GetBool("dom.enable_user_timing", false);
   }
 
   WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   MOZ_ASSERT(workerPrivate);
   workerPrivate->AssertIsOnWorkerThread();
 
   nsRefPtr<PrefEnabledRunnable> runnable =
-    new PrefEnabledRunnable(workerPrivate);
+    new PrefEnabledRunnable(workerPrivate,
+                            NS_LITERAL_CSTRING("dom.enable_user_timing"));
+  runnable->Dispatch(workerPrivate->GetJSContext());
+
+  return runnable->IsEnabled();
+}
+
+/* static */ bool
+nsPerformance::IsObserverEnabled(JSContext* aCx, JSObject* aGlobal)
+{
+  if (NS_IsMainThread()) {
+    return Preferences::GetBool("dom.enable_performance_observer", false);
+  }
+
+  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
+  MOZ_ASSERT(workerPrivate);
+  workerPrivate->AssertIsOnWorkerThread();
+
+  nsRefPtr<PrefEnabledRunnable> runnable =
+    new PrefEnabledRunnable(workerPrivate,
+                            NS_LITERAL_CSTRING("dom.enable_performance_observer"));
   runnable->Dispatch(workerPrivate->GetJSContext());
 
   return runnable->IsEnabled();
 }
 
 void
 nsPerformance::InsertUserEntry(PerformanceEntry* aEntry)
 {
@@ -1019,16 +1044,20 @@ PerformanceBase::TimingNotification(Perf
   }
 }
 
 void
 PerformanceBase::InsertUserEntry(PerformanceEntry* aEntry)
 {
   mUserEntries.InsertElementSorted(aEntry,
                                    PerformanceEntryComparator());
+
+  NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers,
+                                           PerformanceObserver,
+                                           Notify, (aEntry));
 }
 
 void
 PerformanceBase::SetResourceTimingBufferSize(uint64_t aMaxSize)
 {
   mResourceTimingBufferSize = aMaxSize;
 }
 
@@ -1042,9 +1071,24 @@ PerformanceBase::InsertResourceEntry(Per
   }
 
   mResourceEntries.InsertElementSorted(aEntry,
                                        PerformanceEntryComparator());
   if (mResourceEntries.Length() == mResourceTimingBufferSize) {
     // call onresourcetimingbufferfull
     DispatchBufferFullEvent();
   }
+  NS_OBSERVER_ARRAY_NOTIFY_XPCOM_OBSERVERS(mObservers,
+                                           PerformanceObserver,
+                                           Notify, (aEntry));
 }
+
+void
+PerformanceBase::AddObserver(PerformanceObserver* aObserver)
+{
+  mObservers.AppendElementUnlessExists(aObserver);
+}
+
+void
+PerformanceBase::RemoveObserver(PerformanceObserver* aObserver)
+{
+  mObservers.RemoveElement(aObserver);
+}
--- a/dom/base/nsPerformance.h
+++ b/dom/base/nsPerformance.h
@@ -21,16 +21,17 @@
 class nsITimedChannel;
 class nsPerformance;
 class nsIHttpChannel;
 
 namespace mozilla {
 class ErrorResult;
 namespace dom {
   class PerformanceEntry;
+  class PerformanceObserver;
 } // namespace dom
 } // namespace mozilla
 
 // Script "performance.timing" object
 class nsPerformanceTiming final : public nsWrapperCache
 {
 public:
   typedef mozilla::TimeStamp TimeStamp;
@@ -295,16 +296,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PerformanceBase,
                                            DOMEventTargetHelper)
 
   PerformanceBase();
   explicit PerformanceBase(nsPIDOMWindow* aWindow);
 
   typedef mozilla::dom::PerformanceEntry PerformanceEntry;
+  typedef mozilla::dom::PerformanceObserver PerformanceObserver;
 
   void GetEntries(nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
   void GetEntriesByType(const nsAString& aEntryType,
                         nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
   void GetEntriesByName(const nsAString& aName,
                         const mozilla::dom::Optional<nsAString>& aEntryType,
                         nsTArray<nsRefPtr<PerformanceEntry>>& aRetval);
   void ClearResourceTimings();
@@ -316,16 +318,19 @@ public:
   void Measure(const nsAString& aName,
                const mozilla::dom::Optional<nsAString>& aStartMark,
                const mozilla::dom::Optional<nsAString>& aEndMark,
                mozilla::ErrorResult& aRv);
   void ClearMeasures(const mozilla::dom::Optional<nsAString>& aName);
 
   void SetResourceTimingBufferSize(uint64_t aMaxSize);
 
+  void AddObserver(PerformanceObserver* aObserver);
+  void RemoveObserver(PerformanceObserver* aObserver);
+
 protected:
   virtual ~PerformanceBase();
 
   virtual void InsertUserEntry(PerformanceEntry* aEntry);
   void InsertResourceEntry(PerformanceEntry* aEntry);
 
   void ClearUserEntries(const mozilla::dom::Optional<nsAString>& aEntryName,
                         const nsAString& aEntryType);
@@ -348,16 +353,18 @@ protected:
   bool IsResourceEntryLimitReached() const
   {
     return mResourceEntries.Length() >= mResourceTimingBufferSize;
   }
 
   void LogEntry(PerformanceEntry* aEntry, const nsACString& aOwner) const;
   void TimingNotification(PerformanceEntry* aEntry, const nsACString& aOwner, uint64_t epoch);
 
+  nsTObserverArray<PerformanceObserver*> mObservers;
+
 private:
   nsTArray<nsRefPtr<PerformanceEntry>> mUserEntries;
   nsTArray<nsRefPtr<PerformanceEntry>> mResourceEntries;
 
   uint64_t mResourceTimingBufferSize;
   static const uint64_t kDefaultResourceTimingBufferSize = 150;
 };
 
@@ -371,16 +378,18 @@ public:
                 nsPerformance* aParentPerformance);
 
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(nsPerformance,
                                                          PerformanceBase)
 
   static bool IsEnabled(JSContext* aCx, JSObject* aGlobal);
 
+  static bool IsObserverEnabled(JSContext* aCx, JSObject* aGlobal);
+
   nsDOMNavigationTiming* GetDOMTiming() const
   {
     return mDOMTiming;
   }
 
   nsITimedChannel* GetChannel() const
   {
     return mChannel;
--- a/dom/base/nsStyleLinkElement.cpp
+++ b/dom/base/nsStyleLinkElement.cpp
@@ -114,16 +114,22 @@ nsStyleLinkElement::OverrideBaseURI(nsIU
 }
 
 /* virtual */ void
 nsStyleLinkElement::SetLineNumber(uint32_t aLineNumber)
 {
   mLineNumber = aLineNumber;
 }
 
+/* virtual */ uint32_t
+nsStyleLinkElement::GetLineNumber()
+{
+  return mLineNumber;
+}
+
 /* static */ bool
 nsStyleLinkElement::IsImportEnabled()
 {
   static bool sAdded = false;
   static bool sImportsEnabled;
   if (!sAdded) {
     // This part runs only once because of the static flag.
     Preferences::AddBoolVarCache(&sImportsEnabled,
--- a/dom/base/nsStyleLinkElement.h
+++ b/dom/base/nsStyleLinkElement.h
@@ -47,16 +47,17 @@ public:
                               bool* aWillNotify,
                               bool* aIsAlternate,
                               bool aForceReload) override;
   NS_IMETHOD SetEnableUpdates(bool aEnableUpdates) override;
   NS_IMETHOD GetCharset(nsAString& aCharset) override;
 
   virtual void OverrideBaseURI(nsIURI* aNewBaseURI) override;
   virtual void SetLineNumber(uint32_t aLineNumber) override;
+  virtual uint32_t GetLineNumber() override;
 
   enum RelValue {
     ePREFETCH =     0x00000001,
     eDNS_PREFETCH = 0x00000002,
     eSTYLESHEET =   0x00000004,
     eNEXT =         0x00000008,
     eALTERNATE =    0x00000010,
     eHTMLIMPORT =   0x00000020,
--- a/dom/base/test/mochitest.ini
+++ b/dom/base/test/mochitest.ini
@@ -242,16 +242,18 @@ support-files =
   file_webaudioLoop.html
   file_webaudioLoop2.html
   file_pluginAudio.html
   noaudio.webm
   referrer_helper.js
   referrer_testserver.sjs
   script_postmessages_fileList.js
   iframe_postMessages.html
+  test_performance_observer.js
+  performance_observer.html
 
 [test_anonymousContent_api.html]
 [test_anonymousContent_append_after_reflow.html]
 [test_anonymousContent_insert.html]
 [test_anonymousContent_manipulate_content.html]
 [test_appname_override.html]
 [test_async_setTimeout_stack.html]
 [test_async_setTimeout_stack_across_globals.html]
@@ -800,16 +802,17 @@ support-files = file_bug1011748_redirect
 [test_bug1037687.html]
 [test_element.matches.html]
 [test_user_select.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug1081686.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s
 [test_window_define_nonconfigurable.html]
 [test_root_iframe.html]
+[test_performance_observer.html]
 [test_performance_user_timing.html]
 [test_bug1126851.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 [test_bug1118689.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 [test_integer_attr_with_leading_zero.html]
 [test_getAttribute_after_createAttribute.html]
 [test_script_loader_crossorigin_data_url.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/performance_observer.html
@@ -0,0 +1,79 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset=utf-8>
+<html>
+<head>
+<title>Test for performance observer</title>
+<script>
+'use strict';
+[
+ "async_test", "test", "setup",
+ "assert_true", "assert_equals", "assert_array_equals",
+ "assert_throws"
+].forEach(func => {
+  window[func] = opener[func].bind(opener);
+});
+function done() {
+  opener.add_completion_callback(() => {
+    self.close();
+  });
+  opener.done();
+}
+
+</script>
+<script src="test_performance_observer.js"></script>
+</head>
+<body>
+<div id="log"></div>
+<script>
+function promiseXHR(aUrl) {
+  return new Promise(resolve => {
+    var xmlhttp = new XMLHttpRequest();
+    xmlhttp.onload = resolve;
+    xmlhttp.open("get", aUrl, true);
+    xmlhttp.send();
+  });
+}
+
+async_test(t => {
+  performance.clearResourceTimings();
+
+  var observedEntryList;
+  var observedEntries = [];
+  var observer = new PerformanceObserver(list => {
+    list.getEntries().forEach(entry => observedEntries.push(entry));
+    observedEntryList = list;
+  });
+  observer.observe({entryTypes: ['resource']});
+
+  assert_equals(observedEntries.length, 0);
+
+  promiseXHR("test-data.json").then(t.step_func_done(() => {
+    assert_equals(observedEntries.length, 1);
+    assert_array_equals(observedEntries,
+                        performance.getEntriesByType("resource"),
+                        "Observed 'resource' entries should equal to entries obtained by getEntriesByType.");
+
+    // getEntries filtering tests
+    assert_array_equals(observedEntryList.getEntries({name: "http://mochi.test:8888/tests/dom/base/test/test-data.json"}),
+                        performance.getEntriesByName("http://mochi.test:8888/tests/dom/base/test/test-data.json"),
+                        "getEntries with name filter should return correct results.");
+    assert_array_equals(observedEntryList.getEntries({entryType: "resource"}),
+                        performance.getEntriesByType("resource"),
+                        "getEntries with entryType filter should return correct results.");
+    assert_array_equals(observedEntryList.getEntries({initiatorType: "xmlhttprequest"}),
+                        performance.getEntriesByType("resource"),
+                        "getEntries with initiatorType filter should return correct results.");
+    assert_array_equals(observedEntryList.getEntries({initiatorType: "link"}),
+                        [],
+                        "getEntries with non-existent initiatorType filter should return an empty array.");
+  }));
+}, "resource-timing test");
+
+done();
+
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_performance_observer.html
@@ -0,0 +1,17 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for performance observer</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+'use strict';
+SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]},
+                          function() {
+                            window.open("performance_observer.html");
+                          });
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/test_performance_observer.js
@@ -0,0 +1,240 @@
+setup({ explicit_done: true });
+
+test(t => {
+  assert_throws({name: "TypeError"}, function() {
+    new PerformanceObserver();
+  }, "PerformanceObserver constructor should throw TypeError if no argument is specified.");
+
+  assert_throws({name: "TypeError"}, function() {
+    new PerformanceObserver({});
+  }, "PerformanceObserver constructor should throw TypeError if the argument is not a function.");
+}, "Test that PerformanceObserver constructor throws exception");
+
+test(t => {
+  var observer = new PerformanceObserver(() => {
+  });
+
+  assert_throws({name: "TypeError"}, function() {
+    observer.observe();
+  }, "observe() should throw TypeError exception if no option specified.");
+
+  assert_throws({name: "TypeError"}, function() {
+    observer.observe({ unsupportedAttribute: "unsupported" });
+  }, "obsrve() should throw TypeError exception if the option has no 'entryTypes' attribute.");
+
+  assert_throws({name: "TypeError"}, function() {
+    observer.observe({ entryTypes: [] });
+  }, "obsrve() should throw TypeError exception if 'entryTypes' attribute is an empty sequence.");
+
+  assert_throws({name: "TypeError"}, function() {
+    observer.observe({ entryTypes: null });
+  }, "obsrve() should throw TypeError exception if 'entryTypes' attribute is null.");
+
+  assert_throws({name: "TypeError"}, function() {
+    observer.observe({ entryTypes: ["invalid"]});
+  }, "obsrve() should throw TypeError exception if 'entryTypes' attribute value is invalid.");
+}, "Test that PerformanceObserver.observe throws exception");
+
+test(t => {
+  performance.clearMarks();
+  performance.clearMeasures();
+
+  var observedEntries = [];
+  var observer = new PerformanceObserver(list => {
+    list.getEntries().forEach(entry => observedEntries.push(entry));
+  });
+  observer.observe({entryTypes: ['mark', 'measure']});
+
+  assert_equals(observedEntries.length, 0,
+                "User timing entries should never be observed.");
+  assert_equals(performance.getEntriesByType("mark").length, 0,
+                "There should be no 'mark' entries.");
+  assert_equals(performance.getEntriesByType("measure").length, 0,
+                "There should be no 'measure' entries.");
+
+  performance.mark("test-start");
+  performance.mark("test-end");
+  performance.measure("test-measure", "test-start", "test-end");
+
+  assert_equals(observedEntries.length, 3,
+                "There should be three observed entries.");
+
+  var markEntries = observedEntries.filter(entry => {
+    return entry.entryType == "mark";
+  });
+  assert_array_equals(markEntries, performance.getEntriesByType("mark"),
+                      "Observed 'mark' entries should equal to entries obtained by getEntriesByType.");
+
+  var measureEntries = observedEntries.filter(entry => {
+    return entry.entryType == "measure";
+  });
+  assert_array_equals(measureEntries, performance.getEntriesByType("measure"),
+                      "Observed 'measure' entries should equal to entries obtained by getEntriesByType.");
+}, "Test for user-timing with PerformanceObserver");
+
+test(t => {
+  performance.clearMarks();
+  performance.clearMeasures();
+
+  var observedEntries = [];
+  var observer = new PerformanceObserver(list => {
+    list.getEntries().forEach(entry => observedEntries.push(entry));
+  });
+  observer.observe({entryTypes: ['mark', 'measure']});
+
+  observer.disconnect();
+
+  assert_equals(observedEntries.length, 0,
+                "User timing entries should be never observed.");
+
+  performance.mark("test-start");
+  performance.mark("test-end");
+  performance.measure("test-measure", "test-start", "test-end");
+
+  assert_equals(performance.getEntriesByType("mark").length, 2);
+  assert_equals(performance.getEntriesByType("measure").length, 1);
+
+  assert_equals(observedEntries.length, 0,
+                "User timing entries should be never observed after disconnecting observer.");
+}, "Nothing should be notified after disconnecting observer");
+
+test(t => {
+  performance.clearMarks();
+  performance.clearMeasures();
+
+  var observedEntryList;
+  var observer = new PerformanceObserver(list => {
+    observedEntryList = list;
+  });
+  observer.observe({entryTypes: ['mark']});
+
+  performance.mark("test");
+  assert_array_equals(observedEntryList.getEntries({"entryType": "mark"}),
+                      performance.getEntriesByType("mark"),
+                      "getEntries with entryType filter should return correct results.");
+
+  assert_array_equals(observedEntryList.getEntries({"name": "test"}),
+                      performance.getEntriesByName("test"),
+                      "getEntries with name filter should return correct results.");
+
+  assert_array_equals(observedEntryList.getEntries({"name": "test",
+                                                    "entryType": "mark"}),
+                      performance.getEntriesByName("test"),
+                      "getEntries with name and entryType filter should return correct results.");
+
+  assert_array_equals(observedEntryList.getEntries({"name": "invalid"}),
+                      [],
+                      "getEntries with non-existent name filter should return an empty array.");
+
+  assert_array_equals(observedEntryList.getEntries({"name": "test",
+                                                    "entryType": "measure"}),
+                      [],
+                      "getEntries with name filter and non-existent entryType should return an empty array.");
+
+  assert_array_equals(observedEntryList.getEntries({"name": "invalid",
+                                                    "entryType": "mark"}),
+                      [],
+                      "getEntries with non-existent name and entryType filter should return an empty array.");
+
+  assert_array_equals(observedEntryList.getEntries({initiatorType: "xmlhttprequest"}),
+                      [],
+                      "getEntries with initiatorType filter should return an empty array.");
+}, "Test for PerformanceObserverEntryList.getEntries");
+
+test(t => {
+  performance.clearMarks();
+  performance.clearMeasures();
+
+  var observedEntryList;
+  var observer = new PerformanceObserver(list => {
+    observedEntryList = list;
+  });
+  observer.observe({entryTypes: ['mark', 'measure']});
+
+  performance.mark("test");
+  assert_array_equals(observedEntryList.getEntriesByType("mark"),
+                      performance.getEntriesByType("mark"));
+
+  performance.measure("test-measure", "test", "test");
+  assert_array_equals(observedEntryList.getEntriesByType("measure"),
+                      performance.getEntriesByType("measure"));
+}, "Test for PerformanceObserverEntryList.getEntriesByType");
+
+test(t => {
+  performance.clearMarks();
+  performance.clearMeasures();
+
+  var observedEntryList;
+  var observer = new PerformanceObserver(list => {
+    observedEntryList = list;
+  });
+  observer.observe({entryTypes: ['mark', 'measure']});
+
+  performance.mark("test");
+  assert_array_equals(observedEntryList.getEntriesByName("test"),
+                      performance.getEntriesByName("test"));
+
+  performance.measure("test-measure", "test", "test");
+  assert_array_equals(observedEntryList.getEntriesByName("test-measure"),
+                      performance.getEntriesByName("test-measure"));
+}, "Test for PerformanceObserverEntryList.getEntriesByName");
+
+test(t => {
+  performance.clearMarks();
+  performance.clearMeasures();
+
+  var observedEntries = [];
+  var observer = new PerformanceObserver(list => {
+    list.getEntries().forEach(entry => observedEntries.push(entry));
+  });
+
+  observer.observe({entryTypes: ['mark', 'measure']});
+  observer.observe({entryTypes: ['mark', 'measure']});
+
+  performance.mark("test-start");
+  performance.mark("test-end");
+  performance.measure("test-measure", "test-start", "test-end");
+
+  assert_equals(observedEntries.length, 3,
+                "Observed user timing entries should have only three entries.");
+}, "Test that invoking observe method twice affects nothing");
+
+test(t => {
+  performance.clearMarks();
+  performance.clearMeasures();
+
+  var observedEntries = [];
+  var observer = new PerformanceObserver(list => {
+    list.getEntries().forEach(entry => observedEntries.push(entry));
+  });
+
+  observer.observe({entryTypes: ['mark', 'measure']});
+  observer.observe({entryTypes: ['mark']});
+
+  performance.mark("test-start");
+  performance.mark("test-end");
+  performance.measure("test-measure", "test-start", "test-end");
+
+  assert_equals(observedEntries.length, 2,
+                "Observed user timing entries should have only two entries.");
+}, "Test that observing filter is replaced by a new filter");
+
+test(t => {
+  performance.clearMarks();
+  performance.clearMeasures();
+
+  var observedEntries = [];
+  var observer = new PerformanceObserver(list => {
+    list.getEntries().forEach(entry => observedEntries.push(entry));
+  });
+
+  observer.observe({entryTypes: ['mark']});
+  observer.observe({entryTypes: ['measure']});
+
+  performance.mark("test-start");
+  performance.mark("test-end");
+  performance.measure("test-measure", "test-start", "test-end");
+
+  assert_equals(observedEntries.length, 1,
+                "Observed user timing entries should have only 1 entries.");
+}, "Test that observing filter is replaced by a new filter");
--- a/dom/canvas/WebGLContext.cpp
+++ b/dom/canvas/WebGLContext.cpp
@@ -667,16 +667,17 @@ PopulateCapFallbackQueue(const SurfaceCa
         nextCaps.depth = false;
         PopulateCapFallbackQueue(nextCaps, out_fallbackCaps);
     }
 }
 
 static bool
 CreateOffscreen(GLContext* gl, const WebGLContextOptions& options,
                 const nsCOMPtr<nsIGfxInfo>& gfxInfo, WebGLContext* webgl,
+                layers::LayersBackend layersBackend,
                 layers::ISurfaceAllocator* surfAllocator)
 {
     SurfaceCaps baseCaps;
 
     baseCaps.color = true;
     baseCaps.alpha = options.alpha;
     baseCaps.antialias = options.antialias;
     baseCaps.depth = options.depth;
@@ -684,17 +685,17 @@ CreateOffscreen(GLContext* gl, const Web
     baseCaps.preserve = options.preserveDrawingBuffer;
     baseCaps.stencil = options.stencil;
 
     if (!baseCaps.alpha)
         baseCaps.premultAlpha = true;
 
     if (gl->IsANGLE() ||
         (gl->GetContextType() == GLContextType::GLX &&
-         gfxPlatform::GetPlatform()->GetCompositorBackend() == LayersBackend::LAYERS_OPENGL))
+         layersBackend == LayersBackend::LAYERS_OPENGL))
     {
         // We can't use no-alpha formats on ANGLE yet because of:
         // https://code.google.com/p/angleproject/issues/detail?id=764
         // GLX only supports GL_RGBA pixmaps as well. Since we can't blit from
         // an RGB FB to GLX's RGBA FB, force RGBA when surface sharing.
         baseCaps.alpha = true;
     }
 
@@ -758,17 +759,18 @@ WebGLContext::CreateOffscreenGL(bool for
                                               CreateContextFlags::NONE;
 
     gl = CreateHeadlessGL(flags, gfxInfo, this);
 
     do {
         if (!gl)
             break;
 
-        if (!CreateOffscreen(gl, mOptions, gfxInfo, this, surfAllocator))
+        if (!CreateOffscreen(gl, mOptions, gfxInfo, this,
+                             GetCompositorBackendType(), surfAllocator))
             break;
 
         if (!InitAndValidateGL())
             break;
 
         return true;
     } while (false);
 
@@ -1287,16 +1289,27 @@ WebGLContext::GetCanvasLayer(nsDisplayLi
     canvasLayer->SetContentFlags(flags);
     canvasLayer->Updated();
 
     mResetLayer = false;
 
     return canvasLayer.forget();
 }
 
+layers::LayersBackend
+WebGLContext::GetCompositorBackendType() const
+{
+    nsIWidget* docWidget = nsContentUtils::WidgetForDocument(mCanvasElement->OwnerDoc());
+    if (docWidget) {
+        layers::LayerManager* layerManager = docWidget->GetLayerManager();
+        return layerManager->GetCompositorBackendType();
+    }
+    return LayersBackend::LAYERS_NONE;
+}
+
 void
 WebGLContext::GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval)
 {
     retval.SetNull();
     if (IsContextLost())
         return;
 
     dom::WebGLContextAttributes& result = retval.SetValue();
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -358,16 +358,18 @@ public:
 
     // WebIDL WebGLRenderingContext API
     dom::HTMLCanvasElement* GetCanvas() const { return mCanvasElement; }
     GLsizei DrawingBufferWidth() const { return IsContextLost() ? 0 : mWidth; }
     GLsizei DrawingBufferHeight() const {
         return IsContextLost() ? 0 : mHeight;
     }
 
+    layers::LayersBackend GetCompositorBackendType() const;
+
     void
     GetContextAttributes(dom::Nullable<dom::WebGLContextAttributes>& retval);
 
     bool IsContextLost() const { return mContextStatus != ContextNotLost; }
     void GetSupportedExtensions(JSContext* cx,
                                 dom::Nullable< nsTArray<nsString> >& retval);
     void GetExtension(JSContext* cx, const nsAString& name,
                       JS::MutableHandle<JSObject*> retval, ErrorResult& rv);
--- a/dom/ipc/ProcessHangMonitor.cpp
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -670,20 +670,20 @@ HangMonitoredProcess::GetHangType(uint32
   switch (mHangData.type()) {
    case HangData::TSlowScriptData:
     *aHangType = SLOW_SCRIPT;
     break;
    case HangData::TPluginHangData:
     *aHangType = PLUGIN_HANG;
     break;
    default:
-    MOZ_ASSERT(false);
+    MOZ_ASSERT_UNREACHABLE("Unexpected HangData type");
     return NS_ERROR_UNEXPECTED;
-    break;
   }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HangMonitoredProcess::GetScriptBrowser(nsIDOMElement** aBrowser)
 {
   MOZ_RELEASE_ASSERT(NS_IsMainThread());
   if (mHangData.type() != HangData::TSlowScriptData) {
--- a/dom/media/DecodedStream.cpp
+++ b/dom/media/DecodedStream.cpp
@@ -192,60 +192,164 @@ DecodedStreamData::SetPlaying(bool aPlay
     mPlaying = aPlaying;
     UpdateStreamBlocking(mStream, !mPlaying);
   }
 }
 
 class OutputStreamListener : public MediaStreamListener {
   typedef MediaStreamListener::MediaStreamGraphEvent MediaStreamGraphEvent;
 public:
-  OutputStreamListener(DecodedStream* aDecodedStream, MediaStream* aStream)
-    : mDecodedStream(aDecodedStream), mStream(aStream) {}
+  explicit OutputStreamListener(OutputStreamData* aOwner) : mOwner(aOwner) {}
 
   void NotifyEvent(MediaStreamGraph* aGraph, MediaStreamGraphEvent event) override
   {
     if (event == EVENT_FINISHED) {
       nsCOMPtr<nsIRunnable> r = NS_NewRunnableMethod(
         this, &OutputStreamListener::DoNotifyFinished);
       aGraph->DispatchToMainThreadAfterStreamStateUpdate(r.forget());
     }
   }
 
   void Forget()
   {
     MOZ_ASSERT(NS_IsMainThread());
-    mDecodedStream = nullptr;
+    mOwner = nullptr;
   }
 
 private:
   void DoNotifyFinished()
   {
     MOZ_ASSERT(NS_IsMainThread());
-    if (mDecodedStream) {
+    if (mOwner) {
       // Remove the finished stream so it won't block the decoded stream.
-      mDecodedStream->Remove(mStream);
+      mOwner->Remove();
     }
   }
 
   // Main thread only
-  DecodedStream* mDecodedStream;
-  nsRefPtr<MediaStream> mStream;
+  OutputStreamData* mOwner;
 };
 
 OutputStreamData::~OutputStreamData()
 {
+  MOZ_ASSERT(NS_IsMainThread());
   mListener->Forget();
+  // Break the connection to the input stream if necessary.
+  if (mPort) {
+    mPort->Destroy();
+  }
+}
+
+void
+OutputStreamData::Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream)
+{
+  mOwner = aOwner;
+  mStream = aStream;
+  mListener = new OutputStreamListener(this);
+  aStream->AddListener(mListener);
+}
+
+void
+OutputStreamData::Connect(MediaStream* aStream)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!mPort, "Already connected?");
+  MOZ_ASSERT(!mStream->IsDestroyed(), "Can't connect a destroyed stream.");
+
+  // The output stream must stay in sync with the input stream, so if
+  // either stream is blocked, we block the other.
+  mPort = mStream->AllocateInputPort(aStream,
+    MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT);
+  // Unblock the output stream now. The input stream is responsible for
+  // controlling blocking from now on.
+  mStream->ChangeExplicitBlockerCount(-1);
+}
+
+bool
+OutputStreamData::Disconnect()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // During cycle collection, DOMMediaStream can be destroyed and send
+  // its Destroy message before this decoder is destroyed. So we have to
+  // be careful not to send any messages after the Destroy().
+  if (mStream->IsDestroyed()) {
+    return false;
+  }
+
+  // Disconnect the existing port if necessary.
+  if (mPort) {
+    mPort->Destroy();
+    mPort = nullptr;
+  }
+  // Block the stream again. It will be unlocked when connecting
+  // to the input stream.
+  mStream->ChangeExplicitBlockerCount(1);
+  return true;
 }
 
 void
-OutputStreamData::Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream)
+OutputStreamData::Remove()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mOwner->Remove(mStream);
+}
+
+void
+OutputStreamManager::Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  // Ensure that aStream finishes the moment mDecodedStream does.
+  if (aFinishWhenEnded) {
+    aStream->SetAutofinish(true);
+  }
+
+  OutputStreamData* p = mStreams.AppendElement();
+  p->Init(this, aStream);
+
+  // Connect to the input stream if we have one. Otherwise the output stream
+  // will be connected in Connect().
+  if (mInputStream) {
+    p->Connect(mInputStream);
+  }
+}
+
+void
+OutputStreamManager::Remove(MediaStream* aStream)
 {
-  mStream = aStream;
-  mListener = new OutputStreamListener(aDecodedStream, aStream);
-  aStream->AddListener(mListener);
+  MOZ_ASSERT(NS_IsMainThread());
+  for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
+    if (mStreams[i].Equals(aStream)) {
+      mStreams.RemoveElementAt(i);
+      break;
+    }
+  }
+}
+
+void
+OutputStreamManager::Connect(MediaStream* aStream)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mInputStream = aStream;
+  for (auto&& os : mStreams) {
+    os.Connect(aStream);
+  }
+}
+
+void
+OutputStreamManager::Disconnect()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  mInputStream = nullptr;
+  for (int32_t i = mStreams.Length() - 1; i >= 0; --i) {
+    if (!mStreams[i].Disconnect()) {
+      // Probably the DOMMediaStream was GCed. Clean up.
+      mStreams.RemoveElementAt(i);
+    }
+  }
 }
 
 DecodedStream::DecodedStream(MediaQueue<MediaData>& aAudioQueue,
                              MediaQueue<MediaData>& aVideoQueue)
   : mMonitor("DecodedStream::mMonitor")
   , mPlaying(false)
   , mVolume(1.0)
   , mAudioQueue(aAudioQueue)
@@ -284,37 +388,17 @@ DecodedStream::DestroyData()
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   // Avoid the redundant blocking to output stream.
   if (!mData) {
     return;
   }
 
-  // All streams are having their SourceMediaStream disconnected, so they
-  // need to be explicitly blocked again.
-  auto& outputStreams = OutputStreams();
-  for (int32_t i = outputStreams.Length() - 1; i >= 0; --i) {
-    OutputStreamData& os = outputStreams[i];
-    // Explicitly remove all existing ports.
-    // This is not strictly necessary but it's good form.
-    MOZ_ASSERT(os.mPort, "Double-delete of the ports!");
-    os.mPort->Destroy();
-    os.mPort = nullptr;
-    // During cycle collection, nsDOMMediaStream can be destroyed and send
-    // its Destroy message before this decoder is destroyed. So we have to
-    // be careful not to send any messages after the Destroy().
-    if (os.mStream->IsDestroyed()) {
-      // Probably the DOM MediaStream was GCed. Clean up.
-      outputStreams.RemoveElementAt(i);
-    } else {
-      os.mStream->ChangeExplicitBlockerCount(1);
-    }
-  }
-
+  mOutputStreamManager.Disconnect();
   mData = nullptr;
 }
 
 void
 DecodedStream::RecreateData()
 {
   nsRefPtr<DecodedStream> self = this;
   nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction([self] () -> void {
@@ -323,113 +407,61 @@ DecodedStream::RecreateData()
   AbstractThread::MainThread()->Dispatch(r.forget());
 }
 
 void
 DecodedStream::RecreateData(MediaStreamGraph* aGraph)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  MOZ_ASSERT((aGraph && !mData && OutputStreams().IsEmpty()) || // first time
+  MOZ_ASSERT((aGraph && !mData && !HasConsumers()) || // first time
              (!aGraph && mData)); // 2nd time and later
 
   if (!aGraph) {
     aGraph = mData->mStream->Graph();
   }
   auto source = aGraph->CreateSourceStream(nullptr);
   DestroyData();
   mData.reset(new DecodedStreamData(source, mPlaying));
 
-  // Note that the delay between removing ports in DestroyDecodedStream
+  // Note that the delay between removing ports in DestroyData
   // and adding new ones won't cause a glitch since all graph operations
   // between main-thread stable states take effect atomically.
-  auto& outputStreams = OutputStreams();
-  for (int32_t i = outputStreams.Length() - 1; i >= 0; --i) {
-    OutputStreamData& os = outputStreams[i];
-    MOZ_ASSERT(!os.mStream->IsDestroyed(), "Should've been removed in DestroyData()");
-    Connect(&os);
-  }
-}
-
-nsTArray<OutputStreamData>&
-DecodedStream::OutputStreams()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  GetReentrantMonitor().AssertCurrentThreadIn();
-  return mOutputStreams;
+  mOutputStreamManager.Connect(mData->mStream);
 }
 
 bool
 DecodedStream::HasConsumers() const
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-  return mOutputStreams.IsEmpty();
+  return !mOutputStreamManager.IsEmpty();
 }
 
 ReentrantMonitor&
 DecodedStream::GetReentrantMonitor() const
 {
   return mMonitor;
 }
 
 void
-DecodedStream::Connect(OutputStreamData* aStream)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  GetReentrantMonitor().AssertCurrentThreadIn();
-  NS_ASSERTION(!aStream->mPort, "Already connected?");
-
-  // The output stream must stay in sync with the decoded stream, so if
-  // either stream is blocked, we block the other.
-  aStream->mPort = aStream->mStream->AllocateInputPort(mData->mStream,
-      MediaInputPort::FLAG_BLOCK_INPUT | MediaInputPort::FLAG_BLOCK_OUTPUT);
-  // Unblock the output stream now. While it's connected to DecodedStream,
-  // DecodedStream is responsible for controlling blocking.
-  aStream->mStream->ChangeExplicitBlockerCount(-1);
-}
-
-void
 DecodedStream::Connect(ProcessedMediaStream* aStream, bool aFinishWhenEnded)
 {
   MOZ_ASSERT(NS_IsMainThread());
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
 
   if (!mData) {
     RecreateData(aStream->Graph());
   }
 
-  OutputStreamData* os = OutputStreams().AppendElement();
-  os->Init(this, aStream);
-  Connect(os);
-  if (aFinishWhenEnded) {
-    // Ensure that aStream finishes the moment mDecodedStream does.
-    aStream->SetAutofinish(true);
-  }
+  mOutputStreamManager.Add(aStream, aFinishWhenEnded);
 }
 
 void
 DecodedStream::Remove(MediaStream* aStream)
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
-
-  auto& streams = OutputStreams();
-  for (int32_t i = streams.Length() - 1; i >= 0; --i) {
-    auto& os = streams[i];
-    MediaStream* p = os.mStream.get();
-    if (p == aStream) {
-      if (os.mPort) {
-        os.mPort->Destroy();
-        os.mPort = nullptr;
-      }
-      streams.RemoveElementAt(i);
-      break;
-    }
-  }
+  mOutputStreamManager.Remove(aStream);
 }
 
 void
 DecodedStream::SetPlaying(bool aPlaying)
 {
   ReentrantMonitorAutoEnter mon(GetReentrantMonitor());
   mPlaying = aPlaying;
   if (mData) {
--- a/dom/media/DecodedStream.h
+++ b/dom/media/DecodedStream.h
@@ -22,35 +22,78 @@ namespace mozilla {
 
 class DecodedStream;
 class DecodedStreamData;
 class MediaData;
 class MediaInputPort;
 class MediaStream;
 class MediaStreamGraph;
 class OutputStreamListener;
+class OutputStreamManager;
 class ProcessedMediaStream;
 class ReentrantMonitor;
 
 template <class T> class MediaQueue;
 
 namespace layers {
 class Image;
 } // namespace layers
 
 class OutputStreamData {
 public:
   ~OutputStreamData();
-  void Init(DecodedStream* aDecodedStream, ProcessedMediaStream* aStream);
+  void Init(OutputStreamManager* aOwner, ProcessedMediaStream* aStream);
+
+  // Connect mStream to the input stream.
+  void Connect(MediaStream* aStream);
+  // Disconnect mStream from its input stream.
+  // Return false is mStream is already destroyed, otherwise true.
+  bool Disconnect();
+  // Called by OutputStreamListener to remove self from the output streams
+  // managed by OutputStreamManager.
+  void Remove();
+  // Return true if aStream points to the same object as mStream.
+  // Used by OutputStreamManager to remove an output stream.
+  bool Equals(MediaStream* aStream)
+  {
+    return mStream == aStream;
+  }
+
+private:
+  OutputStreamManager* mOwner;
   nsRefPtr<ProcessedMediaStream> mStream;
-  // mPort connects DecodedStreamData::mStream to our mStream.
+  // mPort connects our mStream to an input stream.
   nsRefPtr<MediaInputPort> mPort;
   nsRefPtr<OutputStreamListener> mListener;
 };
 
+class OutputStreamManager {
+public:
+  // Add the output stream to the collection.
+  void Add(ProcessedMediaStream* aStream, bool aFinishWhenEnded);
+  // Remove the output stream from the collection.
+  void Remove(MediaStream* aStream);
+  // Return true if the collection empty.
+  bool IsEmpty() const
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    return mStreams.IsEmpty();
+  }
+  // Connect all output streams in the collection to the input stream.
+  void Connect(MediaStream* aStream);
+  // Disconnect all output streams from the input stream.
+  void Disconnect();
+
+private:
+  // Keep the input stream so we can connect the output streams that
+  // are added after Connect().
+  nsRefPtr<MediaStream> mInputStream;
+  nsTArray<OutputStreamData> mStreams;
+};
+
 class DecodedStream {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DecodedStream);
 public:
   DecodedStream(MediaQueue<MediaData>& aAudioQueue,
                 MediaQueue<MediaData>& aVideoQueue);
 
   // Mimic MDSM::StartAudioThread.
   // Must be called before any calls to SendData().
@@ -79,26 +122,24 @@ public:
   void SendData();
 
 protected:
   virtual ~DecodedStream();
 
 private:
   ReentrantMonitor& GetReentrantMonitor() const;
   void RecreateData(MediaStreamGraph* aGraph);
-  void Connect(OutputStreamData* aStream);
-  nsTArray<OutputStreamData>& OutputStreams();
   void InitTracks();
   void AdvanceTracks();
   void SendAudio(double aVolume, bool aIsSameOrigin);
   void SendVideo(bool aIsSameOrigin);
 
   UniquePtr<DecodedStreamData> mData;
   // Data about MediaStreams that are being fed by the decoder.
-  nsTArray<OutputStreamData> mOutputStreams;
+  OutputStreamManager mOutputStreamManager;
 
   // TODO: This is a temp solution to get rid of decoder monitor on the main
   // thread in MDSM::AddOutputStream and MDSM::RecreateDecodedStream as
   // required by bug 1146482. DecodedStream needs to release monitor before
   // calling back into MDSM functions in order to prevent deadlocks.
   //
   // Please move all capture-stream related code from MDSM into DecodedStream
   // and apply "dispatch + mirroring" to get rid of this monitor in the future.
--- a/dom/media/MediaDecoder.cpp
+++ b/dom/media/MediaDecoder.cpp
@@ -614,19 +614,19 @@ nsresult MediaDecoder::Seek(double aTime
   }
   return NS_OK;
 }
 
 void MediaDecoder::CallSeek(const SeekTarget& aTarget)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mSeekRequest.DisconnectIfExists();
-  mSeekRequest.Begin(ProxyMediaCall(mDecoderStateMachine->OwnerThread(),
-                                    mDecoderStateMachine.get(), __func__,
-                                    &MediaDecoderStateMachine::Seek, aTarget)
+  mSeekRequest.Begin(InvokeAsync(mDecoderStateMachine->OwnerThread(),
+                                 mDecoderStateMachine.get(), __func__,
+                                 &MediaDecoderStateMachine::Seek, aTarget)
     ->Then(AbstractThread::MainThread(), __func__, this,
            &MediaDecoder::OnSeekResolved, &MediaDecoder::OnSeekRejected));
 }
 
 double MediaDecoder::GetCurrentTime()
 {
   MOZ_ASSERT(NS_IsMainThread());
   return mLogicalPosition;
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -714,18 +714,18 @@ MediaDecoderStateMachine::OnNotDecoded(M
   }
 
   // If the decoder is waiting for data, we tell it to call us back when the
   // data arrives.
   if (aReason == MediaDecoderReader::WAITING_FOR_DATA) {
     MOZ_ASSERT(mReader->IsWaitForDataSupported(),
                "Readers that send WAITING_FOR_DATA need to implement WaitForData");
     nsRefPtr<MediaDecoderStateMachine> self = this;
-    WaitRequestRef(aType).Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
-                                               &MediaDecoderReader::WaitForData, aType)
+    WaitRequestRef(aType).Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+                                            &MediaDecoderReader::WaitForData, aType)
       ->Then(OwnerThread(), __func__,
              [self] (MediaData::Type aType) -> void {
                ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
                self->WaitRequestRef(aType).Complete();
                self->DispatchDecodeTasksIfNeeded();
              },
              [self] (WaitForDataRejectValue aRejection) -> void {
                ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
@@ -1272,17 +1272,17 @@ void MediaDecoderStateMachine::Shutdown(
 
   // Shut down our start time rendezvous.
   if (mStartTimeRendezvous) {
     mStartTimeRendezvous->Destroy();
   }
 
   // Put a task in the decode queue to shutdown the reader.
   // the queue to spin down.
-  ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::Shutdown)
+  InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__, &MediaDecoderReader::Shutdown)
     ->Then(OwnerThread(), __func__, this,
            &MediaDecoderStateMachine::FinishShutdown,
            &MediaDecoderStateMachine::FinishShutdown);
   DECODER_LOG("Shutdown started");
 }
 
 void MediaDecoderStateMachine::StartDecoding()
 {
@@ -1573,19 +1573,19 @@ MediaDecoderStateMachine::InitiateSeek()
         mCurrentSeek.mTarget.mEventVisibility);
   AbstractThread::MainThread()->Dispatch(startEvent.forget());
 
   // Reset our state machine and decoding pipeline before seeking.
   Reset();
 
   // Do the seek.
   nsRefPtr<MediaDecoderStateMachine> self = this;
-  mSeekRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
-                                    &MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
-                                    Duration().ToMicroseconds())
+  mSeekRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+                                 &MediaDecoderReader::Seek, mCurrentSeek.mTarget.mTime,
+                                 Duration().ToMicroseconds())
     ->Then(OwnerThread(), __func__,
            [self] (int64_t) -> void {
              ReentrantMonitorAutoEnter mon(self->mDecoder->GetReentrantMonitor());
              self->mSeekRequest.Complete();
              // We must decode the first samples of active streams, so we can determine
              // the new stream time. So dispatch tasks to do that.
              self->mDecodeToSeekTarget = true;
              self->DispatchDecodeTasksIfNeeded();
@@ -1643,25 +1643,25 @@ MediaDecoderStateMachine::RequestAudioDa
 {
   MOZ_ASSERT(OnTaskQueue());
   AssertCurrentThreadInMonitor();
 
   SAMPLE_LOG("Queueing audio task - queued=%i, decoder-queued=%o",
              AudioQueue().GetSize(), mReader->SizeOfAudioQueueInFrames());
 
   if (mSentFirstFrameLoadedEvent) {
-    mAudioDataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(),
-                                           __func__, &MediaDecoderReader::RequestAudioData)
+    mAudioDataRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(),
+                                        __func__, &MediaDecoderReader::RequestAudioData)
       ->Then(OwnerThread(), __func__, this,
              &MediaDecoderStateMachine::OnAudioDecoded,
              &MediaDecoderStateMachine::OnAudioNotDecoded));
   } else {
     mAudioDataRequest.Begin(
-      ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
-                     &MediaDecoderReader::RequestAudioData)
+      InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+                  &MediaDecoderReader::RequestAudioData)
       ->Then(OwnerThread(), __func__, mStartTimeRendezvous.get(),
              &StartTimeRendezvous::ProcessFirstSample<AudioDataPromise>,
              &StartTimeRendezvous::FirstSampleRejected<AudioData>)
       ->CompletionPromise()
       ->Then(OwnerThread(), __func__, this,
              &MediaDecoderStateMachine::OnAudioDecoded,
              &MediaDecoderStateMachine::OnAudioNotDecoded)
     );
@@ -1727,27 +1727,27 @@ MediaDecoderStateMachine::RequestVideoDa
     static_cast<uint32_t>(VideoQueue().GetSize()) <= SCARCE_VIDEO_QUEUE_SIZE;
 
   SAMPLE_LOG("Queueing video task - queued=%i, decoder-queued=%o, skip=%i, time=%lld",
              VideoQueue().GetSize(), mReader->SizeOfVideoQueueInFrames(), skipToNextKeyFrame,
              currentTime);
 
   if (mSentFirstFrameLoadedEvent) {
     mVideoDataRequest.Begin(
-      ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
-                     &MediaDecoderReader::RequestVideoData,
-                     skipToNextKeyFrame, currentTime, forceDecodeAhead)
+      InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+                  &MediaDecoderReader::RequestVideoData,
+                  skipToNextKeyFrame, currentTime, forceDecodeAhead)
       ->Then(OwnerThread(), __func__, this,
              &MediaDecoderStateMachine::OnVideoDecoded,
              &MediaDecoderStateMachine::OnVideoNotDecoded));
   } else {
     mVideoDataRequest.Begin(
-      ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
-                     &MediaDecoderReader::RequestVideoData,
-                     skipToNextKeyFrame, currentTime, forceDecodeAhead)
+      InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+                  &MediaDecoderReader::RequestVideoData,
+                  skipToNextKeyFrame, currentTime, forceDecodeAhead)
       ->Then(OwnerThread(), __func__, mStartTimeRendezvous.get(),
              &StartTimeRendezvous::ProcessFirstSample<VideoDataPromise>,
              &StartTimeRendezvous::FirstSampleRejected<VideoData>)
       ->CompletionPromise()
       ->Then(OwnerThread(), __func__, this,
              &MediaDecoderStateMachine::OnVideoDecoded,
              &MediaDecoderStateMachine::OnVideoNotDecoded));
   }
@@ -2273,18 +2273,18 @@ nsresult MediaDecoderStateMachine::RunSt
       return NS_OK;
     }
 
     case DECODER_STATE_DECODING_METADATA: {
       if (!mMetadataRequest.Exists()) {
         DECODER_LOG("Dispatching AsyncReadMetadata");
         // Set mode to METADATA since we are about to read metadata.
         mResource->SetReadMode(MediaCacheStream::MODE_METADATA);
-        mMetadataRequest.Begin(ProxyMediaCall(DecodeTaskQueue(), mReader.get(), __func__,
-                                              &MediaDecoderReader::AsyncReadMetadata)
+        mMetadataRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+                                           &MediaDecoderReader::AsyncReadMetadata)
           ->Then(OwnerThread(), __func__, this,
                  &MediaDecoderStateMachine::OnMetadataRead,
                  &MediaDecoderStateMachine::OnMetadataNotRead));
 
       }
       return NS_OK;
     }
 
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -275,17 +275,22 @@ MediaFormatReader::AsyncReadMetadata()
 
   if (mInitDone) {
     // We are returning from dormant.
     if (!EnsureDecodersCreated()) {
       mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__);
       return p;
     }
     MOZ_ASSERT(!mDecodersInitRequest.Exists());
-    EnsureDecodersInitialized();
+    if (EnsureDecodersInitialized()) {
+      nsRefPtr<MetadataHolder> metadata = new MetadataHolder();
+      metadata->mInfo = mInfo;
+      metadata->mTags = nullptr;
+      mMetadataPromise.Resolve(metadata, __func__);
+    }
     return p;
   }
 
   mDemuxerInitRequest.Begin(mDemuxer->Init()
                        ->Then(OwnerThread(), __func__, this,
                               &MediaFormatReader::OnDemuxerInitDone,
                               &MediaFormatReader::OnDemuxerInitFailed));
   return p;
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -9,16 +9,17 @@
 #include "MediaFormatReader.h"
 #include "MP4Demuxer.h"
 #include "mozilla/Preferences.h"
 #include "nsCharSeparatedTokenizer.h"
 #ifdef MOZ_EME
 #include "mozilla/CDMProxy.h"
 #endif
 #include "mozilla/Logging.h"
+#include "nsMimeTypes.h"
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsIGfxInfo.h"
 #include "AndroidBridge.h"
 #endif
@@ -136,16 +137,22 @@ MP4Decoder::CanHandleMediaType(const nsA
   if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) {
     return MP4Decoder::CanCreateAACDecoder() &&
            (aCodecs.IsEmpty() ||
             IsSupportedAudioCodec(aCodecs,
                                   aOutContainsAAC,
                                   aOutContainsMP3));
   }
 
+#ifdef MOZ_GONK_MEDIACODEC
+  if (aType.EqualsASCII(VIDEO_3GPP)) {
+    return Preferences::GetBool("media.fragmented-mp4.gonk.enabled", false);
+  }
+#endif
+
   if (!aType.EqualsASCII("video/mp4") ||
       !MP4Decoder::CanCreateH264Decoder()) {
     return false;
   }
 
   // Verify that all the codecs specifed are ones that we expect that
   // we can play.
   nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
--- a/dom/media/gstreamer/GStreamerReader.cpp
+++ b/dom/media/gstreamer/GStreamerReader.cpp
@@ -868,31 +868,35 @@ GStreamerReader::Seek(int64_t aTarget, i
   LOG(LogLevel::Debug, "seek completed");
 
   return SeekPromise::CreateAndResolve(aTarget, __func__);
 }
 
 media::TimeIntervals GStreamerReader::GetBuffered()
 {
   MOZ_ASSERT(OnTaskQueue());
+  if (!HaveStartTime()) {
+    return media::TimeIntervals();
+  }
   media::TimeIntervals buffered;
   if (!mInfo.HasValidMedia()) {
     return buffered;
   }
 
 #if GST_VERSION_MAJOR == 0
   GstFormat format = GST_FORMAT_TIME;
 #endif
   AutoPinned<MediaResource> resource(mDecoder->GetResource());
   nsTArray<MediaByteRange> ranges;
   resource->GetCachedRanges(ranges);
 
-  if (resource->IsDataCachedToEndOfResource(0) && mDuration.Ref().isSome()) {
+  if (resource->IsDataCachedToEndOfResource(0)) {
     /* fast path for local or completely cached files */
-    gint64 duration = mDuration.Ref().ref().ToMicroseconds();
+    gint64 duration =
+       mDuration.Ref().refOr(media::TimeUnit::FromMicroseconds(0)).ToMicroseconds();
     LOG(LogLevel::Debug, "complete range [0, %f] for [0, %li]",
         (double) duration / GST_MSECOND, GetDataLength());
     buffered +=
       media::TimeInterval(media::TimeUnit::FromMicroseconds(0),
                           media::TimeUnit::FromMicroseconds(duration));
     return buffered;
   }
 
--- a/dom/media/gtest/TestMP4Demuxer.cpp
+++ b/dom/media/gtest/TestMP4Demuxer.cpp
@@ -1,17 +1,17 @@
 /* -*- 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 "gtest/gtest.h"
 #include "MP4Demuxer.h"
 #include "MP4Stream.h"
-#include "MozPromise.h"
+#include "mozilla/MozPromise.h"
 #include "MediaDataDemuxer.h"
 #include "mozilla/SharedThreadPool.h"
 #include "mozilla/TaskQueue.h"
 #include "mozilla/ArrayUtils.h"
 #include "MockMediaResource.h"
 #include "VideoUtils.h"
 
 using namespace mozilla;
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -30,18 +30,18 @@ MediaSourceDemuxer::MediaSourceDemuxer()
   , mMonitor("MediaSourceDemuxer")
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 nsRefPtr<MediaSourceDemuxer::InitPromise>
 MediaSourceDemuxer::Init()
 {
-  return ProxyMediaCall(GetTaskQueue(), this, __func__,
-                        &MediaSourceDemuxer::AttemptInit);
+  return InvokeAsync(GetTaskQueue(), this, __func__,
+                     &MediaSourceDemuxer::AttemptInit);
 }
 
 nsRefPtr<MediaSourceDemuxer::InitPromise>
 MediaSourceDemuxer::AttemptInit()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   if (ScanSourceBuffersForContent()) {
@@ -267,26 +267,26 @@ MediaSourceTrackDemuxer::GetInfo() const
 {
   return mParent->GetTrackInfo(mType)->Clone();
 }
 
 nsRefPtr<MediaSourceTrackDemuxer::SeekPromise>
 MediaSourceTrackDemuxer::Seek(media::TimeUnit aTime)
 {
   MOZ_ASSERT(mParent, "Called after BreackCycle()");
-  return ProxyMediaCall(mParent->GetTaskQueue(), this, __func__,
-                        &MediaSourceTrackDemuxer::DoSeek, aTime);
+  return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
+                     &MediaSourceTrackDemuxer::DoSeek, aTime);
 }
 
 nsRefPtr<MediaSourceTrackDemuxer::SamplesPromise>
 MediaSourceTrackDemuxer::GetSamples(int32_t aNumSamples)
 {
   MOZ_ASSERT(mParent, "Called after BreackCycle()");
-  return ProxyMediaCall(mParent->GetTaskQueue(), this, __func__,
-                        &MediaSourceTrackDemuxer::DoGetSamples, aNumSamples);
+  return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
+                     &MediaSourceTrackDemuxer::DoGetSamples, aNumSamples);
 }
 
 void
 MediaSourceTrackDemuxer::Reset()
 {
   MOZ_ASSERT(mParent, "Called after BreackCycle()");
   nsRefPtr<MediaSourceTrackDemuxer> self = this;
   nsCOMPtr<nsIRunnable> task =
@@ -307,19 +307,19 @@ MediaSourceTrackDemuxer::GetNextRandomAc
   MonitorAutoLock mon(mMonitor);
   *aTime = mNextRandomAccessPoint;
   return NS_OK;
 }
 
 nsRefPtr<MediaSourceTrackDemuxer::SkipAccessPointPromise>
 MediaSourceTrackDemuxer::SkipToNextRandomAccessPoint(media::TimeUnit aTimeThreshold)
 {
-  return ProxyMediaCall(mParent->GetTaskQueue(), this, __func__,
-                        &MediaSourceTrackDemuxer::DoSkipToNextRandomAccessPoint,
-                        aTimeThreshold);
+  return InvokeAsync(mParent->GetTaskQueue(), this, __func__,
+                     &MediaSourceTrackDemuxer::DoSkipToNextRandomAccessPoint,
+                     aTimeThreshold);
 }
 
 int64_t
 MediaSourceTrackDemuxer::GetEvictionOffset(media::TimeUnit aTime)
 {
   // Unused.
   return 0;
 }
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -263,19 +263,19 @@ TrackBuffer::BufferAppend()
   if (decoders.Length()) {
     // We're going to have to wait for the decoder to initialize, the promise
     // will be resolved once initialization completes.
     return p;
   }
 
   nsRefPtr<TrackBuffer> self = this;
 
-  ProxyMediaCall(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
-                 &TrackBuffer::UpdateBufferedRanges,
-                 mLastAppendRange, /* aNotifyParent */ true)
+  InvokeAsync(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
+              &TrackBuffer::UpdateBufferedRanges,
+              mLastAppendRange, /* aNotifyParent */ true)
       ->Then(mParentDecoder->GetReader()->OwnerThread(), __func__,
              [self] {
                self->mInitializationPromise.ResolveIfExists(self->HasInitSegment(), __func__);
              },
              [self] (nsresult) { MOZ_CRASH("Never called."); });
 
   return p;
 }
@@ -937,19 +937,19 @@ TrackBuffer::CompleteInitializeDecoder(S
 
   // Tell our reader that we have more data to ensure that playback starts if
   // required when data is appended.
   NotifyTimeRangesChanged();
 
   MSE_DEBUG("Reader %p activated",
             aDecoder->GetReader());
   nsRefPtr<TrackBuffer> self = this;
-  ProxyMediaCall(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
-                 &TrackBuffer::UpdateBufferedRanges,
-                 Interval<int64_t>(), /* aNotifyParent */ true)
+  InvokeAsync(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
+              &TrackBuffer::UpdateBufferedRanges,
+              Interval<int64_t>(), /* aNotifyParent */ true)
       ->Then(mParentDecoder->GetReader()->OwnerThread(), __func__,
              [self] {
                self->mInitializationPromise.ResolveIfExists(self->HasInitSegment(), __func__);
              },
              [self] (nsresult) { MOZ_CRASH("Never called."); });
 }
 
 bool
@@ -1269,19 +1269,19 @@ TrackBuffer::RangeRemoval(TimeUnit aStar
   }
 
   RemoveEmptyDecoders(decoders);
 
   nsRefPtr<RangeRemovalPromise> p = mRangeRemovalPromise.Ensure(__func__);
 
   // Make sure our buffered ranges got updated before resolving promise.
   nsRefPtr<TrackBuffer> self = this;
-  ProxyMediaCall(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
-                 &TrackBuffer::UpdateBufferedRanges,
-                 Interval<int64_t>(), /* aNotifyParent */ false)
+  InvokeAsync(mParentDecoder->GetReader()->OwnerThread(), this, __func__,
+              &TrackBuffer::UpdateBufferedRanges,
+              Interval<int64_t>(), /* aNotifyParent */ false)
     ->Then(mParentDecoder->GetReader()->OwnerThread(), __func__,
            [self] {
              self->mRangeRemovalPromise.ResolveIfExists(true, __func__);
            },
            [self] (nsresult) { MOZ_CRASH("Never called."); });
 
   return p;
 }
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -151,18 +151,18 @@ TrackBuffersManager::AppendIncomingBuffe
 }
 
 nsRefPtr<TrackBuffersManager::AppendPromise>
 TrackBuffersManager::BufferAppend()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("");
 
-  return ProxyMediaCall(GetTaskQueue(), this,
-                        __func__, &TrackBuffersManager::InitSegmentParserLoop);
+  return InvokeAsync(GetTaskQueue(), this,
+                     __func__, &TrackBuffersManager::InitSegmentParserLoop);
 }
 
 // Abort any pending AppendData.
 // We don't really care about really aborting our inner loop as by spec the
 // process is happening asynchronously, as such where and when we would abort is
 // non-deterministic. The SourceBuffer also makes sure BufferAppend
 // isn't called should the appendBuffer be immediately aborted.
 // We do however want to ensure that no new task will be dispatched on our task
@@ -206,19 +206,19 @@ TrackBuffersManager::ResetParserState()
 nsRefPtr<TrackBuffersManager::RangeRemovalPromise>
 TrackBuffersManager::RangeRemoval(TimeUnit aStart, TimeUnit aEnd)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MSE_DEBUG("From %.2f to %.2f", aStart.ToSeconds(), aEnd.ToSeconds());
 
   mEnded = false;
 
-  return ProxyMediaCall(GetTaskQueue(), this, __func__,
-                        &TrackBuffersManager::CodedFrameRemovalWithPromise,
-                        TimeInterval(aStart, aEnd));
+  return InvokeAsync(GetTaskQueue(), this, __func__,
+                     &TrackBuffersManager::CodedFrameRemovalWithPromise,
+                     TimeInterval(aStart, aEnd));
 }
 
 TrackBuffersManager::EvictDataResult
 TrackBuffersManager::EvictData(TimeUnit aPlaybackTime,
                                uint32_t aThreshold,
                                TimeUnit* aBufferStartTime)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -147,17 +147,16 @@ EXPORTS += [
     'VideoFrameContainer.h',
     'VideoSegment.h',
     'VideoUtils.h',
     'VorbisUtils.h',
 ]
 
 EXPORTS.mozilla += [
     'MediaManager.h',
-    'MozPromise.h',
     'StateMirroring.h',
     'StateWatching.h',
 ]
 
 EXPORTS.mozilla.media.webrtc += [
     'webrtc/WebrtcGlobal.h',
 ]
 
--- a/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp
+++ b/dom/media/platforms/agnostic/gmp/MediaDataDecoderProxy.cpp
@@ -29,18 +29,18 @@ MediaDataDecoderProxy::InternalInit()
   return mProxyDecoder->Init();
 }
 
 nsRefPtr<MediaDataDecoder::InitPromise>
 MediaDataDecoderProxy::Init()
 {
   MOZ_ASSERT(!mIsShutdown);
 
-  return ProxyMediaCall(mProxyThreadWrapper, this, __func__,
-                        &MediaDataDecoderProxy::InternalInit);
+  return InvokeAsync(mProxyThreadWrapper, this, __func__,
+                     &MediaDataDecoderProxy::InternalInit);
 }
 
 nsresult
 MediaDataDecoderProxy::Input(MediaRawData* aSample)
 {
   MOZ_ASSERT(!IsOnProxyThread());
   MOZ_ASSERT(!mIsShutdown);
 
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -732,16 +732,18 @@ skip-if = appname == "seamonkey"
 [test_playback.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_playback_errors.html]
 skip-if = toolkit == 'gonk' # bug 1128845
 [test_playback_rate.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #bug 845162
 [test_playback_rate_playpause.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
+[test_playback_reactivate.html]
+#skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_played.html]
 [test_preload_actions.html]
 [test_preload_attribute.html]
 [test_preload_suspend.html]
 skip-if = true # bug 493692
 [test_progress.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_reactivate.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_playback_reactivate.html
@@ -0,0 +1,98 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test playback with dormant of media files that should play OK</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  <script type="text/javascript" src="manifest.js"></script>
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+/* This testcase wants to test a video element's playback is not break
+   by dormant.
+   When the metadata is loaded, we remove the video element to trigger dormant.
+   Then set a timer to append the video element back and play it.
+   Test pass if the video plays to the end.
+*/
+
+// longer timeout for slow platforms
+if (isSlowPlatform()) {
+  SimpleTest.requestLongerTimeout(1.5);
+  SimpleTest.requestCompleteLog();
+}
+
+var manager = new MediaTestManager;
+
+function startTest(test, token) {
+  var v = document.createElement('video');
+  v.preload = "metadata";
+  v.token = token;
+
+  var handler = {
+    "ontimeout": function() {
+      Log(token, "timed out: ended=" + v.seenEnded + ", suspend=" + v.seenSuspend);
+    }
+  };
+  manager.started(token, handler);
+
+  v.src = test.name;
+  v.name = test.name;
+
+  var check = function(test, v) { return function() {
+    is(test.name, v.name, test.name + ": Name should match #1");
+    Log(v.token, "removeChild: " + v.name);
+    document.body.removeChild(v);
+    var appendAndPlayElement = function() {
+      Log(v.token, "appendChild: " + v.name);
+      document.body.appendChild(v);
+      Log(v.token, "Element play: " + v.name);
+      v.play();
+    }
+    setTimeout(appendAndPlayElement, 2000);
+  }}(test, v);
+
+  var finish = function() {
+    v.finished = true;
+    removeNodeAndSource(v);
+    manager.finished(v.token);
+  }
+
+  var checkEnded = function(test, v) { return function() {
+    is(test.name, v.name, test.name + ": Name should match #2");
+    checkMetadata(test.name, v, test);
+    is(v.readyState, v.HAVE_CURRENT_DATA, test.name + " checking readyState");
+    ok(v.readyState != v.NETWORK_LOADED, test.name + " shouldn't report NETWORK_LOADED");
+    ok(v.ended, test.name + " checking playback has ended");
+
+    finish();
+  }}(test, v);
+
+
+  v.addEventListener("loadedmetadata", check, false);
+  v.addEventListener("ended", checkEnded, false);
+
+  document.body.appendChild(v);
+
+  // Debug timeouts on slow platforms.
+  if (isSlowPlatform()) {
+    var events = ["suspend", "play", "canplay", "canplaythrough", "loadstart", "loadedmetadata",
+                  "loadeddata", "playing", "ended", "error", "stalled", "emptied", "abort",
+                  "waiting", "pause"];
+    function logEvent(e) {
+      var v = e.target;
+      Log(e.target.token, "got " + e.type);
+    }
+    events.forEach(function(e) {
+      v.addEventListener(e, logEvent, false);
+    });
+  }
+}
+
+manager.runTests(gSmallTests, startTest);
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1033,17 +1033,17 @@ int32_t
 
   return (int32_t)count;
 }
 
 NPError
 _destroystream(NPP npp, NPStream *pstream, NPError reason)
 {
   if (!NS_IsMainThread()) {
-    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_write called from the wrong thread\n"));
+    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_destroystream called from the wrong thread\n"));
     return NPERR_INVALID_PARAM;
   }
   NPN_PLUGIN_LOG(PLUGIN_LOG_NORMAL,
                  ("NPN_DestroyStream: npp=%p, url=%s, reason=%d\n", (void*)npp,
                   pstream->url, (int)reason));
 
   if (!npp)
     return NPERR_INVALID_INSTANCE_ERROR;
@@ -2619,16 +2619,21 @@ void
     NS_DispatchToMainThread(evt);
   }
 }
 
 NPError
 _getvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
                 char **value, uint32_t *len)
 {
+  if (!NS_IsMainThread()) {
+    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getvalueforurl called from the wrong thread\n"));
+    return NPERR_GENERIC_ERROR;
+  }
+
   if (!instance) {
     return NPERR_INVALID_PARAM;
   }
 
   if (!url || !*url || !len) {
     return NPERR_INVALID_URL;
   }
 
@@ -2665,41 +2670,45 @@ NPError
           !*value) {
         return NPERR_GENERIC_ERROR;
       }
 
       *len = strlen(*value);
       return NPERR_NO_ERROR;
     }
 
-    break;
   default:
     // Fall through and return an error...
     ;
   }
 
   return NPERR_GENERIC_ERROR;
 }
 
 NPError
 _setvalueforurl(NPP instance, NPNURLVariable variable, const char *url,
                 const char *value, uint32_t len)
 {
+  if (!NS_IsMainThread()) {
+    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_setvalueforurl called from the wrong thread\n"));
+    return NPERR_GENERIC_ERROR;
+  }
+
   if (!instance) {
     return NPERR_INVALID_PARAM;
   }
 
   if (!url || !*url) {
     return NPERR_INVALID_URL;
   }
 
   switch (variable) {
   case NPNURLVCookie:
     {
-      if (!url || !value || (0 >= len))
+      if (!value || 0 == len)
         return NPERR_INVALID_PARAM;
 
       nsresult rv = NS_ERROR_FAILURE;
       nsCOMPtr<nsIIOService> ioService(do_GetService(NS_IOSERVICE_CONTRACTID, &rv));
       if (NS_FAILED(rv))
         return NPERR_GENERIC_ERROR;
 
       nsCOMPtr<nsICookieService> cookieService = do_GetService(NS_COOKIESERVICE_CONTRACTID, &rv);
@@ -2734,16 +2743,21 @@ NPError
 }
 
 NPError
 _getauthenticationinfo(NPP instance, const char *protocol, const char *host,
                        int32_t port, const char *scheme, const char *realm,
                        char **username, uint32_t *ulen, char **password,
                        uint32_t *plen)
 {
+  if (!NS_IsMainThread()) {
+    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_getauthenticationinfo called from the wrong thread\n"));
+    return NPERR_GENERIC_ERROR;
+  }
+
   if (!instance || !protocol || !host || !scheme || !realm || !username ||
       !ulen || !password || !plen)
     return NPERR_INVALID_PARAM;
 
   *username = nullptr;
   *password = nullptr;
   *ulen = 0;
   *plen = 0;
@@ -2790,42 +2804,57 @@ NPError
   *plen = *password ? pwd8.Length() : 0;
 
   return NPERR_NO_ERROR;
 }
 
 uint32_t
 _scheduletimer(NPP instance, uint32_t interval, NPBool repeat, PluginTimerFunc timerFunc)
 {
+  if (!NS_IsMainThread()) {
+    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_scheduletimer called from the wrong thread\n"));
+    return 0;
+  }
+
   nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
   if (!inst)
     return 0;
 
   return inst->ScheduleTimer(interval, repeat, timerFunc);
 }
 
 void
 _unscheduletimer(NPP instance, uint32_t timerID)
 {
+  if (!NS_IsMainThread()) {
+    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_unscheduletimer called from the wrong thread\n"));
+    return;
+  }
+
 #ifdef MOZ_WIDGET_ANDROID
   // Sometimes Flash calls this with a dead NPP instance. Ensure the one we have
   // here is valid and maps to a nsNPAPIPluginInstance.
   nsNPAPIPluginInstance *inst = nsNPAPIPluginInstance::GetFromNPP(instance);
 #else
   nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
 #endif
   if (!inst)
     return;
 
   inst->UnscheduleTimer(timerID);
 }
 
 NPError
 _popupcontextmenu(NPP instance, NPMenu* menu)
 {
+  if (!NS_IsMainThread()) {
+    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_popupcontextmenu called from the wrong thread\n"));
+    return 0;
+  }
+
 #ifdef MOZ_WIDGET_COCOA
   nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
 
   double pluginX, pluginY;
   double screenX, screenY;
 
   const NPCocoaEvent* currentEvent = static_cast<NPCocoaEvent*>(inst->GetCurrentEvent());
   if (!currentEvent) {
@@ -2865,26 +2894,36 @@ NPError
     NS_WARNING("Not supported on this platform!");
     return NPERR_GENERIC_ERROR;
 #endif
 }
 
 NPBool
 _convertpoint(NPP instance, double sourceX, double sourceY, NPCoordinateSpace sourceSpace, double *destX, double *destY, NPCoordinateSpace destSpace)
 {
+  if (!NS_IsMainThread()) {
+    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_convertpoint called from the wrong thread\n"));
+    return 0;
+  }
+
   nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
   if (!inst)
     return false;
 
   return inst->ConvertPoint(sourceX, sourceY, sourceSpace, destX, destY, destSpace);
 }
 
 void
 _urlredirectresponse(NPP instance, void* notifyData, NPBool allow)
 {
+  if (!NS_IsMainThread()) {
+    NPN_PLUGIN_LOG(PLUGIN_LOG_ALWAYS,("NPN_convertpoint called from the wrong thread\n"));
+    return;
+  }
+
   nsNPAPIPluginInstance *inst = (nsNPAPIPluginInstance *)instance->ndata;
   if (!inst) {
     return;
   }
 
   inst->URLRedirectResponse(notifyData, allow);
 }
 
--- a/dom/plugins/base/nsPluginHost.cpp
+++ b/dom/plugins/base/nsPluginHost.cpp
@@ -3559,17 +3559,16 @@ nsPluginHost::AddHeadersToChannel(const 
     // FINALLY: we can set the header!
 
     rv = aChannel->SetRequestHeader(headerName, headerValue, true);
     if (NS_FAILED(rv)) {
       rv = NS_ERROR_NULL_POINTER;
       return rv;
     }
   }
-  return rv;
 }
 
 nsresult
 nsPluginHost::StopPluginInstance(nsNPAPIPluginInstance* aInstance)
 {
   if (PluginDestructionGuard::DelayDestroy(aInstance)) {
     return NS_OK;
   }
--- a/dom/plugins/ipc/PluginInstanceChild.cpp
+++ b/dom/plugins/ipc/PluginInstanceChild.cpp
@@ -3605,20 +3605,19 @@ PluginInstanceChild::ReadbackDifferenceR
     // because PluginHost is not able to modify that surface
 #if defined(MOZ_X11)
     if (mBackSurface->GetType() != gfxSurfaceType::Xlib &&
         !gfxSharedImageSurface::IsSharedImage(mBackSurface))
         return false;
 #elif defined(XP_WIN)
     if (!SharedDIBSurface::IsSharedDIBSurface(mBackSurface))
         return false;
-#else
-    return false;
 #endif
 
+#if defined(MOZ_X11) || defined(XP_WIN)
     if (mCurrentSurface->GetContentType() != mBackSurface->GetContentType())
         return false;
 
     if (mSurfaceDifferenceRect.IsEmpty())
         return true;
 
     PLUGIN_LOG_DEBUG(
         ("[InstanceChild][%p] Reading back part of <x=%d,y=%d, w=%d,h=%d>",
@@ -3634,16 +3633,19 @@ PluginInstanceChild::ReadbackDifferenceR
     result.Sub(mSurfaceDifferenceRect, nsIntRegion(rect));
     nsIntRegionRectIterator iter(result);
     const nsIntRect* r;
     while ((r = iter.Next()) != nullptr) {
         dt->CopySurface(source, *r, r->TopLeft());
     }
 
     return true;
+#else
+    return false;
+#endif
 }
 
 void
 PluginInstanceChild::InvalidateRectDelayed(void)
 {
     if (!mCurrentInvalidateTask) {
         return;
     }
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -37,10 +37,10 @@ FAIL_ON_WARNINGS = True
 
 FINAL_LIBRARY = 'xul'
 LOCAL_INCLUDES += [
     '/caps',
     '/netwerk/base',
 ]
 
 if CONFIG['GNU_CC']:
-    CFLAGS += ['-Wshadow']
-    CXXFLAGS += ['-Wshadow']
+    CFLAGS += ['-Wshadow', '-Wformat-security']
+    CXXFLAGS += ['-Wshadow', '-Wformat-security']
--- a/dom/storage/DOMStorageCache.cpp
+++ b/dom/storage/DOMStorageCache.cpp
@@ -460,31 +460,37 @@ DOMStorageCache::GetItem(const DOMStorag
 
   return NS_OK;
 }
 
 nsresult
 DOMStorageCache::SetItem(const DOMStorage* aStorage, const nsAString& aKey,
                          const nsString& aValue, nsString& aOld)
 {
+  // Size of the cache that will change after this action.
+  int64_t delta = 0;
+
   if (Persist(aStorage)) {
     WaitForPreload(Telemetry::LOCALDOMSTORAGE_SETVALUE_BLOCKING_MS);
     if (NS_FAILED(mLoadResult)) {
       return mLoadResult;
     }
   }
 
   Data& data = DataSet(aStorage);
   if (!data.mKeys.Get(aKey, &aOld)) {
     SetDOMStringToNull(aOld);
+
+    // We only consider key size if the key doesn't exist before.
+    delta += static_cast<int64_t>(aKey.Length());
   }
 
-  // Check the quota first
-  const int64_t delta = static_cast<int64_t>(aValue.Length()) -
-                        static_cast<int64_t>(aOld.Length());
+  delta += static_cast<int64_t>(aValue.Length()) -
+           static_cast<int64_t>(aOld.Length());
+
   if (!ProcessUsageDelta(aStorage, delta)) {
     return NS_ERROR_DOM_QUOTA_REACHED;
   }
 
   if (aValue == aOld && DOMStringIsNull(aValue) == DOMStringIsNull(aOld)) {
     return NS_SUCCESS_DOM_NO_OPERATION;
   }
 
@@ -520,17 +526,18 @@ DOMStorageCache::RemoveItem(const DOMSto
 
   Data& data = DataSet(aStorage);
   if (!data.mKeys.Get(aKey, &aOld)) {
     SetDOMStringToNull(aOld);
     return NS_SUCCESS_DOM_NO_OPERATION;
   }
 
   // Recalculate the cached data size
-  const int64_t delta = -(static_cast<int64_t>(aOld.Length()));
+  const int64_t delta = -(static_cast<int64_t>(aOld.Length()) +
+                          static_cast<int64_t>(aKey.Length()));
   unused << ProcessUsageDelta(aStorage, delta);
   data.mKeys.Remove(aKey);
 
   if (Persist(aStorage)) {
     if (!sDatabase) {
       NS_ERROR("Writing to localStorage after the database has been shut down"
                ", data lose!");
       return NS_ERROR_NOT_INITIALIZED;
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -729,17 +729,17 @@ var interfaceNamesInGlobalScope =
     {name: "MediaKeyStatusMap", android: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaQueryList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaRecorder",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "MediaSource", linux: false, release: false},
+    "MediaSource",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaStream",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaStreamAudioDestinationNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaStreamAudioSourceNode",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "MediaStreamEvent",
@@ -983,19 +983,19 @@ var interfaceNamesInGlobalScope =
     "ShadowRoot", // Bogus, but the test harness forces it on.  See bug 1159768.
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SharedWorker",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "SimpleGestureEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SimpleTest", xbl: false},
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "SourceBuffer", linux: false, release: false},
-// IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "SourceBufferList", linux: false, release: false},
+    "SourceBuffer",
+// IMPORTANT: Do not change this list without review from a DOM peer!
+    "SourceBufferList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesisErrorEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesisEvent", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesis", b2g: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "SpeechSynthesisUtterance", b2g: true},
@@ -1341,17 +1341,17 @@ var interfaceNamesInGlobalScope =
     "URL",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "URLSearchParams",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "UserProximityEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "ValidityState",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    {name: "VideoPlaybackQuality", linux: false, release: false},
+    "VideoPlaybackQuality",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "VideoStreamTrack",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VRDevice", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VRPositionState", disabled: true},
 // IMPORTANT: Do not change this list without review from a DOM peer!
     {name: "VRFieldOfView", disabled: true},
--- a/dom/webidl/Performance.webidl
+++ b/dom/webidl/Performance.webidl
@@ -66,8 +66,9 @@ partial interface Performance {
   void mark(DOMString markName);
   [Func="nsPerformance::IsEnabled"]
   void clearMarks(optional DOMString markName);
   [Func="nsPerformance::IsEnabled", Throws]
   void measure(DOMString measureName, optional DOMString startMark, optional DOMString endMark);
   [Func="nsPerformance::IsEnabled"]
   void clearMeasures(optional DOMString measureName);
 };
+
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PerformanceObserver.webidl
@@ -0,0 +1,23 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * https://w3c.github.io/performance-timeline/#the-performance-observer-interface
+ */
+
+dictionary PerformanceObserverInit {
+  required sequence<DOMString> entryTypes;
+};
+
+callback PerformanceObserverCallback = void (PerformanceObserverEntryList entries, PerformanceObserver observer);
+
+[Func="nsPerformance::IsObserverEnabled",
+ Constructor(PerformanceObserverCallback callback),
+ Exposed=(Window,Worker)]
+interface PerformanceObserver {
+  [Throws]
+  void observe(PerformanceObserverInit options);
+  void disconnect();
+};
new file mode 100644
--- /dev/null
+++ b/dom/webidl/PerformanceObserverEntryList.webidl
@@ -0,0 +1,24 @@
+/* -*- Mode: IDL; 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/.
+ *
+ * The origin of this IDL file is
+ * https://w3c.github.io/performance-timeline/#the-performanceobserverentrylist-interface
+ */
+
+// XXX should be moved into Performance.webidl.
+dictionary PerformanceEntryFilterOptions {
+  DOMString name;
+  DOMString entryType;
+  DOMString initiatorType;
+};
+
+[Func="nsPerformance::IsObserverEnabled", Exposed=(Window,Worker)]
+interface PerformanceObserverEntryList {
+  PerformanceEntryList getEntries(optional PerformanceEntryFilterOptions filter);
+  PerformanceEntryList getEntriesByType(DOMString entryType);
+  PerformanceEntryList getEntriesByName(DOMString name,
+                                        optional DOMString entryType);
+};
+
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -348,16 +348,18 @@ WEBIDL_FILES = [
     'PaintRequestList.webidl',
     'PannerNode.webidl',
     'ParentNode.webidl',
     'Performance.webidl',
     'PerformanceEntry.webidl',
     'PerformanceMark.webidl',
     'PerformanceMeasure.webidl',
     'PerformanceNavigation.webidl',
+    'PerformanceObserver.webidl',
+    'PerformanceObserverEntryList.webidl',
     'PerformanceResourceTiming.webidl',
     'PerformanceTiming.webidl',
     'PeriodicWave.webidl',
     'Permissions.webidl',
     'PermissionSettings.webidl',
     'PermissionStatus.webidl',
     'PhoneNumberService.webidl',
     'Plugin.webidl',
--- a/dom/workers/test/mochitest.ini
+++ b/dom/workers/test/mochitest.ini
@@ -101,18 +101,20 @@ support-files =
   bug1062920_worker.js
   webSocket_sharedWorker.js
   bug1104064_worker.js
   worker_consoleAndBlobs.js
   bug1132395_sharedWorker.js
   bug1132924_worker.js
   empty.html
   worker_performance_user_timing.js
+  worker_performance_observer.js
   sharedworker_performance_user_timing.js
   referrer.sjs
+  performance_observer.html
 
 [test_404.html]
 [test_atob.html]
 [test_blobConstructor.html]
 [test_blobWorkers.html]
 [test_bug949946.html]
 [test_bug978260.html]
 [test_bug998474.html]
@@ -163,16 +165,17 @@ skip-if = (toolkit == 'gonk' && debug) #
 skip-if = buildapp == 'mulet'
 [test_newError.html]
 [test_notification.html]
 [test_notification_child.html]
 [test_notification_permission.html]
 [test_onLine.html]
 skip-if = (toolkit == 'gonk' && debug) #debug-only failure
 [test_performance_user_timing.html]
+[test_performance_observer.html]
 [test_promise.html]
 [test_promise_resolved_with_string.html]
 [test_recursion.html]
 [test_recursiveOnerror.html]
 [test_relativeLoad.html]
 skip-if = buildapp == 'b2g' # b2g(Failed to load script: relativeLoad_import.js) b2g-debug(Failed to load script: relativeLoad_import.js) b2g-desktop(Failed to load script: relativeLoad_import.js)
 [test_resolveWorker.html]
 [test_resolveWorker-assignment.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/performance_observer.html
@@ -0,0 +1,32 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset=utf-8>
+<html>
+<head>
+<title>Test for performance observer in worker</title>
+</head>
+<body>
+<div id="log"></div>
+<script>
+[
+ "async_test", "test", "setup",
+ "assert_true", "assert_equals", "assert_array_equals",
+ "assert_throws", "fetch_tests_from_worker"
+].forEach(func => {
+  window[func] = opener[func].bind(opener);
+});
+
+function done() {
+  opener.add_completion_callback(() => {
+    self.close();
+  });
+  opener.done();
+}
+
+fetch_tests_from_worker(new Worker("worker_performance_observer.js"));
+done();
+</script>
+</body>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/test_performance_observer.html
@@ -0,0 +1,17 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>Test for performance observer in worker</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id=log></div>
+<script>
+'use strict';
+SpecialPowers.pushPrefEnv({"set": [["dom.enable_performance_observer", true]]},
+                          function() {
+                            window.open("performance_observer.html");
+                          });
+</script>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/worker_performance_observer.js
@@ -0,0 +1,4 @@
+importScripts(['/resources/testharness.js']);
+importScripts(['../../../dom/base/test/test_performance_observer.js']);
+
+done();
--- a/dom/xslt/xpath/txLiteralExpr.cpp
+++ b/dom/xslt/xpath/txLiteralExpr.cpp
@@ -74,17 +74,17 @@ txLiteralExpr::toString(nsAString& aStr)
             return;
         }
         case txAExprResult::STRING:
         {
             StringResult* strRes =
                 static_cast<StringResult*>(static_cast<txAExprResult*>
                                        (mValue));
             char16_t ch = '\'';
-            if (strRes->mValue.FindChar(ch) != kNotFound) {
+            if (strRes->mValue.Contains(ch)) {
                 ch = '\"';
             }
             aStr.Append(ch);
             aStr.Append(strRes->mValue);
             aStr.Append(ch);
             return;
         }
         case txAExprResult::RESULT_TREE_FRAGMENT:
--- a/dom/xslt/xslt/txExecutionState.cpp
+++ b/dom/xslt/xslt/txExecutionState.cpp
@@ -372,17 +372,17 @@ txIEvalContext*
 txExecutionState::getEvalContext()
 {
     return mEvalContext;
 }
 
 const txXPathNode*
 txExecutionState::retrieveDocument(const nsAString& aUri)
 {
-    NS_ASSERTION(aUri.FindChar(char16_t('#')) == kNotFound,
+    NS_ASSERTION(!aUri.Contains(char16_t('#')),
                  "Remove the fragment.");
 
     if (mDisableLoads) {
         return nullptr;
     }
 
     MOZ_LOG(txLog::xslt, LogLevel::Debug,
            ("Retrieve Document %s", NS_LossyConvertUTF16toASCII(aUri).get()));
--- a/extensions/auth/nsHttpNegotiateAuth.cpp
+++ b/extensions/auth/nsHttpNegotiateAuth.cpp
@@ -311,17 +311,17 @@ nsHttpNegotiateAuth::TestNonFqdn(nsIURI 
 
     if (!TestBoolPref(kNegotiateAuthAllowNonFqdn))
         return false;
 
     if (NS_FAILED(uri->GetAsciiHost(host)))
         return false;
 
     // return true if host does not contain a dot and is not an ip address
-    return !host.IsEmpty() && host.FindChar('.') == kNotFound &&
+    return !host.IsEmpty() && !host.Contains('.') &&
            PR_StringToNetAddr(host.BeginReading(), &addr) != PR_SUCCESS;
 }
 
 bool
 nsHttpNegotiateAuth::TestPref(nsIURI *uri, const char *pref)
 {
     nsCOMPtr<nsIPrefBranch> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
     if (!prefs)
--- a/gfx/ipc/SharedDIB.h
+++ b/gfx/ipc/SharedDIB.h
@@ -12,16 +12,18 @@
 namespace mozilla {
 namespace gfx {
 
 class SharedDIB
 {
 public:
   typedef base::SharedMemoryHandle Handle;
 
+  static const uint32_t kBytesPerPixel = 4;
+
 public:
   SharedDIB();
   ~SharedDIB();
 
   // Create and allocate a new shared dib.
   nsresult Create(uint32_t aSize);
 
   // Destroy or release resources associated with this dib.
--- a/gfx/ipc/SharedDIBSurface.cpp
+++ b/gfx/ipc/SharedDIBSurface.cpp
@@ -7,18 +7,16 @@
 
 #include "cairo.h"
 
 namespace mozilla {
 namespace gfx {
 
 static const cairo_user_data_key_t SHAREDDIB_KEY = {0};
 
-static const long kBytesPerPixel = 4;
-
 bool
 SharedDIBSurface::Create(HDC adc, uint32_t aWidth, uint32_t aHeight,
                          bool aTransparent)
 {
   nsresult rv = mSharedDIB.Create(adc, aWidth, aHeight, aTransparent);
   if (NS_FAILED(rv) || !mSharedDIB.IsValid())
     return false;
 
@@ -37,17 +35,17 @@ SharedDIBSurface::Attach(Handle aHandle,
   InitSurface(aWidth, aHeight, aTransparent);
   return true;
 }
 
 void
 SharedDIBSurface::InitSurface(uint32_t aWidth, uint32_t aHeight,
                               bool aTransparent)
 {
-  long stride = long(aWidth * kBytesPerPixel);
+  long stride = long(aWidth * SharedDIB::kBytesPerPixel);
   unsigned char* data = reinterpret_cast<unsigned char*>(mSharedDIB.GetBits());
 
   gfxImageFormat format = aTransparent ? gfxImageFormat::ARGB32 : gfxImageFormat::RGB24;
 
   gfxImageSurface::InitWithData(data, IntSize(aWidth, aHeight),
                                 stride, format);
 
   cairo_surface_set_user_data(mSurface, &SHAREDDIB_KEY, this, nullptr);
--- a/gfx/ipc/SharedDIBWin.cpp
+++ b/gfx/ipc/SharedDIBWin.cpp
@@ -6,17 +6,16 @@
 #include "SharedDIBWin.h"
 #include "gfxAlphaRecovery.h"
 #include "nsMathUtils.h"
 #include "nsDebug.h"
 
 namespace mozilla {
 namespace gfx {
 
-static const uint32_t kBytesPerPixel = 4;
 static const uint32_t kByteAlign = 1 << gfxAlphaRecovery::GoodAlignmentLog2();
 static const uint32_t kHeaderBytes =
   (sizeof(BITMAPV4HEADER) + kByteAlign - 1) & ~(kByteAlign - 1);
 
 SharedDIBWin::SharedDIBWin() :
     mSharedHdc(nullptr)
   , mSharedBmp(nullptr)
   , mOldObj(nullptr)
@@ -131,11 +130,10 @@ SharedDIBWin::SetupSurface(HDC aHdc, BIT
   if (!mSharedBmp)
     return NS_ERROR_FAILURE;
 
   mOldObj = SelectObject(mSharedHdc, mSharedBmp);
 
   return NS_OK;
 }
 
-
 } // gfx
 } // mozilla
--- a/gfx/ipc/moz.build
+++ b/gfx/ipc/moz.build
@@ -12,27 +12,29 @@ EXPORTS.mozilla.gfx += [
     'SharedDIB.h',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     EXPORTS.mozilla.gfx += [
         'SharedDIBSurface.h',
         'SharedDIBWin.h',
     ]
-    SOURCES += [
+    UNIFIED_SOURCES += [
         'SharedDIBSurface.cpp',
         'SharedDIBWin.cpp',
     ]
 
-SOURCES += [
+UNIFIED_SOURCES += [
     'SharedDIB.cpp',
 ]
 
 IPDL_SOURCES = [
     'GraphicsMessages.ipdlh',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
 
 CXXFLAGS += CONFIG['MOZ_CAIRO_CFLAGS']
 CXXFLAGS += CONFIG['TK_CFLAGS']
+
+FAIL_ON_WARNINGS = True
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -478,21 +478,25 @@ CloneLayerTreePropertiesInternal(Layer* 
   switch (aRoot->GetType()) {
     case Layer::TYPE_CONTAINER:
     case Layer::TYPE_REF:
       return MakeUnique<ContainerLayerProperties>(aRoot->AsContainerLayer());
     case Layer::TYPE_COLOR:
       return MakeUnique<ColorLayerProperties>(static_cast<ColorLayer*>(aRoot));
     case Layer::TYPE_IMAGE:
       return MakeUnique<ImageLayerProperties>(static_cast<ImageLayer*>(aRoot), aIsMask);
-    default:
+    case Layer::TYPE_CANVAS:
+    case Layer::TYPE_READBACK:
+    case Layer::TYPE_SHADOW:
+    case Layer::TYPE_PAINTED:
       return MakeUnique<LayerPropertiesBase>(aRoot);
   }
 
-  return UniquePtr<LayerPropertiesBase>(nullptr);
+  MOZ_ASSERT_UNREACHABLE("Unexpected root layer type");
+  return MakeUnique<LayerPropertiesBase>(aRoot);
 }
 
 /* static */ UniquePtr<LayerProperties>
 LayerProperties::CloneFrom(Layer* aRoot)
 {
   return CloneLayerTreePropertiesInternal(aRoot);
 }
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -655,17 +655,21 @@ public:
   }
 
   virtual bool DoSample(FrameMetrics& aFrameMetrics,
                         const TimeDuration& aDelta) override
   {
     // Can't inline these variables due to short-circuit evaluation.
     bool continueX = mApzc.mX.SampleOverscrollAnimation(aDelta);
     bool continueY = mApzc.mY.SampleOverscrollAnimation(aDelta);
-    return continueX || continueY;
+    if (!continueX && !continueY) {
+      mApzc.OverscrollAnimationEnding();
+      return false;
+    }
+    return true;
   }
 private:
   AsyncPanZoomController& mApzc;
 };
 
 class SmoothScrollAnimation : public AsyncPanZoomAnimation {
 public:
   SmoothScrollAnimation(AsyncPanZoomController& aApzc,
@@ -2169,27 +2173,34 @@ void AsyncPanZoomController::AcceptFling
   if (velocity.x != 0.0f) {
     predictedDelta.x = -velocity.x / log(1.0 - friction);
   }
   if (velocity.y != 0.0f) {
     predictedDelta.y = -velocity.y / log(1.0 - friction);
   }
   CSSPoint predictedDestination = mFrameMetrics.GetScrollOffset() + predictedDelta / mFrameMetrics.GetZoom();
 
-  nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
-  if (controller) {
-    APZC_LOG("%p fling snapping.  friction: %f velocity: %f, %f "
-             "predictedDelta: %f, %f position: %f, %f "
-             "predictedDestination: %f, %f\n",
-             this, friction, velocity.x, velocity.y, (float)predictedDelta.x,
-             (float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x,
-             (float)mFrameMetrics.GetScrollOffset().y,
-             (float)predictedDestination.x, (float)predictedDestination.y);
-    controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
-                                 predictedDestination);
+  // If the fling will overscroll, don't request a fling snap, because the
+  // resulting content scrollTo() would unnecessarily cancel the overscroll
+  // animation.
+  bool flingWillOverscroll = IsOverscrolled() && ((velocity.x * mX.GetOverscroll() >= 0) ||
+                                                  (velocity.y * mY.GetOverscroll() >= 0));
+  if (!flingWillOverscroll) {
+    nsRefPtr<GeckoContentController> controller = GetGeckoContentController();
+    if (controller) {
+      APZC_LOG("%p fling snapping.  friction: %f velocity: %f, %f "
+               "predictedDelta: %f, %f position: %f, %f "
+               "predictedDestination: %f, %f\n",
+               this, friction, velocity.x, velocity.y, (float)predictedDelta.x,
+               (float)predictedDelta.y, (float)mFrameMetrics.GetScrollOffset().x,
+               (float)mFrameMetrics.GetScrollOffset().y,
+               (float)predictedDestination.x, (float)predictedDestination.y);
+      controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
+                                   predictedDestination);
+    }
   }
 
   StartAnimation(fling);
 }
 
 bool AsyncPanZoomController::AttemptFling(ParentLayerPoint aVelocity,
                                           const nsRefPtr<const OverscrollHandoffChain>& aOverscrollHandoffChain,
                                           bool aHandoff) {
@@ -3314,10 +3325,24 @@ void AsyncPanZoomController::ShareCompos
       // so the content process know which APZC sent this shared FrameMetrics.
       if (!compositor->SendSharedCompositorFrameMetrics(mem, handle, mLayersId, mAPZCId)) {
         APZC_LOG("%p failed to share FrameMetrics with content process.", this);
       }
     }
   }
 }
 
+void AsyncPanZoomController::OverscrollAnimationEnding() {
+  // If we got into overscroll from a fling, that fling did not request a
+  // fling snap to avoid a resulting scrollTo from cancelling the overscroll
+  // animation too early. We do still want to request a fling snap, though,
+  // in case the end of the axis at which we're overscrolled is not a valid
+  // snap point, so we request one now. If there are no snap points, this will
+  // do nothing. If there are snap points, we'll get a scrollTo that snaps us
+  // back to the nearest valid snap point.
+  if (nsRefPtr<GeckoContentController> controller = GetGeckoContentController()) {
+    controller->RequestFlingSnap(mFrameMetrics.GetScrollId(),
+                                 mFrameMetrics.GetScrollOffset());
+  }
+}
+
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -639,16 +639,20 @@ protected:
   static AxisLockMode GetAxisLockMode();
 
   // Helper function for OnSingleTapUp() and OnSingleTapConfirmed().
   nsEventStatus GenerateSingleTap(const ScreenIntPoint& aPoint, mozilla::Modifiers aModifiers);
 
   // Common processing at the end of a touch block.
   void OnTouchEndOrCancel();
 
+  // This is called by OverscrollAnimation to notify us when the overscroll
+  // animation is ending.
+  void OverscrollAnimationEnding();
+
   uint64_t mLayersId;
   nsRefPtr<CompositorParent> mCompositorParent;
   TaskThrottler mPaintThrottler;
 
   /* Access to the following two fields is protected by the mRefPtrMonitor,
      since they are accessed on the UI thread but can be cleared on the
      compositor thread. */
   nsRefPtr<GeckoContentController> mGeckoContentController;
--- a/gfx/thebes/gfxDWriteFontList.cpp
+++ b/gfx/thebes/gfxDWriteFontList.cpp
@@ -839,16 +839,22 @@ gfxDWriteFontList::MakePlatformFont(cons
         // We don't know how to deal with 0 faces either.
         delete entry;
         return nullptr;
     }
 
     return entry;
 }
 
+enum DWriteInitError {
+    errGDIInterop = 1,
+    errSystemFontCollection = 2,
+    errNoFonts = 3
+};
+
 nsresult
 gfxDWriteFontList::InitFontList()
 {
     LARGE_INTEGER frequency;          // ticks per second
     LARGE_INTEGER t1, t2, t3, t4, t5; // ticks
     double elapsedTime, upTime;
     char nowTime[256], nowDate[256];
 
@@ -869,39 +875,45 @@ gfxDWriteFontList::InitFontList()
     gfxPlatformFontList::InitFontList();
 
     mFontSubstitutes.Clear();
     mNonExistingFonts.Clear();
 
     hr = gfxWindowsPlatform::GetPlatform()->GetDWriteFactory()->
         GetGdiInterop(getter_AddRefs(mGDIInterop));
     if (FAILED(hr)) {
+        Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
+                              uint32_t(errGDIInterop));
         return NS_ERROR_FAILURE;
     }
 
     QueryPerformanceCounter(&t2); // base-class/interop initialization
 
     nsRefPtr<IDWriteFactory> factory =
         gfxWindowsPlatform::GetPlatform()->GetDWriteFactory();
 
     hr = factory->GetSystemFontCollection(getter_AddRefs(mSystemFonts));
     NS_ASSERTION(SUCCEEDED(hr), "GetSystemFontCollection failed!");
 
     if (FAILED(hr)) {
+        Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
+                              uint32_t(errSystemFontCollection));
         return NS_ERROR_FAILURE;
     }
 
     QueryPerformanceCounter(&t3); // system font collection
 
     GetFontsFromCollection(mSystemFonts);
 
     // if no fonts found, something is out of whack, bail and use GDI backend
     NS_ASSERTION(mFontFamilies.Count() != 0,
                  "no fonts found in the system fontlist -- holy crap batman!");
     if (mFontFamilies.Count() == 0) {
+        Telemetry::Accumulate(Telemetry::DWRITEFONT_INIT_PROBLEM,
+                              uint32_t(errNoFonts));
         return NS_ERROR_FAILURE;
     }
 
     QueryPerformanceCounter(&t4); // iterate over system fonts
 
 #ifdef MOZ_BUNDLED_FONTS
     mBundledFonts = CreateBundledFontsCollection(factory);
     if (mBundledFonts) {
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2060,36 +2060,29 @@ gfxPlatform::GetLog(eGfxLog aWhichLog)
         sTextrunuiLog = PR_NewLogModule("textrunui");
         sCmapDataLog = PR_NewLogModule("cmapdata");
         sTextPerfLog = PR_NewLogModule("textperf");
     }
 
     switch (aWhichLog) {
     case eGfxLog_fontlist:
         return sFontlistLog;
-        break;
     case eGfxLog_fontinit:
         return sFontInitLog;
-        break;
     case eGfxLog_textrun:
         return sTextrunLog;
-        break;
     case eGfxLog_textrunui:
         return sTextrunuiLog;
-        break;
     case eGfxLog_cmapdata:
         return sCmapDataLog;
-        break;
     case eGfxLog_textperf:
         return sTextPerfLog;
-        break;
-    default:
-        break;
     }
 
+    MOZ_ASSERT_UNREACHABLE("Unexpected log type");
     return nullptr;
 }
 
 int
 gfxPlatform::GetScreenDepth() const
 {
     NS_WARNING("GetScreenDepth not implemented on this platform -- returning 0!");
     return 0;
--- a/gfx/thebes/gfxTextRun.cpp
+++ b/gfx/thebes/gfxTextRun.cpp
@@ -3264,17 +3264,17 @@ gfxMissingFontRecorder::Flush()
             continue;
         }
         for (uint32_t j = 0; j < 32; ++j) {
             if (!(mMissingFonts[i] & (1 << j))) {
                 continue;
             }
             mNotifiedFonts[i] |= (1 << j);
             if (!fontNeeded.IsEmpty()) {
-                fontNeeded.Append(PRUnichar(','));
+                fontNeeded.Append(char16_t(','));
             }
             uint32_t tag = GetScriptTagForCode(i * 32 + j);
             fontNeeded.Append(char16_t(tag >> 24));
             fontNeeded.Append(char16_t((tag >> 16) & 0xff));
             fontNeeded.Append(char16_t((tag >> 8) & 0xff));
             fontNeeded.Append(char16_t(tag & 0xff));
         }
         mMissingFonts[i] = 0;
--- a/js/src/asmjs/AsmJSValidate.cpp
+++ b/js/src/asmjs/AsmJSValidate.cpp
@@ -471,16 +471,31 @@ SkipEmptyStatements(ParseNode* pn)
 
 static inline ParseNode*
 NextNonEmptyStatement(ParseNode* pn)
 {
     return SkipEmptyStatements(pn->pn_next);
 }
 
 static bool
+GetToken(AsmJSParser& parser, TokenKind* tkp)
+{
+    TokenStream& ts = parser.tokenStream;
+    TokenKind tk;
+    while (true) {
+        if (!ts.getToken(&tk, TokenStream::Operand))
+            return false;
+        if (tk != TOK_SEMI)
+            break;
+    }
+    *tkp = tk;
+    return true;
+}
+
+static bool
 PeekToken(AsmJSParser& parser, TokenKind* tkp)
 {
     TokenStream& ts = parser.tokenStream;
     TokenKind tk;
     while (true) {
         if (!ts.peekToken(&tk, TokenStream::Operand))
             return false;
         if (tk != TOK_SEMI)
@@ -1103,29 +1118,32 @@ namespace {
 class MOZ_STACK_CLASS ModuleCompiler
 {
   public:
     class Func
     {
         Signature sig_;
         PropertyName* name_;
         Label* entry_;
+        uint32_t firstUseOffset_;
         uint32_t funcIndex_;
         uint32_t srcBegin_;
         uint32_t srcEnd_;
         uint32_t compileTime_;
         bool defined_;
 
       public:
-        Func(PropertyName* name, Signature&& sig, Label* entry, uint32_t funcIndex)
-          : sig_(Move(sig)), name_(name), entry_(entry), funcIndex_(funcIndex), srcBegin_(0),
-            srcEnd_(0), compileTime_(0), defined_(false)
+        Func(PropertyName* name, uint32_t firstUseOffset, Signature&& sig, Label* entry,
+             uint32_t funcIndex)
+          : sig_(Move(sig)), name_(name), entry_(entry), firstUseOffset_(firstUseOffset),
+            funcIndex_(funcIndex), srcBegin_(0), srcEnd_(0), compileTime_(0), defined_(false)
         {}
 
         PropertyName* name() const { return name_; }
+        uint32_t firstUseOffset() const { return firstUseOffset_; }
         bool defined() const { return defined_; }
         uint32_t funcIndex() const { return funcIndex_; }
 
         void define(ParseNode* fn) {
             MOZ_ASSERT(!defined_);
             defined_ = true;
             srcBegin_ = fn->pn_pos.begin;
             srcEnd_ = fn->pn_pos.end;
@@ -1282,35 +1300,40 @@ class MOZ_STACK_CLASS ModuleCompiler
         }
     };
 
     typedef Vector<const Func*> FuncPtrVector;
 
     class FuncPtrTable
     {
         Signature sig_;
+        FuncPtrVector elems_;
+        PropertyName* name_;
+        uint32_t firstUseOffset_;
         uint32_t mask_;
         uint32_t globalDataOffset_;
         uint32_t tableIndex_;
-        FuncPtrVector elems_;
 
       public:
-        FuncPtrTable(ExclusiveContext* cx, Signature&& sig, uint32_t mask, uint32_t gdo,
-                     uint32_t tableIndex)
-          : sig_(Move(sig)), mask_(mask), globalDataOffset_(gdo), tableIndex_(tableIndex),
-            elems_(cx)
+        FuncPtrTable(ExclusiveContext* cx, PropertyName* name, uint32_t firstUseOffset, Signature&& sig,
+                     uint32_t mask, uint32_t gdo, uint32_t tableIndex)
+          : sig_(Move(sig)), elems_(cx), name_(name), firstUseOffset_(firstUseOffset), mask_(mask),
+            globalDataOffset_(gdo), tableIndex_(tableIndex)
         {}
 
         FuncPtrTable(FuncPtrTable&& rhs)
-          : sig_(Move(rhs.sig_)), mask_(rhs.mask_), globalDataOffset_(rhs.globalDataOffset_),
-            elems_(Move(rhs.elems_))
+          : sig_(Move(rhs.sig_)), elems_(Move(rhs.elems_)), name_(rhs.name()),
+            firstUseOffset_(rhs.firstUseOffset()), mask_(rhs.mask_),
+            globalDataOffset_(rhs.globalDataOffset_)
         {}
 
         Signature& sig() { return sig_; }
         const Signature& sig() const { return sig_; }
+        PropertyName* name() const { return name_; }
+        uint32_t firstUseOffset() const { return firstUseOffset_; }
         unsigned mask() const { return mask_; }
         unsigned globalDataOffset() const { return globalDataOffset_; }
         unsigned tableIndex() const { return tableIndex_; }
 
         bool initialized() const { return !elems_.empty(); }
         void initElems(FuncPtrVector&& elems) { elems_ = Move(elems); MOZ_ASSERT(initialized()); }
         unsigned numElems() const { MOZ_ASSERT(initialized()); return elems_.length(); }
         const Func& elem(unsigned i) const { return *elems_[i]; }
@@ -1591,55 +1614,55 @@ class MOZ_STACK_CLASS ModuleCompiler
         MOZ_ASSERT(errorOffset_ == UINT32_MAX);
         MOZ_ASSERT(str);
         errorOffset_ = offset;
         errorString_ = DuplicateString(cx_, str);
         return false;
     }
 
     bool fail(ParseNode* pn, const char* str) {
-        if (pn)
-            return failOffset(pn->pn_pos.begin, str);
-
-        // The exact rooting static analysis does not perform dataflow analysis, so it believes
-        // that unrooted things on the stack during compilation may still be accessed after this.
-        // Since pn is typically only null under OOM, this suppression simply forces any GC to be
-        // delayed until the compilation is off the stack and more memory can be freed.
-        gc::AutoSuppressGC nogc(cx_);
-        TokenPos pos;
-        TokenStream::Modifier modifier = tokenStream().hasLookahead() ? tokenStream().getLookaheadModifier() : TokenStream::None;
-        if (!tokenStream().peekTokenPos(&pos, modifier))
-            return false;
-        return failOffset(pos.begin, str);
-    }
-
-    bool failfVA(ParseNode* pn, const char* fmt, va_list ap) {
+        return failOffset(pn->pn_pos.begin, str);
+    }
+
+    bool failfVAOffset(uint32_t offset, const char* fmt, va_list ap) {
         MOZ_ASSERT(!errorString_);
         MOZ_ASSERT(errorOffset_ == UINT32_MAX);
         MOZ_ASSERT(fmt);
-        errorOffset_ = pn ? pn->pn_pos.begin : tokenStream().currentToken().pos.end;
+        errorOffset_ = offset;
         errorString_.reset(JS_vsmprintf(fmt, ap));
         return false;
     }
 
+    bool failfOffset(uint32_t offset, const char* fmt, ...) {
+        va_list ap;
+        va_start(ap, fmt);
+        failfVAOffset(offset, fmt, ap);
+        va_end(ap);
+        return false;
+    }
+
     bool failf(ParseNode* pn, const char* fmt, ...) {
         va_list ap;
         va_start(ap, fmt);
-        failfVA(pn, fmt, ap);
+        failfVAOffset(pn->pn_pos.begin, fmt, ap);
         va_end(ap);
         return false;
     }
 
+    bool failNameOffset(uint32_t offset, const char* fmt, PropertyName* name) {
+        // This function is invoked without the caller properly rooting its locals.
+        gc::AutoSuppressGC suppress(cx_);
+        JSAutoByteString bytes;
+        if (AtomToPrintableString(cx_, name, &bytes))
+            failfOffset(offset, fmt, bytes.ptr());
+        return false;
+    }
+
     bool failName(ParseNode* pn, const char* fmt, PropertyName* name) {
-        // This function is invoked without the caller properly rooting its locals.
-        gc::AutoSuppressGC suppress(cx_);
-        JSAutoByteString bytes;
-        if (AtomToPrintableString(cx_, name, &bytes))
-            failf(pn, fmt, bytes.ptr());
-        return false;
+        return failNameOffset(pn->pn_pos.begin, fmt, name);
     }
 
     bool failOverRecursed() {
         errorOverRecursed_ = true;
         return false;
     }
 
     /*************************************************** Read-only interface */
@@ -1762,51 +1785,52 @@ class MOZ_STACK_CLASS ModuleCompiler
         Global* global = moduleLifo_.new_<Global>(which);
         if (!global)
             return false;
         global->u.varOrConst.scalarOrSimdIndex_ = scalarOrSimdIndex;
         global->u.varOrConst.compilerIndex_ = globalsVector_.length();
         global->u.varOrConst.type_ = VarType(coercion).toType().which();
         return globalsVector_.append(global) && globals_.putNew(varName, global);
     }
-    bool addFunction(PropertyName* name, Signature&& sig, Func** func, uint32_t* outFuncIndex) {
+    bool addFunction(PropertyName* name, uint32_t firstUseOffset, Signature&& sig, Func** func,
+                     uint32_t* outFuncIndex) {
         MOZ_ASSERT(!finishedFunctionBodies_);
         uint32_t funcIndex = functions_.length();
         if (outFuncIndex)
             *outFuncIndex = funcIndex;
         Global* global = moduleLifo_.new_<Global>(Global::Function);
         if (!global)
             return false;
         global->u.funcIndex_ = funcIndex;
         if (!globals_.putNew(name, global))
             return false;
         Label* entry = moduleLifo_.new_<Label>();
         if (!entry)
             return false;
-        *func = moduleLifo_.new_<Func>(name, Move(sig), entry, funcIndex);
+        *func = moduleLifo_.new_<Func>(name, firstUseOffset, Move(sig), entry, funcIndex);
         if (!*func)
             return false;
         return functions_.append(*func);
     }
-    bool addFuncPtrTable(PropertyName* name, Signature&& sig, uint32_t mask, FuncPtrTable** table,
-                         uint32_t* tableIndexOut)
+    bool addFuncPtrTable(PropertyName* name, uint32_t offset, Signature&& sig, uint32_t mask,
+                         FuncPtrTable** table, uint32_t* tableIndexOut)
     {
         uint32_t tableIndex = funcPtrTables_.length();
         if (tableIndexOut)
             *tableIndexOut = tableIndex;
         Global* global = moduleLifo_.new_<Global>(Global::FuncPtrTable);
         if (!global)
             return false;
         global->u.funcPtrTableIndex_ = tableIndex;
         if (!globals_.putNew(name, global))
             return false;
         uint32_t globalDataOffset;
         if (!module_->addFuncPtrTable(/* numElems = */ mask + 1, &globalDataOffset))
             return false;
-        FuncPtrTable tmpTable(cx_, Move(sig), mask, globalDataOffset, tableIndex);
+        FuncPtrTable tmpTable(cx_, name, offset, Move(sig), mask, globalDataOffset, tableIndex);
         if (!funcPtrTables_.append(Move(tmpTable)))
             return false;
         *table = &funcPtrTables_.back();
         return true;
     }
     bool addByteLength(PropertyName* name) {
         canValidateChangeHeap_ = true;
         if (!module_->addByteLength())
@@ -2967,17 +2991,17 @@ class FunctionBuilder
     {
         return m_.fail(pn, str);
     }
 
     bool failf(ParseNode* pn, const char* fmt, ...)
     {
         va_list ap;
         va_start(ap, fmt);
-        m_.failfVA(pn, fmt, ap);
+        m_.failfVAOffset(pn->pn_pos.begin, fmt, ap);
         va_end(ap);
         return false;
     }
 
     bool failName(ParseNode* pn, const char* fmt, PropertyName* name)
     {
         return m_.failName(pn, fmt, name);
     }
@@ -4708,16 +4732,21 @@ CheckNewArrayView(ModuleCompiler& m, Pro
         if (global->which() != ModuleCompiler::Global::ArrayViewCtor)
             return m.failName(ctorExpr, "%s must be an imported array view constructor", globalName);
 
         field = nullptr;
         type = global->viewType();
         shared = global->viewIsSharedView();
     }
 
+#if !defined(ENABLE_SHARED_ARRAY_BUFFER)
+    if (shared)
+        return m.fail(ctorExpr, "shared views not supported by this build");
+#endif
+
     if (!CheckNewArrayViewArgs(m, ctorExpr, bufferName))
         return false;
 
     if (!m.module().isValidViewSharedness(shared))
         return m.failName(ctorExpr, "%s has different sharedness than previous view constructors", globalName);
 
     return m.addArrayView(varName, type, field, shared);
 }
@@ -4852,16 +4881,21 @@ CheckGlobalDotImport(ModuleCompiler& m, 
         if (field == m.cx()->names().Infinity)
             return m.addGlobalConstant(varName, PositiveInfinity<double>(), field);
         if (field == m.cx()->names().byteLength)
             return m.addByteLength(varName);
 
         Scalar::Type type;
         bool shared = false;
         if (IsArrayViewCtorName(m, field, &type, &shared)) {
+#if !defined(ENABLE_SHARED_ARRAY_BUFFER)
+            if (shared)
+                return m.fail(initNode, "shared views not supported by this build");
+#endif
+
             if (!m.module().isValidViewSharedness(shared))
                 return m.failName(initNode, "'%s' has different sharedness than previous view constructors", field);
             return m.addArrayViewCtor(varName, type, field, shared);
         }
 
         return m.failName(initNode, "'%s' is not a standard constant or typed array name", field);
     }
 
@@ -4913,22 +4947,25 @@ CheckModuleProcessingDirectives(ModuleCo
     while (true) {
         bool matched;
         if (!ts.matchToken(&matched, TOK_STRING, TokenStream::Operand))
             return false;
         if (!matched)
             return true;
 
         if (!IsIgnoredDirectiveName(m.cx(), ts.currentToken().atom()))
-            return m.fail(nullptr, "unsupported processing directive");
-
-        if (!ts.matchToken(&matched, TOK_SEMI))
-            return false;
-        if (!matched)
-            return m.fail(nullptr, "expected semicolon after string literal");
+            return m.failOffset(ts.currentToken().pos.begin, "unsupported processing directive");
+
+        TokenKind tt;
+        if (!ts.getToken(&tt))
+            return false;
+        if (tt != TOK_SEMI) {
+            return m.failOffset(ts.currentToken().pos.begin,
+                                "expected semicolon after string literal");
+        }
     }
 }
 
 static bool
 CheckModuleGlobals(ModuleCompiler& m)
 {
     while (true) {
         ParseNode* varStmt;
@@ -6304,17 +6341,17 @@ CheckSignatureAgainstExisting(ModuleComp
 static bool
 CheckFunctionSignature(ModuleCompiler& m, ParseNode* usepn, Signature&& sig, PropertyName* name,
                        ModuleCompiler::Func** func, uint32_t* funcIndex = nullptr)
 {
     ModuleCompiler::Func* existing = m.lookupFunction(name);
     if (!existing) {
         if (!CheckModuleLevelName(m, usepn, name))
             return false;
-        return m.addFunction(name, Move(sig), func, funcIndex);
+        return m.addFunction(name, usepn->pn_pos.begin, Move(sig), func, funcIndex);
     }
 
     if (!CheckSignatureAgainstExisting(m, usepn, sig, existing->sig()))
         return false;
 
     *func = existing;
     if (funcIndex)
         *funcIndex = existing->funcIndex();
@@ -6406,17 +6443,17 @@ CheckFuncPtrTableAgainstExisting(ModuleC
             *tableIndex = existing->funcPtrTableIndex();
         *tableOut = &table;
         return true;
     }
 
     if (!CheckModuleLevelName(m, usepn, name))
         return false;
 
-    return m.addFuncPtrTable(name, Move(sig), mask, tableOut, tableIndex);
+    return m.addFuncPtrTable(name, usepn->pn_pos.begin, Move(sig), mask, tableOut, tableIndex);
 }
 
 static bool
 CheckFuncPtrCall(FunctionBuilder& f, ParseNode* callNode, RetType retType, Type* type)
 {
     if (!f.canCall()) {
         return f.fail(callNode, "function-pointer call expressions may not be nested inside heap "
                                 "expressions when the module contains a change-heap function");
@@ -9638,39 +9675,39 @@ CheckHeapLengthCondition(ModuleCompiler&
         return m.fail(maxLengthNode, "maximum length must be greater or equal to minimum length");
 
     return true;
 }
 
 static bool
 CheckReturnBoolLiteral(ModuleCompiler& m, ParseNode* stmt, bool retval)
 {
-    if (!stmt)
-        return m.fail(stmt, "expected return statement");
-
     if (stmt->isKind(PNK_STATEMENTLIST)) {
-        stmt = SkipEmptyStatements(ListHead(stmt));
-        if (!stmt || NextNonEmptyStatement(stmt))
+        ParseNode* next = SkipEmptyStatements(ListHead(stmt));
+        if (!next)
+            return m.fail(stmt, "expected return statement");
+        stmt = next;
+        if (NextNonEmptyStatement(stmt))
             return m.fail(stmt, "expected single return statement");
     }
 
     if (!stmt->isKind(PNK_RETURN))
         return m.fail(stmt, "expected return statement");
 
     ParseNode* returnExpr = ReturnExpr(stmt);
     if (!returnExpr || !returnExpr->isKind(retval ? PNK_TRUE : PNK_FALSE))
         return m.failf(stmt, "expected 'return %s;'", retval ? "true" : "false");
 
     return true;
 }
 
 static bool
 CheckReassignmentTo(ModuleCompiler& m, ParseNode* stmt, PropertyName* lhsName, ParseNode** rhs)
 {
-    if (!stmt || !stmt->isKind(PNK_SEMI))
+    if (!stmt->isKind(PNK_SEMI))
         return m.fail(stmt, "missing reassignment");
 
     ParseNode* assign = UnaryKid(stmt);
     if (!assign || !assign->isKind(PNK_ASSIGN))
         return m.fail(stmt, "missing reassignment");
 
     ParseNode* lhs = BinaryLeft(assign);
     if (!IsUseOfName(lhs, lhsName))
@@ -9724,19 +9761,23 @@ CheckChangeHeap(ModuleCompiler& m, Parse
 
     uint32_t mask, min = 0, max;  // initialize min to silence GCC warning
     if (!CheckHeapLengthCondition(m, cond, newBufferName, &mask, &min, &max))
         return false;
 
     if (!CheckReturnBoolLiteral(m, thenStmt, false))
         return false;
 
-    stmtIter = NextNonEmptyStatement(stmtIter);
-
-    for (unsigned i = 0; i < m.numArrayViews(); i++, stmtIter = NextNonEmptyStatement(stmtIter)) {
+    ParseNode* next = NextNonEmptyStatement(stmtIter);
+
+    for (unsigned i = 0; i < m.numArrayViews(); i++, next = NextNonEmptyStatement(stmtIter)) {
+        if (!next)
+            return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
+        stmtIter = next;
+
         const ModuleCompiler::ArrayView& view = m.arrayView(i);
 
         ParseNode* rhs;
         if (!CheckReassignmentTo(m, stmtIter, view.name, &rhs))
             return false;
 
         if (!rhs->isKind(PNK_NEW))
             return m.failName(rhs, "expecting assignment of new array view to %s", view.name);
@@ -9750,23 +9791,30 @@ CheckChangeHeap(ModuleCompiler& m, Parse
             return m.fail(rhs, "expecting name of imported typed array constructor");
         if (global->viewType() != view.type)
             return m.fail(rhs, "can't change the type of a global view variable");
 
         if (!CheckNewArrayViewArgs(m, ctorExpr, newBufferName))
             return false;
     }
 
+    if (!next)
+        return m.failOffset(stmtIter->pn_pos.end, "missing reassignment");
+    stmtIter = next;
+
     ParseNode* rhs;
     if (!CheckReassignmentTo(m, stmtIter, bufferName, &rhs))
         return false;
     if (!IsUseOfName(rhs, newBufferName))
         return m.failName(stmtIter, "expecting assignment of new buffer to %s", bufferName);
 
-    stmtIter = NextNonEmptyStatement(stmtIter);
+    next = NextNonEmptyStatement(stmtIter);
+    if (!next)
+        return m.failOffset(stmtIter->pn_pos.end, "expected return statement");
+    stmtIter = next;
 
     if (!CheckReturnBoolLiteral(m, stmtIter, true))
         return false;
 
     stmtIter = NextNonEmptyStatement(stmtIter);
     if (stmtIter)
         return m.fail(stmtIter, "expecting end of function");
 
@@ -9819,17 +9867,17 @@ ParseFunction(ModuleCompiler& m, ParseNo
                             /* blockScopeDepth = */ 0);
     if (!funpc.init(m.parser()))
         return false;
 
     if (!m.parser().functionArgsAndBodyGeneric(InAllowed, YieldIsName, fn, fun, Statement)) {
         if (tokenStream.hadError() || directives == newDirectives)
             return false;
 
-        return m.fail(nullptr, "encountered new directive");
+        return m.fail(fn, "encountered new directive in function");
     }
 
     MOZ_ASSERT(!tokenStream.hadError());
     MOZ_ASSERT(directives == newDirectives);
 
     fn->pn_blockid = outerpc->blockid();
 
     *fnOut = fn;
@@ -10373,18 +10421,21 @@ GenerateCode(ModuleCompiler& m, ModuleCo
     // CodeGenerator so we can destroy it now (via ScopedJSDeletePtr).
     return true;
 }
 
 static bool
 CheckAllFunctionsDefined(ModuleCompiler& m)
 {
     for (unsigned i = 0; i < m.numFunctions(); i++) {
-        if (!m.function(i).entry().bound())
-            return m.failName(nullptr, "missing definition of function %s", m.function(i).name());
+        if (!m.function(i).entry().bound()) {
+            ModuleCompiler::Func& f = m.function(i);
+            return m.failNameOffset(f.firstUseOffset(),
+                                    "missing definition of function %s", f.name());
+        }
     }
 
     return true;
 }
 
 static bool
 CheckFunctionsSequential(ModuleCompiler& m)
 {
@@ -10759,18 +10810,22 @@ CheckFuncPtrTables(ModuleCompiler& m)
             break;
         for (ParseNode* var = VarListHead(varStmt); var; var = NextNode(var)) {
             if (!CheckFuncPtrTable(m, var))
                 return false;
         }
     }
 
     for (unsigned i = 0; i < m.numFuncPtrTables(); i++) {
-        if (!m.funcPtrTable(i).initialized())
-            return m.fail(nullptr, "expecting function-pointer table");
+        ModuleCompiler::FuncPtrTable& funcPtrTable = m.funcPtrTable(i);
+        if (!funcPtrTable.initialized()) {
+            return m.failNameOffset(funcPtrTable.firstUseOffset(),
+                                    "function-pointer table %s wasn't defined",
+                                    funcPtrTable.name());
+        }
     }
 
     return true;
 }
 
 static bool
 CheckModuleExportFunction(ModuleCompiler& m, ParseNode* pn, PropertyName* maybeFieldName = nullptr)
 {
@@ -10812,23 +10867,26 @@ CheckModuleExportObject(ModuleCompiler& 
 
     return true;
 }
 
 static bool
 CheckModuleReturn(ModuleCompiler& m)
 {
     TokenKind tk;
-    if (!PeekToken(m.parser(), &tk))
-        return false;
+    if (!GetToken(m.parser(), &tk))
+        return false;
+    TokenStream& ts = m.parser().tokenStream;
     if (tk != TOK_RETURN) {
-        if (tk == TOK_RC || tk == TOK_EOF)
-            return m.fail(nullptr, "expecting return statement");
-        return m.fail(nullptr, "invalid asm.js statement");
-    }
+        const char* msg = (tk == TOK_RC || tk == TOK_EOF)
+                          ? "expecting return statement"
+                          : "invalid asm.js. statement";
+        return m.failOffset(ts.currentToken().pos.begin, msg);
+    }
+    ts.ungetToken();
 
     ParseNode* returnStmt = m.parser().statement(YieldIsName);
     if (!returnStmt)
         return false;
 
     ParseNode* returnExpr = ReturnExpr(returnStmt);
     if (!returnExpr)
         return m.fail(returnStmt, "export statement must return something");
@@ -12015,36 +12073,40 @@ CheckModule(ExclusiveContext* cx, AsmJSP
         return false;
 
     if (!CheckModuleGlobals(m))
         return false;
 
     m.startFunctionBodies();
 
 #if !defined(ENABLE_SHARED_ARRAY_BUFFER)
-    if (m.module().hasArrayView() && m.module().isSharedView())
-        return m.fail(nullptr, "shared views not supported by this build");
+    MOZ_ASSERT(!m.module().hasArrayView() || !m.module().isSharedView());
 #endif
 
     if (!CheckFunctions(m))
         return false;
 
     m.finishFunctionBodies();
 
     if (!CheckFuncPtrTables(m))
         return false;
 
     if (!CheckModuleReturn(m))
         return false;
 
     TokenKind tk;
-    if (!PeekToken(m.parser(), &tk))
-        return false;
-    if (tk != TOK_EOF && tk != TOK_RC)
-        return m.fail(nullptr, "top-level export (return) must be the last statement");
+    if (!GetToken(m.parser(), &tk))
+        return false;
+    TokenStream& ts = m.parser().tokenStream;
+    if (tk == TOK_EOF || tk == TOK_RC) {
+        ts.ungetToken();
+    } else {
+        return m.failOffset(ts.currentToken().pos.begin,
+                            "top-level export (return) must be the last statement");
+    }
 
     // Delay flushing until dynamic linking. The inhibited range is set by the
     // masm.executableCopy() called transitively by FinishModule.
     AutoFlushICache afc("CheckModule", /* inhibit = */ true);
 
     ScopedJSDeletePtr<AsmJSModule> module;
     if (!FinishModule(m, &module))
         return false;
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1790,16 +1790,17 @@ ia64*-hpux*)
         dnl with the linker doing most of the work in the whole-program
         dnl optimization/PGO case. I think it's probably a compiler bug,
         dnl but we work around it here.
         PROFILE_USE_CFLAGS="-GL -wd4624 -wd4952"
         dnl XXX: should be -LTCG:PGOPTIMIZE, but that fails on libxul.
         dnl Probably also a compiler bug, but what can you do?
         PROFILE_USE_LDFLAGS="-LTCG:PGUPDATE"
         LDFLAGS="$LDFLAGS -DYNAMICBASE"
+        RCFLAGS="-nologo"
     fi
     AC_DEFINE(HAVE_SNPRINTF)
     AC_DEFINE(HAVE__MSIZE)
     AC_DEFINE(_WINDOWS)
     AC_DEFINE(WIN32)
     AC_DEFINE(XP_WIN)
     AC_DEFINE(XP_WIN32)
     AC_DEFINE(HW_THREADS)
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -423,27 +423,20 @@ BytecodeCompiler::checkArgumentsWithinEv
 {
     if (fun->hasRest()) {
         // It's an error to use |arguments| in a function that has a rest
         // parameter.
         parser->report(ParseError, false, nullptr, JSMSG_ARGUMENTS_AND_REST);
         return false;
     }
 
-    // Force construction of arguments objects for functions that use
-    // |arguments| within an eval.
     RootedScript script(cx, fun->getOrCreateScript(cx));
     if (!script)
         return false;
 
-    if (script->argumentsHasVarBinding()) {
-        if (!JSScript::argumentsOptimizationFailed(cx, script))
-            return false;
-    }
-
     // It's an error to use |arguments| in a legacy generator expression.
     if (script->isGeneratorExp() && script->isLegacyGenerator()) {
         parser->report(ParseError, false, nullptr, JSMSG_BAD_GENEXP_BODY, js_arguments_str);
         return false;
     }
 
     return true;
 }
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -951,24 +951,18 @@ Parser<FullParseHandler>::checkFunctionA
      * Now that all possible 'arguments' bindings have been added, note whether
      * 'arguments' has a local binding and whether it unconditionally needs an
      * arguments object. (Also see the flags' comments in ContextFlags.)
      */
     if (argumentsHasLocalBinding) {
         FunctionBox* funbox = pc->sc->asFunctionBox();
         funbox->setArgumentsHasLocalBinding();
 
-        /*
-         * If a script has both explicit mentions of 'arguments' and dynamic
-         * name lookups which could access the arguments, an arguments object
-         * must be created eagerly. The SSA analysis used for lazy arguments
-         * cannot cope with dynamic name accesses, so any 'arguments' accessed
-         * via a NAME opcode must force construction of the arguments object.
-         */
-        if (pc->sc->bindingsAccessedDynamically() && maybeArgDef)
+        /* Dynamic scope access destroys all hope of optimization. */
+        if (pc->sc->bindingsAccessedDynamically())
             funbox->setDefinitelyNeedsArgsObj();
 
         /*
          * If a script contains the debugger statement either directly or
          * within an inner function, the arguments object must be created
          * eagerly. The debugger can walk the scope chain and observe any
          * values along it.
          */
@@ -986,19 +980,16 @@ Parser<FullParseHandler>::checkFunctionA
             for (AtomDefnListMap::Range r = pc->decls().all(); !r.empty(); r.popFront()) {
                 DefinitionList& dlist = r.front().value();
                 for (DefinitionList::Range dr = dlist.all(); !dr.empty(); dr.popFront()) {
                     Definition* dn = dr.front<FullParseHandler>();
                     if (dn->kind() == Definition::ARG && dn->isAssigned())
                         funbox->setDefinitelyNeedsArgsObj();
                 }
             }
-            /* Watch for mutation of arguments through e.g. eval(). */
-            if (pc->sc->bindingsAccessedDynamically())
-                funbox->setDefinitelyNeedsArgsObj();
         }
     }
 
     return true;
 }
 
 template <>
 bool
--- a/js/src/frontend/TokenStream.h
+++ b/js/src/frontend/TokenStream.h
@@ -484,26 +484,16 @@ class MOZ_STACK_CLASS TokenStream
             break;
           default:
             MOZ_CRASH("unexpected modifier exception");
         }
         tokens[(cursor + 1) & ntokensMask].modifierException = modifierException;
 #endif
     }
 
-    bool hasLookahead() { return lookahead > 0; }
-
-    Modifier getLookaheadModifier() {
-#ifdef DEBUG
-        return nextToken().modifier;
-#else
-        return None;
-#endif
-    }
-
     void
     verifyConsistentModifier(Modifier modifier, Token lookaheadToken) {
 #ifdef DEBUG
         // Easy case: modifiers match.
         if (modifier == lookaheadToken.modifier)
             return;
 
         if (lookaheadToken.modifierException == OperandIsNone) {
@@ -986,16 +976,18 @@ class MOZ_STACK_CLASS TokenStream
     void updateLineInfoForEOL();
     void updateFlagsForEOL();
 
     const Token& nextToken() {
         MOZ_ASSERT(hasLookahead());
         return tokens[(cursor + 1) & ntokensMask];
     }
 
+    bool hasLookahead() { return lookahead > 0; }
+
     // Options used for parsing/tokenizing.
     const ReadOnlyCompileOptions& options_;
 
     Token               tokens[ntokens];    // circular token buffer
     unsigned            cursor;             // index of last parsed token
     unsigned            lookahead;          // count of lookahead tokens
     unsigned            lineno;             // current line number
     Flags               flags;              // flags -- see above
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -3495,55 +3495,16 @@ CodeGenerator::visitGetDynamicName(LGetD
     masm.loadValue(Address(masm.getStackPointer(), 0), out);
     masm.adjustStack(sizeof(Value));
 
     Label undefined;
     masm.branchTestUndefined(Assembler::Equal, out, &undefined);
     bailoutFrom(&undefined, lir->snapshot());
 }
 
-void
-CodeGenerator::emitFilterArgumentsOrEval(LInstruction* lir, Register string,
-                                         Register temp1, Register temp2)
-{
-    masm.loadJSContext(temp2);
-
-    masm.setupUnalignedABICall(temp1);
-    masm.passABIArg(temp2);
-    masm.passABIArg(string);
-    masm.callWithABI(JS_FUNC_TO_DATA_PTR(void*, FilterArgumentsOrEval));
-
-    Label bail;
-    masm.branchIfFalseBool(ReturnReg, &bail);
-    bailoutFrom(&bail, lir->snapshot());
-}
-
-void
-CodeGenerator::visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS* lir)
-{
-    emitFilterArgumentsOrEval(lir, ToRegister(lir->getString()),
-                              ToRegister(lir->temp1()),
-                              ToRegister(lir->temp2()));
-}
-
-void
-CodeGenerator::visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV* lir)
-{
-    ValueOperand input = ToValue(lir, LFilterArgumentsOrEvalV::Input);
-
-    // Act as nop on non-strings.
-    Label done;
-    masm.branchTestString(Assembler::NotEqual, input, &done);
-
-    emitFilterArgumentsOrEval(lir, masm.extractString(input, ToRegister(lir->temp3())),
-                              ToRegister(lir->temp1()), ToRegister(lir->temp2()));
-
-    masm.bind(&done);
-}
-
 typedef bool (*DirectEvalSFn)(JSContext*, HandleObject, HandleScript, HandleValue, HandleValue,
                               HandleString, jsbytecode*, MutableHandleValue);
 static const VMFunction DirectEvalStringInfo = FunctionInfo<DirectEvalSFn>(DirectEvalStringFromIon);
 
 void
 CodeGenerator::visitCallDirectEval(LCallDirectEval* lir)
 {
     Register scopeChain = ToRegister(lir->getScopeChain());
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -144,18 +144,16 @@ class CodeGenerator : public CodeGenerat
     void emitCallInvokeFunction(LApplyArgsGeneric* apply, Register extraStackSize);
     void emitPushArguments(LApplyArgsGeneric* apply, Register extraStackSpace);
     void emitPopArguments(LApplyArgsGeneric* apply, Register extraStackSize);
     void visitApplyArgsGeneric(LApplyArgsGeneric* apply);
     void visitBail(LBail* lir);
     void visitUnreachable(LUnreachable* unreachable);
     void visitEncodeSnapshot(LEncodeSnapshot* lir);
     void visitGetDynamicName(LGetDynamicName* lir);
-    void visitFilterArgumentsOrEvalS(LFilterArgumentsOrEvalS* lir);
-    void visitFilterArgumentsOrEvalV(LFilterArgumentsOrEvalV* lir);
     void visitCallDirectEval(LCallDirectEval* lir);
     void visitDoubleToInt32(LDoubleToInt32* lir);
     void visitFloat32ToInt32(LFloat32ToInt32* lir);
     void visitNewArrayCallVM(LNewArray* lir);
     void visitNewArray(LNewArray* lir);
     void visitOutOfLineNewArray(OutOfLineNewArray* ool);
     void visitNewArrayCopyOnWrite(LNewArrayCopyOnWrite* lir);
     void visitNewArrayDynamicLength(LNewArrayDynamicLength* lir);
--- a/js/src/jit/ExecutableAllocator.h
+++ b/js/src/jit/ExecutableAllocator.h
@@ -231,17 +231,21 @@ class ExecutableAllocator
         MOZ_ASSERT(pool->m_allocation.pages);
         if (destroyCallback) {
             // Do not allow GC during the page release callback.
             JS::AutoSuppressGCAnalysis nogc;
             destroyCallback(pool->m_allocation.pages, pool->m_allocation.size);
         }
         systemRelease(pool->m_allocation);
         MOZ_ASSERT(m_pools.initialized());
-        m_pools.remove(m_pools.lookup(pool));   // this asserts if |pool| is not in m_pools
+
+        // Pool may not be present in m_pools if we hit OOM during creation.
+        auto ptr = m_pools.lookup(pool);
+        if (ptr)
+            m_pools.remove(ptr);
     }
 
     void addSizeOfCode(JS::CodeSizes* sizes) const;
 
     void setDestroyCallback(DestroyCallback destroyCallback) {
         this->destroyCallback = destroyCallback;
     }
 
@@ -294,17 +298,23 @@ class ExecutableAllocator
         if (!a.pages)
             return nullptr;
 
         ExecutablePool* pool = js_new<ExecutablePool>(this, a);
         if (!pool) {
             systemRelease(a);
             return nullptr;
         }
-        m_pools.put(pool);
+
+        if (!m_pools.put(pool)) {
+            js_delete(pool);
+            systemRelease(a);
+            return nullptr;
+        }
+
         return pool;
     }
 
   public:
     ExecutablePool* poolForSize(size_t n)
     {
         // Try to fit in an existing small allocator.  Use the pool with the
         // least available space that is big enough (best-fit).  This is the
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -3465,36 +3465,24 @@ jit::AnalyzeArgumentsUsage(JSContext* cx
     MOZ_ASSERT(!script->analyzedArgsUsage());
 
     // Treat the script as needing an arguments object until we determine it
     // does not need one. This both allows us to easily see where the arguments
     // object can escape through assignments to the function's named arguments,
     // and also simplifies handling of early returns.
     script->setNeedsArgsObj(true);
 
-    // Always construct arguments objects when in debug mode and for generator
-    // scripts (generators can be suspended when speculation fails).
+    // Always construct arguments objects when in debug mode, for generator
+    // scripts (generators can be suspended when speculation fails) or when
+    // direct eval is present.
     //
     // FIXME: Don't build arguments for ES6 generator expressions.
-    if (scriptArg->isDebuggee() || script->isGenerator())
+    if (scriptArg->isDebuggee() || script->isGenerator() || script->bindingsAccessedDynamically())
         return true;
 
-    // If the script has dynamic name accesses which could reach 'arguments',
-    // the parser will already have checked to ensure there are no explicit
-    // uses of 'arguments' in the function. If there are such uses, the script
-    // will be marked as definitely needing an arguments object.
-    //
-    // New accesses on 'arguments' can occur through 'eval' or the debugger
-    // statement. In the former case, we will dynamically detect the use and
-    // mark the arguments optimization as having failed.
-    if (script->bindingsAccessedDynamically()) {
-        script->setNeedsArgsObj(false);
-        return true;
-    }
-
     if (!jit::IsIonEnabled(cx))
         return true;
 
     static const uint32_t MAX_SCRIPT_SIZE = 10000;
     if (script->length() > MAX_SCRIPT_SIZE)
         return true;
 
     if (!script->ensureHasTypes(cx))
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6594,19 +6594,16 @@ IonBuilder::jsop_eval(uint32_t argc)
                 CallInfo evalCallInfo(alloc(), /* constructing = */ false);
                 if (!evalCallInfo.init(current, /* argc = */ 0))
                     return false;
 
                 return makeCall(nullptr, evalCallInfo);
             }
         }
 
-        MInstruction* filterArguments = MFilterArgumentsOrEval::New(alloc(), string);
-        current->add(filterArguments);
-
         MInstruction* ins = MCallDirectEval::New(alloc(), scopeChain, string,
                                                  thisValue, newTargetValue, pc);
         current->add(ins);
         current->push(ins);
 
         TemporaryTypeSet* types = bytecodeTypes(pc);
         return resumeAfter(ins) && pushTypeBarrier(ins, types, BarrierKind::TypeSet);
     }
--- a/js/src/jit/JitcodeMap.cpp
+++ b/js/src/jit/JitcodeMap.cpp
@@ -850,17 +850,17 @@ JitcodeGlobalTable::sweep(JSRuntime* rt)
         JitcodeGlobalEntry* entry = e.front();
 
         if (!entry->zone()->isCollecting() || entry->zone()->isGCFinished())
             continue;
 
         if (entry->baseEntry().isJitcodeAboutToBeFinalized())
             e.removeFront();
         else
-            entry->sweep(rt);
+            entry->sweepChildren(rt);
     }
 }
 
 template <class ShouldMarkProvider>
 bool
 JitcodeGlobalEntry::BaseEntry::markJitcode(JSTracer* trc)
 {
     if (ShouldMarkProvider::ShouldMark(&jitcode_)) {
@@ -890,17 +890,17 @@ JitcodeGlobalEntry::BaselineEntry::mark(
     if (ShouldMarkProvider::ShouldMark(&script_)) {
         TraceManuallyBarrieredEdge(trc, &script_, "jitcodeglobaltable-baselineentry-script");
         return true;
     }
     return false;
 }
 
 void
-JitcodeGlobalEntry::BaselineEntry::sweep()
+JitcodeGlobalEntry::BaselineEntry::sweepChildren()
 {
     MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&script_));
 }
 
 bool
 JitcodeGlobalEntry::BaselineEntry::isMarkedFromAnyThread()
 {
     return IsMarkedUnbarriered(&script_) ||
@@ -941,17 +941,17 @@ JitcodeGlobalEntry::IonEntry::mark(JSTra
             markedAny = true;
         }
     }
 
     return markedAny;
 }
 
 void
-JitcodeGlobalEntry::IonEntry::sweep()
+JitcodeGlobalEntry::IonEntry::sweepChildren()
 {
     for (unsigned i = 0; i < numScripts(); i++)
         MOZ_ALWAYS_FALSE(IsAboutToBeFinalizedUnbarriered(&sizedScriptList()->pairs[i].script));
 
     if (!optsAllTypes_)
         return;
 
     for (IonTrackedTypeWithAddendum* iter = optsAllTypes_->begin();
@@ -999,21 +999,21 @@ bool
 JitcodeGlobalEntry::IonCacheEntry::mark(JSTracer* trc)
 {
     JitcodeGlobalEntry entry;
     RejoinEntry(trc->runtime(), *this, nativeStartAddr(), &entry);
     return entry.mark<ShouldMarkProvider>(trc);
 }
 
 void
-JitcodeGlobalEntry::IonCacheEntry::sweep(JSRuntime* rt)
+JitcodeGlobalEntry::IonCacheEntry::sweepChildren(JSRuntime* rt)
 {
     JitcodeGlobalEntry entry;
     RejoinEntry(rt, *this, nativeStartAddr(), &entry);
-    entry.sweep(rt);
+    entry.sweepChildren(rt);
 }
 
 bool
 JitcodeGlobalEntry::IonCacheEntry::isMarkedFromAnyThread(JSRuntime* rt)
 {
     JitcodeGlobalEntry entry;
     RejoinEntry(rt, *this, nativeStartAddr(), &entry);
     return entry.isMarkedFromAnyThread(rt);
--- a/js/src/jit/JitcodeMap.h
+++ b/js/src/jit/JitcodeMap.h
@@ -364,17 +364,17 @@ class JitcodeGlobalEntry
             uint32_t* entryOffsetOut);
 
         void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index,
                                         JS::ForEachTrackedOptimizationAttemptOp& op);
         void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index,
                                          IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
 
         template <class ShouldMarkProvider> bool mark(JSTracer* trc);
-        void sweep();
+        void sweepChildren();
         bool isMarkedFromAnyThread();
     };
 
     struct BaselineEntry : public BaseEntry
     {
         JSScript* script_;
         const char* str_;
 
@@ -422,17 +422,17 @@ class JitcodeGlobalEntry
 
         uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results,
                                  uint32_t maxResults) const;
 
         void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr,
                                          JSScript** script, jsbytecode** pc) const;
 
         template <class ShouldMarkProvider> bool mark(JSTracer* trc);
-        void sweep();
+        void sweepChildren();
         bool isMarkedFromAnyThread();
     };
 
     struct IonCacheEntry : public BaseEntry
     {
         void* rejoinAddr_;
         JS::TrackedOutcome trackedOutcome_;
 
@@ -471,17 +471,17 @@ class JitcodeGlobalEntry
             void* ptr,
             uint32_t* entryOffsetOut);
         void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index,
                                         JS::ForEachTrackedOptimizationAttemptOp& op);
         void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index,
                                          IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op);
 
         template <class ShouldMarkProvider> bool mark(JSTracer* trc);
-        void sweep(JSRuntime* rt);
+        void sweepChildren(JSRuntime* rt);
         bool isMarkedFromAnyThread(JSRuntime* rt);
     };
 
     // Dummy entries are created for jitcode generated when profiling is not turned on,
     // so that they have representation in the global table if they are on the
     // stack when profiling is enabled.
     struct DummyEntry : public BaseEntry
     {
@@ -926,26 +926,26 @@ class JitcodeGlobalEntry
           case Dummy:
             break;
           default:
             MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
         }
         return markedAny;
     }
 
-    void sweep(JSRuntime* rt) {
+    void sweepChildren(JSRuntime* rt) {
         switch (kind()) {
           case Ion:
-            ionEntry().sweep();
+            ionEntry().sweepChildren();
             break;
           case Baseline:
-            baselineEntry().sweep();
+            baselineEntry().sweepChildren();
             break;
           case IonCache:
-            ionCacheEntry().sweep(rt);
+            ionCacheEntry().sweepChildren(rt);
           case Dummy:
             break;
           default:
             MOZ_CRASH("Invalid JitcodeGlobalEntry kind.");
         }
     }
 
     bool isMarkedFromAnyThread(JSRuntime* rt) {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -581,40 +581,16 @@ LIRGenerator::visitGetDynamicName(MGetDy
                                                         tempFixed(CallTempReg3),
                                                         tempFixed(CallTempReg4));
 
     assignSnapshot(lir, Bailout_DynamicNameNotFound);
     defineReturn(lir, ins);
 }
 
 void
-LIRGenerator::visitFilterArgumentsOrEval(MFilterArgumentsOrEval* ins)
-{
-    MDefinition* string = ins->getString();
-    MOZ_ASSERT(string->type() == MIRType_String || string->type() == MIRType_Value);
-
-    LInstruction* lir;
-    if (string->type() == MIRType_String) {
-        lir = new(alloc()) LFilterArgumentsOrEvalS(useFixed(string, CallTempReg0),
-                                                   tempFixed(CallTempReg1),
-                                                   tempFixed(CallTempReg2));
-    } else {
-        lir = new(alloc()) LFilterArgumentsOrEvalV(tempFixed(CallTempReg0),
-                                                   tempFixed(CallTempReg1),
-                                                   tempFixed(CallTempReg2));
-        useBoxFixed(lir, LFilterArgumentsOrEvalV::Input, string,
-                    CallTempReg3, CallTempReg4);
-    }
-
-    assignSnapshot(lir, Bailout_StringArgumentsEval);
-    add(lir, ins);
-    assignSafepoint(lir, ins);
-}
-
-void
 LIRGenerator::visitCallDirectEval(MCallDirectEval* ins)
 {
     MDefinition* scopeChain = ins->getScopeChain();
     MOZ_ASSERT(scopeChain->type() == MIRType_Object);
 
     MDefinition* string = ins->getString();
     MOZ_ASSERT(string->type() == MIRType_String);
 
--- a/js/src/jit/Lowering.h
+++ b/js/src/jit/Lowering.h
@@ -101,17 +101,16 @@ class LIRGenerator : public LIRGenerator
     void visitApplyArgs(MApplyArgs* apply);
     void visitArraySplice(MArraySplice* splice);
     void visitBail(MBail* bail);
     void visitUnreachable(MUnreachable* unreachable);
     void visitEncodeSnapshot(MEncodeSnapshot* ins);
     void visitAssertFloat32(MAssertFloat32* ins);
     void visitAssertRecoveredOnBailout(MAssertRecoveredOnBailout* ins);
     void visitGetDynamicName(MGetDynamicName* ins);
-    void visitFilterArgumentsOrEval(MFilterArgumentsOrEval* ins);
     void visitCallDirectEval(MCallDirectEval* ins);
     void visitTest(MTest* test);
     void visitGotoWithFake(MGotoWithFake* ins);
     void visitFunctionDispatch(MFunctionDispatch* ins);
     void visitObjectGroupDispatch(MObjectGroupDispatch* ins);
     void visitCompare(MCompare* comp);
     void visitTypeOf(MTypeOf* ins);
     void visitToId(MToId* ins);
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -4046,44 +4046,16 @@ class MGetDynamicName
     MDefinition* getName() const {
         return getOperand(1);
     }
     bool possiblyCalls() const override {
         return true;
     }
 };
 
-// Bailout if the input string contains 'arguments' or 'eval'.
-class MFilterArgumentsOrEval
-  : public MAryInstruction<1>,
-    public BoxExceptPolicy<0, MIRType_String>::Data
-{
-  protected:
-    explicit MFilterArgumentsOrEval(MDefinition* string)
-    {
-        initOperand(0, string);
-        setGuard();
-        setResultType(MIRType_None);
-    }
-
-  public:
-    INSTRUCTION_HEADER(FilterArgumentsOrEval)
-
-    static MFilterArgumentsOrEval* New(TempAllocator& alloc, MDefinition* string) {
-        return new(alloc) MFilterArgumentsOrEval(string);
-    }
-
-    MDefinition* getString() const {
-        return getOperand(0);
-    }
-    bool possiblyCalls() const override {
-        return true;
-    }
-};
-
 class MCallDirectEval
   : public MAryInstruction<4>,
     public Mix4Policy<ObjectPolicy<0>,
                       StringPolicy<1>,
                       BoxPolicy<2>,
                       BoxPolicy<3> >::Data
 {
   protected:
--- a/js/src/jit/MOpcodes.h
+++ b/js/src/jit/MOpcodes.h
@@ -64,17 +64,16 @@ namespace jit {
     _(ApplyArgs)                                                            \
     _(ArraySplice)                                                          \
     _(Bail)                                                                 \
     _(Unreachable)                                                          \
     _(EncodeSnapshot)                                                       \
     _(AssertFloat32)                                                        \
     _(AssertRecoveredOnBailout)                                             \
     _(GetDynamicName)                                                       \
-    _(FilterArgumentsOrEval)                                                \
     _(CallDirectEval)                                                       \
     _(BitNot)                                                               \
     _(TypeOf)                                                               \
     _(ToId)                                                                 \
     _(BitAnd)                                                               \
     _(BitOr)                                                                \
     _(BitXor)                                                               \
     _(Lsh)                                                                  \
--- a/js/src/jit/VMFunctions.cpp
+++ b/js/src/jit/VMFunctions.cpp
@@ -583,35 +583,16 @@ GetDynamicName(JSContext* cx, JSObject* 
     if (LookupNameNoGC(cx, atom->asPropertyName(), scopeChain, &scope, &pobj, &shape)) {
         if (FetchNameNoGC(pobj, shape, MutableHandleValue::fromMarkedLocation(vp)))
             return;
     }
 
     vp->setUndefined();
 }
 
-bool
-FilterArgumentsOrEval(JSContext* cx, JSString* str)
-{
-    // ensureLinear() is fallible, but cannot GC: it can only allocate a
-    // character buffer for the flattened string. If this call fails then the
-    // calling Ion code will bailout, resume in Baseline and likely fail again
-    // when trying to flatten the string and unwind the stack.
-    JS::AutoCheckCannotGC nogc;
-    JSLinearString* linear = str->ensureLinear(cx);
-    if (!linear)
-        return false;
-
-    static const char16_t arguments[] = {'a', 'r', 'g', 'u', 'm', 'e', 'n', 't', 's'};
-    static const char16_t eval[] = {'e', 'v', 'a', 'l'};
-
-    return !StringHasPattern(linear, arguments, mozilla::ArrayLength(arguments)) &&
-        !StringHasPattern(linear, eval, mozilla::ArrayLength(eval));
-}
-
 void
 PostWriteBarrier(JSRuntime* rt, JSObject* obj)
 {
     MOZ_ASSERT(!IsInsideNursery(obj));
     rt->gc.storeBuffer.putWholeCellFromMainThread(obj);
 }
 
 void
--- a/js/src/jit/VMFunctions.h
+++ b/js/src/jit/VMFunctions.h
@@ -632,18 +632,16 @@ bool OperatorIn(JSContext* cx, HandleVal
 bool OperatorInI(JSContext* cx, uint32_t index, HandleObject obj, bool* out);
 
 bool GetIntrinsicValue(JSContext* cx, HandlePropertyName name, MutableHandleValue rval);
 
 bool CreateThis(JSContext* cx, HandleObject callee, MutableHandleValue rval);
 
 void GetDynamicName(JSContext* cx, JSObject* scopeChain, JSString* str, Value* vp);
 
-bool FilterArgumentsOrEval(JSContext* cx, JSString* str);
-
 void PostWriteBarrier(JSRuntime* rt, JSObject* obj);
 void PostGlobalWriteBarrier(JSRuntime* rt, JSObject* obj);
 
 uint32_t GetIndexFromString(JSString* str);
 
 bool DebugPrologue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool* mustReturn);
 bool DebugEpilogue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc, bool ok);
 bool DebugEpilogueOnBaselineReturn(JSContext* cx, BaselineFrame* frame, jsbytecode* pc);
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1869,74 +1869,16 @@ class LGetDynamicName : public LCallInst
     const LDefinition* temp2() {
         return getTemp(1);
     }
     const LDefinition* temp3() {
         return getTemp(2);
     }
 };
 
-class LFilterArgumentsOrEvalS : public LCallInstructionHelper<0, 1, 2>
-{
-  public:
-    LIR_HEADER(FilterArgumentsOrEvalS)
-
-    LFilterArgumentsOrEvalS(const LAllocation& string, const LDefinition& temp1,
-                            const LDefinition& temp2)
-    {
-        setOperand(0, string);
-        setTemp(0, temp1);
-        setTemp(1, temp2);
-    }
-
-    MFilterArgumentsOrEval* mir() const {
-        return mir_->toFilterArgumentsOrEval();
-    }
-
-    const LAllocation* getString() {
-        return getOperand(0);
-    }
-    const LDefinition* temp1() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-};
-
-class LFilterArgumentsOrEvalV : public LCallInstructionHelper<0, BOX_PIECES, 3>
-{
-  public:
-    LIR_HEADER(FilterArgumentsOrEvalV)
-
-    LFilterArgumentsOrEvalV(const LDefinition& temp1, const LDefinition& temp2,
-                            const LDefinition& temp3)
-    {
-        setTemp(0, temp1);
-        setTemp(1, temp2);
-        setTemp(2, temp3);
-    }
-
-    static const size_t Input = 0;
-
-    MFilterArgumentsOrEval* mir() const {
-        return mir_->toFilterArgumentsOrEval();
-    }
-
-    const LDefinition* temp1() {
-        return getTemp(0);
-    }
-    const LDefinition* temp2() {
-        return getTemp(1);
-    }
-    const LDefinition* temp3() {
-        return getTemp(2);
-    }
-};
-
 class LCallDirectEval : public LCallInstructionHelper<BOX_PIECES, 2 + (2 * BOX_PIECES), 0>
 {
   public:
     LIR_HEADER(CallDirectEval)
 
     LCallDirectEval(const LAllocation& scopeChain, const LAllocation& string)
     {
         setOperand(0, scopeChain);
--- a/js/src/jit/shared/LOpcodes-shared.h
+++ b/js/src/jit/shared/LOpcodes-shared.h
@@ -72,18 +72,16 @@
     _(CallKnown)                    \
     _(CallGeneric)                  \
     _(CallNative)                   \
     _(ApplyArgsGeneric)             \
     _(Bail)                         \
     _(Unreachable)                  \
     _(EncodeSnapshot)               \
     _(GetDynamicName)               \
-    _(FilterArgumentsOrEvalS)       \
-    _(FilterArgumentsOrEvalV)       \
     _(CallDirectEval)               \
     _(StackArgT)                    \
     _(StackArgV)                    \
     _(CreateThis)                   \
     _(CreateThisWithProto)          \
     _(CreateThisWithTemplate)       \
     _(CreateArgumentsObject)        \
     _(GetArgumentsObjectArg)        \
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -2363,17 +2363,17 @@ nsIFrame::BuildDisplayListForChild(nsDis
     // but the spec says it acts like the rest of these
     || disp->mChildPerspective.GetUnit() == eStyleUnit_Coord
     || disp->mMixBlendMode != NS_STYLE_BLEND_NORMAL
     || nsSVGIntegrationUtils::UsingEffectsForFrame(child)
     || (child->GetStateBits() & NS_FRAME_HAS_VR_CONTENT);
 
   bool isPositioned = disp->IsAbsPosContainingBlock(child);
   bool isStackingContext =
-    (isPositioned && (disp->mPosition == NS_STYLE_POSITION_STICKY ||
+    (isPositioned && (disp->IsPositionForcingStackingContext() ||
                       pos->mZIndex.GetUnit() == eStyleUnit_Integer)) ||
      (disp->mWillChangeBitField & NS_STYLE_WILL_CHANGE_STACKING_CONTEXT) ||
      disp->mIsolation != NS_STYLE_ISOLATION_AUTO ||
      isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT);
 
   if (isVisuallyAtomic || isPositioned || (!isSVG && disp->IsFloating(child)) ||
       ((disp->mClipFlags & NS_STYLE_CLIP_RECT) &&
        IsSVGContentWithCSSClip(child)) ||
--- a/layout/inspector/inDOMUtils.cpp
+++ b/layout/inspector/inDOMUtils.cpp
@@ -7,16 +7,17 @@
 #include "mozilla/EventStates.h"
 
 #include "inDOMUtils.h"
 #include "inLayoutUtils.h"
 
 #include "nsIServiceManager.h"
 #include "nsISupportsArray.h"
 #include "nsString.h"
+#include "nsIStyleSheetLinkingElement.h"
 #include "nsIDOMElement.h"
 #include "nsIDocument.h"
 #include "nsIPresShell.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMCharacterData.h"
 #include "nsRuleNode.h"
 #include "nsIStyleRule.h"
 #include "mozilla/css/StyleRule.h"
@@ -297,16 +298,43 @@ inDOMUtils::GetRuleColumn(nsIDOMCSSRule*
     return NS_ERROR_FAILURE;
   }
 
   *_retval = rule->GetColumnNumber();
   return NS_OK;
 }
 
 NS_IMETHODIMP
+inDOMUtils::GetRelativeRuleLine(nsIDOMCSSRule* aRule, uint32_t* _retval)
+{
+  NS_ENSURE_ARG_POINTER(aRule);
+
+  Rule* rule = aRule->GetCSSRule();
+  if (!rule) {
+    return NS_ERROR_FAILURE;
+  }
+
+  uint32_t lineNumber = rule->GetLineNumber();
+  CSSStyleSheet* sheet = rule->GetStyleSheet();
+  if (sheet) {
+    nsINode* owningNode = sheet->GetOwnerNode();
+    if (owningNode) {
+      nsCOMPtr<nsIStyleSheetLinkingElement> link =
+        do_QueryInterface(owningNode);
+      if (link) {
+        lineNumber -= link->GetLineNumber() - 1;
+      }
+    }
+  }
+
+  *_retval = lineNumber;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 inDOMUtils::GetCSSLexer(const nsAString& aText, JSContext* aCx,
                         JS::MutableHandleValue aResult)
 {
   MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
   JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
   nsAutoPtr<CSSLexer> lexer(new CSSLexer(aText));
   if (!WrapNewBindingNonWrapperCachedObject(aCx, scope, lexer, aResult)) {
     return NS_ERROR_FAILURE;
--- a/layout/inspector/inIDOMUtils.idl
+++ b/layout/inspector/inIDOMUtils.idl
@@ -12,27 +12,37 @@ interface nsIDOMDocument;
 interface nsIDOMCSSRule;
 interface nsIDOMCSSStyleRule;
 interface nsIDOMNode;
 interface nsIDOMNodeList;
 interface nsIDOMFontFaceList;
 interface nsIDOMRange;
 interface nsIDOMCSSStyleSheet;
 
-[scriptable, uuid(60b4cbf7-2a08-4419-8937-6ef495417824)]
+[scriptable, uuid(d67c0463-592e-4d7c-b67e-923ee3f6c643)]
 interface inIDOMUtils : nsISupports
 {
   // CSS utilities
   void getAllStyleSheets (in nsIDOMDocument aDoc,
                           [optional] out unsigned long aLength,
                           [array, size_is (aLength), retval] out nsISupports aSheets);
   nsISupportsArray getCSSStyleRules(in nsIDOMElement aElement, [optional] in DOMString aPseudo);
   unsigned long getRuleLine(in nsIDOMCSSRule aRule);
   unsigned long getRuleColumn(in nsIDOMCSSRule aRule);
 
+  /**
+   * Like getRuleLine, but if the rule is in a <style> element,
+   * returns a line number relative to the start of the element.
+   *
+   * @param nsIDOMCSSRule aRule the rule to examine
+   * @return the line number of the rule, possibly relative to the
+   *         <style> element
+   */
+  unsigned long getRelativeRuleLine(in nsIDOMCSSRule aRule);
+
   [implicit_jscontext]
   jsval getCSSLexer(in DOMString aText);
 
   // Utilities for working with selectors.  We don't have a JS OM representation
   // of a single selector or a selector list yet, but given a rule we can index
   // into the selector list.
   //
   // This is a somewhat backwards API; once we move StyleRule to WebIDL we
--- a/layout/inspector/tests/mochitest.ini
+++ b/layout/inspector/tests/mochitest.ini
@@ -11,12 +11,13 @@ support-files =
 [test_bug557726.html]
 [test_bug609549.xhtml]
 [test_bug806192.html]
 [test_bug856317.html]
 [test_bug877690.html]
 [test_bug1006595.html]
 [test_color_to_rgba.html]
 [test_css_property_is_valid.html]
+[test_getRelativeRuleLine.html]
 [test_get_all_style_sheets.html]
 [test_is_valid_css_color.html]
 [test_isinheritableproperty.html]
 [test_selectormatcheselement.html]
new file mode 100644
--- /dev/null
+++ b/layout/inspector/tests/test_getRelativeRuleLine.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test inDOMUtils::getRelativeRuleLine</title>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <style>
+    @supports (not (whatever: 72 zq)) {
+      #test {
+	background-color: #f0c;
+      }
+    }
+
+    #test {
+      color: #f0c;
+    }
+  </style>
+  <style>#test { color: red; }</style>
+  <style>
+     @invalidatkeyword {
+     }
+
+     #test {
+       color: blue;
+     }
+  </style>
+  <script type="application/javascript;version=1.8">
+  let utils = SpecialPowers.Cc["@mozilla.org/inspector/dom-utils;1"]
+                           .getService(SpecialPowers.Ci.inIDOMUtils);
+
+  let tests = [
+    { sheetNo: 0, ruleNo: 0, lineNo: 1, columnNo: 1 },
+    { sheetNo: 1, ruleNo: 0, lineNo: 2, columnNo: 15 },
+    { sheetNo: 1, ruleNo: 1, lineNo: 8, columnNo: 5 },
+    { sheetNo: 2, ruleNo: 0, lineNo: 1, columnNo: 1 },
+    { sheetNo: 3, ruleNo: 0, lineNo: 5, columnNo: 6 },
+  ];
+
+  function doTest() {
+    for (let test of tests) {
+      let sheet = document.styleSheets[test.sheetNo];
+      let rule = sheet.cssRules[test.ruleNo];
+      let line = utils.getRelativeRuleLine(rule);
+      let column = utils.getRuleColumn(rule);
+      info("testing sheet " + test.sheetNo + ", rule " + test.ruleNo);
+      is(line, test.lineNo, "line number is correct");
+      is(column, test.columnNo, "column number is correct");
+    }
+
+    SimpleTest.finish();
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  addLoadEvent(doTest);
+  </script>
+</head>
+<body>
+<h1>Test inDOMUtils::getRelativeRuleLine</h1>
+<p id="display"></p>
+<div id="content" style="display: none">
+
+</div>
+<pre id="test">
+</pre>
+</body>
+</html>
--- a/layout/mathml/nsMathMLChar.h
+++ b/layout/mathml/nsMathMLChar.h
@@ -56,17 +56,17 @@ struct nsGlyphCode {
     char16_t code[2];
     uint32_t glyphID;
   };
   int8_t   font;
 
   bool IsGlyphID() const { return font == -1; }
 
   int32_t Length() const {
-    return (IsGlyphID() || code[1] == PRUnichar('\0') ? 1 : 2);
+    return (IsGlyphID() || code[1] == char16_t('\0') ? 1 : 2);
   }
   bool Exists() const
   {
     return IsGlyphID() ? glyphID != 0 : code[0] != 0;
   }
   bool operator==(const nsGlyphCode& other) const
   {
     return (other.font == font &&
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1179288-1-ref.html
@@ -0,0 +1,3 @@
+<!DOCTYPE HTML>
+<body>
+<div style="position:absolute; left:0; top:0; width:100px; height:100px; background:lime"></div>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/bugs/1179288-1.html
@@ -0,0 +1,6 @@
+<!DOCTYPE HTML>
+<body>
+<div style="position:fixed; left:0; top:0">
+  <div style="position:absolute; z-index:2; left:0; top:0; width:100px; height:100px; background:red"></div>
+</div>
+<div style="position:absolute; z-index:1; left:0; top:0; width:100px; height:100px; background:lime"></div>
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1924,11 +1924,12 @@ skip-if(!B2G) fuzzy-if(B2G,102,577) == 1
 skip-if(!B2G) fuzzy-if(B2G,101,887) == 1133905-6-vh-rtl.html 1133905-ref-vh-rtl.html
 skip-if(B2G||Mulet) == 1150021-1.xul 1150021-1-ref.xul
 == 1151145-1.html 1151145-1-ref.html
 == 1151306-1.html 1151306-1-ref.html
 == 1153845-1.html 1153845-1-ref.html
 == 1155828-1.html 1155828-1-ref.html
 == 1156129-1.html 1156129-1-ref.html
 == 1169331-1.html 1169331-1-ref.html
+fuzzy(1,74) fuzzy-if(gtkWidget,6,79) == 1174332-1.html 1174332-1-ref.html
 == 1179078-1.html 1179078-1-ref.html
-fuzzy(1,74) fuzzy-if(gtkWidget,6,79) == 1174332-1.html 1174332-1-ref.html
+== 1179288-1.html 1179288-1-ref.html
 == 1190635-1.html 1190635-1-ref.html
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-align-self-baseline-vert-001.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-align-self-baseline-vert-001.html
@@ -23,18 +23,16 @@
       margin: 0;
       font: 20px Ahem;
       line-height: 20px;
       /* Baseline is 0.8em = 16px from top */
     }
     .flexContainer {
       display: inline-flex;
       flex-direction: column;
-      display: -webkit-inline-flex;
-      -webkit-flex-direction: column;
       background: lightblue;
       align-items: baseline;
     }
     .hugeAndUnaligned {
       font-size: 35px;
       line-height: 35px;
       /* This one flex item won't be baseline-aligned, so it won't impact
          the flex container's positioning */
--- a/layout/reftests/w3c-css/submitted/reftest.list
+++ b/layout/reftests/w3c-css/submitted/reftest.list
@@ -64,8 +64,11 @@ include transforms/reftest.list
 # User Interface Level 3
 include ui3/reftest.list
 
 # Values and Units Level 3
 include values3/reftest.list
 
 # Variables Level 1
 include variables/reftest.list
+
+# CSS will-change Level 1
+include will-change/reftest.list
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/green-square-100-by-100-ref.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change reference</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px; background: green }
+</style>
+<body>
+  <div></div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/reftest.list
@@ -0,0 +1,12 @@
+== will-change-stacking-context-clip-path-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-filter-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-height-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-isolation-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-mask-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-mix-blend-mode-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-opacity-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-perspective-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-position-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-transform-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-transform-style-1.html green-square-100-by-100-ref.html
+== will-change-stacking-context-z-index-1.html green-square-100-by-100-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-clip-path-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: clip-path' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/css-masking/#the-clip-path">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: clip-path; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-filter-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: filter' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/filter-effects/#FilterProperty">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: filter; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-height-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: height' does not create a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: height; background: green }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: red }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-isolation-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: isolation' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/compositing-1/#isolation">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: isolation; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-mask-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: mask' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/css-masking/#the-mask-image">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: mask; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-mix-blend-mode-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: mix-blend-mode' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/compositing-1/#mix-blend-mode">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: mix-blend-mode; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-opacity-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: opacity' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="https://drafts.csswg.org/css-color-3/#transparency">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: opacity; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-perspective-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: perspective' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/css3-transforms/#perspective-property">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: perspective; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-position-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: position' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/css3-positioning/#sticky-pos">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: position; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-transform-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: transform' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/css3-transforms/#transform-property">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: transform; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-transform-style-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: transform-style' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/css3-transforms/#transform-style-property">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: transform-style; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/w3c-css/submitted/will-change/will-change-stacking-context-z-index-1.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>CSS will-change: 'will-change: z-index' creates a stacking context</title>
+<link rel="author" title="L. David Baron" href="http://dbaron.org/">
+<link rel="author" title="Mozilla" href="http://www.mozilla.org/">
+<link rel="help" href="https://drafts.csswg.org/css-will-change-1/#will-change">
+<link rel="help" href="http://www.w3.org/TR/css3-positioning/#layered-presentation">
+<link rel="match" href="green-square-100-by-100-ref.html">
+<meta name="assert" content="If any non-initial value of a property would create a stacking context on the element, specifying that property in will-change must create a stacking context on the element.">
+<style>
+html, body { margin: 0; padding: 0; }
+div { width: 100px; height: 100px }
+#wc { will-change: z-index; background: red }
+#child { position: absolute; top: 0; left: 0; z-index: -1; background: green }
+</style>
+<body>
+  <div id="wc">
+    <div id="child">
+    </div>
+  </div>
+</body>
--- a/layout/style/AnimationCommon.cpp
+++ b/layout/style/AnimationCommon.cpp
@@ -284,38 +284,27 @@ CommonAnimationManager::AddStyleUpdatesT
 {
   TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
 
   PRCList* next = PR_LIST_HEAD(&mElementCollections);
   while (next != &mElementCollections) {
     AnimationCollection* collection = static_cast<AnimationCollection*>(next);
     next = PR_NEXT_LINK(next);
 
-    collection->EnsureStyleRuleFor(now, EnsureStyleRule_IsNotThrottled);
+    collection->EnsureStyleRuleFor(now);
 
     dom::Element* elementToRestyle = collection->GetElementToRestyle();
     if (elementToRestyle) {
       nsRestyleHint rshint = collection->IsForTransitions()
         ? eRestyle_CSSTransitions : eRestyle_CSSAnimations;
       aTracker.AddPendingRestyle(elementToRestyle, rshint, nsChangeHint(0));
     }
   }
 }
 
-void
-CommonAnimationManager::NotifyCollectionUpdated(AnimationCollection&
-                                                  aCollection)
-{
-  MaybeStartObservingRefreshDriver();
-  mPresContext->ClearLastStyleUpdateForAllAnimations();
-  mPresContext->RestyleManager()->IncrementAnimationGeneration();
-  aCollection.UpdateAnimationGeneration(mPresContext);
-  aCollection.PostRestyleForAnimation(mPresContext);
-}
-
 /* static */ bool
 CommonAnimationManager::ExtractComputedValueForTransition(
                           nsCSSProperty aProperty,
                           nsStyleContext* aStyleContext,
                           StyleAnimationValue& aComputedValue)
 {
   bool result = StyleAnimationValue::ExtractComputedValue(aProperty,
                                                           aStyleContext,
@@ -386,29 +375,22 @@ CommonAnimationManager::FlushAnimations(
        l != &mElementCollections;
        l = PR_NEXT_LINK(l)) {
     AnimationCollection* collection = static_cast<AnimationCollection*>(l);
 
     if (collection->mStyleRuleRefreshTime == now) {
       continue;
     }
 
+    if (aFlags == Cannot_Throttle) {
+      collection->RequestRestyle(AnimationCollection::RestyleType::Standard);
+    }
+
     nsAutoAnimationMutationBatch mb(collection->mElement);
     collection->Tick();
-
-    bool canThrottleTick = aFlags == Can_Throttle;
-    for (auto iter = collection->mAnimations.cbegin();
-         canThrottleTick && iter != collection->mAnimations.cend();
-         ++iter) {
-      canThrottleTick &= (*iter)->CanThrottle();
-    }
-
-    collection->RequestRestyle(canThrottleTick ?
-                               AnimationCollection::RestyleType::Throttled :
-                               AnimationCollection::RestyleType::Standard);
   }
 
   MaybeStartOrStopObservingRefreshDriver();
 }
 
 nsIStyleRule*
 CommonAnimationManager::GetAnimationRule(mozilla::dom::Element* aElement,
                                          nsCSSPseudoElements::Type aPseudoType)
@@ -431,18 +413,17 @@ CommonAnimationManager::GetAnimationRule
   }
 
   RestyleManager* restyleManager = mPresContext->RestyleManager();
   if (restyleManager->SkipAnimationRules()) {
     return nullptr;
   }
 
   collection->EnsureStyleRuleFor(
-    mPresContext->RefreshDriver()->MostRecentRefresh(),
-    EnsureStyleRule_IsNotThrottled);
+    mPresContext->RefreshDriver()->MostRecentRefresh());
 
   return collection->mStyleRule;
 }
 
 /* static */ const CommonAnimationManager::LayerAnimationRecord
   CommonAnimationManager::sLayerAnimationInfo[] =
     { { eCSSProperty_transform,
         nsDisplayItem::TYPE_TRANSFORM,
@@ -726,40 +707,16 @@ AnimationCollection::CanPerformOnComposi
   // No properties to animate
   if (!existsProperty) {
     return false;
   }
 
   return true;
 }
 
-void
-AnimationCollection::PostUpdateLayerAnimations()
-{
-  nsCSSPropertySet propsHandled;
-  for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
-    const auto& properties = mAnimations[animIdx]->GetEffect()->Properties();
-    for (size_t propIdx = properties.Length(); propIdx-- != 0; ) {
-      nsCSSProperty prop = properties[propIdx].mProperty;
-      if (nsCSSProps::PropHasFlags(prop,
-                                   CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR) &&
-          !propsHandled.HasProperty(prop)) {
-        propsHandled.AddProperty(prop);
-        nsChangeHint changeHint = CommonAnimationManager::
-          LayerAnimationRecordFor(prop)->mChangeHint;
-        dom::Element* element = GetElementToRestyle();
-        if (element) {
-          mManager->mPresContext->RestyleManager()->
-            PostRestyleEvent(element, nsRestyleHint(0), changeHint);
-        }
-      }
-    }
-  }
-}
-
 bool
 AnimationCollection::HasCurrentAnimationOfProperty(nsCSSProperty
                                                      aProperty) const
 {
   for (Animation* animation : mAnimations) {
     if (animation->HasCurrentEffect() &&
         animation->GetEffect()->HasAnimationOfProperty(aProperty)) {
       return true;
@@ -804,26 +761,16 @@ AnimationCollection::GetElementToRestyle
     return nullptr;
   }
   if (!pseudoFrame) {
     return nullptr;
   }
   return pseudoFrame->GetContent()->AsElement();
 }
 
-void
-AnimationCollection::NotifyAnimationUpdated()
-{
-  // On the next flush, force us to update the style rule
-  mNeedsRefreshes = true;
-  mStyleRuleRefreshTime = TimeStamp();
-
-  mManager->NotifyCollectionUpdated(*this);
-}
-
 /* static */ void
 AnimationCollection::LogAsyncAnimationFailure(nsCString& aMessage,
                                                      const nsIContent* aContent)
 {
   if (aContent) {
     aMessage.AppendLiteral(" [");
     aMessage.Append(nsAtomCString(aContent->NodeInfo()->NameAtom()));
 
@@ -864,51 +811,31 @@ AnimationCollection::Tick()
 {
   for (size_t animIdx = 0, animEnd = mAnimations.Length();
        animIdx != animEnd; animIdx++) {
     mAnimations[animIdx]->Tick();
   }
 }
 
 void
-AnimationCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime,
-                                        EnsureStyleRuleFlags aFlags)
+AnimationCollection::EnsureStyleRuleFor(TimeStamp aRefreshTime)
 {
   mHasPendingAnimationRestyle = false;
 
   if (!mNeedsRefreshes) {
     mStyleRuleRefreshTime = aRefreshTime;
     return;
   }
 
   if (!mStyleRuleRefreshTime.IsNull() &&
       mStyleRuleRefreshTime == aRefreshTime) {
     // mStyleRule may be null and valid, if we have no style to apply.
     return;
   }
 
-  // If we're performing animations on the compositor thread, then we can skip
-  // most of the work in this method. But even if we are throttled, then we
-  // have to do the work if an animation is ending in order to get correct end
-  // of animation behavior (the styles of the animation disappear, or the fill
-  // mode behavior). CanThrottle returns false for any finishing animations
-  // so we can force style recalculation in that case.
-  if (aFlags == EnsureStyleRule_IsThrottled) {
-    for (size_t animIdx = mAnimations.Length(); animIdx-- != 0; ) {
-      if (!mAnimations[animIdx]->CanThrottle()) {
-        aFlags = EnsureStyleRule_IsNotThrottled;
-        break;
-      }
-    }
-  }
-
-  if (aFlags == EnsureStyleRule_IsThrottled) {
-    return;
-  }
-
   if (mManager->IsAnimationManager()) {
     // Update cascade results before updating the style rule, since the
     // cascade results can influence the style rule.
     static_cast<nsAnimationManager*>(mManager)->MaybeUpdateCascadeResults(this);
   }
 
   mStyleRuleRefreshTime = aRefreshTime;
   mStyleRule = nullptr;
@@ -1025,43 +952,58 @@ AnimationCollection::RequestRestyle(Rest
     // Pres context will be null after the manager is disconnected.
     return;
   }
 
   MOZ_ASSERT(mElement->GetCrossShadowCurrentDoc() == presContext->Document(),
              "Element::UnbindFromTree should have destroyed the element "
              "transition/animations object");
 
-  // SetNeedStyleFlush is cheap and required regardless of the restyle type
-  // so we do it unconditionally. Furthermore, if the posted animation restyle
-  // has been postponed due to the element being display:none (i.e.
-  // mHasPendingAnimationRestyle is set) then we should still mark the
-  // document as needing a style flush.
-  presContext->Document()->SetNeedStyleFlush();
+  // Steps for Restyle::Layer:
+
+  if (aRestyleType == RestyleType::Layer) {
+    mStyleRuleRefreshTime = TimeStamp();
+    // FIXME: We should be able to remove these two lines once we move
+    // ticking to animation timelines as part of bug 1151731.
+    mNeedsRefreshes = true;
+    mManager->MaybeStartObservingRefreshDriver();
 
-  // If we are already waiting on an animation restyle then there's nothing
-  // more to do.
+    // Prompt layers to re-sync their animations.
+    presContext->ClearLastStyleUpdateForAllAnimations();
+    presContext->RestyleManager()->IncrementAnimationGeneration();
+    UpdateAnimationGeneration(presContext);
+  }
+
+  // Steps for RestyleType::Standard and above:
+
   if (mHasPendingAnimationRestyle) {
     return;
   }
 
   // Upgrade throttled restyles if other factors prevent
   // throttling (e.g. async animations are not enabled).
   if (aRestyleType == RestyleType::Throttled) {
     TimeStamp now = presContext->RefreshDriver()->MostRecentRefresh();
     if (!CanPerformOnCompositorThread(CanAnimateFlags(0)) ||
         !CanThrottleAnimation(now)) {
       aRestyleType = RestyleType::Standard;
     }
   }
 
-  if (aRestyleType == RestyleType::Standard) {
+  if (aRestyleType >= RestyleType::Standard) {
     mHasPendingAnimationRestyle = true;
     PostRestyleForAnimation(presContext);
+    return;
   }
+
+  // Steps for RestyleType::Throttled:
+
+  MOZ_ASSERT(aRestyleType == RestyleType::Throttled,
+             "Should have already handled all non-throttled restyles");
+  presContext->Document()->SetNeedStyleFlush();
 }
 
 void
 AnimationCollection::UpdateAnimationGeneration(nsPresContext* aPresContext)
 {
   mAnimationGeneration =
     aPresContext->RestyleManager()->GetAnimationGeneration();
 }
--- a/layout/style/AnimationCommon.h
+++ b/layout/style/AnimationCommon.h
@@ -95,20 +95,16 @@ public:
           aContent->GetProperty(nsGkAtoms::transitionsProperty)) {
         return true;
       }
     } while ((aContent = aContent->GetParent()));
 
     return false;
   }
 
-  // Notify this manager that one of its collections of animations,
-  // has been updated.
-  void NotifyCollectionUpdated(AnimationCollection& aCollection);
-
   enum FlushFlags {
     Can_Throttle,
     Cannot_Throttle
   };
   void FlushAnimations(FlushFlags aFlags);
 
   nsIStyleRule* GetAnimationRule(dom::Element* aElement,
                                  nsCSSPseudoElements::Type aPseudoType);
@@ -233,21 +229,16 @@ public:
 private:
   ~AnimValuesStyleRule() {}
 
   InfallibleTArray<PropertyValuePair> mPropertyValuePairs;
 };
 
 typedef InfallibleTArray<nsRefPtr<dom::Animation>> AnimationPtrArray;
 
-enum EnsureStyleRuleFlags {
-  EnsureStyleRule_IsThrottled,
-  EnsureStyleRule_IsNotThrottled
-};
-
 struct AnimationCollection : public PRCList
 {
   AnimationCollection(dom::Element *aElement, nsIAtom *aElementProperty,
                       CommonAnimationManager *aManager)
     : mElement(aElement)
     , mElementProperty(aElementProperty)
     , mManager(aManager)
     , mAnimationGeneration(0)
@@ -276,33 +267,41 @@ struct AnimationCollection : public PRCL
     mElement->DeleteProperty(mElementProperty);
   }
 
   static void PropertyDtor(void *aObject, nsIAtom *aPropertyName,
                            void *aPropertyValue, void *aData);
 
   void Tick();
 
-  void EnsureStyleRuleFor(TimeStamp aRefreshTime, EnsureStyleRuleFlags aFlags);
+  void EnsureStyleRuleFor(TimeStamp aRefreshTime);
 
   enum CanAnimateFlags {
     // Testing for width, height, top, right, bottom, or left.
     CanAnimate_HasGeometricProperty = 1,
     // Allow the case where OMTA is allowed in general, but not for the
     // specified property.
     CanAnimate_AllowPartial = 2
   };
 
   enum class RestyleType {
     // Animation style has changed but the compositor is applying the same
     // change so we might be able to defer updating the main thread until it
     // becomes necessary.
     Throttled,
     // Animation style has changed and needs to be updated on the main thread.
-    Standard
+    Standard,
+    // Animation style has changed and needs to be updated on the main thread
+    // as well as forcing animations on layers to be updated.
+    // This is needed in cases such as when an animation becomes paused or has
+    // its playback rate changed. In such a case, although the computed style
+    // and refresh driver time might not change, we still need to ensure the
+    // corresponding animations on layers are updated to reflect the new
+    // configuration of the animation.
+    Layer
   };
   void RequestRestyle(RestyleType aRestyleType);
 
 private:
   static bool
   CanAnimatePropertyOnCompositor(const dom::Element *aElement,
                                  nsCSSProperty aProperty,
                                  CanAnimateFlags aFlags);
@@ -327,18 +326,16 @@ public:
   // (This is useful for determining whether throttle the animation
   // (suppress main-thread style updates).)
   //
   // Note that this does not test whether the element's layer uses
   // off-main-thread compositing, although it does check whether
   // off-main-thread compositing is enabled as a whole.
   bool CanPerformOnCompositorThread(CanAnimateFlags aFlags) const;
 
-  void PostUpdateLayerAnimations();
-
   bool HasCurrentAnimationOfProperty(nsCSSProperty aProperty) const;
 
   bool IsForElement() const { // rather than for a pseudo-element
     return mElementProperty == nsGkAtoms::animationsProperty ||
            mElementProperty == nsGkAtoms::transitionsProperty;
   }
 
   bool IsForBeforePseudo() const {
@@ -384,18 +381,16 @@ public:
     dom::Element* element = GetElementToRestyle();
     if (element) {
       nsRestyleHint hint = IsForTransitions() ? eRestyle_CSSTransitions
                                               : eRestyle_CSSAnimations;
       aPresContext->PresShell()->RestyleForAnimation(element, hint);
     }
   }
 
-  void NotifyAnimationUpdated();
-
   static void LogAsyncAnimationFailure(nsCString& aMessage,
                                        const nsIContent* aContent = nullptr);
 
   dom::Element *mElement;
 
   // the atom we use in mElement's prop table (must be a static atom,
   // i.e., in an atom list)
   nsIAtom *mElementProperty;
--- a/layout/style/nsAnimationManager.cpp
+++ b/layout/style/nsAnimationManager.cpp
@@ -529,17 +529,17 @@ nsAnimationManager::CheckAnimationRule(n
   // Cancel removed animations
   for (size_t newAnimIdx = newAnimations.Length(); newAnimIdx-- != 0; ) {
     newAnimations[newAnimIdx]->CancelFromStyle();
   }
 
   UpdateCascadeResults(aStyleContext, collection);
 
   TimeStamp refreshTime = mPresContext->RefreshDriver()->MostRecentRefresh();
-  collection->EnsureStyleRuleFor(refreshTime, EnsureStyleRule_IsNotThrottled);
+  collection->EnsureStyleRuleFor(refreshTime);
   // We don't actually dispatch the pending events now.  We'll either
   // dispatch them the next time we get a refresh driver notification
   // or the next time somebody calls
   // nsPresShell::FlushPendingNotifications.
   if (mEventDispatcher.HasQueuedEvents()) {
     mPresContext->Document()->SetNeedStyleFlush();
   }
 
@@ -944,19 +944,14 @@ nsAnimationManager::UpdateCascadeResults
           // we set mWinsInCascade as though they were, but they don't
           // suppress animations lower in the cascade.)
           propertiesOverridden.AddProperty(prop.mProperty);
         }
       }
     }
   }
 
+  // If there is any change in the cascade result, update animations on layers
+  // with the winning animations.
   if (changed) {
-    nsPresContext* presContext = aElementAnimations->mManager->PresContext();
-    presContext->RestyleManager()->IncrementAnimationGeneration();
-    aElementAnimations->UpdateAnimationGeneration(presContext);
-    aElementAnimations->PostUpdateLayerAnimations();
-
-    // Invalidate our style rule.
-    aElementAnimations->mNeedsRefreshes = true;
-    aElementAnimations->mStyleRuleRefreshTime = TimeStamp();
+    aElementAnimations->RequestRestyle(AnimationCollection::RestyleType::Layer);
   }
 }
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2658,17 +2658,18 @@ CSS_PROP_LOGICAL(
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_DISPLAY(
     opacity,
     opacity,
     Opacity,
     CSS_PROPERTY_PARSE_VALUE |
         CSS_PROPERTY_APPLIES_TO_PLACEHOLDER |
-        CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR,
+        CSS_PROPERTY_CAN_ANIMATE_ON_COMPOSITOR |
+        CSS_PROPERTY_CREATES_STACKING_CONTEXT,
     "",
     VARIANT_HN,
     nullptr,
     offsetof(nsStyleDisplay, mOpacity),
     eStyleAnimType_float)
 CSS_PROP_DISPLAY(
     -moz-orient,
     orient,
@@ -2994,17 +2995,17 @@ CSS_PROP_VISIBILITY(
     kPointerEventsKTable,
     offsetof(nsStyleVisibility, mPointerEvents),
     eStyleAnimType_EnumU8)
 CSS_PROP_DISPLAY(
     position,
     position,
     Position,
     CSS_PROPERTY_PARSE_VALUE |
-        // For position: sticky
+        // For position: sticky/fixed
         CSS_PROPERTY_CREATES_STACKING_CONTEXT,
     "",
     VARIANT_HK,
     kPositionKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_QUOTES(
     quotes,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -147,17 +147,17 @@ nsStyleFont::Init(nsPresContext* aPresCo
 
   nsAutoString language;
   aPresContext->Document()->GetContentLanguage(language);
   language.StripWhitespace();
 
   // Content-Language may be a comma-separated list of language codes,
   // in which case the HTML5 spec says to treat it as unknown
   if (!language.IsEmpty() &&
-      language.FindChar(char16_t(',')) == kNotFound) {
+      !language.Contains(char16_t(','))) {
     mLanguage = do_GetAtom(language);
     // NOTE:  This does *not* count as an explicit language; in other
     // words, it doesn't trigger language-specific hyphenation.
   } else {
     // we didn't find a (usable) Content-Language, so we fall back
     // to whatever the presContext guessed from the charset
     mLanguage = aPresContext->GetLanguageFromCharset();
   }
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -2218,16 +2218,20 @@ struct nsStyleDisplay {
     return NS_STYLE_POSITION_ABSOLUTE == mPosition ||
            NS_STYLE_POSITION_FIXED == mPosition;
   }
 
   bool IsRelativelyPositionedStyle() const {
     return NS_STYLE_POSITION_RELATIVE == mPosition ||
            NS_STYLE_POSITION_STICKY == mPosition;
   }
+  bool IsPositionForcingStackingContext() const {
+    return NS_STYLE_POSITION_STICKY == mPosition ||
+           NS_STYLE_POSITION_FIXED == mPosition;
+  }
 
   static bool IsRubyDisplayType(uint8_t aDisplay) {
     return NS_STYLE_DISPLAY_RUBY == aDisplay ||
            NS_STYLE_DISPLAY_RUBY_BASE == aDisplay ||
            NS_STYLE_DISPLAY_RUBY_BASE_CONTAINER == aDisplay ||
            NS_STYLE_DISPLAY_RUBY_TEXT == aDisplay ||
            NS_STYLE_DISPLAY_RUBY_TEXT_CONTAINER == aDisplay;
   }
--- a/layout/style/nsTransitionManager.cpp
+++ b/layout/style/nsTransitionManager.cpp
@@ -462,17 +462,17 @@ nsTransitionManager::StyleContextChanged
     UpdateCascadeResultsWithTransitions(collection);
 
     // Set the style rule refresh time to null so that EnsureStyleRuleFor
     // creates a new style rule if we started *or* stopped transitions.
     collection->mStyleRuleRefreshTime = TimeStamp();
     collection->UpdateCheckGeneration(mPresContext);
     collection->mNeedsRefreshes = true;
     TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
-    collection->EnsureStyleRuleFor(now, EnsureStyleRule_IsNotThrottled);
+    collection->EnsureStyleRuleFor(now);
   }
 
   // We want to replace the new style context with the after-change style.
   *aNewStyleContext = afterChangeStyle;
   if (collection) {
     // Since we have transition styles, we have to undo this replacement.
     // The check of collection->mCheckGeneration against the restyle
     // manager's GetAnimationGeneration() will ensure that we don't go
@@ -819,19 +819,17 @@ nsTransitionManager::UpdateCascadeResult
   nsCSSPropertySet propertiesWithTransitions;
 #endif
 
   // http://dev.w3.org/csswg/css-transitions/#application says that
   // transitions do not apply when the same property has a CSS Animation
   // on that element (even though animations are lower in the cascade).
   if (aAnimations) {
     TimeStamp now = mPresContext->RefreshDriver()->MostRecentRefresh();
-    // Passing EnsureStyleRule_IsThrottled is OK since we will
-    // unthrottle when animations are finishing.
-    aAnimations->EnsureStyleRuleFor(now, EnsureStyleRule_IsThrottled);
+    aAnimations->EnsureStyleRuleFor(now);
 
     if (aAnimations->mStyleRule) {
       aAnimations->mStyleRule->AddPropertiesToSet(propertiesUsed);
     }
   }
 
   // Since we should never have more than one transition for the same
   // property, it doesn't matter what order we iterate the transitions.
@@ -852,24 +850,20 @@ nsTransitionManager::UpdateCascadeResult
     // properties into propertiesUsed
 #ifdef DEBUG
     MOZ_ASSERT(!propertiesWithTransitions.HasProperty(prop.mProperty),
                "we're assuming we have only one transition per property");
     propertiesWithTransitions.AddProperty(prop.mProperty);
 #endif
   }
 
+  // If there is any change in the cascade result, update animations on layers
+  // with the winning animations.
   if (changed) {
-    mPresContext->RestyleManager()->IncrementAnimationGeneration();
-    aTransitions->UpdateAnimationGeneration(mPresContext);
-    aTransitions->PostUpdateLayerAnimations();
-
-    // Invalidate our style rule.
-    aTransitions->mStyleRuleRefreshTime = TimeStamp();
-    aTransitions->mNeedsRefreshes = true;
+    aTransitions->RequestRestyle(AnimationCollection::RestyleType::Layer);
   }
 }
 
 /*
  * nsIStyleRuleProcessor implementation
  */
 
 /* virtual */ size_t
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -913,31 +913,32 @@ static void
 handle_channel_layout(cubeb_stream * stm,  WAVEFORMATEX ** mix_format, const cubeb_stream_params * stream_params)
 {
   /* Common case: the hardware is stereo. Up-mixing and down-mixing will be
    * handled in the callback. */
   if ((*mix_format)->nChannels <= 2) {
     return;
   }
 
-  /* Otherwise, the hardware supports more than two channels. */
-  WAVEFORMATEX hw_mixformat = **mix_format;
-
   /* The docs say that GetMixFormat is always of type WAVEFORMATEXTENSIBLE [1],
    * so the reinterpret_cast below should be safe. In practice, this is not
    * true, and we just want to bail out and let the rest of the code find a good
    * conversion path instead of trying to make WASAPI do it by itself.
    * [1]: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370811%28v=vs.85%29.aspx*/
   if ((*mix_format)->wFormatTag != WAVE_FORMAT_EXTENSIBLE) {
     return;
   }
 
+  WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(*mix_format);
+
+  /* Stash a copy of the original mix format in case we need to restore it later. */
+  WAVEFORMATEXTENSIBLE hw_mix_format = *format_pcm;
+
   /* The hardware is in surround mode, we want to only use front left and front
    * right. Try that, and check if it works. */
-  WAVEFORMATEXTENSIBLE * format_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>((*mix_format));
   switch (stream_params->channels) {
     case 1: /* Mono */
       format_pcm->dwChannelMask = KSAUDIO_SPEAKER_MONO;
       break;
     case 2: /* Stereo */
       format_pcm->dwChannelMask = KSAUDIO_SPEAKER_STEREO;
       break;
     default:
@@ -964,17 +965,17 @@ handle_channel_layout(cubeb_stream * stm
     WAVEFORMATEXTENSIBLE * closest_pcm = reinterpret_cast<WAVEFORMATEXTENSIBLE *>(closest);
     XASSERT(closest_pcm->SubFormat == format_pcm->SubFormat);
     CoTaskMemFree(*mix_format);
     *mix_format = closest;
   } else if (hr == AUDCLNT_E_UNSUPPORTED_FORMAT) {
     /* Not supported, no suggestion. This should not happen, but it does in the
      * field with some sound cards. We restore the mix format, and let the rest
      * of the code figure out the right conversion path. */
-    **mix_format = hw_mixformat;
+    *reinterpret_cast<WAVEFORMATEXTENSIBLE *>(*mix_format) = hw_mix_format;
   } else if (hr == S_OK) {
     LOG("Requested format accepted by WASAPI.\n");
   }
 }
 
 int setup_wasapi_stream(cubeb_stream * stm)
 {
   HRESULT hr;
--- a/media/libvpx/moz.build
+++ b/media/libvpx/moz.build
@@ -40,17 +40,17 @@ if CONFIG['VPX_ARM_ASM']:
             if f.endswith('.asm') else f
             for f in sorted(arm_asm_files)
         ])
     else:
         SOURCES += sorted(arm_asm_files)
 
     for f in SOURCES:
         if f.endswith('.c') and 'neon' in f:
-            SOURCES[f].flags += ['-march=armv7-a', '-mthumb', '-mfloat-abi=softfp', '-mfpu=neon']
+            SOURCES[f].flags += CONFIG['VPX_ASFLAGS']
 
 # boolhuff_armv5te.asm defines the same functions as boolhuff.c instead of
 # using RTCD, so we have to make sure we only add one of the two.
 if 'vp8/encoder/arm/armv5te/boolhuff_armv5te.asm' not in arm_asm_files:
     SOURCES += [
         'vp8/encoder/boolhuff.c',
     ]
 
deleted file mode 100644
--- a/memory/jemalloc/0001-Dont-overwrite-VERSION-on-a-git-repository.patch
+++ /dev/null
@@ -1,68 +0,0 @@
-diff --git a/configure b/configure
---- a/configure
-+++ b/configure
-@@ -6662,28 +6662,6 @@ fi
- 
- 
- 
--if test "x`git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
--        rm -f "${srcroot}VERSION"
--  for pattern in '[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \
--                 '[0-9].[0-9][0-9].[0-9]' '[0-9].[0-9][0-9].[0-9][0-9]' \
--                 '[0-9][0-9].[0-9].[0-9]' '[0-9][0-9].[0-9].[0-9][0-9]' \
--                 '[0-9][0-9].[0-9][0-9].[0-9]' \
--                 '[0-9][0-9].[0-9][0-9].[0-9][0-9]'; do
--    if test ! -e "${srcroot}VERSION" ; then
--      git describe --long --abbrev=40 --match="${pattern}" > "${srcroot}VERSION.tmp" 2>/dev/null
--      if test $? -eq 0 ; then
--        mv "${srcroot}VERSION.tmp" "${srcroot}VERSION"
--        break
--      fi
--    fi
--  done
--fi
--rm -f "${srcroot}VERSION.tmp"
--if test ! -e "${srcroot}VERSION" ; then
--  { $as_echo "$as_me:${as_lineno-$LINENO}: result: Missing VERSION file, and unable to generate it; creating bogus VERSION" >&5
--$as_echo "Missing VERSION file, and unable to generate it; creating bogus VERSION" >&6; }
--  echo "0.0.0-0-g0000000000000000000000000000000000000000" > "${srcroot}VERSION"
--fi
- jemalloc_version=`cat "${srcroot}VERSION"`
- jemalloc_version_major=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print $1}'`
- jemalloc_version_minor=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print $2}'`
-diff --git a/configure.ac b/configure.ac
---- a/configure.ac
-+++ b/configure.ac
-@@ -1055,32 +1055,6 @@ dnl ============================================================================
- dnl jemalloc configuration.
- dnl 
- 
--dnl Set VERSION if source directory is inside a git repository.
--if test "x`git rev-parse --is-inside-work-tree 2>/dev/null`" = "xtrue" ; then
--  dnl Pattern globs aren't powerful enough to match both single- and
--  dnl double-digit version numbers, so iterate over patterns to support up to
--  dnl version 99.99.99 without any accidental matches.
--  rm -f "${srcroot}VERSION"
--  for pattern in ['[0-9].[0-9].[0-9]' '[0-9].[0-9].[0-9][0-9]' \
--                 '[0-9].[0-9][0-9].[0-9]' '[0-9].[0-9][0-9].[0-9][0-9]' \
--                 '[0-9][0-9].[0-9].[0-9]' '[0-9][0-9].[0-9].[0-9][0-9]' \
--                 '[0-9][0-9].[0-9][0-9].[0-9]' \
--                 '[0-9][0-9].[0-9][0-9].[0-9][0-9]']; do
--    if test ! -e "${srcroot}VERSION" ; then
--      git describe --long --abbrev=40 --match="${pattern}" > "${srcroot}VERSION.tmp" 2>/dev/null
--      if test $? -eq 0 ; then
--        mv "${srcroot}VERSION.tmp" "${srcroot}VERSION"
--        break
--      fi
--    fi
--  done
--fi
--rm -f "${srcroot}VERSION.tmp"
--if test ! -e "${srcroot}VERSION" ; then
--  AC_MSG_RESULT(
--    [Missing VERSION file, and unable to generate it; creating bogus VERSION])
--  echo "0.0.0-0-g0000000000000000000000000000000000000000" > "${srcroot}VERSION"
--fi
- jemalloc_version=`cat "${srcroot}VERSION"`
- jemalloc_version_major=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]1}'`
- jemalloc_version_minor=`echo ${jemalloc_version} | tr ".g-" " " | awk '{print [$]2}'`
deleted file mode 100644
--- a/memory/jemalloc/0002-Move-variable-declaration-to-the-top-its-block-for-M.patch
+++ /dev/null
@@ -1,35 +0,0 @@
-From 9c6a8d3b0cc14fd26b119ad08f190e537771464f Mon Sep 17 00:00:00 2001
-From: Guilherme Goncalves <guilherme.p.gonc@gmail.com>
-Date: Wed, 17 Dec 2014 14:46:35 -0200
-Subject: [PATCH] Move variable declaration to the top its block for MSVC
- compatibility.
-
----
- src/arena.c | 4 ++--
- 1 file changed, 2 insertions(+), 2 deletions(-)
-
-diff --git a/src/arena.c b/src/arena.c
-index bf78995..1eb4000 100644
---- a/src/arena.c
-+++ b/src/arena.c
-@@ -2022,6 +2022,7 @@ arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr,
- 		 * following run, then merge the first part with the existing
- 		 * allocation.
- 		 */
-+		arena_run_t *run;
- 		size_t flag_dirty, splitsize, usize;
- 
- 		usize = s2u(size + extra);
-@@ -2030,8 +2031,7 @@ arena_ralloc_large_grow(arena_t *arena, arena_chunk_t *chunk, void *ptr,
- 		assert(usize >= usize_min);
- 		splitsize = usize - oldsize;
- 
--		arena_run_t *run = &arena_miscelm_get(chunk,
--		    pageind+npages)->run;
-+		run = &arena_miscelm_get(chunk, pageind+npages)->run;
- 		arena_run_split_large(arena, run, splitsize, zero);
- 
- 		size = oldsize + splitsize;
--- 
-2.1.3
-
deleted file mode 100644
--- a/memory/jemalloc/0003-Add-a-isblank-definition-for-MSVC-2013.patch
+++ /dev/null
@@ -1,30 +0,0 @@
-From 4dd4193c59ff3f77e4ab36214ec63425ca834323 Mon Sep 17 00:00:00 2001
-From: Guilherme Goncalves <guilherme.p.gonc@gmail.com>
-Date: Thu, 18 Dec 2014 15:01:21 +0900
-Subject: [PATCH] Add a isblank definition for MSVC < 2013
-
----
- include/jemalloc/internal/jemalloc_internal_decls.h | 7 +++++++
- 1 file changed, 7 insertions(+)
-
-diff --git a/include/jemalloc/internal/jemalloc_internal_decls.h b/include/jemalloc/internal/jemalloc_internal_decls.h
-index fb2effb..65f2e4b 100644
---- a/include/jemalloc/internal/jemalloc_internal_decls.h
-+++ b/include/jemalloc/internal/jemalloc_internal_decls.h
-@@ -52,6 +52,13 @@ typedef intptr_t ssize_t;
- #  define __func__ __FUNCTION__
- /* Disable warnings about deprecated system functions. */
- #  pragma warning(disable: 4996)
-+#if _MSC_VER < 1800
-+static int
-+isblank(int c)
-+{
-+	return (c == '\t' || c == ' ');
-+}
-+#endif
- #else
- #  include <unistd.h>
- #endif
--- 
-2.1.3
-
deleted file mode 100644
--- a/memory/jemalloc/0004-Implement-stats.bookkeeping.patch
+++ /dev/null
@@ -1,158 +0,0 @@
-commit a8a2e2b34e21870733c75121f431530d42d4d3cd
-Author: Guilherme Goncalves <guilherme.p.gonc@gmail.com>
-Date:   Tue Dec 23 15:21:04 2014 -0200
-
-    Implement stats.bookkeeping.
-    
-    This measures the number of bytes used for arena and chunk headers and all base
-    allocations (note that arena headers are already allocated through the base
-    allocator). It does not account for internal allocations made through arenas.
-
-diff --git a/include/jemalloc/internal/base.h b/include/jemalloc/internal/base.h
-index 3fb80b9..9dc431c 100644
---- a/include/jemalloc/internal/base.h
-+++ b/include/jemalloc/internal/base.h
-@@ -9,6 +9,10 @@
- /******************************************************************************/
- #ifdef JEMALLOC_H_EXTERNS
- 
-+/* base_mtx synchronizes base allocations and protects base_allocated */
-+extern malloc_mutex_t	base_mtx;
-+extern size_t base_allocated;
-+
- void	*base_alloc(size_t size);
- void	*base_calloc(size_t number, size_t size);
- extent_node_t *base_node_alloc(void);
-diff --git a/include/jemalloc/internal/ctl.h b/include/jemalloc/internal/ctl.h
-index a3e899e..cbb0923 100644
---- a/include/jemalloc/internal/ctl.h
-+++ b/include/jemalloc/internal/ctl.h
-@@ -52,6 +52,7 @@ struct ctl_arena_stats_s {
- struct ctl_stats_s {
- 	size_t			allocated;
- 	size_t			active;
-+	size_t			bookkeeping;
- 	size_t			mapped;
- 	struct {
- 		size_t		current;	/* stats_chunks.curchunks */
-diff --git a/include/jemalloc/internal/private_symbols.txt b/include/jemalloc/internal/private_symbols.txt
-index ee973c9..93271ce 100644
---- a/include/jemalloc/internal/private_symbols.txt
-+++ b/include/jemalloc/internal/private_symbols.txt
-@@ -91,8 +91,10 @@ atomic_sub_uint32
- atomic_sub_uint64
- atomic_sub_z
- base_alloc
-+base_allocated
- base_boot
- base_calloc
-+base_mtx
- base_node_alloc
- base_node_dalloc
- base_postfork_child
-diff --git a/src/base.c b/src/base.c
-index 409c7bb..3350ca3 100644
---- a/src/base.c
-+++ b/src/base.c
-@@ -4,7 +4,7 @@
- /******************************************************************************/
- /* Data. */
- 
--static malloc_mutex_t	base_mtx;
-+malloc_mutex_t	base_mtx;
- 
- /*
-  * Current pages that are being used for internal memory allocations.  These
-@@ -16,6 +16,8 @@ static void		*base_next_addr;
- static void		*base_past_addr; /* Addr immediately past base_pages. */
- static extent_node_t	*base_nodes;
- 
-+size_t base_allocated;
-+
- /******************************************************************************/
- 
- static bool
-@@ -54,6 +56,8 @@ base_alloc(size_t size)
- 	/* Allocate. */
- 	ret = base_next_addr;
- 	base_next_addr = (void *)((uintptr_t)base_next_addr + csize);
-+	if (config_stats)
-+		base_allocated += csize;
- 	malloc_mutex_unlock(&base_mtx);
- 	JEMALLOC_VALGRIND_MAKE_MEM_UNDEFINED(ret, csize);
- 
-diff --git a/src/ctl.c b/src/ctl.c
-index b367c9f..1999145 100644
---- a/src/ctl.c
-+++ b/src/ctl.c
-@@ -187,6 +187,7 @@ INDEX_PROTO(stats_arenas_i)
- CTL_PROTO(stats_cactive)
- CTL_PROTO(stats_allocated)
- CTL_PROTO(stats_active)
-+CTL_PROTO(stats_bookkeeping)
- CTL_PROTO(stats_mapped)
- 
- /******************************************************************************/
-@@ -448,12 +449,13 @@ static const ctl_indexed_node_t stats_arenas_node[] = {
- };
- 
- static const ctl_named_node_t stats_node[] = {
--	{NAME("cactive"),	CTL(stats_cactive)},
--	{NAME("allocated"),	CTL(stats_allocated)},
--	{NAME("active"),	CTL(stats_active)},
--	{NAME("mapped"),	CTL(stats_mapped)},
--	{NAME("chunks"),	CHILD(named, stats_chunks)},
--	{NAME("arenas"),	CHILD(indexed, stats_arenas)}
-+	{NAME("cactive"),		CTL(stats_cactive)},
-+	{NAME("allocated"),		CTL(stats_allocated)},
-+	{NAME("active"),		CTL(stats_active)},
-+	{NAME("bookkeeping"),	CTL(stats_bookkeeping)},
-+	{NAME("mapped"),		CTL(stats_mapped)},
-+	{NAME("chunks"),		CHILD(named, stats_chunks)},
-+	{NAME("arenas"),		CHILD(indexed, stats_arenas)}
- };
- 
- static const ctl_named_node_t	root_node[] = {
-@@ -705,6 +707,12 @@ ctl_refresh(void)
- 		ctl_stats.active =
- 		    (ctl_stats.arenas[ctl_stats.narenas].pactive << LG_PAGE);
- 		ctl_stats.mapped = (ctl_stats.chunks.current << opt_lg_chunk);
-+
-+		/* add chunk headers to bookkeeping */
-+		ctl_stats.bookkeeping = ctl_stats.chunks.current * (map_bias << LG_PAGE);
-+		malloc_mutex_lock(&base_mtx);
-+		ctl_stats.bookkeeping += base_allocated;
-+		malloc_mutex_unlock(&base_mtx);
- 	}
- 
- 	ctl_epoch++;
-@@ -1806,6 +1814,7 @@ CTL_RO_NL_CGEN(config_prof, lg_prof_sample, lg_prof_sample, size_t)
- CTL_RO_CGEN(config_stats, stats_cactive, &stats_cactive, size_t *)
- CTL_RO_CGEN(config_stats, stats_allocated, ctl_stats.allocated, size_t)
- CTL_RO_CGEN(config_stats, stats_active, ctl_stats.active, size_t)
-+CTL_RO_CGEN(config_stats, stats_bookkeeping, ctl_stats.bookkeeping, size_t)
- CTL_RO_CGEN(config_stats, stats_mapped, ctl_stats.mapped, size_t)
- 
- CTL_RO_CGEN(config_stats, stats_chunks_current, ctl_stats.chunks.current,
-diff --git a/test/unit/stats.c b/test/unit/stats.c
-index 946e737..e84cbf7 100644
---- a/test/unit/stats.c
-+++ b/test/unit/stats.c
-@@ -3,7 +3,7 @@
- TEST_BEGIN(test_stats_summary)
- {
- 	size_t *cactive;
--	size_t sz, allocated, active, mapped;
-+	size_t sz, allocated, active, mapped, bookkeeping;
- 	int expected = config_stats ? 0 : ENOENT;
- 
- 	sz = sizeof(cactive);
-@@ -17,6 +17,8 @@ TEST_BEGIN(test_stats_summary)
- 	    "Unexpected mallctl() result");
- 	assert_d_eq(mallctl("stats.mapped", &mapped, &sz, NULL, 0), expected,
- 	    "Unexpected mallctl() result");
-+	assert_d_eq(mallctl("stats.bookkeeping", &bookkeeping, &sz, NULL, 0),
-+	    expected, "Unexpected mallctl() result");
- 
- 	if (config_stats) {
- 		assert_zu_le(active, *cactive,
deleted file mode 100644
--- a/memory/jemalloc/0005-Bug-1121314-Avoid-needing-the-arena-in-chunk_alloc_d.patch
+++ /dev/null
@@ -1,58 +0,0 @@
-From 722400adde3b4cb307fdc310e6296e10e6ace7ae Mon Sep 17 00:00:00 2001
-From: Mike Hommey <mh+mozilla@glandium.org>
-Date: Wed, 14 Jan 2015 12:49:24 +0900
-Subject: [PATCH] Bug 1121314 - Avoid needing the arena in chunk_alloc_default
- to avoid possible infinite loops involving a0malloc
-
----
- src/chunk.c | 21 +++++++++++++--------
- 1 file changed, 13 insertions(+), 8 deletions(-)
-
-diff --git a/src/chunk.c b/src/chunk.c
-index 7926452..32e659c 100644
---- a/src/chunk.c
-+++ b/src/chunk.c
-@@ -247,27 +247,32 @@ chunk_alloc_arena(chunk_alloc_t *chunk_alloc, chunk_dalloc_t *chunk_dalloc,
- 	return (ret);
- }
- 
- /* Default arena chunk allocation routine in the absence of user override. */
- void *
- chunk_alloc_default(void *new_addr, size_t size, size_t alignment, bool *zero,
-     unsigned arena_ind)
- {
--	arena_t *arena;
-+	dss_prec_t dss_prec = dss_prec_disabled;
- 
--	arena = arena_get(tsd_fetch(), arena_ind, false, true);
--	/*
--	 * The arena we're allocating on behalf of must have been initialized
--	 * already.
--	 */
--	assert(arena != NULL);
-+	if (have_dss) {
-+		arena_t *arena;
-+
-+		arena = arena_get(tsd_fetch(), arena_ind, false, true);
-+		/*
-+		 * The arena we're allocating on behalf of must have been
-+		 * initialized already.
-+		 */
-+		assert(arena != NULL);
-+		dss_prec = arena->dss_prec;
-+	}
- 
- 	return (chunk_alloc_core(new_addr, size, alignment, false, zero,
--	    arena->dss_prec));
-+	    dss_prec));
- }
- 
- static void
- chunk_record(extent_tree_t *chunks_szad, extent_tree_t *chunks_ad, void *chunk,
-     size_t size)
- {
- 	bool unzeroed;
- 	extent_node_t *xnode, *node, *prev, *xprev, key;
--- 
-2.2.1.dirty
-
deleted file mode 100644
--- a/memory/jemalloc/0006-Make-opt.lg_dirty_mult-work-as-documented.patch
+++ /dev/null
@@ -1,47 +0,0 @@
-From 6505733012458d8fcd0ae8e1f1acdc9ffe33ff35 Mon Sep 17 00:00:00 2001
-From: Mike Hommey <mh@glandium.org>
-Date: Wed, 4 Feb 2015 07:16:55 +0900
-Subject: [PATCH] Make opt.lg_dirty_mult work as documented
-
-The documentation for opt.lg_dirty_mult says:
-    Per-arena minimum ratio (log base 2) of active to dirty
-    pages.  Some dirty unused pages may be allowed to accumulate,
-    within the limit set by the ratio (or one chunk worth of dirty
-    pages, whichever is greater) (...)
-
-The restriction in parentheses currently doesn't happen. This makes
-jemalloc aggressively madvise(), which in turns increases the amount
-of page faults significantly.
-
-For instance, this resulted in several(!) hundred(!) milliseconds
-startup regression on Firefox for Android.
-
-This may require further tweaking, but starting with actually doing
-what the documentation says is a good start.
----
- src/arena.c | 2 ++
- 1 file changed, 2 insertions(+)
-
-diff --git a/src/arena.c b/src/arena.c
-index 984b8ad..a5033bf 100644
---- a/src/arena.c
-+++ b/src/arena.c
-@@ -850,6 +850,7 @@ arena_maybe_purge(arena_t *arena)
- 	if (opt_lg_dirty_mult < 0)
- 		return;
- 	threshold = (arena->nactive >> opt_lg_dirty_mult);
-+	threshold = threshold < chunk_npages ? chunk_npages : threshold;
- 	/*
- 	 * Don't purge unless the number of purgeable pages exceeds the
- 	 * threshold.
-@@ -893,6 +894,7 @@ arena_compute_npurge(arena_t *arena, bool all)
- 	 */
- 	if (!all) {
- 		size_t threshold = (arena->nactive >> opt_lg_dirty_mult);
-+		threshold = threshold < chunk_npages ? chunk_npages : threshold;
- 
- 		npurge = arena->ndirty - threshold;
- 	} else
--- 
-2.3.0.3.g98027e3
-
deleted file mode 100644
--- a/memory/jemalloc/0007-Preserve-LastError-when-calling-TlsGetValue.patch
+++ /dev/null
@@ -1,45 +0,0 @@
-From f44c19741e98f4a87cf20e59e31634bb8a29c657 Mon Sep 17 00:00:00 2001
-From: Mike Hommey <mh@glandium.org>
-Date: Wed, 4 Mar 2015 10:54:10 +0900
-Subject: [PATCH] Preserve LastError when calling TlsGetValue
-
-TlsGetValue has a semantic difference with pthread_getspecific, in that it
-can return a non-error NULL value, so it always sets the LastError.
-But allocator callers may not be expecting calling e.g. free() to change
-the value of the last error, so preserve it.
----
- include/jemalloc/internal/tsd.h | 8 ++++++--
- 1 file changed, 6 insertions(+), 2 deletions(-)
-
-diff --git a/include/jemalloc/internal/tsd.h b/include/jemalloc/internal/tsd.h
-index dbb91a2..62a887e 100644
---- a/include/jemalloc/internal/tsd.h
-+++ b/include/jemalloc/internal/tsd.h
-@@ -277,9 +277,11 @@ a_name##tsd_set(a_type *val)						\
- a_attr bool								\
- a_name##tsd_cleanup_wrapper(void)					\
- {									\
--	a_name##tsd_wrapper_t *wrapper;					\
-+	DWORD error = GetLastError();					\
-+	a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)	\
-+	    TlsGetValue(a_name##tsd_tsd);				\
-+	SetLastError(error);						\
- 									\
--	wrapper = (a_name##tsd_wrapper_t *)TlsGetValue(a_name##tsd_tsd);\
- 	if (wrapper == NULL)						\
- 		return (false);						\
- 	if (a_cleanup != malloc_tsd_no_cleanup &&			\
-@@ -307,8 +309,10 @@ a_name##tsd_wrapper_set(a_name##tsd_wrapper_t *wrapper)			\
- a_attr a_name##tsd_wrapper_t *						\
- a_name##tsd_wrapper_get(void)						\
- {									\
-+	DWORD error = GetLastError();					\
- 	a_name##tsd_wrapper_t *wrapper = (a_name##tsd_wrapper_t *)	\
- 	    TlsGetValue(a_name##tsd_tsd);				\
-+	SetLastError(error);						\
- 									\
- 	if (unlikely(wrapper == NULL)) {				\
- 		wrapper = (a_name##tsd_wrapper_t *)			\
--- 
-2.3.0.3.g98027e3
-
deleted file mode 100644
--- a/memory/jemalloc/0008-Make-without-export-actually-work.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-From 7c46fd59cce6afb14cdc6c819f662b6e81638f84 Mon Sep 17 00:00:00 2001
-From: Mike Hommey <mh@glandium.org>
-Date: Wed, 4 Mar 2015 21:48:01 +0900
-Subject: [PATCH] Make --without-export actually work
-
-9906660 added a --without-export configure option to avoid exporting
-jemalloc symbols, but the option didn't actually work.
----
- include/jemalloc/internal/jemalloc_internal_defs.h.in |  6 ++++++
- include/jemalloc/jemalloc_macros.h.in                 | 14 +++++++++-----
- 2 files changed, 15 insertions(+), 5 deletions(-)
-
-diff --git a/include/jemalloc/internal/jemalloc_internal_defs.h.in b/include/jemalloc/internal/jemalloc_internal_defs.h.in
-index 0f0db8a..191abc5 100644
---- a/include/jemalloc/internal/jemalloc_internal_defs.h.in
-+++ b/include/jemalloc/internal/jemalloc_internal_defs.h.in
-@@ -232,4 +232,10 @@
- /* Adaptive mutex support in pthreads. */
- #undef JEMALLOC_HAVE_PTHREAD_MUTEX_ADAPTIVE_NP
- 
-+/*
-+ * If defined, jemalloc symbols are not exported (doesn't work when
-+ * JEMALLOC_PREFIX is not defined).
-+ */
-+#undef JEMALLOC_EXPORT
-+
- #endif /* JEMALLOC_INTERNAL_DEFS_H_ */
-diff --git a/include/jemalloc/jemalloc_macros.h.in b/include/jemalloc/jemalloc_macros.h.in
-index 7d1dcf4..72f2a08 100644
---- a/include/jemalloc/jemalloc_macros.h.in
-+++ b/include/jemalloc/jemalloc_macros.h.in
-@@ -32,16 +32,20 @@
- 
- #ifdef JEMALLOC_HAVE_ATTR
- #  define JEMALLOC_ATTR(s) __attribute__((s))
--#  define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default"))
-+#  ifndef JEMALLOC_EXPORT
-+#    define JEMALLOC_EXPORT JEMALLOC_ATTR(visibility("default"))
-+#  endif
- #  define JEMALLOC_ALIGNED(s) JEMALLOC_ATTR(aligned(s))
- #  define JEMALLOC_SECTION(s) JEMALLOC_ATTR(section(s))
- #  define JEMALLOC_NOINLINE JEMALLOC_ATTR(noinline)
- #elif _MSC_VER
- #  define JEMALLOC_ATTR(s)
--#  ifdef DLLEXPORT
--#    define JEMALLOC_EXPORT __declspec(dllexport)
--#  else
--#    define JEMALLOC_EXPORT __declspec(dllimport)
-+#  ifndef JEMALLOC_EXPORT
-+#    ifdef DLLEXPORT
-+#      define JEMALLOC_EXPORT __declspec(dllexport)
-+#    else
-+#      define JEMALLOC_EXPORT __declspec(dllimport)
-+#    endif
- #  endif
- #  define JEMALLOC_ALIGNED(s) __declspec(align(s))
- #  define JEMALLOC_SECTION(s) __declspec(allocate(s))
--- 
-2.3.0.4.g34b1174
-
new file mode 100755
--- /dev/null
+++ b/memory/jemalloc/helper/git
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+# jemalloc's configure runs git to determine the version. But when building
+# from a gecko git clone, the git commands it uses is going to pick gecko's
+# information, not jemalloc's, which is useless. So pretend we don't have git
+# at all. That will make jemalloc's configure pick the in-tree VERSION file.
+
+exit 1
--- a/memory/jemalloc/moz.build
+++ b/memory/jemalloc/moz.build
@@ -14,16 +14,17 @@ UNIFIED_SOURCES += [
     'src/src/chunk_mmap.c',
     'src/src/ckh.c',
     'src/src/extent.c',
     'src/src/hash.c',
     'src/src/huge.c',
     'src/src/jemalloc.c',
     'src/src/mb.c',
     'src/src/mutex.c',
+    'src/src/pages.c',
     'src/src/prof.c',
     'src/src/quarantine.c',
     'src/src/rtree.c',
     'src/src/stats.c',
     'src/src/tcache.c',
     'src/src/tsd.c',
     'src/src/util.c',
     # FIXME do we ever want valgrind.c?
--- a/memory/jemalloc/src/COPYING
+++ b/memory/jemalloc/src/COPYING
@@ -1,15 +1,15 @@
 Unless otherwise specified, files in the jemalloc source distribution are
 subject to the following license:
 --------------------------------------------------------------------------------
-Copyright (C) 2002-2014 Jason Evans <jasone@canonware.com>.
+Copyright (C) 2002-2015 Jason Evans <jasone@canonware.com>.
 All rights reserved.
 Copyright (C) 2007-2012 Mozilla Foundation.  All rights reserved.
-Copyright (C) 2009-2014 Facebook, Inc.  All rights reserved.
+Copyright (C) 2009-2015 Facebook, Inc.  All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 1. Redistributions of source code must retain the above copyright notice(s),
    this list of conditions and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright notice(s),
    this list of conditions and the following disclaimer in the documentation
    and/or other materials provided with the distribution.
--- a/memory/jemalloc/src/ChangeLog
+++ b/memory/jemalloc/src/ChangeLog
@@ -1,32 +1,188 @@
 Following are change highlights associated with official releases.  Important
-bug fixes are all mentioned, but internal enhancements are omitted here for
-brevity (even though they are more fun to write about).  Much more detail can be
-found in the git revision history:
+bug fixes are all mentioned, but some internal enhancements are omitted here for
+brevity.  Much more detail can be found in the git revision history:
 
     https://github.com/jemalloc/jemalloc
 
+* 4.0.0 (August 17, 2015)
+
+  This version contains many speed and space optimizations, both minor and
+  major.  The major themes are generalization, unification, and simplification.
+  Although many of these optimizations cause no visible behavior change, their
+  cumulative effect is substantial.
+
+  New features:
+  - Normalize size class spacing to be consistent across the complete size
+    range.  By default there are four size classes per size doubling, but this
+    is now configurable via the --with-lg-size-class-group option.  Also add the
+    --with-lg-page, --with-lg-page-sizes, --with-lg-quantum, and
+    --with-lg-tiny-min options, which can be used to tweak page and size class
+    settings.  Impacts:
+    + Worst case performance for incrementally growing/shrinking reallocation
+      is improved because there are far fewer size classes, and therefore
+      copying happens less often.
+    + Internal fragmentation is limited to 20% for all but the smallest size
+      classes (those less than four times the quantum).  (1B + 4 KiB)
+      and (1B + 4 MiB) previously suffered nearly 50% internal fragmentation.
+    + Chunk fragmentation tends to be lower because there are fewer distinct run
+      sizes to pack.
+  - Add support for explicit tcaches.  The "tcache.create", "tcache.flush", and
+    "tcache.destroy" mallctls control tcache lifetime and flushing, and the
+    MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to the *allocx() API
+    control which tcache is used for each operation.
+  - Implement per thread heap profiling, as well as the ability to
+    enable/disable heap profiling on a per thread basis.  Add the "prof.reset",
+    "prof.lg_sample", "thread.prof.name", "thread.prof.active",
+    "opt.prof_thread_active_init", "prof.thread_active_init", and
+    "thread.prof.active" mallctls.
+  - Add support for per arena application-specified chunk allocators, configured
+    via the "arena.<i>.chunk_hooks" mallctl.
+  - Refactor huge allocation to be managed by arenas, so that arenas now
+    function as general purpose independent allocators.  This is important in
+    the context of user-specified chunk allocators, aside from the scalability
+    benefits.  Related new statistics:
+    + The "stats.arenas.<i>.huge.allocated", "stats.arenas.<i>.huge.nmalloc",
+      "stats.arenas.<i>.huge.ndalloc", and "stats.arenas.<i>.huge.nrequests"
+      mallctls provide high level per arena huge allocation statistics.
+    + The "arenas.nhchunks", "arenas.hchunk.<i>.size",
+      "stats.arenas.<i>.hchunks.<j>.nmalloc",
+      "stats.arenas.<i>.hchunks.<j>.ndalloc",
+      "stats.arenas.<i>.hchunks.<j>.nrequests", and
+      "stats.arenas.<i>.hchunks.<j>.curhchunks" mallctls provide per size class
+      statistics.
+  - Add the 'util' column to malloc_stats_print() output, which reports the
+    proportion of available regions that are currently in use for each small
+    size class.
+  - Add "alloc" and "free" modes for for junk filling (see the "opt.junk"
+    mallctl), so that it is possible to separately enable junk filling for
+    allocation versus deallocation.
+  - Add the jemalloc-config script, which provides information about how
+    jemalloc was configured, and how to integrate it into application builds.
+  - Add metadata statistics, which are accessible via the "stats.metadata",
+    "stats.arenas.<i>.metadata.mapped", and
+    "stats.arenas.<i>.metadata.allocated" mallctls.
+  - Add the "stats.resident" mallctl, which reports the upper limit of
+    physically resident memory mapped by the allocator.
+  - Add per arena control over unused dirty page purging, via the
+    "arenas.lg_dirty_mult", "arena.<i>.lg_dirty_mult", and
+    "stats.arenas.<i>.lg_dirty_mult" mallctls.
+  - Add the "prof.gdump" mallctl, which makes it possible to toggle the gdump
+    feature on/off during program execution.
+  - Add sdallocx(), which implements sized deallocation.  The primary
+    optimization over dallocx() is the removal of a metadata read, which often
+    suffers an L1 cache miss.
+  - Add missing header includes in jemalloc/jemalloc.h, so that applications
+    only have to #include <jemalloc/jemalloc.h>.
+  - Add support for additional platforms:
+    + Bitrig
+    + Cygwin
+    + DragonFlyBSD
+    + iOS
+    + OpenBSD
+    + OpenRISC/or1k
+
+  Optimizations:
+  - Maintain dirty runs in per arena LRUs rather than in per arena trees of
+    dirty-run-containing chunks.  In practice this change significantly reduces
+    dirty page purging volume.
+  - Integrate whole chunks into the unused dirty page purging machinery.  This
+    reduces the cost of repeated huge allocation/deallocation, because it
+    effectively introduces a cache of chunks.
+  - Split the arena chunk map into two separate arrays, in order to increase
+    cache locality for the frequently accessed bits.
+  - Move small run metadata out of runs, into arena chunk headers.  This reduces
+    run fragmentation, smaller runs reduce external fragmentation for small size
+    classes, and packed (less uniformly aligned) metadata layout improves CPU
+    cache set distribution.
+  - Randomly distribute large allocation base pointer alignment relative to page
+    boundaries in order to more uniformly utilize CPU cache sets.  This can be
+    disabled via the --disable-cache-oblivious configure option, and queried via
+    the "config.cache_oblivious" mallctl.
+  - Micro-optimize the fast paths for the public API functions.
+  - Refactor thread-specific data to reside in a single structure.  This assures
+    that only a single TLS read is necessary per call into the public API.
+  - Implement in-place huge allocation growing and shrinking.
+  - Refactor rtree (radix tree for chunk lookups) to be lock-free, and make
+    additional optimizations that reduce maximum lookup depth to one or two
+    levels.  This resolves what was a concurrency bottleneck for per arena huge
+    allocation, because a global data structure is critical for determining
+    which arenas own which huge allocations.
+
+  Incompatible changes:
+  - Replace --enable-cc-silence with --disable-cc-silence to suppress spurious
+    warnings by default.
+  - Assure that the constness of malloc_usable_size()'s return type matches that
+    of the system implementation.
+  - Change the heap profile dump format to support per thread heap profiling,
+    rename pprof to jeprof, and enhance it with the --thread=<n> option.  As a
+    result, the bundled jeprof must now be used rather than the upstream
+    (gperftools) pprof.
+  - Disable "opt.prof_final" by default, in order to avoid atexit(3), which can
+    internally deadlock on some platforms.
+  - Change the "arenas.nlruns" mallctl type from size_t to unsigned.
+  - Replace the "stats.arenas.<i>.bins.<j>.allocated" mallctl with
+    "stats.arenas.<i>.bins.<j>.curregs".
+  - Ignore MALLOC_CONF in set{uid,gid,cap} binaries.
+  - Ignore MALLOCX_ARENA(a) in dallocx(), in favor of using the
+    MALLOCX_TCACHE(tc) and MALLOCX_TCACHE_NONE flags to control tcache usage.
+
+  Removed features:
+  - Remove the *allocm() API, which is superseded by the *allocx() API.
+  - Remove the --enable-dss options, and make dss non-optional on all platforms
+    which support sbrk(2).
+  - Remove the "arenas.purge" mallctl, which was obsoleted by the
+    "arena.<i>.purge" mallctl in 3.1.0.
+  - Remove the unnecessary "opt.valgrind" mallctl; jemalloc automatically
+    detects whether it is running inside Valgrind.
+  - Remove the "stats.huge.allocated", "stats.huge.nmalloc", and
+    "stats.huge.ndalloc" mallctls.
+  - Remove the --enable-mremap option.
+  - Remove the "stats.chunks.current", "stats.chunks.total", and
+    "stats.chunks.high" mallctls.
+
+  Bug fixes:
+  - Fix the cactive statistic to decrease (rather than increase) when active
+    memory decreases.  This regression was first released in 3.5.0.
+  - Fix OOM handling in memalign() and valloc().  A variant of this bug existed
+    in all releases since 2.0.0, which introduced these functions.
+  - Fix an OOM-related regression in arena_tcache_fill_small(), which could
+    cause cache corruption on OOM.  This regression was present in all releases
+    from 2.2.0 through 3.6.0.
+  - Fix size class overflow handling for malloc(), posix_memalign(), memalign(),
+    calloc(), and realloc() when profiling is enabled.
+  - Fix the "arena.<i>.dss" mallctl to return an error if "primary" or
+    "secondary" precedence is specified, but sbrk(2) is not supported.
+  - Fix fallback lg_floor() implementations to handle extremely large inputs.
+  - Ensure the default purgeable zone is after the default zone on OS X.
+  - Fix latent bugs in atomic_*().
+  - Fix the "arena.<i>.dss" mallctl to handle read-only calls.
+  - Fix tls_model configuration to enable the initial-exec model when possible.
+  - Mark malloc_conf as a weak symbol so that the application can override it.
+  - Correctly detect glibc's adaptive pthread mutexes.
+  - Fix the --without-export configure option.
+
 * 3.6.0 (March 31, 2014)
 
   This version contains a critical bug fix for a regression present in 3.5.0 and
   3.5.1.
 
   Bug fixes:
   - Fix a regression in arena_chunk_alloc() that caused crashes during
     small/large allocation if chunk allocation failed.  In the absence of this
     bug, chunk allocation failure would result in allocation failure, e.g.  NULL
     return from malloc().  This regression was introduced in 3.5.0.
   - Fix backtracing for gcc intrinsics-based backtracing by specifying
     -fno-omit-frame-pointer to gcc.  Note that the application (and all the
     libraries it links to) must also be compiled with this option for
     backtracing to be reliable.
   - Use dss allocation precedence for huge allocations as well as small/large
     allocations.
-  - Fix test assertion failure message formatting.  This bug did not manifect on
+  - Fix test assertion failure message formatting.  This bug did not manifest on
     x86_64 systems because of implementation subtleties in va_list.
   - Fix inconsequential test failures for hash and SFMT code.
 
   New features:
   - Support heap profiling on FreeBSD.  This feature depends on the proc
     filesystem being mounted during heap profile dumping.
 
 * 3.5.1 (February 25, 2014)
@@ -511,17 +667,17 @@ found in the git revision history:
 
   New features:
   - Implement autoconf-based configuration system.
   - Add mallctl*(), for the purposes of introspection and run-time
     configuration.
   - Make it possible for the application to manually flush a thread's cache, via
     the "tcache.flush" mallctl.
   - Base maximum dirty page count on proportion of active memory.
-  - Compute various addtional run-time statistics, including per size class
+  - Compute various additional run-time statistics, including per size class
     statistics for large objects.
   - Expose malloc_stats_print(), which can be called repeatedly by the
     application.
   - Simplify the malloc_message() signature to only take one string argument,
     and incorporate an opaque data pointer argument for use by the application
     in combination with malloc_stats_print().
   - Add support for allocation backed by one or more swap files, and allow the
     application to disable over-commit if swap files are in use.
--- a/memory/jemalloc/src/INSTALL
+++ b/memory/jemalloc/src/INSTALL
@@ -102,25 +102,25 @@ any of the following arguments (not a de
       coverage_unit
       coverage_integration
       coverage_stress
 
     These targets do not clear code coverage results from previous runs, and
     there are interactions between the various coverage targets, so it is
     usually advisable to run 'make clean' between repeated code coverage runs.
 
---enable-ivsalloc
-    Enable validation code, which verifies that pointers reside within
-    jemalloc-owned chunks before dereferencing them.  This incurs a substantial
-    performance hit.
-
 --disable-stats
     Disable statistics gathering functionality.  See the "opt.stats_print"
     option documentation for usage details.
 
+--enable-ivsalloc
+    Enable validation code, which verifies that pointers reside within
+    jemalloc-owned chunks before dereferencing them.  This incurs a minor
+    performance hit.
+
 --enable-prof
     Enable heap profiling and leak detection functionality.  See the "opt.prof"
     option documentation for usage details.  When enabled, there are several
     approaches to backtracing, and the configure script chooses the first one
     in the following list that appears to function correctly:
 
     + libunwind      (requires --enable-prof-libunwind)
     + libgcc         (unless --disable-prof-libgcc)
@@ -180,16 +180,25 @@ any of the following arguments (not a de
     practice, this feature usually has little impact on performance unless
     thread-specific caching is disabled.
 
 --disable-tls
     Disable thread-local storage (TLS), which allows for fast access to
     thread-local variables via the __thread keyword.  If TLS is available,
     jemalloc uses it for several purposes.
 
+--disable-cache-oblivious
+    Disable cache-oblivious large allocation alignment for large allocation
+    requests with no alignment constraints.  If this feature is disabled, all
+    large allocations are page-aligned as an implementation artifact, which can
+    severely harm CPU cache utilization.  However, the cache-oblivious layout
+    comes at the cost of one extra page per large allocation, which in the
+    most extreme case increases physical memory usage for the 16 KiB size class
+    to 20 KiB.
+
 --with-xslroot=<path>
     Specify where to find DocBook XSL stylesheets when building the
     documentation.
 
 --with-lg-page=<lg-page>
     Specify the base 2 log of the system page size.  This option is only useful
     when cross compiling, since the configure script automatically determines
     the host's page size by default.
--- a/memory/jemalloc/src/Makefile.in
+++ b/memory/jemalloc/src/Makefile.in
@@ -43,18 +43,20 @@ AUTOCONF := @AUTOCONF@
 _RPATH = @RPATH@
 RPATH = $(if $(1),$(call _RPATH,$(1)))
 cfghdrs_in := $(addprefix $(srcroot),@cfghdrs_in@)
 cfghdrs_out := @cfghdrs_out@
 cfgoutputs_in := $(addprefix $(srcroot),@cfgoutputs_in@)
 cfgoutputs_out := @cfgoutputs_out@
 enable_autogen := @enable_autogen@
 enable_code_coverage := @enable_code_coverage@
+enable_prof := @enable_prof@
 enable_valgrind := @enable_valgrind@
 enable_zone_allocator := @enable_zone_allocator@
+MALLOC_CONF := @JEMALLOC_CPREFIX@MALLOC_CONF
 DSO_LDFLAGS = @DSO_LDFLAGS@
 SOREV = @SOREV@
 PIC_CFLAGS = @PIC_CFLAGS@
 CTARGET = @CTARGET@
 LDTARGET = @LDTARGET@
 MKLIB = @MKLIB@
 AR = @AR@
 ARFLAGS = @ARFLAGS@
@@ -68,26 +70,27 @@ TEST_LIBRARY_PATH := PATH="$(PATH):$(obj
 else
 TEST_LIBRARY_PATH :=
 endif
 endif
 
 LIBJEMALLOC := $(LIBPREFIX)jemalloc$(install_suffix)
 
 # Lists of files.
-BINS := $(srcroot)bin/pprof $(objroot)bin/jemalloc.sh
+BINS := $(objroot)bin/jemalloc-config $(objroot)bin/jemalloc.sh $(objroot)bin/jeprof
 C_HDRS := $(objroot)include/jemalloc/jemalloc$(install_suffix).h
 C_SRCS := $(srcroot)src/jemalloc.c $(srcroot)src/arena.c \
 	$(srcroot)src/atomic.c $(srcroot)src/base.c $(srcroot)src/bitmap.c \
 	$(srcroot)src/chunk.c $(srcroot)src/chunk_dss.c \
 	$(srcroot)src/chunk_mmap.c $(srcroot)src/ckh.c $(srcroot)src/ctl.c \
 	$(srcroot)src/extent.c $(srcroot)src/hash.c $(srcroot)src/huge.c \
-	$(srcroot)src/mb.c $(srcroot)src/mutex.c $(srcroot)src/prof.c \
-	$(srcroot)src/quarantine.c $(srcroot)src/rtree.c $(srcroot)src/stats.c \
-	$(srcroot)src/tcache.c $(srcroot)src/util.c $(srcroot)src/tsd.c
+	$(srcroot)src/mb.c $(srcroot)src/mutex.c $(srcroot)src/pages.c \
+	$(srcroot)src/prof.c $(srcroot)src/quarantine.c $(srcroot)src/rtree.c \
+	$(srcroot)src/stats.c $(srcroot)src/tcache.c $(srcroot)src/util.c \
+	$(srcroot)src/tsd.c
 ifeq ($(enable_valgrind), 1)
 C_SRCS += $(srcroot)src/valgrind.c
 endif
 ifeq ($(enable_zone_allocator), 1)
 C_SRCS += $(srcroot)src/zone.c
 endif
 ifeq ($(IMPORTLIB),$(SO))
 STATIC_LIBS := $(objroot)lib/$(LIBJEMALLOC).$(A)
@@ -99,24 +102,24 @@ STATIC_LIBS += $(objroot)lib/$(LIBJEMALL
 endif
 DSOS := $(objroot)lib/$(LIBJEMALLOC).$(SOREV)
 ifneq ($(SOREV),$(SO))
 DSOS += $(objroot)lib/$(LIBJEMALLOC).$(SO)
 endif
 PC := $(objroot)jemalloc.pc
 MAN3 := $(objroot)doc/jemalloc$(install_suffix).3
 DOCS_XML := $(objroot)doc/jemalloc$(install_suffix).xml
-DOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.html)
-DOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(srcroot)%.3)
+DOCS_HTML := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.html)
+DOCS_MAN3 := $(DOCS_XML:$(objroot)%.xml=$(objroot)%.3)
 DOCS := $(DOCS_HTML) $(DOCS_MAN3)
 C_TESTLIB_SRCS := $(srcroot)test/src/btalloc.c $(srcroot)test/src/btalloc_0.c \
 	$(srcroot)test/src/btalloc_1.c $(srcroot)test/src/math.c \
-	$(srcroot)test/src/mtx.c $(srcroot)test/src/SFMT.c \
-	$(srcroot)test/src/test.c $(srcroot)test/src/thd.c \
-	$(srcroot)test/src/timer.c
+	$(srcroot)test/src/mtx.c $(srcroot)test/src/mq.c \
+	$(srcroot)test/src/SFMT.c $(srcroot)test/src/test.c \
+	$(srcroot)test/src/thd.c $(srcroot)test/src/timer.c
 C_UTIL_INTEGRATION_SRCS := $(srcroot)src/util.c
 TESTS_UNIT := $(srcroot)test/unit/atomic.c \
 	$(srcroot)test/unit/bitmap.c \
 	$(srcroot)test/unit/ckh.c \
 	$(srcroot)test/unit/hash.c \
 	$(srcroot)test/unit/junk.c \
 	$(srcroot)test/unit/junk_alloc.c \
 	$(srcroot)test/unit/junk_free.c \
@@ -132,25 +135,27 @@ TESTS_UNIT := $(srcroot)test/unit/atomic
 	$(srcroot)test/unit/prof_reset.c \
 	$(srcroot)test/unit/prof_thread_name.c \
 	$(srcroot)test/unit/ql.c \
 	$(srcroot)test/unit/qr.c \
 	$(srcroot)test/unit/quarantine.c \
 	$(srcroot)test/unit/rb.c \
 	$(srcroot)test/unit/rtree.c \
 	$(srcroot)test/unit/SFMT.c \
+	$(srcroot)test/unit/size_classes.c \
 	$(srcroot)test/unit/stats.c \
 	$(srcroot)test/unit/tsd.c \
 	$(srcroot)test/unit/util.c \
 	$(srcroot)test/unit/zero.c
 TESTS_INTEGRATION := $(srcroot)test/integration/aligned_alloc.c \
 	$(srcroot)test/integration/allocated.c \
 	$(srcroot)test/integration/sdallocx.c \
 	$(srcroot)test/integration/mallocx.c \
 	$(srcroot)test/integration/MALLOCX_ARENA.c \
+	$(srcroot)test/integration/overflow.c \
 	$(srcroot)test/integration/posix_memalign.c \
 	$(srcroot)test/integration/rallocx.c \
 	$(srcroot)test/integration/thread_arena.c \
 	$(srcroot)test/integration/thread_tcache_enabled.c \
 	$(srcroot)test/integration/xallocx.c \
 	$(srcroot)test/integration/chunk.c
 TESTS_STRESS := $(srcroot)test/stress/microbench.c
 TESTS := $(TESTS_UNIT) $(TESTS_INTEGRATION) $(TESTS_STRESS)
@@ -176,20 +181,20 @@ TESTS_OBJS := $(TESTS_UNIT_OBJS) $(TESTS
 
 .SECONDARY : $(TESTS_OBJS)
 
 # Default target.
 all: build_lib
 
 dist: build_doc
 
-$(srcroot)doc/%.html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl
+$(objroot)doc/%.html : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/html.xsl
 	$(XSLTPROC) -o $@ $(objroot)doc/html.xsl $<
 
-$(srcroot)doc/%.3 : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/manpages.xsl
+$(objroot)doc/%.3 : $(objroot)doc/%.xml $(srcroot)doc/stylesheet.xsl $(objroot)doc/manpages.xsl
 	$(XSLTPROC) -o $@ $(objroot)doc/manpages.xsl $<
 
 build_doc_html: $(DOCS_HTML)
 build_doc_man: $(DOCS_MAN3)
 build_doc: $(DOCS)
 
 #
 # Include generated dependency files.
@@ -339,21 +344,25 @@ check_unit_dir:
 check_integration_dir:
 	@mkdir -p $(objroot)test/integration
 check_stress_dir:
 	@mkdir -p $(objroot)test/stress
 check_dir: check_unit_dir check_integration_dir check_stress_dir
 
 check_unit: tests_unit check_unit_dir
 	$(SHELL) $(objroot)test/test.sh $(TESTS_UNIT:$(srcroot)%.c=$(objroot)%)
+check_integration_prof: tests_integration check_integration_dir
+ifeq ($(enable_prof), 1)
+	$(MALLOC_CONF)="prof:true" $(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
+endif
 check_integration: tests_integration check_integration_dir
 	$(SHELL) $(objroot)test/test.sh $(TESTS_INTEGRATION:$(srcroot)%.c=$(objroot)%)
 check_stress: tests_stress check_stress_dir
 	$(SHELL) $(objroot)test/test.sh $(TESTS_STRESS:$(srcroot)%.c=$(objroot)%)
-check: tests check_dir
+check: tests check_dir check_integration_prof
 	$(SHELL) $(objroot)test/test.sh $(TESTS:$(srcroot)%.c=$(objroot)%)
 
 ifeq ($(enable_code_coverage), 1)
 coverage_unit: check_unit
 	$(SHELL) $(srcroot)coverage.sh $(srcroot)src jet $(C_JET_OBJS)
 	$(SHELL) $(srcroot)coverage.sh $(srcroot)test/src unit $(C_TESTLIB_UNIT_OBJS)
 	$(SHELL) $(srcroot)coverage.sh $(srcroot)test/unit unit $(TESTS_UNIT_OBJS)
 
@@ -403,26 +412,28 @@ clean:
 	rm -f $(TESTS_OBJS:%.$(O)=%.d)
 	rm -f $(TESTS_OBJS:%.$(O)=%.gcda)
 	rm -f $(TESTS_OBJS:%.$(O)=%.gcno)
 	rm -f $(TESTS_OBJS:%.$(O)=%.out)
 	rm -f $(DSOS) $(STATIC_LIBS)
 	rm -f $(objroot)*.gcov.*
 
 distclean: clean
+	rm -f $(objroot)bin/jemalloc-config
 	rm -f $(objroot)bin/jemalloc.sh
+	rm -f $(objroot)bin/jeprof
 	rm -f $(objroot)config.log
 	rm -f $(objroot)config.status
 	rm -f $(objroot)config.stamp
 	rm -f $(cfghdrs_out)
 	rm -f $(cfgoutputs_out)
 
 relclean: distclean
 	rm -f $(objroot)configure
-	rm -f $(srcroot)VERSION
+	rm -f $(objroot)VERSION
 	rm -f $(DOCS_HTML)
 	rm -f $(DOCS_MAN3)
 
 #===============================================================================
 # Re-configuration rules.
 
 ifeq ($(enable_autogen), 1)
 $(srcroot)configure : $(srcroot)configure.ac
--- a/memory/jemalloc/src/VERSION
+++ b/memory/jemalloc/src/VERSION
@@ -1,1 +1,1 @@
-3.6.0-204-gb4acf7300a4ca3423ca36fe227e9bc2e23f25b9f
+4.0.0-0-g6e98caf8f064482b9ab292ef3638dea67420bbc2
new file mode 100644
--- /dev/null
+++ b/memory/jemalloc/src/bin/jemalloc-config.in
@@ -0,0 +1,79 @@
+#!/bin/sh
+
+usage() {
+	cat <<EOF
+Usage:
+  @BINDIR@/jemalloc-config <option>
+Options:
+  --help | -h  : Print usage.
+  --version    : Print jemalloc version.
+  --revision   : Print shared library revision number.
+  --config     : Print configure options used to build jemalloc.
+  --prefix     : Print installation directory prefix.
+  --bindir     : Print binary installation directory.
+  --datadir    : Print data installation directory.
+  --includedir : Print include installation directory.
+  --libdir     : Print library installation directory.
+  --mandir     : Print manual page installation directory.
+  --cc         : Print compiler used to build jemalloc.
+  --cflags     : Print compiler flags used to build jemalloc.
+  --cppflags   : Print preprocessor flags used to build jemalloc.
+  --ldflags    : Print library flags used to build jemalloc.
+  --libs       : Print libraries jemalloc was linked against.
+EOF
+}
+
+prefix="@prefix@"
+exec_prefix="@exec_prefix@"
+
+case "$1" in
+--help | -h)
+	usage
+	exit 0
+	;;
+--version)
+	echo "@jemalloc_version@"
+	;;
+--revision)
+	echo "@rev@"
+	;;
+--config)
+	echo "@CONFIG@"
+	;;
+--prefix)
+	echo "@PREFIX@"
+	;;
+--bindir)
+	echo "@BINDIR@"
+	;;
+--datadir)
+	echo "@DATADIR@"
+	;;
+--includedir)
+	echo "@INCLUDEDIR@"
+	;;
+--libdir)
+	echo "@LIBDIR@"
+	;;
+--mandir)
+	echo "@MANDIR@"
+	;;
+--cc)
+	echo "@CC@"
+	;;
+--cflags)
+	echo "@CFLAGS@"
+	;;
+--cppflags)
+	echo "@CPPFLAGS@"
+	;;
+--ldflags)
+	echo "@LDFLAGS@ @EXTRA_LDFLAGS@"
+	;;
+--libs)
+	echo "@LIBS@"
+	;;
+*)
+	usage
+	exit 1
+esac
new file mode 100644
--- /dev/null
+++ b/memory/jemalloc/src/bin/jeprof.in
@@ -0,0 +1,5510 @@
+#! /usr/bin/env perl
+
+# Copyright (c) 1998-2007, Google Inc.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+#     * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#     * Redistributions in binary form must reproduce the above
+# copyright notice, this list of conditions and the following disclaimer
+# in the documentation and/or other materials provided with the
+# distribution.
+#     * Neither the name of Google Inc. nor the names of its
+# contributors may be used to endorse or promote products derived from
+# this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+# ---
+# Program for printing the profile generated by common/profiler.cc,
+# or by the heap profiler (common/debugallocation.cc)
+#
+# The profile contains a sequence of entries of the form:
+#       <count> <stack trace>
+# This program parses the profile, and generates user-readable
+# output.
+#
+# Examples:
+#
+# % tools/jeprof "program" "profile"
+#   Enters "interactive" mode
+#
+# % tools/jeprof --text "program" "profile"
+#   Generates one line per procedure
+#
+# % tools/jeprof --gv "program" "profile"
+#   Generates annotated call-graph and displays via "gv"
+#
+# % tools/jeprof --gv --focus=Mutex "program" "profile"
+#   Restrict to code paths that involve an entry that matches "Mutex"
+#
+# % tools/jeprof --gv --focus=Mutex --ignore=string "program" "profile"
+#   Restrict to code paths that involve an entry that matches "Mutex"
+#   and does not match "string"
+#
+# % tools/jeprof --list=IBF_CheckDocid "program" "profile"
+#   Generates disassembly listing of all routines with at least one
+#   sample that match the --list=<regexp> pattern.  The listing is
+#   annotated with the flat and cumulative sample counts at each line.
+#
+# % tools/jeprof --disasm=IBF_CheckDocid "program" "profile"
+#   Generates disassembly listing of all routines with at least one
+#   sample that match the --disasm=<regexp> pattern.  The listing is
+#   annotated with the flat and cumulative sample counts at each PC value.
+#
+# TODO: Use color to indicate files?
+
+use strict;
+use warnings;
+use Getopt::Long;
+
+my $JEPROF_VERSION = "@jemalloc_version@";
+my $PPROF_VERSION = "2.0";
+
+# These are the object tools we use which can come from a
+# user-specified location using --tools, from the JEPROF_TOOLS
+# environment variable, or from the environment.
+my %obj_tool_map = (
+  "objdump" => "objdump",
+  "nm" => "nm",
+  "addr2line" => "addr2line",
+  "c++filt" => "c++filt",
+  ## ConfigureObjTools may add architecture-specific entries:
+  #"nm_pdb" => "nm-pdb",       # for reading windows (PDB-format) executables
+  #"addr2line_pdb" => "addr2line-pdb",                                # ditto
+  #"otool" => "otool",         # equivalent of objdump on OS X
+);
+# NOTE: these are lists, so you can put in commandline flags if you want.
+my @DOT = ("dot");          # leave non-absolute, since it may be in /usr/local
+my @GV = ("gv");
+my @EVINCE = ("evince");    # could also be xpdf or perhaps acroread
+my @KCACHEGRIND = ("kcachegrind");
+my @PS2PDF = ("ps2pdf");
+# These are used for dynamic profiles
+my @URL_FETCHER = ("curl", "-s");
+
+# These are the web pages that servers need to support for dynamic profiles
+my $HEAP_PAGE = "/pprof/heap";
+my $PROFILE_PAGE = "/pprof/profile";   # must support cgi-param "?seconds=#"
+my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
+                                                # ?seconds=#&event=x&period=n
+my $GROWTH_PAGE = "/pprof/growth";
+my $CONTENTION_PAGE = "/pprof/contention";
+my $WALL_PAGE = "/pprof/wall(?:\\?.*)?";  # accepts options like namefilter
+my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
+my $CENSUSPROFILE_PAGE = "/pprof/censusprofile(?:\\?.*)?"; # must support cgi-param
+                                                       # "?seconds=#",
+                                                       # "?tags_regexp=#" and
+                                                       # "?type=#".
+my $SYMBOL_PAGE = "/pprof/symbol";     # must support symbol lookup via POST
+my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
+
+# These are the web pages that can be named on the command line.
+# All the alternatives must begin with /.
+my $PROFILES = "($HEAP_PAGE|$PROFILE_PAGE|$PMUPROFILE_PAGE|" .
+               "$GROWTH_PAGE|$CONTENTION_PAGE|$WALL_PAGE|" .
+               "$FILTEREDPROFILE_PAGE|$CENSUSPROFILE_PAGE)";
+
+# default binary name
+my $UNKNOWN_BINARY = "(unknown)";
+
+# There is a pervasive dependency on the length (in hex characters,
+# i.e., nibbles) of an address, distinguishing between 32-bit and
+# 64-bit profiles.  To err on the safe size, default to 64-bit here:
+my $address_length = 16;
+
+my $dev_null = "/dev/null";
+if (! -e $dev_null && $^O =~ /MSWin/) {    # $^O is the OS perl was built for
+  $dev_null = "nul";
+}
+
+# A list of paths to search for shared object files
+my @prefix_list = ();
+
+# Special routine name that should not have any symbols.
+# Used as separator to parse "addr2line -i" output.
+my $sep_symbol = '_fini';
+my $sep_address = undef;
+
+##### Argument parsing #####
+
+sub usage_string {
+  return <<EOF;
+Usage:
+jeprof [options] <program> <profiles>
+   <profiles> is a space separated list of profile names.
+jeprof [options] <symbolized-profiles>
+   <symbolized-profiles> is a list of profile files where each file contains
+   the necessary symbol mappings  as well as profile data (likely generated
+   with --raw).
+jeprof [options] <profile>
+   <profile> is a remote form.  Symbols are obtained from host:port$SYMBOL_PAGE
+
+   Each name can be:
+   /path/to/profile        - a path to a profile file
+   host:port[/<service>]   - a location of a service to get profile from
+
+   The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
+                         $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
+                         $CENSUSPROFILE_PAGE, or /pprof/filteredprofile.
+   For instance:
+     jeprof http://myserver.com:80$HEAP_PAGE
+   If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
+jeprof --symbols <program>
+   Maps addresses to symbol names.  In this mode, stdin should be a
+   list of library mappings, in the same format as is found in the heap-
+   and cpu-profile files (this loosely matches that of /proc/self/maps
+   on linux), followed by a list of hex addresses to map, one per line.
+
+   For more help with querying remote servers, including how to add the
+   necessary server-side support code, see this filename (or one like it):
+
+   /usr/doc/gperftools-$PPROF_VERSION/pprof_remote_servers.html
+
+Options:
+   --cum               Sort by cumulative data
+   --base=<base>       Subtract <base> from <profile> before display
+   --interactive       Run in interactive mode (interactive "help" gives help) [default]
+   --seconds=<n>       Length of time for dynamic profiles [default=30 secs]
+   --add_lib=<file>    Read additional symbols and line info from the given library
+   --lib_prefix=<dir>  Comma separated list of library path prefixes
+
+Reporting Granularity:
+   --addresses         Report at address level
+   --lines             Report at source line level
+   --functions         Report at function level [default]
+   --files             Report at source file level
+
+Output type:
+   --text              Generate text report
+   --callgrind         Generate callgrind format to stdout
+   --gv                Generate Postscript and display
+   --evince            Generate PDF and display
+   --web               Generate SVG and display
+   --list=<regexp>     Generate source listing of matching routines
+   --disasm=<regexp>   Generate disassembly of matching routines
+   --symbols           Print demangled symbol names found at given addresses
+   --dot               Generate DOT file to stdout
+   --ps                Generate Postcript to stdout
+   --pdf               Generate PDF to stdout
+   --svg               Generate SVG to stdout
+   --gif               Generate GIF to stdout
+   --raw               Generate symbolized jeprof data (useful with remote fetch)
+
+Heap-Profile Options:
+   --inuse_space       Display in-use (mega)bytes [default]
+   --inuse_objects     Display in-use objects
+   --alloc_space       Display allocated (mega)bytes
+   --alloc_objects     Display allocated objects
+   --show_bytes        Display space in bytes
+   --drop_negative     Ignore negative differences
+
+Contention-profile options:
+   --total_delay       Display total delay at each region [default]
+   --contentions       Display number of delays at each region
+   --mean_delay        Display mean delay at each region
+
+Call-graph Options:
+   --nodecount=<n>     Show at most so many nodes [default=80]
+   --nodefraction=<f>  Hide nodes below <f>*total [default=.005]
+   --edgefraction=<f>  Hide edges below <f>*total [default=.001]
+   --maxdegree=<n>     Max incoming/outgoing edges per node [default=8]
+   --focus=<regexp>    Focus on nodes matching <regexp>
+   --thread=<n>        Show profile for thread <n>
+   --ignore=<regexp>   Ignore nodes matching <regexp>
+   --scale=<n>         Set GV scaling [default=0]
+   --heapcheck         Make nodes with non-0 object counts
+                       (i.e. direct leak generators) more visible
+
+Miscellaneous:
+   --tools=<prefix or binary:fullpath>[,...]   \$PATH for object tool pathnames
+   --test              Run unit tests
+   --help              This message
+   --version           Version information
+
+Environment Variables:
+   JEPROF_TMPDIR        Profiles directory. Defaults to \$HOME/jeprof
+   JEPROF_TOOLS         Prefix for object tools pathnames
+
+Examples:
+
+jeprof /bin/ls ls.prof
+                       Enters "interactive" mode
+jeprof --text /bin/ls ls.prof
+                       Outputs one line per procedure
+jeprof --web /bin/ls ls.prof
+                       Displays annotated call-graph in web browser
+jeprof --gv /bin/ls ls.prof
+                       Displays annotated call-graph via 'gv'
+jeprof --gv --focus=Mutex /bin/ls ls.prof
+                       Restricts to code paths including a .*Mutex.* entry
+jeprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
+                       Code paths including Mutex but not string
+jeprof --list=getdir /bin/ls ls.prof
+                       (Per-line) annotated source listing for getdir()
+jeprof --disasm=getdir /bin/ls ls.prof
+                       (Per-PC) annotated disassembly for getdir()
+
+jeprof http://localhost:1234/
+                       Enters "interactive" mode
+jeprof --text localhost:1234
+                       Outputs one line per procedure for localhost:1234
+jeprof --raw localhost:1234 > ./local.raw
+jeprof --text ./local.raw
+                       Fetches a remote profile for later analysis and then
+                       analyzes it in text mode.
+EOF
+}
+
+sub version_string {
+  return <<EOF
+jeprof (part of jemalloc $JEPROF_VERSION)
+based on pprof (part of gperftools $PPROF_VERSION)
+
+Copyright 1998-2007 Google Inc.
+
+This is BSD licensed software; see the source for copying conditions
+and license information.
+There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
+PARTICULAR PURPOSE.
+EOF
+}
+
+sub usage {
+  my $msg = shift;
+  print STDERR "$msg\n\n";
+  print STDERR usage_string();
+  print STDERR "\nFATAL ERROR: $msg\n";    # just as a reminder
+  exit(1);
+}
+
+sub Init() {
+  # Setup tmp-file name and handler to clean it up.
+  # We do this in the very beginning so that we can use
+  # error() and cleanup() function anytime here after.
+  $main::tmpfile_sym = "/tmp/jeprof$$.sym";
+  $main::tmpfile_ps = "/tmp/jeprof$$";
+  $main::next_tmpfile = 0;
+  $SIG{'INT'} = \&sighandler;
+
+  # Cache from filename/linenumber to source code
+  $main::source_cache = ();
+
+  $main::opt_help = 0;
+  $main::opt_version = 0;
+
+  $main::opt_cum = 0;
+  $main::opt_base = '';
+  $main::opt_addresses = 0;
+  $main::opt_lines = 0;
+  $main::opt_functions = 0;
+  $main::opt_files = 0;
+  $main::opt_lib_prefix = "";
+
+  $main::opt_text = 0;
+  $main::opt_callgrind = 0;
+  $main::opt_list = "";
+  $main::opt_disasm = "";
+  $main::opt_symbols = 0;
+  $main::opt_gv = 0;
+  $main::opt_evince = 0;
+  $main::opt_web = 0;
+  $main::opt_dot = 0;
+  $main::opt_ps = 0;
+  $main::opt_pdf = 0;
+  $main::opt_gif = 0;
+  $main::opt_svg = 0;
+  $main::opt_raw = 0;
+
+  $main::opt_nodecount = 80;
+  $main::opt_nodefraction = 0.005;
+  $main::opt_edgefraction = 0.001;
+  $main::opt_maxdegree = 8;
+  $main::opt_focus = '';
+  $main::opt_thread = undef;
+  $main::opt_ignore = '';
+  $main::opt_scale = 0;
+  $main::opt_heapcheck = 0;
+  $main::opt_seconds = 30;
+  $main::opt_lib = "";
+
+  $main::opt_inuse_space   = 0;
+  $main::opt_inuse_objects = 0;
+  $main::opt_alloc_space   = 0;
+  $main::opt_alloc_objects = 0;
+  $main::opt_show_bytes    = 0;
+  $main::opt_drop_negative = 0;
+  $main::opt_interactive   = 0;
+
+  $main::opt_total_delay = 0;
+  $main::opt_contentions = 0;
+  $main::opt_mean_delay = 0;
+
+  $main::opt_tools   = "";
+  $main::opt_debug   = 0;
+  $main::opt_test    = 0;
+
+  # These are undocumented flags used only by unittests.
+  $main::opt_test_stride = 0;
+
+  # Are we using $SYMBOL_PAGE?
+  $main::use_symbol_page = 0;
+
+  # Files returned by TempName.
+  %main::tempnames = ();
+
+  # Type of profile we are dealing with
+  # Supported types:
+  #     cpu
+  #     heap
+  #     growth
+  #     contention
+  $main::profile_type = '';     # Empty type means "unknown"
+
+  GetOptions("help!"          => \$main::opt_help,
+             "version!"       => \$main::opt_version,
+             "cum!"           => \$main::opt_cum,
+             "base=s"         => \$main::opt_base,
+             "seconds=i"      => \$main::opt_seconds,
+             "add_lib=s"      => \$main::opt_lib,
+             "lib_prefix=s"   => \$main::opt_lib_prefix,
+             "functions!"     => \$main::opt_functions,
+             "lines!"         => \$main::opt_lines,
+             "addresses!"     => \$main::opt_addresses,
+             "files!"         => \$main::opt_files,
+             "text!"          => \$main::opt_text,
+             "callgrind!"     => \$main::opt_callgrind,
+             "list=s"         => \$main::opt_list,
+             "disasm=s"       => \$main::opt_disasm,
+             "symbols!"       => \$main::opt_symbols,
+             "gv!"            => \$main::opt_gv,
+             "evince!"        => \$main::opt_evince,
+             "web!"           => \$main::opt_web,
+             "dot!"           => \$main::opt_dot,
+             "ps!"            => \$main::opt_ps,
+             "pdf!"           => \$main::opt_pdf,
+             "svg!"           => \$main::opt_svg,
+             "gif!"           => \$main::opt_gif,
+             "raw!"           => \$main::opt_raw,
+             "interactive!"   => \$main::opt_interactive,
+             "nodecount=i"    => \$main::opt_nodecount,
+             "nodefraction=f" => \$main::opt_nodefraction,
+             "edgefraction=f" => \$main::opt_edgefraction,
+             "maxdegree=i"    => \$main::opt_maxdegree,
+             "focus=s"        => \$main::opt_focus,
+             "thread=s"       => \$main::opt_thread,
+             "ignore=s"       => \$main::opt_ignore,
+             "scale=i"        => \$main::opt_scale,
+             "heapcheck"      => \$main::opt_heapcheck,
+             "inuse_space!"   => \$main::opt_inuse_space,
+             "inuse_objects!" => \$main::opt_inuse_objects,
+             "alloc_space!"   => \$main::opt_alloc_space,
+             "alloc_objects!" => \$main::opt_alloc_objects,
+             "show_bytes!"    => \$main::opt_show_bytes,
+             "drop_negative!" => \$main::opt_drop_negative,
+             "total_delay!"   => \$main::opt_total_delay,
+             "contentions!"   => \$main::opt_contentions,
+             "mean_delay!"    => \$main::opt_mean_delay,
+             "tools=s"        => \$main::opt_tools,
+             "test!"          => \$main::opt_test,
+             "debug!"         => \$main::opt_debug,
+             # Undocumented flags used only by unittests:
+             "test_stride=i"  => \$main::opt_test_stride,
+      ) || usage("Invalid option(s)");
+
+  # Deal with the standard --help and --version
+  if ($main::opt_help) {
+    print usage_string();
+    exit(0);
+  }
+
+  if ($main::opt_version) {
+    print version_string();
+    exit(0);
+  }
+
+  # Disassembly/listing/symbols mode requires address-level info
+  if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
+    $main::opt_functions = 0;
+    $main::opt_lines = 0;
+    $main::opt_addresses = 1;
+    $main::opt_files = 0;
+  }
+
+  # Check heap-profiling flags
+  if ($main::opt_inuse_space +
+      $main::opt_inuse_objects +
+      $main::opt_alloc_space +
+      $main::opt_alloc_objects > 1) {
+    usage("Specify at most on of --inuse/--alloc options");
+  }
+
+  # Check output granularities
+  my $grains =
+      $main::opt_functions +
+      $main::opt_lines +
+      $main::opt_addresses +
+      $main::opt_files +
+      0;
+  if ($grains > 1) {
+    usage("Only specify one output granularity option");
+  }
+  if ($grains == 0) {
+    $main::opt_functions = 1;
+  }
+
+  # Check output modes
+  my $modes =
+      $main::opt_text +
+      $main::opt_callgrind +
+      ($main::opt_list eq '' ? 0 : 1) +
+      ($main::opt_disasm eq '' ? 0 : 1) +
+      ($main::opt_symbols == 0 ? 0 : 1) +
+      $main::opt_gv +
+      $main::opt_evince +
+      $main::opt_web +
+      $main::opt_dot +
+      $main::opt_ps +
+      $main::opt_pdf +
+      $main::opt_svg +
+      $main::opt_gif +
+      $main::opt_raw +
+      $main::opt_interactive +
+      0;
+  if ($modes > 1) {
+    usage("Only specify one output mode");
+  }
+  if ($modes == 0) {
+    if (-t STDOUT) {  # If STDOUT is a tty, activate interactive mode
+      $main::opt_interactive = 1;
+    } else {
+      $main::opt_text = 1;
+    }
+  }
+
+  if ($main::opt_test) {
+    RunUnitTests();
+    # Should not return
+    exit(1);
+  }
+
+  # Binary name and profile arguments list
+  $main::prog = "";
+  @main::pfile_args = ();
+
+  # Remote profiling without a binary (using $SYMBOL_PAGE instead)
+  if (@ARGV > 0) {
+    if (IsProfileURL($ARGV[0])) {
+      $main::use_symbol_page = 1;
+    } elsif (IsSymbolizedProfileFile($ARGV[0])) {
+      $main::use_symbolized_profile = 1;
+      $main::prog = $UNKNOWN_BINARY;  # will be set later from the profile file
+    }
+  }
+
+  if ($main::use_symbol_page || $main::use_symbolized_profile) {
+    # We don't need a binary!
+    my %disabled = ('--lines' => $main::opt_lines,
+                    '--disasm' => $main::opt_disasm);
+    for my $option (keys %disabled) {
+      usage("$option cannot be used without a binary") if $disabled{$option};
+    }
+    # Set $main::prog later...
+    scalar(@ARGV) || usage("Did not specify profile file");
+  } elsif ($main::opt_symbols) {
+    # --symbols needs a binary-name (to run nm on, etc) but not profiles
+    $main::prog = shift(@ARGV) || usage("Did not specify program");
+  } else {
+    $main::prog = shift(@ARGV) || usage("Did not specify program");
+    scalar(@ARGV) || usage("Did not specify profile file");
+  }
+
+  # Parse profile file/location arguments
+  foreach my $farg (@ARGV) {
+    if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
+      my $machine = $1;
+      my $num_machines = $2;
+      my $path = $3;
+      for (my $i = 0; $i < $num_machines; $i++) {
+        unshift(@main::pfile_args, "$i.$machine$path");
+      }
+    } else {
+      unshift(@main::pfile_args, $farg);
+    }
+  }
+
+  if ($main::use_symbol_page) {
+    unless (IsProfileURL($main::pfile_args[0])) {
+      error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
+    }
+    CheckSymbolPage();
+    $main::prog = FetchProgramName();
+  } elsif (!$main::use_symbolized_profile) {  # may not need objtools!
+    ConfigureObjTools($main::prog)
+  }
+
+  # Break the opt_lib_prefix into the prefix_list array
+  @prefix_list = split (',', $main::opt_lib_prefix);
+
+  # Remove trailing / from the prefixes, in the list to prevent
+  # searching things like /my/path//lib/mylib.so
+  foreach (@prefix_list) {
+    s|/+$||;
+  }
+}
+
+sub FilterAndPrint {
+  my ($profile, $symbols, $libs, $thread) = @_;
+
+  # Get total data in profile
+  my $total = TotalProfile($profile);
+
+  # Remove uniniteresting stack items
+  $profile = RemoveUninterestingFrames($symbols, $profile);
+
+  # Focus?
+  if ($main::opt_focus ne '') {
+    $profile = FocusProfile($symbols, $profile, $main::opt_focus);
+  }
+
+  # Ignore?
+  if ($main::opt_ignore ne '') {
+    $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
+  }
+
+  my $calls = ExtractCalls($symbols, $profile);
+
+  # Reduce profiles to required output granularity, and also clean
+  # each stack trace so a given entry exists at most once.
+  my $reduced = ReduceProfile($symbols, $profile);
+
+  # Get derived profiles
+  my $flat = FlatProfile($reduced);
+  my $cumulative = CumulativeProfile($reduced);
+
+  # Print
+  if (!$main::opt_interactive) {
+    if ($main::opt_disasm) {
+      PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm);
+    } elsif ($main::opt_list) {
+      PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
+    } elsif ($main::opt_text) {
+      # Make sure the output is empty when have nothing to report
+      # (only matters when --heapcheck is given but we must be
+      # compatible with old branches that did not pass --heapcheck always):
+      if ($total != 0) {
+        printf("Total%s: %s %s\n",
+               (defined($thread) ? " (t$thread)" : ""),
+               Unparse($total), Units());
+      }
+      PrintText($symbols, $flat, $cumulative, -1);
+    } elsif ($main::opt_raw) {
+      PrintSymbolizedProfile($symbols, $profile, $main::prog);
+    } elsif ($main::opt_callgrind) {
+      PrintCallgrind($calls);
+    } else {
+      if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
+        if ($main::opt_gv) {
+          RunGV(TempName($main::next_tmpfile, "ps"), "");
+        } elsif ($main::opt_evince) {
+          RunEvince(TempName($main::next_tmpfile, "pdf"), "");
+        } elsif ($main::opt_web) {
+          my $tmp = TempName($main::next_tmpfile, "svg");
+          RunWeb($tmp);
+          # The command we run might hand the file name off
+          # to an already running browser instance and then exit.
+          # Normally, we'd remove $tmp on exit (right now),
+          # but fork a child to remove $tmp a little later, so that the
+          # browser has time to load it first.
+          delete $main::tempnames{$tmp};
+          if (fork() == 0) {
+            sleep 5;
+            unlink($tmp);
+            exit(0);
+          }
+        }
+      } else {
+        cleanup();
+        exit(1);
+      }
+    }
+  } else {
+    InteractiveMode($profile, $symbols, $libs, $total);
+  }
+}
+
+sub Main() {
+  Init();
+  $main::collected_profile = undef;
+  @main::profile_files = ();
+  $main::op_time = time();
+
+  # Printing symbols is special and requires a lot less info that most.
+  if ($main::opt_symbols) {
+    PrintSymbols(*STDIN);   # Get /proc/maps and symbols output from stdin
+    return;
+  }
+
+  # Fetch all profile data
+  FetchDynamicProfiles();
+
+  # this will hold symbols that we read from the profile files
+  my $symbol_map = {};
+
+  # Read one profile, pick the last item on the list
+  my $data = ReadProfile($main::prog, pop(@main::profile_files));
+  my $profile = $data->{profile};
+  my $pcs = $data->{pcs};
+  my $libs = $data->{libs};   # Info about main program and shared libraries
+  $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
+
+  # Add additional profiles, if available.
+  if (scalar(@main::profile_files) > 0) {
+    foreach my $pname (@main::profile_files) {
+      my $data2 = ReadProfile($main::prog, $pname);
+      $profile = AddProfile($profile, $data2->{profile});
+      $pcs = AddPcs($pcs, $data2->{pcs});
+      $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
+    }
+  }
+
+  # Subtract base from profile, if specified
+  if ($main::opt_base ne '') {
+    my $base = ReadProfile($main::prog, $main::opt_base);
+    $profile = SubtractProfile($profile, $base->{profile});
+    $pcs = AddPcs($pcs, $base->{pcs});
+    $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
+  }
+
+  # Collect symbols
+  my $symbols;
+  if ($main::use_symbolized_profile) {
+    $symbols = FetchSymbols($pcs, $symbol_map);
+  } elsif ($main::use_symbol_page) {
+    $symbols = FetchSymbols($pcs);
+  } else {
+    # TODO(csilvers): $libs uses the /proc/self/maps data from profile1,
+    # which may differ from the data from subsequent profiles, especially
+    # if they were run on different machines.  Use appropriate libs for
+    # each pc somehow.
+    $symbols = ExtractSymbols($libs, $pcs);
+  }
+
+  if (!defined($main::opt_thread)) {
+    FilterAndPrint($profile, $symbols, $libs);
+  }
+  if (defined($data->{threads})) {
+    foreach my $thread (sort { $a <=> $b } keys(%{$data->{threads}})) {
+      if (defined($main::opt_thread) &&
+          ($main::opt_thread eq '*' || $main::opt_thread == $thread)) {
+        my $thread_profile = $data->{threads}{$thread};
+        FilterAndPrint($thread_profile, $symbols, $libs, $thread);
+      }
+    }
+  }
+
+  cleanup();
+  exit(0);
+}
+
+##### Entry Point #####
+
+Main();
+
+# Temporary code to detect if we're running on a Goobuntu system.
+# These systems don't have the right stuff installed for the special
+# Readline libraries to work, so as a temporary workaround, we default
+# to using the normal stdio code, rather than the fancier readline-based
+# code
+sub ReadlineMightFail {
+  if (-e '/lib/libtermcap.so.2') {
+    return 0;  # libtermcap exists, so readline should be okay
+  } else {
+    return 1;
+  }
+}
+
+sub RunGV {
+  my $fname = shift;
+  my $bg = shift;       # "" or " &" if we should run in background
+  if (!system(ShellEscape(@GV, "--version") . " >$dev_null 2>&1")) {
+    # Options using double dash are supported by this gv version.
+    # Also, turn on noantialias to better handle bug in gv for
+    # postscript files with large dimensions.
+    # TODO: Maybe we should not pass the --noantialias flag
+    # if the gv version is known to work properly without the flag.
+    system(ShellEscape(@GV, "--scale=$main::opt_scale", "--noantialias", $fname)
+           . $bg);
+  } else {
+    # Old gv version - only supports options that use single dash.
+    print STDERR ShellEscape(@GV, "-scale", $main::opt_scale) . "\n";
+    system(ShellEscape(@GV, "-scale", "$main::opt_scale", $fname) . $bg);
+  }
+}
+
+sub RunEvince {
+  my $fname = shift;
+  my $bg = shift;       # "" or " &" if we should run in background
+  system(ShellEscape(@EVINCE, $fname) . $bg);
+}
+
+sub RunWeb {
+  my $fname = shift;
+  print STDERR "Loading web page file:///$fname\n";
+
+  if (`uname` =~ /Darwin/) {
+    # OS X: open will use standard preference for SVG files.
+    system("/usr/bin/open", $fname);
+    return;
+  }
+
+  # Some kind of Unix; try generic symlinks, then specific browsers.
+  # (Stop once we find one.)
+  # Works best if the browser is already running.
+  my @alt = (
+    "/etc/alternatives/gnome-www-browser",
+    "/etc/alternatives/x-www-browser",
+    "google-chrome",
+    "firefox",
+  );
+  foreach my $b (@alt) {
+    if (system($b, $fname) == 0) {
+      return;
+    }
+  }
+
+  print STDERR "Could not load web browser.\n";
+}
+
+sub RunKcachegrind {
+  my $fname = shift;
+  my $bg = shift;       # "" or " &" if we should run in background
+  print STDERR "Starting '@KCACHEGRIND " . $fname . $bg . "'\n";
+  system(ShellEscape(@KCACHEGRIND, $fname) . $bg);
+}
+
+
+##### Interactive helper routines #####
+
+sub InteractiveMode {
+  $| = 1;  # Make output unbuffered for interactive mode
+  my ($orig_profile, $symbols, $libs, $total) = @_;
+
+  print STDERR "Welcome to jeprof!  For help, type 'help'.\n";
+
+  # Use ReadLine if it's installed and input comes from a console.
+  if ( -t STDIN &&
+       !ReadlineMightFail() &&
+       defined(eval {require Term::ReadLine}) ) {
+    my $term = new Term::ReadLine 'jeprof';
+    while ( defined ($_ = $term->readline('(jeprof) '))) {
+      $term->addhistory($_) if /\S/;
+      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
+        last;    # exit when we get an interactive command to quit
+      }
+    }
+  } else {       # don't have readline
+    while (1) {
+      print STDERR "(jeprof) ";
+      $_ = <STDIN>;
+      last if ! defined $_ ;
+      s/\r//g;         # turn windows-looking lines into unix-looking lines
+
+      # Save some flags that might be reset by InteractiveCommand()
+      my $save_opt_lines = $main::opt_lines;
+
+      if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
+        last;    # exit when we get an interactive command to quit
+      }
+
+      # Restore flags
+      $main::opt_lines = $save_opt_lines;
+    }
+  }
+}
+
+# Takes two args: orig profile, and command to run.
+# Returns 1 if we should keep going, or 0 if we were asked to quit
+sub InteractiveCommand {
+  my($orig_profile, $symbols, $libs, $total, $command) = @_;
+  $_ = $command;                # just to make future m//'s easier
+  if (!defined($_)) {
+    print STDERR "\n";
+    return 0;
+  }
+  if (m/^\s*quit/) {
+    return 0;
+  }
+  if (m/^\s*help/) {
+    InteractiveHelpMessage();
+    return 1;
+  }
+  # Clear all the mode options -- mode is controlled by "$command"
+  $main::opt_text = 0;
+  $main::opt_callgrind = 0;
+  $main::opt_disasm = 0;
+  $main::opt_list = 0;
+  $main::opt_gv = 0;
+  $main::opt_evince = 0;
+  $main::opt_cum = 0;
+
+  if (m/^\s*(text|top)(\d*)\s*(.*)/) {
+    $main::opt_text = 1;
+
+    my $line_limit = ($2 ne "") ? int($2) : 10;
+
+    my $routine;
+    my $ignore;
+    ($routine, $ignore) = ParseInteractiveArgs($3);
+
+    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
+    my $reduced = ReduceProfile($symbols, $profile);
+
+    # Get derived profiles
+    my $flat = FlatProfile($reduced);
+    my $cumulative = CumulativeProfile($reduced);
+
+    PrintText($symbols, $flat, $cumulative, $line_limit);
+    return 1;
+  }
+  if (m/^\s*callgrind\s*([^ \n]*)/) {
+    $main::opt_callgrind = 1;
+
+    # Get derived profiles
+    my $calls = ExtractCalls($symbols, $orig_profile);
+    my $filename = $1;
+    if ( $1 eq '' ) {
+      $filename = TempName($main::next_tmpfile, "callgrind");
+    }
+    PrintCallgrind($calls, $filename);
+    if ( $1 eq '' ) {
+      RunKcachegrind($filename, " & ");
+      $main::next_tmpfile++;
+    }
+
+    return 1;
+  }
+  if (m/^\s*(web)?list\s*(.+)/) {
+    my $html = (defined($1) && ($1 eq "web"));
+    $main::opt_list = 1;
+
+    my $routine;
+    my $ignore;
+    ($routine, $ignore) = ParseInteractiveArgs($2);
+
+    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
+    my $reduced = ReduceProfile($symbols, $profile);
+
+    # Get derived profiles
+    my $flat = FlatProfile($reduced);
+    my $cumulative = CumulativeProfile($reduced);
+
+    PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
+    return 1;
+  }
+  if (m/^\s*disasm\s*(.+)/) {
+    $main::opt_disasm = 1;
+
+    my $routine;
+    my $ignore;
+    ($routine, $ignore) = ParseInteractiveArgs($1);
+
+    # Process current profile to account for various settings
+    my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
+    my $reduced = ReduceProfile($symbols, $profile);
+
+    # Get derived profiles
+    my $flat = FlatProfile($reduced);
+    my $cumulative = CumulativeProfile($reduced);
+
+    PrintDisassembly($libs, $flat, $cumulative, $routine);
+    return 1;
+  }
+  if (m/^\s*(gv|web|evince)\s*(.*)/) {
+    $main::opt_gv = 0;
+    $main::opt_evince = 0;
+    $main::opt_web = 0;
+    if ($1 eq "gv") {
+      $main::opt_gv = 1;
+    } elsif ($1 eq "evince") {
+      $main::opt_evince = 1;
+    } elsif ($1 eq "web") {
+      $main::opt_web = 1;
+    }
+
+    my $focus;
+    my $ignore;
+    ($focus, $ignore) = ParseInteractiveArgs($2);
+
+    # Process current profile to account for various settings
+    my $profile = ProcessProfile($total, $orig_profile, $symbols,
+                                 $focus, $ignore);
+    my $reduced = ReduceProfile($symbols, $profile);
+
+    # Get derived profiles
+    my $flat = FlatProfile($reduced);
+    my $cumulative = CumulativeProfile($reduced);
+
+    if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
+      if ($main::opt_gv) {
+        RunGV(TempName($main::next_tmpfile, "ps"), " &");
+      } elsif ($main::opt_evince) {
+        RunEvince(TempName($main::next_tmpfile, "pdf"), " &");
+      } elsif ($main::opt_web) {
+        RunWeb(TempName($main::next_tmpfile, "svg"));
+      }
+      $main::next_tmpfile++;
+    }
+    return 1;
+  }
+  if (m/^\s*$/) {
+    return 1;
+  }
+  print STDERR "Unknown command: try 'help'.\n";
+  return 1;
+}
+
+
+sub ProcessProfile {
+  my $total_count = shift;
+  my $orig_profile = shift;
+  my $symbols = shift;
+  my $focus = shift;
+  my $ignore = shift;
+
+  # Process current profile to account for various settings
+  my $profile = $orig_profile;
+  printf("Total: %s %s\n", Unparse($total_count), Units());
+  if ($focus ne '') {
+    $profile = FocusProfile($symbols, $profile, $focus);
+    my $focus_count = TotalProfile($profile);
+    printf("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
+           $focus,
+           Unparse($focus_count), Units(),
+           Unparse($total_count), ($focus_count*100.0) / $total_count);
+  }
+  if ($ignore ne '') {
+    $profile = IgnoreProfile($symbols, $profile, $ignore);
+    my $ignore_count = TotalProfile($profile);
+    printf("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
+           $ignore,
+           Unparse($ignore_count), Units(),
+           Unparse($total_count),
+           ($ignore_count*100.0) / $total_count);
+  }
+
+  return $profile;
+}
+
+sub InteractiveHelpMessage {
+  print STDERR <<ENDOFHELP;
+Interactive jeprof mode
+
+Commands:
+  gv
+  gv [focus] [-ignore1] [-ignore2]
+      Show graphical hierarchical display of current profile.  Without
+      any arguments, shows all samples in the profile.  With the optional
+      "focus" argument, restricts the samples shown to just those where
+      the "focus" regular expression matches a routine name on the stack
+      trace.
+
+  web
+  web [focus] [-ignore1] [-ignore2]
+      Like GV, but displays profile in your web browser instead of using
+      Ghostview. Works best if your web browser is already running.
+      To change the browser that gets used:
+      On Linux, set the /etc/alternatives/gnome-www-browser symlink.
+      On OS X, change the Finder association for SVG files.
+
+  list [routine_regexp] [-ignore1] [-ignore2]
+      Show source listing of routines whose names match "routine_regexp"
+
+  weblist [routine_regexp] [-ignore1] [-ignore2]
+     Displays a source listing of routines whose names match "routine_regexp"
+     in a web browser.  You can click on source lines to view the
+     corresponding disassembly.
+
+  top [--cum] [-ignore1] [-ignore2]
+  top20 [--cum] [-ignore1] [-ignore2]
+  top37 [--cum] [-ignore1] [-ignore2]
+      Show top lines ordered by flat profile count, or cumulative count
+      if --cum is specified.  If a number is present after 'top', the
+      top K routines will be shown (defaults to showing the top 10)
+
+  disasm [routine_regexp] [-ignore1] [-ignore2]
+      Show disassembly of routines whose names match "routine_regexp",
+      annotated with sample counts.
+
+  callgrind
+  callgrind [filename]
+      Generates callgrind file. If no filename is given, kcachegrind is called.
+
+  help - This listing
+  quit or ^D - End jeprof
+
+For commands that accept optional -ignore tags, samples where any routine in
+the stack trace matches the regular expression in any of the -ignore
+parameters will be ignored.
+
+Further pprof details are available at this location (or one similar):
+
+ /usr/doc/gperftools-$PPROF_VERSION/cpu_profiler.html
+ /usr/doc/gperftools-$PPROF_VERSION/heap_profiler.html
+
+ENDOFHELP
+}
+sub ParseInteractiveArgs {
+  my $args = shift;
+  my $focus = "";
+  my $ignore = "";
+  my @x = split(/ +/, $args);
+  foreach $a (@x) {
+    if ($a =~ m/^(--|-)lines$/) {
+      $main::opt_lines = 1;
+    } elsif ($a =~ m/^(--|-)cum$/) {
+      $main::opt_cum = 1;
+    } elsif ($a =~ m/^-(.*)/) {
+      $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
+    } else {
+      $focus .= (($focus ne "") ? "|" : "" ) . $a;
+    }
+  }
+  if ($ignore ne "") {
+    print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
+  }
+  return ($focus, $ignore);
+}
+
+##### Output code #####
+
+sub TempName {
+  my $fnum = shift;
+  my $ext = shift;
+  my $file = "$main::tmpfile_ps.$fnum.$ext";
+  $main::tempnames{$file} = 1;
+  return $file;
+}
+
+# Print profile data in packed binary format (64-bit) to standard out
+sub PrintProfileData {
+  my $profile = shift;
+
+  # print header (64-bit style)
+  # (zero) (header-size) (version) (sample-period) (zero)
+  print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
+
+  foreach my $k (keys(%{$profile})) {
+    my $count = $profile->{$k};
+    my @addrs = split(/\n/, $k);
+    if ($#addrs >= 0) {
+      my $depth = $#addrs + 1;
+      # int(foo / 2**32) is the only reliable way to get rid of bottom
+      # 32 bits on both 32- and 64-bit systems.
+      print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
+      print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
+
+      foreach my $full_addr (@addrs) {
+        my $addr = $full_addr;
+        $addr =~ s/0x0*//;  # strip off leading 0x, zeroes
+        if (length($addr) > 16) {
+          print STDERR "Invalid address in profile: $full_addr\n";
+          next;
+        }
+        my $low_addr = substr($addr, -8);       # get last 8 hex chars
+        my $high_addr = substr($addr, -16, 8);  # get up to 8 more hex chars
+        print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
+      }
+    }
+  }
+}
+
+# Print symbols and profile data
+sub PrintSymbolizedProfile {
+  my $symbols = shift;
+  my $profile = shift;
+  my $prog = shift;
+
+  $SYMBOL_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
+  my $symbol_marker = $&;
+
+  print '--- ', $symbol_marker, "\n";
+  if (defined($prog)) {
+    print 'binary=', $prog, "\n";
+  }
+  while (my ($pc, $name) = each(%{$symbols})) {
+    my $sep = ' ';
+    print '0x', $pc;
+    # We have a list of function names, which include the inlined
+    # calls.  They are separated (and terminated) by --, which is
+    # illegal in function names.
+    for (my $j = 2; $j <= $#{$name}; $j += 3) {
+      print $sep, $name->[$j];
+      $sep = '--';
+    }
+    print "\n";
+  }
+  print '---', "\n";
+
+  $PROFILE_PAGE =~ m,[^/]+$,;    # matches everything after the last slash
+  my $profile_marker = $&;
+  print '--- ', $profile_marker, "\n";
+  if (defined($main::collected_profile)) {
+    # if used with remote fetch, simply dump the collected profile to output.
+    open(SRC, "<$main::collected_profile");
+    while (<SRC>) {
+      print $_;
+    }
+    close(SRC);
+  } else {
+    # dump a cpu-format profile to standard out
+    PrintProfileData($profile);
+  }
+}
+
+# Print text output
+sub PrintText {
+  my $symbols = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $line_limit = shift;
+
+  my $total = TotalProfile($flat);
+
+  # Which profile to sort by?
+  my $s = $main::opt_cum ? $cumulative : $flat;
+
+  my $running_sum = 0;
+  my $lines = 0;
+  foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
+                 keys(%{$cumulative})) {
+    my $f = GetEntry($flat, $k);
+    my $c = GetEntry($cumulative, $k);
+    $running_sum += $f;
+
+    my $sym = $k;
+    if (exists($symbols->{$k})) {
+      $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
+      if ($main::opt_addresses) {
+        $sym = $k . " " . $sym;
+      }
+    }
+
+    if ($f != 0 || $c != 0) {
+      printf("%8s %6s %6s %8s %6s %s\n",
+             Unparse($f),
+             Percent($f, $total),
+             Percent($running_sum, $total),
+             Unparse($c),
+             Percent($c, $total),
+             $sym);
+    }
+    $lines++;
+    last if ($line_limit >= 0 && $lines >= $line_limit);
+  }
+}
+
+# Callgrind format has a compression for repeated function and file
+# names.  You show the name the first time, and just use its number
+# subsequently.  This can cut down the file to about a third or a
+# quarter of its uncompressed size.  $key and $val are the key/value
+# pair that would normally be printed by callgrind; $map is a map from
+# value to number.
+sub CompressedCGName {
+  my($key, $val, $map) = @_;
+  my $idx = $map->{$val};
+  # For very short keys, providing an index hurts rather than helps.
+  if (length($val) <= 3) {
+    return "$key=$val\n";
+  } elsif (defined($idx)) {
+    return "$key=($idx)\n";
+  } else {
+    # scalar(keys $map) gives the number of items in the map.
+    $idx = scalar(keys(%{$map})) + 1;
+    $map->{$val} = $idx;
+    return "$key=($idx) $val\n";
+  }
+}
+
+# Print the call graph in a way that's suiteable for callgrind.
+sub PrintCallgrind {
+  my $calls = shift;
+  my $filename;
+  my %filename_to_index_map;
+  my %fnname_to_index_map;
+
+  if ($main::opt_interactive) {
+    $filename = shift;
+    print STDERR "Writing callgrind file to '$filename'.\n"
+  } else {
+    $filename = "&STDOUT";
+  }
+  open(CG, ">$filename");
+  printf CG ("events: Hits\n\n");
+  foreach my $call ( map { $_->[0] }
+                     sort { $a->[1] cmp $b ->[1] ||
+                            $a->[2] <=> $b->[2] }
+                     map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
+                           [$_, $1, $2] }
+                     keys %$calls ) {
+    my $count = int($calls->{$call});
+    $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
+    my ( $caller_file, $caller_line, $caller_function,
+         $callee_file, $callee_line, $callee_function ) =
+       ( $1, $2, $3, $5, $6, $7 );
+
+    # TODO(csilvers): for better compression, collect all the
+    # caller/callee_files and functions first, before printing
+    # anything, and only compress those referenced more than once.
+    printf CG CompressedCGName("fl", $caller_file, \%filename_to_index_map);
+    printf CG CompressedCGName("fn", $caller_function, \%fnname_to_index_map);
+    if (defined $6) {
+      printf CG CompressedCGName("cfl", $callee_file, \%filename_to_index_map);
+      printf CG CompressedCGName("cfn", $callee_function, \%fnname_to_index_map);
+      printf CG ("calls=$count $callee_line\n");
+    }
+    printf CG ("$caller_line $count\n\n");
+  }
+}
+
+# Print disassembly for all all routines that match $main::opt_disasm
+sub PrintDisassembly {
+  my $libs = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $disasm_opts = shift;
+
+  my $total = TotalProfile($flat);
+
+  foreach my $lib (@{$libs}) {
+    my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
+    my $offset = AddressSub($lib->[1], $lib->[3]);
+    foreach my $routine (sort ByName keys(%{$symbol_table})) {
+      my $start_addr = $symbol_table->{$routine}->[0];
+      my $end_addr = $symbol_table->{$routine}->[1];
+      # See if there are any samples in this routine
+      my $length = hex(AddressSub($end_addr, $start_addr));
+      my $addr = AddressAdd($start_addr, $offset);
+      for (my $i = 0; $i < $length; $i++) {
+        if (defined($cumulative->{$addr})) {
+          PrintDisassembledFunction($lib->[0], $offset,
+                                    $routine, $flat, $cumulative,
+                                    $start_addr, $end_addr, $total);
+          last;
+        }
+        $addr = AddressInc($addr);
+      }
+    }
+  }
+}
+
+# Return reference to array of tuples of the form:
+#       [start_address, filename, linenumber, instruction, limit_address]
+# E.g.,
+#       ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
+sub Disassemble {
+  my $prog = shift;
+  my $offset = shift;
+  my $start_addr = shift;
+  my $end_addr = shift;
+
+  my $objdump = $obj_tool_map{"objdump"};
+  my $cmd = ShellEscape($objdump, "-C", "-d", "-l", "--no-show-raw-insn",
+                        "--start-address=0x$start_addr",
+                        "--stop-address=0x$end_addr", $prog);
+  open(OBJDUMP, "$cmd |") || error("$cmd: $!\n");
+  my @result = ();
+  my $filename = "";
+  my $linenumber = -1;
+  my $last = ["", "", "", ""];
+  while (<OBJDUMP>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    chop;
+    if (m|\s*([^:\s]+):(\d+)\s*$|) {
+      # Location line of the form:
+      #   <filename>:<linenumber>
+      $filename = $1;
+      $linenumber = $2;
+    } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
+      # Disassembly line -- zero-extend address to full length
+      my $addr = HexExtend($1);
+      my $k = AddressAdd($addr, $offset);
+      $last->[4] = $k;   # Store ending address for previous instruction
+      $last = [$k, $filename, $linenumber, $2, $end_addr];
+      push(@result, $last);
+    }
+  }
+  close(OBJDUMP);
+  return @result;
+}
+
+# The input file should contain lines of the form /proc/maps-like
+# output (same format as expected from the profiles) or that looks
+# like hex addresses (like "0xDEADBEEF").  We will parse all
+# /proc/maps output, and for all the hex addresses, we will output
+# "short" symbol names, one per line, in the same order as the input.
+sub PrintSymbols {
+  my $maps_and_symbols_file = shift;
+
+  # ParseLibraries expects pcs to be in a set.  Fine by us...
+  my @pclist = ();   # pcs in sorted order
+  my $pcs = {};
+  my $map = "";
+  foreach my $line (<$maps_and_symbols_file>) {
+    $line =~ s/\r//g;    # turn windows-looking lines into unix-looking lines
+    if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
+      push(@pclist, HexExtend($1));
+      $pcs->{$pclist[-1]} = 1;
+    } else {
+      $map .= $line;
+    }
+  }
+
+  my $libs = ParseLibraries($main::prog, $map, $pcs);
+  my $symbols = ExtractSymbols($libs, $pcs);
+
+  foreach my $pc (@pclist) {
+    # ->[0] is the shortname, ->[2] is the full name
+    print(($symbols->{$pc}->[0] || "??") . "\n");
+  }
+}
+
+
+# For sorting functions by name
+sub ByName {
+  return ShortFunctionName($a) cmp ShortFunctionName($b);
+}
+
+# Print source-listing for all all routines that match $list_opts
+sub PrintListing {
+  my $total = shift;
+  my $libs = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $list_opts = shift;
+  my $html = shift;
+
+  my $output = \*STDOUT;
+  my $fname = "";
+
+  if ($html) {
+    # Arrange to write the output to a temporary file
+    $fname = TempName($main::next_tmpfile, "html");
+    $main::next_tmpfile++;
+    if (!open(TEMP, ">$fname")) {
+      print STDERR "$fname: $!\n";
+      return;
+    }
+    $output = \*TEMP;
+    print $output HtmlListingHeader();
+    printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
+                    $main::prog, Unparse($total), Units());
+  }
+
+  my $listed = 0;
+  foreach my $lib (@{$libs}) {
+    my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
+    my $offset = AddressSub($lib->[1], $lib->[3]);
+    foreach my $routine (sort ByName keys(%{$symbol_table})) {
+      # Print if there are any samples in this routine
+      my $start_addr = $symbol_table->{$routine}->[0];
+      my $end_addr = $symbol_table->{$routine}->[1];
+      my $length = hex(AddressSub($end_addr, $start_addr));
+      my $addr = AddressAdd($start_addr, $offset);
+      for (my $i = 0; $i < $length; $i++) {
+        if (defined($cumulative->{$addr})) {
+          $listed += PrintSource(
+            $lib->[0], $offset,
+            $routine, $flat, $cumulative,
+            $start_addr, $end_addr,
+            $html,
+            $output);
+          last;
+        }
+        $addr = AddressInc($addr);
+      }
+    }
+  }
+
+  if ($html) {
+    if ($listed > 0) {
+      print $output HtmlListingFooter();
+      close($output);
+      RunWeb($fname);
+    } else {
+      close($output);
+      unlink($fname);
+    }
+  }
+}
+
+sub HtmlListingHeader {
+  return <<'EOF';
+<DOCTYPE html>
+<html>
+<head>
+<title>Pprof listing</title>
+<style type="text/css">
+body {
+  font-family: sans-serif;
+}
+h1 {
+  font-size: 1.5em;
+  margin-bottom: 4px;
+}
+.legend {
+  font-size: 1.25em;
+}
+.line {
+  color: #aaaaaa;
+}
+.nop {
+  color: #aaaaaa;
+}
+.unimportant {
+  color: #cccccc;
+}
+.disasmloc {
+  color: #000000;
+}
+.deadsrc {
+  cursor: pointer;
+}
+.deadsrc:hover {
+  background-color: #eeeeee;
+}
+.livesrc {
+  color: #0000ff;
+  cursor: pointer;
+}
+.livesrc:hover {
+  background-color: #eeeeee;
+}
+.asm {
+  color: #008800;
+  display: none;
+}
+</style>
+<script type="text/javascript">
+function jeprof_toggle_asm(e) {
+  var target;
+  if (!e) e = window.event;
+  if (e.target) target = e.target;
+  else if (e.srcElement) target = e.srcElement;
+
+  if (target) {
+    var asm = target.nextSibling;
+    if (asm && asm.className == "asm") {
+      asm.style.display = (asm.style.display == "block" ? "" : "block");
+      e.preventDefault();
+      return false;
+    }
+  }
+}
+</script>
+</head>
+<body>
+EOF
+}
+
+sub HtmlListingFooter {
+  return <<'EOF';
+</body>
+</html>
+EOF
+}
+
+sub HtmlEscape {
+  my $text = shift;
+  $text =~ s/&/&amp;/g;
+  $text =~ s/</&lt;/g;
+  $text =~ s/>/&gt;/g;
+  return $text;
+}
+
+# Returns the indentation of the line, if it has any non-whitespace
+# characters.  Otherwise, returns -1.
+sub Indentation {
+  my $line = shift;
+  if (m/^(\s*)\S/) {
+    return length($1);
+  } else {
+    return -1;
+  }
+}
+
+# If the symbol table contains inlining info, Disassemble() may tag an
+# instruction with a location inside an inlined function.  But for
+# source listings, we prefer to use the location in the function we
+# are listing.  So use MapToSymbols() to fetch full location
+# information for each instruction and then pick out the first
+# location from a location list (location list contains callers before
+# callees in case of inlining).
+#
+# After this routine has run, each entry in $instructions contains:
+#   [0] start address
+#   [1] filename for function we are listing
+#   [2] line number for function we are listing
+#   [3] disassembly
+#   [4] limit address
+#   [5] most specific filename (may be different from [1] due to inlining)
+#   [6] most specific line number (may be different from [2] due to inlining)
+sub GetTopLevelLineNumbers {
+  my ($lib, $offset, $instructions) = @_;
+  my $pcs = [];
+  for (my $i = 0; $i <= $#{$instructions}; $i++) {
+    push(@{$pcs}, $instructions->[$i]->[0]);
+  }
+  my $symbols = {};
+  MapToSymbols($lib, $offset, $pcs, $symbols);
+  for (my $i = 0; $i <= $#{$instructions}; $i++) {
+    my $e = $instructions->[$i];
+    push(@{$e}, $e->[1]);
+    push(@{$e}, $e->[2]);
+    my $addr = $e->[0];
+    my $sym = $symbols->{$addr};
+    if (defined($sym)) {
+      if ($#{$sym} >= 2 && $sym->[1] =~ m/^(.*):(\d+)$/) {
+        $e->[1] = $1;  # File name
+        $e->[2] = $2;  # Line number
+      }
+    }
+  }
+}
+
+# Print source-listing for one routine
+sub PrintSource {
+  my $prog = shift;
+  my $offset = shift;
+  my $routine = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $start_addr = shift;
+  my $end_addr = shift;
+  my $html = shift;
+  my $output = shift;
+
+  # Disassemble all instructions (just to get line numbers)
+  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
+  GetTopLevelLineNumbers($prog, $offset, \@instructions);
+
+  # Hack 1: assume that the first source file encountered in the
+  # disassembly contains the routine
+  my $filename = undef;
+  for (my $i = 0; $i <= $#instructions; $i++) {
+    if ($instructions[$i]->[2] >= 0) {
+      $filename = $instructions[$i]->[1];
+      last;
+    }
+  }
+  if (!defined($filename)) {
+    print STDERR "no filename found in $routine\n";
+    return 0;
+  }
+
+  # Hack 2: assume that the largest line number from $filename is the
+  # end of the procedure.  This is typically safe since if P1 contains
+  # an inlined call to P2, then P2 usually occurs earlier in the
+  # source file.  If this does not work, we might have to compute a
+  # density profile or just print all regions we find.
+  my $lastline = 0;
+  for (my $i = 0; $i <= $#instructions; $i++) {
+    my $f = $instructions[$i]->[1];
+    my $l = $instructions[$i]->[2];
+    if (($f eq $filename) && ($l > $lastline)) {
+      $lastline = $l;
+    }
+  }
+
+  # Hack 3: assume the first source location from "filename" is the start of
+  # the source code.
+  my $firstline = 1;
+  for (my $i = 0; $i <= $#instructions; $i++) {
+    if ($instructions[$i]->[1] eq $filename) {
+      $firstline = $instructions[$i]->[2];
+      last;
+    }
+  }
+
+  # Hack 4: Extend last line forward until its indentation is less than
+  # the indentation we saw on $firstline
+  my $oldlastline = $lastline;
+  {
+    if (!open(FILE, "<$filename")) {
+      print STDERR "$filename: $!\n";
+      return 0;
+    }
+    my $l = 0;
+    my $first_indentation = -1;
+    while (<FILE>) {
+      s/\r//g;         # turn windows-looking lines into unix-looking lines
+      $l++;
+      my $indent = Indentation($_);
+      if ($l >= $firstline) {
+        if ($first_indentation < 0 && $indent >= 0) {
+          $first_indentation = $indent;
+          last if ($first_indentation == 0);
+        }
+      }
+      if ($l >= $lastline && $indent >= 0) {
+        if ($indent >= $first_indentation) {
+          $lastline = $l+1;
+        } else {
+          last;
+        }
+      }
+    }
+    close(FILE);
+  }
+
+  # Assign all samples to the range $firstline,$lastline,
+  # Hack 4: If an instruction does not occur in the range, its samples
+  # are moved to the next instruction that occurs in the range.
+  my $samples1 = {};        # Map from line number to flat count
+  my $samples2 = {};        # Map from line number to cumulative count
+  my $running1 = 0;         # Unassigned flat counts
+  my $running2 = 0;         # Unassigned cumulative counts
+  my $total1 = 0;           # Total flat counts
+  my $total2 = 0;           # Total cumulative counts
+  my %disasm = ();          # Map from line number to disassembly
+  my $running_disasm = "";  # Unassigned disassembly
+  my $skip_marker = "---\n";
+  if ($html) {
+    $skip_marker = "";
+    for (my $l = $firstline; $l <= $lastline; $l++) {
+      $disasm{$l} = "";
+    }
+  }
+  my $last_dis_filename = '';
+  my $last_dis_linenum = -1;
+  my $last_touched_line = -1;  # To detect gaps in disassembly for a line
+  foreach my $e (@instructions) {
+    # Add up counts for all address that fall inside this instruction
+    my $c1 = 0;
+    my $c2 = 0;
+    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
+      $c1 += GetEntry($flat, $a);
+      $c2 += GetEntry($cumulative, $a);
+    }
+
+    if ($html) {
+      my $dis = sprintf("      %6s %6s \t\t%8s: %s ",
+                        HtmlPrintNumber($c1),
+                        HtmlPrintNumber($c2),
+                        UnparseAddress($offset, $e->[0]),
+                        CleanDisassembly($e->[3]));
+
+      # Append the most specific source line associated with this instruction
+      if (length($dis) < 80) { $dis .= (' ' x (80 - length($dis))) };
+      $dis = HtmlEscape($dis);
+      my $f = $e->[5];
+      my $l = $e->[6];
+      if ($f ne $last_dis_filename) {
+        $dis .= sprintf("<span class=disasmloc>%s:%d</span>",
+                        HtmlEscape(CleanFileName($f)), $l);
+      } elsif ($l ne $last_dis_linenum) {
+        # De-emphasize the unchanged file name portion
+        $dis .= sprintf("<span class=unimportant>%s</span>" .
+                        "<span class=disasmloc>:%d</span>",
+                        HtmlEscape(CleanFileName($f)), $l);
+      } else {
+        # De-emphasize the entire location
+        $dis .= sprintf("<span class=unimportant>%s:%d</span>",
+                        HtmlEscape(CleanFileName($f)), $l);
+      }
+      $last_dis_filename = $f;
+      $last_dis_linenum = $l;
+      $running_disasm .= $dis;
+      $running_disasm .= "\n";
+    }
+
+    $running1 += $c1;
+    $running2 += $c2;
+    $total1 += $c1;
+    $total2 += $c2;
+    my $file = $e->[1];
+    my $line = $e->[2];
+    if (($file eq $filename) &&
+        ($line >= $firstline) &&
+        ($line <= $lastline)) {
+      # Assign all accumulated samples to this line
+      AddEntry($samples1, $line, $running1);
+      AddEntry($samples2, $line, $running2);
+      $running1 = 0;
+      $running2 = 0;
+      if ($html) {
+        if ($line != $last_touched_line && $disasm{$line} ne '') {
+          $disasm{$line} .= "\n";
+        }
+        $disasm{$line} .= $running_disasm;
+        $running_disasm = '';
+        $last_touched_line = $line;
+      }
+    }
+  }
+
+  # Assign any leftover samples to $lastline
+  AddEntry($samples1, $lastline, $running1);
+  AddEntry($samples2, $lastline, $running2);
+  if ($html) {
+    if ($lastline != $last_touched_line && $disasm{$lastline} ne '') {
+      $disasm{$lastline} .= "\n";
+    }
+    $disasm{$lastline} .= $running_disasm;
+  }
+
+  if ($html) {
+    printf $output (
+      "<h1>%s</h1>%s\n<pre onClick=\"jeprof_toggle_asm()\">\n" .
+      "Total:%6s %6s (flat / cumulative %s)\n",
+      HtmlEscape(ShortFunctionName($routine)),
+      HtmlEscape(CleanFileName($filename)),
+      Unparse($total1),
+      Unparse($total2),
+      Units());
+  } else {
+    printf $output (
+      "ROUTINE ====================== %s in %s\n" .
+      "%6s %6s Total %s (flat / cumulative)\n",
+      ShortFunctionName($routine),
+      CleanFileName($filename),
+      Unparse($total1),
+      Unparse($total2),
+      Units());
+  }
+  if (!open(FILE, "<$filename")) {
+    print STDERR "$filename: $!\n";
+    return 0;
+  }
+  my $l = 0;
+  while (<FILE>) {
+    s/\r//g;         # turn windows-looking lines into unix-looking lines
+    $l++;
+    if ($l >= $firstline - 5 &&
+        (($l <= $oldlastline + 5) || ($l <= $lastline))) {
+      chop;
+      my $text = $_;
+      if ($l == $firstline) { print $output $skip_marker; }
+      my $n1 = GetEntry($samples1, $l);
+      my $n2 = GetEntry($samples2, $l);
+      if ($html) {
+        # Emit a span that has one of the following classes:
+        #    livesrc -- has samples
+        #    deadsrc -- has disassembly, but with no samples
+        #    nop     -- has no matching disasembly
+        # Also emit an optional span containing disassembly.
+        my $dis = $disasm{$l};
+        my $asm = "";
+        if (defined($dis) && $dis ne '') {
+          $asm = "<span class=\"asm\">" . $dis . "</span>";
+        }
+        my $source_class = (($n1 + $n2 > 0)
+                            ? "livesrc"
+                            : (($asm ne "") ? "deadsrc" : "nop"));
+        printf $output (
+          "<span class=\"line\">%5d</span> " .
+          "<span class=\"%s\">%6s %6s %s</span>%s\n",
+          $l, $source_class,
+          HtmlPrintNumber($n1),
+          HtmlPrintNumber($n2),
+          HtmlEscape($text),
+          $asm);
+      } else {
+        printf $output(
+          "%6s %6s %4d: %s\n",
+          UnparseAlt($n1),
+          UnparseAlt($n2),
+          $l,
+          $text);
+      }
+      if ($l == $lastline)  { print $output $skip_marker; }
+    };
+  }
+  close(FILE);
+  if ($html) {
+    print $output "</pre>\n";
+  }
+  return 1;
+}
+
+# Return the source line for the specified file/linenumber.
+# Returns undef if not found.
+sub SourceLine {
+  my $file = shift;
+  my $line = shift;
+
+  # Look in cache
+  if (!defined($main::source_cache{$file})) {
+    if (100 < scalar keys(%main::source_cache)) {
+      # Clear the cache when it gets too big
+      $main::source_cache = ();
+    }
+
+    # Read all lines from the file
+    if (!open(FILE, "<$file")) {
+      print STDERR "$file: $!\n";
+      $main::source_cache{$file} = [];  # Cache the negative result
+      return undef;
+    }
+    my $lines = [];
+    push(@{$lines}, "");        # So we can use 1-based line numbers as indices
+    while (<FILE>) {
+      push(@{$lines}, $_);
+    }
+    close(FILE);
+
+    # Save the lines in the cache
+    $main::source_cache{$file} = $lines;
+  }
+
+  my $lines = $main::source_cache{$file};
+  if (($line < 0) || ($line > $#{$lines})) {
+    return undef;
+  } else {
+    return $lines->[$line];
+  }
+}
+
+# Print disassembly for one routine with interspersed source if available
+sub PrintDisassembledFunction {
+  my $prog = shift;
+  my $offset = shift;
+  my $routine = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $start_addr = shift;
+  my $end_addr = shift;
+  my $total = shift;
+
+  # Disassemble all instructions
+  my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
+
+  # Make array of counts per instruction
+  my @flat_count = ();
+  my @cum_count = ();
+  my $flat_total = 0;
+  my $cum_total = 0;
+  foreach my $e (@instructions) {
+    # Add up counts for all address that fall inside this instruction
+    my $c1 = 0;
+    my $c2 = 0;
+    for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
+      $c1 += GetEntry($flat, $a);
+      $c2 += GetEntry($cumulative, $a);
+    }
+    push(@flat_count, $c1);
+    push(@cum_count, $c2);
+    $flat_total += $c1;
+    $cum_total += $c2;
+  }
+
+  # Print header with total counts
+  printf("ROUTINE ====================== %s\n" .
+         "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
+         ShortFunctionName($routine),
+         Unparse($flat_total),
+         Unparse($cum_total),
+         Units(),
+         ($cum_total * 100.0) / $total);
+
+  # Process instructions in order
+  my $current_file = "";
+  for (my $i = 0; $i <= $#instructions; ) {
+    my $e = $instructions[$i];
+
+    # Print the new file name whenever we switch files
+    if ($e->[1] ne $current_file) {
+      $current_file = $e->[1];
+      my $fname = $current_file;
+      $fname =~ s|^\./||;   # Trim leading "./"
+
+      # Shorten long file names
+      if (length($fname) >= 58) {
+        $fname = "..." . substr($fname, -55);
+      }
+      printf("-------------------- %s\n", $fname);
+    }
+
+    # TODO: Compute range of lines to print together to deal with
+    # small reorderings.
+    my $first_line = $e->[2];
+    my $last_line = $first_line;
+    my %flat_sum = ();
+    my %cum_sum = ();
+    for (my $l = $first_line; $l <= $last_line; $l++) {
+      $flat_sum{$l} = 0;
+      $cum_sum{$l} = 0;
+    }
+
+    # Find run of instructions for this range of source lines
+    my $first_inst = $i;
+    while (($i <= $#instructions) &&
+           ($instructions[$i]->[2] >= $first_line) &&
+           ($instructions[$i]->[2] <= $last_line)) {
+      $e = $instructions[$i];
+      $flat_sum{$e->[2]} += $flat_count[$i];
+      $cum_sum{$e->[2]} += $cum_count[$i];
+      $i++;
+    }
+    my $last_inst = $i - 1;
+
+    # Print source lines
+    for (my $l = $first_line; $l <= $last_line; $l++) {
+      my $line = SourceLine($current_file, $l);
+      if (!defined($line)) {
+        $line = "?\n";
+        next;
+      } else {
+        $line =~ s/^\s+//;
+      }
+      printf("%6s %6s %5d: %s",
+             UnparseAlt($flat_sum{$l}),
+             UnparseAlt($cum_sum{$l}),
+             $l,
+             $line);
+    }
+
+    # Print disassembly
+    for (my $x = $first_inst; $x <= $last_inst; $x++) {
+      my $e = $instructions[$x];
+      printf("%6s %6s    %8s: %6s\n",
+             UnparseAlt($flat_count[$x]),
+             UnparseAlt($cum_count[$x]),
+             UnparseAddress($offset, $e->[0]),
+             CleanDisassembly($e->[3]));
+    }
+  }
+}
+
+# Print DOT graph
+sub PrintDot {
+  my $prog = shift;
+  my $symbols = shift;
+  my $raw = shift;
+  my $flat = shift;
+  my $cumulative = shift;
+  my $overall_total = shift;
+
+  # Get total
+  my $local_total = TotalProfile($flat);
+  my $nodelimit = int($main::opt_nodefraction * $local_total);
+  my $edgelimit = int($main::opt_edgefraction * $local_total);
+  my $nodecount = $main::opt_nodecount;
+
+  # Find nodes to include
+  my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
+                     abs(GetEntry($cumulative, $a))
+                     || $a cmp $b }
+              keys(%{$cumulative}));
+  my $last = $nodecount - 1;
+  if ($last > $#list) {
+    $last = $#list;
+  }
+  while (($last >= 0) &&
+         (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
+    $last--;
+  }
+  if ($last < 0) {
+    print STDERR "No nodes to print\n";
+    return 0;
+  }
+
+  if ($nodelimit > 0 || $edgelimit > 0) {
+    printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
+                   Unparse($nodelimit), Units(),
+                   Unparse($edgelimit), Units());
+  }
+
+  # Open DOT output file
+  my $output;
+  my $escaped_dot = ShellEscape(@DOT);
+  my $escaped_ps2pdf = ShellEscape(@PS2PDF);
+  if ($main::opt_gv) {
+    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "ps"));
+    $output = "| $escaped_dot -Tps2 >$escaped_outfile";
+  } elsif ($main::opt_evince) {
+    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "pdf"));
+    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - $escaped_outfile";
+  } elsif ($main::opt_ps) {
+    $output = "| $escaped_dot -Tps2";
+  } elsif ($main::opt_pdf) {
+    $output = "| $escaped_dot -Tps2 | $escaped_ps2pdf - -";
+  } elsif ($main::opt_web || $main::opt_svg) {
+    # We need to post-process the SVG, so write to a temporary file always.
+    my $escaped_outfile = ShellEscape(TempName($main::next_tmpfile, "svg"));
+    $output = "| $escaped_dot -Tsvg >$escaped_outfile";
+  } elsif ($main::opt_gif) {
+    $output = "| $escaped_dot -Tgif";
+  } else {
+    $output = ">&STDOUT";
+  }
+  open(DOT, $output) || error("$output: $!\n");
+
+  # Title
+  printf DOT ("digraph \"%s; %s %s\" {\n",
+              $prog,
+              Unparse($overall_total),
+              Units());
+  if ($main::opt_pdf) {
+    # The output is more printable if we set the page size for dot.
+    printf DOT ("size=\"8,11\"\n");
+  }
+  printf DOT ("node [width=0.375,height=0.25];\n");
+
+  # Print legend
+  printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
+              "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
+              $prog,
+              sprintf("Total %s: %s", Units(), Unparse($overall_total)),
+              sprintf("Focusing on: %s", Unparse($local_total)),
+              sprintf("Dropped nodes with <= %s abs(%s)",
+                      Unparse($nodelimit), Units()),
+              sprintf("Dropped edges with <= %s %s",
+                      Unparse($edgelimit), Units())
+              );
+
+  # Print nodes
+  my %node = ();
+  my $nextnode = 1;
+  foreach my $a (@list[0..$last]) {
+    # Pick font size
+    my $f = GetEntry($flat, $a);
+    my $c = GetEntry($cumulative, $a);
+
+    my $fs = 8;
+    if ($local_total > 0) {
+      $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
+    }
+
+    $node{$a} = $nextnode++;
+    my $sym = $a;
+    $sym =~ s/\s+/\\n/g;
+    $sym =~ s/::/\\n/g;
+
+    # Extra cumulative info to print for non-leaves
+    my $extra = "";
+    if ($f != $c) {
+      $extra = sprintf("\\rof %s (%s)",
+                       Unparse($c),
+                       Percent($c, $local_total));
+    }
+    my $style = "";
+    if ($main::opt_heapcheck) {
+      if ($f > 0) {
+        # make leak-causing nodes more visible (add a background)
+        $style = ",style=filled,fillcolor=gray"
+      } elsif ($f < 0) {
+        # make anti-leak-causing nodes (which almost never occur)
+        # stand out as well (triple border)
+        $style = ",peripheries=3"
+      }
+    }
+
+    printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
+                "\",shape=box,fontsize=%.1f%s];\n",
+                $node{$a},
+                $sym,
+                Unparse($f),
+                Percent($f, $local_total),
+                $extra,
+                $fs,
+                $style,
+               );
+  }
+
+  # Get edges and counts per edge
+  my %edge = ();
+  my $n;
+  my $fullname_to_shortname_map = {};
+  FillFullnameToShortnameMap($symbols, $fullname_to_shortname_map);
+  foreach my $k (keys(%{$raw})) {
+    # TODO: omit low %age edges
+    $n = $raw->{$k};
+    my @translated = TranslateStack($symbols, $fullname_to_shortname_map, $k);
+    for (my $i = 1; $i <= $#translated; $i++) {
+      my $src = $translated[$i];
+      my $dst = $translated[$i-1];
+      #next if ($src eq $dst);  # Avoid self-edges?
+      if (exists($node{$src}) && exists($node{$dst})) {
+        my $edge_label = "$src\001$dst";
+        if (!exists($edge{$edge_label})) {
+          $edge{$edge_label} = 0;
+        }
+        $edge{$edge_label} += $n;
+      }
+    }
+  }
+
+  # Print edges (process in order of decreasing counts)
+  my %indegree = ();   # Number of incoming edges added per node so far
+  my %outdegree = ();  # Number of outgoing edges added per node so far
+  foreach my $e (sort { $edge{$b} <=> $edge{$a} } keys(%edge)) {
+    my @x = split(/\001/, $e);
+    $n = $edge{$e};
+
+    # Initialize degree of kept incoming and outgoing edges if necessary
+    my $src = $x[0];
+    my $dst = $x[1];
+    if (!exists($outdegree{$src})) { $outdegree{$src} = 0; }
+    if (!exists($indegree{$dst})) { $indegree{$dst} = 0; }
+
+    my $keep;
+    if ($indegree{$dst} == 0) {
+      # Keep edge if needed for reachability
+      $keep = 1;
+    } elsif (abs($n) <= $edgelimit) {
+      # Drop if we are below --edgefraction
+      $keep = 0;
+    } elsif ($outdegree{$src} >= $main::opt_maxdegree ||
+             $indegree{$dst} >= $main::opt_maxdegree) {
+      # Keep limited number of in/out edges per node
+      $keep = 0;
+    } else {
+      $keep = 1;
+    }
+
+    if ($keep) {
+      $outdegree{$src}++;
+      $indegree{$dst}++;
+
+      # Compute line width based on edge count
+      my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
+      if ($fraction > 1) { $fraction = 1; }
+      my $w = $fraction * 2;
+      if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
+        # SVG output treats line widths < 1 poorly.
+        $w = 1;
+      }
+
+      # Dot sometimes segfaults if given edge weights that are too large, so
+      # we cap the weights at a large value
+      my $edgeweight = abs($n) ** 0.7;
+      if ($edgeweight > 100000) { $edgeweight = 100000; }
+      $edgeweight = int($edgeweight);
+
+      my $style = sprintf("setlinewidth(%f)", $w);
+      if ($x[1] =~ m/\(inline\)/) {
+        $style .= ",dashed";
+      }
+
+      # Use a slightly squashed function of the edge count as the weight
+      printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
+                  $node{$x[0]},
+                  $node{$x[1]},
+                  Unparse($n),
+                  $edgeweight,
+                  $style);
+    }
+  }
+
+  print DOT ("}\n");
+  close(DOT);
+
+  if ($main::opt_web || $main::opt_svg) {
+    # Rewrite SVG to be more usable inside web browser.
+    RewriteSvg(TempName($main::next_tmpfile, "svg"));
+  }
+
+  return 1;
+}
+
+sub RewriteSvg {
+  my $svgfile = shift;
+
+  open(SVG, $svgfile) || die "open temp svg: $!";
+  my @svg = <SVG>;
+  close(SVG);
+  unlink $svgfile;
+  my $svg = join('', @svg);
+
+  # Dot's SVG output is
+  #
+  #    <svg width="___" height="___"
+  #     viewBox="___" xmlns=...>
+  #    <g id="graph0" transform="...">
+  #    ...
+  #    </g>
+  #    </svg>
+  #
+  # Change it to
+  #
+  #    <svg width="100%" height="100%"
+  #     xmlns=...>
+  #    $svg_javascript
+  #    <g id="viewport" transform="translate(0,0)">
+  #    <g id="graph0" transform="...">
+  #    ...
+  #    </g>
+  #    </g>
+  #    </svg>
+
+  # Fix width, height; drop viewBox.
+  $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
+
+  # Insert script, viewport <g> above first <g>
+  my $svg_javascript = SvgJavascript();
+  my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
+  $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
+
+  # Insert final </g> above </svg>.
+  $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
+  $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
+
+  if ($main::opt_svg) {
+    # --svg: write to standard output.
+    print $svg;
+  } else {
+    # Write back to temporary file.
+    open(SVG, ">$svgfile") || die "open $svgfile: $!";
+    print SVG $svg;
+    close(SVG);
+  }
+}
+
+sub SvgJavascript {
+  return <<'EOF';
+<script type="text/ecmascript"><![CDATA[
+// SVGPan
+// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
+// Local modification: if(true || ...) below to force panning, never moving.
+
+/**
+ *  SVGPan library 1.2
+ * ====================
+ *
+ * Given an unique existing element with id "viewport", including the
+ * the library into any SVG adds the following capabilities:
+ *
+ *  - Mouse panning
+ *  - Mouse zooming (using the wheel)
+ *  - Object dargging
+ *
+ * Known issues:
+ *
+ *  - Zooming (while panning) on Safari has still some issues
+ *
+ * Releases:
+ *
+ * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
+ *	Fixed a bug with browser mouse handler interaction
+ *
+ * 1.1, Wed Feb  3 17:39:33 GMT 2010, Zeng Xiaohui
+ *	Updated the zoom code to support the mouse wheel on Safari/Chrome
+ *
+ * 1.0, Andrea Leofreddi
+ *	First release
+ *
+ * This code is licensed under the following BSD license:
+ *
+ * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ *    1. Redistributions of source code must retain the above copyright notice, this list of
+ *       conditions and the following disclaimer.
+ *
+ *    2. Redistributions in binary form must reproduce the above copyright notice, this list
+ *       of conditions and the following disclaimer in the documentation and/or other materials
+ *       provided with the distribution.