merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 10 Mar 2016 11:51:35 +0100
changeset 339110 dd1abe874252e507b825a0a4e1063b0e13578288
parent 339109 aaacd6e2d9f16ec8fc1b53aab81f758e67dfe458 (current diff)
parent 338957 b0e1cefc94ce86adc374eb2d05791b00e0753b20 (diff)
child 339111 3af1d3b4876df020925173cac7684492cb1ab519
child 339114 492074389f3c5bb3a1342d9766dccd201afa4336
child 339119 0d38e391b3a15ff78e4922071a067640990a44fc
child 339136 7a0f7bd1873cc08767946c8032bcb476d603a8ae
child 339153 efa6f740eb67be025851230adf6bd571d772e7b5
child 339164 915cba39e909094e433c912ab0ac3d70ce8ab01c
child 339166 7121bc5eed24d4a0356a3094b8d8f4f7b16f4e45
child 339172 14cf2728186d1d4bc251bbb541b860398ca6898d
child 339175 8d6f683b6ee193537c76aede509bcf5b50a39b67
child 339176 851cb3a17fe6d0d8fe745888d2ec14b50d5d365b
child 339193 f21b28b7c03d18449a6ef1c9f188c7cd73f798a7
child 339209 31cf9a38cd068f9face9b6e2d241f27ed8723fba
child 339226 11e8b6e5e895c3c140180725c889d10ba9552102
child 339240 853a1b4adb5eea8a99dd9d1610f04f999963eaae
child 339241 f77e102a5e1f4b549e4aeb32ef0f3034e407eedd
child 339242 f09648da6e34fea63e5066b7af3395d6f96fd92f
child 339245 a1330c3fc9b101ecec2bde74ed6b94ccafed8a10
child 339246 9410f7772e9a81e4c586868697d4a1ced6957654
child 339248 83f6fd2877ad88473123f2ecaccaabe02c7b69df
child 339249 fec570608696d5a3b78454098d552829c41136de
child 339314 6b9c0852c67950214e165bdb6a42ea631e8bad0a
child 339315 c4bbc34a753ec947665f6d05f0104dd76a876c4f
child 339316 0ed2e133b8bbba1ceb93b139124a4653364c4af1
child 339317 ed54814020e3242078d3b95801e83b72c45e09f9
child 339328 c1aa80db925721c9ca9f4a3978f11a4a26ce628e
child 339360 c27444067cf26d8c31b1ae95703cd12df91c69be
child 339374 331573efe549d925234a03d8ab077864b12ea3a3
child 339393 1d1d1cc11679076d32e28a0d99e9dc273d667ff9
child 339394 05305f49614b6ceffaf3b5f2c66b9db060b7d71a
child 339395 2275c06e1b481ac5c282a1a5bf6e20af4a28fce1
child 339396 6723a4a8aeaa7bbfe1c1df0f07226e760e8ddf7d
child 339397 fb572bb32f85fca4cbdd93697df3ee572c152012
child 339398 d7679c9dbb170a95a080c478f38c052f4ac77c6f
child 339401 dfd9a997b9b130f71bebc49052da039a33ab0b1b
child 339446 77551f58f8c54db75680f2c4cbbb299d76284ac5
child 339455 767e0126510e0f65798a53a29c6b7e1469a03139
child 339459 16a2fc861a9012978bd85e3f63c4ae479068daac
child 339525 2592be714347d315b41cd622efc0e19945f6a2b2
child 339532 a77d8ab6b8bf06e8903d322158a3d818968b6769
child 339535 3918b82e1a75d76ba6d21d178f66081cb7f673d2
child 339619 02012e498a7992b0eaf83d9fcb7ccc86355d89d3
child 339651 1dab54e2f0e291364005b110ceac963521c27c17
child 339890 a5ac6f86d2a11caaae47292a9c37a96903ab0570
child 339957 346c75412bc84e092e78e928fcfd5c560e893ad8
child 340012 549e54d45f7e59e8d111adee536a42a7d19ec1cd
child 340530 ce7a7e74c5c91ff8bd957fb81689d1c859ce43d8
child 340541 47ac0d0537e06f5474ccc8cc08c9e1ee7612e672
child 340634 fab8fd2c54616e3202c31fd544f145e9f2ad9ef0
child 340635 0c4ff55103ea212d4e170b4d29694868b681d457
child 340692 043390eeb89e8f0c23d5c135c5d850f42c9a1701
child 341163 dbbd36b12191b3b55ad052d762ab8b80377ec8d1
child 341172 3517289d68e3831d21b89e7e76fa9656402879e7
child 341648 0828435b2a32e79ce00785209daa8341d1d66b58
push id12629
push userbmo:ato@mozilla.com
push dateThu, 10 Mar 2016 11:32:47 +0000
reviewersmerge
milestone48.0a1
merge mozilla-inbound to mozilla-central a=merge
build/unix/Makefile.in
dom/indexedDB/test/extensions/Makefile.in
dom/media/test/chrome.ini
dom/media/test/test_texttrack_chrome.html
dom/media/test/test_texttrackcue_chrome.html
dom/media/test/test_texttracklist_chrome.html
dom/tests/mochitest/bugs/test_bug406375.html
dom/tests/mochitest/bugs/test_bug437361.html
dom/tests/mochitest/bugs/test_bug504862.html
dom/workers/test/extensions/bootstrap/Makefile.in
dom/workers/test/extensions/traditional/Makefile.in
image/Deinterlacer.cpp
image/Deinterlacer.h
js/src/old-configure.in
layout/base/tests/chrome/scroll_selection_into_view_window.html
layout/base/tests/chrome/test_scroll_selection_into_view.html
mobile/android/app/mobile.js
old-configure.in
testing/taskcluster/tasks/branches/base_job_flags.yml
testing/taskcluster/tasks/branches/base_jobs.yml
testing/web-platform/meta/web-animations/animation-effect-timing/duration.html.ini
toolkit/themes/linux/global/icons/webapps-16.png
toolkit/themes/linux/global/icons/webapps-64.png
toolkit/themes/osx/global/icons/webapps-16.png
toolkit/themes/osx/global/icons/webapps-16@2x.png
toolkit/themes/osx/global/icons/webapps-64.png
toolkit/themes/windows/global/icons/webapps-16.png
toolkit/themes/windows/global/icons/webapps-64.png
xpcom/idl-parser/xpidl/Makefile.in
xpcom/io/nsISimpleUnicharStreamFactory.idl
xpcom/typelib/xpt/tools/Makefile.in
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1250297 - Doesn't actually need a clobber, but updating the tree to an older changeset does.
+Bug 1253431 - Testing to see if a clobber fixes OSX debug build issues
--- a/accessible/interfaces/nsIAccessible.idl
+++ b/accessible/interfaces/nsIAccessible.idl
@@ -75,16 +75,21 @@ interface nsIAccessible : nsISupports
   readonly attribute long indexInParent;
 
   /**
    * The DOM node this nsIAccessible is associated with.
    */
   readonly attribute nsIDOMNode DOMNode;
 
   /**
+    * For remote accessibles the id of the related DOM node.
+    */
+  readonly attribute DOMString id;
+
+  /**
    * The document accessible that this access node resides in.
    */
   readonly attribute nsIAccessibleDocument document;
 
   /**
    * The root document accessible that this access node resides in.
    */
   readonly attribute nsIAccessibleDocument rootDocument;
--- a/accessible/ipc/DocAccessibleChild.cpp
+++ b/accessible/ipc/DocAccessibleChild.cpp
@@ -2045,10 +2045,31 @@ DocAccessibleChild::RecvExtents(const ui
       *aY = screenRect.y;
       *aWidth = screenRect.width;
       *aHeight = screenRect.height;
     }
   }
   return true;
 }
 
+bool
+DocAccessibleChild::RecvDOMNodeID(const uint64_t& aID, nsString* aDOMNodeID)
+{
+  Accessible* acc = IdToAccessible(aID);
+  if (!acc) {
+    return true;
+  }
+
+  nsIContent* content = acc->GetContent();
+  if (!content) {
+    return true;
+  }
+
+  nsIAtom* id = content->GetID();
+  if (id) {
+    id->ToString(*aDOMNodeID);
+  }
+
+  return true;
+}
+
 }
 }
--- a/accessible/ipc/DocAccessibleChild.h
+++ b/accessible/ipc/DocAccessibleChild.h
@@ -487,16 +487,17 @@ public:
                                      bool* aOk) override;
 
   virtual bool RecvExtents(const uint64_t& aID,
                            const bool& aNeedsScreenCoords,
                            int32_t* aX,
                            int32_t* aY,
                            int32_t* aWidth,
                            int32_t* aHeight) override;
+  virtual bool RecvDOMNodeID(const uint64_t& aID, nsString* aDOMNodeID) override;
 private:
 
   Accessible* IdToAccessible(const uint64_t& aID) const;
   Accessible* IdToAccessibleLink(const uint64_t& aID) const;
   Accessible* IdToAccessibleSelect(const uint64_t& aID) const;
   HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
   TextLeafAccessible* IdToTextLeafAccessible(const uint64_t& aID) const;
   ImageAccessible* IdToImageAccessible(const uint64_t& aID) const;
--- a/accessible/ipc/PDocAccessible.ipdl
+++ b/accessible/ipc/PDocAccessible.ipdl
@@ -252,12 +252,13 @@ child:
   prio(high) sync MimeType(uint64_t aID) returns(nsString aMime);
   prio(high) sync URLDocTypeMimeType(uint64_t aID) returns(nsString aURL, nsString aDocType, nsString aMimeType);
 
   prio(high) sync AccessibleAtPoint(uint64_t aID, int32_t aX, int32_t aY, bool aNeedsScreenCoords, uint32_t aWhich)
     returns(uint64_t aResult, bool aOk);
 
   prio(high) sync Extents(uint64_t aID, bool aNeedsScreenCoords)
     returns(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight);
+  prio(high) sync DOMNodeID(uint64_t aID) returns(nsString aDOMNodeID);
 };
 
 }
 }
--- a/accessible/ipc/ProxyAccessible.cpp
+++ b/accessible/ipc/ProxyAccessible.cpp
@@ -1148,16 +1148,22 @@ ProxyAccessible::AccessibleAtPoint(int32
 
 void
 ProxyAccessible::Extents(bool aNeedsScreenCoords, int32_t* aX, int32_t* aY,
                         int32_t* aWidth, int32_t* aHeight)
 {
   Unused << mDoc->SendExtents(mID, aNeedsScreenCoords, aX, aY, aWidth, aHeight);
 }
 
+void
+ProxyAccessible::DOMNodeID(nsString& aID)
+{
+  Unused << mDoc->SendDOMNodeID(mID, &aID);
+}
+
 Accessible*
 ProxyAccessible::OuterDocOfRemoteBrowser() const
 {
   auto tab = static_cast<dom::TabParent*>(mDoc->Manager());
   dom::Element* frame = tab->GetOwnerElement();
   NS_ASSERTION(frame, "why isn't the tab in a frame!");
   if (!frame)
     return nullptr;
--- a/accessible/ipc/ProxyAccessible.h
+++ b/accessible/ipc/ProxyAccessible.h
@@ -359,16 +359,22 @@ public:
 
   ProxyAccessible* AccessibleAtPoint(int32_t aX, int32_t aY,
                                      bool aNeedsScreenCoords);
 
   void Extents(bool aNeedsScreenCoords, int32_t* aX, int32_t* aY,
                int32_t* aWidth, int32_t* aHeight);
 
   /**
+   * Return the id of the dom node this accessible represents.  Note this
+   * should probably only be used for testing.
+   */
+  void DOMNodeID(nsString& aID);
+
+  /**
    * Allow the platform to store a pointers worth of data on us.
    */
   uintptr_t GetWrapper() const { return mWrapper; }
   void SetWrapper(uintptr_t aWrapper) { mWrapper = aWrapper; }
 
   /*
    * Return the ID of the accessible being proxied.
    */
--- a/accessible/xpcom/xpcAccessible.cpp
+++ b/accessible/xpcom/xpcAccessible.cpp
@@ -169,16 +169,31 @@ xpcAccessible::GetDOMNode(nsIDOMNode** a
   nsINode* node = Intl()->GetNode();
   if (node)
     CallQueryInterface(node, aDOMNode);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+xpcAccessible::GetId(nsAString& aID)
+{
+  ProxyAccessible* proxy = IntlGeneric().AsProxy();
+  if (!proxy) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsString id;
+  proxy->DOMNodeID(id);
+  aID.Assign(id);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 xpcAccessible::GetDocument(nsIAccessibleDocument** aDocument)
 {
   NS_ENSURE_ARG_POINTER(aDocument);
   *aDocument = nullptr;
 
   if (!Intl())
     return NS_ERROR_FAILURE;
 
--- a/accessible/xpcom/xpcAccessible.h
+++ b/accessible/xpcom/xpcAccessible.h
@@ -33,16 +33,17 @@ public:
   NS_IMETHOD GetLastChild(nsIAccessible** aLastChild) final override;
   NS_IMETHOD GetChildCount(int32_t* aChildCount) final override;
   NS_IMETHOD GetChildAt(int32_t aChildIndex, nsIAccessible** aChild)
     final override;
   NS_IMETHOD GetChildren(nsIArray** aChildren) final override;
   NS_IMETHOD GetIndexInParent(int32_t* aIndexInParent) final override;
 
   NS_IMETHOD GetDOMNode(nsIDOMNode** aDOMNode) final override;
+  NS_IMETHOD GetId(nsAString& aID) final override;
   NS_IMETHOD GetDocument(nsIAccessibleDocument** aDocument) final override;
   NS_IMETHOD GetRootDocument(nsIAccessibleDocument** aRootDocument)
     final override;
 
   NS_IMETHOD GetRole(uint32_t* aRole) final override;
   NS_IMETHOD GetState(uint32_t* aState, uint32_t* aExtraState)
     final override;
 
--- a/b2g/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.3/r183744",
-"size": 59602619,
-"digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b",
+"version": "clang 3.8.0/r247539",
+"size": 121389802,
+"digest": "2be6b42cfa1e92de4b49a57123f54043fec2d3cf8385276516dc6aaed99c88768ac4aebd7ce2e007ab074163523da29223436a4d1aef82f0f750f08f1b14cd71",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
--- a/b2g/dev/config/tooltool-manifests/macosx64/releng.manifest
+++ b/b2g/dev/config/tooltool-manifests/macosx64/releng.manifest
@@ -1,13 +1,13 @@
 [
 {
-"version": "clang 3.3/r183744",
-"size": 59602619,
-"digest": "86662ebc0ef650490559005948c4f0cb015dad72c7cac43732c2bf2995247081e30c139cf8008d19670a0009fc302c4eee2676981ee3f9ff4a15c01af22b783b",
+"version": "clang 3.8.0/r247539",
+"size": 121389802,
+"digest": "2be6b42cfa1e92de4b49a57123f54043fec2d3cf8385276516dc6aaed99c88768ac4aebd7ce2e007ab074163523da29223436a4d1aef82f0f750f08f1b14cd71",
 "algorithm": "sha512",
 "filename": "clang.tar.bz2",
 "unpack": true
 },
 {
 "size": 167175,
 "digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
 "algorithm": "sha512",
new file mode 100644
--- /dev/null
+++ b/browser/config/mozconfigs/linux64/artifact
@@ -0,0 +1,6 @@
+MOZ_AUTOMATION_BUILD_SYMBOLS=0
+MOZ_AUTOMATION_L10N_CHECK=0
+
+. "$topsrcdir/browser/config/mozconfigs/linux64/nightly"
+
+ac_add_options --enable-artifact-builds
--- a/browser/installer/Makefile.in
+++ b/browser/installer/Makefile.in
@@ -135,18 +135,22 @@ ifeq (x86,$(CPU_ARCH))
 ifdef _MSC_VER
 ifndef CLANG_CL
 DEFINES += -DWOW_HELPER
 endif
 endif
 endif
 
 
+# Builds using the hybrid FasterMake/RecursiveMake backend will
+# fail to produce a langpack. See bug 1255096.
 libs::
+ifeq (,$(filter FasterMake+RecursiveMake,$(BUILD_BACKENDS)))
 	$(MAKE) -C $(DEPTH)/browser/locales langpack
+endif
 
 ifeq (WINNT,$(OS_ARCH))
 PKGCOMP_FIND_OPTS =
 else
 PKGCOMP_FIND_OPTS = -L
 endif
 ifeq (Darwin, $(OS_ARCH))
 FINDPATH = $(_APPNAME)/Contents/MacOS
--- a/browser/themes/shared/tabs.inc.css
+++ b/browser/themes/shared/tabs.inc.css
@@ -87,16 +87,17 @@
   list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
 }
 
 .tab-icon-overlay {
   width: 16px;
   height: 16px;
   margin-top: -8px;
   margin-inline-start: -15px;
+  margin-inline-end: -1px;
   position: relative;
 }
 
 .tab-icon-overlay[crashed] {
   list-style-image: url("chrome://browser/skin/tabbrowser/crashed.svg");
 }
 
 .tab-icon-overlay[soundplaying],
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -540,17 +540,17 @@ bool isClassRefCounted(const CXXRecordDe
       return true;
     }
   }
 
   return false;
 }
 
 bool isClassRefCounted(QualType T) {
-  while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
+  while (const clang::ArrayType *arrTy = T->getAsArrayTypeUnsafe())
     T = arrTy->getElementType();
   CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
   return clazz ? isClassRefCounted(clazz) : false;
 }
 
 template <class T> bool IsInSystemHeader(const ASTContext &AC, const T &D) {
   auto &SourceManager = AC.getSourceManager();
   auto ExpansionLoc = SourceManager.getExpansionLoc(D.getLocStart());
@@ -585,24 +585,24 @@ const FieldDecl *getBaseRefCntMember(con
     if (refCntMember) {
       return refCntMember;
     }
   }
   return 0;
 }
 
 const FieldDecl *getBaseRefCntMember(QualType T) {
-  while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
+  while (const clang::ArrayType *arrTy = T->getAsArrayTypeUnsafe())
     T = arrTy->getElementType();
   CXXRecordDecl *clazz = T->getAsCXXRecordDecl();
   return clazz ? getBaseRefCntMember(clazz) : 0;
 }
 
 bool typeHasVTable(QualType T) {
-  while (const ArrayType *arrTy = T->getAsArrayTypeUnsafe())
+  while (const clang::ArrayType *arrTy = T->getAsArrayTypeUnsafe())
     T = arrTy->getElementType();
   CXXRecordDecl *offender = T->getAsCXXRecordDecl();
   return offender && offender->hasDefinition() && offender->isDynamicClass();
 }
 }
 
 namespace clang {
 namespace ast_matchers {
@@ -825,17 +825,17 @@ CustomTypeAnnotation::directAnnotationRe
   // Check if we have a cached answer
   void *Key = T.getAsOpaquePtr();
   ReasonCache::iterator Cached = Cache.find(T.getAsOpaquePtr());
   if (Cached != Cache.end()) {
     return Cached->second;
   }
 
   // Check if we have a type which we can recurse into
-  if (const ArrayType *Array = T->getAsArrayTypeUnsafe()) {
+  if (const clang::ArrayType *Array = T->getAsArrayTypeUnsafe()) {
     if (hasEffectiveAnnotation(Array->getElementType())) {
       AnnotationReason Reason = {Array->getElementType(), RK_ArrayElement,
                                  nullptr};
       Cache[Key] = Reason;
       return Reason;
     }
   }
 
--- a/build/mach_bootstrap.py
+++ b/build/mach_bootstrap.py
@@ -4,18 +4,18 @@
 
 from __future__ import print_function, unicode_literals
 
 import errno
 import json
 import os
 import platform
 import random
+import subprocess
 import sys
-import time
 import uuid
 import __builtin__
 
 from types import ModuleType
 
 
 STATE_DIR_FIRST_RUN = '''
 mach and the build system store shared state in a common directory on the
@@ -184,20 +184,16 @@ CATEGORIES = {
     'disabled': {
         'short': 'Disabled',
         'long': 'The disabled commands are hidden by default. Use -v to display them. These commands are unavailable for your current context, run "mach <command>" to see why.',
         'priority': 0,
     }
 }
 
 
-# Server to which to submit telemetry data
-BUILD_TELEMETRY_SERVER = 'http://52.88.27.118/build-metrics-dev'
-
-
 # We submit data to telemetry approximately every this many mach invocations
 TELEMETRY_SUBMISSION_FREQUENCY = 10
 
 
 def get_state_dir():
     """Obtain the path to a directory to hold state.
 
     Returns a tuple of the path and a bool indicating whether the value came
@@ -339,71 +335,35 @@ def bootstrap(topsrcdir, mozilla_dir=Non
 
 
         For now,  we will use this to handle build system telemetry.
         """
         # Don't do anything when...
         if should_skip_dispatch(context, handler):
             return
 
+        # We call mach environment in client.mk which would cause the
+        # data submission below to block the forward progress of make.
+        if handler.name in ('environment'):
+            return
+
         # We have not opted-in to telemetry
         if 'BUILD_SYSTEM_TELEMETRY' not in os.environ:
             return
 
         # Every n-th operation
         if random.randint(1, TELEMETRY_SUBMISSION_FREQUENCY) != 1:
             return
 
-        # No data to work with anyway
-        outgoing = os.path.join(get_state_dir()[0], 'telemetry', 'outgoing')
-        if not os.path.isdir(outgoing):
-            return
-
-        # We can't import requests until after it has been added during the
-        # bootstrapping below.
-        import requests
-
-        submitted = os.path.join(get_state_dir()[0], 'telemetry', 'submitted')
-        try:
-            os.mkdir(submitted)
-        except OSError as e:
-            if e.errno != errno.EEXIST:
-                raise
-
-        session = requests.Session()
-        for filename in os.listdir(outgoing):
-            path = os.path.join(outgoing, filename)
-            if os.path.isdir(path) or not path.endswith('.json'):
-                continue
-            with open(path, 'r') as f:
-                data = f.read()
-                try:
-                    r = session.post(BUILD_TELEMETRY_SERVER, data=data,
-                                     headers={'Content-Type': 'application/json'})
-                except Exception as e:
-                    print('Exception posting to telemetry server: %s' % str(e))
-                    break
-                # TODO: some of these errors are likely not recoverable, as
-                # written, we'll retry indefinitely
-                if r.status_code != 200:
-                    print('Error posting to telemetry: %s %s' %
-                          (r.status_code, r.text))
-                    continue
-
-            os.rename(os.path.join(outgoing, filename),
-                      os.path.join(submitted, filename))
-
-        session.close()
-
-        # Discard submitted data that is >= 30 days old
-        now = time.time()
-        for filename in os.listdir(submitted):
-            ctime = os.stat(os.path.join(submitted, filename)).st_ctime
-            if now - ctime >= 60*60*24*30:
-                os.remove(os.path.join(submitted, filename))
+        with open(os.devnull, 'wb') as devnull:
+            subprocess.Popen([sys.executable,
+                              os.path.join(topsrcdir, 'build',
+                                           'submit_telemetry_data.py'),
+                              get_state_dir()[0]],
+                              stdout=devnull, stderr=devnull)
 
     def populate_context(context, key=None):
         if key is None:
             return
         if key == 'state_dir':
             state_dir, is_environ = get_state_dir()
             if is_environ:
                 if not os.path.exists(state_dir):
--- a/build/moz.configure/init.configure
+++ b/build/moz.configure/init.configure
@@ -213,23 +213,52 @@ def application(app, help):
 def default_project(build_env, help):
     if build_env['TOPOBJDIR'].endswith('/js/src'):
         return 'js'
     return 'browser'
 
 option('--enable-project', nargs=1, default=default_project,
        help='Project to build')
 
-@depends('--enable-project', check_build_environment, '--help')
-def include_project_configure(project, build_env, help):
+option('--with-external-source-dir', env='EXTERNAL_SOURCE_DIR', nargs=1,
+       help='External directory containing additional build files')
+
+@depends('--enable-project', '--with-external-source-dir',
+         check_build_environment, '--help')
+def include_project_configure(project, external_source_dir, build_env, help):
     if not project:
         error('--enable-project is required.')
 
-    path = os.path.join(build_env['TOPSRCDIR'], project[0], 'moz.configure')
+    base_dir = build_env['TOPSRCDIR']
+    if external_source_dir:
+        set_config('EXTERNAL_SOURCE_DIR', external_source_dir[0])
+        base_dir = os.path.join(base_dir, external_source_dir[0])
+
+    path = os.path.join(base_dir, project[0], 'moz.configure')
     if not os.path.exists(path):
         error('Cannot find project %s' % project[0])
     return path
 
-include(include_project_configure)
+@depends(include_project_configure, check_build_environment, '--help')
+def build_project(include_project_configure, build_env, help):
+    return os.path.dirname(os.path.relpath(include_project_configure,
+                                           build_env['TOPSRCDIR']))
+
+
+# This is temporary until js/src/configure and configure are merged.
+@depends(build_project)
+def extra_old_configure_args(build_project):
+    if build_project != 'js':
+        return []
+    return False
 
-@depends('--enable-project')
-def build_app(project):
-    return project[0]
+# Use instead of option() in js/moz.configure
+@template
+def js_option(*args, **kwargs):
+    opt = option(*args, **kwargs)
+
+    @depends(opt.option, extra_old_configure_args)
+    def js_option(value, extra_old_configure_args):
+        if extra_old_configure_args is not False:
+            extra_old_configure_args.append(value.format(opt.option))
+
+
+include(include_project_configure)
--- a/build/moz.configure/old.configure
+++ b/build/moz.configure/old.configure
@@ -55,20 +55,22 @@ def autoconf(mozconfig, autoconf):
 
     set_config('AUTOCONF', autoconf)
     return autoconf
 
 
 option(env='OLD_CONFIGURE', nargs=1, help='Path to the old configure script')
 
 @depends('OLD_CONFIGURE', mozconfig, autoconf, check_build_environment, shell,
-         virtualenv_python, compile_environment, build_app)
+         virtualenv_python, compile_environment, build_project,
+         extra_old_configure_args, '--with-external-source-dir')
 @advanced
 def prepare_configure(old_configure, mozconfig, autoconf, build_env, shell,
-                      python, compile_env, build_app):
+                      python, compile_env, build_project,
+                      extra_old_configure_args, external_source_dir):
     import glob
     import itertools
     import subprocess
     import sys
     # Import getmtime without overwriting the sandbox os.path.
     from os.path import getmtime
 
     from mozbuild.shellutil import quote
@@ -121,17 +123,22 @@ def prepare_configure(old_configure, moz
                 print("%s=%s" % (key, quote(value)), file=out)
             for t in ('env', 'vars'):
                 for key in mozconfig[t]['removed'].keys():
                     print("unset %s" % key, file=out)
 
         print('PYTHON=%s' % quote(python), file=out)
         if compile_env:
             print('COMPILE_ENVIRONMENT=1', file=out)
-        print('MOZ_BUILD_APP=%s' % build_app, file=out)
+        print('MOZ_BUILD_APP=%s' % build_project, file=out)
+        if external_source_dir:
+            print(external_source_dir.format('EXTERNAL_SOURCE_DIR'), file=out)
+
+    if extra_old_configure_args:
+        cmd += extra_old_configure_args
 
     return cmd
 
 
 @template
 def old_configure_options(*options):
     for opt in options:
         option(opt, nargs='*', help='Help missing for old configure options')
@@ -308,17 +315,16 @@ def old_configure_options(*options):
     '--with-crashreporter-enable-percent',
     '--with-cross-lib',
     '--with-debug-label',
     '--with-default-mozilla-five-home',
     '--with-distribution-id',
     '--with-doc-include-dirs',
     '--with-doc-input-dirs',
     '--with-doc-output-dir',
-    '--with-external-source-dir',
     '--with-float-abi',
     '--with-fpu',
     '--with-gl-provider',
     '--with-gonk',
     '--with-gonk-toolchain-prefix',
     '--with-google-api-keyfile',
     '--with-google-oauth-api-keyfile',
     '--with-gradle',
@@ -354,16 +360,22 @@ def old_configure_options(*options):
     '--with-thumb-interwork',
     '--with-unify-dist',
     '--with-user-appdir',
     '--with-windows-version',
     '--with-x',
     '--with-xulrunner-stub-name',
     '--x-includes',
     '--x-libraries',
+
+    # Below are the configure flags used by comm-central.
+    '--enable-ldap',
+    '--enable-mapi',
+    '--enable-calendar',
+    '--enable-incomplete-external-linkage',
 )
 @advanced
 def old_configure(prepare_configure, all_options, *options):
     import codecs
     import os
     import subprocess
     import sys
     import types
--- a/build/mozconfig.common
+++ b/build/mozconfig.common
@@ -11,9 +11,11 @@
 # of this file.
 
 mk_add_options AUTOCLOBBER=1
 
 ac_add_options --enable-crashreporter
 
 ac_add_options --enable-release
 
+ac_add_options --enable-js-shell
+
 . "$topsrcdir/build/mozconfig.automation"
new file mode 100644
--- /dev/null
+++ b/build/submit_telemetry_data.py
@@ -0,0 +1,77 @@
+# 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/.
+
+import errno
+import logging
+import os
+import sys
+import time
+
+HERE = os.path.abspath(os.path.dirname(__file__))
+sys.path.append(os.path.join(HERE, '..', 'python', 'requests'))
+import requests
+
+
+# Server to which to submit telemetry data
+BUILD_TELEMETRY_SERVER = 'http://52.88.27.118/build-metrics-dev'
+
+
+def submit_telemetry_data(statedir):
+
+    # No data to work with anyway
+    outgoing = os.path.join(statedir, 'telemetry', 'outgoing')
+    if not os.path.isdir(outgoing):
+        return 0
+
+    submitted = os.path.join(statedir, 'telemetry', 'submitted')
+    try:
+        os.mkdir(submitted)
+    except OSError as e:
+        if e.errno != errno.EEXIST:
+            raise
+
+    session = requests.Session()
+    for filename in os.listdir(outgoing):
+        path = os.path.join(outgoing, filename)
+        if os.path.isdir(path) or not path.endswith('.json'):
+            continue
+        with open(path, 'r') as f:
+            data = f.read()
+            try:
+                r = session.post(BUILD_TELEMETRY_SERVER, data=data,
+                                 headers={'Content-Type': 'application/json'})
+            except Exception as e:
+                logging.error('Exception posting to telemetry '
+                              'server: %s' % str(e))
+                break
+            # TODO: some of these errors are likely not recoverable, as
+            # written, we'll retry indefinitely
+            if r.status_code != 200:
+                logging.error('Error posting to telemetry: %s %s' %
+                              (r.status_code, r.text))
+                continue
+
+        os.rename(os.path.join(outgoing, filename),
+                  os.path.join(submitted, filename))
+
+    session.close()
+
+    # Discard submitted data that is >= 30 days old
+    now = time.time()
+    for filename in os.listdir(submitted):
+        ctime = os.stat(os.path.join(submitted, filename)).st_ctime
+        if now - ctime >= 60*60*24*30:
+            os.remove(os.path.join(submitted, filename))
+
+    return 0
+
+
+if __name__ == '__main__':
+    if len(sys.argv) != 2:
+        print('usage: python submit_telemetry_data.py <statedir>')
+        sys.exit(1)
+    statedir = sys.argv[1]
+    logging.basicConfig(filename=os.path.join(statedir, 'telemetry', 'telemetry.log'),
+                        format='%(asctime)s %(message)s')
+    sys.exit(submit_telemetry_data(statedir))
deleted file mode 100644
--- a/build/unix/Makefile.in
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- makefile -*-
-#
-# 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/.
-
-SDK_BINARY = run-mozilla.sh
-
-ifneq ($(LLVM_SYMBOLIZER),)
-# Install a copy of the llvm-symbolizer binary to dist/bin, so it can
-# be used for symbolizing traces for e.g. AddressSanitizer
-LLVMSYM_EXECUTABLES=$(LLVM_SYMBOLIZER)
-LLVMSYM_DEST=$(FINAL_TARGET)
-INSTALL_TARGETS += LLVMSYM
-endif
-
-include $(topsrcdir)/config/rules.mk
-
-libs:: $(srcdir)/run-mozilla.sh
-	$(INSTALL) $< $(DIST)/bin
-
-# EOF
--- a/build/unix/moz.build
+++ b/build/unix/moz.build
@@ -5,8 +5,18 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION'] or CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']:
     DIRS += ['stdc++compat']
 
 if CONFIG['USE_ELF_HACK']:
     DIRS += ['elfhack']
 
+if CONFIG['LLVM_SYMBOLIZER']:
+    FINAL_TARGET_FILES += ['/' + CONFIG['LLVM_SYMBOLIZER']]
+
+SDK_FILES.bin += [
+    'run-mozilla.sh',
+]
+
+FINAL_TARGET_FILES += [
+    'run-mozilla.sh',
+]
--- a/config/config.mk
+++ b/config/config.mk
@@ -366,20 +366,16 @@ endif
 HOST_CFLAGS += $(HOST_DEFINES) $(MOZBUILD_HOST_CFLAGS)
 HOST_CXXFLAGS += $(HOST_DEFINES) $(MOZBUILD_HOST_CXXFLAGS)
 
 #
 # Name of the binary code directories
 #
 # Override defaults
 
-# Default location of include files
-IDL_PARSER_DIR = $(topsrcdir)/xpcom/idl-parser
-IDL_PARSER_CACHE_DIR = $(DEPTH)/xpcom/idl-parser
-
 SDK_LIB_DIR = $(DIST)/sdk/lib
 SDK_BIN_DIR = $(DIST)/sdk/bin
 
 DEPENDENCIES	= .md
 
 ifdef MACOSX_DEPLOYMENT_TARGET
 export MACOSX_DEPLOYMENT_TARGET
 endif # MACOSX_DEPLOYMENT_TARGET
--- a/config/makefiles/xpidl/Makefile.in
+++ b/config/makefiles/xpidl/Makefile.in
@@ -29,18 +29,18 @@ idl_deps_dir := .deps
 dist_idl_dir := $(DIST)/idl
 dist_include_dir := $(DIST)/include
 process_py := $(topsrcdir)/python/mozbuild/mozbuild/action/xpidl-process.py
 
 # TODO we should use py_action, but that would require extra directories to be
 # in the virtualenv.
 %.xpt:
 	@echo "$(@F)"
-	$(PYTHON_PATH) $(PLY_INCLUDE) -I$(IDL_PARSER_DIR) -I$(IDL_PARSER_CACHE_DIR) \
-		$(process_py) --cache-dir $(IDL_PARSER_CACHE_DIR) --depsdir $(idl_deps_dir) \
+	$(PYTHON_PATH) $(PLY_INCLUDE) -I$(topsrcdir)/xpcom/idl-parser -I$(DEPTH)/xpcom/idl-parser/xpidl \
+		$(process_py) --cache-dir $(DEPTH)/xpcom/idl-parser/xpidl --depsdir $(idl_deps_dir) \
 		$(dist_idl_dir) $(dist_include_dir) $(@D) $(libxul_sdk_includes) \
 		$(basename $(notdir $@)) $($(basename $(notdir $@))_deps)
 # When some IDL is added or removed, if the actual IDL file was already, or
 # still is, in the tree, simple dependencies can't detect that the XPT needs
 # to be rebuilt.
 # Add the current value of $($(xpidl_module)_deps) in the depend file, such that
 # we can later check if the value has changed since last build, which will
 # indicate whether IDLs were added or removed.
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -257,17 +257,16 @@ LIBRARY :=
 SHARED_LIBRARY :=
 IMPORT_LIBRARY :=
 REAL_LIBRARY :=
 PROGRAM :=
 SIMPLE_PROGRAMS :=
 HOST_LIBRARY :=
 HOST_PROGRAM :=
 HOST_SIMPLE_PROGRAMS :=
-SDK_BINARY := $(filter %.py,$(SDK_BINARY))
 SDK_LIBRARY :=
 endif
 
 ALL_TRASH = \
 	$(GARBAGE) $(TARGETS) $(OBJS) $(PROGOBJS) LOGS TAGS a.out \
 	$(filter-out $(ASFILES),$(OBJS:.$(OBJ_SUFFIX)=.s)) $(OBJS:.$(OBJ_SUFFIX)=.ii) \
 	$(OBJS:.$(OBJ_SUFFIX)=.i) $(OBJS:.$(OBJ_SUFFIX)=.i_o) \
 	$(HOST_PROGOBJS) $(HOST_OBJS) $(IMPORT_LIBRARY) \
@@ -1165,28 +1164,16 @@ ifneq (,$(SDK_LIBRARY))
 ifndef NO_DIST_INSTALL
 SDK_LIBRARY_FILES := $(SDK_LIBRARY)
 SDK_LIBRARY_DEST := $(SDK_LIB_DIR)
 SDK_LIBRARY_TARGET := target
 INSTALL_TARGETS += SDK_LIBRARY
 endif
 endif # SDK_LIBRARY
 
-# SDK_BINARY is still used in various makefiles for non-products of the
-# compilation, so we need to keep that running on the libs tier.
-ifneq (,$(strip $(SDK_BINARY)))
-ifndef NO_DIST_INSTALL
-SDK_BINARY_FILES := $(SDK_BINARY)
-SDK_BINARY_DEST := $(SDK_BIN_DIR)
-# SDK_BINARY_TARGET is set in xpcom/idl-parser/Makefile.in
-SDK_BINARY_TARGET ?= libs target
-INSTALL_TARGETS += SDK_BINARY
-endif
-endif # SDK_BINARY
-
 ################################################################################
 # CHROME PACKAGING
 
 chrome::
 	$(MAKE) realchrome
 	$(LOOP_OVER_DIRS)
 
 $(FINAL_TARGET)/chrome: $(call mkdir_deps,$(FINAL_TARGET)/chrome)
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -801,17 +801,16 @@ nsDocShell::nsDocShell()
   , mInEnsureScriptEnv(false)
 #endif
   , mAffectPrivateSessionLifetime(true)
   , mInvisible(false)
   , mHasLoadedNonBlankURI(false)
   , mDefaultLoadFlags(nsIRequest::LOAD_NORMAL)
   , mBlankTiming(false)
   , mFrameType(FRAME_TYPE_REGULAR)
-  , mIsInIsolatedMozBrowser(false)
   , mParentCharsetSource(0)
   , mJSRunToCompletionDepth(0)
 {
   mHistoryID = ++gDocshellIDCounter;
   if (gDocShellCount++ == 0) {
     NS_ASSERTION(sURIFixup == nullptr,
                  "Huh, sURIFixup not null in first nsDocShell ctor!");
 
@@ -3493,18 +3492,18 @@ nsDocShell::CanAccessItem(nsIDocShellTre
   }
 
   if (targetDS->GetIsInIsolatedMozBrowserElement() !=
         accessingDS->GetIsInIsolatedMozBrowserElement() ||
       targetDS->GetAppId() != accessingDS->GetAppId()) {
     return false;
   }
 
-  if (static_cast<nsDocShell*>(targetDS.get())->GetOriginAttributes().mUserContextId !=
-      static_cast<nsDocShell*>(accessingDS.get())->GetOriginAttributes().mUserContextId) {
+  if (static_cast<nsDocShell*>(targetDS.get())->GetOriginAttributes() !=
+      static_cast<nsDocShell*>(accessingDS.get())->GetOriginAttributes()) {
     return false;
   }
 
   // A private document can't access a non-private one, and vice versa.
   if (static_cast<nsDocShell*>(targetDS.get())->UsePrivateBrowsing() !=
       static_cast<nsDocShell*>(accessingDS.get())->UsePrivateBrowsing()) {
     return false;
   }
@@ -4007,17 +4006,16 @@ nsDocShell::AddChild(nsIDocShellTreeItem
     childDocShell->SetUseGlobalHistory(true);
   }
 
   if (aChild->ItemType() != mItemType) {
     return NS_OK;
   }
 
   aChild->SetTreeOwner(mTreeOwner);
-  childDocShell->SetIsInIsolatedMozBrowserElement(mIsInIsolatedMozBrowser);
 
   nsCOMPtr<nsIDocShell> childAsDocShell(do_QueryInterface(aChild));
   if (!childAsDocShell) {
     return NS_OK;
   }
 
   // charset, style-disabling, and zoom will be inherited in SetupNewViewer()
 
@@ -9604,40 +9602,41 @@ nsDocShell::InternalLoad(nsIURI* aURI,
   if (!aURI) {
     return NS_ERROR_NULL_POINTER;
   }
 
   NS_ENSURE_TRUE(IsValidLoadType(aLoadType), NS_ERROR_INVALID_ARG);
 
   NS_ENSURE_TRUE(!mIsBeingDestroyed, NS_ERROR_NOT_AVAILABLE);
 
+  rv = EnsureScriptEnvironment();
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
+
   // wyciwyg urls can only be loaded through history. Any normal load of
   // wyciwyg through docshell is  illegal. Disallow such loads.
   if (aLoadType & LOAD_CMD_NORMAL) {
     bool isWyciwyg = false;
     rv = aURI->SchemeIs("wyciwyg", &isWyciwyg);
     if ((isWyciwyg && NS_SUCCEEDED(rv)) || NS_FAILED(rv)) {
       return NS_ERROR_FAILURE;
     }
   }
 
   bool isJavaScript = false;
   if (NS_FAILED(aURI->SchemeIs("javascript", &isJavaScript))) {
     isJavaScript = false;
   }
 
-  //
   // First, notify any nsIContentPolicy listeners about the document load.
   // Only abort the load if a content policy listener explicitly vetos it!
-  //
-  nsCOMPtr<Element> requestingElement;
   // Use nsPIDOMWindow since we _want_ to cross the chrome boundary if needed
-  if (mScriptGlobal) {
-    requestingElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
-  }
+  nsCOMPtr<Element> requestingElement =
+    mScriptGlobal->AsOuter()->GetFrameElementInternal();
 
   RefPtr<nsGlobalWindow> MMADeathGrip = mScriptGlobal;
 
   int16_t shouldLoad = nsIContentPolicy::ACCEPT;
   uint32_t contentType;
   bool isNewDocShell = false;
   bool isTargetTopLevelDocShell = false;
   nsCOMPtr<nsIDocShell> targetDocShell;
@@ -10495,20 +10494,26 @@ nsDocShell::DoURILoad(nsIURI* aURI,
                       nsIRequest** aRequest,
                       bool aIsNewWindowTarget,
                       bool aBypassClassifier,
                       bool aForceAllowCookies,
                       const nsAString& aSrcdoc,
                       nsIURI* aBaseURI,
                       nsContentPolicyType aContentPolicyType)
 {
+  // Double-check that we're still around to load this URI.
+  if (mIsBeingDestroyed) {
+    // Return NS_OK despite not doing anything to avoid throwing exceptions from
+    // nsLocation::SetHref if the unload handler of the existing page tears us
+    // down.
+    return NS_OK;
+  }
+
   nsresult rv;
-  nsCOMPtr<nsIURILoader> uriLoader;
-
-  uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
+  nsCOMPtr<nsIURILoader> uriLoader = do_GetService(NS_URI_LOADER_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   nsLoadFlags loadFlags = mDefaultLoadFlags;
   if (aFirstParty) {
     // tag first party URL loads
     loadFlags |= nsIChannel::LOAD_INITIAL_DOCUMENT_URI;
@@ -10537,75 +10542,72 @@ nsDocShell::DoURILoad(nsIURI* aURI,
       }
       nestedURI->GetInnerURI(getter_AddRefs(tempURI));
       nestedURI = do_QueryInterface(tempURI);
     }
   }
 
   // For mozWidget, display a load error if we navigate to a page which is not
   // claimed in |widgetPages|.
-  if (mScriptGlobal) {
-    // When we go to display a load error for an invalid mozWidget page, we will
-    // try to load an about:neterror page, which is also an invalid mozWidget
-    // page. To avoid recursion, we skip this check if aURI's scheme is "about".
-
-    // The goal is to prevent leaking sensitive information of an invalid page of
-    // an app, so allowing about:blank would not be conflict to the goal.
-    bool isAbout = false;
-    rv = aURI->SchemeIs("about", &isAbout);
-    if (NS_SUCCEEDED(rv) && !isAbout &&
-        nsIDocShell::GetIsApp()) {
-      nsCOMPtr<Element> frameElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
-      if (frameElement) {
-        nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(frameElement);
-        // |GetReallyIsApp| indicates the browser frame is a valid app or widget.
-        // Here we prevent navigating to an app or widget which loses its validity
-        // by loading invalid page or other way.
-        if (browserFrame && !browserFrame->GetReallyIsApp()) {
-          nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
-          if (serv) {
-              serv->NotifyObservers(GetDocument(), "invalid-widget", nullptr);
-          }
-          return NS_ERROR_MALFORMED_URI;
+  // When we go to display a load error for an invalid mozWidget page, we will
+  // try to load an about:neterror page, which is also an invalid mozWidget
+  // page. To avoid recursion, we skip this check if aURI's scheme is "about".
+
+  // The goal is to prevent leaking sensitive information of an invalid page of
+  // an app, so allowing about:blank would not be conflict to the goal.
+  bool isAbout = false;
+  rv = aURI->SchemeIs("about", &isAbout);
+  if (NS_SUCCEEDED(rv) && !isAbout &&
+      nsIDocShell::GetIsApp()) {
+    nsCOMPtr<Element> frameElement = mScriptGlobal->AsOuter()->GetFrameElementInternal();
+    if (frameElement) {
+      nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(frameElement);
+      // |GetReallyIsApp| indicates the browser frame is a valid app or widget.
+      // Here we prevent navigating to an app or widget which loses its validity
+      // by loading invalid page or other way.
+      if (browserFrame && !browserFrame->GetReallyIsApp()) {
+        nsCOMPtr<nsIObserverService> serv = services::GetObserverService();
+        if (serv) {
+            serv->NotifyObservers(GetDocument(), "invalid-widget", nullptr);
         }
+        return NS_ERROR_MALFORMED_URI;
       }
     }
   }
 
   // open a channel for the url
   nsCOMPtr<nsIChannel> channel;
 
   bool isSrcdoc = !aSrcdoc.IsVoid();
 
-  // There are three cases we care about:
-  // * Null mScriptGlobal: shouldn't happen but does (see bug 1240246). In this
-  //   case, we create a loadingPrincipal as for a top-level load, but we leave
-  //   requestingNode and requestingWindow null.
+  // There are two cases we care about:
   // * Top-level load (GetFrameElementInternal returns null). In this case,
   //   requestingNode is null, but requestingWindow is our mScriptGlobal.
   //   TODO we want to pass null for loadingPrincipal in this case.
   // * Subframe load: requestingWindow is null, but requestingNode is the frame
   //   element for the load. loadingPrincipal is the NodePrincipal of the frame
   //   element.
   nsCOMPtr<nsINode> requestingNode;
   nsCOMPtr<nsPIDOMWindowOuter> requestingWindow;
 
   nsCOMPtr<nsIPrincipal> loadingPrincipal;
-  if (mScriptGlobal) {
-    requestingNode = mScriptGlobal->AsOuter()->GetFrameElementInternal();
-    if (requestingNode) {
-      // If we have a requesting node, then use that as our loadingPrincipal.
-      loadingPrincipal = requestingNode->NodePrincipal();
-    } else {
-      MOZ_ASSERT(aContentPolicyType == nsIContentPolicy::TYPE_DOCUMENT);
-      requestingWindow = mScriptGlobal->AsOuter();
-    }
-  }
-
-  if (!loadingPrincipal) {
+  requestingNode = mScriptGlobal->AsOuter()->GetFrameElementInternal();
+  if (requestingNode) {
+    // If we have a requesting node, then use that as our loadingPrincipal.
+    loadingPrincipal = requestingNode->NodePrincipal();
+  } else {
+    if (aContentPolicyType != nsIContentPolicy::TYPE_DOCUMENT) {
+      // If this isn't a top-level load and mScriptGlobal's frame element is
+      // null, then the element got removed from the DOM while we were trying to
+      // load this resource. This docshell is scheduled for destruction already,
+      // so bail out here.
+      return NS_OK;
+    }
+
+    requestingWindow = mScriptGlobal->AsOuter();
     if (mItemType != typeChrome) {
       nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
       ssm->GetDocShellCodebasePrincipal(aURI, this, getter_AddRefs(loadingPrincipal));
     } else {
       // This is a top-level chrome load, use a system principal for the
       // loadingPrincipal.
       loadingPrincipal = nsContentUtils::GetSystemPrincipal();
     }
@@ -13911,41 +13913,35 @@ nsDocShell::GetInheritedFrameType()
   }
 
   return static_cast<nsDocShell*>(parent.get())->GetInheritedFrameType();
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsIsolatedMozBrowserElement(bool* aIsIsolatedMozBrowserElement)
 {
-  bool result = mFrameType == FRAME_TYPE_BROWSER && mIsInIsolatedMozBrowser;
+  bool result = mFrameType == FRAME_TYPE_BROWSER &&
+                mOriginAttributes.mInIsolatedMozBrowser;
   *aIsIsolatedMozBrowserElement = result;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsInIsolatedMozBrowserElement(bool* aIsInIsolatedMozBrowserElement)
 {
-  MOZ_ASSERT(!mIsInIsolatedMozBrowser ||
+  MOZ_ASSERT(!mOriginAttributes.mInIsolatedMozBrowser ||
              (GetInheritedFrameType() == FRAME_TYPE_BROWSER),
              "Isolated mozbrowser should only be true inside browser frames");
   bool result = (GetInheritedFrameType() == FRAME_TYPE_BROWSER) &&
-                mIsInIsolatedMozBrowser;
+                mOriginAttributes.mInIsolatedMozBrowser;
   *aIsInIsolatedMozBrowserElement = result;
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
-nsDocShell::SetIsInIsolatedMozBrowserElement(bool aIsInIsolatedMozBrowserElement)
-{
-  mIsInIsolatedMozBrowser = aIsInIsolatedMozBrowserElement;
-  return NS_OK;
-}
-
-/* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetIsInMozBrowserOrApp(bool* aIsInMozBrowserOrApp)
 {
   *aIsInMozBrowserOrApp = (GetInheritedFrameType() != FRAME_TYPE_REGULAR);
   return NS_OK;
 }
 
 /* [infallible] */ NS_IMETHODIMP
 nsDocShell::GetAppId(uint32_t* aAppId)
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -995,19 +995,16 @@ protected:
   // This flag means that mTiming has been initialized but nulled out.
   // We will check the innerWin's timing before creating a new one
   // in MaybeInitTiming()
   bool mBlankTiming;
 
   // Are we a regular frame, a browser frame, or an app frame?
   uint32_t mFrameType;
 
-  // Whether we are in an isolated mozbrowser frame.
-  bool mIsInIsolatedMozBrowser;
-
   nsString mPaymentRequestId;
 
   nsString GetInheritedPaymentRequestId();
 
   nsString mInterceptedDocumentId;
 
 private:
   nsCString mForcedCharset;
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -804,17 +804,17 @@ interface nsIDocShell : nsIDocShellTreeI
    * isolated since isolation is disabled.  Isolation can only be disabled if
    * the containing document is chrome.
    *
    * Our notion here of "contained in" means: Walk up the docshell hierarchy in
    * this process until we hit an <iframe mozapp> or <iframe mozbrowser> (or
    * until the hierarchy ends).  Return true iff the docshell we stopped on has
    * isIsolatedMozBrowserElement == true.
    */
-  [infallible] attribute boolean isInIsolatedMozBrowserElement;
+  [infallible] readonly attribute boolean isInIsolatedMozBrowserElement;
 
   /**
    * Returns true if this docshell corresponds to an <iframe mozbrowser> or
    * <iframe mozapp>, or if this docshell is contained in an <iframe mozbrowser>
    * or <iframe mozapp>.  <xul:browser> returns false here.
    *
    * To compute this value, we walk up the docshell hierarchy.  If we encounter
    * a docshell with isMozBrowserOrApp before we hit the end of the hierarchy,
--- a/docshell/base/timeline/ObservedDocShell.cpp
+++ b/docshell/base/timeline/ObservedDocShell.cpp
@@ -5,34 +5,40 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ObservedDocShell.h"
 
 #include "AbstractTimelineMarker.h"
 #include "LayerTimelineMarker.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Move.h"
+#include "mozilla/ScopeExit.h"
 
 namespace mozilla {
 
 ObservedDocShell::ObservedDocShell(nsIDocShell* aDocShell)
   : MarkersStorage("ObservedDocShellMutex")
   , mDocShell(aDocShell)
+  , mPopping(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
 }
 
 void
 ObservedDocShell::AddMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
 {
   // Only allow main thread markers to go into this list. No need to lock
   // here since `mTimelineMarkers` will only be accessed or modified on the
   // main thread only.
   MOZ_ASSERT(NS_IsMainThread());
-  mTimelineMarkers.AppendElement(Move(aMarker));
+  // Don't accept any markers generated by the process of popping
+  // markers.
+  if (!mPopping) {
+    mTimelineMarkers.AppendElement(Move(aMarker));
+  }
 }
 
 void
 ObservedDocShell::AddOTMTMarker(UniquePtr<AbstractTimelineMarker>&& aMarker)
 {
   // Only allow off the main thread markers to go into this list. Since most
   // of our markers come from the main thread, be a little more efficient and
   // avoid dealing with multithreading scenarios until all the markers are
@@ -53,16 +59,20 @@ ObservedDocShell::ClearMarkers()
 
 void
 ObservedDocShell::PopMarkers(JSContext* aCx,
                              nsTArray<dom::ProfileTimelineMarker>& aStore)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MutexAutoLock lock(GetLock()); // for `mOffTheMainThreadTimelineMarkers`.
 
+  MOZ_RELEASE_ASSERT(!mPopping);
+  mPopping = true;
+  auto resetPopping = MakeScopeExit([&] { mPopping = false; });
+
   // First, move all of our markers into a single array. We'll chose
   // the `mTimelineMarkers` store because that's where we expect most of
   // our markers to be.
   mTimelineMarkers.AppendElements(Move(mOffTheMainThreadTimelineMarkers));
 
   // If we see an unpaired START, we keep it around for the next call
   // to ObservedDocShell::PopMarkers. We store the kept START objects here.
   nsTArray<UniquePtr<AbstractTimelineMarker>> keptStartMarkers;
--- a/docshell/base/timeline/ObservedDocShell.h
+++ b/docshell/base/timeline/ObservedDocShell.h
@@ -27,16 +27,17 @@ struct ProfileTimelineMarker;
 // allowed to exist. See TimelineConsumers for register/unregister logic.
 class ObservedDocShell : public MarkersStorage
 {
 private:
   RefPtr<nsIDocShell> mDocShell;
 
   // Main thread only.
   nsTArray<UniquePtr<AbstractTimelineMarker>> mTimelineMarkers;
+  bool mPopping;
 
   // Off the main thread only.
   nsTArray<UniquePtr<AbstractTimelineMarker>> mOffTheMainThreadTimelineMarkers;
 
 public:
   explicit ObservedDocShell(nsIDocShell* aDocShell);
   nsIDocShell* operator*() const { return mDocShell.get(); }
 
--- a/dom/animation/AnimationEffectTiming.cpp
+++ b/dom/animation/AnimationEffectTiming.cpp
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/AnimationEffectTiming.h"
 
 #include "mozilla/dom/AnimatableBinding.h"
 #include "mozilla/dom/AnimationEffectTimingBinding.h"
+#include "mozilla/TimingParams.h"
 
 namespace mozilla {
 namespace dom {
 
 JSObject*
 AnimationEffectTiming::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return AnimationEffectTimingBinding::Wrap(aCx, this, aGivenProto);
@@ -34,34 +35,28 @@ AnimationEffectTiming::SetEndDelay(doubl
     return;
   }
   mTiming.mEndDelay = endDelay;
 
   NotifyTimingUpdate();
 }
 
 void
-AnimationEffectTiming::SetDuration(const UnrestrictedDoubleOrString& aDuration)
+AnimationEffectTiming::SetDuration(const UnrestrictedDoubleOrString& aDuration,
+                                   ErrorResult& aRv)
 {
-  if (mTiming.mDuration.IsUnrestrictedDouble() &&
-      aDuration.IsUnrestrictedDouble() &&
-      mTiming.mDuration.GetAsUnrestrictedDouble() ==
-        aDuration.GetAsUnrestrictedDouble()) {
+  Maybe<StickyTimeDuration> newDuration =
+    TimingParams::ParseDuration(aDuration, aRv);
+  if (aRv.Failed()) {
     return;
   }
 
-  if (mTiming.mDuration.IsString() && aDuration.IsString() &&
-      mTiming.mDuration.GetAsString() == aDuration.GetAsString()) {
+  if (mTiming.mDuration == newDuration) {
     return;
   }
 
-  if (aDuration.IsUnrestrictedDouble()) {
-    mTiming.mDuration.SetAsUnrestrictedDouble() =
-      aDuration.GetAsUnrestrictedDouble();
-  } else {
-    mTiming.mDuration.SetAsString() = aDuration.GetAsString();
-  }
+  mTiming.mDuration = newDuration;
 
   NotifyTimingUpdate();
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/AnimationEffectTiming.h
+++ b/dom/animation/AnimationEffectTiming.h
@@ -20,17 +20,18 @@ public:
     : AnimationEffectTimingReadOnly(aTiming)
     , mEffect(aEffect) { }
 
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   void Unlink() override { mEffect = nullptr; }
 
   void SetEndDelay(double aEndDelay);
-  void SetDuration(const UnrestrictedDoubleOrString& aDuration);
+  void SetDuration(const UnrestrictedDoubleOrString& aDuration,
+                   ErrorResult& aRv);
 
 private:
   void NotifyTimingUpdate();
   KeyframeEffect* MOZ_NON_OWNING_REF mEffect;
 };
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/animation/AnimationEffectTimingReadOnly.cpp
+++ b/dom/animation/AnimationEffectTimingReadOnly.cpp
@@ -8,138 +8,41 @@
 
 #include "mozilla/AnimationUtils.h"
 #include "mozilla/dom/AnimatableBinding.h"
 #include "mozilla/dom/AnimationEffectTimingReadOnlyBinding.h"
 #include "mozilla/dom/CSSPseudoElement.h"
 #include "mozilla/dom/KeyframeEffectBinding.h"
 
 namespace mozilla {
-
-TimingParams::TimingParams(const dom::AnimationEffectTimingProperties& aRhs,
-                           const dom::Element* aTarget)
-  : mDuration(aRhs.mDuration)
-  , mDelay(TimeDuration::FromMilliseconds(aRhs.mDelay))
-  , mEndDelay(TimeDuration::FromMilliseconds(aRhs.mEndDelay))
-  , mIterations(aRhs.mIterations)
-  , mIterationStart(aRhs.mIterationStart)
-  , mDirection(aRhs.mDirection)
-  , mFill(aRhs.mFill)
-{
-  mFunction = AnimationUtils::ParseEasing(aTarget, aRhs.mEasing);
-}
-
-TimingParams::TimingParams(double aDuration)
-{
-  mDuration.SetAsUnrestrictedDouble() = aDuration;
-}
-
-template <class OptionsType>
-static const dom::AnimationEffectTimingProperties&
-GetTimingProperties(const OptionsType& aOptions);
-
-template <>
-/* static */ const dom::AnimationEffectTimingProperties&
-GetTimingProperties(
-  const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions)
-{
-  MOZ_ASSERT(aOptions.IsKeyframeEffectOptions());
-  return aOptions.GetAsKeyframeEffectOptions();
-}
-
-template <>
-/* static */ const dom::AnimationEffectTimingProperties&
-GetTimingProperties(
-  const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions)
-{
-  MOZ_ASSERT(aOptions.IsKeyframeAnimationOptions());
-  return aOptions.GetAsKeyframeAnimationOptions();
-}
-
-template <class OptionsType>
-static TimingParams
-TimingParamsFromOptionsUnion(
-  const OptionsType& aOptions,
-  const Nullable<dom::ElementOrCSSPseudoElement>& aTarget)
-{
-  if (aOptions.IsUnrestrictedDouble()) {
-    return TimingParams(aOptions.GetAsUnrestrictedDouble());
-  } else {
-    // If aTarget is a pseudo element, we pass its parent element because
-    // TimingParams only needs its owner doc to parse easing and both pseudo
-    // element and its parent element should have the same owner doc.
-    // Bug 1246320: Avoid passing the element for parsing the timing function
-    RefPtr<dom::Element> targetElement;
-    if (!aTarget.IsNull()) {
-      const dom::ElementOrCSSPseudoElement& target = aTarget.Value();
-      MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
-                 "Uninitialized target");
-      if (target.IsElement()) {
-        targetElement = &target.GetAsElement();
-      } else {
-        targetElement = target.GetAsCSSPseudoElement().ParentElement();
-      }
-    }
-    return TimingParams(GetTimingProperties(aOptions), targetElement);
-  }
-}
-
-/* static */ TimingParams
-TimingParams::FromOptionsUnion(
-  const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
-  const Nullable<dom::ElementOrCSSPseudoElement>& aTarget)
-{
-  return TimingParamsFromOptionsUnion(aOptions, aTarget);
-}
-
-/* static */ TimingParams
-TimingParams::FromOptionsUnion(
-  const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
-  const Nullable<dom::ElementOrCSSPseudoElement>& aTarget)
-{
-  return TimingParamsFromOptionsUnion(aOptions, aTarget);
-}
-
-bool
-TimingParams::operator==(const TimingParams& aOther) const
-{
-  bool durationEqual;
-  if (mDuration.IsUnrestrictedDouble()) {
-    durationEqual = aOther.mDuration.IsUnrestrictedDouble() &&
-                    (mDuration.GetAsUnrestrictedDouble() ==
-                     aOther.mDuration.GetAsUnrestrictedDouble());
-  } else {
-    // We consider all string values and uninitialized values as meaning "auto".
-    // Since mDuration is either a string or uninitialized, we consider it equal
-    // if aOther.mDuration is also either a string or uninitialized.
-    durationEqual = !aOther.mDuration.IsUnrestrictedDouble();
-  }
-  return durationEqual &&
-         mDelay == aOther.mDelay &&
-         mIterations == aOther.mIterations &&
-         mIterationStart == aOther.mIterationStart &&
-         mDirection == aOther.mDirection &&
-         mFill == aOther.mFill &&
-         mFunction == aOther.mFunction;
-}
-
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(AnimationEffectTimingReadOnly, mParent)
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(AnimationEffectTimingReadOnly, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(AnimationEffectTimingReadOnly, Release)
 
 JSObject*
 AnimationEffectTimingReadOnly::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return AnimationEffectTimingReadOnlyBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
+AnimationEffectTimingReadOnly::GetDuration(
+    OwningUnrestrictedDoubleOrString& aRetVal) const
+{
+  if (mTiming.mDuration) {
+    aRetVal.SetAsUnrestrictedDouble() = mTiming.mDuration->ToMilliseconds();
+  } else {
+    aRetVal.SetAsString().AssignLiteral("auto");
+  }
+}
+
+void
 AnimationEffectTimingReadOnly::GetEasing(nsString& aRetVal) const
 {
   if (mTiming.mFunction.isSome()) {
     mTiming.mFunction->AppendToString(aRetVal);
   } else {
     aRetVal.AssignLiteral("linear");
   }
 }
--- a/dom/animation/AnimationEffectTimingReadOnly.h
+++ b/dom/animation/AnimationEffectTimingReadOnly.h
@@ -5,71 +5,23 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_AnimationEffectTimingReadOnly_h
 #define mozilla_dom_AnimationEffectTimingReadOnly_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/TimingParams.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsWrapperCache.h"
 
-// X11 has a #define for None
-#ifdef None
-#undef None
-#endif
-#include "mozilla/dom/AnimationEffectReadOnlyBinding.h"  // for FillMode
-                                                         // and PlaybackDirection
-
 namespace mozilla {
-
-namespace dom {
-struct AnimationEffectTimingProperties;
-class Element;
-class UnrestrictedDoubleOrKeyframeEffectOptions;
-class UnrestrictedDoubleOrKeyframeAnimationOptions;
-class ElementOrCSSPseudoElement;
-}
-
-struct TimingParams
-{
-  TimingParams() = default;
-  TimingParams(const dom::AnimationEffectTimingProperties& aTimingProperties,
-               const dom::Element* aTarget);
-  explicit TimingParams(double aDuration);
-
-  static TimingParams FromOptionsUnion(
-    const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
-    const Nullable<dom::ElementOrCSSPseudoElement>& aTarget);
-  static TimingParams FromOptionsUnion(
-    const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
-    const Nullable<dom::ElementOrCSSPseudoElement>& aTarget);
-
-  // The unitialized state of mDuration represents "auto".
-  // Bug 1237173: We will replace this with Maybe<TimeDuration>.
-  dom::OwningUnrestrictedDoubleOrString mDuration;
-  TimeDuration mDelay;      // Initializes to zero
-  TimeDuration mEndDelay;
-  double mIterations = 1.0; // Can be NaN, negative, +/-Infinity
-  double mIterationStart = 0.0;
-  dom::PlaybackDirection mDirection = dom::PlaybackDirection::Normal;
-  dom::FillMode mFill = dom::FillMode::Auto;
-  Maybe<ComputedTimingFunction> mFunction;
-
-  bool operator==(const TimingParams& aOther) const;
-  bool operator!=(const TimingParams& aOther) const
-  {
-    return !(*this == aOther);
-  }
-};
-
-
 namespace dom {
 
 class AnimationEffectTimingReadOnly : public nsWrapperCache
 {
 public:
   AnimationEffectTimingReadOnly() = default;
   explicit AnimationEffectTimingReadOnly(const TimingParams& aTiming)
     : mTiming(aTiming) { }
@@ -84,20 +36,17 @@ public:
   nsISupports* GetParentObject() const { return mParent; }
   JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   double Delay() const { return mTiming.mDelay.ToMilliseconds(); }
   double EndDelay() const { return mTiming.mEndDelay.ToMilliseconds(); }
   FillMode Fill() const { return mTiming.mFill; }
   double IterationStart() const { return mTiming.mIterationStart; }
   double Iterations() const { return mTiming.mIterations; }
-  void GetDuration(OwningUnrestrictedDoubleOrString& aRetVal) const
-  {
-    aRetVal = mTiming.mDuration;
-  }
+  void GetDuration(OwningUnrestrictedDoubleOrString& aRetVal) const;
   PlaybackDirection Direction() const { return mTiming.mDirection; }
   void GetEasing(nsString& aRetVal) const;
 
   const TimingParams& AsTimingParams() const { return mTiming; }
   void SetTimingParams(const TimingParams& aTiming) { mTiming = aTiming; }
 
   virtual void Unlink() { }
 
--- a/dom/animation/KeyframeEffect.cpp
+++ b/dom/animation/KeyframeEffect.cpp
@@ -233,22 +233,22 @@ KeyframeEffectReadOnly::GetComputedTimin
                           const Nullable<TimeDuration>& aLocalTime,
                           const TimingParams& aTiming)
 {
   const StickyTimeDuration zeroDuration;
 
   // Always return the same object to benefit from return-value optimization.
   ComputedTiming result;
 
-  if (aTiming.mDuration.IsUnrestrictedDouble()) {
-    double durationMs = aTiming.mDuration.GetAsUnrestrictedDouble();
-    if (!IsNaN(durationMs) && durationMs >= 0.0f) {
-      result.mDuration = StickyTimeDuration::FromMilliseconds(durationMs);
-    }
+  if (aTiming.mDuration) {
+    MOZ_ASSERT(aTiming.mDuration.ref() >= zeroDuration,
+               "Iteration duration should be positive");
+    result.mDuration = aTiming.mDuration.ref();
   }
+
   result.mIterations = IsNaN(aTiming.mIterations) || aTiming.mIterations < 0.0f ?
                        1.0f :
                        aTiming.mIterations;
   result.mIterationStart = std::max(aTiming.mIterationStart, 0.0);
 
   result.mActiveDuration = ActiveDuration(result.mDuration, result.mIterations);
   result.mEndTime = aTiming.mDelay + result.mActiveDuration +
                     aTiming.mEndDelay;
--- a/dom/animation/KeyframeEffect.h
+++ b/dom/animation/KeyframeEffect.h
@@ -14,18 +14,19 @@
 #include "mozilla/AnimationPerformanceWarning.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/ComputedTimingFunction.h" // ComputedTimingFunction
 #include "mozilla/LayerAnimationInfo.h"     // LayerAnimations::kRecords
 #include "mozilla/OwningNonNull.h"          // OwningNonNull<...>
 #include "mozilla/StickyTimeDuration.h"
 #include "mozilla/StyleAnimationValue.h"
 #include "mozilla/TimeStamp.h"
+#include "mozilla/TimingParams.h"
 #include "mozilla/dom/AnimationEffectReadOnly.h"
-#include "mozilla/dom/AnimationEffectTimingReadOnly.h" // TimingParams
+#include "mozilla/dom/AnimationEffectTimingReadOnly.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/KeyframeBinding.h"
 #include "mozilla/dom/Nullable.h"
 
 
 struct JSContext;
 class nsCSSPropertySet;
 class nsIContent;
@@ -198,19 +199,23 @@ public:
   // KeyframeEffectReadOnly interface
   static already_AddRefed<KeyframeEffectReadOnly>
   Constructor(const GlobalObject& aGlobal,
               const Nullable<ElementOrCSSPseudoElement>& aTarget,
               JS::Handle<JSObject*> aFrames,
               const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
               ErrorResult& aRv)
   {
+    TimingParams timingParams =
+      TimingParams::FromOptionsUnion(aOptions, aTarget, aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
     return ConstructKeyframeEffect<KeyframeEffectReadOnly>(
-      aGlobal, aTarget, aFrames,
-      TimingParams::FromOptionsUnion(aOptions, aTarget), aRv);
+             aGlobal, aTarget, aFrames, timingParams, aRv);
   }
 
   void GetTarget(Nullable<OwningElementOrCSSPseudoElement>& aRv) const;
   void GetFrames(JSContext*& aCx,
                  nsTArray<JSObject*>& aResult,
                  ErrorResult& aRv);
 
   // Temporary workaround to return both the target element and pseudo-type
@@ -425,19 +430,23 @@ public:
 
   static already_AddRefed<KeyframeEffect>
   Constructor(const GlobalObject& aGlobal,
               const Nullable<ElementOrCSSPseudoElement>& aTarget,
               JS::Handle<JSObject*> aFrames,
               const UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
               ErrorResult& aRv)
   {
+    TimingParams timingParams =
+      TimingParams::FromOptionsUnion(aOptions, aTarget, aRv);
+    if (aRv.Failed()) {
+      return nullptr;
+    }
     return ConstructKeyframeEffect<KeyframeEffect>(
-      aGlobal, aTarget, aFrames,
-      TimingParams::FromOptionsUnion(aOptions, aTarget), aRv);
+      aGlobal, aTarget, aFrames, timingParams, aRv);
   }
 
   // More generalized version for Animatable.animate.
   // Not exposed to content.
   static already_AddRefed<KeyframeEffect>
   inline Constructor(const GlobalObject& aGlobal,
                      const Nullable<ElementOrCSSPseudoElement>& aTarget,
                      JS::Handle<JSObject*> aFrames,
new file mode 100644
--- /dev/null
+++ b/dom/animation/TimingParams.cpp
@@ -0,0 +1,115 @@
+/* -*- 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 "mozilla/TimingParams.h"
+
+namespace mozilla {
+
+template <class OptionsType>
+static const dom::AnimationEffectTimingProperties&
+GetTimingProperties(const OptionsType& aOptions);
+
+template <>
+/* static */ const dom::AnimationEffectTimingProperties&
+GetTimingProperties(
+  const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions)
+{
+  MOZ_ASSERT(aOptions.IsKeyframeEffectOptions());
+  return aOptions.GetAsKeyframeEffectOptions();
+}
+
+template <>
+/* static */ const dom::AnimationEffectTimingProperties&
+GetTimingProperties(
+  const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions)
+{
+  MOZ_ASSERT(aOptions.IsKeyframeAnimationOptions());
+  return aOptions.GetAsKeyframeAnimationOptions();
+}
+
+template <class OptionsType>
+static TimingParams
+TimingParamsFromOptionsUnion(
+  const OptionsType& aOptions,
+  const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
+  ErrorResult& aRv)
+{
+  TimingParams result;
+  if (aOptions.IsUnrestrictedDouble()) {
+    double durationInMs = aOptions.GetAsUnrestrictedDouble();
+    if (durationInMs >= 0) {
+      result.mDuration.emplace(
+        StickyTimeDuration::FromMilliseconds(durationInMs));
+    } else {
+      aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+    }
+  } else {
+    // If aTarget is a pseudo element, we pass its parent element because
+    // TimingParams only needs its owner doc to parse easing and both pseudo
+    // element and its parent element should have the same owner doc.
+    // Bug 1246320: Avoid passing the element for parsing the timing function
+    RefPtr<dom::Element> targetElement;
+    if (!aTarget.IsNull()) {
+      const dom::ElementOrCSSPseudoElement& target = aTarget.Value();
+      MOZ_ASSERT(target.IsElement() || target.IsCSSPseudoElement(),
+                 "Uninitialized target");
+      if (target.IsElement()) {
+        targetElement = &target.GetAsElement();
+      } else {
+        targetElement = target.GetAsCSSPseudoElement().ParentElement();
+      }
+    }
+    const dom::AnimationEffectTimingProperties& timing =
+      GetTimingProperties(aOptions);
+    Maybe<StickyTimeDuration> duration =
+      TimingParams::ParseDuration(timing.mDuration, aRv);
+    if (aRv.Failed()) {
+      return result;
+    }
+    result.mDuration = duration;
+    result.mDelay = TimeDuration::FromMilliseconds(timing.mDelay);
+    result.mEndDelay = TimeDuration::FromMilliseconds(timing.mEndDelay);
+    result.mIterations = timing.mIterations;
+    result.mIterationStart = timing.mIterationStart;
+    result.mDirection = timing.mDirection;
+    result.mFill = timing.mFill;
+    result.mFunction =
+      AnimationUtils::ParseEasing(targetElement, timing.mEasing);
+  }
+  return result;
+}
+
+/* static */ TimingParams
+TimingParams::FromOptionsUnion(
+  const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
+  const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
+  ErrorResult& aRv)
+{
+  return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv);
+}
+
+/* static */ TimingParams
+TimingParams::FromOptionsUnion(
+  const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
+  const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
+  ErrorResult& aRv)
+{
+  return TimingParamsFromOptionsUnion(aOptions, aTarget, aRv);
+}
+
+bool
+TimingParams::operator==(const TimingParams& aOther) const
+{
+  return mDuration == aOther.mDuration &&
+         mDelay == aOther.mDelay &&
+         mIterations == aOther.mIterations &&
+         mIterationStart == aOther.mIterationStart &&
+         mDirection == aOther.mDirection &&
+         mFill == aOther.mFill &&
+         mFunction == aOther.mFunction;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/animation/TimingParams.h
@@ -0,0 +1,86 @@
+/* -*- 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_TimingParams_h
+#define mozilla_TimingParams_h
+
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/UnionTypes.h" // For OwningUnrestrictedDoubleOrString
+#include "mozilla/ComputedTimingFunction.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/TimeStamp.h" // for TimeDuration
+
+// X11 has a #define for None
+#ifdef None
+#undef None
+#endif
+#include "mozilla/dom/AnimationEffectReadOnlyBinding.h"  // for FillMode
+                                                         // and PlaybackDirection
+
+namespace mozilla {
+
+namespace dom {
+struct AnimationEffectTimingProperties;
+class Element;
+class UnrestrictedDoubleOrKeyframeEffectOptions;
+class UnrestrictedDoubleOrKeyframeAnimationOptions;
+class ElementOrCSSPseudoElement;
+}
+
+struct TimingParams
+{
+  TimingParams() = default;
+
+  static TimingParams FromOptionsUnion(
+    const dom::UnrestrictedDoubleOrKeyframeEffectOptions& aOptions,
+    const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
+    ErrorResult& aRv);
+  static TimingParams FromOptionsUnion(
+    const dom::UnrestrictedDoubleOrKeyframeAnimationOptions& aOptions,
+    const Nullable<dom::ElementOrCSSPseudoElement>& aTarget,
+    ErrorResult& aRv);
+
+  // Range-checks and validates an UnrestrictedDoubleOrString or
+  // OwningUnrestrictedDoubleOrString object and converts to a
+  // StickyTimeDuration value or Nothing() if aDuration is "auto".
+  // Caller must check aRv.Failed().
+  template <class DoubleOrString>
+  static Maybe<StickyTimeDuration> ParseDuration(DoubleOrString& aDuration,
+                                                 ErrorResult& aRv) {
+    Maybe<StickyTimeDuration> result;
+    if (aDuration.IsUnrestrictedDouble()) {
+      double durationInMs = aDuration.GetAsUnrestrictedDouble();
+      if (durationInMs >= 0) {
+        result.emplace(StickyTimeDuration::FromMilliseconds(durationInMs));
+        return result;
+      }
+    } else if (aDuration.GetAsString().EqualsLiteral("auto")) {
+      return result;
+    }
+    aRv.Throw(NS_ERROR_DOM_TYPE_ERR);
+    return result;
+  }
+
+  // mDuration.isNothing() represents the "auto" value
+  Maybe<StickyTimeDuration> mDuration;
+  TimeDuration mDelay;      // Initializes to zero
+  TimeDuration mEndDelay;
+  double mIterations = 1.0; // Can be NaN, negative, +/-Infinity
+  double mIterationStart = 0.0;
+  dom::PlaybackDirection mDirection = dom::PlaybackDirection::Normal;
+  dom::FillMode mFill = dom::FillMode::Auto;
+  Maybe<ComputedTimingFunction> mFunction;
+
+  bool operator==(const TimingParams& aOther) const;
+  bool operator!=(const TimingParams& aOther) const
+  {
+    return !(*this == aOther);
+  }
+};
+
+} // namespace mozilla
+
+#endif // mozilla_TimingParams_h
--- a/dom/animation/moz.build
+++ b/dom/animation/moz.build
@@ -23,16 +23,17 @@ EXPORTS.mozilla += [
     'AnimationPerformanceWarning.h',
     'AnimationUtils.h',
     'AnimValuesStyleRule.h',
     'ComputedTimingFunction.h',
     'EffectCompositor.h',
     'EffectSet.h',
     'PendingAnimationTracker.h',
     'PseudoElementHashEntry.h',
+    'TimingParams.h',
 ]
 
 UNIFIED_SOURCES += [
     'Animation.cpp',
     'AnimationEffectReadOnly.cpp',
     'AnimationEffectTiming.cpp',
     'AnimationEffectTimingReadOnly.cpp',
     'AnimationPerformanceWarning.cpp',
@@ -41,16 +42,17 @@ UNIFIED_SOURCES += [
     'AnimValuesStyleRule.cpp',
     'ComputedTimingFunction.cpp',
     'CSSPseudoElement.cpp',
     'DocumentTimeline.cpp',
     'EffectCompositor.cpp',
     'EffectSet.cpp',
     'KeyframeEffect.cpp',
     'PendingAnimationTracker.cpp',
+    'TimingParams.cpp',
 ]
 
 LOCAL_INCLUDES += [
     '/dom/base',
     '/layout/base',
     '/layout/style',
 ]
 
--- a/dom/animation/test/chrome/test_restyles.html
+++ b/dom/animation/test/chrome/test_restyles.html
@@ -343,29 +343,32 @@ waitForAllPaints(function() {
     is(markers.length, 1,
        'Bug 1235478: Animations running on the compositor should only once ' +
        'update style when currentTime is set to middle of duration time');
     yield ensureElementRemoval(div);
   });
 
   add_task_if_omta_enabled(function* change_duration_and_currenttime() {
     var div = addDiv(null);
-    var animation = div.animate({ opacity: [ 0, 1 ] }, 10000);
+    var animation = div.animate({ opacity: [ 0, 1 ] }, 100000); //100s
 
     yield animation.ready;
     ok(animation.isRunningOnCompositor);
 
-    animation.currentTime = 50000;
+    // Set currentTime to a time longer than duration.
+    animation.currentTime = 500000; // 500s
 
+    // Now the animation immediately get back from compositor.
     ok(!animation.isRunningOnCompositor);
 
-    animation.effect.timing.duration = 100000;
+    // Extend the duration.
+    animation.effect.timing.duration = 800000; // 800s
     var markers = yield observeStyling(5);
     is(markers.length, 1,
-       'Animations running on the compositor should update style' +
+       'Animations running on the compositor should update style ' +
        'when timing.duration is made longer than the current time');
 
     yield ensureElementRemoval(div);
   });
 
 });
 
 </script>
--- a/dom/apps/AppsUtils.jsm
+++ b/dom/apps/AppsUtils.jsm
@@ -368,25 +368,16 @@ this.AppsUtils = {
     // Instead, we check if the app is installed under /system/b2g
     let isCoreApp = false;
 
 #ifdef MOZ_WIDGET_GONK
     isCoreApp = app.basePath == this.getCoreAppsBasePath();
 #endif
     debug(app.basePath + " isCoreApp: " + isCoreApp);
 
-    // Before bug 910473, this is a temporary workaround to get correct path
-    // from child process in mochitest.
-    let prefName = "dom.mozApps.auto_confirm_install";
-    if (Services.prefs.prefHasUserValue(prefName) &&
-        Services.prefs.getBoolPref(prefName)) {
-      return { "path": app.basePath + "/" + app.id,
-               "isCoreApp": isCoreApp };
-    }
-
     return { "path": app.basePath + "/" + app.id,
              "isCoreApp": isCoreApp };
   },
 
   /**
     * Remove potential HTML tags from displayable fields in the manifest.
     * We check name, description, developer name, and permission description
     */
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -3348,19 +3348,24 @@ Element::Animate(const Nullable<ElementO
   if (js::GetContextCompartment(aContext) !=
       js::GetObjectCompartment(ownerGlobal->GetGlobalJSObject())) {
     ac.emplace(aContext, ownerGlobal->GetGlobalJSObject());
     if (!JS_WrapObject(aContext, &frames)) {
       return nullptr;
     }
   }
 
+  TimingParams timingParams =
+    TimingParams::FromOptionsUnion(aOptions, aTarget, aError);
+  if (aError.Failed()) {
+    return nullptr;
+  }
+
   RefPtr<KeyframeEffect> effect =
-    KeyframeEffect::Constructor(global, aTarget, frames,
-      TimingParams::FromOptionsUnion(aOptions, aTarget), aError);
+    KeyframeEffect::Constructor(global, aTarget, frames, timingParams, aError);
   if (aError.Failed()) {
     return nullptr;
   }
 
   RefPtr<Animation> animation =
     Animation::Constructor(global, effect,
                            referenceElement->OwnerDoc()->Timeline(), aError);
   if (aError.Failed()) {
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -329,16 +329,17 @@ AutoJSAPI::~AutoJSAPI()
   }
 }
 
 void
 AutoJSAPI::InitInternal(JSObject* aGlobal, JSContext* aCx, bool aIsMainThread)
 {
   MOZ_ASSERT(aCx);
   MOZ_ASSERT(aIsMainThread == NS_IsMainThread());
+  MOZ_ASSERT(!JS_IsExceptionPending(aCx));
 
   mCx = aCx;
   mIsMainThread = aIsMainThread;
   if (aIsMainThread) {
     // This Rooted<> is necessary only as long as AutoCxPusher::AutoCxPusher
     // can GC, which is only possible because XPCJSContextStack::Push calls
     // nsIPrincipal.Equals. Once that is removed, the Rooted<> will no longer
     // be necessary.
@@ -409,24 +410,16 @@ AutoJSAPI::Init(nsIGlobalObject* aGlobal
 
 bool
 AutoJSAPI::Init(JSObject* aObject)
 {
   return Init(xpc::NativeGlobal(aObject));
 }
 
 bool
-AutoJSAPI::InitWithLegacyErrorReporting(nsIGlobalObject* aGlobalObject)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  return Init(aGlobalObject, FindJSContext(aGlobalObject));
-}
-
-bool
 AutoJSAPI::Init(nsPIDOMWindowInner* aWindow, JSContext* aCx)
 {
   return Init(nsGlobalWindow::Cast(aWindow), aCx);
 }
 
 bool
 AutoJSAPI::Init(nsPIDOMWindowInner* aWindow)
 {
@@ -440,28 +433,16 @@ AutoJSAPI::Init(nsGlobalWindow* aWindow,
 }
 
 bool
 AutoJSAPI::Init(nsGlobalWindow* aWindow)
 {
   return Init(static_cast<nsIGlobalObject*>(aWindow));
 }
 
-bool
-AutoJSAPI::InitWithLegacyErrorReporting(nsPIDOMWindowInner* aWindow)
-{
-  return InitWithLegacyErrorReporting(nsGlobalWindow::Cast(aWindow));
-}
-
-bool
-AutoJSAPI::InitWithLegacyErrorReporting(nsGlobalWindow* aWindow)
-{
-  return InitWithLegacyErrorReporting(static_cast<nsIGlobalObject*>(aWindow));
-}
-
 // Even with autoJSAPIOwnsErrorReporting, the JS engine still sends warning
 // reports to the JSErrorReporter as soon as they are generated. These go
 // directly to the console, so we can handle them easily here.
 //
 // Eventually, SpiderMonkey will have a special-purpose callback for warnings
 // only.
 void
 WarningOnlyErrorReporter(JSContext* aCx, const char* aMessage, JSErrorReport* aRep)
@@ -544,17 +525,17 @@ AutoJSAPI::ReportException()
       nsPIDOMWindowInner* inner = win ? win->AsInner() : nullptr;
       xpcReport->Init(jsReport.report(), jsReport.message(),
                       nsContentUtils::IsCallerChrome(),
                       inner ? inner->WindowID() : 0);
       if (inner && jsReport.report()->errorNumber != JSMSG_OUT_OF_MEMORY) {
         DispatchScriptErrorEvent(inner, JS_GetRuntime(cx()), xpcReport, exn);
       } else {
         JS::Rooted<JSObject*> stack(cx(),
-          xpc::FindExceptionStackForConsoleReport(cx(), inner, exn));
+          xpc::FindExceptionStackForConsoleReport(inner, exn));
         xpcReport->LogToConsoleWithStack(stack);
       }
     } else {
       // On a worker, we just use the worker error reporting mechanism and don't
       // bother with xpc::ErrorReport.  This will ensure that all the right
       // events (which are a lot more complicated than in the window case) get
       // fired.
       workers::WorkerPrivate* worker = workers::GetCurrentThreadWorkerPrivate();
@@ -607,16 +588,26 @@ AutoEntryScript::AutoEntryScript(nsIGlob
 {
   MOZ_ASSERT(aGlobalObject);
   MOZ_ASSERT_IF(!aCx, aIsMainThread); // cx is mandatory off-main-thread.
   MOZ_ASSERT_IF(aCx && aIsMainThread, aCx == FindJSContext(aGlobalObject));
 
   if (aIsMainThread && gRunToCompletionListeners > 0) {
     mDocShellEntryMonitor.emplace(cx(), aReason);
   }
+
+  TakeOwnershipOfErrorReporting();
+}
+
+AutoEntryScript::AutoEntryScript(JSObject* aObject,
+                                 const char *aReason,
+                                 bool aIsMainThread,
+                                 JSContext* aCx)
+  : AutoEntryScript(xpc::NativeGlobal(aObject), aReason, aIsMainThread, aCx)
+{
 }
 
 AutoEntryScript::~AutoEntryScript()
 {
   // GC when we pop a script entry point. This is a useful heuristic that helps
   // us out on certain (flawed) benchmarks like sunspider, because it lets us
   // avoid GCing during the timing loop.
   JS_MaybeGC(cx());
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -234,37 +234,24 @@ public:
   bool Init(JSObject* aObject);
 
   // Unsurprisingly, this uses aCx and enters the compartment of aGlobalObject.
   // If aGlobalObject or its associated JS global are null then it returns
   // false and use of cx() will cause an assertion.
   // If aCx is null it will cause an assertion.
   bool Init(nsIGlobalObject* aGlobalObject, JSContext* aCx);
 
-  // This may only be used on the main thread.
-  // This attempts to use the JSContext associated with aGlobalObject, otherwise
-  // it uses the SafeJSContext. It then enters the compartment of aGlobalObject.
-  // This means that existing error reporting mechanisms that use the JSContext
-  // to find the JSErrorReporter should still work as before.
-  // We should be able to remove this around bug 981198.
-  // If aGlobalObject or its associated JS global are null then it returns
-  // false and use of cx() will cause an assertion.
-  bool InitWithLegacyErrorReporting(nsIGlobalObject* aGlobalObject);
-
   // Convenience functions to take an nsPIDOMWindow* or nsGlobalWindow*,
   // when it is more easily available than an nsIGlobalObject.
   bool Init(nsPIDOMWindowInner* aWindow);
   bool Init(nsPIDOMWindowInner* aWindow, JSContext* aCx);
 
   bool Init(nsGlobalWindow* aWindow);
   bool Init(nsGlobalWindow* aWindow, JSContext* aCx);
 
-  bool InitWithLegacyErrorReporting(nsPIDOMWindowInner* aWindow);
-  bool InitWithLegacyErrorReporting(nsGlobalWindow* aWindow);
-
   JSContext* cx() const {
     MOZ_ASSERT(mCx, "Must call Init before using an AutoJSAPI");
     MOZ_ASSERT_IF(mIsMainThread, CxPusherIsStackTop());
     return mCx;
   }
 
   bool CxPusherIsStackTop() const { return mCxPusher->IsStackTop(); }
 
@@ -341,16 +328,22 @@ class MOZ_STACK_CLASS AutoEntryScript : 
                                         protected ScriptSettingsStackEntry {
 public:
   AutoEntryScript(nsIGlobalObject* aGlobalObject,
                   const char *aReason,
                   bool aIsMainThread = NS_IsMainThread(),
                   // Note: aCx is mandatory off-main-thread.
                   JSContext* aCx = nullptr);
 
+  AutoEntryScript(JSObject* aObject, // Any object from the relevant global
+                  const char *aReason,
+                  bool aIsMainThread = NS_IsMainThread(),
+                  // Note: aCx is mandatory off-main-thread.
+                  JSContext* aCx = nullptr);
+
   ~AutoEntryScript();
 
   void SetWebIDLCallerPrincipal(nsIPrincipal *aPrincipal) {
     mWebIDLCallerPrincipal = aPrincipal;
   }
 
 private:
   // A subclass of AutoEntryMonitor that notifies the docshell.
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -144,23 +144,19 @@ public:
                            const char16_t* aError,
                            const char16_t** aFormatStrings,
                            uint32_t aFormatStringsLen);
 
   nsresult DoOnMessageAvailable(const nsACString& aMsg,
                                 bool isBinary);
 
   // ConnectionCloseEvents: 'error' event if needed, then 'close' event.
-  // - These must not be dispatched while we are still within an incoming call
-  //   from JS (ex: close()).  Set 'sync' to false in that case to dispatch in a
-  //   separate new event.
   nsresult ScheduleConnectionCloseEvents(nsISupports* aContext,
-                                         nsresult aStatusCode,
-                                         bool sync);
-  // 2nd half of ScheduleConnectionCloseEvents, sometimes run in its own event.
+                                         nsresult aStatusCode);
+  // 2nd half of ScheduleConnectionCloseEvents, run in its own event.
   void DispatchConnectionCloseEvents();
 
   nsresult UpdateURI();
 
   void AddRefObject();
   void ReleaseObject();
 
   bool RegisterFeature();
@@ -511,24 +507,21 @@ WebSocketImpl::CloseConnection(uint16_t 
              "Should only get here for early websocket cancel/error");
 
   // Server won't be sending us a close code, so use what's passed in here.
   mCloseEventCode = aReasonCode;
   CopyUTF8toUTF16(aReasonString, mCloseEventReason);
 
   mWebSocket->SetReadyState(WebSocket::CLOSING);
 
-  // Can be called from Cancel() or Init() codepaths, so need to dispatch
-  // onerror/onclose asynchronously
   ScheduleConnectionCloseEvents(
                     nullptr,
                     (aReasonCode == nsIWebSocketChannel::CLOSE_NORMAL ||
                      aReasonCode == nsIWebSocketChannel::CLOSE_GOING_AWAY) ?
-                     NS_OK : NS_ERROR_FAILURE,
-                    false);
+                     NS_OK : NS_ERROR_FAILURE);
 
   return NS_OK;
 }
 
 nsresult
 WebSocketImpl::ConsoleError()
 {
   AssertIsOnTargetThread();
@@ -792,24 +785,22 @@ WebSocketImpl::OnStop(nsISupports* aCont
   }
 
   // We can be CONNECTING here if connection failed.
   // We can be OPEN if we have encountered a fatal protocol error
   // We can be CLOSING if close() was called and/or server initiated close.
   MOZ_ASSERT(mWebSocket->ReadyState() != WebSocket::CLOSED,
              "Shouldn't already be CLOSED when OnStop called");
 
-  // called by network stack, not JS, so can dispatch JS events synchronously
-  return ScheduleConnectionCloseEvents(aContext, aStatusCode, true);
+  return ScheduleConnectionCloseEvents(aContext, aStatusCode);
 }
 
 nsresult
 WebSocketImpl::ScheduleConnectionCloseEvents(nsISupports* aContext,
-                                             nsresult aStatusCode,
-                                             bool sync)
+                                             nsresult aStatusCode)
 {
   AssertIsOnTargetThread();
 
   // no-op if some other code has already initiated close event
   if (!mOnCloseScheduled) {
     mCloseEventWasClean = NS_SUCCEEDED(aStatusCode);
 
     if (aStatusCode == NS_BASE_STREAM_CLOSED) {
@@ -819,21 +810,17 @@ WebSocketImpl::ScheduleConnectionCloseEv
 
     if (NS_FAILED(aStatusCode)) {
       ConsoleError();
       mFailed = true;
     }
 
     mOnCloseScheduled = true;
 
-    if (sync) {
-      DispatchConnectionCloseEvents();
-    } else {
-      NS_DispatchToCurrentThread(new CallDispatchConnectionCloseEvents(this));
-    }
+    NS_DispatchToCurrentThread(new CallDispatchConnectionCloseEvents(this));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 WebSocketImpl::OnAcknowledge(nsISupports *aContext, uint32_t aSize)
 {
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -1950,17 +1950,16 @@ nsFrameLoader::MaybeCreateDocShell()
     if (containingApp) {
       NS_ENSURE_SUCCESS(containingApp->GetLocalId(&containingAppId),
                         NS_ERROR_FAILURE);
     }
 
     attrs.mAppId = containingAppId;
     attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
     mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_BROWSER);
-    mDocShell->SetIsInIsolatedMozBrowserElement(OwnerIsIsolatedMozBrowserFrame());
   }
 
   // Grab the userContextId from owner if XUL
   nsAutoString userContextIdStr;
   if ((namespaceID == kNameSpaceID_XUL) &&
       mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, userContextIdStr) &&
       !userContextIdStr.IsEmpty()) {
     nsresult rv;
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1098,27 +1098,17 @@ nsFrameMessageManager::ReceiveMessage(ns
       if (!wrappedJS) {
         continue;
       }
 
       if (!wrappedJS->GetJSObject()) {
         continue;
       }
 
-      // Note - The ergonomics here will get a lot better with bug 971673:
-      //
-      // AutoEntryScript aes;
-      // if (!aes.Init(wrappedJS->GetJSObject())) {
-      //   continue;
-      // }
-      // JSContext* cx = aes.cx();
-      nsIGlobalObject* nativeGlobal =
-        xpc::NativeGlobal(js::GetGlobalForObjectCrossCompartment(wrappedJS->GetJSObject()));
-      AutoEntryScript aes(nativeGlobal, "message manager handler");
-      aes.TakeOwnershipOfErrorReporting();
+      AutoEntryScript aes(wrappedJS->GetJSObject(), "message manager handler");
       JSContext* cx = aes.cx();
       JS::Rooted<JSObject*> object(cx, wrappedJS->GetJSObject());
 
       // The parameter for the listener function.
       JS::Rooted<JSObject*> param(cx, JS_NewPlainObject(cx));
       NS_ENSURE_TRUE(param, NS_ERROR_OUT_OF_MEMORY);
 
       JS::Rooted<JS::Value> targetv(cx);
@@ -1690,19 +1680,17 @@ nsMessageManagerScriptExecutor::LoadScri
     // with a different WillRunInGlobalScope() value.
     bool shouldCache = !holder;
     TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope,
                                  shouldCache, &script);
   }
 
   JS::Rooted<JSObject*> global(rt, mGlobal->GetJSObject());
   if (global) {
-    AutoEntryScript aes(xpc::NativeGlobal(global),
-                        "message manager script load");
-    aes.TakeOwnershipOfErrorReporting();
+    AutoEntryScript aes(global, "message manager script load");
     JSContext* cx = aes.cx();
     if (script) {
       if (aRunInGlobalScope) {
         JS::CloneAndExecuteScript(cx, script);
       } else {
         JS::Rooted<JSObject*> scope(cx);
         bool ok = js::ExecuteInGlobalAndReturnScope(cx, global, script, &scope);
         if (ok) {
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -3416,19 +3416,19 @@ nsGlobalWindow::SetArguments(nsIArray *a
 nsresult
 nsGlobalWindow::DefineArgumentsProperty(nsIArray *aArguments)
 {
   MOZ_ASSERT(IsInnerWindow());
   MOZ_ASSERT(!mIsModalContentWindow); // Handled separately.
 
   nsIScriptContext *ctx = GetOuterWindowInternal()->mContext;
   NS_ENSURE_TRUE(aArguments && ctx, NS_ERROR_NOT_INITIALIZED);
-  AutoJSContext cx;
-
-  JS::Rooted<JSObject*> obj(cx, GetWrapperPreserveColor());
+
+  JS::Rooted<JSObject*> obj(nsContentUtils::RootingCx(),
+                            GetWrapperPreserveColor());
   return ctx->SetProperty(obj, "arguments", aArguments);
 }
 
 //*****************************************************************************
 // nsGlobalWindow::nsIScriptObjectPrincipal
 //*****************************************************************************
 
 nsIPrincipal*
@@ -11910,23 +11910,22 @@ nsGlobalWindow::RunTimeoutHandler(nsTime
 
     const char* filename = nullptr;
     uint32_t lineNo = 0, dummyColumn = 0;
     handler->GetLocation(&filename, &lineNo, &dummyColumn);
 
     // New script entry point required, due to the "Create a script" sub-step of
     // http://www.whatwg.org/specs/web-apps/current-work/#timer-initialisation-steps
     nsAutoMicroTask mt;
-    AutoEntryScript entryScript(this, reason, true, aScx->GetNativeContext());
-    entryScript.TakeOwnershipOfErrorReporting();
-    JS::CompileOptions options(entryScript.cx());
+    AutoEntryScript aes(this, reason, true, aScx->GetNativeContext());
+    JS::CompileOptions options(aes.cx());
     options.setFileAndLine(filename, lineNo)
            .setVersion(JSVERSION_DEFAULT);
-    JS::Rooted<JSObject*> global(entryScript.cx(), FastGetGlobalJSObject());
-    nsJSUtils::EvaluateString(entryScript.cx(), nsDependentString(script),
+    JS::Rooted<JSObject*> global(aes.cx(), FastGetGlobalJSObject());
+    nsJSUtils::EvaluateString(aes.cx(), nsDependentString(script),
                               global, options);
   } else {
     // Hold strong ref to ourselves while we call the callback.
     nsCOMPtr<nsISupports> me(static_cast<nsIDOMWindow *>(this));
     ErrorResult ignored;
     JS::Rooted<JS::Value> ignoredVal(CycleCollectedJSRuntime::Get()->Runtime());
     callback->Call(me, handler->GetArgs(), &ignoredVal, ignored, reason);
     ignored.SuppressException();
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -222,37 +222,38 @@ static const char*
 ProcessNameForCollectorLog()
 {
   return XRE_GetProcessType() == GeckoProcessType_Default ?
     "default" : "content";
 }
 
 namespace xpc {
 
-// This handles JS Exceptions (via ExceptionStackOrNull), as well as DOM and XPC Exceptions.
+// This handles JS Exceptions (via ExceptionStackOrNull), as well as DOM and XPC
+// Exceptions.
 //
-// Note that the returned object is _not_ wrapped into the compartment of cx.
+// Note that the returned object is _not_ wrapped into the compartment of
+// exceptionValue.
 JSObject*
-FindExceptionStackForConsoleReport(JSContext* cx,
-                                   nsPIDOMWindowInner* win,
+FindExceptionStackForConsoleReport(nsPIDOMWindowInner* win,
                                    JS::HandleValue exceptionValue)
 {
   if (!exceptionValue.isObject()) {
     return nullptr;
   }
 
   if (win && win->InnerObjectsFreed()) {
     // Pretend like we have no stack, so we don't end up keeping the global
     // alive via the stack.
     return nullptr;
   }
 
+  JSContext* cx = nsContentUtils::RootingCxForThread();
   JS::RootedObject exceptionObject(cx, &exceptionValue.toObject());
-  JSAutoCompartment ac(cx, exceptionObject);
-  JS::RootedObject stackObject(cx, ExceptionStackOrNull(cx, exceptionObject));
+  JSObject* stackObject = ExceptionStackOrNull(exceptionObject);
   if (stackObject) {
     return stackObject;
   }
 
   // It is not a JS Exception, try DOM Exception.
   RefPtr<Exception> exception;
   UNWRAP_OBJECT(DOMException, exceptionObject, exception);
   if (!exception) {
@@ -265,19 +266,19 @@ FindExceptionStackForConsoleReport(JSCon
 
   nsCOMPtr<nsIStackFrame> stack = exception->GetLocation();
   if (!stack) {
     return nullptr;
   }
   JS::RootedValue value(cx);
   stack->GetNativeSavedFrame(&value);
   if (value.isObject()) {
-    stackObject = &value.toObject();
+    return &value.toObject();
   }
-  return stackObject;
+  return nullptr;
 }
 
 } /* namespace xpc */
 
 static PRTime
 GetCollectionTimeDelta()
 {
   PRTime now = PR_Now();
@@ -430,27 +431,28 @@ public:
     , mError(aRuntime, aError)
   {}
 
   NS_IMETHOD Run()
   {
     nsEventStatus status = nsEventStatus_eIgnore;
     nsPIDOMWindowInner* win = mWindow;
     MOZ_ASSERT(win);
+    MOZ_ASSERT(NS_IsMainThread());
     // First, notify the DOM that we have a script error, but only if
     // our window is still the current inner.
+    JSContext* rootingCx = nsContentUtils::RootingCx();
     if (win->IsCurrentInnerWindow() && win->GetDocShell() && !sHandlingScriptError) {
       AutoRestore<bool> recursionGuard(sHandlingScriptError);
       sHandlingScriptError = true;
 
       RefPtr<nsPresContext> presContext;
       win->GetDocShell()->GetPresContext(getter_AddRefs(presContext));
 
-      ThreadsafeAutoJSContext cx;
-      RootedDictionary<ErrorEventInit> init(cx);
+      RootedDictionary<ErrorEventInit> init(rootingCx);
       init.mCancelable = true;
       init.mFilename = mReport->mFileName;
       init.mBubbles = true;
 
       NS_NAMED_LITERAL_STRING(xoriginMsg, "Script error.");
       if (!mReport->mIsMuted) {
         init.mMessage = mReport->mErrorMsg;
         init.mLineno = mReport->mLineNumber;
@@ -467,31 +469,19 @@ public:
                                 NS_LITERAL_STRING("error"), init);
       event->SetTrusted(true);
 
       EventDispatcher::DispatchDOMEvent(win, nullptr, event, presContext,
                                         &status);
     }
 
     if (status != nsEventStatus_eConsumeNoDefault) {
-      if (mError.isObject()) {
-        AutoJSAPI jsapi;
-        if (NS_WARN_IF(!jsapi.Init(mError.toObjectOrNull()))) {
-          mReport->LogToConsole();
-          return NS_OK;
-        }
-        JSContext* cx = jsapi.cx();
-        JS::Rooted<JS::Value> exn(cx, mError);
-        JS::RootedObject stack(cx,
-          xpc::FindExceptionStackForConsoleReport(cx, win, exn));
-        mReport->LogToConsoleWithStack(stack);
-      } else {
-        mReport->LogToConsole();
-      }
-
+      JS::Rooted<JSObject*> stack(rootingCx,
+        xpc::FindExceptionStackForConsoleReport(win, mError));
+      mReport->LogToConsoleWithStack(stack);
     }
 
     return NS_OK;
   }
 
 private:
   nsCOMPtr<nsPIDOMWindowInner>  mWindow;
   RefPtr<xpc::ErrorReport>      mReport;
@@ -560,17 +550,17 @@ SystemErrorReporter(JSContext *cx, const
 
     // If we can't dispatch an event to a window, report it to the console
     // directly. This includes the case where the error was an OOM, because
     // triggering a scripted event handler is likely to generate further OOMs.
     if (!win || JSREPORT_IS_WARNING(xpcReport->mFlags) ||
         report->errorNumber == JSMSG_OUT_OF_MEMORY)
     {
       JS::Rooted<JSObject*> stack(cx,
-        xpc::FindExceptionStackForConsoleReport(cx, win, exception));
+        xpc::FindExceptionStackForConsoleReport(win, exception));
       xpcReport->LogToConsoleWithStack(stack);
       return;
     }
 
     // Otherwise, we need to asynchronously invoke onerror before we can decide
     // whether or not to report the error to the console.
     DispatchScriptErrorEvent(win, JS_GetRuntime(cx), xpcReport, exception);
   }
@@ -844,43 +834,43 @@ nsJSContext::InitContext()
 
   return NS_OK;
 }
 
 nsresult
 nsJSContext::SetProperty(JS::Handle<JSObject*> aTarget, const char* aPropName, nsISupports* aArgs)
 {
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetGlobalObject()))) {
+  if (NS_WARN_IF(!jsapi.Init(GetGlobalObject()))) {
     return NS_ERROR_FAILURE;
   }
-  MOZ_ASSERT(jsapi.cx() == mContext,
-             "AutoJSAPI should have found our own JSContext*");
-
-  JS::AutoValueVector args(mContext);
-
-  JS::Rooted<JSObject*> global(mContext, GetWindowProxy());
+  jsapi.TakeOwnershipOfErrorReporting();
+  JSContext* cx = jsapi.cx();
+
+  JS::AutoValueVector args(cx);
+
+  JS::Rooted<JSObject*> global(cx, GetWindowProxy());
   nsresult rv =
     ConvertSupportsTojsvals(aArgs, global, args);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // got the arguments, now attach them.
 
   for (uint32_t i = 0; i < args.length(); ++i) {
-    if (!JS_WrapValue(mContext, args[i])) {
+    if (!JS_WrapValue(cx, args[i])) {
       return NS_ERROR_FAILURE;
     }
   }
 
-  JS::Rooted<JSObject*> array(mContext, ::JS_NewArrayObject(mContext, args));
+  JS::Rooted<JSObject*> array(cx, ::JS_NewArrayObject(cx, args));
   if (!array) {
     return NS_ERROR_FAILURE;
   }
 
-  return JS_DefineProperty(mContext, aTarget, aPropName, array, 0) ? NS_OK : NS_ERROR_FAILURE;
+  return JS_DefineProperty(cx, aTarget, aPropName, array, 0) ? NS_OK : NS_ERROR_FAILURE;
 }
 
 nsresult
 nsJSContext::ConvertSupportsTojsvals(nsISupports* aArgs,
                                      JS::Handle<JSObject*> aScope,
                                      JS::AutoValueVector& aArgsOut)
 {
   nsresult rv = NS_OK;
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -821,19 +821,20 @@ nsScriptLoader::AttemptAsyncScriptCompil
   }
 
   nsCOMPtr<nsIScriptGlobalObject> globalObject = GetScriptGlobalObject();
   if (!globalObject) {
     return NS_ERROR_FAILURE;
   }
 
   AutoJSAPI jsapi;
-  if (!jsapi.InitWithLegacyErrorReporting(globalObject)) {
+  if (!jsapi.Init(globalObject)) {
     return NS_ERROR_FAILURE;
   }
+  jsapi.TakeOwnershipOfErrorReporting();
 
   JSContext* cx = jsapi.cx();
   JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
   JS::CompileOptions options(cx);
   FillCompileOptionsForRequest(jsapi, aRequest, global, &options);
 
   if (!JS::CanCompileOffThread(cx, options, aRequest->mScriptTextLength)) {
     return NS_ERROR_FAILURE;
@@ -1108,20 +1109,19 @@ nsScriptLoader::EvaluateScript(nsScriptL
   JSVersion version = JSVersion(aRequest->mJSVersion);
   if (version == JSVERSION_UNKNOWN) {
     return NS_OK;
   }
 
   // New script entry point required, due to the "Create a script" sub-step of
   // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
   nsAutoMicroTask mt;
-  AutoEntryScript entryScript(globalObject, "<script> element", true,
-                              context->GetNativeContext());
-  entryScript.TakeOwnershipOfErrorReporting();
-  JS::Rooted<JSObject*> global(entryScript.cx(),
+  AutoEntryScript aes(globalObject, "<script> element", true,
+                      context->GetNativeContext());
+  JS::Rooted<JSObject*> global(aes.cx(),
                                globalObject->GetGlobalJSObject());
 
   bool oldProcessingScriptTag = context->GetProcessingScriptTag();
   context->SetProcessingScriptTag(true);
   nsresult rv;
   {
     // Update our current script.
     AutoCurrentScriptUpdater scriptUpdater(this, aRequest->mElement);
@@ -1132,19 +1132,19 @@ nsScriptLoader::EvaluateScript(nsScriptL
       // executed in the context of the master document. During the
       // execution currentScript of the master should refer to this
       // script. So let's update the mCurrentScript of the ScriptLoader
       // of the master document too.
       masterScriptUpdater.emplace(master->ScriptLoader(),
                                   aRequest->mElement);
     }
 
-    JS::CompileOptions options(entryScript.cx());
-    FillCompileOptionsForRequest(entryScript, aRequest, global, &options);
-    rv = nsJSUtils::EvaluateString(entryScript.cx(), aSrcBuf, global, options,
+    JS::CompileOptions options(aes.cx());
+    FillCompileOptionsForRequest(aes, aRequest, global, &options);
+    rv = nsJSUtils::EvaluateString(aes.cx(), aSrcBuf, global, options,
                                    aRequest->OffThreadTokenPtr());
   }
 
   context->SetProcessingScriptTag(oldProcessingScriptTag);
   return rv;
 }
 
 void
--- a/dom/base/nsXMLHttpRequest.h
+++ b/dom/base/nsXMLHttpRequest.h
@@ -202,17 +202,16 @@ public:
   nsISupports* GetParentObject()
   {
     return GetOwner();
   }
 
   // The WebIDL constructors.
   static already_AddRefed<nsXMLHttpRequest>
   Constructor(const mozilla::dom::GlobalObject& aGlobal,
-              JSContext* aCx,
               const mozilla::dom::MozXMLHttpRequestParameters& aParams,
               ErrorResult& aRv)
   {
     nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
     nsCOMPtr<nsIScriptObjectPrincipal> principal =
       do_QueryInterface(aGlobal.GetAsSupports());
     if (!global || ! principal) {
       aRv.Throw(NS_ERROR_FAILURE);
@@ -222,28 +221,27 @@ public:
     RefPtr<nsXMLHttpRequest> req = new nsXMLHttpRequest();
     req->Construct(principal->GetPrincipal(), global);
     req->InitParameters(aParams.mMozAnon, aParams.mMozSystem);
     return req.forget();
   }
 
   static already_AddRefed<nsXMLHttpRequest>
   Constructor(const mozilla::dom::GlobalObject& aGlobal,
-              JSContext* aCx,
               const nsAString& ignored,
               ErrorResult& aRv)
   {
     // Pretend like someone passed null, so we can pick up the default values
     mozilla::dom::MozXMLHttpRequestParameters params;
-    if (!params.Init(aCx, JS::NullHandleValue)) {
+    if (!params.Init(aGlobal.Context(), JS::NullHandleValue)) {
       aRv.Throw(NS_ERROR_UNEXPECTED);
       return nullptr;
     }
 
-    return Constructor(aGlobal, aCx, params, aRv);
+    return Constructor(aGlobal, params, aRv);
   }
 
   void Construct(nsIPrincipal* aPrincipal,
                  nsIGlobalObject* aGlobalObject,
                  nsIURI* aBaseURI = nullptr,
                  nsILoadGroup* aLoadGroup = nullptr)
   {
     MOZ_ASSERT(aPrincipal);
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -3222,17 +3222,16 @@ WrappedJSToDictionary(nsISupports* aObje
   NS_ENSURE_TRUE(obj, false);
 
   nsIGlobalObject* global = xpc::NativeGlobal(obj);
   NS_ENSURE_TRUE(global, false);
 
   // we need this AutoEntryScript here because the spec requires us to execute
   // getters when parsing a dictionary
   AutoEntryScript aes(global, "WebIDL dictionary creation");
-  aes.TakeOwnershipOfErrorReporting();
 
   JS::Rooted<JS::Value> v(aes.cx(), JS::ObjectValue(*obj));
   return aDictionary.Init(aes.cx(), v);
 }
 
 
 template<class T, class S>
 inline RefPtr<T>
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1626,17 +1626,17 @@ DOMInterfaces = {
     'headerFile': 'mozilla/dom/workers/bindings/Navigator.h',
     'workers': True,
     'implicitJSContext': ['getDataStores'],
 },
 
 'XMLHttpRequest': [
 {
     'nativeType': 'nsXMLHttpRequest',
-    'implicitJSContext': [ 'constructor', 'send'],
+    'implicitJSContext': [ 'send'],
 },
 {
     'workers': True,
 }],
 
 'XMLHttpRequestEventTarget': {
     'nativeType': 'nsXHREventTarget',
     'headerFile': 'nsXMLHttpRequest.h',
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -184,19 +184,16 @@ CallbackObject::CallSetup::CallSetup(Cal
   //
   // Note that if the callback is a wrapper, this will not be the same
   // compartment that we ended up in with mAutoEntryScript above, because the
   // entry point is based off of the unwrapped callback (realCallback).
   mAc.emplace(cx, *mRootedCallable);
 
   // And now we're ready to go.
   mCx = cx;
-
-  // Make sure the JS engine doesn't report exceptions we want to re-throw.
-  mAutoEntryScript->TakeOwnershipOfErrorReporting();
 }
 
 bool
 CallbackObject::CallSetup::ShouldRethrowException(JS::Handle<JS::Value> aException)
 {
   if (mExceptionHandling == eRethrowExceptions) {
     if (!mCompartment) {
       // Caller didn't ask us to filter for only exceptions we subsume.
--- a/dom/bindings/Exceptions.cpp
+++ b/dom/bindings/Exceptions.cpp
@@ -134,17 +134,17 @@ Throw(JSContext* aCx, nsresult aRv, cons
 }
 
 void
 ThrowAndReport(nsPIDOMWindowInner* aWindow, nsresult aRv)
 {
   MOZ_ASSERT(aRv != NS_ERROR_UNCATCHABLE_EXCEPTION,
              "Doesn't make sense to report uncatchable exceptions!");
   AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(aWindow))) {
+  if (NS_WARN_IF(!jsapi.Init(aWindow))) {
     return;
   }
   jsapi.TakeOwnershipOfErrorReporting();
 
   Throw(jsapi.cx(), aRv);
 }
 
 already_AddRefed<Exception>
--- a/dom/browser-element/BrowserElementChildPreload.js
+++ b/dom/browser-element/BrowserElementChildPreload.js
@@ -1045,17 +1045,17 @@ BrowserElementChild.prototype = {
       sandboxPrototype: content,
       sandboxName: "browser-api-execute-script",
       allowWaivers: false,
       sameZoneAs: content
     });
 
     try {
       let sandboxRv = Cu.evalInSandbox(data.json.args.script, sandbox, "1.8");
-      if (sandboxRv instanceof Promise) {
+      if (sandboxRv instanceof sandbox.Promise) {
         sandboxRv.then(rv => {
           if (isJSON(rv)) {
             sendSuccess(rv);
           } else {
             sendError("Value returned (resolve) by promise is not a valid JSON object");
           }
         }, error => {
           if (isJSON(error)) {
--- a/dom/crypto/test/mochitest.ini
+++ b/dom/crypto/test/mochitest.ini
@@ -16,9 +16,10 @@ skip-if = toolkit == 'android' # bug 120
 [test_WebCrypto_ECDSA.html]
 [test_WebCrypto_HKDF.html]
 [test_WebCrypto_JWK.html]
 [test_WebCrypto_Normalize.html]
 [test_WebCrypto_PBKDF2.html]
 [test_WebCrypto_Reject_Generating_Keys_Without_Usages.html]
 [test_WebCrypto_RSA_OAEP.html]
 [test_WebCrypto_RSA_PSS.html]
+[test_WebCrypto_Structured_Cloning.html]
 [test_WebCrypto_Wrap_Unwrap.html]
new file mode 100644
--- /dev/null
+++ b/dom/crypto/test/test_WebCrypto_Structured_Cloning.html
@@ -0,0 +1,305 @@
+<!DOCTYPE html>
+<html>
+
+<head>
+<title>WebCrypto Test Suite</title>
+<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
+<link rel="stylesheet" href="./test_WebCrypto.css"/>
+<script src="/tests/SimpleTest/SimpleTest.js"></script>
+
+<!-- Utilities for manipulating ABVs -->
+<script src="util.js"></script>
+
+<!-- A simple wrapper around IndexedDB -->
+<script src="simpledb.js"></script>
+
+<!-- Test vectors drawn from the literature -->
+<script src="./test-vectors.js"></script>
+
+<!-- General testing framework -->
+<script src="./test-array.js"></script>
+
+<script>/*<![CDATA[*/
+"use strict";
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: AES-CTR",
+  function() {
+    var that = this;
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var iv = crypto.getRandomValues(new Uint8Array(16));
+    var alg = {name: "AES-CTR", length: 128, iv};
+
+    var counter = new Uint8Array(16);
+    var algEncrypt = {name: "AES-CTR", length: 128, counter};
+
+    crypto.subtle.generateKey(alg, true, ["encrypt"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => crypto.subtle.encrypt(algEncrypt, x, data))
+      .then(complete(that), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: AES-CBC",
+  function() {
+    var that = this;
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var iv = crypto.getRandomValues(new Uint8Array(16));
+    var alg = {name: "AES-CBC", length: 128, iv};
+
+    crypto.subtle.generateKey(alg, true, ["encrypt"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => crypto.subtle.encrypt(alg, x, data))
+      .then(complete(that), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: AES-GCM",
+  function() {
+    var that = this;
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var iv = crypto.getRandomValues(new Uint8Array(16));
+    var alg = {name: "AES-GCM", length: 128, iv};
+
+    crypto.subtle.generateKey(alg, true, ["encrypt"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => crypto.subtle.encrypt(alg, x, data))
+      .then(complete(that), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: AES-KW",
+  function() {
+    var that = this;
+    var alg = {name: "AES-KW", length: 128};
+
+    crypto.subtle.generateKey(alg, true, ["wrapKey"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => crypto.subtle.wrapKey("raw", x, x, "AES-KW"))
+      .then(complete(that), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: HMAC",
+  function() {
+    var that = this;
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var alg = {name: "HMAC", length: 256, hash: "SHA-256"};
+
+    crypto.subtle.generateKey(alg, true, ["sign", "verify"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => crypto.subtle.sign("HMAC", x, data))
+      .then(complete(that), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: PBKDF2",
+  function() {
+    var that = this;
+    var key = new TextEncoder("utf-8").encode("password");
+
+    var alg = {
+      name: "PBKDF2",
+      hash: "SHA-1",
+      salt: crypto.getRandomValues(new Uint8Array(8)),
+      iterations: 4096
+    };
+
+    crypto.subtle.importKey("raw", key, "PBKDF2", true, ["deriveBits"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => crypto.subtle.deriveBits(alg, x, 160))
+      .then(complete(that), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: HKDF",
+  function() {
+    var that = this;
+    var key = new TextEncoder("utf-8").encode("password");
+
+    var alg = {
+      name: "HKDF",
+      hash: "SHA-256",
+      salt: new Uint8Array(),
+      info: new Uint8Array()
+    };
+
+    crypto.subtle.importKey("raw", key, "HKDF", false, ["deriveBits"])
+      .then(util.clone)
+      .then(x => crypto.subtle.deriveBits(alg, x, 16))
+      .then(complete(that), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: RSA-OAEP",
+  function() {
+    var that = this;
+    var data = crypto.getRandomValues(new Uint8Array(128));
+
+    var alg = {
+      name: "RSA-OAEP",
+      hash: "SHA-256",
+      modulusLength: 2048,
+      publicExponent: new Uint8Array([1, 0, 1])
+    };
+
+    crypto.subtle.generateKey(alg, true, ["encrypt", "decrypt"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => {
+        return crypto.subtle.encrypt(alg, x.publicKey, data)
+          .then(ct => crypto.subtle.decrypt(alg, x.privateKey, ct));
+      })
+      .then(complete(that), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: RSASSA-PKCS1-v1_5",
+  function() {
+    var that = this;
+    var data = crypto.getRandomValues(new Uint8Array(128));
+
+    var alg = {
+      name: "RSASSA-PKCS1-v1_5",
+      hash: "SHA-256",
+      modulusLength: 2048,
+      publicExponent: new Uint8Array([1, 0, 1])
+    };
+
+    crypto.subtle.generateKey(alg, true, ["sign", "verify"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => {
+        return crypto.subtle.sign(alg, x.privateKey, data)
+          .then(sig => crypto.subtle.verify(alg, x.publicKey, sig, data));
+      })
+      .then(complete(that, x => x), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+TestArray.addTest(
+  "Structured Cloning: RSA-PSS",
+  function() {
+    var that = this;
+    var data = crypto.getRandomValues(new Uint8Array(128));
+
+    var alg = {
+      name: "RSA-PSS",
+      hash: "SHA-256",
+      modulusLength: 2048,
+      publicExponent: new Uint8Array([1, 0, 1]),
+      saltLength: 20
+    };
+
+    crypto.subtle.generateKey(alg, true, ["sign", "verify"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => {
+        return crypto.subtle.sign(alg, x.privateKey, data)
+          .then(sig => crypto.subtle.verify(alg, x.publicKey, sig, data));
+      })
+      .then(complete(that, x => x), error(that));
+  }
+);
+
+// -----------------------------------------------------------------------------
+/*TestArray.addTest(
+  "Structured Cloning: DH",
+  function() {
+    var that = this;
+    var alg = {name: "DH", prime: tv.dh.prime, generator: new Uint8Array([2])};
+
+    crypto.subtle.generateKey(alg, true, ["deriveBits"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => {
+        var alg = {name: "DH", public: x.publicKey};
+        return crypto.subtle.deriveBits(alg, x.privateKey, 16);
+      })
+      .then(complete(that), error(that));
+  }
+);*/
+
+// -----------------------------------------------------------------------------
+/*TestArray.addTest(
+  "Structured Cloning: ECDH",
+  function() {
+    var that = this;
+    var alg = {name: "ECDH", namedCurve: "P-256"};
+
+    crypto.subtle.generateKey(alg, true, ["deriveBits"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => {
+        var alg = {name: "ECDH", public: x.publicKey};
+        return crypto.subtle.deriveBits(alg, x.privateKey, 16);
+      })
+      .then(complete(that), error(that));
+  }
+);*/
+
+// -----------------------------------------------------------------------------
+/*TestArray.addTest(
+  "Structured Cloning: ECDSA",
+  function() {
+    var that = this;
+    var data = crypto.getRandomValues(new Uint8Array(128));
+    var alg = {name: "ECDSA", namedCurve: "P-256", hash: "SHA-256"};
+
+    crypto.subtle.generateKey(alg, true, ["sign", "verify"])
+      .then(util.cloneExportCompareKeys)
+      .then(x => {
+        return crypto.subtle.sign(alg, x.privateKey, data)
+          .then(sig => crypto.subtle.verify(alg, x.publicKey, sig, data));
+      })
+      .then(complete(that), error(that));
+  }
+);*/
+/*]]>*/</script>
+</head>
+
+<body>
+
+<div id="content">
+	<div id="head">
+		<b>Web</b>Crypto<br>
+	</div>
+
+    <div id="start" onclick="start();">RUN ALL</div>
+
+    <div id="resultDiv" class="content">
+    Summary:
+    <span class="pass"><span id="passN">0</span> passed, </span>
+    <span class="fail"><span id="failN">0</span> failed, </span>
+    <span class="pending"><span id="pendingN">0</span> pending.</span>
+    <br/>
+    <br/>
+
+    <table id="results">
+        <tr>
+            <th>Test</th>
+            <th>Result</th>
+            <th>Time</th>
+        </tr>
+    </table>
+
+    </div>
+
+    <div id="foot"></div>
+</div>
+
+</body>
+</html>
--- a/dom/crypto/test/util.js
+++ b/dom/crypto/test/util.js
@@ -37,16 +37,54 @@ var util = {
     }
 
     var abv = new Uint8Array(hex.length / 2);
     for (var i=0; i<abv.length; ++i) {
       abv[i] = parseInt(hex.substr(2*i, 2), 16);
     }
     return abv;
   },
+
+  clone: function (obj) {
+    return new Promise(resolve => {
+      let {port1, port2} = new MessageChannel();
+
+      // Wait for the cloned object to arrive.
+      port1.onmessage = msg => resolve(msg.data);
+
+      // Clone the object.
+      port2.postMessage(obj);
+    });
+  },
+
+  cloneExportCompareKeys: function (key) {
+    return util.clone(key).then(clone => {
+      var exports = [];
+
+      if (key instanceof CryptoKey) {
+        exports.push(crypto.subtle.exportKey("raw", key));
+        exports.push(crypto.subtle.exportKey("raw", clone));
+      } else {
+        exports.push(crypto.subtle.exportKey("spki", key.publicKey));
+        exports.push(crypto.subtle.exportKey("spki", clone.publicKey));
+        exports.push(crypto.subtle.exportKey("pkcs8", key.privateKey));
+        exports.push(crypto.subtle.exportKey("pkcs8", clone.privateKey));
+      }
+
+      return Promise.all(exports).then(pairs => {
+        for (var i = 0; i < pairs.length; i += 2) {
+          if (!util.memcmp(pairs[i], pairs[i + 1])) {
+            throw new Error("keys don't match");
+          }
+        }
+
+        return clone;
+      });
+    });
+  }
 };
 
 function exists(x) {
   return (x !== undefined);
 }
 
 function hasFields(object, fields) {
   return fields
--- a/dom/geolocation/nsGeolocationSettings.cpp
+++ b/dom/geolocation/nsGeolocationSettings.cpp
@@ -238,17 +238,16 @@ nsGeolocationSettings::HandleGeolocation
   JS::Rooted<JSObject*> obj(nsContentUtils::RootingCx(), &aVal.toObject());
   MOZ_ASSERT(obj);
   nsIGlobalObject* global = xpc::NativeGlobal(obj);
   NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
 
   // because the spec requires calling getters when enumerating the key of a
   // dictionary
   AutoEntryScript aes(global, "geolocation.app_settings enumeration");
-  aes.TakeOwnershipOfErrorReporting();
   JSContext *cx = aes.cx();
   JS::Rooted<JS::IdVector> ids(cx, JS::IdVector(cx));
 
   // if we get no ids then the exception list is empty and we can return here.
   if (!JS_Enumerate(cx, obj, &ids)) {
       return;
   }
 
@@ -322,17 +321,16 @@ nsGeolocationSettings::HandleGeolocation
   // root the object and get the global
   JS::Rooted<JSObject*> obj(nsContentUtils::RootingCx(), &aVal.toObject());
   MOZ_ASSERT(obj);
   nsIGlobalObject* global = xpc::NativeGlobal(obj);
   NS_ENSURE_TRUE_VOID(global && global->GetGlobalJSObject());
 
   // the spec requires calling getters when accessing array by index
   AutoEntryScript aes(global, "geolocation.always_precise indexing");
-  aes.TakeOwnershipOfErrorReporting();
   JSContext *cx = aes.cx();
 
   bool isArray;
   if (!JS_IsArrayObject(cx, obj, &isArray) || !isArray) {
     return;
   }
 
   uint32_t length;
--- a/dom/html/nsHTMLDocument.cpp
+++ b/dom/html/nsHTMLDocument.cpp
@@ -1218,19 +1218,21 @@ nsHTMLDocument::CreateDummyChannelForCoo
   // The cookie service reads the privacy status of the channel we pass to it in
   // order to determine which cookie database to query.  In some cases we don't
   // have a proper channel to hand it to the cookie service though.  This
   // function creates a dummy channel that is not used to load anything, for the
   // sole purpose of handing it to the cookie service.  DO NOT USE THIS CHANNEL
   // FOR ANY OTHER PURPOSE.
   MOZ_ASSERT(!mChannel);
 
+  // The following channel is never openend, so it does not matter what
+  // securityFlags we pass; let's follow the principle of least privilege.
   nsCOMPtr<nsIChannel> channel;
   NS_NewChannel(getter_AddRefs(channel), aCodebaseURI, this,
-                nsILoadInfo::SEC_NORMAL,
+                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                 nsIContentPolicy::TYPE_INVALID);
   nsCOMPtr<nsIPrivateBrowsingChannel> pbChannel =
     do_QueryInterface(channel);
   nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
   nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
   if (!pbChannel || !loadContext) {
     return nullptr;
   }
--- a/dom/icc/Icc.cpp
+++ b/dom/icc/Icc.cpp
@@ -14,16 +14,17 @@
 #include "mozilla/dom/MozStkCommandEvent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "nsIIccInfo.h"
 #include "nsIIccService.h"
 #include "nsIStkCmdFactory.h"
 #include "nsIStkProactiveCmd.h"
 #include "nsServiceManagerUtils.h"
+#include "nsContentUtils.h"
 
 using mozilla::dom::icc::IccCallback;
 using mozilla::dom::icc::IccContact;
 
 namespace mozilla {
 namespace dom {
 
 namespace {
@@ -88,21 +89,17 @@ nsresult
 Icc::NotifyEvent(const nsAString& aName)
 {
   return DispatchTrustedEvent(aName);
 }
 
 nsresult
 Icc::NotifyStkEvent(const nsAString& aName, nsIStkProactiveCmd* aStkProactiveCmd)
 {
-  AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetOwner()))) {
-    return NS_ERROR_UNEXPECTED;
-  }
-  JSContext* cx = jsapi.cx();
+  JSContext* cx = nsContentUtils::RootingCxForThread();
   JS::Rooted<JS::Value> value(cx);
 
   nsCOMPtr<nsIStkCmdFactory> cmdFactory =
     do_GetService(ICC_STK_CMD_FACTORY_CONTRACTID);
   NS_ENSURE_TRUE(cmdFactory, NS_ERROR_UNEXPECTED);
 
   cmdFactory->CreateCommandMessage(aStkProactiveCmd, &value);
   NS_ENSURE_TRUE(value.isObject(), NS_ERROR_UNEXPECTED);
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -24,16 +24,17 @@
 #include "mozilla/Endian.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/storage.h"
 #include "mozilla/unused.h"
+#include "mozilla/UniquePtrExtensions.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/dom/StructuredCloneTags.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/filehandle/ActorsParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
@@ -248,28 +249,16 @@ const int32_t kDEBUGThreadPriority = nsI
 const uint32_t kDEBUGThreadSleepMS = 0;
 
 const int32_t kDEBUGTransactionThreadPriority =
   nsISupportsPriority::PRIORITY_NORMAL;
 const uint32_t kDEBUGTransactionThreadSleepMS = 0;
 
 #endif
 
-struct FreeDeleter
-{
-  void
-  operator()(void* aPtr) const
-  {
-    free(aPtr);
-  }
-};
-
-template <typename T>
-using UniqueFreePtr = UniquePtr<T, FreeDeleter>;
-
 template <size_t N>
 MOZ_CONSTEXPR size_t
 LiteralStringLength(const char (&aArr)[N])
 {
   static_assert(N, "Zero-length string literal?!");
 
   // Don't include the null terminator.
   return N - 1;
--- a/dom/indexedDB/IDBRequest.cpp
+++ b/dom/indexedDB/IDBRequest.cpp
@@ -363,22 +363,23 @@ IDBRequest::SetResultCallback(ResultCall
   if (GetScriptOwner()) {
     // If we have a script owner we want the SafeJSContext and then to enter the
     // script owner's compartment.
     autoJS.Init();
     ac.emplace(autoJS.cx(), GetScriptOwner());
   } else {
     // Otherwise our owner is a window and we use that to initialize.
     MOZ_ASSERT(GetOwner());
-    if (!autoJS.InitWithLegacyErrorReporting(GetOwner())) {
+    if (!autoJS.Init(GetOwner())) {
       IDB_WARNING("Failed to initialize AutoJSAPI!");
       SetError(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
       return;
     }
   }
+  autoJS.TakeOwnershipOfErrorReporting();
 
   JSContext* cx = autoJS.cx();
 
   AssertIsRooted();
 
   JS::Rooted<JS::Value> result(cx);
   nsresult rv = aCallback->GetResult(cx, &result);
   if (NS_WARN_IF(NS_FAILED(rv))) {
deleted file mode 100644
--- a/dom/indexedDB/test/extensions/Makefile.in
+++ /dev/null
@@ -1,12 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
-
-GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
-
-include $(topsrcdir)/config/rules.mk
-
-libs::
-	@(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..bbe2430e261f95e33adc1b970b2a6e53db627bfd
GIT binary patch
literal 5605
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
BX)FK$
--- a/dom/indexedDB/test/extensions/install.rdf
+++ b/dom/indexedDB/test/extensions/install.rdf
@@ -2,21 +2,30 @@
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <Description about="urn:mozilla:install-manifest">
     <em:name>IndexedDBTest</em:name>
     <em:description>IndexedDB functions for use in testing.</em:description>
     <em:creator>Mozilla</em:creator>
-    <em:version>2013.10.10</em:version>
-#expand    <em:id>__XPI_NAME__-test@mozilla.org</em:id>
+    <em:version>2016.03.09</em:version>
+    <em:id>indexedDB-test@mozilla.org</em:id>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:targetApplication>
       <Description>
-        <em:id>toolkit@mozilla.org</em:id>
-#expand        <em:minVersion>__MOZILLA_VERSION_U__</em:minVersion>
-#expand        <em:maxVersion>__MOZILLA_VERSION_U__</em:maxVersion>
+        <!-- Firefox -->
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>45.0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+    <em:targetApplication>
+      <Description>
+        <!-- Fennec -->
+        <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
+        <em:minVersion>45.0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
       </Description>
     </em:targetApplication>
   </Description>
 </RDF>
--- a/dom/indexedDB/test/extensions/moz.build
+++ b/dom/indexedDB/test/extensions/moz.build
@@ -1,15 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPI_NAME = 'indexedDB'
 
-FINAL_TARGET_PP_FILES += [
+FINAL_TARGET_FILES += [
+    'bootstrap.js',
     'install.rdf',
 ]
 
-FINAL_TARGET_FILES += [
-    'bootstrap.js',
+TEST_HARNESS_FILES.testing.mochitest.extensions += [
+    'indexedDB-test@mozilla.org.xpi',
 ]
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -861,19 +861,16 @@ TabChild::NotifyTabContextUpdated()
 {
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
   MOZ_ASSERT(docShell);
 
   if (!docShell) {
     return;
   }
 
-  if (IsMozBrowserElement()) {
-    docShell->SetIsInIsolatedMozBrowserElement(IsIsolatedMozBrowserElement());
-  }
   docShell->SetFrameType(IsMozBrowserElement() ?
                            nsIDocShell::FRAME_TYPE_BROWSER :
                            HasOwnApp() ?
                              nsIDocShell::FRAME_TYPE_APP :
                              nsIDocShell::FRAME_TYPE_REGULAR);
   nsDocShell::Cast(docShell)->SetOriginAttributes(OriginAttributesRef());
 }
 
@@ -1698,17 +1695,17 @@ TabChild::RecvSuppressDisplayport(const 
 {
   if (aEnabled) {
     mActiveSuppressDisplayport++;
   } else {
     mActiveSuppressDisplayport--;
   }
 
   MOZ_ASSERT(mActiveSuppressDisplayport >= 0);
-  APZCCallbackHelper::SuppressDisplayport(aEnabled);
+  APZCCallbackHelper::SuppressDisplayport(aEnabled, GetPresShell());
   return true;
 }
 
 void
 TabChild::HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
                           const ScrollableLayerGuid& aGuid)
 {
   TABC_LOG("Handling double tap at %s with %p %p\n",
@@ -2377,17 +2374,17 @@ TabChild::RecvDestroy()
 
   // Need to close undeleted ContentPermissionRequestChilds before tab is closed.
   for (auto& permissionRequestChild : childArray) {
       auto child = static_cast<RemotePermissionRequest*>(permissionRequestChild);
       child->Destroy();
   }
 
   while (mActiveSuppressDisplayport > 0) {
-    APZCCallbackHelper::SuppressDisplayport(false);
+    APZCCallbackHelper::SuppressDisplayport(false, nullptr);
     mActiveSuppressDisplayport--;
   }
 
   if (mTabChildGlobal) {
     // Message handlers are called from the event loop, so it better be safe to
     // run script.
     MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
     mTabChildGlobal->DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
--- a/dom/json/nsJSON.cpp
+++ b/dom/json/nsJSON.cpp
@@ -409,21 +409,24 @@ nsJSON::DecodeInternal(JSContext* cx,
     if (!mURI)
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   nsresult rv;
   nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
   NS_ENSURE_TRUE(nullPrincipal, NS_ERROR_FAILURE);
 
+  // The ::Decode function is deprecated [Bug 675797] and the following
+  // channel is never openend, so it does not matter what securityFlags
+  // we pass to NS_NewInputStreamChannel here.
   rv = NS_NewInputStreamChannel(getter_AddRefs(jsonChannel),
                                 mURI,
                                 aStream,
                                 nullPrincipal,
-                                nsILoadInfo::SEC_NORMAL,
+                                nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                                 nsIContentPolicy::TYPE_OTHER,
                                 NS_LITERAL_CSTRING("application/json"));
 
   if (!jsonChannel || NS_FAILED(rv))
     return NS_ERROR_FAILURE;
 
   RefPtr<nsJSONListener> jsonListener =
     new nsJSONListener(cx, aRetval.address(), aNeedsConverter);
--- a/dom/jsurl/nsJSProtocolHandler.cpp
+++ b/dom/jsurl/nsJSProtocolHandler.cpp
@@ -235,39 +235,40 @@ nsresult nsJSThunk::EvaluateScript(nsICh
     nsCOMPtr<nsIScriptSecurityManager> securityManager;
     securityManager = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
     if (NS_FAILED(rv))
         return rv;
 
     // New script entry point required, due to the "Create a script" step of
     // http://www.whatwg.org/specs/web-apps/current-work/#javascript-protocol
     nsAutoMicroTask mt;
-    AutoEntryScript entryScript(innerGlobal, "javascript: URI", true,
-                                scriptContext->GetNativeContext());
-    // We want to make sure we report any exceptions that happen before we
-    // return, since whatever happens inside our execution shouldn't affect any
-    // other scripts that might happen to be running.
-    entryScript.TakeOwnershipOfErrorReporting();
-    JSContext* cx = entryScript.cx();
+    AutoEntryScript aes(innerGlobal, "javascript: URI", true,
+                        scriptContext->GetNativeContext());
+    JSContext* cx = aes.cx();
     JS::Rooted<JSObject*> globalJSObject(cx, innerGlobal->GetGlobalJSObject());
     NS_ENSURE_TRUE(globalJSObject, NS_ERROR_UNEXPECTED);
 
     //-- Don't execute unless the script principal subsumes the
     //   principal of the context.
     nsIPrincipal* objectPrincipal = nsContentUtils::ObjectPrincipal(globalJSObject);
 
     bool subsumes;
     rv = principal->Subsumes(objectPrincipal, &subsumes);
     if (NS_FAILED(rv))
         return rv;
 
     if (!subsumes) {
         return NS_ERROR_DOM_RETVAL_UNDEFINED;
     }
 
+    // Fail if someone tries to execute in a global with system principal.
+    if (nsContentUtils::IsSystemPrincipal(objectPrincipal)) {
+        return NS_ERROR_DOM_SECURITY_ERR;
+    }
+
     JS::Rooted<JS::Value> v (cx, JS::UndefinedValue());
     // Finally, we have everything needed to evaluate the expression.
     JS::CompileOptions options(cx);
     options.setFileAndLine(mURL.get(), 1)
            .setVersion(JSVERSION_DEFAULT);
     nsJSUtils::EvaluateOptions evalOptions(cx);
     evalOptions.setCoerceToString(true);
     rv = nsJSUtils::EvaluateString(cx, NS_ConvertUTF8toUTF16(script),
@@ -416,21 +417,23 @@ nsresult nsJSChannel::Init(nsIURI *aURI)
     // and the underlying Input Stream will not be created...
     nsCOMPtr<nsIChannel> channel;
 
     nsCOMPtr<nsIPrincipal> nullPrincipal = nsNullPrincipal::Create();
     NS_ENSURE_TRUE(nullPrincipal, NS_ERROR_FAILURE);
 
     // If the resultant script evaluation actually does return a value, we
     // treat it as html.
+    // The following channel is never openend, so it does not matter what
+    // securityFlags we pass; let's follow the principle of least privilege.
     rv = NS_NewInputStreamChannel(getter_AddRefs(channel),
                                   aURI,
                                   mIOThunk,
                                   nullPrincipal,
-                                  nsILoadInfo::SEC_NORMAL,
+                                  nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                                   nsIContentPolicy::TYPE_OTHER,
                                   NS_LITERAL_CSTRING("text/html"));
     if (NS_FAILED(rv)) return rv;
 
     rv = mIOThunk->Init(aURI);
     if (NS_SUCCEEDED(rv)) {
         mStreamChannel = channel;
         mPropertyBag = do_QueryInterface(channel);
--- a/dom/media/MediaDecoderReader.h
+++ b/dom/media/MediaDecoderReader.h
@@ -83,16 +83,18 @@ public:
 
   // Note that, conceptually, WaitForData makes sense in a non-exclusive sense.
   // But in the current architecture it's only ever used exclusively (by MDSM),
   // so we mark it that way to verify our assumptions. If you have a use-case
   // for multiple WaitForData consumers, feel free to flip the exclusivity here.
   using WaitForDataPromise =
     MozPromise<MediaData::Type, WaitForDataRejectValue, IsExclusive>;
 
+  using BufferedUpdatePromise = MozPromise<bool, bool, IsExclusive>;
+
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaDecoderReader)
 
   // The caller must ensure that Shutdown() is called before aDecoder is
   // destroyed.
   explicit MediaDecoderReader(AbstractMediaDecoder* aDecoder);
 
   // Initializes the reader, returns NS_OK on success, or NS_ERROR_FAILURE
   // on failure.
@@ -227,16 +229,26 @@ public:
   void NotifyDataArrived()
   {
     MOZ_ASSERT(OnTaskQueue());
     NS_ENSURE_TRUE_VOID(!mShutdown);
     NotifyDataArrivedInternal();
     UpdateBuffered();
   }
 
+  // Update the buffered ranges and upon doing so return a promise
+  // to indicate success. Overrides may need to do extra work to ensure
+  // buffered is up to date.
+  virtual RefPtr<BufferedUpdatePromise> UpdateBufferedWithPromise()
+  {
+    MOZ_ASSERT(OnTaskQueue());
+    UpdateBuffered();
+    return BufferedUpdatePromise::CreateAndResolve(true, __func__);
+  }
+
   virtual MediaQueue<AudioData>& AudioQueue() { return mAudioQueue; }
   virtual MediaQueue<VideoData>& VideoQueue() { return mVideoQueue; }
 
   AbstractCanonical<media::TimeIntervals>* CanonicalBuffered()
   {
     return &mBuffered;
   }
 
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -857,19 +857,18 @@ MediaDecoderStateMachine::MaybeFinishDec
     return false;
   }
   FinishDecodeFirstFrame();
   if (!mQueuedSeek.Exists()) {
     return false;
   }
 
   // We can now complete the pending seek.
-  mPendingSeek.Steal(mQueuedSeek);
   SetState(DECODER_STATE_SEEKING);
-  ScheduleStateMachine();
+  InitiateSeek(mQueuedSeek);
   return true;
 }
 
 void
 MediaDecoderStateMachine::OnVideoDecoded(MediaData* aVideoSample,
                                          TimeStamp aDecodeStartTime)
 {
   MOZ_ASSERT(OnTaskQueue());
@@ -949,20 +948,18 @@ MediaDecoderStateMachine::OnVideoDecoded
           // We are doing a fastSeek, but we ended up *before* the previous
           // playback position. This is surprising UX, so switch to an accurate
           // seek and decode to the seek target. This is not conformant to the
           // spec, fastSeek should always be fast, but until we get the time to
           // change all Readers to seek to the keyframe after the currentTime
           // in this case, we'll just decode forward. Bug 1026330.
           mCurrentSeek.mTarget.SetType(SeekTarget::Accurate);
         }
-        if (mCurrentSeek.mTarget.IsFast() ||
-            mPendingSeek.Exists()) {
-          // Non-precise seek; or a pending seek exists ; we can stop the seek
-          // at the first sample.
+        if (mCurrentSeek.mTarget.IsFast()) {
+          // Non-precise seek. We can stop the seek at the first sample.
           Push(video, MediaData::VIDEO_DATA);
         } else {
           // We're doing an accurate seek. We still need to discard
           // MediaData up to the one containing exact seek target.
           if (NS_FAILED(DropVideoUpToSeekTarget(video))) {
             DecodeError();
             return;
           }
@@ -1265,18 +1262,16 @@ MediaDecoderStateMachine::SetDormant(boo
   mPendingDormant.reset();
 
   DECODER_LOG("SetDormant=%d", aDormant);
 
   if (aDormant) {
     if (mState == DECODER_STATE_SEEKING) {
       if (mQueuedSeek.Exists()) {
         // Keep latest seek target
-      } else if (mPendingSeek.Exists()) {
-        mQueuedSeek.Steal(mPendingSeek);
       } else if (mCurrentSeek.Exists()) {
         mQueuedSeek.Steal(mCurrentSeek);
       } else {
         mQueuedSeek.mTarget = SeekTarget(mCurrentPosition,
                                          SeekTarget::Accurate,
                                          MediaDecoderEventVisibility::Suppressed);
         // XXXbholley - Nobody is listening to this promise. Do we need to pass it
         // back to MediaDecoder when we come out of dormant?
@@ -1285,17 +1280,16 @@ MediaDecoderStateMachine::SetDormant(boo
     } else {
       mQueuedSeek.mTarget = SeekTarget(mCurrentPosition,
                                        SeekTarget::Accurate,
                                        MediaDecoderEventVisibility::Suppressed);
       // XXXbholley - Nobody is listening to this promise. Do we need to pass it
       // back to MediaDecoder when we come out of dormant?
       RefPtr<MediaDecoder::SeekPromise> unused = mQueuedSeek.mPromise.Ensure(__func__);
     }
-    mPendingSeek.RejectIfExists(__func__);
     mCurrentSeek.RejectIfExists(__func__);
     SetState(DECODER_STATE_DORMANT);
     if (IsPlaying()) {
       StopPlayback();
     }
 
     Reset();
 
@@ -1320,18 +1314,19 @@ MediaDecoderStateMachine::Shutdown()
   MOZ_ASSERT(OnTaskQueue());
 
   // Once we've entered the shutdown state here there's no going back.
   // Change state before issuing shutdown request to threads so those
   // threads can start exiting cleanly during the Shutdown call.
   ScheduleStateMachine();
   SetState(DECODER_STATE_SHUTDOWN);
 
+  mBufferedUpdateRequest.DisconnectIfExists();
+
   mQueuedSeek.RejectIfExists(__func__);
-  mPendingSeek.RejectIfExists(__func__);
   mCurrentSeek.RejectIfExists(__func__);
 
 #ifdef MOZ_EME
   mCDMProxyPromise.DisconnectIfExists();
 #endif
 
   if (IsPlaying()) {
     StopPlayback();
@@ -1375,19 +1370,18 @@ void MediaDecoderStateMachine::StartDeco
       // the first samples in order to determine the media start time,
       // we have the start time from last time we loaded.
       // FinishDecodeFirstFrame will be launched upon completion of the seek when
       // we have data ready to play.
       MOZ_ASSERT(mQueuedSeek.Exists() && mSentFirstFrameLoadedEvent,
                  "Return from dormant must have queued seek");
     }
     if (mQueuedSeek.Exists()) {
-      mPendingSeek.Steal(mQueuedSeek);
       SetState(DECODER_STATE_SEEKING);
-      ScheduleStateMachine();
+      InitiateSeek(mQueuedSeek);
       return;
     }
   }
 
   mDecodeStartTime = TimeStamp::Now();
 
   CheckIfDecodeComplete();
   if (mState == DECODER_STATE_COMPLETED) {
@@ -1493,24 +1487,24 @@ MediaDecoderStateMachine::Seek(SeekTarge
   if (mState < DECODER_STATE_DECODING ||
       (IsDecodingFirstFrame() && !mReader->ForceZeroStartTime())) {
     DECODER_LOG("Seek() Not Enough Data to continue at this stage, queuing seek");
     mQueuedSeek.RejectIfExists(__func__);
     mQueuedSeek.mTarget = aTarget;
     return mQueuedSeek.mPromise.Ensure(__func__);
   }
   mQueuedSeek.RejectIfExists(__func__);
-  mPendingSeek.RejectIfExists(__func__);
-  mPendingSeek.mTarget = aTarget;
-
-  DECODER_LOG("Changed state to SEEKING (to %lld)", mPendingSeek.mTarget.GetTime().ToMicroseconds());
+
+  DECODER_LOG("Changed state to SEEKING (to %lld)", aTarget.GetTime().ToMicroseconds());
   SetState(DECODER_STATE_SEEKING);
-  ScheduleStateMachine();
-
-  return mPendingSeek.mPromise.Ensure(__func__);
+
+  SeekJob seekJob;
+  seekJob.mTarget = aTarget;
+  InitiateSeek(seekJob);
+  return mCurrentSeek.mPromise.Ensure(__func__);
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::InvokeSeek(SeekTarget aTarget)
 {
   return InvokeAsync(OwnerThread(), this, __func__,
                      &MediaDecoderStateMachine::Seek, aTarget);
 }
@@ -1580,22 +1574,22 @@ MediaDecoderStateMachine::DispatchDecode
                 GetDecodedAudioDuration(),
                 VideoQueue().Duration());
     nsCOMPtr<nsIRunnable> task = NS_NewRunnableMethod(mReader, &MediaDecoderReader::SetIdle);
     DecodeTaskQueue()->Dispatch(task.forget());
   }
 }
 
 void
-MediaDecoderStateMachine::InitiateSeek()
+MediaDecoderStateMachine::InitiateSeek(SeekJob& aSeekJob)
 {
   MOZ_ASSERT(OnTaskQueue());
 
   mCurrentSeek.RejectIfExists(__func__);
-  mCurrentSeek.Steal(mPendingSeek);
+  mCurrentSeek.Steal(aSeekJob);
 
   // Bound the seek time to be inside the media range.
   int64_t end = Duration().ToMicroseconds();
   NS_ASSERTION(end != -1, "Should know end time by now");
   int64_t seekTime = mCurrentSeek.mTarget.GetTime().ToMicroseconds();
   seekTime = std::min(seekTime, end);
   seekTime = std::max(int64_t(0), seekTime);
   NS_ASSERTION(seekTime >= 0 && seekTime <= end,
@@ -2015,22 +2009,35 @@ MediaDecoderStateMachine::EnqueueLoadedM
                               Move(visibility));
   mSentLoadedMetadataEvent = true;
 }
 
 void
 MediaDecoderStateMachine::EnqueueFirstFrameLoadedEvent()
 {
   MOZ_ASSERT(OnTaskQueue());
-  MediaDecoderEventVisibility visibility =
-    mSentFirstFrameLoadedEvent ? MediaDecoderEventVisibility::Suppressed
-                               : MediaDecoderEventVisibility::Observable;
-  mFirstFrameLoadedEvent.Notify(nsAutoPtr<MediaInfo>(new MediaInfo(mInfo)),
-                                Move(visibility));
+  // Track value of mSentFirstFrameLoadedEvent from before updating it
+  bool firstFrameBeenLoaded = mSentFirstFrameLoadedEvent;
   mSentFirstFrameLoadedEvent = true;
+  RefPtr<MediaDecoderStateMachine> self = this;
+  mBufferedUpdateRequest.Begin(InvokeAsync(DecodeTaskQueue(), mReader.get(), __func__,
+    &MediaDecoderReader::UpdateBufferedWithPromise)
+    ->Then(OwnerThread(),
+    __func__,
+    // Resolve
+    [self, firstFrameBeenLoaded]() {
+      self->mBufferedUpdateRequest.Complete();
+      MediaDecoderEventVisibility visibility =
+        firstFrameBeenLoaded ? MediaDecoderEventVisibility::Suppressed
+                             : MediaDecoderEventVisibility::Observable;
+      self->mFirstFrameLoadedEvent.Notify(nsAutoPtr<MediaInfo>(new MediaInfo(self->mInfo)),
+                                          Move(visibility));
+    },
+    // Reject
+    []() { MOZ_CRASH("Should not reach"); }));
 }
 
 bool
 MediaDecoderStateMachine::IsDecodingFirstFrame()
 {
   return mState == DECODER_STATE_DECODING && mDecodingFirstFrame;
 }
 
@@ -2093,28 +2100,20 @@ MediaDecoderStateMachine::SeekCompleted(
       newCurrentTime = std::min(audioStart, video->mTime);
     } else {
       newCurrentTime = audioStart;
     }
   } else {
     newCurrentTime = video ? video->mTime : seekTime;
   }
 
-  // Change state to DECODING or COMPLETED now. SeekingStopped will
-  // call MediaDecoderStateMachine::Seek to reset our state to SEEKING
-  // if we need to seek again.
-
+  // Change state to DECODING or COMPLETED now.
   bool isLiveStream = mResource->IsLiveStream();
   State nextState;
-  if (mPendingSeek.Exists()) {
-    // A new seek target came in while we were processing the old one. No rest
-    // for the seeking.
-    DECODER_LOG("A new seek came along while we were finishing the old one - staying in SEEKING");
-    nextState = DECODER_STATE_SEEKING;
-  } else if (GetMediaTime() == Duration().ToMicroseconds() && !isLiveStream) {
+  if (GetMediaTime() == Duration().ToMicroseconds() && !isLiveStream) {
     // Seeked to end of media, move to COMPLETED state. Note we don't do
     // this when playing a live stream, since the end of media will advance
     // once we download more data!
     DECODER_LOG("Changed state from SEEKING (to %lld) to COMPLETED", seekTime);
     // Explicitly set our state so we don't decode further, and so
     // we report playback ended to the media element.
     nextState = DECODER_STATE_COMPLETED;
   } else {
@@ -2311,19 +2310,16 @@ nsresult MediaDecoderStateMachine::RunSt
       DECODER_LOG("Buffered for %.3lfs", (now - mBufferingStart).ToSeconds());
       StartDecoding();
 
       NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled");
       return NS_OK;
     }
 
     case DECODER_STATE_SEEKING: {
-      if (mPendingSeek.Exists()) {
-        InitiateSeek();
-      }
       return NS_OK;
     }
 
     case DECODER_STATE_COMPLETED: {
       if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING && IsPlaying()) {
         StopPlayback();
       }
       // Play the remaining media. We want to run AdvanceFrame() at least
--- a/dom/media/MediaDecoderStateMachine.h
+++ b/dom/media/MediaDecoderStateMachine.h
@@ -522,19 +522,57 @@ protected:
 
   // Dispatches a LoadedMetadataEvent.
   // This is threadsafe and can be called on any thread.
   // The decoder monitor must be held.
   void EnqueueLoadedMetadataEvent();
 
   void EnqueueFirstFrameLoadedEvent();
 
+  struct SeekJob {
+    void Steal(SeekJob& aOther)
+    {
+      MOZ_DIAGNOSTIC_ASSERT(!Exists());
+      mTarget = aOther.mTarget;
+      aOther.mTarget.Reset();
+      mPromise = Move(aOther.mPromise);
+    }
+
+    bool Exists()
+    {
+      MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty());
+      return mTarget.IsValid();
+    }
+
+    void Resolve(bool aAtEnd, const char* aCallSite)
+    {
+      mTarget.Reset();
+      MediaDecoder::SeekResolveValue val(aAtEnd, mTarget.mEventVisibility);
+      mPromise.Resolve(val, aCallSite);
+    }
+
+    void RejectIfExists(const char* aCallSite)
+    {
+      mTarget.Reset();
+      mPromise.RejectIfExists(true, aCallSite);
+    }
+
+    ~SeekJob()
+    {
+      MOZ_DIAGNOSTIC_ASSERT(!mTarget.IsValid());
+      MOZ_DIAGNOSTIC_ASSERT(mPromise.IsEmpty());
+    }
+
+    SeekTarget mTarget;
+    MozPromiseHolder<MediaDecoder::SeekPromise> mPromise;
+  };
+
   // Clears any previous seeking state and initiates a new see on the decoder.
   // The decoder monitor must be held.
-  void InitiateSeek();
+  void InitiateSeek(SeekJob& aSeekJob);
 
   nsresult DispatchAudioDecodeTaskIfNeeded();
 
   // Ensures a task to decode audio has been dispatched to the decode task queue.
   // If a task to decode has already been dispatched, this does nothing,
   // otherwise this dispatches a task to do the decode.
   // This is called on the state machine or decode threads.
   // The decoder monitor must be held.
@@ -829,60 +867,19 @@ private:
   // The decoder monitor must be held.
   bool IsLogicallyPlaying()
   {
     MOZ_ASSERT(OnTaskQueue());
     return mPlayState == MediaDecoder::PLAY_STATE_PLAYING ||
            mNextPlayState == MediaDecoder::PLAY_STATE_PLAYING;
   }
 
-  struct SeekJob {
-    void Steal(SeekJob& aOther)
-    {
-      MOZ_DIAGNOSTIC_ASSERT(!Exists());
-      mTarget = aOther.mTarget;
-      aOther.mTarget.Reset();
-      mPromise = Move(aOther.mPromise);
-    }
-
-    bool Exists()
-    {
-      MOZ_ASSERT(mTarget.IsValid() == !mPromise.IsEmpty());
-      return mTarget.IsValid();
-    }
-
-    void Resolve(bool aAtEnd, const char* aCallSite)
-    {
-      mTarget.Reset();
-      MediaDecoder::SeekResolveValue val(aAtEnd, mTarget.mEventVisibility);
-      mPromise.Resolve(val, aCallSite);
-    }
-
-    void RejectIfExists(const char* aCallSite)
-    {
-      mTarget.Reset();
-      mPromise.RejectIfExists(true, aCallSite);
-    }
-
-    ~SeekJob()
-    {
-      MOZ_DIAGNOSTIC_ASSERT(!mTarget.IsValid());
-      MOZ_DIAGNOSTIC_ASSERT(mPromise.IsEmpty());
-    }
-
-    SeekTarget mTarget;
-    MozPromiseHolder<MediaDecoder::SeekPromise> mPromise;
-  };
-
-  // Queued seek - moves to mPendingSeek when DecodeFirstFrame completes.
+  // Queued seek - moves to mCurrentSeek when DecodeFirstFrame completes.
   SeekJob mQueuedSeek;
 
-  // Position to seek to in microseconds when the seek state transition occurs.
-  SeekJob mPendingSeek;
-
   // The position that we're currently seeking to.
   SeekJob mCurrentSeek;
 
   // Media Fragment end time in microseconds. Access controlled by decoder monitor.
   int64_t mFragmentEndTime;
 
   // The media sink resource.  Used on the state machine thread.
   RefPtr<media::MediaSink> mMediaSink;
@@ -1131,16 +1128,19 @@ private:
   MediaInfo mInfo;
 
   nsAutoPtr<MetadataTags> mMetadataTags;
 
   mozilla::MediaMetadataManager mMetadataManager;
 
   mozilla::RollingMean<uint32_t, uint32_t> mCorruptFrames;
 
+  // Track our request to update the buffered ranges
+  MozPromiseRequestHolder<MediaDecoderReader::BufferedUpdatePromise> mBufferedUpdateRequest;
+
   // True if we need to call FinishDecodeFirstFrame() upon frame decoding
   // successeeding.
   bool mDecodingFirstFrame;
 
   // True if we are back from DECODER_STATE_DORMANT state and
   // LoadedMetadataEvent was already sent.
   bool mSentLoadedMetadataEvent;
   // True if we are back from DECODER_STATE_DORMANT state and
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1614,16 +1614,30 @@ MediaFormatReader::GetBuffered()
   if (!intervals.Length() ||
       intervals.GetStart() == media::TimeUnit::FromMicroseconds(0)) {
     // IntervalSet already starts at 0 or is empty, nothing to shift.
     return intervals;
   }
   return intervals.Shift(media::TimeUnit::FromMicroseconds(-startTime));
 }
 
+// For the MediaFormatReader override we need to force an update to the
+// buffered ranges, so we call NotifyDataArrive
+RefPtr<MediaDecoderReader::BufferedUpdatePromise>
+MediaFormatReader::UpdateBufferedWithPromise() {
+  MOZ_ASSERT(OnTaskQueue());
+  // Call NotifyDataArrive to force a recalculation of the buffered
+  // ranges. UpdateBuffered alone will not force a recalculation, so we
+  // use NotifyDataArrived which sets flags to force this recalculation.
+  // See MediaFormatReader::UpdateReceivedNewData for an example of where
+  // the new data flag is used.
+  NotifyDataArrived();
+  return BufferedUpdatePromise::CreateAndResolve(true, __func__);
+}
+
 void MediaFormatReader::ReleaseMediaResources()
 {
   // Before freeing a video codec, all video buffers needed to be released
   // even from graphics pipeline.
   if (mVideoFrameContainer) {
     mVideoFrameContainer->ClearCurrentFrame();
   }
   mVideo.mInitPromise.DisconnectIfExists();
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -50,16 +50,18 @@ public:
   Seek(SeekTarget aTarget, int64_t aUnused) override;
 
 protected:
   void NotifyDataArrivedInternal() override;
 
 public:
   media::TimeIntervals GetBuffered() override;
 
+  RefPtr<BufferedUpdatePromise> UpdateBufferedWithPromise() override;
+
   bool ForceZeroStartTime() const override;
 
   // For Media Resource Management
   void ReleaseMediaResources() override;
 
   nsresult ResetDecode() override;
 
   RefPtr<ShutdownPromise> Shutdown() override;
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -60,18 +60,16 @@ if CONFIG['MOZ_OMX_DECODER']:
 if CONFIG['MOZ_EME']:
     DIRS += ['eme']
 
 TEST_DIRS += [
     'compiledtest',
     'gtest',
 ]
 
-MOCHITEST_CHROME_MANIFESTS += ['test/chrome.ini']
-
 MOCHITEST_MANIFESTS += [
     'test/mochitest.ini',
     'tests/mochitest/identity/mochitest.ini',
     'tests/mochitest/ipc/mochitest.ini',
 ]
 
 if CONFIG['MOZ_WEBRTC']:
     MOCHITEST_MANIFESTS += ['tests/mochitest/mochitest.ini']
deleted file mode 100644
--- a/dom/media/test/chrome.ini
+++ /dev/null
@@ -1,12 +0,0 @@
-#Media chrome tests
-
-[DEFAULT]
-skip-if = buildapp == 'b2g'
-support-files =
-  basic.vtt
-  seek.webm
-
-[test_texttrackcue_chrome.html]
-skip-if = os=='mac' && debug # bug 1130751
-[test_texttrack_chrome.html]
-[test_texttracklist_chrome.html]
--- a/dom/media/test/mochitest.ini
+++ b/dom/media/test/mochitest.ini
@@ -843,19 +843,22 @@ tags=msg capturestream
 [test_streams_individual_pause.html]
 tags=msg
 [test_streams_srcObject.html]
 tags=msg capturestream
 [test_streams_tracks.html]
 tags=msg capturestream
 [test_texttrack.html]
 [test_texttrackcue.html]
+[test_texttrackcue_moz.html]
 [test_texttrackevents_video.html]
 [test_texttracklist.html]
+[test_texttracklist_moz.html]
 [test_texttrackregion.html]
+[test_texttrack_moz.html]
 [test_timeupdate_small_files.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
 [test_trackelementevent.html]
 [test_trackevent.html]
 [test_unseekable.html]
 skip-if = toolkit == 'gonk' || (toolkit == 'android' && processor == 'x86') #x86 only and bug 1128845 on gonk
 [test_video_to_canvas.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 914439
deleted file mode 100644
--- a/dom/media/test/test_texttrack_chrome.html
+++ /dev/null
@@ -1,57 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=881976
--->
-<head>
-  <meta charset='utf-8'>
-  <title>Test for Bug 881976 - TextTrackCue Computed Position</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<p id="display"></p>
-<div id="content">
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
-  function() {
-    var video = document.createElement("video");
-
-    // Check if adding a text track manually sets the TextTarckList correctly.
-    video.addTextTrack("subtitles", "", "");
-    is(video.textTracks[0].textTrackList, video.textTracks, "The Track's TextTrackList should be the Video's TextTrackList.");
-
-
-    // Check if loading a Track via a TrackElement sets the TextTarckList correctly.
-    video.src = "seek.webm";
-    video.preload = "auto";
-
-    var trackElement = document.createElement("track");
-    trackElement.src = "basic.vtt";
-    trackElement.kind = "subtitles";
-
-    video.appendChild(trackElement);
-    document.getElementById("content").appendChild(video);
-
-    video.addEventListener("loadedmetadata", function run_tests() {
-      // Re-que run_tests() at the end of the event loop until the track
-      // element has loaded its data.
-      if (trackElement.readyState == 1) {
-        setTimeout(run_tests, 0);
-        return;
-      }
-      is(trackElement.readyState, 2, "Track::ReadyState should be set to LOADED.");
-      is(trackElement.track.textTrackList, video.textTracks, "TrackElement's Track's TextTrackList should be the Video's TextTrackList.");
-
-      SimpleTest.finish();
-    });
-  }
-);
-
-</script>
-</pre>
-</body>
-</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_texttrack_moz.html
@@ -0,0 +1,66 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=881976
+-->
+<head>
+  <meta charset='utf-8'>
+  <title>Test for Bug 881976 - TextTrackCue Computed Position</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [["media.webvtt.regions.enabled", true]]},
+    function() {
+      var video = document.createElement("video");
+
+      // Check if adding a text track manually sets the TextTrackList correctly.
+
+      // HTMLTrackElement.textTrackList is an extension available only to
+      // privileged code, so we need to access it through the SpecialPowers
+      // object.
+      video.addTextTrack("subtitles", "", "");
+      is(SpecialPowers.unwrap(SpecialPowers.wrap(video.textTracks[0]).textTrackList),
+          video.textTracks,
+          "The Track's TextTrackList should be the Video's TextTrackList.");
+
+
+      // Check if loading a Track via a TrackElement sets the TextTrackList correctly.
+      video.src = "seek.webm";
+      video.preload = "auto";
+
+      var trackElement = document.createElement("track");
+      trackElement.src = "basic.vtt";
+      trackElement.kind = "subtitles";
+
+      video.appendChild(trackElement);
+      document.getElementById("content").appendChild(video);
+
+      video.addEventListener("loadedmetadata", function run_tests() {
+        // Re-que run_tests() at the end of the event loop until the track
+        // element has loaded its data.
+        if (trackElement.readyState == HTMLTrackElement.LOADING) {
+          setTimeout(run_tests, 0);
+          return;
+        }
+        is(trackElement.readyState, HTMLTrackElement.LOADED,
+            "Track::ReadyState should be set to LOADED.");
+        is(SpecialPowers.unwrap(SpecialPowers.wrap(trackElement.track).textTrackList),
+           video.textTracks,
+           "TrackElement's Track's TextTrackList should be the Video's TextTrackList.");
+
+        SimpleTest.finish();
+      });
+    }
+  );
+</script>
+</pre>
+</body>
+</html>
deleted file mode 100644
--- a/dom/media/test/test_texttrackcue_chrome.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=967157
--->
-<head>
-  <meta charset='utf-8'>
-  <title>Test for Bug 967157 - Setting TextTrackCue::DisplayState should set TextTrackCue::HasBeenReset to false</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<p id="display"></p>
-<div id="content">
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-var cue = new VTTCue(0, 1, "Some text.");
-is(cue.hasBeenReset, false, "Cue's hasBeenReset flag should be false.");
-is(cue.displayState, null, "Cue's displayState should be null.");
-
-cue.startTime = 0.5;
-is(cue.hasBeenReset, true, "Cue's hasBeenReset flag should now be true.");
-
-cue.displayState = document.createElement("div");
-is(cue.hasBeenReset, false, "Cue's hasBeenReset flag should now be false.");
-SimpleTest.finish();
-</script>
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_texttrackcue_moz.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=967157
+-->
+<head>
+  <meta charset='utf-8'>
+  <title>Test for Bug 967157 - Setting TextTrackCue::DisplayState should set TextTrackCue::HasBeenReset to false</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+  SimpleTest.waitForExplicitFinish();
+
+  var cue = SpecialPowers.wrap(new VTTCue(0, 1, "Some text."));
+  is(cue.hasBeenReset, false, "Cue's hasBeenReset flag should be false.");
+  is(cue.displayState, null, "Cue's displayState should be null.");
+
+  cue.startTime = 0.5;
+  is(cue.hasBeenReset, true, "Cue's hasBeenReset flag should now be true.");
+
+  cue.displayState = document.createElement("div");
+  is(cue.hasBeenReset, false, "Cue's hasBeenReset flag should now be false.");
+
+  SimpleTest.finish();
+</script>
+</body>
+</html>
deleted file mode 100644
--- a/dom/media/test/test_texttracklist_chrome.html
+++ /dev/null
@@ -1,26 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=881976
--->
-<head>
-  <meta charset='utf-8'>
-  <title>Test for Bug 881976 - TextTrackCue Computed Position</title>
-  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<p id="display"></p>
-<div id="content">
-</div>
-<pre id="test">
-<script class="testbody" type="text/javascript">
-SimpleTest.waitForExplicitFinish();
-
-var video = document.createElement("video");
-is(video.textTracks.mediaElement, video, "Video's TextTrackList's MediaElement reference should be set to the video.");
-SimpleTest.finish();
-</script>
-</pre>
-</body>
-</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/media/test/test_texttracklist_moz.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=881976
+-->
+<head>
+  <meta charset='utf-8'>
+  <title>Test for Bug 881976 - TextTrackCue Computed Position</title>
+  <script type="text/javascript" src="/MochiKit/MochiKit.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<p id="display"></p>
+<div id="content">
+</div>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+SimpleTest.waitForExplicitFinish();
+
+var video = document.createElement("video");
+var trackList = video.textTracks;
+ok(trackList instanceof TextTrackList,
+   "Video's textTracks should be a TextTrackList");
+var trackParent = SpecialPowers.unwrap(
+    SpecialPowers.wrap(trackList).mediaElement
+);
+is(trackParent, video,
+   "Video's TextTrackList's MediaElement reference should be set to the video.");
+SimpleTest.finish();
+</script>
+</pre>
+</body>
+</html>
--- a/dom/plugins/base/nsJSNPRuntime.cpp
+++ b/dom/plugins/base/nsJSNPRuntime.cpp
@@ -126,38 +126,41 @@ inline bool
 NPObjectIsOutOfProcessProxy(NPObject *obj)
 {
   return obj->_class == PluginScriptableObjectParent::GetClass() ||
          obj->_class == PluginAsyncSurrogate::GetClass();
 }
 
 } // namespace
 
-// Helper class that reports any JS exceptions that were thrown while
-// the plugin executed JS.
-
-class MOZ_STACK_CLASS AutoJSExceptionReporter
+// Helper class that suppresses any JS exceptions that were thrown while
+// the plugin executed JS, if the nsJSObjWrapper has a destroy pending.
+// Note that this class is the product (vestige?) of a long evolution in how
+// error reporting worked, and hence the mIsDestroyPending check, and hence this
+// class in general, may or may not actually be necessary.
+
+class MOZ_STACK_CLASS AutoJSExceptionSuppressor
 {
 public:
-  AutoJSExceptionReporter(dom::AutoJSAPI& jsapi, nsJSObjWrapper* aWrapper)
-    : mJsapi(jsapi)
+  AutoJSExceptionSuppressor(dom::AutoEntryScript& aes, nsJSObjWrapper* aWrapper)
+    : mAes(aes)
     , mIsDestroyPending(aWrapper->mDestroyPending)
   {
-    jsapi.TakeOwnershipOfErrorReporting();
+    MOZ_ASSERT(aes.OwnsErrorReporting());
   }
 
-  ~AutoJSExceptionReporter()
+  ~AutoJSExceptionSuppressor()
   {
     if (mIsDestroyPending) {
-      mJsapi.ClearException();
+      mAes.ClearException();
     }
   }
 
 protected:
-  dom::AutoJSAPI& mJsapi;
+  dom::AutoEntryScript& mAes;
   bool mIsDestroyPending;
 };
 
 
 NPClass nsJSObjWrapper::sJSObjWrapperNPClass =
   {
     NP_CLASS_STRUCT_VERSION,
     nsJSObjWrapper::NP_Allocate,
@@ -723,34 +726,36 @@ GetProperty(JSContext *cx, JSObject *obj
   return ::JS_GetPropertyById(cx, obj, id, rval);
 }
 
 // static
 bool
 nsJSObjWrapper::NP_HasMethod(NPObject *npobj, NPIdentifier id)
 {
   NPP npp = NPPStack::Peek();
-  dom::AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetGlobalObject(npp)))) {
+  nsIGlobalObject* globalObject = GetGlobalObject(npp);
+  if (NS_WARN_IF(!globalObject)) {
     return false;
   }
-  JSContext *cx = jsapi.cx();
+
+  dom::AutoEntryScript aes(globalObject, "NPAPI HasMethod");
+  JSContext *cx = aes.cx();
 
   if (!npobj) {
     ThrowJSException(cx,
                      "Null npobj in nsJSObjWrapper::NP_HasMethod!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   JSAutoCompartment ac(cx, npjsobj->mJSObj);
 
-  AutoJSExceptionReporter reporter(jsapi, npjsobj);
+  AutoJSExceptionSuppressor suppressor(aes, npjsobj);
 
   JS::Rooted<JS::Value> v(cx);
   bool ok = GetProperty(cx, npjsobj->mJSObj, id, &v);
 
   return ok && !v.isPrimitive() &&
     ::JS_ObjectIsFunction(cx, v.toObjectOrNull());
 }
 
@@ -780,17 +785,17 @@ doInvoke(NPObject *npobj, NPIdentifier m
   VOID_TO_NPVARIANT(*result);
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
   JSAutoCompartment ac(cx, jsobj);
   JS::Rooted<JS::Value> fv(cx);
 
-  AutoJSExceptionReporter reporter(aes, npjsobj);
+  AutoJSExceptionSuppressor suppressor(aes, npjsobj);
 
   if (method != NPIdentifier_VOID) {
     if (!GetProperty(cx, jsobj, method, &fv) ||
         ::JS_TypeOfValue(cx, fv) != JSTYPE_FUNCTION) {
       return false;
     }
   } else {
     fv.setObject(*jsobj);
@@ -849,33 +854,35 @@ nsJSObjWrapper::NP_InvokeDefault(NPObjec
                   result);
 }
 
 // static
 bool
 nsJSObjWrapper::NP_HasProperty(NPObject *npobj, NPIdentifier npid)
 {
   NPP npp = NPPStack::Peek();
-  dom::AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetGlobalObject(npp)))) {
+  nsIGlobalObject* globalObject = GetGlobalObject(npp);
+  if (NS_WARN_IF(!globalObject)) {
     return false;
   }
-  JSContext *cx = jsapi.cx();
+
+  dom::AutoEntryScript aes(globalObject, "NPAPI HasProperty");
+  JSContext *cx = aes.cx();
 
   if (!npobj) {
     ThrowJSException(cx,
                      "Null npobj in nsJSObjWrapper::NP_HasProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   bool found, ok = false;
 
-  AutoJSExceptionReporter reporter(jsapi, npjsobj);
+  AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
   JSAutoCompartment ac(cx, jsobj);
 
   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
                "id must be either string or int!\n");
   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   ok = ::JS_HasPropertyById(cx, jsobj, id, &found);
   return ok && found;
@@ -902,17 +909,17 @@ nsJSObjWrapper::NP_GetProperty(NPObject 
     ThrowJSException(cx,
                      "Null npobj in nsJSObjWrapper::NP_GetProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
-  AutoJSExceptionReporter reporter(aes, npjsobj);
+  AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JSAutoCompartment ac(cx, npjsobj->mJSObj);
 
   JS::Rooted<JS::Value> v(cx);
   return (GetProperty(cx, npjsobj->mJSObj, id, &v) &&
           JSValToNPVariant(npp, cx, v, result));
 }
 
 // static
@@ -937,17 +944,17 @@ nsJSObjWrapper::NP_SetProperty(NPObject 
                      "Null npobj in nsJSObjWrapper::NP_SetProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   bool ok = false;
 
-  AutoJSExceptionReporter reporter(aes, npjsobj);
+  AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::Rooted<JSObject*> jsObj(cx, npjsobj->mJSObj);
   JSAutoCompartment ac(cx, jsObj);
 
   JS::Rooted<JS::Value> v(cx, NPVariantToJSVal(npp, cx, value));
 
   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
                "id must be either string or int!\n");
   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
@@ -956,32 +963,34 @@ nsJSObjWrapper::NP_SetProperty(NPObject 
   return ok;
 }
 
 // static
 bool
 nsJSObjWrapper::NP_RemoveProperty(NPObject *npobj, NPIdentifier npid)
 {
   NPP npp = NPPStack::Peek();
-  dom::AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetGlobalObject(npp)))) {
+  nsIGlobalObject* globalObject = GetGlobalObject(npp);
+  if (NS_WARN_IF(!globalObject)) {
     return false;
   }
-  JSContext *cx = jsapi.cx();
+
+  dom::AutoEntryScript aes(globalObject, "NPAPI RemoveProperty");
+  JSContext *cx = aes.cx();
 
   if (!npobj) {
     ThrowJSException(cx,
                      "Null npobj in nsJSObjWrapper::NP_RemoveProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
-  AutoJSExceptionReporter reporter(jsapi, npjsobj);
+  AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::ObjectOpResult result;
   JS::Rooted<JSObject*> obj(cx, npjsobj->mJSObj);
   JSAutoCompartment ac(cx, obj);
 
   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
                "id must be either string or int!\n");
   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   if (!::JS_DeletePropertyById(cx, obj, id, result))
@@ -1005,35 +1014,37 @@ nsJSObjWrapper::NP_RemoveProperty(NPObje
 }
 
 //static
 bool
 nsJSObjWrapper::NP_Enumerate(NPObject *npobj, NPIdentifier **idarray,
                              uint32_t *count)
 {
   NPP npp = NPPStack::Peek();
-  dom::AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetGlobalObject(npp)))) {
+  nsIGlobalObject* globalObject = GetGlobalObject(npp);
+  if (NS_WARN_IF(!globalObject)) {
     return false;
   }
-  JSContext *cx = jsapi.cx();
+
+  dom::AutoEntryScript aes(globalObject, "NPAPI Enumerate");
+  JSContext *cx = aes.cx();
 
   *idarray = 0;
   *count = 0;
 
   if (!npobj) {
     ThrowJSException(cx,
                      "Null npobj in nsJSObjWrapper::NP_Enumerate!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
 
-  AutoJSExceptionReporter reporter(jsapi, npjsobj);
+  AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
   JSAutoCompartment ac(cx, jsobj);
 
   JS::Rooted<JS::IdVector> ida(cx, JS::IdVector(cx));
   if (!JS_Enumerate(cx, jsobj, &ida)) {
     return false;
   }
 
@@ -2275,33 +2286,35 @@ NPObjectMember_toPrimitive(JSContext *cx
   return true;
 }
 
 // static
 bool
 nsJSObjWrapper::HasOwnProperty(NPObject *npobj, NPIdentifier npid)
 {
   NPP npp = NPPStack::Peek();
-  dom::AutoJSAPI jsapi;
-  if (NS_WARN_IF(!jsapi.InitWithLegacyErrorReporting(GetGlobalObject(npp)))) {
+  nsIGlobalObject* globalObject = GetGlobalObject(npp);
+  if (NS_WARN_IF(!globalObject)) {
     return false;
   }
-  JSContext *cx = jsapi.cx();
+
+  dom::AutoEntryScript aes(globalObject, "NPAPI HasOwnProperty");
+  JSContext *cx = aes.cx();
 
   if (!npobj) {
     ThrowJSException(cx,
                      "Null npobj in nsJSObjWrapper::NP_HasOwnProperty!");
 
     return false;
   }
 
   nsJSObjWrapper *npjsobj = (nsJSObjWrapper *)npobj;
   bool found, ok = false;
 
-  AutoJSExceptionReporter reporter(jsapi, npjsobj);
+  AutoJSExceptionSuppressor suppressor(aes, npjsobj);
   JS::Rooted<JSObject*> jsobj(cx, npjsobj->mJSObj);
   JSAutoCompartment ac(cx, jsobj);
 
   NS_ASSERTION(NPIdentifierIsInt(npid) || NPIdentifierIsString(npid),
                "id must be either string or int!\n");
   JS::Rooted<jsid> id(cx, NPIdentifierToJSId(npid));
   ok = ::JS_AlreadyHasOwnPropertyById(cx, jsobj, id, &found);
   return ok && found;
--- a/dom/plugins/base/nsNPAPIPlugin.cpp
+++ b/dom/plugins/base/nsNPAPIPlugin.cpp
@@ -1352,17 +1352,16 @@ bool
 
   nsGlobalWindow* win = nsGlobalWindow::Cast(doc->GetInnerWindow());
   if (NS_WARN_IF(!win || !win->FastGetGlobalJSObject())) {
     return false;
   }
 
   nsAutoMicroTask mt;
   dom::AutoEntryScript aes(win, "NPAPI NPN_evaluate");
-  aes.TakeOwnershipOfErrorReporting();
   JSContext* cx = aes.cx();
 
   JS::Rooted<JSObject*> obj(cx, nsNPObjWrapper::GetNewOrUsed(npp, cx, npobj));
 
   if (!obj) {
     return false;
   }
 
--- a/dom/system/gonk/AudioManager.cpp
+++ b/dom/system/gonk/AudioManager.cpp
@@ -1356,49 +1356,37 @@ AudioManager::VolumeStreamState::SetVolu
   bool exist = mVolumeIndexes.Get(device, &oldVolumeIndex);
   if (exist && aIndex == oldVolumeIndex) {
     // No update
     return NS_OK;
   }
 
   // AudioPolicyManager::setStreamVolumeIndex() set volumes of all active
   // devices for stream.
-  nsresult rv = SetVolumeIndex(aIndex, device);
+  nsresult rv;
+  rv = SetVolumeIndexToConsistentDeviceIfNeeded(aIndex, device);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
-  // Workaround to make audio volume control consisitent.
-  // Active devices of AUDIO_STREAM_NOTIFICATION are affected by
-  // AUDIO_STREAM_MUSIC's activity. It makes audio volume control inconsistent.
-  // See Bug 1196724
-  if (device != AUDIO_DEVICE_OUT_SPEAKER &&
-      mStreamType == AUDIO_STREAM_NOTIFICATION) {
-      // Rescaling of index is not necessary.
-      rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        return rv;
-      }
-  }
-
   return NS_OK;
 }
 
 nsresult
 AudioManager::VolumeStreamState::SetVolumeIndexToAliasStreams(uint32_t aIndex,
                                                               uint32_t aDevice)
 {
   uint32_t oldVolumeIndex = 0;
   bool exist = mVolumeIndexes.Get(aDevice, &oldVolumeIndex);
   if (exist && aIndex == oldVolumeIndex) {
     // No update
     return NS_OK;
   }
 
-  nsresult rv = SetVolumeIndex(aIndex, aDevice);
+  nsresult rv = SetVolumeIndexToConsistentDeviceIfNeeded(aIndex, aDevice);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
   for (int32_t streamType = 0; streamType < AUDIO_STREAM_MAX; streamType++) {
     if ((streamType != mStreamType) &&
          sStreamVolumeAliasTbl[streamType] == mStreamType) {
       // Rescaling of index is not necessary.
@@ -1409,16 +1397,37 @@ AudioManager::VolumeStreamState::SetVolu
       }
     }
   }
 
   return NS_OK;
 }
 
 nsresult
+AudioManager::VolumeStreamState::SetVolumeIndexToConsistentDeviceIfNeeded(uint32_t aIndex, uint32_t aDevice)
+{
+  nsresult rv;
+  if (aDevice == AUDIO_DEVICE_OUT_SPEAKER || aDevice == AUDIO_DEVICE_OUT_EARPIECE) {
+    // Set AUDIO_DEVICE_OUT_SPEAKER and AUDIO_DEVICE_OUT_EARPIECE to same volume.
+    rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_SPEAKER);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    rv = SetVolumeIndex(aIndex, AUDIO_DEVICE_OUT_EARPIECE);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  } else {
+    // No alias device
+    rv = SetVolumeIndex(aIndex, aDevice);
+  }
+  return rv;  
+}
+
+nsresult
 AudioManager::VolumeStreamState::SetVolumeIndex(uint32_t aIndex,
                                                 uint32_t aDevice,
                                                 bool aUpdateCache)
 {
   status_t rv;
 #if ANDROID_VERSION >= 17
   if (aUpdateCache) {
     mVolumeIndexes.Put(aDevice, aIndex);
--- a/dom/system/gonk/AudioManager.h
+++ b/dom/system/gonk/AudioManager.h
@@ -82,16 +82,17 @@ public:
     uint32_t GetVolumeIndex();
     uint32_t GetVolumeIndex(uint32_t aDevice);
     void ClearCurrentVolumeUpdated();
     // Set volume index to all active devices.
     // Active devices are chosen by android AudioPolicyManager.
     nsresult SetVolumeIndexToActiveDevices(uint32_t aIndex);
     // Set volume index to all alias streams for device. Alias streams have same volume.
     nsresult SetVolumeIndexToAliasStreams(uint32_t aIndex, uint32_t aDevice);
+    nsresult SetVolumeIndexToConsistentDeviceIfNeeded(uint32_t aIndex, uint32_t aDevice);
     nsresult SetVolumeIndex(uint32_t aIndex, uint32_t aDevice, bool aUpdateCache = true);
     // Restore volume index to all devices. Called when AudioFlinger is restarted.
     void RestoreVolumeIndexToAllDevices();
   private:
     AudioManager& mManager;
     const int32_t mStreamType;
     uint32_t mLastDevices;
     bool mIsDevicesChanged;
--- a/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -1,46 +1,57 @@
 [DEFAULT]
-skip-if = e10s # Bug ?????? - most of these tests fail for currently unknown reasons.
 support-files =
   browser_frame_elements.html
   page_privatestorageevent.html
   position.html
   test-console-api.html
   test_bug1004814.html
   worker_bug1004814.js
   geo_leak_test.html
 
 [browser_test_toolbars_visibility.js]
+skip-if = e10s
 support-files =
   test_new_window_from_content_child.html
 [browser_bug1008941_dismissGeolocationHanger.js]
 skip-if = buildapp == 'mulet'
 [browser_test__content.js]
+skip-if = e10s
 [browser_ConsoleAPITests.js]
+skip-if = e10s
 [browser_ConsoleStorageAPITests.js]
+skip-if = e10s
 [browser_ConsoleStoragePBTest_perwindowpb.js]
+skip-if = e10s
 [browser_autofocus_background.js]
 skip-if= buildapp == 'mulet'
 [browser_autofocus_preference.js]
 [browser_bug1238427.js]
+skip-if = e10s
 [browser_bug396843.js]
+skip-if = e10s
 [browser_focus_steal_from_chrome.js]
+skip-if = e10s
 [browser_focus_steal_from_chrome_during_mousedown.js]
+skip-if = e10s
 [browser_frame_elements.js]
+skip-if = e10s
 [browser_localStorage_privatestorageevent.js]
+skip-if = e10s
 [browser_test_new_window_from_content.js]
-skip-if = (toolkit == 'android' || buildapp == 'b2g' || buildapp == 'mulet')
+skip-if = (toolkit == 'android' || buildapp == 'b2g' || buildapp == 'mulet') || e10s
 support-files =
   test_new_window_from_content_child.html
 [browser_webapps_permissions.js]
 # TODO: Re-enable permissions tests on Mac, bug 795334
-skip-if = buildapp != "b2g"
+skip-if = buildapp != "b2g" || e10s
 support-files =
   test-webapp.webapp
   test-webapp-reinstall.webapp
   test-webapp-original.webapp
   test-webapps-permissions.html
 [browser_webapps_perms_reinstall.js]
 disabled = re-enable when bug 794920 is fixed
 [browser_xhr_sandbox.js]
-skip-if= buildapp == 'mulet'
+skip-if= buildapp == 'mulet' || e10s
 [browser_bug1004814.js]
+skip-if = e10s
--- a/dom/tests/browser/browser_autofocus_background.js
+++ b/dom/tests/browser/browser_autofocus_background.js
@@ -1,55 +1,37 @@
-function test() {
-  waitForExplicitFinish();
-
-  let fm = Components.classes["@mozilla.org/focus-manager;1"]
-                     .getService(Components.interfaces.nsIFocusManager);
-
+add_task(function* () {
   let tabs = [ gBrowser.selectedTab, gBrowser.addTab() ];
 
   // The first tab has an autofocused element.
   // The second tab is exactly like the first one without the autofocus.
   let testingList = [
     { uri: "data:text/html,<!DOCTYPE html><html><body><input autofocus id='target'></body></html>",
       tagName: "INPUT"},
   ];
 
-  function runTest() {
-    // Set the focus to the first tab.
-    tabs[0].linkedBrowser.focus();
+  // Set the focus to the first tab.
+  tabs[0].linkedBrowser.focus();
+
+  // Load the second tab in the background.
+  let loadedPromise = BrowserTestUtils.browserLoaded(tabs[1].linkedBrowser);
+  tabs[1].linkedBrowser.loadURI(testingList[0].uri);
+  yield loadedPromise;
 
-    // Load the first tab on background.
-    tabs[1].linkedBrowser.addEventListener("load", onLoadBackgroundTab, true);
-    tabs[1].linkedBrowser.loadURI(testingList[0].uri);
-  }
+  for (var i = 0; i < testingList.length; ++i) {
+    // Get active element in the tab.
+    let tagName = yield ContentTask.spawn(tabs[i + 1].linkedBrowser, null, function* () {
+      return content.document.activeElement.tagName;
+    });
 
-  function onLoadBackgroundTab() {
-    tabs[1].linkedBrowser.removeEventListener("load", onLoadBackgroundTab, true);
-
-    // The focus event (from autofocus) has been sent,
-    // let's test with executeSoon so we are sure the test is done
-    // after the focus event is processed.
-    executeSoon(doTest);
+    is(tagName, testingList[i].tagName,
+       "The background tab's focused element should be " + testingList[i].tagName);
   }
 
-  function doTest() {
-    for (var i=0; i<testingList.length; ++i) {
-      // Get active element in the tab.
-      var e = tabs[i+1].linkedBrowser.contentDocument.activeElement;
+  is(document.activeElement, tabs[0].linkedBrowser,
+     "The background tab's focused element should not cause the tab to be focused");
 
-      is(e.tagName, testingList[i].tagName,
-         "The background tab's focused element should be " +
-         testingList[i].tagName);
-      isnot(fm.focusedElement, e,
-            "The background tab's focused element should not be the focus " +
-            "manager focused element");
-    }
+  // Cleaning up.
+  for (let i = 1; i < tabs.length; i++) {
+    yield BrowserTestUtils.removeTab(tabs[i]);
+  }
+});
 
-    // Cleaning up.
-    for (let i = 1; i < tabs.length; i++) {
-      gBrowser.removeTab(tabs[i]);
-    }
-    finish();
-  }
-
-  runTest();
-}
--- a/dom/tests/browser/browser_autofocus_preference.js
+++ b/dom/tests/browser/browser_autofocus_preference.js
@@ -1,24 +1,18 @@
-function test() {
-  waitForExplicitFinish();
+add_task(function* () {
+  yield new Promise(resolve => {
+    SpecialPowers.pushPrefEnv({"set": [["browser.autofocus", false]]}, resolve);
+  });
 
-  var prefs = Components.classes["@mozilla.org/preferences-service;1"]
-            .getService(Components.interfaces.nsIPrefBranch);
-  var gAutofocusPref = prefs.getBoolPref("browser.autofocus");
-  prefs.setBoolPref("browser.autofocus", false);
-
-  gBrowser.selectedBrowser.addEventListener("load", function () {
-    gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
+  const url = "data:text/html,<!DOCTYPE html><html><body><input autofocus><button autofocus></button><textarea autofocus></textarea><select autofocus></select></body></html>";
 
-    executeSoon(function () {
-      is(gBrowser.selectedBrowser.contentDocument.activeElement,
-         gBrowser.selectedBrowser.contentDocument.body,
-         "foo");
+  let loadedPromise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
+  gBrowser.selectedBrowser.loadURI(url);
+  yield loadedPromise;
 
-      prefs.setBoolPref("browser.autofocus", gAutofocusPref);
+  yield new Promise(resolve => executeSoon(resolve));
 
-      finish();
-    });
-  }, true);
+  yield ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+    is(content.document.activeElement, content.document.body, "body focused");
+  });
+});
 
-  gBrowser.selectedBrowser.loadURI("data:text/html,<!DOCTYPE html><html><body><input autofocus><button autofocus></button><textarea autofocus></textarea><select autofocus></select></body></html>");
-}
--- a/dom/tests/browser/browser_bug1008941_dismissGeolocationHanger.js
+++ b/dom/tests/browser/browser_bug1008941_dismissGeolocationHanger.js
@@ -5,55 +5,24 @@
 "use strict";
 
 const TEST_URI = "http://example.com/" +
                  "browser/dom/tests/browser/position.html";
 
 add_task(function testDismissHanger() {
   info("Check that location is not shared when dismissing the geolocation hanger");
 
-  let promisePanelShown = waitForPanelShow();
-
-  gBrowser.selectedTab = gBrowser.addTab(TEST_URI);
-  yield waitForPageLoad(gBrowser.selectedTab);
-  info("Page was loaded");
-
+  let promisePanelShown = BrowserTestUtils.waitForEvent(PopupNotifications.panel, "popupshown", true);
+  yield BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URI);
   yield promisePanelShown;
-  info("Panel is shown");
 
   // click outside the Geolocation hanger to dismiss it
   window.document.getElementById("nav-bar").click();
   info("Clicked outside the Geolocation panel to dismiss it");
 
-  let result = gBrowser.getBrowserForTab(gBrowser.selectedTab)
-                       .contentDocument.body.innerHTML;
-  ok(result.includes("location..."), "Location is not shared");
-});
-
-add_task(function asyncCleanup() {
-  // close the tab
-  gBrowser.removeTab(gBrowser.selectedTab);
-  info("Cleanup: Closed the tab");
-});
+  let hasLocation = yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function*() {
+    return content.document.body.innerHTML.includes("location...");
+  });
 
-function waitForPageLoad(aTab) {
-  return new Promise(resolve => {
-    function onTabLoad(event) {
-      aTab.linkedBrowser.removeEventListener("load", onTabLoad, true);
-      info("Load tab event received");
-      resolve();
-    };
-
-    aTab.linkedBrowser.addEventListener("load", onTabLoad, true, true);
-  });
-}
+  ok(hasLocation, "Location is not shared");
 
-function waitForPanelShow(aPanel) {
-  return new Promise(resolve => {
-    function onPopupShown(event) {
-      PopupNotifications.panel.removeEventListener("popupshown", onPopupShown, true);
-      info("Popup shown event received");
-      resolve();
-    }
-
-    PopupNotifications.panel.addEventListener("popupshown", onPopupShown, true, true);
-  });
-}
+  yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
+});
--- a/dom/tests/mochitest/bugs/mochitest.ini
+++ b/dom/tests/mochitest/bugs/mochitest.ini
@@ -42,17 +42,17 @@ support-files =
 [test_bug159849.html]
 [test_bug260264.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(dom.disable_open_during_load not implemented in b2g) b2g-debug(dom.disable_open_during_load not implemented in b2g) b2g-desktop(dom.disable_open_during_load not implemented in b2g)
 [test_bug260264_nested.html]
 skip-if = buildapp == 'b2g' # b2g(dom.disable_open_during_load not implemented in b2g) b2g-debug(dom.disable_open_during_load not implemented in b2g) b2g-desktop(dom.disable_open_during_load not implemented in b2g)
 [test_bug265203.html]
 [test_bug291377.html]
 [test_bug291653.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #TIMED_OUT # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
 [test_bug304459.html]
 [test_bug308856.html]
 [test_bug327891.html]
 [test_bug333983.html]
 [test_bug335976.xhtml]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 [test_bug342448.html]
 [test_bug345521.html]
@@ -68,40 +68,40 @@ skip-if = buildapp == 'mulet' || (builda
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug393974.html]
 [test_bug394769.html]
 [test_bug396843.html]
 skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 [test_bug400204.html]
 [test_bug404748.html]
 [test_bug406375.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug411103.html]
 [test_bug414291.html]
 [test_bug427744.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android'
 [test_bug42976.html]
 [test_bug430276.html]
 [test_bug437361.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(dom.disable_open_during_load not implemented in b2g, showmodaldialog) b2g-debug(dom.disable_open_during_load not implemented in b2g, showmodaldialog) b2g-desktop(dom.disable_open_during_load not implemented in b2g, showmodaldialog)
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # b2g(dom.disable_open_during_load not implemented in b2g, showmodaldialog) b2g-debug(dom.disable_open_during_load not implemented in b2g, showmodaldialog) b2g-desktop(dom.disable_open_during_load not implemented in b2g, showmodaldialog)
 [test_bug440572.html]
 [test_bug456151.html]
 [test_bug458091.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug459848.html]
 [test_bug465263.html]
 [test_bug479143.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
 [test_bug484775.html]
 [test_bug492925.html]
 [test_bug49312.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug495219.html]
 [test_bug504862.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #RANDOM # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #RANDOM # b2g(showmodaldialog) b2g-debug(showmodaldialog) b2g-desktop(showmodaldialog)
 [test_bug529328.html]
 [test_bug531176.html]
 [test_bug531542.html]
 [test_bug534149.html]
 [test_bug541530.html]
 [test_bug545314.html]
 [test_bug548828.html]
 [test_bug558973.html]
--- a/dom/tests/mochitest/bugs/test_bug291653.html
+++ b/dom/tests/mochitest/bugs/test_bug291653.html
@@ -18,30 +18,39 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 
 /** Test for Bug 291653 **/
 SimpleTest.waitForExplicitFinish();
 SimpleTest.requestFlakyTimeout("untriaged");
 
 var secondListenerDidRun = false;
 
-var w = window.open("file_bug291653.html", "foo", "width=300,height=300");
+var w;
+function start() {
+  if ("showModalDialog" in window) {
+    w = window.open("file_bug291653.html", "foo", "width=300,height=300");
+  } else {
+    // window.showModalDialog doesn't exist in e10s mode, nothing to do in this test.
+    ok(true, "nothing to do in e10s mode");
+    SimpleTest.finish();
+  }
+}
 
 function closeTest() {
   w.setTimeout("close()", 0);
   setTimeout("finish()", 500);
 }
 
 function finish() {
   ok(!secondListenerDidRun, "Shouldn't have run second listener!");
   SimpleTest.finish();
 }
 
 function end() {
   setTimeout("closeTest()", 500);
 }
 
-
+start();
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/bugs/test_bug406375.html
+++ b/dom/tests/mochitest/bugs/test_bug406375.html
@@ -18,17 +18,19 @@ https://bugzilla.mozilla.org/show_bug.cg
 <script type="application/javascript">
 
 /** Test for Bug 406375 **/
 
 
 SimpleTest.waitForExplicitFinish();
 
 function runTest() {
-  window.showModalDialog("file_bug406375.html");
+  if ("showModalDialog" in window) {
+    window.showModalDialog("file_bug406375.html");
+  }
   ok(true, "This test should not hang");
 
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
--- a/dom/tests/mochitest/bugs/test_bug437361.html
+++ b/dom/tests/mochitest/bugs/test_bug437361.html
@@ -27,26 +27,30 @@ https://bugzilla.mozilla.org/show_bug.cg
   }
 
   function testOtherExceptionsNotTrapped() {
     is(false, SpecialPowers.getBoolPref("dom.disable_open_during_load"), "mozprefs sanity check");
     window.showModalDialog('about:config'); // forbidden by SecurityCheckURL
   }
 
   function test(disableOpen, exceptionExpected, testFn, errorMsg) {
-    var oldPrefVal = SpecialPowers.getBoolPref("dom.disable_open_during_load");
-    try {
-      SpecialPowers.setBoolPref("dom.disable_open_during_load", disableOpen);
-      testFn();
-      ok(!exceptionExpected, errorMsg);
-    } catch (_) {
-      ok(exceptionExpected, errorMsg);
-    }
-    finally {
-      SpecialPowers.setBoolPref("dom.disable_open_during_load", oldPrefVal);
+    if ("showModalDialog" in window) {
+      var oldPrefVal = SpecialPowers.getBoolPref("dom.disable_open_during_load");
+      try {
+        SpecialPowers.setBoolPref("dom.disable_open_during_load", disableOpen);
+        testFn();
+        ok(!exceptionExpected, errorMsg);
+      } catch (_) {
+        ok(exceptionExpected, errorMsg);
+      }
+      finally {
+        SpecialPowers.setBoolPref("dom.disable_open_during_load", oldPrefVal);
+      }
+    } else {
+      ok(true, "nothing to do in e10s mode");
     }
   }
 
   test(true, false, testModalDialogBlockedCleanly,
        "Blocked showModalDialog caused an exception.");
        
   test(false, false, testModalDialogAllowed,
        "showModalDialog was blocked even though dom.disable_open_during_load was false.");
--- a/dom/tests/mochitest/bugs/test_bug479143.html
+++ b/dom/tests/mochitest/bugs/test_bug479143.html
@@ -14,27 +14,29 @@ https://bugzilla.mozilla.org/show_bug.cg
 <div id="content" style="display: none"></div>
 
 <pre id="test">
 <script class="testbody" type="text/javascript">
 
   SimpleTest.waitForExplicitFinish();
 
   setTimeout(function() {
-    var interval = setInterval(function() { var i = 0; i++; }, 10);
+    if ("showModalDialog" in window) {
+      var interval = setInterval(function() { var i = 0; i++; }, 10);
 
-    var xhr = new XMLHttpRequest();
-    xhr.open("GET", "test_bug479143.html", false);
-    xhr.send(null);
+      var xhr = new XMLHttpRequest();
+      xhr.open("GET", "test_bug479143.html", false);
+      xhr.send(null);
 
-    window.showModalDialog("javascript:" +
-                           "setTimeout(function() { window.close(); }, 1000);",
-                           null);
+      window.showModalDialog("javascript:" +
+                             "setTimeout(function() { window.close(); }, 1000);",
+                             null);
 
-    clearInterval(interval);
+      clearInterval(interval);
+    }
 
     ok(true, "did not crash");
 
     SimpleTest.finish();
   }, 0);
 
 </script>
 </pre>
--- a/dom/tests/mochitest/bugs/test_bug504862.html
+++ b/dom/tests/mochitest/bugs/test_bug504862.html
@@ -15,27 +15,31 @@ https://bugzilla.mozilla.org/show_bug.cg
 /** Test for Bug 504862 **/
 SimpleTest.waitForExplicitFinish();
 function onMsgRcv(event)
 {
   is(event.data, "args: undefined", "Unexpected cross origin dialog arguments.");
 }
 
 function runTest() {
-  window.addEventListener("message", onMsgRcv, false);
+  if ("showModalDialog" in window) {
+    window.addEventListener("message", onMsgRcv, false);
 
-  var result = window.showModalDialog("file_bug504862.html", "my args");
-  // NB: We used to clear returnValue on each navigation, but now we do a
-  // security check on access, so we can safely make returnValue live on
-  // the browsing context, per spec.
-  is(result, 3, "window sees previous dialog documents return value.");
+    var result = window.showModalDialog("file_bug504862.html", "my args");
+    // NB: We used to clear returnValue on each navigation, but now we do a
+    // security check on access, so we can safely make returnValue live on
+    // the browsing context, per spec.
+    is(result, 3, "window sees previous dialog documents return value.");
 
-  result = window.showModalDialog("http://test1.example.com/tests/dom/tests/mochitest/bugs/file_bug504862.html", "my args");
+    result = window.showModalDialog("http://test1.example.com/tests/dom/tests/mochitest/bugs/file_bug504862.html", "my args");
 
-  is(result, undefined, "Able to see return value from cross origin dialog.");
+    is(result, undefined, "Able to see return value from cross origin dialog.");
+  } else {
+    ok(true, "nothing to do in e10s mode");
+  }
 
   SimpleTest.finish();
 }
 
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -53,27 +53,27 @@ skip-if = ((buildapp == 'mulet' || build
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug628069_2.html]
 [test_bug631440.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug653364.html]
 [test_bug861217.html]
 [test_clientRects.html]
 [test_clipboard_events.html]
-skip-if = e10s || buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
+skip-if = buildapp == 'b2g' # b2g(clipboard undefined) b2g-debug(clipboard undefined) b2g-desktop(clipboard undefined)
 [test_consoleAPI.html]
 [test_DOMMatrix.html]
 [test_domWindowUtils.html]
 [test_domWindowUtils_scrollXY.html]
 [test_domWindowUtils_scrollbarSize.html]
 [test_donottrack.html]
 skip-if = buildapp == 'mulet'
 [test_focus_legend_noparent.html]
 [test_focusrings.xul]
-skip-if = e10s || buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
 [test_for_of.html]
 [test_frameElementWrapping.html]
 [test_pointerPreserves3D.html]
 [test_pointerPreserves3DClip.html]
 [test_framedhistoryframes.html]
 [test_idleapi_permissions.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_img_mutations.html]
--- a/dom/tests/mochitest/general/test_clipboard_events.html
+++ b/dom/tests/mochitest/general/test_clipboard_events.html
@@ -26,84 +26,74 @@ var clipboardInitialValue = "empty";
 // Test that clearing and reading the clipboard works.  A random number
 // is used to make sure that leftover clipboard values from a previous
 // test run don't cause a false-positive test.
 var cb_text = "empty_" + Math.random();
 setClipboardText(cb_text);
 
 is(getClipboardText(), cb_text, "set/get clipboard text failed");
 
-// Some test functions need to be run with delays.
-var delayedTests = [];
-
 var cachedCutData, cachedCopyData, cachedPasteData;
 
-// Ensure window focus before running tests, otherwise key events can
-// misfire.  We set the onfocus event handler here to actually begin
-// running tests, and call window.focus() afterwards.
-window.onfocus = function()
-{
-  window.onfocus = null;
+// A list of test functions to run.  Before each test function is run, the
+// clipboard is initialized to clipboardInitialValue, and the contents of
+// div#content are set as the window's selection.
+var testFunctions = [
+  test_dom_oncopy,
+  test_dom_oncut,
+  test_dom_onpaste,
+  test_dom_oncopy_abort,
+  test_input_oncopy,
+  test_input_oncut,
+  test_input_onpaste,
+  test_input_oncopy_abort,
+  test_input_oncut_abort,
+  test_input_onpaste_abort,
+  test_input_cut_dataTransfer,
+  test_input_cut_abort_dataTransfer,
+  test_input_copy_dataTransfer,
+  test_input_paste_dataTransfer,
+  test_input_paste_abort_dataTransfer,
+  test_input_copypaste_dataTransfer_multiple,
+  test_input_copy_button_dataTransfer,
+  test_eventspref_disabled
+  ];
 
-  // A list of test functions to run.  Before each test function is run, the
-  // clipboard is initialized to clipboardInitialValue, and the contents of
-  // div#content are set as the window's selection.
-  var testFunctions = [
-    test_dom_oncopy,
-    test_dom_oncut,
-    test_dom_onpaste,
-    test_dom_oncopy_abort,
-    test_input_oncopy,
-    test_input_oncut,
-    test_input_onpaste,
-    test_input_oncopy_abort,
-    test_input_oncut_abort,
-    test_input_onpaste_abort,
-    test_input_cut_dataTransfer,
-    test_input_cut_abort_dataTransfer,
-    test_input_copy_dataTransfer,
-    test_input_paste_dataTransfer,
-    test_input_paste_abort_dataTransfer,
-    test_input_copypaste_dataTransfer_multiple,
-    test_input_copy_button_dataTransfer,
-    test_eventspref_disabled
-    ];
+function doTests()
+{
+  // Init clipboard
+  setClipboardText(clipboardInitialValue);
+
+  // Reset value of contentInput.
+  contentInput.value = "INPUT TEXT";
 
-  // Run the main tests.  This will also populate the delayedTests array
-  for (let i = 0; i < testFunctions.length; i++) {
-    // Init clipboard
-    setClipboardText(clipboardInitialValue);
-
-    // Reset value of contentInput.
-    contentInput.value = "INPUT TEXT";
-
-    var func = testFunctions[i];
-    func();
+  if (testFunctions.length) {
+    let func = testFunctions.shift();
+    let result = func();
+    if (result instanceof Promise) {
+      result.then(doTests);
+    }
+    else {
+      doTests();
+    }
   }
+  else {
+    // Check if the cached clipboard data can be accessed or modified
+    // and whether it modifies the real clipboard
+    checkCachedDataTransfer(cachedCutData, "cut");
+    checkCachedDataTransfer(cachedCopyData, "copy");
+    checkCachedDataTransfer(cachedPasteData, "paste");
 
-  // Check if the cached clipboard data can be accessed or modified
-  // and whether it modifies the real clipboard
-  checkCachedDataTransfer(cachedCutData, "cut");
-  checkCachedDataTransfer(cachedCopyData, "copy");
-  checkCachedDataTransfer(cachedPasteData, "paste");
+    checkSyntheticEvents();
 
-  checkSyntheticEvents();
-
-  SimpleTest.finish();
+    SimpleTest.finish();
+  }
 }
 
-// Calling .focus begins the test run.
 SimpleTest.waitForExplicitFinish();
-window.focus();
-
-function getLoadContext() {
-  return SpecialPowers.wrap(window).QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
-               .getInterface(SpecialPowers.Ci.nsIWebNavigation)
-               .QueryInterface(SpecialPowers.Ci.nsILoadContext);
-}
 
 function getClipboardText() {
   return SpecialPowers.getClipboardData("text/unicode");
 }
 
 
 function setClipboardText(text) {
   var helper = SpecialPowers.Cc["@mozilla.org/widget/clipboardhelper;1"]
@@ -530,48 +520,52 @@ function test_input_copy_button_dataTran
 
   } finally {
     document.documentElement.oncopy = null;
   }
 }
 
 function test_eventspref_disabled() {
   // Disable clipboard events
-  SpecialPowers.setBoolPref("dom.event.clipboardevents.enabled", false);
+  return new Promise(resolve => {
+    SpecialPowers.pushPrefEnv({"set": [['dom.event.clipboardevents.enabled', false]]}, doPrefDisabledTest);
 
-  var event_fired = false;
-  contentInput.oncut = function() { event_fired = true; };
-  contentInput.oncopy = function() { event_fired = true; };
-  contentInput.onpaste = function() { event_fired = true; };
-  try {
-    selectContentInput();
-    contentInput.setSelectionRange(1, 4);
-    synthesizeKey("x", {accelKey: 1});
-    is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled");
-    is(getClipboardText(), "NPU", "cut changed clipboard when preference is disabled");
-    ok(!event_fired, "cut event did not fire when preference is disabled")
+    function doPrefDisabledTest() {
+      var event_fired = false;
+      contentInput.oncut = function() { event_fired = true; };
+      contentInput.oncopy = function() { event_fired = true; };
+      contentInput.onpaste = function() { event_fired = true; };
+      try {
+        selectContentInput();
+        contentInput.setSelectionRange(1, 4);
+        synthesizeKey("x", {accelKey: 1});
+        is(contentInput.value, "IT TEXT", "cut changed text when preference is disabled");
+        is(getClipboardText(), "NPU", "cut changed clipboard when preference is disabled");
+        ok(!event_fired, "cut event did not fire when preference is disabled")
 
-    event_fired = false;
-    contentInput.setSelectionRange(3, 6);
-    synthesizeKey("c", {accelKey: 1});
-    is(getClipboardText(), "TEX", "copy changed clipboard when preference is disabled");
-    ok(!event_fired, "copy event did not fire when preference is disabled")
+        event_fired = false;
+        contentInput.setSelectionRange(3, 6);
+        synthesizeKey("c", {accelKey: 1});
+        is(getClipboardText(), "TEX", "copy changed clipboard when preference is disabled");
+        ok(!event_fired, "copy event did not fire when preference is disabled")
 
-    event_fired = false;
-    contentInput.setSelectionRange(0, 2);
-    synthesizeKey("v", {accelKey: 1});
-    is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled");
-    ok(!event_fired, "paste event did not fire when preference is disabled")
-  } finally {
-    contentInput.oncut = null;
-    contentInput.oncopy = null;
-    contentInput.onpaste = null;
-  }
+        event_fired = false;
+        contentInput.setSelectionRange(0, 2);
+        synthesizeKey("v", {accelKey: 1});
+        is(contentInput.value, "TEX TEXT", "paste changed text when preference is disabled");
+        ok(!event_fired, "paste event did not fire when preference is disabled")
+      } finally {
+        contentInput.oncut = null;
+        contentInput.oncopy = null;
+        contentInput.onpaste = null;
+      }
 
-  SpecialPowers.clearUserPref("dom.event.clipboardevents.enabled");
+      SpecialPowers.popPrefEnv(resolve);
+    }
+  });
 }
 
 let expectedData = [];
 
 // Check to make that synthetic events do not change the clipboard
 function checkSyntheticEvents()
 {
   let syntheticSpot = document.getElementById("syntheticSpot");
@@ -670,12 +664,14 @@ function checkCachedDataTransfer(cd, eve
 
   var exh = false;
   try { cd.mozClearDataAt("text/plain", 0); } catch (ex) { exh = true; }
   ok(eventtype == "paste" ? exh : !exh, "exception occured clearing " + testprefix);
 
   is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix);
 }
 
+SimpleTest.waitForFocus(doTests);
+
 </script>
 </pre>
 </body>
 </html>
--- a/dom/tests/mochitest/general/test_focusrings.xul
+++ b/dom/tests/mochitest/general/test_focusrings.xul
@@ -16,41 +16,30 @@
 #l2:focus, #b2:focus { outline: 2px solid red; }
 </html:style>
 
 <script>
 <![CDATA[
 
 SimpleTest.waitForExplicitFinish();
 
-function setOrRestoreTabFocus(newValue) {
-  const prefSvcContractID = "@mozilla.org/preferences-service;1";
-  const prefSvcIID = SpecialPowers.Ci.nsIPrefService;
-  var prefs = SpecialPowers.Cc[prefSvcContractID].getService(prefSvcIID)
-                                                   .getBranch("accessibility.");
-  if (!newValue) {
-    if (prefs.prefHasUserValue("tabfocus")) {
-      prefs.clearUserPref("tabfocus");
-    }
-  } else {
-    prefs.setIntPref("tabfocus", newValue);
-  }
-}
-
 function snapShot(element) {
   var rect = element.getBoundingClientRect();
   adjustedRect = { left: rect.left - 6, top: rect.top - 6,
                    width: rect.width + 12, height: rect.height + 12 }
   return SpecialPowers.snapshotRect(window, adjustedRect, "transparent");
 }
 
+function initTest()
+{
+  SpecialPowers.pushPrefEnv({"set": [['accessibility.tabfocus', 7]]}, runTest);
+}
+
 function runTest()
 {
-  setOrRestoreTabFocus(7);
-
   var isMac = (navigator.platform.indexOf("Mac") >= 0);
   var isWin = (navigator.platform.indexOf("Win") >= 0);
 
   function checkFocus(element, visible, testid)
   {
     var outline = getComputedStyle(element, "").outlineWidth;
     is(outline, visible ? "2px" : "0px", testid);
   }
@@ -68,26 +57,16 @@ function runTest()
   // we can't really test the situation on Windows where a dialog doesn't show
   // focus rings until a key is pressed, as the default state depends on what
   // kind of real user input, mouse or key, was last entered. But we can handle
   // the test regardless of which user input last occurred.
   $("l1").focus();
   var expectedVisible = (!isWin || getComputedStyle($("l1"), "").outlineWidth == "2px");
   testHTMLElements(htmlElements, isMac, isWin && !expectedVisible);
 
-  if (isMac) {
-    var prefs = SpecialPowers.Cc["@mozilla.org/preferences-service;1"].
-                  getService(SpecialPowers.Ci.nsIPrefBranch);
-    prefs.setBoolPref("accessibility.mouse_focuses_formcontrol", true);
-
-    testHTMLElements(htmlElementsMacPrefSet, true, false);
-
-    prefs.setBoolPref("accessibility.mouse_focuses_formcontrol", false);
-  }
-
   $("l1").focus();
   checkFocus($("l1"), expectedVisible, "appearance on list after focus() with :moz-focusring");
   $("l2").focus();
 
   checkFocus($("l2"), true, "appearance on list after focus() with :focus");
 
   is(getComputedStyle($("l1"), "").outlineWidth, "0px", "appearance on previous list after focus() with :focus");
 
@@ -105,17 +84,27 @@ function runTest()
   synthesizeMouse($("b2"), 4, 4, { });
   checkFocus($("b2"), !isMac, "appearance on button after mouse focus with :focus");
 
   // after a key is pressed, the focus ring will always be visible
   $("l2").focus();
   synthesizeKey("VK_TAB", { });
   checkFocus($("l3"), true, "appearance on list after tab focus");
 
-  setOrRestoreTabFocus(0);
+  if (isMac) {
+    SpecialPowers.pushPrefEnv({"set": [['accessibility.mouse_focuses_formcontrol', true]]}, testMacFocusesFormControl);
+  }
+  else {
+    SimpleTest.finish();
+  }
+}
+
+function testMacFocusesFormControl()
+{
+  testHTMLElements(htmlElementsMacPrefSet, true, false);
   SimpleTest.finish();
 }
 
 var htmlElements = [
   "<button id='elem'>Button</button>",
   "<input id='elem' class='canfocus'>",
   "<input id='elem' type='password' class='canfocus'>",
   "<input id='elem' type='button'>",
--- a/dom/webidl/AnimationEffectTiming.webidl
+++ b/dom/webidl/AnimationEffectTiming.webidl
@@ -16,14 +16,15 @@ interface AnimationEffectTiming : Animat
    //inherit attribute double                             delay;
    inherit attribute double                             endDelay;
    //Bug 1244637 - implement AnimationEffectTiming fill
    //inherit attribute FillMode                           fill;
    //Bug 1244638 - implement AnimationEffectTiming iterationStart
    //inherit attribute double                             iterationStart;
    //Bug 1244640 - implement AnimationEffectTiming iterations
    //inherit attribute unrestricted double                iterations;
-   inherit attribute (unrestricted double or DOMString) duration;
+  [SetterThrows]
+  inherit attribute (unrestricted double or DOMString) duration;
    //Bug 1244642 - implement AnimationEffectTiming direction
    //inherit attribute PlaybackDirection                  direction;
    //Bug 1244643 - implement AnimationEffectTiming easing
    //inherit attribute DOMString                          easing;
 };
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -356,29 +356,29 @@ struct RespondWithClosure
   {
   }
 };
 
 void RespondWithCopyComplete(void* aClosure, nsresult aStatus)
 {
   nsAutoPtr<RespondWithClosure> data(static_cast<RespondWithClosure*>(aClosure));
   nsCOMPtr<nsIRunnable> event;
-  if (NS_SUCCEEDED(aStatus)) {
-    event = new FinishResponse(data->mInterceptedChannel,
-                               data->mInternalResponse,
-                               data->mWorkerChannelInfo,
-                               data->mScriptSpec,
-                               data->mResponseURLSpec);
-  } else {
+  if (NS_WARN_IF(NS_FAILED(aStatus))) {
     AsyncLog(data->mInterceptedChannel, data->mRespondWithScriptSpec,
              data->mRespondWithLineNumber, data->mRespondWithColumnNumber,
              NS_LITERAL_CSTRING("InterceptionFailedWithURL"),
              data->mRequestURL);
     event = new CancelChannelRunnable(data->mInterceptedChannel,
                                       NS_ERROR_INTERCEPTION_FAILED);
+  } else {
+    event = new FinishResponse(data->mInterceptedChannel,
+                               data->mInternalResponse,
+                               data->mWorkerChannelInfo,
+                               data->mScriptSpec,
+                               data->mResponseURLSpec);
   }
   MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(event)));
 }
 
 namespace {
 
 void
 ExtractErrorValues(JSContext* aCx, JS::Handle<JS::Value> aValue,
@@ -628,25 +628,44 @@ RespondWithHandler::ResolvedCallback(JSC
     response->SetBodyUsed();
 
     nsCOMPtr<nsIOutputStream> responseBody;
     rv = mInterceptedChannel->GetResponseBody(getter_AddRefs(responseBody));
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
 
+    const uint32_t kCopySegmentSize = 4096;
+
+    // Depending on how the Response passed to .respondWith() was created, we may
+    // get a non-buffered input stream.  In addition, in some configurations the
+    // destination channel's output stream can be unbuffered.  We wrap the output
+    // stream side here so that NS_AsyncCopy() works.  Wrapping the output side
+    // provides the most consistent operation since there are fewer stream types
+    // we are writing to.  The input stream can be a wide variety of concrete
+    // objects which may or many not play well with NS_InputStreamIsBuffered().
+    if (!NS_OutputStreamIsBuffered(responseBody)) {
+      nsCOMPtr<nsIOutputStream> buffered;
+      rv = NS_NewBufferedOutputStream(getter_AddRefs(buffered), responseBody,
+           kCopySegmentSize);
+      if (NS_WARN_IF(NS_FAILED(rv))) {
+        return;
+      }
+      responseBody = buffered;
+    }
+
     nsCOMPtr<nsIEventTarget> stsThread = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
     if (NS_WARN_IF(!stsThread)) {
       return;
     }
 
     // XXXnsm, Fix for Bug 1141332 means that if we decide to make this
     // streaming at some point, we'll need a different solution to that bug.
-    rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_READSEGMENTS, 4096,
-                      RespondWithCopyComplete, closure.forget());
+    rv = NS_AsyncCopy(body, responseBody, stsThread, NS_ASYNCCOPY_VIA_WRITESEGMENTS,
+                      kCopySegmentSize, RespondWithCopyComplete, closure.forget());
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
   } else {
     RespondWithCopyComplete(closure.forget(), NS_OK);
   }
 
   MOZ_ASSERT(!closure);
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -6006,19 +6006,17 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
     if (info->mIsInterval) {
       reason = "setInterval handler";
     } else {
       reason = "setTimeout handler";
     }
 
     { // scope for the AutoEntryScript, so it comes off the stack before we do
       // Promise::PerformMicroTaskCheckpoint.
-      AutoEntryScript entryScript(xpc::NativeGlobal(global), reason,
-                                  false, aCx);
-      entryScript.TakeOwnershipOfErrorReporting();
+      AutoEntryScript aes(global, reason, false, aCx);
       if (!info->mTimeoutCallable.isUndefined()) {
         JS::Rooted<JS::Value> rval(aCx);
         JS::HandleValueArray args =
           JS::HandleValueArray::fromMarkedLocation(info->mExtraArgVals.Length(),
                                                    info->mExtraArgVals.Elements()->address());
         JS::Rooted<JS::Value> callable(aCx, info->mTimeoutCallable);
         if (!JS_CallFunctionValue(aCx, global, callable, args, &rval) &&
             !JS_IsExceptionPending(aCx)) {
--- a/dom/workers/WorkerRunnable.cpp
+++ b/dom/workers/WorkerRunnable.cpp
@@ -322,18 +322,18 @@ WorkerRunnable::Run()
                 isMainThread ? nullptr : GetCurrentThreadJSContext());
     jsapi = aes.ptr();
     cx = aes->cx();
   } else {
     maybeJSAPI.emplace();
     maybeJSAPI->Init();
     jsapi = maybeJSAPI.ptr();
     cx = jsapi->cx();
+    jsapi->TakeOwnershipOfErrorReporting();
   }
-  jsapi->TakeOwnershipOfErrorReporting();
 
   // Note that we can't assert anything about mWorkerPrivate->GetWrapper()
   // existing, since it may in fact have been GCed (and we may be one of the
   // runnables cleaning up the worker as a result).
 
   // If we are on the parent thread and that thread is not the main thread,
   // then we must be a dedicated worker (because there are no
   // Shared/ServiceWorkers whose parent is itself a worker) and then we
deleted file mode 100644
--- a/dom/workers/test/extensions/bootstrap/Makefile.in
+++ /dev/null
@@ -1,12 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
-
-GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
-
-include $(topsrcdir)/config/rules.mk
-
-libs::
-	@(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
--- a/dom/workers/test/extensions/bootstrap/bootstrap.js
+++ b/dom/workers/test/extensions/bootstrap/bootstrap.js
@@ -21,27 +21,17 @@ var gWorkerAndCallback = {
   _ensureStarted: function() {
     if (!this._worker) {
       throw new Error("Not yet started!");
     }
   },
 
   start: function(data) {
     if (!this._worker) {
-      var file = data.installPath;
-      var fileuri = file.isDirectory() ?
-                    Services.io.newFileURI(file) :
-                    Services.io.newURI('jar:' + file.path + '!/', null, null);
-      var resourceName = encodeURIComponent(data.id);
-
-      Services.io.getProtocolHandler("resource").
-                  QueryInterface(Ci.nsIResProtocolHandler).
-                  setSubstitution(resourceName, fileuri);
-
-      this._worker = new Worker("resource://" + resourceName + "/worker.js");
+      this._worker = new Worker("chrome://workerbootstrap/content/worker.js");
       this._worker.onerror = function(event) {
         Cu.reportError(event.message);
         event.preventDefault();
       };
     }
   },
 
   stop: function() {
--- a/dom/workers/test/extensions/bootstrap/install.rdf
+++ b/dom/workers/test/extensions/bootstrap/install.rdf
@@ -2,21 +2,30 @@
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <Description about="urn:mozilla:install-manifest">
     <em:name>WorkerTestBootstrap</em:name>
     <em:description>Worker functions for use in testing.</em:description>
     <em:creator>Mozilla</em:creator>
-    <em:version>2011.10.10</em:version>
-#expand    <em:id>__XPI_NAME__-test@mozilla.org</em:id>
+    <em:version>2016.03.09</em:version>
+    <em:id>workerbootstrap-test@mozilla.org</em:id>
     <em:type>2</em:type>
     <em:bootstrap>true</em:bootstrap>
     <em:targetApplication>
       <Description>
-        <em:id>toolkit@mozilla.org</em:id>
-#expand        <em:minVersion>__MOZILLA_VERSION_U__</em:minVersion>
-#expand        <em:maxVersion>__MOZILLA_VERSION_U__</em:maxVersion>
+        <!-- Firefox -->
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>45.0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+    <em:targetApplication>
+      <Description>
+        <!-- Fennec -->
+        <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
+        <em:minVersion>45.0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
       </Description>
     </em:targetApplication>
   </Description>
 </RDF>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/extensions/bootstrap/jar.mn
@@ -0,0 +1,3 @@
+workerbootstrap.jar:
+% content workerbootstrap %content/
+  content/worker.js (worker.js)
--- a/dom/workers/test/extensions/bootstrap/moz.build
+++ b/dom/workers/test/extensions/bootstrap/moz.build
@@ -1,16 +1,20 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 XPI_NAME = 'workerbootstrap'
 
-FINAL_TARGET_PP_FILES += [
+JAR_MANIFESTS += ['jar.mn']
+USE_EXTENSION_MANIFEST = True
+NO_JS_MANIFEST = True
+
+FINAL_TARGET_FILES += [
+    'bootstrap.js',
     'install.rdf',
 ]
 
-FINAL_TARGET_FILES += [
-    'bootstrap.js',
-    'worker.js',
+TEST_HARNESS_FILES.testing.mochitest.extensions += [
+    'workerbootstrap-test@mozilla.org.xpi',
 ]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..2dab975db0c8293b5e63a1e7a14bc632d253e890
GIT binary patch
literal 7202
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
AxBvhE
deleted file mode 100644
--- a/dom/workers/test/extensions/traditional/Makefile.in
+++ /dev/null
@@ -1,12 +0,0 @@
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
-
-GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
-
-include $(topsrcdir)/config/rules.mk
-
-libs::
-	@(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
--- a/dom/workers/test/extensions/traditional/WorkerTest.js
+++ b/dom/workers/test/extensions/traditional/WorkerTest.js
@@ -17,27 +17,17 @@ var gWorkerAndCallback = {
   _ensureStarted: function() {
     if (!this._worker) {
       throw new Error("Not yet started!");
     }
   },
 
   start: function() {
     if (!this._worker) {
-      var file = __LOCATION__.parent.parent;
-      var fileuri = file.isDirectory() ?
-                    Services.io.newFileURI(file) :
-                    Services.io.newURI('jar:' + file.path + '!/', null, null);
-      var resourceName = "worker-test";
-
-      Services.io.getProtocolHandler("resource").
-                  QueryInterface(Ci.nsIResProtocolHandler).
-                  setSubstitution(resourceName, fileuri);
-
-      var worker = new Worker("resource://" + resourceName + "/worker.js");
+      var worker = new Worker("chrome://worker/content/worker.js");
       worker.onerror = function(event) {
         Cu.reportError(event.message);
         event.preventDefault();
       };
 
       this._worker = worker;
     }
   },
--- a/dom/workers/test/extensions/traditional/install.rdf
+++ b/dom/workers/test/extensions/traditional/install.rdf
@@ -2,20 +2,29 @@
 
 <RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
      xmlns:em="http://www.mozilla.org/2004/em-rdf#">
 
   <Description about="urn:mozilla:install-manifest">
     <em:name>WorkerTest</em:name>
     <em:description>Worker functions for use in testing.</em:description>
     <em:creator>Mozilla</em:creator>
-    <em:version>2011.10.10</em:version>
-#expand    <em:id>__XPI_NAME__-test@mozilla.org</em:id>
+    <em:version>2016.03.09</em:version>
+    <em:id>worker-test@mozilla.org</em:id>
     <em:type>2</em:type>
     <em:targetApplication>
       <Description>
-        <em:id>toolkit@mozilla.org</em:id>
-#expand        <em:minVersion>__MOZILLA_VERSION_U__</em:minVersion>
-#expand        <em:maxVersion>__MOZILLA_VERSION_U__</em:maxVersion>
+        <!-- Firefox -->
+        <em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
+        <em:minVersion>45.0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+    <em:targetApplication>
+      <Description>
+        <!-- Fennec -->
+        <em:id>{aa3c5121-dab2-40e2-81ca-7ea25febc110}</em:id>
+        <em:minVersion>45.0</em:minVersion>
+        <em:maxVersion>*</em:maxVersion>
       </Description>
     </em:targetApplication>
   </Description>
 </RDF>
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/extensions/traditional/jar.mn
@@ -0,0 +1,3 @@
+worker.jar:
+% content worker %content/
+  content/worker.js (worker.js)
--- a/dom/workers/test/extensions/traditional/moz.build
+++ b/dom/workers/test/extensions/traditional/moz.build
@@ -12,15 +12,19 @@ XPIDL_MODULE = 'WorkerTest'
 
 EXTRA_COMPONENTS += [
     'WorkerTest.js',
     'WorkerTest.manifest',
 ]
 
 XPI_NAME = 'worker'
 
-FINAL_TARGET_PP_FILES += [
+JAR_MANIFESTS += ['jar.mn']
+USE_EXTENSION_MANIFEST = True
+NO_JS_MANIFEST = True
+
+FINAL_TARGET_FILES += [
     'install.rdf',
 ]
 
-FINAL_TARGET_FILES += [
-    'worker.js',
+TEST_HARNESS_FILES.testing.mochitest.extensions += [
+    'worker-test@mozilla.org.xpi',
 ]
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..8d2386894c720d74ce83a474c353a5b4d417f948
GIT binary patch
literal 8333
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
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/file_blob_response_worker.js
@@ -0,0 +1,38 @@
+function makeFileBlob(obj) {
+  return new Promise(function(resolve, reject) {
+    var request = indexedDB.open('file_blob_response_worker', 1);
+    request.onerror = reject;
+    request.onupgradeneeded = function(evt) {
+      var db = evt.target.result;
+      db.onerror = reject;
+
+      var objectStore = db.createObjectStore('test', { autoIncrement: true });
+      var index = objectStore.createIndex('test', 'index');
+    };
+
+    request.onsuccess = function(evt) {
+      var db = evt.target.result;
+      db.onerror = reject;
+
+      var blob = new Blob([JSON.stringify(obj)],
+                          { type: 'application/json' });
+      var data = { blob: blob, index: 5 };
+
+      objectStore = db.transaction('test', 'readwrite').objectStore('test');
+      objectStore.add(data).onsuccess = function(evt) {
+        var key = evt.target.result;
+        objectStore = db.transaction('test').objectStore('test');
+        objectStore.get(key).onsuccess = function(evt) {
+          resolve(evt.target.result.blob);
+        };
+      };
+    };
+  });
+}
+
+self.addEventListener('fetch', function(evt) {
+  var result = { value: 'success' };
+  evt.respondWith(makeFileBlob(result).then(function(blob) {
+    return new Response(blob)
+  }));
+});
--- a/dom/workers/test/serviceworkers/mochitest.ini
+++ b/dom/workers/test/serviceworkers/mochitest.ini
@@ -192,16 +192,17 @@ support-files =
   xslt_worker.js
   xslt/*
   unresolved_fetch_worker.js
   header_checker.sjs
   openWindow_worker.js
   redirect.sjs
   open_window/client.html
   lorem_script.js
+  file_blob_response_worker.js
 
 [test_bug1151916.html]
 [test_claim.html]
 [test_claim_fetch.html]
 [test_claim_oninstall.html]
 [test_close.html]
 [test_controller.html]
 [test_cross_origin_url_after_redirect.html]
@@ -288,13 +289,14 @@ skip-if = e10s && debug && os == 'win'
 [test_eventsource_intercept.html]
 [test_not_intercept_plugin.html]
 [test_file_blob_upload.html]
 [test_unresolved_fetch_interception.html]
 [test_hsts_upgrade_intercept.html]
 [test_csp_upgrade-insecure_intercept.html]
 [test_serviceworker_header.html]
 [test_openWindow.html]
-skip-if = toolkit == "android" || toolkit == "gonk" || (e10s && debug && os == 'win')
+skip-if = toolkit == "gonk" || (e10s && debug && os == 'win')
 [test_imagecache.html]
 [test_imagecache_max_age.html]
 [test_importscript_mixedcontent.html]
 tags = mcb
+[test_file_blob_response.html]
new file mode 100644
--- /dev/null
+++ b/dom/workers/test/serviceworkers/test_file_blob_response.html
@@ -0,0 +1,86 @@
+<!--
+  Any copyright is dedicated to the Public Domain.
+  http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Bug 1253777 - Test interception using file blob response body</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<p id="display"></p>
+<div id="content" style="display: none"></div>
+<pre id="test"></pre>
+<script class="testbody" type="text/javascript">
+  var registration;
+  var scope = './file_blob_response/';
+  function start() {
+    return navigator.serviceWorker.register("file_blob_response_worker.js",
+                                            { scope: scope })
+      .then(function(swr) {
+        registration = swr;
+        return new Promise(function(resolve) {
+          registration.installing.onstatechange = function(evt) {
+            if (evt.target.state === 'activated') {
+              evt.target.onstate = null;
+              resolve();
+            }
+          }
+        });
+      });
+  }
+
+  function unregister() {
+    return registration.unregister().then(function(result) {
+      ok(result, "Unregister should return true.");
+    }, function(e) {
+      ok(false, "Unregistering the SW failed with " + e + "\n");
+    });
+  }
+
+  function withFrame(url) {
+    return new Promise(function(resolve, reject) {
+      var content = document.getElementById("content");
+      ok(content, "Parent exists.");
+
+      var frame = document.createElement("iframe");
+      frame.setAttribute('src', url);
+      content.appendChild(frame);
+
+      frame.addEventListener('load', function loadCallback(evt) {
+        frame.removeEventListener('load', loadCallback);
+        resolve(frame);
+      });
+    });
+  }
+
+  function runTest() {
+    start()
+      .then(function() {
+        return withFrame(scope + 'dummy.txt');
+      })
+      .then(function(frame) {
+        var result = JSON.parse(frame.contentWindow.document.body.textContent);
+        frame.remove();
+        is(result.value, 'success');
+      })
+      .catch(function(e) {
+        ok(false, "Some test failed with error " + e);
+      })
+      .then(unregister)
+      .then(SimpleTest.finish);
+  }
+
+  SimpleTest.waitForExplicitFinish();
+  SpecialPowers.pushPrefEnv({"set": [
+    ["dom.serviceWorkers.exemptFromPerDomainMax", true],
+    ["dom.serviceWorkers.enabled", true],
+    ["dom.serviceWorkers.testing.enabled", true]
+  ]}, runTest);
+</script>
+</pre>
+</body>
+</html>
+
--- a/dom/xbl/nsXBLProtoImplField.cpp
+++ b/dom/xbl/nsXBLProtoImplField.cpp
@@ -395,19 +395,18 @@ nsXBLProtoImplField::InstallField(JS::Ha
 
   nsIGlobalObject* globalObject = xpc::WindowGlobalOrNull(aBoundNode);
   if (!globalObject) {
     return NS_OK;
   }
 
   // We are going to run script via EvaluateString, so we need a script entry
   // point, but as this is XBL related it does not appear in the HTML spec.
-  AutoEntryScript entryScript(globalObject, "XBL <field> initialization", true);
-  entryScript.TakeOwnershipOfErrorReporting();
-  JSContext* cx = entryScript.cx();
+  AutoEntryScript aes(globalObject, "XBL <field> initialization", true);
+  JSContext* cx = aes.cx();
 
   NS_ASSERTION(!::JS_IsExceptionPending(cx),
                "Shouldn't get here when an exception is pending!");
 
   JSAddonId* addonId = MapURIToAddonID(aBindingDocURI);
 
   Element* boundElement = nullptr;
   nsresult rv = UNWRAP_OBJECT(Element, aBoundNode, boundElement);
@@ -435,17 +434,17 @@ nsXBLProtoImplField::InstallField(JS::Ha
                                  scopeObject, options, evalOptions, &result);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   if (rv == NS_SUCCESS_DOM_SCRIPT_EVALUATION_THREW) {
     // Report the exception now, before we try using the JSContext for
     // the JS_DefineUCProperty call.
-    entryScript.ReportException();
+    aes.ReportException();
   }
 
   // Now, enter the node's compartment, wrap the eval result, and define it on
   // the bound node.
   JSAutoCompartment ac2(cx, aBoundNode);
   nsDependentString name(mName);
   if (!JS_WrapValue(cx, &result) ||
       !::JS_DefineUCProperty(cx, aBoundNode,
--- a/dom/xbl/nsXBLProtoImplMethod.cpp
+++ b/dom/xbl/nsXBLProtoImplMethod.cpp
@@ -285,17 +285,16 @@ nsXBLProtoImplAnonymousMethod::Execute(n
     return NS_OK;
   }
 
   nsAutoMicroTask mt;
 
   // We are going to run script via JS::Call, so we need a script entry point,
   // but as this is XBL related it does not appear in the HTML spec.
   dom::AutoEntryScript aes(global, "XBL <constructor>/<destructor> invocation");
-  aes.TakeOwnershipOfErrorReporting();
   JSContext* cx = aes.cx();
 
   JS::Rooted<JSObject*> globalObject(cx, global->GetGlobalJSObject());
 
   JS::Rooted<JSObject*> scopeObject(cx, xpc::GetScopeForXBLExecution(cx, globalObject, aAddonId));
   NS_ENSURE_TRUE(scopeObject, NS_ERROR_OUT_OF_MEMORY);
 
   JSAutoCompartment ac(cx, scopeObject);
--- a/dom/xul/XULDocument.cpp
+++ b/dom/xul/XULDocument.cpp
@@ -3498,17 +3498,16 @@ XULDocument::ExecuteScript(nsXULPrototyp
     NS_ENSURE_TRUE(scriptObject, NS_ERROR_UNEXPECTED);
 
     // Execute the precompiled script with the given version
     nsAutoMicroTask mt;
 
     // We're about to run script via JS::CloneAndExecuteScript, so we need an
     // AutoEntryScript. This is Gecko specific and not in any spec.
     AutoEntryScript aes(mScriptGlobalObject, "precompiled XUL <script> element");
-    aes.TakeOwnershipOfErrorReporting();
     JSContext* cx = aes.cx();
     JS::Rooted<JSObject*> baseGlobal(cx, JS::CurrentGlobalOrNull(cx));
     NS_ENSURE_TRUE(nsContentUtils::GetSecurityManager()->ScriptAllowed(baseGlobal), NS_OK);
 
     JSAddonId* addonId = mCurrentPrototype ? MapURIToAddonID(mCurrentPrototype->GetURI()) : nullptr;
     JS::Rooted<JSObject*> global(cx, xpc::GetAddonScope(cx, baseGlobal, addonId));
     NS_ENSURE_TRUE(global, NS_ERROR_FAILURE);
 
--- a/dom/xul/templates/nsXULTemplateBuilder.cpp
+++ b/dom/xul/templates/nsXULTemplateBuilder.cpp
@@ -1368,18 +1368,18 @@ nsXULTemplateBuilder::InitHTMLTemplateRo
     if (! global)
         return NS_ERROR_UNEXPECTED;
 
     nsCOMPtr<nsIGlobalObject> innerWin =
         do_QueryInterface(doc->GetInnerWindow());
 
     // We are going to run script via JS_SetProperty, so we need a script entry
     // point, but as this is XUL related it does not appear in the HTML spec.
-    AutoEntryScript entryScript(innerWin, "nsXULTemplateBuilder creation", true);
-    JSContext* jscontext = entryScript.cx();
+    AutoEntryScript aes(innerWin, "nsXULTemplateBuilder creation", true);
+    JSContext* jscontext = aes.cx();
 
     JS::Rooted<JS::Value> v(jscontext);
     rv = nsContentUtils::WrapNative(jscontext, mRoot, mRoot, &v);
     NS_ENSURE_SUCCESS(rv, rv);
 
     JS::Rooted<JSObject*> jselement(jscontext, v.toObjectOrNull());
 
     if (mDB) {
--- a/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp
+++ b/dom/xul/templates/nsXULTemplateQueryProcessorStorage.cpp
@@ -205,20 +205,22 @@ nsXULTemplateQueryProcessorStorage::GetD
 
         rv = databaseFile->AppendNative(path);
         NS_ENSURE_SUCCESS(rv, rv);
     }
     else {
         nsCOMPtr<nsIChannel> channel;
         nsCOMPtr<nsINode> node = do_QueryInterface(aRootNode);
 
+        // The following channel is never openend, so it does not matter what
+        // securityFlags we pass; let's follow the principle of least privilege.
         rv = NS_NewChannel(getter_AddRefs(channel),
                            uri,
                            node,
-                           nsILoadInfo::SEC_NORMAL,
+                           nsILoadInfo::SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED,
                            nsIContentPolicy::TYPE_OTHER);
         NS_ENSURE_SUCCESS(rv, rv);
 
         nsCOMPtr<nsIFileChannel> fileChannel = do_QueryInterface(channel, &rv);
         if (NS_FAILED(rv)) { // if it fails, not a file url
             nsXULContentUtils::LogTemplateError(ERROR_TEMPLATE_STORAGE_BAD_URI);
             return rv;
         }
--- a/editor/txmgr/nsTransactionStack.cpp
+++ b/editor/txmgr/nsTransactionStack.cpp
@@ -6,107 +6,104 @@
 #include "nsAutoPtr.h"
 #include "nsCOMPtr.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsISupportsUtils.h"
 #include "nsTransactionItem.h"
 #include "nsTransactionStack.h"
 #include "nscore.h"
 
-nsTransactionStack::nsTransactionStack(nsTransactionStack::Type aType)
-  : mType(aType)
+class nsTransactionStackDeallocator : public nsDequeFunctor {
+  virtual void* operator() (void* aObject) {
+    RefPtr<nsTransactionItem> releaseMe = dont_AddRef(static_cast<nsTransactionItem*>(aObject));
+    return nullptr;
+  }
+};
+
+nsTransactionStack::nsTransactionStack(Type aType)
+  : nsDeque(new nsTransactionStackDeallocator())
+  , mType(aType)
 {
 }
 
 nsTransactionStack::~nsTransactionStack()
 {
   Clear();
 }
 
 void
-nsTransactionStack::Push(nsTransactionItem *aTransaction)
+nsTransactionStack::Push(nsTransactionItem* aTransactionItem)
 {
-  if (!aTransaction) {
+  if (!aTransactionItem) {
     return;
   }
 
-  // The stack's bottom is the front of the deque, and the top is the back.
-  RefPtr<nsTransactionItem> item(aTransaction);
+  RefPtr<nsTransactionItem> item(aTransactionItem);
   Push(item.forget());
 }
 
 void
-nsTransactionStack::Push(already_AddRefed<nsTransactionItem> aTransaction)
+nsTransactionStack::Push(already_AddRefed<nsTransactionItem> aTransactionItem)
 {
-  RefPtr<nsTransactionItem> item(aTransaction);
+  RefPtr<nsTransactionItem> item(aTransactionItem);
   if (!item) {
     return;
   }
 
-  // XXX we really want to use emplace_back here, but we don't have a
-  // C++11 stdlib everywhere.
-  RefPtr<nsTransactionItem> dummy;
-  auto pushedItem = mDeque.insert(mDeque.end(), dummy);
-  *pushedItem = item.forget();
+  nsDeque::Push(item.forget().take());
 }
 
 already_AddRefed<nsTransactionItem>
 nsTransactionStack::Pop()
 {
-  if (mDeque.empty()) {
-    return nullptr;
-  }
-  RefPtr<nsTransactionItem> ret = mDeque.back().forget();
-  mDeque.pop_back();
-  return ret.forget();
+  RefPtr<nsTransactionItem> item =
+    dont_AddRef(static_cast<nsTransactionItem*>(nsDeque::Pop()));
+  return item.forget();
 }
 
 already_AddRefed<nsTransactionItem>
 nsTransactionStack::PopBottom()
 {
-  if (mDeque.empty()) {
-    return nullptr;
-  }
-  RefPtr<nsTransactionItem> ret = mDeque.front().forget();
-  mDeque.pop_front();
-  return ret.forget();
+  RefPtr<nsTransactionItem> item =
+    dont_AddRef(static_cast<nsTransactionItem*>(nsDeque::PopFront()));
+  return item.forget();
 }
 
 already_AddRefed<nsTransactionItem>
 nsTransactionStack::Peek()
 {
-  if (mDeque.empty()) {
-    return nullptr;
-  }
-  RefPtr<nsTransactionItem> ret = mDeque.back();
-  return ret.forget();
+  RefPtr<nsTransactionItem> item =
+    static_cast<nsTransactionItem*>(nsDeque::Peek());
+  return item.forget();
 }
 
 already_AddRefed<nsTransactionItem>
 nsTransactionStack::GetItem(int32_t aIndex)
 {
-  if (aIndex < 0 || aIndex >= static_cast<int32_t>(mDeque.size())) {
+  if (aIndex < 0 || aIndex >= static_cast<int32_t>(nsDeque::GetSize())) {
     return nullptr;
   }
-  RefPtr<nsTransactionItem> ret = mDeque[aIndex];
-  return ret.forget();
+  RefPtr<nsTransactionItem> item =
+    static_cast<nsTransactionItem*>(nsDeque::ObjectAt(aIndex));
+  return item.forget();
 }
 
 void
 nsTransactionStack::Clear()
 {
-  while (!mDeque.empty()) {
-    RefPtr<nsTransactionItem> tx = mType == FOR_UNDO ? Pop() : PopBottom();
+  while (GetSize() != 0) {
+    RefPtr<nsTransactionItem> item =
+      mType == FOR_UNDO ? Pop() : PopBottom();
   }
 }
 
 void
 nsTransactionStack::DoTraverse(nsCycleCollectionTraversalCallback &cb)
 {
-  int32_t size = mDeque.size();
+  int32_t size = GetSize();
   for (int32_t i = 0; i < size; ++i) {
-    nsTransactionItem* item = mDeque[i];
+    nsTransactionItem* item = static_cast<nsTransactionItem*>(nsDeque::ObjectAt(i));
     if (item) {
       NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "transaction stack mDeque[i]");
       cb.NoteNativeChild(item, NS_CYCLE_COLLECTION_PARTICIPANT(nsTransactionItem));
     }
   }
 }
--- a/editor/txmgr/nsTransactionStack.h
+++ b/editor/txmgr/nsTransactionStack.h
@@ -1,41 +1,40 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsTransactionStack_h__
 #define nsTransactionStack_h__
 
-#include <deque>
+#include "nsDeque.h"
 #include "nsAutoPtr.h"
 
 class nsCycleCollectionTraversalCallback;
 class nsTransactionItem;
 
-class nsTransactionStack
+class nsTransactionStack : private nsDeque
 {
 public:
   enum Type { FOR_UNDO, FOR_REDO };
 
   explicit nsTransactionStack(Type aType);
   ~nsTransactionStack();
 
   void Push(nsTransactionItem *aTransactionItem);
   void Push(already_AddRefed<nsTransactionItem> aTransactionItem);
   already_AddRefed<nsTransactionItem> Pop();
   already_AddRefed<nsTransactionItem> PopBottom();
   already_AddRefed<nsTransactionItem> Peek();
   already_AddRefed<nsTransactionItem> GetItem(int32_t aIndex);
   void Clear();
-  int32_t GetSize() { return mDeque.size(); }
-  bool IsEmpty() const { return mDeque.empty(); }
+  int32_t GetSize() const { return static_cast<int32_t>(nsDeque::GetSize()); }
+  bool IsEmpty() const { return GetSize() == 0; }
 
   void DoUnlink() { Clear(); }
   void DoTraverse(nsCycleCollectionTraversalCallback &cb);
 
 private:
-  std::deque<RefPtr<nsTransactionItem> > mDeque;
   const Type mType;
 };
 
 #endif // nsTransactionStack_h__
--- a/extensions/auth/nsHttpNegotiateAuth.cpp
+++ b/extensions/auth/nsHttpNegotiateAuth.cpp
@@ -33,30 +33,41 @@
 #include "plbase64.h"
 #include "plstr.h"
 #include "prprf.h"
 #include "mozilla/Logging.h"
 #include "prmem.h"
 #include "prnetdb.h"
 #include "mozilla/Likely.h"
 #include "mozilla/Snprintf.h"
+#include "nsIChannel.h"
+#include "nsNetUtil.h"
 
 //-----------------------------------------------------------------------------
 
 static const char kNegotiate[] = "Negotiate";
 static const char kNegotiateAuthTrustedURIs[] = "network.negotiate-auth.trusted-uris";
 static const char kNegotiateAuthDelegationURIs[] = "network.negotiate-auth.delegation-uris";
 static const char kNegotiateAuthAllowProxies[] = "network.negotiate-auth.allow-proxies";
 static const char kNegotiateAuthAllowNonFqdn[] = "network.negotiate-auth.allow-non-fqdn";
 static const char kNegotiateAuthSSPI[] = "network.auth.use-sspi";
 
 #define kNegotiateLen  (sizeof(kNegotiate)-1)
 
 //-----------------------------------------------------------------------------
 
+// Return false when the channel comes from a Private browsing window.
+static bool
+TestNotInPBMode(nsIHttpAuthenticableChannel *authChannel)
+{
+    nsCOMPtr<nsIChannel> bareChannel = do_QueryInterface(authChannel);
+    MOZ_ASSERT(bareChannel);
+    return !NS_UsePrivateBrowsing(bareChannel);
+}
+
 NS_IMETHODIMP
 nsHttpNegotiateAuth::GetAuthFlags(uint32_t *flags)
 {
     //
     // Negotiate Auth creds should not be reused across multiple requests.
     // Only perform the negotiation when it is explicitly requested by the
     // server.  Thus, do *NOT* use the "REUSABLE_CREDENTIALS" flag here.
     //
@@ -108,18 +119,19 @@ nsHttpNegotiateAuth::ChallengeReceived(n
         req_flags |= nsIAuthModule::REQ_PROXY_AUTH;
         nsCOMPtr<nsIProxyInfo> proxyInfo;
         authChannel->GetProxyInfo(getter_AddRefs(proxyInfo));
         NS_ENSURE_STATE(proxyInfo);
 
         proxyInfo->GetHost(service);
     }
     else {
-        bool allowed = TestNonFqdn(uri) ||
-                       TestPref(uri, kNegotiateAuthTrustedURIs);
+        bool allowed = TestNotInPBMode(authChannel) &&
+                       (TestNonFqdn(uri) ||
+                       TestPref(uri, kNegotiateAuthTrustedURIs));
         if (!allowed) {
             LOG(("nsHttpNegotiateAuth::ChallengeReceived URI blocked\n"));
             return NS_ERROR_ABORT;
         }
 
         bool delegation = TestPref(uri, kNegotiateAuthDelegationURIs);
         if (delegation) {
             LOG(("  using REQ_DELEGATE\n"));
--- a/gfx/layers/FrameMetrics.h
+++ b/gfx/layers/FrameMetrics.h
@@ -228,16 +228,22 @@ public:
 
   void CopySmoothScrollInfoFrom(const FrameMetrics& aOther)
   {
     mSmoothScrollOffset = aOther.mSmoothScrollOffset;
     mScrollGeneration = aOther.mScrollGeneration;
     mDoSmoothScroll = aOther.mDoSmoothScroll;
   }
 
+  void UpdateScrollInfo(uint32_t aScrollGeneration, const CSSPoint& aScrollOffset)
+  {
+    mScrollOffset = aScrollOffset;
+    mScrollGeneration = aScrollGeneration;
+  }
+
   // Make a copy of this FrameMetrics object which does not have any pointers
   // to heap-allocated memory (i.e. is Plain Old Data, or 'POD'), and is
   // therefore safe to be placed into shared memory.
   FrameMetrics MakePODObject() const
   {
     FrameMetrics copy = *this;
     copy.mContentDescription.Truncate();
     return copy;
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -3249,16 +3249,26 @@ void AsyncPanZoomController::NotifyLayer
                                                  bool aIsFirstPaint,
                                                  bool aThisLayerTreeUpdated)
 {
   APZThreadUtils::AssertOnCompositorThread();
 
   ReentrantMonitorAutoEnter lock(mMonitor);
   bool isDefault = mFrameMetrics.IsDefault();
 
+  if (!aThisLayerTreeUpdated && !isDefault) {
+    // No new information here, skip it. Note that this is not just an
+    // optimization; it's correctness too. In the case where we get one of these
+    // stale aLayerMetrics *after* a call to NotifyScrollUpdated, processing the
+    // stale aLayerMetrics would clobber the more up-to-date information from
+    // NotifyScrollUpdated.
+    MOZ_ASSERT(aLayerMetrics == mLastContentPaintMetrics);
+    APZC_LOG("%p NotifyLayersUpdated short-circuit\n", this);
+    return;
+  }
   mLastContentPaintMetrics = aLayerMetrics;
 
   mFrameMetrics.SetScrollParentId(aLayerMetrics.GetScrollParentId());
   APZC_LOG_FM(aLayerMetrics, "%p got a NotifyLayersUpdated with aIsFirstPaint=%d, aThisLayerTreeUpdated=%d",
     this, aIsFirstPaint, aThisLayerTreeUpdated);
 
   { // scope lock
     MutexAutoLock lock(mCheckerboardEventLock);
@@ -3452,16 +3462,45 @@ void AsyncPanZoomController::NotifyLayer
 
   if (needContentRepaint) {
     RequestContentRepaint();
   }
   UpdateSharedCompositorFrameMetrics();
 }
 
 void
+AsyncPanZoomController::NotifyScrollUpdated(uint32_t aScrollGeneration,
+                                            const CSSPoint& aScrollOffset)
+{
+  APZThreadUtils::AssertOnCompositorThread();
+  ReentrantMonitorAutoEnter lock(mMonitor);
+
+  APZC_LOG("%p NotifyScrollUpdated(%d, %s)\n", this, aScrollGeneration,
+      Stringify(aScrollOffset).c_str());
+
+  bool scrollOffsetUpdated = aScrollGeneration != mFrameMetrics.GetScrollGeneration();
+  if (!scrollOffsetUpdated) {
+    return;
+  }
+  APZC_LOG("%p updating scroll offset from %s to %s\n", this,
+      Stringify(mFrameMetrics.GetScrollOffset()).c_str(),
+      Stringify(aScrollOffset).c_str());
+
+  mFrameMetrics.UpdateScrollInfo(aScrollGeneration, aScrollOffset);
+  AcknowledgeScrollUpdate();
+  mExpectedGeckoMetrics.UpdateScrollInfo(aScrollGeneration, aScrollOffset);
+  CancelAnimation();
+  RequestContentRepaint();
+  UpdateSharedCompositorFrameMetrics();
+  // We don't call ScheduleComposite() here because that happens higher up
+  // in the call stack, when LayerTransactionParent handles this message.
+  // If we did it here it would incur an extra message posting unnecessarily.
+}
+
+void
 AsyncPanZoomController::AcknowledgeScrollUpdate() const
 {
   // Once layout issues a scroll offset update, it becomes impervious to
   // scroll offset updates from APZ until we acknowledge the update it sent.
   // This prevents APZ updates from clobbering scroll updates from other
   // more "legitimate" sources like content scripts.
   RefPtr<GeckoContentController> controller = GetGeckoContentController();
   if (controller) {
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -183,16 +183,23 @@ public:
    * |aIsFirstPaint| is a flag passed from the shadow
    * layers code indicating that the frame metrics being sent with this call are
    * the initial metrics and the initial paint of the frame has just happened.
    */
   void NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint,
                            bool aThisLayerTreeUpdated);
 
   /**
+   * A lightweight version of NotifyLayersUpdated that allows just the scroll
+   * offset and scroll generation from the main thread to be propagated to APZ.
+   */
+  void NotifyScrollUpdated(uint32_t aScrollGeneration,
+                           const CSSPoint& aScrollOffset);
+
+  /**
    * The platform implementation must set the compositor parent so that we can
    * request composites.
    */
   void SetCompositorParent(CompositorParent* aCompositorParent);
 
   /**
    * Inform this APZC that it will be sharing its FrameMetrics with a cross-process
    * compositor so that the associated content process can access it. This is only
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -119,16 +119,20 @@ ScrollFrameTo(nsIScrollableFrame* aFrame
  * and actual scroll positions.
  */
 static void
 ScrollFrame(nsIContent* aContent,
             FrameMetrics& aMetrics)
 {
   // Scroll the window to the desired spot
   nsIScrollableFrame* sf = nsLayoutUtils::FindScrollableFrameFor(aMetrics.GetScrollId());
+  if (sf) {
+    sf->SetScrollableByAPZ(!aMetrics.IsScrollInfoLayer());
+  }
+
   bool scrollUpdated = false;
   CSSPoint apzScrollOffset = aMetrics.GetScrollOffset();
   CSSPoint actualScrollOffset = ScrollFrameTo(sf, apzScrollOffset, scrollUpdated);
 
   if (scrollUpdated) {
     if (aMetrics.IsScrollInfoLayer()) {
       // In cases where the APZ scroll offset is different from the content scroll
       // offset, we want to interpret the margins as relative to the APZ scroll
@@ -908,22 +912,26 @@ APZCCallbackHelper::NotifyFlushComplete(
   nsCOMPtr<nsIObserverService> observerService = mozilla::services::GetObserverService();
   MOZ_ASSERT(observerService);
   observerService->NotifyObservers(nullptr, "apz-repaints-flushed", nullptr);
 }
 
 static int32_t sActiveSuppressDisplayport = 0;
 
 void
-APZCCallbackHelper::SuppressDisplayport(const bool& aEnabled)
+APZCCallbackHelper::SuppressDisplayport(const bool& aEnabled,
+                                        const nsCOMPtr<nsIPresShell>& aShell)
 {
   if (aEnabled) {
     sActiveSuppressDisplayport++;
   } else {
     sActiveSuppressDisplayport--;
+    if (sActiveSuppressDisplayport == 0 && aShell && aShell->GetRootFrame()) {
+      aShell->GetRootFrame()->SchedulePaint();
+    }
   }
 
   MOZ_ASSERT(sActiveSuppressDisplayport >= 0);
 }
 
 bool
 APZCCallbackHelper::IsDisplayportSuppressed()
 {
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -158,18 +158,24 @@ public:
                                                          const SetAllowedTouchBehaviorCallback& aCallback);
 
     /* Notify content of a mouse scroll testing event. */
     static void NotifyMozMouseScrollEvent(const FrameMetrics::ViewID& aScrollId, const nsString& aEvent);
 
     /* Notify content that the repaint flush is complete. */
     static void NotifyFlushComplete(nsIPresShell* aShell);
 
-    /* Temporarily ignore the Displayport for better paint performance. */
-    static void SuppressDisplayport(const bool& aEnabled);
+    /* Temporarily ignore the Displayport for better paint performance. If at
+     * all possible, pass in a presShell if you have one at the call site, we
+     * use it to trigger a repaint once suppression is disabled. Without that
+     * the displayport may get left at the suppressed size for an extended
+     * period of time and result in unnecessary checkerboarding (see bug
+     * 1255054). */
+    static void SuppressDisplayport(const bool& aEnabled,
+                                    const nsCOMPtr<nsIPresShell>& aShell);
     static bool IsDisplayportSuppressed();
 
 private:
   static uint64_t sLastTargetAPZCNotificationInputBlock;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/composite/AsyncCompositionManager.cpp
+++ b/gfx/layers/composite/AsyncCompositionManager.cpp
@@ -578,18 +578,17 @@ SampleAnimations(Layer* aLayer, TimeStam
     // Since activeAnimations is true, this could mean we keep compositing
     // unnecessarily during the delay, but so long as this only happens while
     // the refresh driver is under test control that should be ok.
     if (elapsedDuration.ToSeconds() < 0) {
       continue;
     }
 
     TimingParams timing;
-    timing.mDuration.SetAsUnrestrictedDouble() =
-      animation.duration().ToMilliseconds();
+    timing.mDuration.emplace(animation.duration());
     // Currently animations run on the compositor have their delay factored
     // into their start time, hence the delay is effectively zero.
     timing.mDelay = TimeDuration(0);
     timing.mIterations = animation.iterations();
     timing.mIterationStart = animation.iterationStart();
     timing.mDirection =
       static_cast<dom::PlaybackDirection>(animation.direction());
     // Animations typically only run on the compositor during their active
@@ -828,17 +827,17 @@ AsyncCompositionManager::ApplyAsyncConte
   // The transform of a mask layer is relative to the masked layer's parent
   // layer. So whenever we apply an async transform to a layer, we need to
   // apply that same transform to the layer's own mask layer.
   // A layer can also have "ancestor" mask layers for any rounded clips from
   // its ancestor scroll frames. A scroll frame mask layer only needs to be
   // async transformed for async scrolls of this scroll frame's ancestor
   // scroll frames, not for async scrolls of this scroll frame itself.
   // In the loop below, we iterate over scroll frames from inside to outside.
-  // At each iteration, this array contains the layer's ancestor mask layers 
+  // At each iteration, this array contains the layer's ancestor mask layers
   // of all scroll frames inside the current one.
   nsTArray<Layer*> ancestorMaskLayers;
 
   for (uint32_t i = 0; i < aLayer->GetFrameMetricsCount(); i++) {
     AsyncPanZoomController* controller = aLayer->GetAsyncPanZoomController(i);
     if (!controller) {
       continue;
     }
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -1297,16 +1297,30 @@ CompositorD3D11::PrepareViewport(const g
 
   Matrix4x4 projection = Matrix4x4::From2D(viewMatrix);
   projection._33 = 0.0f;
 
   PrepareViewport(aSize, projection, 0.0f, 1.0f);
 }
 
 void
+CompositorD3D11::ForcePresent()
+{
+  LayoutDeviceIntRect rect;
+  mWidget->GetClientBounds(rect);
+
+  DXGI_SWAP_CHAIN_DESC desc;
+  mSwapChain->GetDesc(&desc);
+
+  if (desc.BufferDesc.Width == rect.width && desc.BufferDesc.Height == rect.height) {
+    mSwapChain->Present(0, 0);
+  }
+}
+
+void
 CompositorD3D11::PrepareViewport(const gfx::IntSize& aSize,
                                  const gfx::Matrix4x4& aProjection,
                                  float aZNear, float aZFar)
 {
   D3D11_VIEWPORT viewport;
   viewport.MaxDepth = aZFar;
   viewport.MinDepth = aZNear;
   viewport.Width = aSize.width;
--- a/gfx/layers/d3d11/CompositorD3D11.h
+++ b/gfx/layers/d3d11/CompositorD3D11.h
@@ -139,17 +139,17 @@ public:
 #ifdef MOZ_DUMP_PAINTING
   virtual const char* Name() const override { return "Direct3D 11"; }
 #endif
 
   virtual LayersBackend GetBackendType() const override {
     return LayersBackend::LAYERS_D3D11;
   }
 
-  virtual void ForcePresent() { mSwapChain->Present(0, 0); }
+  virtual void ForcePresent();
 
   virtual nsIWidget* GetWidget() const override { return mWidget; }
 
   ID3D11Device* GetDevice() { return mDevice; }
 
   ID3D11DeviceContext* GetDC() { return mContext; }
 
 private:
--- a/gfx/layers/ipc/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -794,16 +794,35 @@ GetAPZCForViewID(Layer* aLayer, FrameMet
         return c;
       }
     }
   }
   return nullptr;
 }
 
 bool
+LayerTransactionParent::RecvUpdateScrollOffset(
+    const FrameMetrics::ViewID& aScrollID,
+    const uint32_t& aScrollGeneration,
+    const CSSPoint& aScrollOffset)
+{
+  if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
+    return false;
+  }
+
+  AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID);
+  if (!controller) {
+    return false;
+  }
+  controller->NotifyScrollUpdated(aScrollGeneration, aScrollOffset);
+  mShadowLayersManager->ForceComposite(this);
+  return true;
+}
+
+bool
 LayerTransactionParent::RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aScrollID,
                                                  const float& aX, const float& aY)
 {
   if (mDestroyed || !layer_manager() || layer_manager()->IsDestroyed()) {
     return false;
   }
 
   AsyncPanZoomController* controller = GetAPZCForViewID(mRoot, aScrollID);
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -123,16 +123,19 @@ protected:
   virtual bool RecvForceComposite() override;
   virtual bool RecvSetTestSampleTime(const TimeStamp& aTime) override;
   virtual bool RecvLeaveTestMode() override;
   virtual bool RecvGetOpacity(PLayerParent* aParent,
                               float* aOpacity) override;
   virtual bool RecvGetAnimationTransform(PLayerParent* aParent,
                                          MaybeTransform* aTransform)
                                          override;
+  virtual bool RecvUpdateScrollOffset(const FrameMetrics::ViewID& aScrollId,
+                                      const uint32_t& aScrollGeneration,
+                                      const CSSPoint& aScrollOffset) override;
   virtual bool RecvSetAsyncScrollOffset(const FrameMetrics::ViewID& aId,
                                         const float& aX, const float& aY) override;
   virtual bool RecvSetAsyncZoom(const FrameMetrics::ViewID& aId,
                                 const float& aValue) override;
   virtual bool RecvFlushApzRepaints() override;
   virtual bool RecvGetAPZTestData(APZTestData* aOutData) override;
   virtual bool RecvRequestProperty(const nsString& aProperty, float* aValue) override;
   virtual bool RecvSetConfirmedTargetAPZC(const uint64_t& aBlockId,
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -17,16 +17,17 @@ include "mozilla/GfxMessageUtils.h";
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::layers::TextureFlags from "mozilla/layers/CompositorTypes.h";
 using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
 using mozilla::layers::FrameMetrics::ViewID from "FrameMetrics.h";
 using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
+using mozilla::CSSPoint from "Units.h";
 
 /**
  * The layers protocol is spoken between thread contexts that manage
  * layer (sub)trees.  The protocol comprises atomically publishing
  * layer subtrees to a "shadow" thread context (which grafts the
  * subtree into its own tree), and atomically updating a published
  * subtree.  ("Atomic" in this sense is wrt painting.)
  */
@@ -84,16 +85,20 @@ parent:
 
   // Returns the value of the transform applied to the layer by animation after
   // factoring out translation components introduced to account for the offset
   // of the corresponding frame and transform origin and after converting to CSS
   // pixels. If the layer is not transformed by animation, the return value will
   // be void_t.
   sync GetAnimationTransform(PLayer layer) returns (MaybeTransform transform);
 
+  // Updates the scroll offset and generation counter on the APZC for the
+  // given scroll id.
+  sync UpdateScrollOffset(ViewID id, uint32_t generation, CSSPoint offset);
+
   // The next time the layer tree is composited, add this async scroll offset in
   // CSS pixels for the given ViewID.
   // Useful for testing rendering of async scrolling.
   sync SetAsyncScrollOffset(ViewID id, float x, float y);
 
   // The next time the layer tree is composited, include this async zoom in
   // for the given ViewID.
   // Useful for testing rendering of async zooming.
--- a/gfx/skia/skia/src/gpu/GrPathUtils.cpp
+++ b/gfx/skia/skia/src/gpu/GrPathUtils.cpp
@@ -262,19 +262,17 @@ void GrPathUtils::QuadUVMatrix::set(cons
             // that (u, v) will always be far away from the quad.
             fM[0] = 0; fM[1] = 0; fM[2] = 100.f;
             fM[3] = 0; fM[4] = 0; fM[5] = 100.f;
         }
     } else {
         double scale = 1.0/det;
 
         // compute adjugate matrix
-        double a0, a1, a2, a3, a4, a5, a6, a7, a8;
-        a0 = y1-y2;
-        a1 = x2-x1;
+        double a2, a3, a4, a5, a6, a7, a8;
         a2 = x1*y2-x2*y1;
 
         a3 = y2-y0;
         a4 = x0-x2;
         a5 = x2*y0-x0*y2;
 
         a6 = y0-y1;
         a7 = x1-x0;
@@ -285,25 +283,21 @@ void GrPathUtils::QuadUVMatrix::set(cons
         m[SkMatrix::kMScaleX] = (float)((0.5*a3 + a6)*scale);
         m[SkMatrix::kMSkewX]  = (float)((0.5*a4 + a7)*scale);
         m[SkMatrix::kMTransX] = (float)((0.5*a5 + a8)*scale);
 
         m[SkMatrix::kMSkewY]  = (float)(a6*scale);
         m[SkMatrix::kMScaleY] = (float)(a7*scale);
         m[SkMatrix::kMTransY] = (float)(a8*scale);
 
-        m[SkMatrix::kMPersp0] = (float)((a0 + a3 + a6)*scale);
-        m[SkMatrix::kMPersp1] = (float)((a1 + a4 + a7)*scale);
+        // kMPersp0 & kMPersp1 should algebraically be zero
+        m[SkMatrix::kMPersp0] = 0.0f;
+        m[SkMatrix::kMPersp1] = 0.0f;
         m[SkMatrix::kMPersp2] = (float)((a2 + a5 + a8)*scale);
 
-        // The matrix should not have perspective.
-        SkDEBUGCODE(static const SkScalar gTOL = 1.f / 100.f);
-        SkASSERT(SkScalarAbs(m.get(SkMatrix::kMPersp0)) < gTOL);
-        SkASSERT(SkScalarAbs(m.get(SkMatrix::kMPersp1)) < gTOL);
-
         // It may not be normalized to have 1.0 in the bottom right
         float m33 = m.get(SkMatrix::kMPersp2);
         if (1.f != m33) {
             m33 = 1.f / m33;
             fM[0] = m33 * m.get(SkMatrix::kMScaleX);
             fM[1] = m33 * m.get(SkMatrix::kMSkewX);
             fM[2] = m33 * m.get(SkMatrix::kMTransX);
             fM[3] = m33 * m.get(SkMatrix::kMSkewY);
deleted file mode 100644
--- a/image/Deinterlacer.cpp
+++ /dev/null
@@ -1,55 +0,0 @@
-/* -*- 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 "Downscaler.h"
-#include "mozilla/UniquePtrExtensions.h"
-
-namespace mozilla {
-namespace image {
-
-Deinterlacer::Deinterlacer(const nsIntSize& aImageSize)
-  : mImageSize(aImageSize)
-{
-  CheckedInt<size_t> bufferSize = mImageSize.width;
-  bufferSize *= mImageSize.height;
-  bufferSize *= sizeof(uint32_t);
-
-  if (!bufferSize.isValid()) {
-    return;
-  }
-
-  mBuffer = MakeUniqueFallible<uint8_t[]>(bufferSize.value());
-}
-
-uint32_t
-Deinterlacer::RowSize() const
-{
-  return mImageSize.width * sizeof(uint32_t);
-}
-
-uint8_t*
-Deinterlacer::RowBuffer(uint32_t aRow)
-{
-  uint32_t offset = aRow * RowSize();
-  MOZ_ASSERT(IsValid(), "Deinterlacer in invalid state");
-  MOZ_ASSERT(offset < mImageSize.width * mImageSize.height * sizeof(uint32_t),
-             "Row is outside of image");
-  return mBuffer.get() + offset;
-}
-
-void
-Deinterlacer::PropagatePassToDownscaler(Downscaler& aDownscaler)
-{
-  MOZ_ASSERT(IsValid(), "Deinterlacer in invalid state");
-  for (int32_t row = 0 ; row < mImageSize.height ; ++row) {
-    memcpy(aDownscaler.RowBuffer(), RowBuffer(row), RowSize());
-    aDownscaler.CommitRow();
-  }
-}
-
-} // namespace image
-} // namespace mozilla
deleted file mode 100644
--- a/image/Deinterlacer.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/* -*- 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/. */
-
-
-/**
- * Deinterlacer is a utility class to allow Downscaler to work with interlaced
- * images.
-
- * Since Downscaler needs to receive rows in top-to-bottom or
- * bottom-to-top order, it can't natively handle interlaced images, in which the
- * rows arrive in an interleaved order. Deinterlacer solves this problem by
- * acting as an intermediate buffer that records decoded rows. Unlike
- * Downscaler, it allows the rows to be written in arbitrary order. After each
- * pass, calling PropagatePassToDownscaler() will downscale every buffered row
- * in a single operation. The rows remain in the buffer, so rows that were
- * written in one pass will be included in subsequent passes.
- */
-
-
-#ifndef mozilla_image_Deinterlacer_h
-#define mozilla_image_Deinterlacer_h
-
-#include "Downscaler.h"
-
-namespace mozilla {
-namespace image {
-
-class Deinterlacer
-{
-public:
-  explicit Deinterlacer(const nsIntSize& aImageSize);
-  bool IsValid() { return !!mBuffer; }
-
-  uint8_t* RowBuffer(uint32_t aRow);
-  void PropagatePassToDownscaler(Downscaler& aDownscaler);
-
-private:
-  uint32_t RowSize() const;
-
-  nsIntSize mImageSize;
-  UniquePtr<uint8_t[]> mBuffer;
-};
-
-
-} // namespace image
-} // namespace mozilla
-
-#endif // mozilla_image_Deinterlacer_h
--- a/image/SurfacePipe.h
+++ b/image/SurfacePipe.h
@@ -54,17 +54,17 @@ struct SurfaceInvalidRect
  */
 enum class WriteState : uint8_t
 {
   NEED_MORE_DATA,  /// The lambda ran out of data.
 
   FINISHED,        /// The lambda is done writing to the surface; future writes
                    /// will fail.
 
-  ERROR            /// The lambda encountered an error. The caller may recover
+  FAILURE          /// The lambda encountered an error. The caller may recover
                    /// if possible and continue to write. (This never indicates
                    /// an error in the SurfacePipe machinery itself; it's only
                    /// generated by the lambdas.)
 };
 
 /**
  * A template alias used to make the return value of WritePixels() lambdas
  * (which may return either a pixel value or a WriteState) easier to specify.
@@ -177,22 +177,22 @@ public:
 
           case WriteState::FINISHED:
             // Make sure that IsSurfaceFinished() returns true so the caller
             // can't write anything else to the pipeline.
             mRowPointer = nullptr;
             mCol = 0;
             return WriteState::FINISHED;
 
-          case WriteState::ERROR:
+          case WriteState::FAILURE:
             // Note that we don't need to record this anywhere, because this
             // indicates an error in aFunc, and there's nothing wrong with our
             // machinery. The caller can recover as needed and continue writing to
             // the row.
-            return WriteState::ERROR;
+            return WriteState::FAILURE;
         }
       }
 
       // We've finished the row.
       mRowPointer = AdvanceRow();
       mCol = 0;
     }
 
@@ -238,17 +238,17 @@ public:
     if (IsSurfaceFinished()) {
       return WriteState::FINISHED;  // Already done.
     }
 
     while (true) {
       PixelType* rowPtr = reinterpret_cast<PixelType*>(mRowPointer);
 
       Maybe<WriteState> result = aFunc(rowPtr, mInputSize.width);
-      if (result != Some(WriteState::ERROR)) {
+      if (result != Some(WriteState::FAILURE)) {
         mCol = 0;
         mRowPointer = AdvanceRow();  // We've finished the row.
       }
 
       if (IsSurfaceFinished()) {
         break;
       }
 
--- a/image/decoders/GIF2.h
+++ b/image/decoders/GIF2.h
@@ -24,17 +24,16 @@ enum { GIF_APPLICATION_EXTENSION_LABEL =
 
 // List of possible parsing states
 typedef enum {
     gif_type,
     gif_global_header,
     gif_global_colormap,
     gif_image_start,
     gif_image_header,
-    gif_image_header_continue,
     gif_image_colormap,
     gif_lzw_start,
     gif_lzw,
     gif_sub_block,
     gif_extension,
     gif_control_extension,
     gif_consume_block,
     gif_skip_block,
@@ -62,45 +61,36 @@ typedef struct gif_struct {
     int avail;                  // Index of next available slot in dictionary
     int oldcode;
     uint8_t firstchar;
     int count;                  // Remaining # bytes in sub-block
     int bits;                   // Number of unread bits in "datum"
     int32_t datum;              // 32-bit input buffer
 
     // Output state machine
-    int ipass;                  // Interlace pass; Ranges 1-4 if interlaced.
-    unsigned rows_remaining;    // Rows remaining to be output
-    unsigned irow;              // Current output row, starting at zero
-    uint8_t* rowp;              // Current output pointer
+    int64_t pixels_remaining;  // Pixels remaining to be output.
 
     // Parameters for image frame currently being decoded
-    unsigned x_offset, y_offset; // With respect to "screen" origin
-    unsigned height, width;
-    unsigned clamped_height;    // Size of the frame rectangle clamped to the
-    unsigned clamped_width;     //  global logical size after x_ and y_offset.
     int tpixel;                 // Index of transparent pixel
     int32_t disposal_method;    // Restore to background, leave in place, etc.
     uint32_t* local_colormap;   // Per-image colormap
     int local_colormap_size;    // Size of local colormap array.
     uint32_t delay_time;        // Display time, in milliseconds,
                                 // for this image in a multi-image GIF
 
     // Global (multi-image) state
     int version;                // Either 89 for GIF89 or 87 for GIF87
-    unsigned screen_width;      // Logical screen width & height
-    unsigned screen_height;
+    int32_t screen_width;       // Logical screen width & height
+    int32_t screen_height;
     uint32_t global_colormap_depth; // Depth of global colormap array
     int images_decoded;         // Counts images for multi-part GIFs
     int loop_count;             // Netscape specific extension block to control
                                 // the number of animation loops a GIF
                                 // renders.
 
-    bool progressive_display;   // If TRUE, do Haeberli interlace hack
-    bool interlaced;            // TRUE, if scanlines arrive interlaced order
     bool is_transparent;        // TRUE, if tpixel is valid
 
     uint16_t  prefix[MAX_BITS];            // LZW decoding tables
     uint8_t*  hold;                        // Accumulation buffer
     uint32_t  global_colormap[MAX_COLORS]; // Default colormap if local not
                                            //   supplied
     uint8_t   suffix[MAX_BITS];            // LZW decoding tables
     uint8_t   stack[MAX_BITS];             // Base of LZW decoder stack
--- a/image/decoders/nsBMPDecoder.cpp
+++ b/image/decoders/nsBMPDecoder.cpp
@@ -257,17 +257,17 @@ nsBMPDecoder::FinishInternal()
     // Invalidate.
     nsIntRect r(0, 0, mH.mWidth, AbsoluteHeight());
     PostInvalidation(r);
 
     if (mDoesHaveTransparency) {
       MOZ_ASSERT(mMayHaveTransparency);
       PostFrameStop(Opacity::SOME_TRANSPARENCY);
     } else {
-      PostFrameStop(Opacity::OPAQUE);
+      PostFrameStop(Opacity::FULLY_OPAQUE);
     }
     PostDecodeDone();
   }
 }
 
 // ----------------------------------------
 // Actual Data Processing
 // ----------------------------------------
--- a/image/decoders/nsGIFDecoder2.cpp
+++ b/image/decoders/nsGIFDecoder2.cpp
@@ -35,19 +35,22 @@ For further information, please contact 
 CompuServe Incorporated maintains a mailing list with all those individuals and
 organizations who wish to receive copies of this document when it is corrected
 or revised. This service is offered free of charge; please provide us with your
 mailing address.
 */
 
 #include <stddef.h>
 
+#include "imgFrame.h"
 #include "nsGIFDecoder2.h"
 #include "nsIInputStream.h"
 #include "RasterImage.h"
+#include "SurfaceFilters.h"
+#include "SurfacePipeFactory.h"
 
 #include "gfxColor.h"
 #include "gfxPlatform.h"
 #include "qcms.h"
 #include <algorithm>
 #include "mozilla/Telemetry.h"
 
 using namespace mozilla::gfx;
@@ -68,22 +71,18 @@ namespace image {
 
 // Get a 16-bit value stored in little-endian format
 #define GETINT16(p)   ((p)[1]<<8|(p)[0])
 //////////////////////////////////////////////////////////////////////
 // GIF Decoder Implementation
 
 nsGIFDecoder2::nsGIFDecoder2(RasterImage* aImage)
   : Decoder(aImage)
-  , mCurrentRow(-1)
-  , mLastFlushedRow(-1)
   , mOldColor(0)
   , mCurrentFrameIndex(-1)
-  , mCurrentPass(0)
-  , mLastFlushedPass(0)
   , mGIFOpen(false)
   , mSawTransparency(false)
 {
   // Clear out the structure, excluding the arrays
   memset(&mGIFStruct, 0, sizeof(mGIFStruct));
 
   // Initialize as "animate once" in case no NETSCAPE2.0 extension is found
   mGIFStruct.loop_count = 1;
@@ -94,104 +93,41 @@ nsGIFDecoder2::nsGIFDecoder2(RasterImage
 }
 
 nsGIFDecoder2::~nsGIFDecoder2()
 {
   free(mGIFStruct.local_colormap);
   free(mGIFStruct.hold);
 }
 
-uint8_t*
-nsGIFDecoder2::GetCurrentRowBuffer()
-{
-  if (!mDownscaler) {
-    MOZ_ASSERT(!mDeinterlacer, "Deinterlacer without downscaler?");
-    uint32_t bpp = mGIFStruct.images_decoded == 0 ? sizeof(uint32_t)
-                                                  : sizeof(uint8_t);
-    return mImageData + mGIFStruct.irow * mGIFStruct.width * bpp;
-  }
-
-  if (!mDeinterlacer) {
-    return mDownscaler->RowBuffer();
-  }
-
-  return mDeinterlacer->RowBuffer(mGIFStruct.irow);
-}
-
-uint8_t*
-nsGIFDecoder2::GetRowBuffer(uint32_t aRow)
-{
-  MOZ_ASSERT(mGIFStruct.images_decoded == 0,
-             "Calling GetRowBuffer on a frame other than the first suggests "
-             "we're deinterlacing animated frames");
-  MOZ_ASSERT(!mDownscaler || mDeinterlacer,
-             "Can't get buffer for a specific row if downscaling "
-             "but not deinterlacing");
-
-  if (mDownscaler) {
-    return mDeinterlacer->RowBuffer(aRow);
-  }
-
-  return mImageData + aRow * mGIFStruct.width * sizeof(uint32_t);
-}
-
 void
 nsGIFDecoder2::FinishInternal()
 {
   MOZ_ASSERT(!HasError(), "Shouldn't call FinishInternal after error!");
 
   // If the GIF got cut off, handle it anyway
   if (!IsMetadataDecode() && mGIFOpen) {
     if (mCurrentFrameIndex == mGIFStruct.images_decoded) {
       EndImageFrame();
     }
     PostDecodeDone(mGIFStruct.loop_count - 1);
     mGIFOpen = false;
   }
 }
 
-// Push any new rows according to mCurrentPass/mLastFlushedPass and
-// mCurrentRow/mLastFlushedRow.  Note: caller is responsible for
-// updating mlastFlushed{Row,Pass}.
-void
-nsGIFDecoder2::FlushImageData(uint32_t fromRow, uint32_t rows)
-{
-  nsIntRect r(mGIFStruct.x_offset, mGIFStruct.y_offset + fromRow,
-              mGIFStruct.width, rows);
-  PostInvalidation(r);
-}
-
 void
 nsGIFDecoder2::FlushImageData()
 {
-  if (mDownscaler) {
-    if (mDownscaler->HasInvalidation()) {
-      DownscalerInvalidRect invalidRect = mDownscaler->TakeInvalidRect();
-      PostInvalidation(invalidRect.mOriginalSizeRect,
-                       Some(invalidRect.mTargetSizeRect));
-    }
+  Maybe<SurfaceInvalidRect> invalidRect = mPipe.TakeInvalidRect();
+  if (!invalidRect) {
     return;
   }
 
-  switch (mCurrentPass - mLastFlushedPass) {
-    case 0:  // same pass
-      if (mCurrentRow - mLastFlushedRow) {
-        FlushImageData(mLastFlushedRow + 1, mCurrentRow - mLastFlushedRow);
-      }
-      break;
-
-    case 1:  // one pass on - need to handle bottom & top rects
-      FlushImageData(0, mCurrentRow + 1);
-      FlushImageData(mLastFlushedRow + 1,
-                     mGIFStruct.clamped_height - (mLastFlushedRow + 1));
-      break;
-
-    default: // more than one pass on - push the whole frame
-      FlushImageData(0, mGIFStruct.clamped_height);
-  }
+  PostInvalidation(invalidRect->mInputSpaceRect,
+                   Some(invalidRect->mOutputSpaceRect));
 }
 
 //******************************************************************************
 // GIF decoder callback methods. Part of public API for GIF2
 //******************************************************************************
 
 //******************************************************************************
 void
@@ -244,112 +180,89 @@ nsGIFDecoder2::ClampToImageRect(const In
     visibleFrameRect.MoveTo(0, 0);
   }
 
   return visibleFrameRect;
 }
 
 //******************************************************************************
 nsresult
-nsGIFDecoder2::BeginImageFrame(uint16_t aDepth)
+nsGIFDecoder2::BeginImageFrame(const IntRect& aFrameRect,
+                               uint16_t aDepth,
+                               bool aIsInterlaced)
 {
   MOZ_ASSERT(HasSize());
 
-  IntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
-                    mGIFStruct.width, mGIFStruct.height);
-
-  bool hasTransparency = CheckForTransparency(frameRect);
+  bool hasTransparency = CheckForTransparency(aFrameRect);
   gfx::SurfaceFormat format = hasTransparency ? SurfaceFormat::B8G8R8A8
                                               : SurfaceFormat::B8G8R8X8;
 
   // Make sure there's no animation if we're downscaling.
   MOZ_ASSERT_IF(mDownscaler, !GetImageMetadata().HasAnimation());
 
-  // Compute the target size and target frame rect. If we're downscaling,
-  // Downscaler will automatically strip out first-frame padding, so the target
-  // frame rect takes up the entire frame regardless.
-  IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
-                                   : GetSize();
-  IntRect targetFrameRect = mDownscaler ? IntRect(IntPoint(), targetSize)
-                                        : frameRect;
+  SurfacePipeFlags pipeFlags = aIsInterlaced
+                             ? SurfacePipeFlags::DEINTERLACE
+                             : SurfacePipeFlags();
+
+  Maybe<SurfacePipe> pipe;
+  if (mGIFStruct.images_decoded == 0) {
+    // This is the first frame. We may be downscaling, so compute the target
+    // size and target frame rect.
+    IntSize targetSize = mDownscaler ? mDownscaler->TargetSize()
+                                     : GetSize();
+    IntRect targetFrameRect = mDownscaler ? IntRect(IntPoint(), targetSize)
+                                          : aFrameRect;
 
-  // Use correct format, RGB for first frame, PAL for following frames
-  // and include transparency to allow for optimization of opaque images
-  nsresult rv = NS_OK;
-  if (mGIFStruct.images_decoded) {
-    // Image data is stored with original depth and palette.
-    rv = AllocateFrame(mGIFStruct.images_decoded, targetSize,
-                       targetFrameRect, format, aDepth);
+    // The first frame may be displayed progressively.
+    pipeFlags |= SurfacePipeFlags::PROGRESSIVE_DISPLAY;
+
+    // The first frame is always decoded into an RGB surface.
+    pipe =
+      SurfacePipeFactory::CreateSurfacePipe(this, mGIFStruct.images_decoded,
+                                            GetSize(), targetSize,
+                                            targetFrameRect, format, pipeFlags);
   } else {
-    // Regardless of depth of input, the first frame is decoded into 24bit RGB.
-    rv = AllocateFrame(mGIFStruct.images_decoded, targetSize,
-                       targetFrameRect, format);
+    // This is an animation frame (and not the first). To minimize the memory
+    // usage of animations, the image data is stored in paletted form.
+    MOZ_ASSERT(!mDownscaler);
+    pipe =
+      SurfacePipeFactory::CreatePalettedSurfacePipe(this, mGIFStruct.images_decoded,
+                                                    GetSize(), aFrameRect, format,
+                                                    aDepth, pipeFlags);
   }
 
   mCurrentFrameIndex = mGIFStruct.images_decoded;
 
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!pipe) {
+    mPipe = SurfacePipe();
+    return NS_ERROR_FAILURE;
   }
 
-  if (mDownscaler) {
-    rv = mDownscaler->BeginFrame(GetSize(), Some(ClampToImageRect(frameRect)),
-                                 mImageData, hasTransparency);
-  }
-
-  return rv;
+  mPipe = Move(*pipe);
+  return NS_OK;
 }
 
 
 //******************************************************************************
 void
 nsGIFDecoder2::EndImageFrame()
 {
   Opacity opacity = Opacity::SOME_TRANSPARENCY;
 
   // First flush all pending image data
-  if (!mGIFStruct.images_decoded) {
+  if (mGIFStruct.images_decoded == 0) {
     // Only need to flush first frame
     FlushImageData();
 
-    // If the first frame is smaller in height than the entire image, send an
-    // invalidation for the area it does not have data for.
-    // This will clear the remaining bits of the placeholder. (Bug 37589)
-    const uint32_t realFrameHeight = mGIFStruct.height + mGIFStruct.y_offset;
-    if (realFrameHeight < mGIFStruct.screen_height) {
-      if (mDownscaler) {
-        IntRect targetRect = IntRect(IntPoint(), mDownscaler->TargetSize());
-        PostInvalidation(IntRect(IntPoint(), GetSize()), Some(targetRect));
-      } else {
-        nsIntRect r(0, realFrameHeight,
-                    mGIFStruct.screen_width,
-                    mGIFStruct.screen_height - realFrameHeight);
-        PostInvalidation(r);
-      }
-    }
-
     // The first frame was preallocated with alpha; if it wasn't transparent, we
     // should fix that. We can also mark it opaque unconditionally if we didn't
     // actually see any transparent pixels - this test is only valid for the
     // first frame.
     if (!mGIFStruct.is_transparent && !mSawTransparency) {
-      opacity = Opacity::OPAQUE;
-    }
-  }
-  mCurrentRow = mLastFlushedRow = -1;
-  mCurrentPass = mLastFlushedPass = 0;
-
-  // Only add frame if we have any rows at all
-  if (mGIFStruct.rows_remaining != mGIFStruct.clamped_height) {
-    if (mGIFStruct.rows_remaining && mGIFStruct.images_decoded) {
-      // Clear the remaining rows (only needed for the animation frames)
-      uint8_t* rowp =
-        mImageData + ((mGIFStruct.clamped_height - mGIFStruct.rows_remaining) *
-                      mGIFStruct.width);
-      memset(rowp, 0, mGIFStruct.rows_remaining * mGIFStruct.width);
+      opacity = Opacity::FULLY_OPAQUE;
     }
   }
 
   // Unconditionally increment images_decoded, because we unconditionally
   // append frames in BeginImageFrame(). This ensures that images_decoded
   // always refers to the frame in mImage we're currently decoding,
   // even if some of them weren't decoded properly and thus are blank.
   mGIFStruct.images_decoded++;
@@ -363,296 +276,173 @@ nsGIFDecoder2::EndImageFrame()
   if (mOldColor) {
     mColormap[mGIFStruct.tpixel] = mOldColor;
     mOldColor = 0;
   }
 
   mCurrentFrameIndex = -1;
 }
 
-
-//******************************************************************************
-// Send the data to the display front-end.
-uint32_t
-nsGIFDecoder2::OutputRow()
+template <typename PixelSize>
+PixelSize
+nsGIFDecoder2::ColormapIndexToPixel(uint8_t aIndex)
 {
-  // Initialize the region in which we're duplicating rows (for the
-  // Haeberli-inspired hack below) to |irow|, which is the row we're writing to
-  // now.
-  int drow_start = mGIFStruct.irow;
-  int drow_end = mGIFStruct.irow;
+  MOZ_ASSERT(sizeof(PixelSize) == sizeof(uint32_t));
 
-  // Protect against too much image data
-  if ((unsigned)drow_start >= mGIFStruct.clamped_height) {
-    NS_WARNING("GIF2.cpp::OutputRow - too much image data");
-    return 0;
+  // Retrieve the next color, clamping to the size of the colormap.
+  uint32_t color = mColormap[aIndex & mColorMask];
+
+  // Check for transparency.
+  if (mGIFStruct.is_transparent) {
+    mSawTransparency = mSawTransparency || color == 0;
   }
 
-  if (!mGIFStruct.images_decoded) {
-    // Haeberli-inspired hack for interlaced GIFs: Replicate lines while
-    // displaying to diminish the "venetian-blind" effect as the image is
-    // loaded. Adjust pixel vertical positions to avoid the appearance of the
-    // image crawling up the screen as successive passes are drawn.
-    if (mGIFStruct.progressive_display && mGIFStruct.interlaced &&
-        (mGIFStruct.ipass < 4)) {
-      // ipass = 1,2,3 results in resp. row_dup = 7,3,1 and row_shift = 3,1,0
-      const uint32_t row_dup = 15 >> mGIFStruct.ipass;
-      const uint32_t row_shift = row_dup >> 1;
+  return color;
+}
+
+template <>
+uint8_t
+nsGIFDecoder2::ColormapIndexToPixel<uint8_t>(uint8_t aIndex)
+{
+  return aIndex & mColorMask;
+}
+
+template <typename PixelSize>
+NextPixel<PixelSize>
+nsGIFDecoder2::YieldPixel(const uint8_t*& aCurrentByteInOut)
+{
+  MOZ_ASSERT(mGIFStruct.stackp >= mGIFStruct.stack);
+
+  // If we don't have any decoded data to yield, try to read some input and
+  // produce some.
+  if (mGIFStruct.stackp == mGIFStruct.stack) {
+    while (mGIFStruct.bits < mGIFStruct.codesize && mGIFStruct.count > 0) {
+      // Feed the next byte into the decoder's 32-bit input buffer.
+      mGIFStruct.datum += int32_t(*aCurrentByteInOut) << mGIFStruct.bits;
+      mGIFStruct.bits += 8;
+      ++aCurrentByteInOut;
+      --mGIFStruct.count;
+    }
 
-      drow_start -= row_shift;
-      drow_end = drow_start + row_dup;
+    if (mGIFStruct.bits < mGIFStruct.codesize) {
+      return AsVariant(WriteState::NEED_MORE_DATA);
+    }
+
+    // Get the leading variable-length symbol from the data stream.
+    int code = mGIFStruct.datum & mGIFStruct.codemask;
+    mGIFStruct.datum >>= mGIFStruct.codesize;
+    mGIFStruct.bits -= mGIFStruct.codesize;
+
+    const int clearCode = ClearCode();
 
-      // Extend if bottom edge isn't covered because of the shift upward.
-      if (((mGIFStruct.clamped_height - 1) - drow_end) <= row_shift) {
-        drow_end = mGIFStruct.clamped_height - 1;
+    // Reset the dictionary to its original state, if requested
+    if (code == clearCode) {
+      mGIFStruct.codesize = mGIFStruct.datasize + 1;
+      mGIFStruct.codemask = (1 << mGIFStruct.codesize) - 1;
+      mGIFStruct.avail = clearCode + 2;
+      mGIFStruct.oldcode = -1;
+      return AsVariant(WriteState::NEED_MORE_DATA);
+    }
+
+    // Check for explicit end-of-stream code. It should only appear after all
+    // image data, but if that was the case we wouldn't be in this function, so
+    // this is always an error condition.
+    if (code == (clearCode + 1)) {
+      return AsVariant(WriteState::FAILURE);
+    }
+
+    if (mGIFStruct.oldcode == -1) {
+      if (code >= MAX_BITS) {
+        return AsVariant(WriteState::FAILURE);  // The code's too big; something's wrong.
       }
 
-      // Clamp first and last rows to upper and lower edge of image.
-      if (drow_start < 0) {
-        drow_start = 0;
-      }
-      if ((unsigned)drow_end >= mGIFStruct.clamped_height) {
-        drow_end = mGIFStruct.clamped_height - 1;
-      }
+      mGIFStruct.firstchar = mGIFStruct.oldcode = code;
+
+      // Yield a pixel at the appropriate index in the colormap.
+      mGIFStruct.pixels_remaining--;
+      return AsVariant(ColormapIndexToPixel<PixelSize>(mGIFStruct.suffix[code]));
     }
 
-    // Row to process
-    uint8_t* rowp = GetCurrentRowBuffer();
+    int incode = code;
+    if (code >= mGIFStruct.avail) {
+      *mGIFStruct.stackp++ = mGIFStruct.firstchar;
+      code = mGIFStruct.oldcode;
 
-    // Convert color indices to Cairo pixels
-    uint8_t* from = rowp + mGIFStruct.clamped_width;
-    uint32_t* to = ((uint32_t*)rowp) + mGIFStruct.clamped_width;
-    uint32_t* cmap = mColormap;
-    for (uint32_t c = mGIFStruct.clamped_width; c > 0; c--) {
-      *--to = cmap[*--from];
-    }
-
-    // check for alpha (only for first frame)
-    if (mGIFStruct.is_transparent && !mSawTransparency) {
-      const uint32_t* rgb = (uint32_t*)rowp;
-      for (uint32_t i = mGIFStruct.clamped_width; i > 0; i--) {
-        if (*rgb++ == 0) {
-          mSawTransparency = true;
-          break;
-        }
+      if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
+        return AsVariant(WriteState::FAILURE);  // Stack overflow; something's wrong.
       }
     }
 
-    // If we're downscaling but not deinterlacing, we're done with this row and
-    // can commit it now. Otherwise, we'll let Deinterlacer do the committing
-    // when we call PropagatePassToDownscaler() at the end of this pass.
-    if (mDownscaler && !mDeinterlacer) {
-      mDownscaler->CommitRow();
-    }
+    while (code >= clearCode) {
+      if ((code >= MAX_BITS) || (code == mGIFStruct.prefix[code])) {
+        return AsVariant(WriteState::FAILURE);
+      }
 
-    if (drow_end > drow_start) {
-      // Duplicate rows if needed to reduce the "venetian blind" effect mentioned
-      // above. This writes out scanlines of the image in a way that isn't ordered
-      // vertically, which is incompatible with the filter that we use for
-      // downscale-during-decode, so we can't do this if we're downscaling.
-      MOZ_ASSERT_IF(mDownscaler, mDeinterlacer);
-      const uint32_t bpr = sizeof(uint32_t) * mGIFStruct.clamped_width;
-      for (int r = drow_start; r <= drow_end; r++) {
-        // Skip the row we wrote to above; that's what we're copying *from*.
-        if (r != int(mGIFStruct.irow)) {
-          memcpy(GetRowBuffer(r), rowp, bpr);
-        }
+      *mGIFStruct.stackp++ = mGIFStruct.suffix[code];
+      code = mGIFStruct.prefix[code];
+
+      if (mGIFStruct.stackp >= mGIFStruct.stack + MAX_BITS) {
+        return AsVariant(WriteState::FAILURE);  // Stack overflow; something's wrong.
       }
     }
-  }
 
-  mCurrentRow = drow_end;
-  mCurrentPass = mGIFStruct.ipass;
-  if (mGIFStruct.ipass == 1) {
-    mLastFlushedPass = mGIFStruct.ipass;   // interlaced starts at 1
-  }
+    *mGIFStruct.stackp++ = mGIFStruct.firstchar = mGIFStruct.suffix[code];
 
-  if (!mGIFStruct.interlaced) {
-    MOZ_ASSERT(!mDeinterlacer);
-    mGIFStruct.irow++;
-  } else {
-    static const uint8_t kjump[5] = { 1, 8, 8, 4, 2 };
-    int currentPass = mGIFStruct.ipass;
+    // Define a new codeword in the dictionary.
+    if (mGIFStruct.avail < 4096) {
+      mGIFStruct.prefix[mGIFStruct.avail] = mGIFStruct.oldcode;
+      mGIFStruct.suffix[mGIFStruct.avail] = mGIFStruct.firstchar;
+      mGIFStruct.avail++;
 
-    do {
-      // Row increments resp. per 8,8,4,2 rows
-      mGIFStruct.irow += kjump[mGIFStruct.ipass];
-      if (mGIFStruct.irow >= mGIFStruct.clamped_height) {
-        // Next pass starts resp. at row 4,2,1,0
-        mGIFStruct.irow = 8 >> mGIFStruct.ipass;
-        mGIFStruct.ipass++;
+      // If we've used up all the codewords of a given length increase the
+      // length of codewords by one bit, but don't exceed the specified maximum
+      // codeword size of 12 bits.
+      if (((mGIFStruct.avail & mGIFStruct.codemask) == 0) &&
+          (mGIFStruct.avail < 4096)) {
+        mGIFStruct.codesize++;
+        mGIFStruct.codemask += mGIFStruct.avail;
       }
-    } while (mGIFStruct.irow >= mGIFStruct.clamped_height);
-
-    // We've finished a pass. If we're downscaling, it's time to propagate the
-    // rows we've decoded so far from our Deinterlacer to our Downscaler.
-    if (mGIFStruct.ipass > currentPass && mDownscaler) {
-      MOZ_ASSERT(mDeinterlacer);
-      mDeinterlacer->PropagatePassToDownscaler(*mDownscaler);
-      FlushImageData();
-      mDownscaler->ResetForNextProgressivePass();
     }
-  }
 
-  return --mGIFStruct.rows_remaining;
-}
-
-//******************************************************************************
-// Perform Lempel-Ziv-Welch decoding
-bool
-nsGIFDecoder2::DoLzw(const uint8_t* q)
-{
-  if (!mGIFStruct.rows_remaining) {
-    return true;
-  }
-  if (MOZ_UNLIKELY(mDownscaler && mDownscaler->IsFrameComplete())) {
-    return true;
+    mGIFStruct.oldcode = incode;
   }
 
-  // Copy all the decoder state variables into locals so the compiler
-  // won't worry about them being aliased.  The locals will be homed
-  // back into the GIF decoder structure when we exit.
-  int avail       = mGIFStruct.avail;
-  int bits        = mGIFStruct.bits;
-  int codesize    = mGIFStruct.codesize;
-  int codemask    = mGIFStruct.codemask;
-  int count       = mGIFStruct.count;
-  int oldcode     = mGIFStruct.oldcode;
-  const int clear_code = ClearCode();
-  uint8_t firstchar = mGIFStruct.firstchar;
-  int32_t datum     = mGIFStruct.datum;
-  uint16_t* prefix  = mGIFStruct.prefix;
-  uint8_t* stackp   = mGIFStruct.stackp;
-  uint8_t* suffix   = mGIFStruct.suffix;
-  uint8_t* stack    = mGIFStruct.stack;
-  uint8_t* rowp     = mGIFStruct.rowp;
-
-  uint8_t* rowend = GetCurrentRowBuffer() + mGIFStruct.clamped_width;
-
-#define OUTPUT_ROW()                                        \
-  PR_BEGIN_MACRO                                            \
-    if (!OutputRow())                                       \
-      goto END;                                             \
-    rowp = GetCurrentRowBuffer();                           \
-    rowend = rowp + mGIFStruct.clamped_width;               \
-  PR_END_MACRO
+  if (MOZ_UNLIKELY(mGIFStruct.stackp <= mGIFStruct.stack)) {
+    MOZ_ASSERT_UNREACHABLE("No decoded data but we didn't return early?");
+    return AsVariant(WriteState::FAILURE);
+  }
 
-  for (const uint8_t* ch = q; count-- > 0; ch++) {
-    // Feed the next byte into the decoder's 32-bit input buffer.
-    datum += ((int32_t)* ch) << bits;
-    bits += 8;
-
-    // Check for underflow of decoder's 32-bit input buffer.
-    while (bits >= codesize) {
-      // Get the leading variable-length symbol from the data stream
-      int code = datum & codemask;
-      datum >>= codesize;
-      bits -= codesize;
-
-      // Reset the dictionary to its original state, if requested
-      if (code == clear_code) {
-        codesize = mGIFStruct.datasize + 1;
-        codemask = (1 << codesize) - 1;
-        avail = clear_code + 2;
-        oldcode = -1;
-        continue;
-      }
-
-      // Check for explicit end-of-stream code
-      if (code == (clear_code + 1)) {
-        // end-of-stream should only appear after all image data
-        return (mGIFStruct.rows_remaining == 0);
-      }
-
-      if (MOZ_UNLIKELY(mDownscaler && mDownscaler->IsFrameComplete())) {
-        goto END;
-      }
+  // Yield a pixel at the appropriate index in the colormap.
+  mGIFStruct.pixels_remaining--;
+  return AsVariant(ColormapIndexToPixel<PixelSize>(*--mGIFStruct.stackp));
+}
 
-      if (oldcode == -1) {
-        if (code >= MAX_BITS) {
-          return false;
-        }
-        *rowp++ = suffix[code] & mColorMask; // ensure index is within colormap
-        if (rowp == rowend) {
-          OUTPUT_ROW();
-        }
-
-        firstchar = oldcode = code;
-        continue;
-      }
-
-      int incode = code;
-      if (code >= avail) {
-        *stackp++ = firstchar;
-        code = oldcode;
-
-        if (stackp >= stack + MAX_BITS) {
-          return false;
-        }
-      }
-
-      while (code >= clear_code) {
-        if ((code >= MAX_BITS) || (code == prefix[code])) {
-          return false;
-        }
-
-        *stackp++ = suffix[code];
-        code = prefix[code];
+bool
+nsGIFDecoder2::DoLzw(const uint8_t* aData)
+{
+  const uint8_t* currentByte = aData;
+  while (mGIFStruct.count > 0 && mGIFStruct.pixels_remaining > 0) {
+    auto result = mGIFStruct.images_decoded > 0
+                ? mPipe.WritePixels<uint8_t>([&]() { return YieldPixel<uint8_t>(currentByte); })
+                : mPipe.WritePixels<uint32_t>([&]() { return YieldPixel<uint32_t>(currentByte); });
 
-        if (stackp == stack + MAX_BITS) {
-          return false;
-        }
-      }
-
-      *stackp++ = firstchar = suffix[code];
-
-      // Define a new codeword in the dictionary.
-      if (avail < 4096) {
-        prefix[avail] = oldcode;
-        suffix[avail] = firstchar;
-        avail++;
+    switch (result) {
+      case WriteState::NEED_MORE_DATA:
+        continue;
 
-        // If we've used up all the codewords of a given length
-        // increase the length of codewords by one bit, but don't
-        // exceed the specified maximum codeword size of 12 bits.
-        if (((avail & codemask) == 0) && (avail < 4096)) {
-          codesize++;
-          codemask += avail;
-        }
-      }
-      oldcode = incode;
+      case WriteState::FINISHED:
+        NS_WARN_IF(mGIFStruct.pixels_remaining > 0);
+        mGIFStruct.pixels_remaining = 0;
+        return true;
 
-      // Copy the decoded data out to the scanline buffer.
-      do {
-        *rowp++ = *--stackp & mColorMask; // ensure index is within colormap
-        if (rowp == rowend) {
-          OUTPUT_ROW();
-
-          // Consume decoded data that falls past the end of the clamped width.
-          stackp -= mGIFStruct.width - mGIFStruct.clamped_width;
-          stackp = std::max(stackp, stack);
-        }
-      } while (stackp > stack);
+      case WriteState::FAILURE:
+        return false;
     }
   }
 
-  END:
-
-  // Home the local copies of the GIF decoder state variables
-  mGIFStruct.avail = avail;
-  mGIFStruct.bits = bits;
-  mGIFStruct.codesize = codesize;
-  mGIFStruct.codemask = codemask;
-  mGIFStruct.count = count;
-  mGIFStruct.oldcode = oldcode;
-  mGIFStruct.firstchar = firstchar;
-  mGIFStruct.datum = datum;
-  mGIFStruct.stackp = stackp;
-  mGIFStruct.rowp = rowp;
-
   return true;
 }
 
 /// Expand the colormap from RGB to Packed ARGB as needed by Cairo.
 /// And apply any LCMS transformation.
 static void
 ConvertColormap(uint32_t* aColormap, uint32_t aColors)
 {
@@ -1060,159 +850,96 @@ nsGIFDecoder2::WriteInternal(const char*
 
         if (mDownscaler) {
           MOZ_ASSERT_UNREACHABLE("Doing downscale-during-decode "
                                  "for an animated image?");
           mDownscaler.reset();
         }
       }
 
+      IntRect frameRect;
+
       // Get image offsets, with respect to the screen origin
-      mGIFStruct.x_offset = GETINT16(q);
-      mGIFStruct.y_offset = GETINT16(q + 2);
+      frameRect.x = GETINT16(q);
+      frameRect.y = GETINT16(q + 2);
 
       // Get image width and height.
-      mGIFStruct.width  = GETINT16(q + 4);
-      mGIFStruct.height = GETINT16(q + 6);
+      frameRect.width  = GETINT16(q + 4);
+      frameRect.height = GETINT16(q + 6);
 
       if (!mGIFStruct.images_decoded) {
         // Work around broken GIF files where the logical screen
         // size has weird width or height.  We assume that GIF87a
         // files don't contain animations.
-        if ((mGIFStruct.screen_height < mGIFStruct.height) ||
-            (mGIFStruct.screen_width < mGIFStruct.width) ||
+        if ((mGIFStruct.screen_height < frameRect.height) ||
+            (mGIFStruct.screen_width < frameRect.width) ||
             (mGIFStruct.version == 87)) {
-          mGIFStruct.screen_height = mGIFStruct.height;
-          mGIFStruct.screen_width = mGIFStruct.width;
-          mGIFStruct.x_offset = 0;
-          mGIFStruct.y_offset = 0;
+          mGIFStruct.screen_height = frameRect.height;
+          mGIFStruct.screen_width = frameRect.width;
+          frameRect.MoveTo(0, 0);
         }
         // Create the image container with the right size.
         BeginGIF();
         if (HasError()) {
           // Setting the size led to an error.
           mGIFStruct.state = gif_error;
           return;
         }
 
         // If we were doing a metadata decode, we're done.
         if (IsMetadataDecode()) {
-          IntRect frameRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
-                            mGIFStruct.width, mGIFStruct.height);
           CheckForTransparency(frameRect);
           return;
         }
       }
 
       // Work around more broken GIF files that have zero image width or height
-      if (!mGIFStruct.height || !mGIFStruct.width) {
-        mGIFStruct.height = mGIFStruct.screen_height;
-        mGIFStruct.width = mGIFStruct.screen_width;
-        if (!mGIFStruct.height || !mGIFStruct.width) {
+      if (!frameRect.height || !frameRect.width) {
+        frameRect.height = mGIFStruct.screen_height;
+        frameRect.width = mGIFStruct.screen_width;
+        if (!frameRect.height || !frameRect.width) {
           mGIFStruct.state = gif_error;
           break;
         }
       }
 
-      // Hack around GIFs with frame rects outside the given screen bounds.
-      IntRect clampedRect =
-        ClampToImageRect(IntRect(mGIFStruct.x_offset, mGIFStruct.y_offset,
-                                 mGIFStruct.width, mGIFStruct.height));
-      if (clampedRect.IsEmpty()) {
-        // XXX Bug 1227546 - Maybe we should treat this as valid?
-        mGIFStruct.state = gif_error;
-        break;
-      }
-      mGIFStruct.clamped_width = clampedRect.width;
-      mGIFStruct.clamped_height = clampedRect.height;
-
-      MOZ_ASSERT(mGIFStruct.clamped_width <= mGIFStruct.width);
-      MOZ_ASSERT(mGIFStruct.clamped_height <= mGIFStruct.height);
-
       // Depth of colors is determined by colormap
       // (q[8] & 0x80) indicates local colormap
       // bits per pixel is (q[8]&0x07 + 1) when local colormap is set
       uint32_t depth = mGIFStruct.global_colormap_depth;
       if (q[8] & 0x80) {
         depth = (q[8]&0x07) + 1;
       }
       uint32_t realDepth = depth;
       while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
         realDepth++;
       }
+
       // Mask to limit the color values within the colormap
       mColorMask = 0xFF >> (8 - realDepth);
 
-      if (NS_FAILED(BeginImageFrame(realDepth))) {
+      // Determine if this frame is interlaced or not.
+      const bool isInterlaced = q[8] & 0x40;
+
+      if (NS_FAILED(BeginImageFrame(frameRect, realDepth, isInterlaced))) {
         mGIFStruct.state = gif_error;
         return;
       }
-      MOZ_FALLTHROUGH; // to continue decoding header.
-    }
 
-    case gif_image_header_continue: {
       // While decoders can reuse frames, we unconditionally increment
       // mGIFStruct.images_decoded when we're done with a frame, so we both can
       // and need to zero out the colormap and image data after every new frame.
       memset(mImageData, 0, mImageDataLength);
       if (mColormap) {
         memset(mColormap, 0, mColormapSize);
       }
 
-      if (!mGIFStruct.images_decoded) {
-        // Send a onetime invalidation for the first frame if it has a y-axis
-        // offset. Otherwise, the area may never be refreshed and the
-        // placeholder will remain on the screen. (Bug 37589)
-        if (mGIFStruct.y_offset > 0) {
-          if (mDownscaler) {
-            IntRect targetRect = IntRect(IntPoint(), mDownscaler->TargetSize());
-            PostInvalidation(IntRect(IntPoint(), GetSize()), Some(targetRect));
-          } else {
-            nsIntRect r(0, 0, mGIFStruct.screen_width, mGIFStruct.y_offset);
-            PostInvalidation(r);
-          }
-        }
-      }
-
-      if (q[8] & 0x40) {
-        mGIFStruct.interlaced = true;
-        mGIFStruct.ipass = 1;
-        if (mDownscaler) {
-          mDeinterlacer.emplace(mDownscaler->FrameSize());
+      // Clear state from last image
+      mGIFStruct.pixels_remaining = frameRect.width * frameRect.height;
 
-          if (!mDeinterlacer->IsValid()) {
-            mDeinterlacer.reset();
-            mGIFStruct.state = gif_error;
-            break;
-          }
-        }
-      } else {
-        mGIFStruct.interlaced = false;
-        mGIFStruct.ipass = 0;
-      }
-
-      // Only apply the Haeberli display hack on the first frame
-      mGIFStruct.progressive_display = (mGIFStruct.images_decoded == 0);
-
-      // Clear state from last image
-      mGIFStruct.irow = 0;
-      mGIFStruct.rows_remaining = mGIFStruct.clamped_height;
-      mGIFStruct.rowp = GetCurrentRowBuffer();
-
-      // Depth of colors is determined by colormap
-      // (q[8] & 0x80) indicates local colormap
-      // bits per pixel is (q[8]&0x07 + 1) when local colormap is set
-      uint32_t depth = mGIFStruct.global_colormap_depth;
-      if (q[8] & 0x80) {
-        depth = (q[8]&0x07) + 1;
-      }
-      uint32_t realDepth = depth;
-      while (mGIFStruct.tpixel >= (1 << realDepth) && (realDepth < 8)) {
-        realDepth++;
-      }
       // has a local colormap?
       if (q[8] & 0x80) {
         mGIFStruct.local_colormap_size = 1 << depth;
         if (!mGIFStruct.images_decoded) {
           // First frame has local colormap, allocate space for it
           // as the image frame doesn't have its own palette
           mColormapSize = sizeof(uint32_t) << realDepth;
           if (!mGIFStruct.local_colormap) {
@@ -1255,19 +982,19 @@ nsGIFDecoder2::WriteInternal(const char*
       ConvertColormap(mColormap, mGIFStruct.local_colormap_size);
       GETN(1, gif_lzw_start);
       break;
 
     case gif_sub_block:
       mGIFStruct.count = *q;
       if (mGIFStruct.count) {
         // Still working on the same image: Process next LZW data block
-        // Make sure there are still rows left. If the GIF data
+        // Make sure there are still pixels left. If the GIF data
         // is corrupt, we may not get an explicit terminator.
-        if (!mGIFStruct.rows_remaining) {
+        if (mGIFStruct.pixels_remaining <= 0) {
 #ifdef DONT_TOLERATE_BROKEN_GIFS
           mGIFStruct.state = gif_error;
           break;
 #else
           // This is an illegal GIF, but we remain tolerant.
           GETN(1, gif_sub_block);
 #endif
           if (mGIFStruct.count == GIF_TRAILER) {
@@ -1327,18 +1054,16 @@ nsGIFDecoder2::WriteInternal(const char*
 
     mGIFStruct.bytes_to_consume -= len;
   }
 
 // We want to flush before returning if we're on the first frame
 done:
   if (!mGIFStruct.images_decoded) {
     FlushImageData();
-    mLastFlushedRow = mCurrentRow;
-    mLastFlushedPass = mCurrentPass;
   }
 }
 
 bool
 nsGIFDecoder2::SetHold(const uint8_t* buf1, uint32_t count1,
                        const uint8_t* buf2 /* = nullptr */,
                        uint32_t count2 /* = 0 */)
 {
--- a/image/decoders/nsGIFDecoder2.h
+++ b/image/decoders/nsGIFDecoder2.h
@@ -3,19 +3,18 @@
  * 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_image_decoders_nsGIFDecoder2_h
 #define mozilla_image_decoders_nsGIFDecoder2_h
 
 #include "Decoder.h"
-#include "Deinterlacer.h"
 #include "GIF2.h"
-#include "nsCOMPtr.h"
+#include "SurfacePipe.h"
 
 namespace mozilla {
 namespace image {
 class RasterImage;
 
 //////////////////////////////////////////////////////////////////////
 // nsGIFDecoder2 Definition
 
@@ -29,52 +28,68 @@ public:
   virtual Telemetry::ID SpeedHistogram() override;
 
 private:
   friend class DecoderFactory;
 
   // Decoders should only be instantiated via DecoderFactory.
   explicit nsGIFDecoder2(RasterImage* aImage);
 
-  uint8_t*  GetCurrentRowBuffer();
-  uint8_t*  GetRowBuffer(uint32_t aRow);
+  /// Called when we begin decoding the image.
+  void      BeginGIF();
 
-  // These functions will be called when the decoder has a decoded row,
-  // frame size information, etc.
-  void      BeginGIF();
-  nsresult  BeginImageFrame(uint16_t aDepth);
+  /**
+   * Called when we begin decoding a frame.
+   *
+   * @param aFrameRect The region of the image that contains data. The region
+   *                   outside this rect is transparent.
+   * @param aDepth The palette depth of this frame.
+   * @param aIsInterlaced If true, this frame is an interlaced frame.
+   */
+  nsresult  BeginImageFrame(const gfx::IntRect& aFrameRect,
+                            uint16_t aDepth,
+                            bool aIsInterlaced);
+
+  /// Called when we finish decoding a frame.
   void      EndImageFrame();
+
+  /// Called when we finish decoding the entire image.
   void      FlushImageData();
-  void      FlushImageData(uint32_t fromRow, uint32_t rows);
 
   nsresult  GifWrite(const uint8_t* buf, uint32_t numbytes);
-  uint32_t  OutputRow();
-  bool      DoLzw(const uint8_t* q);
+
+  /// Transforms a palette index into a pixel.
+  template <typename PixelSize> PixelSize
+  ColormapIndexToPixel(uint8_t aIndex);
+
+  /// A generator function that performs LZW decompression and yields pixels.
+  template <typename PixelSize> NextPixel<PixelSize>
+  YieldPixel(const uint8_t*& aCurrentByte);
+
+  /// The entry point for LZW decompression.
+  bool      DoLzw(const uint8_t* aData);
+
   bool      SetHold(const uint8_t* buf, uint32_t count,
                     const uint8_t* buf2 = nullptr, uint32_t count2 = 0);
   bool      CheckForTransparency(const gfx::IntRect& aFrameRect);
   gfx::IntRect ClampToImageRect(const gfx::IntRect& aFrameRect);
 
   inline int ClearCode() const { return 1 << mGIFStruct.datasize; }
 
-  int32_t mCurrentRow;
-  int32_t mLastFlushedRow;
-
   uint32_t mOldColor;        // The old value of the transparent pixel
 
   // The frame number of the currently-decoding frame when we're in the middle
   // of decoding it, and -1 otherwise.
   int32_t mCurrentFrameIndex;
 
-  uint8_t mCurrentPass;
-  uint8_t mLastFlushedPass;
   uint8_t mColorMask;        // Apply this to the pixel to keep within colormap
   bool mGIFOpen;
   bool mSawTransparency;
 
   gif_struct mGIFStruct;
-  Maybe<Deinterlacer> mDeinterlacer;
+
+  SurfacePipe mPipe;  /// The SurfacePipe used to write to the output surface.
 };
 
 } // namespace image
 } // namespace mozilla
 
 #endif // mozilla_image_decoders_nsGIFDecoder2_h
--- a/image/decoders/nsJPEGDecoder.cpp
+++ b/image/decoders/nsJPEGDecoder.cpp
@@ -1,15 +1,16 @@
 /* -*- 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 "ImageLogging.h"
+#include "imgFrame.h"
 #include "nsJPEGDecoder.h"
 #include "Orientation.h"
 #include "EXIF.h"
 
 #include "nsIInputStream.h"
 
 #include "nspr.h"
 #include "nsCRT.h"
@@ -578,17 +579,17 @@ nsJPEGDecoder::ReadOrientationFromEXIF()
   EXIFData exif = EXIFParser::Parse(marker->data,
                                     static_cast<uint32_t>(marker->data_length));
   return exif.orientation;
 }
 
 void
 nsJPEGDecoder::NotifyDone()
 {
-  PostFrameStop(Opacity::OPAQUE);
+  PostFrameStop(Opacity::FULLY_OPAQUE);
   PostDecodeDone();
 }
 
 void
 nsJPEGDecoder::OutputScanlines(bool* suspend)
 {
   *suspend = false;
 
--- a/image/decoders/nsPNGDecoder.cpp
+++ b/image/decoders/nsPNGDecoder.cpp
@@ -2,16 +2,17 @@
  *
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImageLogging.h" // Must appear first
 #include "gfxColor.h"
 #include "gfxPlatform.h"
+#include "imgFrame.h"
 #include "nsColor.h"
 #include "nsIInputStream.h"
 #include "nsMemory.h"
 #include "nsPNGDecoder.h"
 #include "nsRect.h"
 #include "nspr.h"
 #include "png.h"
 #include "RasterImage.h"
@@ -205,17 +206,17 @@ nsPNGDecoder::EndImageFrame()
   if (mFrameIsHidden) {
     return;
   }
 
   mNumFrames++;
 
   Opacity opacity = Opacity::SOME_TRANSPARENCY;
   if (format == gfx::SurfaceFormat::B8G8R8X8) {
-    opacity = Opacity::OPAQUE;
+    opacity = Opacity::FULLY_OPAQUE;
   }
 
   PostFrameStop(opacity, mAnimInfo.mDispose, mAnimInfo.mTimeout,
                 mAnimInfo.mBlend);
 }
 
 void
 nsPNGDecoder::InitInternal()
--- a/image/imgFrame.cpp
+++ b/image/imgFrame.cpp
@@ -638,17 +638,17 @@ void
 imgFrame::Finish(Opacity aFrameOpacity /* = Opacity::SOME_TRANSPARENCY */,
                  DisposalMethod aDisposalMethod /* = DisposalMethod::KEEP */,
                  int32_t aRawTimeout /* = 0 */,
                  BlendMethod aBlendMethod /* = BlendMethod::OVER */)
 {
   MonitorAutoLock lock(mMonitor);
   MOZ_ASSERT(mLockCount > 0, "Image data should be locked");
 
-  if (aFrameOpacity == Opacity::OPAQUE) {
+  if (aFrameOpacity == Opacity::FULLY_OPAQUE) {
     mHasNoAlpha = true;
   }
 
   mDisposalMethod = aDisposalMethod;
   mTimeout = aRawTimeout;
   mBlendMethod = aBlendMethod;
   ImageUpdatedInternal(GetRect());
 }
--- a/image/imgFrame.h
+++ b/image/imgFrame.h
@@ -36,17 +36,17 @@ enum class DisposalMethod : int8_t {
   CLEAR_ALL = -1,  // Clear the whole image, revealing what's underneath.
   NOT_SPECIFIED,   // Leave the frame and let the new frame draw on top.
   KEEP,            // Leave the frame and let the new frame draw on top.
   CLEAR,           // Clear the frame's area, revealing what's underneath.
   RESTORE_PREVIOUS // Restore the previous (composited) frame.
 };
 
 enum class Opacity : uint8_t {
-  OPAQUE,
+  FULLY_OPAQUE,
   SOME_TRANSPARENCY
 };
 
 /**
  * AnimationData contains all of the information necessary for using an imgFrame
  * as part of an animation.
  *
  * It includes pointers to the raw image data of the underlying imgFrame, but
--- a/image/moz.build
+++ b/image/moz.build
@@ -50,17 +50,16 @@ EXPORTS += [
     'SurfaceFlags.h',
 ]
 
 UNIFIED_SOURCES += [
     'ClippedImage.cpp',
     'DecodePool.cpp',
     'Decoder.cpp',
     'DecoderFactory.cpp',
-    'Deinterlacer.cpp',
     'DynamicImage.cpp',
     'FrameAnimator.cpp',
     'FrozenImage.cpp',
     'Image.cpp',
     'ImageCacheKey.cpp',
     'ImageFactory.cpp',
     'ImageOps.cpp',
     'ImageWrapper.cpp',
--- a/ipc/testshell/TestShellParent.cpp
+++ b/ipc/testshell/TestShellParent.cpp
@@ -66,19 +66,17 @@ TestShellCommandParent::SetCallback(JSCo
 
 bool
 TestShellCommandParent::RunCallback(const nsString& aResponse)
 {
   NS_ENSURE_TRUE(mCallback.isObject(), false);
 
   // We're about to run script via JS_CallFunctionValue, so we need an
   // AutoEntryScript. This is just for testing and not in any spec.
-  dom::AutoEntryScript aes(
-      xpc::NativeGlobal(js::GetGlobalForObjectCrossCompartment(&mCallback.toObject())),
-      "TestShellCommand");
+  dom::AutoEntryScript aes(&mCallback.toObject(), "TestShellCommand");
   JSContext* cx = aes.cx();
   JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
 
   JSString* str = JS_NewUCStringCopyN(cx, aResponse.get(), aResponse.Length());
   NS_ENSURE_TRUE(str, false);
 
   JS::Rooted<JS::Value> strVal(cx, JS::StringValue(str));
 
--- a/js/ipc/WrapperAnswer.cpp
+++ b/js/ipc/WrapperAnswer.cpp
@@ -272,19 +272,18 @@ WrapperAnswer::RecvHasOwn(const ObjectId
     return ok(rs);
 }
 
 bool
 WrapperAnswer::RecvGet(const ObjectId& objId, const JSVariant& receiverVar,
                        const JSIDVariant& idVar, ReturnStatus* rs, JSVariant* result)
 {
     // We may run scripted getters.
-    AutoEntryScript aes(xpc::NativeGlobal(scopeForTargetObjects()),
+    AutoEntryScript aes(scopeForTargetObjects(),
                         "Cross-Process Object Wrapper 'get'");
-    aes.TakeOwnershipOfErrorReporting();
     JSContext* cx = aes.cx();
 
     // The outparam will be written to the buffer, so it must be set even if
     // the parent won't read it.
     *result = UndefinedVariant();
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
@@ -310,19 +309,18 @@ WrapperAnswer::RecvGet(const ObjectId& o
     return ok(rs);
 }
 
 bool
 WrapperAnswer::RecvSet(const ObjectId& objId, const JSIDVariant& idVar, const JSVariant& value,
                        const JSVariant& receiverVar, ReturnStatus* rs)
 {
     // We may run scripted setters.
-    AutoEntryScript aes(xpc::NativeGlobal(scopeForTargetObjects()),
+    AutoEntryScript aes(scopeForTargetObjects(),
                         "Cross-Process Object Wrapper 'set'");
-    aes.TakeOwnershipOfErrorReporting();
     JSContext* cx = aes.cx();
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
         return fail(aes, rs);
 
     LOG("set %s[%s] = %s", ReceiverObj(objId), Identifier(idVar), InVariant(value));
 
@@ -372,19 +370,18 @@ WrapperAnswer::RecvIsExtensible(const Ob
 bool
 WrapperAnswer::RecvCallOrConstruct(const ObjectId& objId,
                                    InfallibleTArray<JSParam>&& argv,
                                    const bool& construct,
                                    ReturnStatus* rs,
                                    JSVariant* result,
                                    nsTArray<JSParam>* outparams)
 {
-    AutoEntryScript aes(xpc::NativeGlobal(scopeForTargetObjects()),
+    AutoEntryScript aes(scopeForTargetObjects(),
                         "Cross-Process Object Wrapper call/construct");
-    aes.TakeOwnershipOfErrorReporting();
     JSContext* cx = aes.cx();
 
     // The outparam will be written to the buffer, so it must be set even if
     // the parent won't read it.
     *result = UndefinedVariant();
 
     RootedObject obj(cx, findObjectById(cx, objId));
     if (!obj)
--- a/js/moz.configure
+++ b/js/moz.configure
@@ -1,5 +1,20 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# /!\ Use js_option() instead of option() in this file. /!\
+# =========================================================
+
+@depends(build_project, '--help')
+def js_shell_default(build_project, help):
+    return build_project == 'js'
+
+js_option('--disable-js-shell', default=js_shell_default,
+       help='Do not build the JS shell')
+
+@depends('--disable-js-shell')
+def js_shell(value):
+    if not value:
+        set_config('JS_DISABLE_SHELL', '1')
--- a/js/public/Class.h
+++ b/js/public/Class.h
@@ -660,17 +660,29 @@ struct JSClass {
 };
 
 #define JSCLASS_HAS_PRIVATE             (1<<0)  // objects have private slot
 #define JSCLASS_DELAY_METADATA_CALLBACK (1<<1)  // class's initialization code
                                                 // will call
                                                 // SetNewObjectMetadata itself
 #define JSCLASS_PRIVATE_IS_NSISUPPORTS  (1<<3)  // private is (nsISupports*)
 #define JSCLASS_IS_DOMJSCLASS           (1<<4)  // objects are DOM
-// Bit 5 is unused.
+#define JSCLASS_HAS_XRAYED_CONSTRUCTOR  (1<<5)  // if wrapped by an xray
+                                                // wrapper, the builtin
+                                                // class's constructor won't
+                                                // be unwrapped and invoked.
+                                                // Instead, the constructor is
+                                                // resolved in the caller's
+                                                // compartment and invoked
+                                                // with a wrapped newTarget.
+                                                // The constructor has to
+                                                // detect and handle this
+                                                // situation.
+                                                // See PromiseConstructor for
+                                                // details.
 #define JSCLASS_EMULATES_UNDEFINED      (1<<6)  // objects of this class act
                                                 // like the value undefined,
                                                 // in some contexts
 #define JSCLASS_USERBIT1                (1<<7)  // Reserved for embeddings.
 
 // To reserve slots fetched and stored via JS_Get/SetReservedSlot, bitwise-or
 // JSCLASS_HAS_RESERVED_SLOTS(n) into the initializer for JSClass.flags, where
 // n is a constant in [1, 255].  Reserved slots are indexed from 0 to n-1.
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -168,17 +168,16 @@ ifdef GNU_CC
 CFLAGS   += -mcpu=v9
 CXXFLAGS += -mcpu=v9
 endif # GNU_CC
 
 endif
 endif
 
 SCRIPTS = $(JS_CONFIG_NAME)
-SDK_BINARY = $(JS_CONFIG_NAME)
 
 $(LIBRARY_NAME).pc: js.pc
 	cp $^ $@
 
 install:: $(LIBRARY_NAME).pc
 	$(SYSINSTALL) $^ $(DESTDIR)$(libdir)/pkgconfig
 
 install:: js-config.h
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -2852,17 +2852,17 @@ class MOZ_STACK_CLASS FunctionValidator
         }
         MOZ_CRASH("unexpected literal type");
     }
     MOZ_WARN_UNUSED_RESULT bool writeCall(ParseNode* pn, Expr op) {
         return encoder().writeExpr(op) &&
                fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
     }
     MOZ_WARN_UNUSED_RESULT bool patchableCall(ParseNode* pn, size_t* offset) {
-        return encoder().writePatchableExpr(offset) &&
+        return encoder().writePatchableOneByteExpr(offset) &&
                fg_.addCallSiteLineNum(m().tokenStream().srcCoords.lineNum(pn->pn_pos.begin));
     }
     MOZ_WARN_UNUSED_RESULT bool writeSimdOp(SimdType simdType, SimdOperation op) {
         return encoder().writeExpr(SimdToExpr(simdType, op));
     }
 };
 
 } /* anonymous namespace */
@@ -3696,68 +3696,68 @@ CheckArrayAccess(FunctionValidator& f, P
     return true;
 }
 
 static bool
 CheckAndPrepareArrayAccess(FunctionValidator& f, ParseNode* viewName, ParseNode* indexExpr,
                            bool isSimd, Scalar::Type* viewType)
 {
     size_t flagsAt;
-    if (!f.encoder().writePatchableU8(&flagsAt))
+    if (!f.encoder().writePatchableFixedU8(&flagsAt))
         return false;
 
     // asm.js doesn't have constant offsets, so just encode a 0.
     if (!f.encoder().writeVarU32(0))
         return false;
 
     size_t prepareAt;
-    if (!f.encoder().writePatchableExpr(&prepareAt))
+    if (!f.encoder().writePatchableOneByteExpr(&prepareAt))
         return false;
 
     int32_t mask;
     if (!CheckArrayAccess(f, viewName, indexExpr, isSimd, viewType, &mask))
         return false;
 
     // asm.js only has naturally-aligned accesses.
     size_t align = TypedArrayElemSize(*viewType);
     MOZ_ASSERT(IsPowerOfTwo(align));
-    f.encoder().patchVarU8(flagsAt, CeilingLog2(align));
+    f.encoder().patchFixedU8(flagsAt, CeilingLog2(align));
 
     // Don't generate the mask op if there is no need for it which could happen for
     // a shift of zero or a SIMD access.
     if (mask != NoMask) {
-        f.encoder().patchExpr(prepareAt, Expr::I32And);
+        f.encoder().patchOneByteExpr(prepareAt, Expr::I32And);
         return f.writeInt32Lit(mask);
     }
 
-    f.encoder().patchExpr(prepareAt, Expr::Id);
+    f.encoder().patchOneByteExpr(prepareAt, Expr::Id);
     return true;
 }
 
 static bool
 CheckLoadArray(FunctionValidator& f, ParseNode* elem, Type* type)
 {
     Scalar::Type viewType;
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     if (!CheckAndPrepareArrayAccess(f, ElemBase(elem), ElemIndex(elem), NoSimd, &viewType))
         return false;
 
     switch (viewType) {
-      case Scalar::Int8:    f.encoder().patchExpr(opcodeAt, Expr::I32Load8S);  break;
-      case Scalar::Uint8:   f.encoder().patchExpr(opcodeAt, Expr::I32Load8U);  break;
-      case Scalar::Int16:   f.encoder().patchExpr(opcodeAt, Expr::I32Load16S); break;
-      case Scalar::Uint16:  f.encoder().patchExpr(opcodeAt, Expr::I32Load16U); break;
+      case Scalar::Int8:    f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Load8S);  break;
+      case Scalar::Uint8:   f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Load8U);  break;
+      case Scalar::Int16:   f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Load16S); break;
+      case Scalar::Uint16:  f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Load16U); break;
       case Scalar::Uint32:
-      case Scalar::Int32:   f.encoder().patchExpr(opcodeAt, Expr::I32Load);    break;
-      case Scalar::Float32: f.encoder().patchExpr(opcodeAt, Expr::F32Load);    break;
-      case Scalar::Float64: f.encoder().patchExpr(opcodeAt, Expr::F64Load);    break;
+      case Scalar::Int32:   f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Load);    break;
+      case Scalar::Float32: f.encoder().patchOneByteExpr(opcodeAt, Expr::F32Load);    break;
+      case Scalar::Float64: f.encoder().patchOneByteExpr(opcodeAt, Expr::F64Load);    break;
       default: MOZ_CRASH("unexpected scalar type");
     }
 
     switch (viewType) {
       case Scalar::Int8:
       case Scalar::Int16:
       case Scalar::Int32:
       case Scalar::Uint8:
@@ -3776,17 +3776,17 @@ CheckLoadArray(FunctionValidator& f, Par
 
     return true;
 }
 
 static bool
 CheckStoreArray(FunctionValidator& f, ParseNode* lhs, ParseNode* rhs, Type* type)
 {
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     Scalar::Type viewType;
     if (!CheckAndPrepareArrayAccess(f, ElemBase(lhs), ElemIndex(lhs), NoSimd, &viewType))
         return false;
 
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsType))
@@ -3812,37 +3812,37 @@ CheckStoreArray(FunctionValidator& f, Pa
         break;
       default:
         MOZ_CRASH("Unexpected view type");
     }
 
     switch (viewType) {
       case Scalar::Int8:
       case Scalar::Uint8:
-        f.encoder().patchExpr(opcodeAt, Expr::I32Store8);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Store8);
         break;
       case Scalar::Int16:
       case Scalar::Uint16:
-        f.encoder().patchExpr(opcodeAt, Expr::I32Store16);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Store16);
         break;
       case Scalar::Int32:
       case Scalar::Uint32:
-        f.encoder().patchExpr(opcodeAt, Expr::I32Store);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Store);
         break;
       case Scalar::Float32:
         if (rhsType.isFloatish())
-            f.encoder().patchExpr(opcodeAt, Expr::F32Store);
+            f.encoder().patchOneByteExpr(opcodeAt, Expr::F32Store);
         else
-            f.encoder().patchExpr(opcodeAt, Expr::F64StoreF32);
+            f.encoder().patchOneByteExpr(opcodeAt, Expr::F64StoreF32);
         break;
       case Scalar::Float64:
         if (rhsType.isFloatish())
-            f.encoder().patchExpr(opcodeAt, Expr::F32StoreF64);
+            f.encoder().patchOneByteExpr(opcodeAt, Expr::F32StoreF64);
         else
-            f.encoder().patchExpr(opcodeAt, Expr::F64Store);
+            f.encoder().patchOneByteExpr(opcodeAt, Expr::F64Store);
         break;
       default: MOZ_CRASH("unexpected scalar type");
     }
 
     *type = rhsType;
     return true;
 }
 
@@ -3964,83 +3964,83 @@ static bool
 CheckMathAbs(FunctionValidator& f, ParseNode* call, Type* type)
 {
     if (CallArgListLength(call) != 1)
         return f.fail(call, "Math.abs must be passed 1 argument");
 
     ParseNode* arg = CallArgList(call);
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     Type argType;
     if (!CheckExpr(f, arg, &argType))
         return false;
 
     if (argType.isSigned()) {
-        f.encoder().patchExpr(opcodeAt, Expr::I32Abs);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Abs);
         *type = Type::Unsigned;
         return true;
     }
 
     if (argType.isMaybeDouble()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F64Abs);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F64Abs);
         *type = Type::Double;
         return true;
     }
 
     if (argType.isMaybeFloat()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F32Abs);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F32Abs);
         *type = Type::Floatish;
         return true;
     }
 
     return f.failf(call, "%s is not a subtype of signed, float? or double?", argType.toChars());
 }
 
 static bool
 CheckMathSqrt(FunctionValidator& f, ParseNode* call, Type* type)
 {
     if (CallArgListLength(call) != 1)
         return f.fail(call, "Math.sqrt must be passed 1 argument");
 
     ParseNode* arg = CallArgList(call);
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     Type argType;
     if (!CheckExpr(f, arg, &argType))
         return false;
 
     if (argType.isMaybeDouble()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F64Sqrt);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F64Sqrt);
         *type = Type::Double;
         return true;
     }
 
     if (argType.isMaybeFloat()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F32Sqrt);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F32Sqrt);
         *type = Type::Floatish;
         return true;
     }
 
     return f.failf(call, "%s is neither a subtype of double? nor float?", argType.toChars());
 }
 
 static bool
 CheckMathMinMax(FunctionValidator& f, ParseNode* callNode, bool isMax, Type* type)
 {
     if (CallArgListLength(callNode) < 2)
         return f.fail(callNode, "Math.min/max must be passed at least 2 arguments");
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     ParseNode* firstArg = CallArgList(callNode);
     Type firstType;
     if (!CheckExpr(f, firstArg, &firstType))
         return false;
 
     Expr expr;
@@ -4055,17 +4055,17 @@ CheckMathMinMax(FunctionValidator& f, Pa
     } else if (firstType.isSigned()) {
         *type = Type::Signed;
         firstType = Type::Signed;
         expr = isMax ? Expr::I32Max : Expr::I32Min;
     } else {
         return f.failf(firstArg, "%s is not a subtype of double?, float? or signed",
                        firstType.toChars());
     }
-    f.encoder().patchExpr(opcodeAt, expr);
+    f.encoder().patchOneByteExpr(opcodeAt, expr);
 
     unsigned numArgs = CallArgListLength(callNode);
     ParseNode* nextArg = NextNode(firstArg);
     for (unsigned i = 1; i < numArgs; i++, nextArg = NextNode(nextArg)) {
         if (i != numArgs - 1 && !f.encoder().writeExpr(expr))
             return false;
 
         Type nextType;
@@ -4116,17 +4116,17 @@ CheckAtomicsFence(FunctionValidator& f, 
     *type = Type::Void;
     return f.encoder().writeExpr(Expr::AtomicsFence);
 }
 
 static bool
 WriteAtomicOperator(FunctionValidator& f, Expr opcode, size_t* viewTypeAt)
 {
     return f.encoder().writeExpr(opcode) &&
-           f.encoder().writePatchableU8(viewTypeAt);
+           f.encoder().writePatchableFixedU8(viewTypeAt);
 }
 
 static bool
 CheckAtomicsLoad(FunctionValidator& f, ParseNode* call, Type* type)
 {
     if (CallArgListLength(call) != 2)
         return f.fail(call, "Atomics.load must be passed 2 arguments");
 
@@ -4136,17 +4136,17 @@ CheckAtomicsLoad(FunctionValidator& f, P
     size_t viewTypeAt;
     if (!WriteAtomicOperator(f, Expr::I32AtomicsLoad, &viewTypeAt))
         return false;
 
     Scalar::Type viewType;
     if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
         return false;
 
-    f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
+    f.encoder().patchFixedU8(viewTypeAt, uint8_t(viewType));
 
     *type = Type::Int;
     return true;
 }
 
 static bool
 CheckAtomicsStore(FunctionValidator& f, ParseNode* call, Type* type)
 {
@@ -4167,17 +4167,17 @@ CheckAtomicsStore(FunctionValidator& f, 
 
     Type rhsType;
     if (!CheckExpr(f, valueArg, &rhsType))
         return false;
 
     if (!rhsType.isIntish())
         return f.failf(arrayArg, "%s is not a subtype of intish", rhsType.toChars());
 
-    f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
+    f.encoder().patchFixedU8(viewTypeAt, uint8_t(viewType));
 
     *type = rhsType;
     return true;
 }
 
 static bool
 CheckAtomicsBinop(FunctionValidator& f, ParseNode* call, Type* type, AtomicOp op)
 {
@@ -4186,31 +4186,31 @@ CheckAtomicsBinop(FunctionValidator& f, 
 
     ParseNode* arrayArg = CallArgList(call);
     ParseNode* indexArg = NextNode(arrayArg);
     ParseNode* valueArg = NextNode(indexArg);
 
     size_t viewTypeAt;
     if (!WriteAtomicOperator(f, Expr::I32AtomicsBinOp, &viewTypeAt))
         return false;
-    if (!f.encoder().writeU8(uint8_t(op)))
+    if (!f.encoder().writeFixedU8(uint8_t(op)))
         return false;
 
     Scalar::Type viewType;
     if (!CheckSharedArrayAtomicAccess(f, arrayArg, indexArg, &viewType))
         return false;
 
     Type valueArgType;
     if (!CheckExpr(f, valueArg, &valueArgType))
         return false;
 
     if (!valueArgType.isIntish())
         return f.failf(valueArg, "%s is not a subtype of intish", valueArgType.toChars());
 
-    f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
+    f.encoder().patchFixedU8(viewTypeAt, uint8_t(viewType));
 
     *type = Type::Int;
     return true;
 }
 
 static bool
 CheckAtomicsIsLockFree(FunctionValidator& f, ParseNode* call, Type* type)
 {
@@ -4255,17 +4255,17 @@ CheckAtomicsCompareExchange(FunctionVali
         return false;
 
     if (!oldValueArgType.isIntish())
         return f.failf(oldValueArg, "%s is not a subtype of intish", oldValueArgType.toChars());
 
     if (!newValueArgType.isIntish())
         return f.failf(newValueArg, "%s is not a subtype of intish", newValueArgType.toChars());
 
-    f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
+    f.encoder().patchFixedU8(viewTypeAt, uint8_t(viewType));
 
     *type = Type::Int;
     return true;
 }
 
 static bool
 CheckAtomicsExchange(FunctionValidator& f, ParseNode* call, Type* type)
 {
@@ -4286,17 +4286,17 @@ CheckAtomicsExchange(FunctionValidator& 
 
     Type valueArgType;
     if (!CheckExpr(f, valueArg, &valueArgType))
         return false;
 
     if (!valueArgType.isIntish())
         return f.failf(arrayArg, "%s is not a subtype of intish", valueArgType.toChars());
 
-    f.encoder().patchU8(viewTypeAt, uint8_t(viewType));
+    f.encoder().patchFixedU8(viewTypeAt, uint8_t(viewType));
 
     *type = Type::Int;
     return true;
 }
 
 static bool
 CheckAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSAtomicsBuiltinFunction func,
                         Type* type)
@@ -4563,29 +4563,29 @@ CheckFFICall(FunctionValidator& f, Parse
     return true;
 }
 
 static bool
 CheckFloatCoercionArg(FunctionValidator& f, ParseNode* inputNode, Type inputType,
                       size_t opcodeAt)
 {
     if (inputType.isMaybeDouble()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F32DemoteF64);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F32DemoteF64);
         return true;
     }
     if (inputType.isSigned()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F32ConvertSI32);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F32ConvertSI32);
         return true;
     }
     if (inputType.isUnsigned()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F32ConvertUI32);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F32ConvertUI32);
         return true;
     }
     if (inputType.isFloatish()) {
-        f.encoder().patchExpr(opcodeAt, Expr::Id);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::Id);
         return true;
     }
 
     return f.failf(inputNode, "%s is not a subtype of signed, unsigned, double? or floatish",
                    inputType.toChars());
 }
 
 static bool
@@ -4595,30 +4595,30 @@ static bool
 CheckCoercionArg(FunctionValidator& f, ParseNode* arg, Type expected, Type* type)
 {
     MOZ_ASSERT(expected.isCanonicalValType());
 
     if (arg->isKind(PNK_CALL))
         return CheckCoercedCall(f, arg, expected, type);
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     Type argType;
     if (!CheckExpr(f, arg, &argType))
         return false;
 
     if (expected.isFloat()) {
         if (!CheckFloatCoercionArg(f, arg, argType, opcodeAt))
             return false;
     } else if (expected.isSimd()) {
         if (!(argType <= expected))
             return f.fail(arg, "argument to SIMD coercion isn't from the correct SIMD type");
-        f.encoder().patchExpr(opcodeAt, Expr::Id);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::Id);
     } else {
         MOZ_CRASH("not call coercions");
     }
 
     *type = Type::ret(expected);
     return true;
 }
 
@@ -4684,19 +4684,19 @@ CheckMathBuiltinCall(FunctionValidator& 
     if (!firstType.isMaybeFloat() && !firstType.isMaybeDouble())
         return f.fail(argNode, "arguments to math call should be a subtype of double? or float?");
 
     bool opIsDouble = firstType.isMaybeDouble();
     if (!opIsDouble && f32 == Expr::Unreachable)
         return f.fail(callNode, "math builtin cannot be used as float");
 
     if (opIsDouble)
-        f.encoder().patchExpr(opcodeAt, f64);
+        f.encoder().patchOneByteExpr(opcodeAt, f64);
     else
-        f.encoder().patchExpr(opcodeAt, f32);
+        f.encoder().patchOneByteExpr(opcodeAt, f32);
 
     if (arity == 2) {
         Type secondType;
         argNode = NextNode(argNode);
         if (!CheckExpr(f, argNode, &secondType))
             return false;
 
         if (firstType.isMaybeDouble() && !secondType.isMaybeDouble())
@@ -4743,17 +4743,17 @@ CheckSimdCallArgsPatchable(FunctionValid
     if (numArgs != expectedArity)
         return f.failf(call, "expected %u arguments to SIMD call, got %u", expectedArity, numArgs);
 
     ParseNode* arg = CallArgList(call);
     for (size_t i = 0; i < numArgs; i++, arg = NextNode(arg)) {
         MOZ_ASSERT(!!arg);
         Type argType;
         size_t patchAt;
-        if (!f.encoder().writePatchableExpr(&patchAt))
+        if (!f.encoder().writePatchableOneByteExpr(&patchAt))
             return false;
         if (!CheckExpr(f, arg, &argType))
             return false;
         if (!checkArg(f, arg, i, argType, patchAt))
             return false;
     }
 
     return true;
@@ -4812,24 +4812,24 @@ class CheckSimdScalarArgs
             if (simdType_ != SimdType::Float32x4 || !actualType.isDoubleLit()) {
                 return f.failf(arg, "%s is not a subtype of %s%s",
                                actualType.toChars(), formalType_.toChars(),
                                simdType_ == SimdType::Float32x4 ? " or doublelit" : "");
             }
 
             // We emitted a double literal and actually want a float32.
             MOZ_ASSERT(patchAt != size_t(-1));
-            f.encoder().patchExpr(patchAt, Expr::F32DemoteF64);
+            f.encoder().patchOneByteExpr(patchAt, Expr::F32DemoteF64);
             return true;
         }
 
         if (patchAt == size_t(-1))
             return true;
 
-        f.encoder().patchExpr(patchAt, Expr::Id);
+        f.encoder().patchOneByteExpr(patchAt, Expr::Id);
         return true;
     }
 };
 
 class CheckSimdSelectArgs
 {
     Type formalType_;
 
@@ -4869,17 +4869,17 @@ class CheckSimdVectorScalarArgs
             if (!(actualType <= Type(formalSimdType_))) {
                 return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
                                Type(formalSimdType_).toChars());
             }
 
             if (patchAt == size_t(-1))
                 return true;
 
-            f.encoder().patchExpr(patchAt, Expr::Id);
+            f.encoder().patchOneByteExpr(patchAt, Expr::Id);
             return true;
         }
 
         // Second argument is the scalar
         return CheckSimdScalarArgs(formalSimdType_)(f, arg, argIndex, actualType, patchAt);
     }
 };
 
@@ -4926,25 +4926,25 @@ class CheckSimdReplaceLaneArgs
         uint32_t u32;
         switch (argIndex) {
           case 0:
             // First argument is the vector
             if (!(actualType <= Type(formalSimdType_))) {
                 return f.failf(arg, "%s is not a subtype of %s", actualType.toChars(),
                                Type(formalSimdType_).toChars());
             }
-            f.encoder().patchExpr(patchAt, Expr::Id);
+            f.encoder().patchOneByteExpr(patchAt, Expr::Id);
             return true;
           case 1:
             // Second argument is the lane (< vector length).
             if (!IsLiteralOrConstInt(f, arg, &u32))
                 return f.failf(arg, "lane selector should be a constant integer literal");
             if (u32 >= GetSimdLanes(formalSimdType_))
                 return f.failf(arg, "lane selector should be in bounds");
-            f.encoder().patchExpr(patchAt, Expr::Id);
+            f.encoder().patchOneByteExpr(patchAt, Expr::Id);
             return true;
           case 2:
             // Third argument is the scalar
             return CheckSimdScalarArgs(formalSimdType_)(f, arg, argIndex, actualType, patchAt);
         }
         return false;
     }
 };
@@ -5076,17 +5076,17 @@ CheckSimdSwizzle(FunctionValidator& f, P
     if (!(vecType <= retType))
         return f.failf(vec, "%s is not a subtype of %s", vecType.toChars(), retType.toChars());
 
     int32_t lanes[4];
     if (!CheckSimdShuffleSelectors(f, NextNode(vec), lanes, 4))
         return false;
 
     for (unsigned i = 0; i < 4; i++) {
-        if (!f.encoder().writeU8(uint8_t(lanes[i])))
+        if (!f.encoder().writeFixedU8(uint8_t(lanes[i])))
             return false;
     }
 
     *type = retType;
     return true;
 }
 
 static bool
@@ -5109,17 +5109,17 @@ CheckSimdShuffle(FunctionValidator& f, P
             return f.failf(arg, "%s is not a subtype of %s", type.toChars(), retType.toChars());
     }
 
     int32_t lanes[4];
     if (!CheckSimdShuffleSelectors(f, arg, lanes, 8))
         return false;
 
     for (unsigned i = 0; i < 4; i++) {
-        if (!f.encoder().writeU8(uint8_t(lanes[i])))
+        if (!f.encoder().writeFixedU8(uint8_t(lanes[i])))
             return false;
     }
 
     *type = retType;
     return true;
 }
 
 static bool
@@ -5373,72 +5373,72 @@ CoerceResult(FunctionValidator& f, Parse
              Type* type)
 {
     MOZ_ASSERT(expected.isCanonical());
 
     // At this point, the bytecode resembles this:
     //      | patchAt | the thing we wanted to coerce | current position |>
     switch (expected.which()) {
       case Type::Void:
-        f.encoder().patchExpr(patchAt, Expr::Id);
+        f.encoder().patchOneByteExpr(patchAt, Expr::Id);
         break;
       case Type::Int:
         if (!actual.isIntish())
             return f.failf(expr, "%s is not a subtype of intish", actual.toChars());
-        f.encoder().patchExpr(patchAt, Expr::Id);
+        f.encoder().patchOneByteExpr(patchAt, Expr::Id);
         break;
       case Type::Float:
         if (!CheckFloatCoercionArg(f, expr, actual, patchAt))
             return false;
         break;
       case Type::Double:
         if (actual.isMaybeDouble())
-            f.encoder().patchExpr(patchAt, Expr::Id);
+            f.encoder().patchOneByteExpr(patchAt, Expr::Id);
         else if (actual.isMaybeFloat())
-            f.encoder().patchExpr(patchAt, Expr::F64PromoteF32);
+            f.encoder().patchOneByteExpr(patchAt, Expr::F64PromoteF32);
         else if (actual.isSigned())
-            f.encoder().patchExpr(patchAt, Expr::F64ConvertSI32);
+            f.encoder().patchOneByteExpr(patchAt, Expr::F64ConvertSI32);
         else if (actual.isUnsigned())
-            f.encoder().patchExpr(patchAt, Expr::F64ConvertUI32);
+            f.encoder().patchOneByteExpr(patchAt, Expr::F64ConvertUI32);
         else
             return f.failf(expr, "%s is not a subtype of double?, float?, signed or unsigned", actual.toChars());
         break;
       default:
         MOZ_ASSERT(expected.isSimd(), "Incomplete switch");
         if (actual != expected)
             return f.failf(expr, "got type %s, expected %s", actual.toChars(), expected.toChars());
-        f.encoder().patchExpr(patchAt, Expr::Id);
+        f.encoder().patchOneByteExpr(patchAt, Expr::Id);
         break;
     }
 
     *type = Type::ret(expected);
     return true;
 }
 
 static bool
 CheckCoercedMathBuiltinCall(FunctionValidator& f, ParseNode* callNode, AsmJSMathBuiltinFunction func,
                             Type ret, Type* type)
 {
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
     Type actual;
     if (!CheckMathBuiltinCall(f, callNode, func, &actual))
         return false;
     return CoerceResult(f, callNode, ret, actual, opcodeAt, type);
 }
 
 static bool
 CheckCoercedSimdCall(FunctionValidator& f, ParseNode* call, const ModuleValidator::Global* global,
                      Type ret, Type* type)
 {
     MOZ_ASSERT(ret.isCanonical());
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     Type actual;
     if (global->isSimdCtor()) {
         if (!CheckSimdCtorCall(f, call, global, &actual))
             return false;
         MOZ_ASSERT(actual.isSimd());
     } else {
@@ -5452,34 +5452,34 @@ CheckCoercedSimdCall(FunctionValidator& 
 
 static bool
 CheckCoercedAtomicsBuiltinCall(FunctionValidator& f, ParseNode* callNode,
                                AsmJSAtomicsBuiltinFunction func, Type ret, Type* type)
 {
     MOZ_ASSERT(ret.isCanonical());
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
     Type actual;
     if (!CheckAtomicsBuiltinCall(f, callNode, func, &actual))
         return false;
     return CoerceResult(f, callNode, ret, actual, opcodeAt, type);
 }
 
 static bool
 CheckCoercedCall(FunctionValidator& f, ParseNode* call, Type ret, Type* type)
 {
     MOZ_ASSERT(ret.isCanonical());
 
     JS_CHECK_RECURSION_DONT_REPORT(f.cx(), return f.m().failOverRecursed());
 
     if (IsNumericLiteral(f.m(), call)) {
         size_t coerceOp;
-        if (!f.encoder().writePatchableExpr(&coerceOp))
+        if (!f.encoder().writePatchableOneByteExpr(&coerceOp))
             return false;
         NumLit lit = ExtractNumericLiteral(f.m(), call);
         if (!f.writeConstExpr(lit))
             return false;
         return CoerceResult(f, call, ret, Type::lit(lit), coerceOp, type);
     }
 
     ParseNode* callee = CallCallee(call);
@@ -5523,17 +5523,17 @@ CheckPos(FunctionValidator& f, ParseNode
 {
     MOZ_ASSERT(pos->isKind(PNK_POS));
     ParseNode* operand = UnaryKid(pos);
 
     if (operand->isKind(PNK_CALL))
         return CheckCoercedCall(f, operand, Type::Double, type);
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     Type actual;
     if (!CheckExpr(f, operand, &actual))
         return false;
 
     return CoerceResult(f, operand, Type::Double, actual, opcodeAt, type);
 }
@@ -5560,69 +5560,69 @@ CheckNot(FunctionValidator& f, ParseNode
 
 static bool
 CheckNeg(FunctionValidator& f, ParseNode* expr, Type* type)
 {
     MOZ_ASSERT(expr->isKind(PNK_NEG));
     ParseNode* operand = UnaryKid(expr);
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     Type operandType;
     if (!CheckExpr(f, operand, &operandType))
         return false;
 
     if (operandType.isInt()) {
-        f.encoder().patchExpr(opcodeAt, Expr::I32Neg);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Neg);
         *type = Type::Intish;
         return true;
     }
 
     if (operandType.isMaybeDouble()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F64Neg);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F64Neg);
         *type = Type::Double;
         return true;
     }
 
     if (operandType.isMaybeFloat()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F32Neg);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F32Neg);
         *type = Type::Floatish;
         return true;
     }
 
     return f.failf(operand, "%s is not a subtype of int, float? or double?", operandType.toChars());
 }
 
 static bool
 CheckCoerceToInt(FunctionValidator& f, ParseNode* expr, Type* type)
 {
     MOZ_ASSERT(expr->isKind(PNK_BITNOT));
     ParseNode* operand = UnaryKid(expr);
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     Type operandType;
     if (!CheckExpr(f, operand, &operandType))
         return false;
 
     if (operandType.isMaybeDouble() || operandType.isMaybeFloat()) {
         Expr opcode = operandType.isMaybeDouble() ? Expr::I32TruncSF64 : Expr::I32TruncSF32;
-        f.encoder().patchExpr(opcodeAt, opcode);
+        f.encoder().patchOneByteExpr(opcodeAt, opcode);
         *type = Type::Signed;
         return true;
     }
 
     if (!operandType.isIntish())
         return f.failf(operand, "%s is not a subtype of double?, float? or intish", operandType.toChars());
 
-    f.encoder().patchExpr(opcodeAt, Expr::Id);
+    f.encoder().patchOneByteExpr(opcodeAt, Expr::Id);
     *type = Type::Signed;
     return true;
 }
 
 static bool
 CheckBitNot(FunctionValidator& f, ParseNode* neg, Type* type)
 {
     MOZ_ASSERT(neg->isKind(PNK_BITNOT));
@@ -5744,43 +5744,43 @@ IsValidIntMultiplyConstant(ModuleValidat
 static bool
 CheckMultiply(FunctionValidator& f, ParseNode* star, Type* type)
 {
     MOZ_ASSERT(star->isKind(PNK_STAR));
     ParseNode* lhs = MultiplyLeft(star);
     ParseNode* rhs = MultiplyRight(star);
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     Type lhsType;
     if (!CheckExpr(f, lhs, &lhsType))
         return false;
 
     Type rhsType;
     if (!CheckExpr(f, rhs, &rhsType))
         return false;
 
     if (lhsType.isInt() && rhsType.isInt()) {
         if (!IsValidIntMultiplyConstant(f.m(), lhs) && !IsValidIntMultiplyConstant(f.m(), rhs))
             return f.fail(star, "one arg to int multiply must be a small (-2^20, 2^20) int literal");
-        f.encoder().patchExpr(opcodeAt, Expr::I32Mul);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::I32Mul);
         *type = Type::Intish;
         return true;
     }
 
     if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F64Mul);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F64Mul);
         *type = Type::Double;
         return true;
     }
 
     if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
-        f.encoder().patchExpr(opcodeAt, Expr::F32Mul);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::F32Mul);
         *type = Type::Floatish;
         return true;
     }
 
     return f.fail(star, "multiply operands must be both int, both double? or both float?");
 }
 
 static bool
@@ -5791,17 +5791,17 @@ CheckAddOrSub(FunctionValidator& f, Pars
     MOZ_ASSERT(expr->isKind(PNK_ADD) || expr->isKind(PNK_SUB));
     ParseNode* lhs = AddSubLeft(expr);
     ParseNode* rhs = AddSubRight(expr);
 
     Type lhsType, rhsType;
     unsigned lhsNumAddOrSub, rhsNumAddOrSub;
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     if (lhs->isKind(PNK_ADD) || lhs->isKind(PNK_SUB)) {
         if (!CheckAddOrSub(f, lhs, &lhsType, &lhsNumAddOrSub))
             return false;
         if (lhsType == Type::Intish)
             lhsType = Type::Int;
     } else {
@@ -5821,23 +5821,23 @@ CheckAddOrSub(FunctionValidator& f, Pars
         rhsNumAddOrSub = 0;
     }
 
     unsigned numAddOrSub = lhsNumAddOrSub + rhsNumAddOrSub + 1;
     if (numAddOrSub > (1<<20))
         return f.fail(expr, "too many + or - without intervening coercion");
 
     if (lhsType.isInt() && rhsType.isInt()) {
-        f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_ADD) ? Expr::I32Add : Expr::I32Sub);
+        f.encoder().patchOneByteExpr(opcodeAt, expr->isKind(PNK_ADD) ? Expr::I32Add : Expr::I32Sub);
         *type = Type::Intish;
     } else if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
-        f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_ADD) ? Expr::F64Add : Expr::F64Sub);
+        f.encoder().patchOneByteExpr(opcodeAt, expr->isKind(PNK_ADD) ? Expr::F64Add : Expr::F64Sub);
         *type = Type::Double;
     } else if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
-        f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_ADD) ? Expr::F32Add : Expr::F32Sub);
+        f.encoder().patchOneByteExpr(opcodeAt, expr->isKind(PNK_ADD) ? Expr::F32Add : Expr::F32Sub);
         *type = Type::Floatish;
     } else {
         return f.failf(expr, "operands to + or - must both be int, float? or double?, got %s and %s",
                        lhsType.toChars(), rhsType.toChars());
     }
 
     if (numAddOrSubOut)
         *numAddOrSubOut = numAddOrSub;
@@ -5845,67 +5845,67 @@ CheckAddOrSub(FunctionValidator& f, Pars
 }
 
 static bool
 CheckDivOrMod(FunctionValidator& f, ParseNode* expr, Type* type)
 {
     MOZ_ASSERT(expr->isKind(PNK_DIV) || expr->isKind(PNK_MOD));
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     ParseNode* lhs = DivOrModLeft(expr);
     ParseNode* rhs = DivOrModRight(expr);
 
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsType))
         return false;
     if (!CheckExpr(f, rhs, &rhsType))
         return false;
 
     if (lhsType.isMaybeDouble() && rhsType.isMaybeDouble()) {
-        f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_DIV) ? Expr::F64Div : Expr::F64Mod);
+        f.encoder().patchOneByteExpr(opcodeAt, expr->isKind(PNK_DIV) ? Expr::F64Div : Expr::F64Mod);
         *type = Type::Double;
         return true;
     }
 
     if (lhsType.isMaybeFloat() && rhsType.isMaybeFloat()) {
         if (expr->isKind(PNK_DIV))
-            f.encoder().patchExpr(opcodeAt, Expr::F32Div);
+            f.encoder().patchOneByteExpr(opcodeAt, Expr::F32Div);
         else
             return f.fail(expr, "modulo cannot receive float arguments");
         *type = Type::Floatish;
         return true;
     }
 
     if (lhsType.isSigned() && rhsType.isSigned()) {
-        f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_DIV) ? Expr::I32DivS : Expr::I32RemS);
+        f.encoder().patchOneByteExpr(opcodeAt, expr->isKind(PNK_DIV) ? Expr::I32DivS : Expr::I32RemS);
         *type = Type::Intish;
         return true;
     }
 
     if (lhsType.isUnsigned() && rhsType.isUnsigned()) {
-        f.encoder().patchExpr(opcodeAt, expr->isKind(PNK_DIV) ? Expr::I32DivU : Expr::I32RemU);
+        f.encoder().patchOneByteExpr(opcodeAt, expr->isKind(PNK_DIV) ? Expr::I32DivU : Expr::I32RemU);
         *type = Type::Intish;
         return true;
     }
 
     return f.failf(expr, "arguments to / or %% must both be double?, float?, signed, or unsigned; "
                    "%s and %s are given", lhsType.toChars(), rhsType.toChars());
 }
 
 static bool
 CheckComparison(FunctionValidator& f, ParseNode* comp, Type* type)
 {
     MOZ_ASSERT(comp->isKind(PNK_LT) || comp->isKind(PNK_LE) || comp->isKind(PNK_GT) ||
                comp->isKind(PNK_GE) || comp->isKind(PNK_EQ) || comp->isKind(PNK_NE));
 
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     ParseNode* lhs = ComparisonLeft(comp);
     ParseNode* rhs = ComparisonRight(comp);
 
     Type lhsType, rhsType;
     if (!CheckExpr(f, lhs, &lhsType))
         return false;
@@ -5961,17 +5961,17 @@ CheckComparison(FunctionValidator& f, Pa
           case JSOP_GT: stmt = Expr::F32Gt; break;
           case JSOP_GE: stmt = Expr::F32Ge; break;
           default: MOZ_CRASH("unexpected comparison op");
         }
     } else {
         MOZ_CRASH("unexpected type");
     }
 
-    f.encoder().patchExpr(opcodeAt, stmt);
+    f.encoder().patchOneByteExpr(opcodeAt, stmt);
     *type = Type::Int;
     return true;
 }
 
 static bool
 CheckBitwise(FunctionValidator& f, ParseNode* bitwise, Type* type)
 {
     ParseNode* lhs = BitwiseLeft(bitwise);
@@ -6317,17 +6317,17 @@ CheckLabel(FunctionValidator& f, ParseNo
     return true;
 }
 
 static bool
 CheckIf(FunctionValidator& f, ParseNode* ifStmt)
 {
   recurse:
     size_t opcodeAt;
-    if (!f.encoder().writePatchableExpr(&opcodeAt))
+    if (!f.encoder().writePatchableOneByteExpr(&opcodeAt))
         return false;
 
     MOZ_ASSERT(ifStmt->isKind(PNK_IF));
     ParseNode* cond = TernaryKid1(ifStmt);
     ParseNode* thenStmt = TernaryKid2(ifStmt);
     ParseNode* elseStmt = TernaryKid3(ifStmt);
 
     Type condType;
@@ -6335,19 +6335,19 @@ CheckIf(FunctionValidator& f, ParseNode*
         return false;
     if (!condType.isInt())
         return f.failf(cond, "%s is not a subtype of int", condType.toChars());
 
     if (!CheckStatement(f, thenStmt))
         return false;
 
     if (!elseStmt) {
-        f.encoder().patchExpr(opcodeAt, Expr::If);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::If);
     } else {
-        f.encoder().patchExpr(opcodeAt, Expr::IfElse);
+        f.encoder().patchOneByteExpr(opcodeAt, Expr::IfElse);
 
         if (elseStmt->isKind(PNK_IF)) {
             ifStmt = elseStmt;
             goto recurse;
         }
 
         if (!CheckStatement(f, elseStmt))
             return false;
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -157,17 +157,17 @@ CheckValType(JSContext* cx, Decoder& d, 
 #endif
         return true;
       default:
         // Note: it's important not to remove this default since readValType()
         // can return ValType values for which there is no enumerator.
         break;
     }
 
-    return Fail(cx, d, "bad value type");
+    return Fail(cx, d, "bad type");
 }
 
 static bool
 CheckExprType(JSContext* cx, Decoder& d, ExprType type)
 {
     return type == ExprType::Void ||
            CheckValType(cx, d, NonVoidToValType(type));
 }
@@ -595,16 +595,18 @@ DecodeReturn(FunctionDecoder& f, ExprTyp
 
     *type = AnyType;
     return true;
 }
 
 static bool
 DecodeExpr(FunctionDecoder& f, ExprType* type)
 {
+    JS_CHECK_RECURSION(f.cx(), return false);
+
     Expr expr;
     if (!f.d().readExpr(&expr))
         return f.fail("unable to read expression");
 
     switch (expr) {
       case Expr::Nop:
         return DecodeNop(f, type);
       case Expr::Call:
--- a/js/src/asmjs/WasmBinary.h
+++ b/js/src/asmjs/WasmBinary.h
@@ -362,23 +362,16 @@ class Encoder
             if (!done)
                 byte |= 0x80;
             if (!bytes_.append(byte))
                 return false;
         } while (!done);
         return true;
     }
 
-    template <class T>
-    MOZ_WARN_UNUSED_RESULT bool writeEnum(T v) {
-        static_assert(uint32_t(T::Limit) <= UINT32_MAX, "fits");
-        MOZ_ASSERT(uint32_t(v) < uint32_t(T::Limit));
-        return writeVarU32(uint32_t(v));
-    }
-
     void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
         do {
             uint8_t assertByte = assertBits & 0x7f;
             uint8_t patchByte = patchBits & 0x7f;
             assertBits >>= 7;
             patchBits >>= 7;
             if (assertBits != 0) {
                 assertByte |= 0x80;
@@ -392,41 +385,34 @@ class Encoder
 
     uint32_t varU32ByteLength(size_t offset) const {
         size_t start = offset;
         while (bytes_[offset] & 0x80)
             offset++;
         return offset - start + 1;
     }
 
-    template <class T>
-    MOZ_WARN_UNUSED_RESULT bool writePatchableEnum(size_t* offset) {
-        *offset = bytes_.length();
-        return writeVarU32(uint32_t(T::Limit));
-    }
-
-    template <class T>
-    void patchEnum(size_t offset, T v) {
-        MOZ_ASSERT(uint32_t(v) < uint32_t(T::Limit));
-        return patchVarU32(offset, uint32_t(v), uint32_t(T::Limit));
-    }
+    static const size_t ExprLimit = 2 * UINT8_MAX - 1;
 
   public:
     explicit Encoder(Bytes& bytes)
       : bytes_(bytes)
     {
         MOZ_ASSERT(empty());
     }
 
     size_t currentOffset() const { return bytes_.length(); }
     bool empty() const { return currentOffset() == 0; }
 
     // Fixed-size encoding operations simply copy the literal bytes (without
     // attempting to align).
 
+    MOZ_WARN_UNUSED_RESULT bool writeFixedU8(uint8_t i) {
+        return write<uint8_t>(i);
+    }
     MOZ_WARN_UNUSED_RESULT bool writeFixedU32(uint32_t i) {
         return write<uint32_t>(i);
     }
     MOZ_WARN_UNUSED_RESULT bool writeFixedF32(float f) {
         return write<float>(f);
     }
     MOZ_WARN_UNUSED_RESULT bool writeFixedF64(double d) {
         return write<double>(d);
@@ -447,50 +433,59 @@ class Encoder
         return writeVarS<int32_t>(i);
     }
     MOZ_WARN_UNUSED_RESULT bool writeVarU64(uint64_t i) {
         return writeVarU<uint64_t>(i);
     }
     MOZ_WARN_UNUSED_RESULT bool writeVarS64(int64_t i) {
         return writeVarS<int64_t>(i);
     }
-    MOZ_WARN_UNUSED_RESULT bool writeExpr(Expr expr) {
-        return writeEnum(expr);
-    }
     MOZ_WARN_UNUSED_RESULT bool writeValType(ValType type) {
-        return writeEnum(type);
+        static_assert(size_t(ValType::Limit) <= INT8_MAX, "fits");
+        return writeFixedU8(size_t(type));
     }
     MOZ_WARN_UNUSED_RESULT bool writeExprType(ExprType type) {
-        return writeEnum(type);
+        static_assert(size_t(ExprType::Limit) <= INT8_MAX, "fits");
+        return writeFixedU8(uint8_t(type));
+    }
+    MOZ_WARN_UNUSED_RESULT bool writeExpr(Expr expr) {
+        static_assert(size_t(Expr::Limit) <= ExprLimit, "fits");
+        if (size_t(expr) < UINT8_MAX)
+            return writeFixedU8(uint8_t(expr));
+        return writeFixedU8(UINT8_MAX) &&
+               writeFixedU8(size_t(expr) - UINT8_MAX);
     }
 
     // Variable-length encodings that allow back-patching.
 
+    MOZ_WARN_UNUSED_RESULT bool writePatchableFixedU8(size_t* offset) {
+        *offset = bytes_.length();
+        return bytes_.append(0xff);
+    }
+    void patchFixedU8(size_t offset, uint8_t i) {
+        MOZ_ASSERT(bytes_[offset] == 0xff);
+        bytes_[offset] = i;
+    }
+
     MOZ_WARN_UNUSED_RESULT bool writePatchableVarU32(size_t* offset) {
         *offset = bytes_.length();
         return writeVarU32(UINT32_MAX);
     }
     void patchVarU32(size_t offset, uint32_t patchBits) {
         return patchVarU32(offset, patchBits, UINT32_MAX);
     }
 
-    MOZ_WARN_UNUSED_RESULT bool writePatchableVarU8(size_t* offset) {
+    MOZ_WARN_UNUSED_RESULT bool writePatchableOneByteExpr(size_t* offset) {
         *offset = bytes_.length();
-        return writeU8(UINT8_MAX);
+        return writeFixedU8(0xff);
     }
-    void patchVarU8(size_t offset, uint8_t patchBits) {
-        MOZ_ASSERT(patchBits < 0x80);
-        return patchU8(offset, patchBits);
-    }
-
-    MOZ_WARN_UNUSED_RESULT bool writePatchableExpr(size_t* offset) {
-        return writePatchableEnum<Expr>(offset);
-    }
-    void patchExpr(size_t offset, Expr expr) {
-        patchEnum(