merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 10 Mar 2016 11:51:35 +0100
changeset 325894 dd1abe874252e507b825a0a4e1063b0e13578288
parent 325719 aaacd6e2d9f16ec8fc1b53aab81f758e67dfe458 (current diff)
parent 325893 b0e1cefc94ce86adc374eb2d05791b00e0753b20 (diff)
child 325895 767e0126510e0f65798a53a29c6b7e1469a03139
child 325905 0d38e391b3a15ff78e4922071a067640990a44fc
child 325967 7a0f7bd1873cc08767946c8032bcb476d603a8ae
push id6048
push userkmoir@mozilla.com
push dateMon, 06 Jun 2016 19:02:08 +0000
treeherdermozilla-beta@46d72a56c57d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone48.0a1
first release with
nightly linux32
dd1abe874252 / 48.0a1 / 20160310030242 / files
nightly linux64
dd1abe874252 / 48.0a1 / 20160310030242 / files
nightly mac
dd1abe874252 / 48.0a1 / 20160310030242 / files
nightly win32
dd1abe874252 / 48.0a1 / 20160310030242 / files
nightly win64
dd1abe874252 / 48.0a1 / 20160310030242 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
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(offset, expr);
+    void patchOneByteExpr(size_t offset, Expr expr) {
+        MOZ_ASSERT(size_t(expr) < UINT8_MAX);
+        MOZ_ASSERT(bytes_[offset] == 0xff);
+        bytes_[offset] = uint8_t(expr);
     }
 
     // Byte ranges start with an LEB128 length followed by an arbitrary sequence
     // of bytes. When used for strings, bytes are to be interpreted as utf8.
 
     MOZ_WARN_UNUSED_RESULT bool writeBytes(const void* bytes, uint32_t numBytes) {
         return writeVarU32(numBytes) &&
                bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
@@ -508,31 +503,16 @@ class Encoder
         MOZ_ASSERT(id[IdSize] == '\0');
         return writePatchableVarU32(offset) &&
                writeVarU32(IdSize) &&
                bytes_.append(reinterpret_cast<const uint8_t*>(id), IdSize);
     }
     void finishSection(size_t offset) {