merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 12 Feb 2016 15:15:17 +0100
changeset 284108 218d16a9ddcc3699be2a19bcab7d5f6b7f8e7515
parent 283949 d992fbeb2b2696cb3854cb3a1f0f89ae09b483e2 (current diff)
parent 284107 375dae281f7b1868a7866ad7e07a8281c49a8d60 (diff)
child 284109 1b8a0036c771be89a2cc470ddb92616b10f96e40
child 284129 d719ac4bcbec13e0ba13a41547788e3bf365c679
child 284161 bbe59290c8257dafec58e13b8f656309ca882054
push id17581
push usercbook@mozilla.com
push dateFri, 12 Feb 2016 14:21:29 +0000
treeherderfx-team@1b8a0036c771 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone47.0a1
merge mozilla-inbound to mozilla-central a=merge
browser/base/content/tabbrowser.xml
config/makefiles/rcs.mk
configure.in
dom/base/nsDOMSettableTokenList.cpp
dom/base/nsDOMSettableTokenList.h
dom/media/test/crashtests/video-crash.webm
dom/media/test/crashtests/video-replay-after-audio-end.html
dom/webidl/DOMSettableTokenList.webidl
gfx/2d/DrawTargetD2D.cpp
gfx/2d/DrawTargetD2D.h
gfx/2d/SourceSurfaceD2D.cpp
gfx/2d/SourceSurfaceD2D.h
gfx/2d/SourceSurfaceD2DTarget.cpp
gfx/2d/SourceSurfaceD2DTarget.h
mobile/android/base/Makefile.in
mobile/android/moz.build
testing/marionette/EventUtils.js
testing/marionette/actions.js
testing/marionette/atoms/atoms.js
testing/marionette/elements.js
testing/marionette/frame-manager.js
testing/marionette/interactions.js
testing/mochitest/b2g_start_script.js
toolkit/mozapps/installer/upload-files.mk
toolkit/xre/make-platformini.py
webapprt/Makefile.in
xpcom/glue/nsProxyRelease.cpp
xulrunner/README.xulrunner
xulrunner/app.mozbuild
xulrunner/app/Makefile.in
xulrunner/app/default16.png
xulrunner/app/default32.png
xulrunner/app/default48.png
xulrunner/app/document.ico
xulrunner/app/install_app.py
xulrunner/app/macbuild/Info.plist.in
xulrunner/app/macbuild/InfoPlist.strings
xulrunner/app/moz.build
xulrunner/app/nsXULRunnerApp.cpp
xulrunner/app/splash.rc
xulrunner/app/xulrunner.exe.manifest
xulrunner/app/xulrunner.ico
xulrunner/app/xulrunner.js
xulrunner/build.mk
xulrunner/config/mozconfig
xulrunner/config/mozconfigs/common
xulrunner/config/mozconfigs/common.override
xulrunner/config/mozconfigs/linux32/xulrunner
xulrunner/config/mozconfigs/linux32/xulrunner-qt
xulrunner/config/mozconfigs/linux64/xulrunner
xulrunner/config/mozconfigs/macosx-universal/xulrunner
xulrunner/config/mozconfigs/win32/xulrunner
xulrunner/config/mozconfigs/win64/xulrunner
xulrunner/confvars.sh
xulrunner/examples/moz.build
xulrunner/examples/simple/application.ini
xulrunner/examples/simple/components/moz.build
xulrunner/examples/simple/components/public/moz.build
xulrunner/examples/simple/components/public/nsISimpleTest.idl
xulrunner/examples/simple/components/src/SimpleTest.cpp
xulrunner/examples/simple/components/src/SimpleTest.js
xulrunner/examples/simple/components/src/SimpleTest.manifest
xulrunner/examples/simple/components/src/moz.build
xulrunner/examples/simple/content/contents.rdf
xulrunner/examples/simple/content/simple.js
xulrunner/examples/simple/content/simple.xul
xulrunner/examples/simple/icons/simple.ico
xulrunner/examples/simple/jar.mn
xulrunner/examples/simple/locale/simple.dtd
xulrunner/examples/simple/moz.build
xulrunner/examples/simple/simple-prefs.js
xulrunner/installer/Makefile.in
xulrunner/installer/debian/changelog.in
xulrunner/installer/debian/compat
xulrunner/installer/debian/control
xulrunner/installer/debian/icon_base64
xulrunner/installer/debian/menu
xulrunner/installer/debian/postinst.in
xulrunner/installer/debian/prerm.in
xulrunner/installer/debian/xulrunner.links.in
xulrunner/installer/debian/xulrunner.service.in
xulrunner/installer/libxul-embedding.pc.in
xulrunner/installer/libxul.pc.in
xulrunner/installer/moz.build
xulrunner/installer/mozilla-js.pc.in
xulrunner/installer/mozilla-nspr.pc.in
xulrunner/installer/mozilla-nss.pc.in
xulrunner/installer/mozilla-plugin.pc.in
xulrunner/locales/all-locales
xulrunner/moz.build
xulrunner/stub/Makefile.in
xulrunner/stub/moz.build
xulrunner/stub/nsXULStub.cpp
xulrunner/stub/xulrunner-stub.exe.manifest
xulrunner/stub/xulrunner-stub.rc
xulrunner/tools/redit/Makefile.in
xulrunner/tools/redit/moz.build
xulrunner/tools/redit/redit.cpp
--- a/Makefile.in
+++ b/Makefile.in
@@ -10,29 +10,35 @@ make_min_ver := 3.81
 ifneq ($(make_min_ver),$(firstword $(sort $(make_min_ver) $(MAKE_VERSION))))
 $(error GNU Make $(make_min_ver) or higher is required)
 endif
 
 export TOPLEVEL_BUILD := 1
 
 default::
 
+ifndef TEST_MOZBUILD
 ifdef MOZ_BUILD_APP
 include $(topsrcdir)/$(MOZ_BUILD_APP)/build.mk
 endif
+endif
 
 include $(topsrcdir)/config/config.mk
 
 GARBAGE_DIRS += _javagen _profile staticlib
 DIST_GARBAGE = config.cache config.log config.status* config-defs.h \
    config/autoconf.mk \
    mozilla-config.h \
    netwerk/necko-config.h xpcom/xpcom-config.h xpcom/xpcom-private.h \
    .mozconfig.mk
 
+ifndef MOZ_PROFILE_USE
+buildid.h source-repo.h: FORCE
+endif
+
 ifdef JS_STANDALONE
 configure_dir = $(topsrcdir)/js/src
 else
 configure_dir = $(topsrcdir)
 endif
 
 BUILD_BACKEND_FILES := $(addprefix backend.,$(addsuffix Backend,$(BUILD_BACKENDS)))
 
@@ -304,22 +310,16 @@ endif # MOZ_CRASHREPORTER
 
 uploadsymbols:
 ifdef MOZ_CRASHREPORTER
 ifdef SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE
 	$(PYTHON) -u $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.py '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
 else
 	$(SHELL) $(topsrcdir)/toolkit/crashreporter/tools/upload_symbols.sh $(SYMBOL_INDEX_NAME) '$(DIST)/$(PKG_PATH)$(SYMBOL_FULL_ARCHIVE_BASENAME).zip'
 endif
-
-# MOZ_SOURCE_STAMP is defined in package-name.mk with a deferred assignment.
-# exporting it makes make run its $(shell) command for each invoked submake,
-# so transform it to an immediate assignment.
-MOZ_SOURCE_STAMP := $(MOZ_SOURCE_STAMP)
-export MOZ_SOURCE_STAMP
 endif
 
 .PHONY: update-packaging
 update-packaging:
 	$(MAKE) -C tools/update-packaging
 
 .PHONY: pretty-package
 pretty-package:
--- a/accessible/base/TreeWalker.cpp
+++ b/accessible/base/TreeWalker.cpp
@@ -75,21 +75,19 @@ TreeWalker::NextChild()
   nsINode* contextNode = mContext->GetNode();
   while (mAnchorNode != contextNode) {
     nsINode* parentNode = mAnchorNode->GetFlattenedTreeParent();
     if (!parentNode || !parentNode->IsElement())
       return nullptr;
 
     nsIContent* parent = parentNode->AsElement();
     top = PushState(parent);
-    while (nsIContent* childNode = Next(top)) {
-      if (childNode == mAnchorNode) {
-        mAnchorNode = parent;
-        return NextChild();
-      }
+    if (top->mDOMIter.Seek(mAnchorNode)) {
+      mAnchorNode = parent;
+      return NextChild();
     }
 
     // XXX We really should never get here, it means we're trying to find an
     // accessible for a dom node where iterating over its parent's children
     // doesn't return it. However this sometimes happens when we're asked for
     // the nearest accessible to place holder content which we ignore.
     mAnchorNode = parent;
   }
--- a/b2g/app/Makefile.in
+++ b/b2g/app/Makefile.in
@@ -1,15 +1,12 @@
 # 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_RCS_MK := 1
-include $(topsrcdir)/config/makefiles/rcs.mk
-
 # Make sure the standalone glue doesn't try to get libxpcom.so from b2g/app.
 NSDISTMODE = copy
 
 include $(topsrcdir)/config/rules.mk
 
 APP_ICON = app
 
 APP_BINARY = $(MOZ_APP_NAME)$(BIN_SUFFIX)
@@ -56,15 +53,14 @@ tools repackage:: $(libs-preqs)
 else # MOZ_WIDGET_TOOLKIT != cocoa
 
 libs::
 	$(NSINSTALL) -D $(DIST)/bin/chrome/icons/default
 
 # Copy the app icon for b2g-desktop
 ifeq ($(OS_ARCH),WINNT)
 	cp $(DIST)/branding/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/$(APP_ICON).ico
-	$(DIST)/bin/redit$(HOST_BIN_SUFFIX) $(DIST)/bin/$(APP_BINARY) $(DIST)/branding/$(APP_ICON).ico
 	cp $(DIST)/branding/$(APP_ICON).ico $(DIST)/bin/chrome/icons/default/default.ico
 else ifneq (gonk,$(MOZ_WIDGET_TOOLKIT))
 	cp $(DIST)/branding/default.png $(DIST)/bin/chrome/icons/default/default.png
 endif
 
 endif
--- a/b2g/app/b2g.js
+++ b/b2g/app/b2g.js
@@ -1010,17 +1010,16 @@ pref("dom.downloads.max_retention_days",
 //
 // To prevent SD card DoS attacks via downloads we disable background handling.
 //
 pref("security.exthelperapp.disable_background_handling", true);
 
 // Inactivity time in milliseconds after which we shut down the OS.File worker.
 pref("osfile.reset_worker_delay", 5000);
 
-pref("apz.displayport_expiry_ms", 0);
 // APZ physics settings, tuned by UX designers
 pref("apz.axis_lock.mode", 2); // Use "sticky" axis locking
 pref("apz.fling_curve_function_x1", "0.41");
 pref("apz.fling_curve_function_y1", "0.0");
 pref("apz.fling_curve_function_x2", "0.80");
 pref("apz.fling_curve_function_y2", "1.0");
 pref("apz.fling_curve_threshold_inches_per_ms", "0.01");
 pref("apz.fling_friction", "0.0019");
--- a/b2g/moz.build
+++ b/b2g/moz.build
@@ -3,15 +3,12 @@
 # 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/.
 
 CONFIGURE_SUBST_FILES += ['installer/Makefile']
 
 DIRS += ['chrome', 'components', 'locales']
 
-if CONFIG['OS_ARCH'] == 'WINNT':
-    DIRS += ['../xulrunner/tools/redit']
-
 if CONFIG['GAIADIR']:
     DIRS += ['gaia']
 
 DIRS += ['app']
--- a/browser/app/Makefile.in
+++ b/browser/app/Makefile.in
@@ -74,17 +74,17 @@ LPROJ_ROOT := $(subst -,_,$(AB_CD))
 else
 LPROJ_ROOT := $(firstword $(subst -, ,$(AB_CD)))
 endif
 LPROJ := Contents/Resources/$(LPROJ_ROOT).lproj
 
 clean clobber repackage::
 	$(RM) -r $(dist_dest)
 
-MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/config/buildid)
+MAC_BUNDLE_VERSION = $(shell $(PYTHON) $(srcdir)/macversion.py --version=$(MOZ_APP_VERSION) --buildid=$(DEPTH)/buildid.h)
 
 .PHONY: repackage
 tools repackage:: $(DIST)/bin/$(MOZ_APP_NAME)
 	$(MKDIR) -p $(dist_dest)/Contents/MacOS
 	$(MKDIR) -p $(dist_dest)/$(LPROJ)
 	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents $(dist_dest) --exclude English.lproj
 	rsync -a --exclude '*.in' $(srcdir)/macbuild/Contents/Resources/English.lproj/ $(dist_dest)/$(LPROJ)
 	sed -e 's/%APP_VERSION%/$(MOZ_APP_VERSION)/' -e 's/%MAC_APP_NAME%/$(MAC_APP_NAME)/' -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' -e 's/%MAC_BUNDLE_VERSION%/$(MAC_BUNDLE_VERSION)/' $(srcdir)/macbuild/Contents/Info.plist.in > $(dist_dest)/Contents/Info.plist
--- a/browser/app/macversion.py
+++ b/browser/app/macversion.py
@@ -23,17 +23,17 @@ if not options.version:
     sys.exit(1)
 
 # We want to build a version number that matches the format allowed for
 # CFBundleVersion (nnnnn[.nn[.nn]]). We'll incorporate both the version
 # number as well as the date, so that it changes at least daily (for nightly
 # builds), but also so that newly-built older versions (e.g. beta build) aren't
 # considered "newer" than previously-built newer versions (e.g. a trunk nightly)
 
-buildid = open(options.buildid, 'r').read()
+define, MOZ_BUILDID, buildid = open(options.buildid, 'r').read().split()
 
 # extract only the major version (i.e. "14" from "14.0b1")
 majorVersion = re.match(r'^(\d+)[^\d].*', options.version).group(1)
 # last two digits of the year
 twodigityear = buildid[2:4]
 month = buildid[4:6]
 if month[0] == '0':
   month = month[1]
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1101,16 +1101,18 @@
             if (listener && listener.mStateFlags) {
               this._callProgressListeners(null, "onUpdateCurrentBrowser",
                                           [listener.mStateFlags, listener.mStatus,
                                            listener.mMessage, listener.mTotalProgress],
                                           true, false);
             }
 
             if (!this._previewMode) {
+              this._recordTabAccess(this.mCurrentTab);
+
               this.mCurrentTab.lastAccessed = Infinity;
               this.mCurrentTab.removeAttribute("unread");
               oldTab.lastAccessed = Date.now();
 
               let oldFindBar = oldTab._findBar;
               if (oldFindBar &&
                   oldFindBar.findMode == oldFindBar.FIND_NORMAL &&
                   !oldFindBar.hidden)
@@ -1293,16 +1295,55 @@
                newFocusedElement.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple"))
             focusFlags |= fm.FLAG_SHOWRING;
         }
 
         fm.setFocus(newBrowser, focusFlags);
         ]]></body>
       </method>
 
+      <!--
+        This function assumes we have an LRU cache of tabs (either
+        images of tab content or their layers). The goal is to find
+        out how far into the cache we need to look in order to find
+        aTab. We record this number in telemetry and also move aTab to
+        the front of the cache.
+
+        A newly created tab has position Infinity in the cache.
+        If a tab is closed, it has no effect on the position of other
+        tabs in the cache since we assume that closing a tab doesn't
+        cause us to load in any other tabs.
+
+        We ignore the effect of dragging tabs between windows.
+      -->
+      <method name="_recordTabAccess">
+	<parameter name="aTab"/>
+        <body><![CDATA[
+          if (!Services.telemetry.canRecordExtended) {
+            return;
+          }
+
+          let tabs = Array.from(this.visibleTabs);
+
+          let pos = aTab.cachePosition;
+          for (let i = 0; i < tabs.length; i++) {
+            // If aTab is moving to the front, everything that was
+            // previously in front of it is bumped up one position.
+            if (tabs[i].cachePosition < pos) {
+              tabs[i].cachePosition++;
+            }
+          }
+          aTab.cachePosition = 0;
+
+          if (isFinite(pos)) {
+            Services.telemetry.getHistogramById("TAB_SWITCH_CACHE_POSITION").add(pos);
+          }
+        ]]></body>
+      </method>
+
       <method name="_tabAttrModified">
         <parameter name="aTab"/>
         <parameter name="aChanged"/>
         <body><![CDATA[
           if (aTab.closing)
             return;
 
           let event = new CustomEvent("TabAttrModified", {
@@ -4262,16 +4303,17 @@
           window.addEventListener("sizemodechange", this, false);
 
           var uniqueId = this._generateUniquePanelID();
           this.mPanelContainer.childNodes[0].id = uniqueId;
           this.mCurrentTab.linkedPanel = uniqueId;
           this.mCurrentTab._tPos = 0;
           this.mCurrentTab._fullyOpen = true;
           this.mCurrentTab.lastAccessed = Infinity;
+          this.mCurrentTab.cachePosition = 0;
           this.mCurrentTab.linkedBrowser = this.mCurrentBrowser;
           this._tabForBrowser.set(this.mCurrentBrowser, this.mCurrentTab);
 
           // set up the shared autoscroll popup
           this._autoScrollPopup = this.mCurrentBrowser._createAutoScrollPopup();
           this._autoScrollPopup.id = "autoscroller";
           this.appendChild(this._autoScrollPopup);
           this.mCurrentBrowser.setAttribute("autoscrollpopup", this._autoScrollPopup.id);
@@ -6158,16 +6200,18 @@
         <getter>
           return this._lastAccessed == Infinity ? Date.now() : this._lastAccessed;
         </getter>
         <setter>
           this._lastAccessed = val;
         </setter>
       </property>
 
+      <field name="cachePosition">Infinity</field>
+
       <field name="mOverCloseButton">false</field>
       <property name="_overPlayingIcon" readonly="true">
         <getter><![CDATA[
           let iconVisible = this.hasAttribute("soundplaying") ||
                             this.hasAttribute("muted");
           let soundPlayingIcon =
             document.getAnonymousElementByAttribute(this, "anonid", "soundplaying-icon");
           let overlayIcon =
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -4,35 +4,17 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 USE_RCS_MK := 1
 include $(topsrcdir)/config/makefiles/makeutils.mk
 
 ifdef MOZ_APP_BASENAME
 APP_INI_DEPS = $(topsrcdir)/config/milestone.txt
 
-MOZ_BUILDID := $(shell cat $(DEPTH)/config/buildid)
-APP_INI_DEPS += $(DEPTH)/config/buildid
-
-DEFINES += -DMOZ_BUILDID=$(MOZ_BUILDID)
-
 APP_INI_DEPS += $(DEPTH)/config/autoconf.mk
-
-MOZ_SOURCE_STAMP := $(firstword $(shell cd $(topsrcdir)/$(MOZ_BUILD_APP)/.. && hg parent --template='{node}\n' 2>/dev/null))
-ifdef MOZ_SOURCE_STAMP
-DEFINES += -DMOZ_SOURCE_STAMP='$(MOZ_SOURCE_STAMP)'
-endif
-
-ifdef MOZ_INCLUDE_SOURCE_INFO
-source_repo ?= $(call getSourceRepo,$(topsrcdir)/$(MOZ_BUILD_APP)/..)
-ifneq (,$(source_repo))
-  DEFINES += -DMOZ_SOURCE_REPO='$(source_repo)'
-endif
-endif
-
 endif
 
 # NOTE: Keep .gdbinit in the topsrcdir for people who run gdb from the topsrcdir.
 # needs to be absolute to be distinct from $(topsrcdir)/.gdbinit
 GDBINIT_OBJDIR_FILES = $(topsrcdir)/.gdbinit
 GDBINIT_OBJDIR_DEST = $(topobjdir)
 INSTALL_TARGETS += GDBINIT_OBJDIR
 
--- a/build/application.ini
+++ b/build/application.ini
@@ -10,16 +10,18 @@
 #endif
 #endif
 #if 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/.
 #endif
 #filter substitution
+#include @TOPOBJDIR@/buildid.h
+#include @TOPOBJDIR@/source-repo.h
 [App]
 Vendor=@MOZ_APP_VENDOR@
 Name=@MOZ_APP_BASENAME@
 RemotingName=@MOZ_APP_REMOTINGNAME@
 #ifdef MOZ_APP_DISPLAYNAME
 CodeName=@MOZ_APP_DISPLAYNAME@
 #endif
 Version=@MOZ_APP_VERSION@
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -2,16 +2,17 @@
 # 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 datetime
 import glob
 import time
 import re
 import os
+import posixpath
 import tempfile
 import shutil
 import subprocess
 import sys
 
 from automation import Automation
 from mozdevice import DMError, DeviceManager
 from mozlog import get_default_logger
@@ -209,17 +210,17 @@ class RemoteAutomation(Automation):
 
         # If crash reporting is disabled (MOZ_CRASHREPORTER!=1), we can't say
         # anything.
         if not self.CRASHREPORTER:
             return False
 
         try:
             dumpDir = tempfile.mkdtemp()
-            remoteCrashDir = self._remoteProfile + '/minidumps/'
+            remoteCrashDir = posixpath.join(self._remoteProfile, 'minidumps')
             if not self._devicemanager.dirExists(remoteCrashDir):
                 # If crash reporting is enabled (MOZ_CRASHREPORTER=1), the
                 # minidumps directory is automatically created when Fennec
                 # (first) starts, so its lack of presence is a hint that
                 # something went wrong.
                 print "Automation Error: No crash directory (%s) found on remote device" % remoteCrashDir
                 # Whilst no crash was found, the run should still display as a failure
                 return True
--- a/build/moz.build
+++ b/build/moz.build
@@ -70,8 +70,10 @@ FINAL_TARGET_FILES += ['/.gdbinit']
 # Install the clang-cl runtime library for ASAN next to the binaries we produce.
 if CONFIG['MOZ_ASAN'] and CONFIG['CLANG_CL']:
     FINAL_TARGET_FILES += ['%' + CONFIG['MOZ_CLANG_RT_ASAN_LIB_PATH']]
 
 if CONFIG['MOZ_APP_BASENAME']:
     FINAL_TARGET_PP_FILES += ['application.ini']
     if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' and CONFIG['MOZ_UPDATER']:
         FINAL_TARGET_PP_FILES += ['update-settings.ini']
+
+DEFINES['TOPOBJDIR'] = TOPOBJDIR
--- a/build/upload.py
+++ b/build/upload.py
@@ -33,17 +33,22 @@
 
 import sys, os
 import re
 import json
 import errno
 import hashlib
 import shutil
 from optparse import OptionParser
-from subprocess import check_call, check_output, STDOUT
+from subprocess import (
+    check_call,
+    check_output,
+    STDOUT,
+    CalledProcessError,
+)
 import redo
 
 def OptionalEnvironmentVariable(v):
     """Return the value of the environment variable named v, or None
     if it's unset (or empty)."""
     if v in os.environ and os.environ[v] != "":
         return os.environ[v]
     return None
@@ -88,17 +93,24 @@ def AppendOptionalArgsToSSHCommandline(c
 def DoSSHCommand(command, user, host, port=None, ssh_key=None):
     """Execute command on user@host using ssh. Optionally use
     port and ssh_key, if provided."""
     cmdline = ["ssh"]
     AppendOptionalArgsToSSHCommandline(cmdline, port, ssh_key)
     cmdline.extend(["%s@%s" % (user, host), command])
 
     with redo.retrying(check_output, sleeptime=10) as f:
-        output = f(cmdline, stderr=STDOUT).strip()
+        try:
+            output = f(cmdline, stderr=STDOUT).strip()
+        except CalledProcessError as e:
+            print "failed ssh command output:"
+            print '=' * 20
+            print e.output
+            print '=' * 20
+            raise
         return output
 
     raise Exception("Command %s returned non-zero exit code" % cmdline)
 
 def DoSCPFile(file, remote_path, user, host, port=None, ssh_key=None):
     """Upload file to user@host:remote_path using scp. Optionally use
     port and ssh_key, if provided."""
     cmdline = ["scp"]
new file mode 100644
--- /dev/null
+++ b/build/variables.py
@@ -0,0 +1,78 @@
+# 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/.
+
+from __future__ import print_function, unicode_literals
+
+import os
+import subprocess
+import sys
+from datetime import datetime
+
+
+def buildid_header(output):
+    buildid = os.environ.get('MOZ_BUILD_DATE')
+    if buildid and len(buildid) != 14:
+        print('Ignoring invalid MOZ_BUILD_DATE: %s' % buildid, file=sys.stderr)
+        buildid = None
+    if not buildid:
+        buildid = datetime.now().strftime('%Y%m%d%H%M%S')
+    output.write("#define MOZ_BUILDID %s\n" % buildid)
+
+
+def get_program_output(*command):
+    try:
+        with open(os.devnull) as stderr:
+            return subprocess.check_output(command, stderr=stderr)
+    except:
+        return ''
+
+
+def get_hg_info(workdir):
+    repo = get_program_output('hg', '-R', workdir, 'path', 'default')
+    if repo:
+        repo = repo.strip()
+        if repo.startswith('ssh://'):
+            repo = 'https://' + repo[6:]
+        repo = repo.rstrip('/')
+
+    changeset = get_program_output(
+        'hg', '-R', workdir, 'parent', '--template={node}')
+
+    return repo, changeset
+
+
+def source_repo_header(output):
+    # We allow the source repo and changeset to be specified via the
+    # environment (see configure)
+    import buildconfig
+    repo = buildconfig.substs.get('MOZ_SOURCE_REPO')
+    changeset = buildconfig.substs.get('MOZ_SOURCE_CHANGESET')
+    source = ''
+
+    if bool(repo) != bool(changeset):
+        raise Exception('MOZ_SOURCE_REPO and MOZ_SOURCE_CHANGESET both must '
+                        'be set (or not set).')
+
+    if not repo:
+        if os.path.exists(os.path.join(buildconfig.topsrcdir, '.hg')):
+            repo, changeset = get_hg_info(buildconfig.topsrcdir)
+
+    if changeset:
+        output.write('#define MOZ_SOURCE_STAMP %s\n' % changeset)
+
+    if repo and buildconfig.substs.get('MOZ_INCLUDE_SOURCE_INFO'):
+        source = '%s/rev/%s' % (repo, changeset)
+        output.write('#define MOZ_SOURCE_REPO %s\n' % repo)
+        output.write('#define MOZ_SOURCE_URL %s\n' % source)
+
+
+def main(args):
+    if (len(args)):
+        func = globals().get(args[0])
+        if func:
+            return func(sys.stdout, *args[1:])
+
+
+if __name__ == '__main__':
+    sys.exit(main(sys.argv[1:]))
--- a/client.mk
+++ b/client.mk
@@ -233,17 +233,17 @@ profiledbuild::
 	$(MAKE) -f $(TOPSRCDIR)/client.mk realbuild MOZ_PROFILE_USE=1 CREATE_MOZCONFIG_JSON=
 
 #####################################################
 # Build date unification
 
 ifdef MOZ_UNIFY_BDATE
 ifndef MOZ_BUILD_DATE
 ifdef MOZ_BUILD_PROJECTS
-MOZ_BUILD_DATE = $(shell $(PYTHON) $(TOPSRCDIR)/toolkit/xre/make-platformini.py --print-buildid)
+MOZ_BUILD_DATE = $(shell $(PYTHON) $(TOPSRCDIR)/build/variables.py buildid_header | awk '{print $$3}')
 export MOZ_BUILD_DATE
 endif
 endif
 endif
 
 #####################################################
 # Preflight, before building any project
 
--- a/config/Makefile.in
+++ b/config/Makefile.in
@@ -27,31 +27,16 @@ NSINSTALL_EXECUTABLES := nsinstall$(HOST
 NSINSTALL_DEST := $(DIST)/bin
 NSINSTALL_TARGET := host
 INSTALL_TARGETS += NSINSTALL
 endif
 endif
 
 include $(topsrcdir)/config/rules.mk
 
-ifndef JS_STANDALONE
-ifndef MOZ_PROFILE_USE
-# Generate a new buildid every time we "export" in config... that's only
-# supposed to be once per-build!
-export:: buildid
-
-buildid: FORCE
-ifdef MOZ_BUILD_DATE
-	printf '%s' $(MOZ_BUILD_DATE) > buildid
-else
-	$(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid > buildid
-endif
-endif
-endif
-
 ifdef WRAP_SYSTEM_INCLUDES
 export-preqs = \
   $(call mkdir_deps,system_wrappers) \
   $(NULL)
 
 export:: $(export-preqs)
 	$(PYTHON) -m mozbuild.action.preprocessor $(DEFINES) $(ACDEFINES) \
 		-DMOZ_TREE_CAIRO=$(MOZ_TREE_CAIRO) \
--- a/config/faster/rules.mk
+++ b/config/faster/rules.mk
@@ -75,28 +75,27 @@ ACDEFINES += -DBUILD_FASTER
 	$(MAKE) -C $(dir $@) $(notdir $@)
 
 # Install files using install manifests
 #
 # The list of base directories is given in INSTALL_MANIFESTS. The
 # corresponding install manifests are named correspondingly, with forward
 # slashes replaced with underscores, and prefixed with `install_`. That is,
 # the install manifest for `dist/bin` would be `install_dist_bin`.
-$(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(TOPOBJDIR)/config/buildid
+$(addprefix install-,$(INSTALL_MANIFESTS)): install-%: $(addprefix $(TOPOBJDIR)/,buildid.h source-repo.h)
 	@# For now, force preprocessed files to be reprocessed every time.
 	@# The overhead is not that big, and this avoids waiting for proper
 	@# support for defines tracking in process_install_manifest.
 	@touch install_$(subst /,_,$*)
 	@# BOOKMARKS_INCLUDE_DIR is for bookmarks.html only.
 	$(PYTHON) -m mozbuild.action.process_install_manifest \
 		--track install_$(subst /,_,$*).track \
 		$(TOPOBJDIR)/$* \
 		-DAB_CD=en-US \
 		-DBOOKMARKS_INCLUDE_DIR=$(TOPSRCDIR)/browser/locales/en-US/profile \
-		-DMOZ_BUILDID=$(shell cat $(TOPOBJDIR)/config/buildid) \
 		$(ACDEFINES) \
 		install_$(subst /,_,$*)
 
 # ============================================================================
 # Below is a set of additional dependencies and variables used to build things
 # that are not supported by data in moz.build.
 
 # The xpidl target in config/makefiles/xpidl requires the install manifest for
--- a/config/makefiles/debugmake.mk
+++ b/config/makefiles/debugmake.mk
@@ -53,16 +53,17 @@ ifneq (,$(filter $(PROGRAM) $(HOST_PROGR
 		EXTRA_DSO_LDOPTS \
 		DEPENDENT_LIBS \
 	)
 	@echo --------------------------------------------------------------------------------
 endif
 	$(LOOP_OVER_DIRS)
 
 showbuild showhost: _DEPEND_CFLAGS=
+showbuild showhost: COMPILE_PDB_FLAG=
 showbuild:
 	$(call print_vars,\
 		MOZ_BUILD_ROOT \
 		MOZ_WIDGET_TOOLKIT \
 		CC \
 		CXX \
 		CCC \
 		CPP \
--- a/config/makefiles/makeutils.mk
+++ b/config/makefiles/makeutils.mk
@@ -112,14 +112,10 @@ topORerr = $(MOZILLA_DIR)
 else
 topORerr = $(if $(topsrcdir),$(topsrcdir),$(error topsrcdir is not defined))
 endif
 
 ifdef USE_AUTOTARGETS_MK # mkdir_deps
   include $(topORerr)/config/makefiles/autotargets.mk
 endif
 
-ifdef USE_RCS_MK
-  include $(topORerr)/config/makefiles/rcs.mk
-endif
-
 ## copy(src, dst): recursive copy
 copy_dir = (cd $(1)/. && $(TAR) $(TAR_CREATE_FLAGS) - .) | (cd $(2)/. && tar -xf -)
deleted file mode 100644
--- a/config/makefiles/rcs.mk
+++ /dev/null
@@ -1,54 +0,0 @@
-# -*- makefile -*-
-# vim:set ts=8 sw=8 sts=8 noet:
-#
-# 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/.
-#
-
-ifdef USE_RCS_MK #{
-
-ifndef INCLUDED_RCS_MK #{
-
-MOZ_RCS_TYPE_HG ?= $(notdir $(wildcard $(topsrcdir)/.hg))
-MOZ_RCS_TYPE_GIT ?= $(notdir $(wildcard $(topsrcdir)/.git))
-
-
-###########################################################################
-# HAVE_MERCURIAL_RCS
-###########################################################################
-ifeq (.hg,$(MOZ_RCS_TYPE_HG)) #{
-
-# Intent: Retrieve the http:// repository path for a directory.
-# Usage: $(call getSourceRepo[,repo_dir|args])
-# Args:
-#   path (optional): repository to query.  Defaults to $(topsrcdir)
-getSourceRepo = \
-  $(call FUNC_getSourceRepo,$(if $(1),cd $(1) && hg,hg --repository $(topsrcdir)))
-
-# return: http://hg.mozilla.org/mozilla-central
-FUNC_getSourceRepo = \
-  $(strip \
-    $(patsubst %/,%,\
-    $(patsubst ssh://%,http://%,\
-    $(firstword $(shell $(getargv) showconfig paths.default))\
-    )))
-
-#} HAVE_MERCURIAL_RCS
-
-###########################################################################
-# HAVE_GIT_RCS
-###########################################################################
-else ifeq (.git,$(MOZ_RCS_TYPE_GIT)) #{
-
-GIT ?= git
-getSourceRepo = \
-  $(shell cd $(topsrcdir) && $(GIT) rev-parse --verify HEAD)
-
-endif #} HAVE_GIT_RCS
-
-
-INCLUDED_RCS_MK := 1
-endif #}
-
-endif #}
--- a/config/version_win.pl
+++ b/config/version_win.pl
@@ -109,17 +109,17 @@ if (!defined($module))
 {
 	$module = $binary;
 	($module) = split(/\./,$module);
 }
 
 my $bufferstr="    ";
 
 my $MILESTONE_FILE = "$topsrcdir/config/milestone.txt";
-my $BUILDID_FILE = "$depth/config/buildid";
+my $BUILDID_FILE = "$depth/buildid.h";
 
 #Read module.ver file
 #Version file overrides for WIN32:
 #WIN32_MODULE_COMMENT
 #WIN32_MODULE_DESCRIPTION
 #WIN32_MODULE_FILEVERSION
 #WIN32_MODULE_COMPANYNAME
 #WIN32_MODULE_FILEVERSION_STRING
@@ -178,17 +178,17 @@ else
 $description =~ s/^\s*(.*)\s*$/$1/;
 $module =~ s/^\s*(.*)\s*$/$1/;
 $depth =~ s/^\s*(.*)\s*$/$1/;
 $binary =~ s/^\s*(.*)\s*$/$1/;
 $displayname =~ s/^\s*(.*)\s*$/$1/;
 
 open(BUILDID, "<", $BUILDID_FILE) || die("Couldn't open buildid file: $BUILDID_FILE");
 $buildid = <BUILDID>;
-$buildid =~ s/\s*$//;
+$buildid =~ s/^#define MOZ_BUILDID\s+(\S+)\s*$/$1/;
 close BUILDID;
 
 my $daycount = daysFromBuildID($buildid);
 
 if ($milestone eq "") {
     $milestone = Moz::Milestone::getOfficialMilestone($MILESTONE_FILE);
 }
 
--- a/configure.in
+++ b/configure.in
@@ -8719,25 +8719,18 @@ AC_SUBST(MOZILLA_OFFICIAL)
 if test "$MOZILLA_OFFICIAL"; then
     AC_DEFINE(MOZILLA_OFFICIAL)
     # Build revisions should always be present in official builds
     MOZ_INCLUDE_SOURCE_INFO=1
 fi
 
 # External builds (specifically Ubuntu) may drop the hg repo information, so we allow to
 # explicitly set the repository and changeset information in.
-if test "$MOZILLA_OFFICIAL"; then
-    if test -z "$MOZ_SOURCE_REPO" && test -z "$MOZ_SOURCE_CHANGESET" && test -d ${_topsrcdir}/.hg; then
-        MOZ_SOURCE_CHANGESET=`cd $_topsrcdir && hg parent --template='{node}'`
-        MOZ_SOURCE_REPO=`cd $_topsrcdir && hg showconfig paths.default | sed -e 's|^ssh://|http://|' -e 's|/$||'`
-    fi
-    SOURCE_REV_URL=$MOZ_SOURCE_REPO/rev/$MOZ_SOURCE_CHANGESET
-fi
-AC_SUBST(SOURCE_REV_URL)
-
+AC_SUBST(MOZ_SOURCE_REPO)
+AC_SUBST(MOZ_SOURCE_CHANGESET)
 AC_SUBST(MOZ_INCLUDE_SOURCE_INFO)
 
 if test "$MOZ_TELEMETRY_REPORTING"; then
     AC_DEFINE(MOZ_TELEMETRY_REPORTING)
 
     # Enable Telemetry by default for nightly and aurora channels
     if test -z "$RELEASE_BUILD"; then
       AC_DEFINE(MOZ_TELEMETRY_ON_BY_DEFAULT)
--- a/dom/archivereader/ArchiveEvent.cpp
+++ b/dom/archivereader/ArchiveEvent.cpp
@@ -44,27 +44,17 @@ ArchiveReaderEvent::ArchiveReaderEvent(A
 : mArchiveReader(aArchiveReader)
 {
   MOZ_COUNT_CTOR(ArchiveReaderEvent);
 }
 
 ArchiveReaderEvent::~ArchiveReaderEvent()
 {
   if (!NS_IsMainThread()) {
-    nsIMIMEService* mimeService;
-    mMimeService.forget(&mimeService);
-
-    if (mimeService) {
-      nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-      NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread! Leaking!");
-
-      if (mainThread) {
-        NS_ProxyRelease(mainThread, mimeService);
-      }
-    }
+    NS_ReleaseOnMainThread(mMimeService.forget());
   }
 
   MOZ_COUNT_DTOR(ArchiveReaderEvent);
 }
 
 // From the filename to the mimetype:
 nsresult
 ArchiveReaderEvent::GetType(nsCString& aExt,
--- a/dom/base/ChildIterator.cpp
+++ b/dom/base/ChildIterator.cpp
@@ -185,38 +185,38 @@ FlattenedChildIterator::Init(bool aIgnor
         MOZ_ASSERT(child->GetBindingParent());
         mXBLInvolved = true;
         break;
       }
     }
   }
 }
 
-void
+bool
 ExplicitChildIterator::Seek(nsIContent* aChildToFind)
 {
   if (aChildToFind->GetParent() == mParent &&
       !aChildToFind->IsRootOfAnonymousSubtree()) {
     // Fast path: just point ourselves to aChildToFind, which is a
     // normal DOM child of ours.
     MOZ_ASSERT(!ShadowRoot::IsShadowInsertionPoint(aChildToFind));
     MOZ_ASSERT(!nsContentUtils::IsContentInsertionPoint(aChildToFind));
     mChild = aChildToFind;
     mIndexInInserted = 0;
     mShadowIterator = nullptr;
     mDefaultChild = nullptr;
     mIsFirst = false;
-    return;
+    return true;
   }
 
   // Can we add more fast paths here based on whether the parent of aChildToFind
   // is a shadow insertion point or content insertion point?
 
   // Slow path: just walk all our kids.
-  Seek(aChildToFind, nullptr);
+  return Seek(aChildToFind, nullptr);
 }
 
 nsIContent*
 ExplicitChildIterator::Get()
 {
   MOZ_ASSERT(!mIsFirst);
 
   if (mIndexInInserted) {
@@ -306,16 +306,47 @@ ExplicitChildIterator::GetPreviousChild(
 
   if (!mChild) {
     mIsFirst = true;
   }
 
   return mChild;
 }
 
+bool
+AllChildrenIterator::Seek(nsIContent* aChildToFind)
+{
+  if (mPhase == eNeedBeforeKid) {
+    mPhase = eNeedExplicitKids;
+    nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
+    if (frame) {
+      nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
+      if (beforeFrame) {
+        if (beforeFrame->GetContent() == aChildToFind) {
+          return true;
+        }
+      }
+    }
+  }
+
+  if (mPhase == eNeedExplicitKids) {
+    if (ExplicitChildIterator::Seek(aChildToFind)) {
+      return true;
+    }
+    mPhase = eNeedAnonKids;
+  }
+
+  nsIContent* child = nullptr;
+  do {
+    child = GetNextChild();
+  } while (child && child != aChildToFind);
+
+  return child == aChildToFind;
+}
+
 nsIContent*
 AllChildrenIterator::GetNextChild()
 {
   if (mPhase == eNeedBeforeKid) {
     mPhase = eNeedExplicitKids;
     nsIFrame* frame = mOriginalContent->GetPrimaryFrame();
     if (frame) {
       nsIFrame* beforeFrame = nsLayoutUtils::GetBeforeFrame(frame);
--- a/dom/base/ChildIterator.h
+++ b/dom/base/ChildIterator.h
@@ -59,17 +59,17 @@ public:
       mIndexInInserted(aOther.mIndexInInserted), mIsFirst(aOther.mIsFirst) {}
 
   nsIContent* GetNextChild();
 
   // Looks for aChildToFind respecting insertion points until aChildToFind is
   // found.  This version can take shortcuts that the two-argument version
   // can't, so can be faster (and in fact can be O(1) instead of O(N) in many
   // cases).
-  void Seek(nsIContent* aChildToFind);
+  bool Seek(nsIContent* aChildToFind);
 
   // Looks for aChildToFind respecting insertion points until aChildToFind is found.
   // or aBound is found. If aBound is nullptr then the seek is unbounded. Returns
   // whether aChildToFind was found as an explicit child prior to encountering
   // aBound.
   bool Seek(nsIContent* aChildToFind, nsIContent* aBound)
   {
     // It would be nice to assert that we find aChildToFind, but bz thinks that
@@ -194,16 +194,18 @@ public:
       , mMutationGuard(aOther.mMutationGuard)
 #endif
       {}
 
 #ifdef DEBUG
   ~AllChildrenIterator() { MOZ_ASSERT(!mMutationGuard.Mutated(0)); }
 #endif
 
+  bool Seek(nsIContent* aChildToFind);
+
   nsIContent* GetNextChild();
   nsIContent* Parent() const { return mOriginalContent; }
 
 private:
   enum IteratorPhase
   {
     eNeedBeforeKid,
     eNeedExplicitKids,
--- a/dom/base/Console.cpp
+++ b/dom/base/Console.cpp
@@ -79,16 +79,21 @@ class ConsoleCallData final
 {
 public:
   NS_INLINE_DECL_REFCOUNTING(ConsoleCallData)
 
   ConsoleCallData()
     : mMethodName(Console::MethodLog)
     , mPrivate(false)
     , mTimeStamp(JS_Now() / PR_USEC_PER_MSEC)
+    , mStartTimerValue(0)
+    , mStartTimerStatus(false)
+    , mStopTimerDuration(0)
+    , mStopTimerStatus(false)
+    , mCountValue(MAX_PAGE_COUNTERS)
     , mIDType(eUnknown)
     , mOuterIDNumber(0)
     , mInnerIDNumber(0)
 #ifdef DEBUG
     , mOwningThread(PR_GetCurrentThread())
 #endif
   { }
 
@@ -167,17 +172,44 @@ public:
   // This is a copy of the arguments we received from the DOM bindings. Console
   // object traces them because this ConsoleCallData calls
   // RegisterConsoleCallData() in the Initialize().
   nsTArray<JS::Heap<JS::Value>> mCopiedArguments;
 
   Console::MethodName mMethodName;
   bool mPrivate;
   int64_t mTimeStamp;
-  DOMHighResTimeStamp mMonotonicTimer;
+
+  // These values are set in the owning thread and they contain the timestamp of
+  // when the new timer has started, the name of it and the status of the
+  // creation of it. If status is false, something went wrong. User
+  // DOMHighResTimeStamp instead mozilla::TimeStamp because we use
+  // monotonicTimer from Performance.now();
+  // They will be set on the owning thread and never touched again on that
+  // thread. They will be used on the main-thread in order to create a
+  // ConsoleTimerStart dictionary when console.time() is used.
+  DOMHighResTimeStamp mStartTimerValue;
+  nsString mStartTimerLabel;
+  bool mStartTimerStatus;
+
+  // These values are set in the owning thread and they contain the duration,
+  // the name and the status of the StopTimer method. If status is false,
+  // something went wrong. They will be set on the owning thread and never
+  // touched again on that thread. They will be used on the main-thread in order
+  // to create a ConsoleTimerEnd dictionary. This members are set when
+  // console.timeEnd() is called.
+  double mStopTimerDuration;
+  nsString mStopTimerLabel;
+  bool mStopTimerStatus;
+
+  // These 2 values are set by IncreaseCounter on the owning thread and they are
+  // used on the main-thread by CreateCounterValue. These members are set when
+  // console.count() is called.
+  nsString mCountLabel;
+  uint32_t mCountValue;
 
   // The concept of outerID and innerID is misleading because when a
   // ConsoleCallData is created from a window, these are the window IDs, but
   // when the object is created from a SharedWorker, a ServiceWorker or a
   // subworker of a ChromeWorker these IDs are the type of worker and the
   // filename of the callee.
   // In Console.jsm the ID is 'jsm'.
   enum {
@@ -340,17 +372,17 @@ private:
     RefPtr<WorkerControlRunnable> runnable =
       new ConsoleReleaseRunnable(mWorkerPrivate, this);
     runnable->Dispatch(nullptr);
   }
 
   void
   RunWithWindow(nsPIDOMWindowInner* aWindow)
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnMainThread();
 
     AutoJSAPI jsapi;
     MOZ_ASSERT(aWindow);
 
     RefPtr<nsGlobalWindow> win = nsGlobalWindow::Cast(aWindow);
     if (NS_WARN_IF(!jsapi.Init(win))) {
       return;
     }
@@ -362,17 +394,17 @@ private:
     }
 
     RunConsole(jsapi.cx(), outerWindow, aWindow);
   }
 
   void
   RunWindowless()
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnMainThread();
 
     WorkerPrivate* wp = mWorkerPrivate;
     while (wp->GetParent()) {
       wp = wp->GetParent();
     }
 
     MOZ_ASSERT(!wp->GetWindow());
 
@@ -512,17 +544,17 @@ private:
     mCallData->CleanupJSObjects();
     return true;
   }
 
   void
   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) override
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnMainThread();
     MOZ_ASSERT(mCallData->mCopiedArguments.IsEmpty());
 
     // The windows have to run in parallel.
     MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow);
 
     if (aOuterWindow) {
       mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID());
     } else {
@@ -559,16 +591,18 @@ private:
   ReleaseData() override
   {
     mCallData = nullptr;
   }
 
   void
   ProcessCallData(JSContext* aCx)
   {
+    AssertIsOnMainThread();
+
     ClearException ce(aCx);
 
     JS::Rooted<JS::Value> argumentsValue(aCx);
     if (!Read(aCx, &argumentsValue)) {
       return;
     }
 
     MOZ_ASSERT(argumentsValue.isObject());
@@ -653,17 +687,17 @@ private:
 
     return true;
   }
 
   void
   RunConsole(JSContext* aCx, nsPIDOMWindowOuter* aOuterWindow,
              nsPIDOMWindowInner* aInnerWindow) override
   {
-    MOZ_ASSERT(NS_IsMainThread());
+    AssertIsOnMainThread();
 
     ClearException ce(aCx);
 
     // Now we could have the correct window (if we are not window-less).
     mClonedData.mParent = aInnerWindow;
 
     JS::Rooted<JS::Value> argumentsValue(aCx);
     bool ok = Read(aCx, &argumentsValue);
@@ -741,16 +775,19 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
 NS_INTERFACE_MAP_END
 
 Console::Console(nsPIDOMWindowInner* aWindow)
   : mWindow(aWindow)
+#ifdef DEBUG
+  , mOwningThread(PR_GetCurrentThread())
+#endif
   , mOuterID(0)
   , mInnerID(0)
 {
   if (mWindow) {
     MOZ_ASSERT(mWindow->IsInnerWindow());
     mInnerID = mWindow->WindowID();
 
     // Without outerwindow any console message coming from this object will not
@@ -769,36 +806,37 @@ Console::Console(nsPIDOMWindowInner* aWi
     }
   }
 
   mozilla::HoldJSObjects(this);
 }
 
 Console::~Console()
 {
+  AssertIsOnOwningThread();
   MOZ_ASSERT(mConsoleCallDataArray.IsEmpty());
 
   if (!NS_IsMainThread()) {
     if (mStorage) {
-      NS_ReleaseOnMainThread(mStorage);
+      NS_ReleaseOnMainThread(mStorage.forget());
     }
 
     if (mSandbox) {
-      NS_ReleaseOnMainThread(mSandbox);
+      NS_ReleaseOnMainThread(mSandbox.forget());
     }
   }
 
   mozilla::DropJSObjects(this);
 }
 
 NS_IMETHODIMP
 Console::Observe(nsISupports* aSubject, const char* aTopic,
                  const char16_t* aData)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnMainThread();
 
   if (strcmp(aTopic, "inner-window-destroyed")) {
     return NS_OK;
   }
 
   nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
   NS_ENSURE_TRUE(wrapper, NS_ERROR_FAILURE);
 
@@ -838,76 +876,86 @@ METHOD(Warn, "warn")
 METHOD(Error, "error")
 METHOD(Exception, "exception")
 METHOD(Debug, "debug")
 METHOD(Table, "table")
 
 void
 Console::Trace(JSContext* aCx)
 {
+  AssertIsOnOwningThread();
+
   const Sequence<JS::Value> data;
   Method(aCx, MethodTrace, NS_LITERAL_STRING("trace"), data);
 }
 
 // Displays an interactive listing of all the properties of an object.
 METHOD(Dir, "dir");
 METHOD(Dirxml, "dirxml");
 
 METHOD(Group, "group")
 METHOD(GroupCollapsed, "groupCollapsed")
 METHOD(GroupEnd, "groupEnd")
 
 void
 Console::Time(JSContext* aCx, const JS::Handle<JS::Value> aTime)
 {
+  AssertIsOnOwningThread();
+
   Sequence<JS::Value> data;
   SequenceRooter<JS::Value> rooter(aCx, &data);
 
   if (!aTime.isUndefined() && !data.AppendElement(aTime, fallible)) {
     return;
   }
 
   Method(aCx, MethodTime, NS_LITERAL_STRING("time"), data);
 }
 
 void
 Console::TimeEnd(JSContext* aCx, const JS::Handle<JS::Value> aTime)
 {
+  AssertIsOnOwningThread();
+
   Sequence<JS::Value> data;
   SequenceRooter<JS::Value> rooter(aCx, &data);
 
   if (!aTime.isUndefined() && !data.AppendElement(aTime, fallible)) {
     return;
   }
 
   Method(aCx, MethodTimeEnd, NS_LITERAL_STRING("timeEnd"), data);
 }
 
 void
 Console::TimeStamp(JSContext* aCx, const JS::Handle<JS::Value> aData)
 {
+  AssertIsOnOwningThread();
+
   Sequence<JS::Value> data;
   SequenceRooter<JS::Value> rooter(aCx, &data);
 
   if (aData.isString() && !data.AppendElement(aData, fallible)) {
     return;
   }
 
   Method(aCx, MethodTimeStamp, NS_LITERAL_STRING("timeStamp"), data);
 }
 
 void
 Console::Profile(JSContext* aCx, const Sequence<JS::Value>& aData)
 {
+  AssertIsOnOwningThread();
   ProfileMethod(aCx, NS_LITERAL_STRING("profile"), aData);
 }
 
 void
 Console::ProfileEnd(JSContext* aCx, const Sequence<JS::Value>& aData)
 {
+  AssertIsOnOwningThread();
   ProfileMethod(aCx, NS_LITERAL_STRING("profileEnd"), aData);
 }
 
 void
 Console::ProfileMethod(JSContext* aCx, const nsAString& aAction,
                        const Sequence<JS::Value>& aData)
 {
   if (!NS_IsMainThread()) {
@@ -961,26 +1009,30 @@ Console::ProfileMethod(JSContext* aCx, c
     obs->NotifyObservers(wrapper, "console-api-profiler", nullptr);
   }
 }
 
 void
 Console::Assert(JSContext* aCx, bool aCondition,
                 const Sequence<JS::Value>& aData)
 {
+  AssertIsOnOwningThread();
+
   if (!aCondition) {
     Method(aCx, MethodAssert, NS_LITERAL_STRING("assert"), aData);
   }
 }
 
 METHOD(Count, "count")
 
 void
 Console::NoopMethod()
 {
+  AssertIsOnOwningThread();
+
   // Nothing to do.
 }
 
 static
 nsresult
 StackFrameToStackEntry(nsIStackFrame* aStackFrame,
                        ConsoleStackEntry& aStackEntry,
                        uint32_t aLanguage)
@@ -1048,16 +1100,18 @@ ReifyStack(nsIStackFrame* aStack, nsTArr
 }
 
 // Queue a call to a console method. See the CALL_DELAY constant.
 void
 Console::Method(JSContext* aCx, MethodName aMethodName,
                 const nsAString& aMethodString,
                 const Sequence<JS::Value>& aData)
 {
+  AssertIsOnOwningThread();
+
   RefPtr<ConsoleCallData> callData(new ConsoleCallData());
 
   ClearException ce(aCx);
 
   callData->Initialize(aCx, aMethodName, aMethodString, aData, this);
 
   if (mWindow) {
     nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(mWindow);
@@ -1115,30 +1169,32 @@ Console::Method(JSContext* aCx, MethodNa
     // before we post our runnable to the main thread.
     callData->mReifiedStack.emplace();
     nsresult rv = ReifyStack(stack, *callData->mReifiedStack);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return;
     }
   }
 
+  DOMHighResTimeStamp monotonicTimer;
+
   // Monotonic timer for 'time' and 'timeEnd'
   if (aMethodName == MethodTime ||
       aMethodName == MethodTimeEnd ||
       aMethodName == MethodTimeStamp) {
     if (mWindow) {
       nsGlobalWindow *win = nsGlobalWindow::Cast(mWindow);
       MOZ_ASSERT(win);
 
       RefPtr<nsPerformance> performance = win->GetPerformance();
       if (!performance) {
         return;
       }
 
-      callData->mMonotonicTimer = performance->Now();
+      monotonicTimer = performance->Now();
 
       nsDocShell* docShell = static_cast<nsDocShell*>(mWindow->GetDocShell());
       RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
       bool isTimelineRecording = timelines && timelines->HasConsumer(docShell);
 
       // The 'timeStamp' recordings do not need an argument; use empty string
       // if no arguments passed in.
       if (isTimelineRecording && aMethodName == MethodTimeStamp) {
@@ -1170,20 +1226,44 @@ Console::Method(JSContext* aCx, MethodNa
           }
         }
       }
     } else {
       WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
       MOZ_ASSERT(workerPrivate);
 
       TimeDuration duration =
-        mozilla::TimeStamp::Now() - workerPrivate->CreationTimeStamp();
+        mozilla::TimeStamp::Now() - workerPrivate->NowBaseTimeStamp();
+
+      monotonicTimer = duration.ToMilliseconds();
+    }
+  }
+
+  if (aMethodName == MethodTime && !aData.IsEmpty()) {
+    callData->mStartTimerStatus = StartTimer(aCx, aData[0],
+                                             monotonicTimer,
+                                             callData->mStartTimerLabel,
+                                             &callData->mStartTimerValue);
+  }
 
-      callData->mMonotonicTimer = duration.ToMilliseconds();
+  else if (aMethodName == MethodTimeEnd && !aData.IsEmpty()) {
+    callData->mStopTimerStatus = StopTimer(aCx, aData[0],
+                                           monotonicTimer,
+                                           callData->mStopTimerLabel,
+                                           &callData->mStopTimerDuration);
+  }
+
+  else if (aMethodName == MethodCount) {
+    ConsoleStackEntry frame;
+    if (callData->mTopStackFrame) {
+      frame = *callData->mTopStackFrame;
     }
+
+    callData->mCountValue = IncreaseCounter(aCx, frame, aData,
+                                            callData->mCountLabel);
   }
 
   JS::Rooted<JSObject*> global(aCx, JS::CurrentGlobalOrNull(aCx));
 
   if (NS_IsMainThread()) {
     callData->SetIDs(mOuterID, mInnerID);
     ProcessCallData(callData, global, aData);
     return;
@@ -1202,16 +1282,18 @@ Console::Method(JSContext* aCx, MethodNa
 enum {
   SLOT_STACKOBJ,
   SLOT_RAW_STACK
 };
 
 bool
 LazyStackGetter(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
 {
+  AssertIsOnMainThread();
+
   JS::CallArgs args = CallArgsFromVp(aArgc, aVp);
   JS::Rooted<JSObject*> callee(aCx, &args.callee());
 
   JS::Value v = js::GetFunctionNativeReserved(&args.callee(), SLOT_RAW_STACK);
   if (v.isUndefined()) {
     // Already reified.
     args.rval().set(js::GetFunctionNativeReserved(callee, SLOT_STACKOBJ));
     return true;
@@ -1238,18 +1320,18 @@ LazyStackGetter(JSContext* aCx, unsigned
   args.rval().set(stackVal);
   return true;
 }
 
 void
 Console::ProcessCallData(ConsoleCallData* aData, JS::Handle<JSObject*> aGlobal,
                          const Sequence<JS::Value>& aArguments)
 {
+  AssertIsOnMainThread();
   MOZ_ASSERT(aData);
-  MOZ_ASSERT(NS_IsMainThread());
 
   ConsoleStackEntry frame;
   if (aData->mTopStackFrame) {
     frame = *aData->mTopStackFrame;
   }
 
   AutoSafeJSContext cx;
   ClearException ce(cx);
@@ -1317,25 +1399,30 @@ Console::ProcessCallData(ConsoleCallData
 
   if (aData->mMethodName == MethodGroup ||
       aData->mMethodName == MethodGroupCollapsed ||
       aData->mMethodName == MethodGroupEnd) {
     ComposeGroupName(cx, aArguments, event.mGroupName);
   }
 
   else if (aData->mMethodName == MethodTime && !aArguments.IsEmpty()) {
-    event.mTimer = StartTimer(cx, aArguments[0], aData->mMonotonicTimer);
+    event.mTimer = CreateStartTimerValue(cx, aData->mStartTimerLabel,
+                                         aData->mStartTimerValue,
+                                         aData->mStartTimerStatus);
   }
 
   else if (aData->mMethodName == MethodTimeEnd && !aArguments.IsEmpty()) {
-    event.mTimer = StopTimer(cx, aArguments[0], aData->mMonotonicTimer);
+    event.mTimer = CreateStopTimerValue(cx, aData->mStopTimerLabel,
+                                        aData->mStopTimerDuration,
+                                        aData->mStopTimerStatus);
   }
 
   else if (aData->mMethodName == MethodCount) {
-    event.mCounter = IncreaseCounter(cx, frame, aArguments);
+    event.mCounter = CreateCounterValue(cx, aData->mCountLabel,
+                                        aData->mCountValue);
   }
 
   // We want to create a console event object and pass it to our
   // nsIConsoleAPIStorage implementation.  We want to define some accessor
   // properties on this object, and those will need to keep an nsIStackFrame
   // alive.  But nsIStackFrame cannot be wrapped in an untrusted scope.  And
   // further, passing untrusted objects to system code is likely to run afoul of
   // Object Xrays.  So we want to wrap in a system-principal scope here.  But
@@ -1429,16 +1516,18 @@ Console::ProcessCallData(ConsoleCallData
 }
 
 namespace {
 
 // Helper method for ProcessArguments. Flushes output, if non-empty, to aSequence.
 bool
 FlushOutput(JSContext* aCx, Sequence<JS::Value>& aSequence, nsString &aOutput)
 {
+  AssertIsOnMainThread();
+
   if (!aOutput.IsEmpty()) {
     JS::Rooted<JSString*> str(aCx, JS_NewUCStringCopyN(aCx,
                                                        aOutput.get(),
                                                        aOutput.Length()));
     if (!str) {
       return false;
     }
 
@@ -1455,16 +1544,18 @@ FlushOutput(JSContext* aCx, Sequence<JS:
 } // namespace
 
 bool
 Console::ProcessArguments(JSContext* aCx,
                           const Sequence<JS::Value>& aData,
                           Sequence<JS::Value>& aSequence,
                           Sequence<JS::Value>& aStyles) const
 {
+  AssertIsOnMainThread();
+
   if (aData.IsEmpty()) {
     return true;
   }
 
   if (aData.Length() == 1 || !aData[0].isString()) {
     return ArgumentsToValueList(aData, aSequence);
   }
 
@@ -1692,17 +1783,17 @@ Console::ProcessArguments(JSContext* aCx
 
   return true;
 }
 
 void
 Console::MakeFormatString(nsCString& aFormat, int32_t aInteger,
                           int32_t aMantissa, char aCh) const
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnMainThread();
 
   aFormat.Append('%');
   if (aInteger >= 0) {
     aFormat.AppendInt(aInteger);
   }
 
   if (aMantissa >= 0) {
     aFormat.Append('.');
@@ -1712,16 +1803,18 @@ Console::MakeFormatString(nsCString& aFo
   aFormat.Append(aCh);
 }
 
 void
 Console::ComposeGroupName(JSContext* aCx,
                           const Sequence<JS::Value>& aData,
                           nsAString& aName) const
 {
+  AssertIsOnMainThread();
+
   for (uint32_t i = 0; i < aData.Length(); ++i) {
     if (i != 0) {
       aName.AppendASCII(" ");
     }
 
     JS::Rooted<JS::Value> value(aCx, aData[i]);
     JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, value));
     if (!jsString) {
@@ -1732,119 +1825,162 @@ Console::ComposeGroupName(JSContext* aCx
     if (!string.init(aCx, jsString)) {
       return;
     }
 
     aName.Append(string);
   }
 }
 
-JS::Value
+bool
 Console::StartTimer(JSContext* aCx, const JS::Value& aName,
-                    DOMHighResTimeStamp aTimestamp)
+                    DOMHighResTimeStamp aTimestamp,
+                    nsAString& aTimerLabel,
+                    DOMHighResTimeStamp* aTimerValue)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aTimerValue);
+
+  *aTimerValue = 0;
 
   if (mTimerRegistry.Count() >= MAX_PAGE_TIMERS) {
+    return false;
+  }
+
+  JS::Rooted<JS::Value> name(aCx, aName);
+  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
+  if (!jsString) {
+    return false;
+  }
+
+  nsAutoJSString label;
+  if (!label.init(aCx, jsString)) {
+    return false;
+  }
+
+  DOMHighResTimeStamp entry;
+  if (!mTimerRegistry.Get(label, &entry)) {
+    mTimerRegistry.Put(label, aTimestamp);
+  } else {
+    aTimestamp = entry;
+  }
+
+  aTimerLabel = label;
+  *aTimerValue = aTimestamp;
+  return true;
+}
+
+JS::Value
+Console::CreateStartTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
+                               DOMHighResTimeStamp aTimerValue,
+                               bool aTimerStatus) const
+{
+  AssertIsOnMainThread();
+
+  if (!aTimerStatus) {
     RootedDictionary<ConsoleTimerError> error(aCx);
 
     JS::Rooted<JS::Value> value(aCx);
     if (!ToJSValue(aCx, error, &value)) {
       return JS::UndefinedValue();
     }
 
     return value;
   }
 
   RootedDictionary<ConsoleTimerStart> timer(aCx);
 
-  JS::Rooted<JS::Value> name(aCx, aName);
-  JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
-  if (!jsString) {
-    return JS::UndefinedValue();
-  }
-
-  nsAutoJSString key;
-  if (!key.init(aCx, jsString)) {
-    return JS::UndefinedValue();
-  }
-
-  timer.mName = key;
-
-  DOMHighResTimeStamp entry;
-  if (!mTimerRegistry.Get(key, &entry)) {
-    mTimerRegistry.Put(key, aTimestamp);
-  } else {
-    aTimestamp = entry;
-  }
-
-  timer.mStarted = aTimestamp;
+  timer.mName = aTimerLabel;
+  timer.mStarted = aTimerValue;
 
   JS::Rooted<JS::Value> value(aCx);
   if (!ToJSValue(aCx, timer, &value)) {
     return JS::UndefinedValue();
   }
 
   return value;
 }
 
-JS::Value
+bool
 Console::StopTimer(JSContext* aCx, const JS::Value& aName,
-                   DOMHighResTimeStamp aTimestamp)
+                   DOMHighResTimeStamp aTimestamp,
+                   nsAString& aTimerLabel,
+                   double* aTimerDuration)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
+  MOZ_ASSERT(aTimerDuration);
+
+  *aTimerDuration = 0;
 
   JS::Rooted<JS::Value> name(aCx, aName);
   JS::Rooted<JSString*> jsString(aCx, JS::ToString(aCx, name));
   if (!jsString) {
-    return JS::UndefinedValue();
+    return false;
   }
 
   nsAutoJSString key;
   if (!key.init(aCx, jsString)) {
-    return JS::UndefinedValue();
+    return false;
   }
 
   DOMHighResTimeStamp entry;
   if (!mTimerRegistry.Get(key, &entry)) {
-    return JS::UndefinedValue();
+    return false;
   }
 
   mTimerRegistry.Remove(key);
 
+  aTimerLabel = key;
+  *aTimerDuration = aTimestamp - entry;
+  return true;
+}
+
+JS::Value
+Console::CreateStopTimerValue(JSContext* aCx, const nsAString& aLabel,
+                              double aDuration, bool aStatus) const
+{
+  AssertIsOnMainThread();
+
+  if (!aStatus) {
+    return JS::UndefinedValue();
+  }
+
   RootedDictionary<ConsoleTimerEnd> timer(aCx);
-  timer.mName = key;
-  timer.mDuration = aTimestamp - entry;
+  timer.mName = aLabel;
+  timer.mDuration = aDuration;
 
   JS::Rooted<JS::Value> value(aCx);
   if (!ToJSValue(aCx, timer, &value)) {
     return JS::UndefinedValue();
   }
 
   return value;
 }
 
 bool
 Console::ArgumentsToValueList(const Sequence<JS::Value>& aData,
                               Sequence<JS::Value>& aSequence) const
 {
+  AssertIsOnMainThread();
+
   for (uint32_t i = 0; i < aData.Length(); ++i) {
     if (!aSequence.AppendElement(aData[i], fallible)) {
       return false;
     }
   }
 
   return true;
 }
 
-JS::Value
+uint32_t
 Console::IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
-                         const Sequence<JS::Value>& aArguments)
+                         const Sequence<JS::Value>& aArguments,
+                         nsAString& aCountLabel)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnOwningThread();
 
   ClearException ce(aCx);
 
   nsAutoString key;
   nsAutoString label;
 
   if (!aArguments.IsEmpty()) {
     JS::Rooted<JS::Value> labelValue(aCx, aArguments[0]);
@@ -1859,35 +1995,50 @@ Console::IncreaseCounter(JSContext* aCx,
 
   if (key.IsEmpty()) {
     key.Append(aFrame.mFilename);
     key.Append(':');
     key.AppendInt(aFrame.mLineNumber);
   }
 
   uint32_t count = 0;
-  if (!mCounterRegistry.Get(key, &count)) {
-    if (mCounterRegistry.Count() >= MAX_PAGE_COUNTERS) {
-      RootedDictionary<ConsoleCounterError> error(aCx);
-
-      JS::Rooted<JS::Value> value(aCx);
-      if (!ToJSValue(aCx, error, &value)) {
-        return JS::UndefinedValue();
-      }
-
-      return value;
-    }
+  if (!mCounterRegistry.Get(key, &count) &&
+      mCounterRegistry.Count() >= MAX_PAGE_COUNTERS) {
+    return MAX_PAGE_COUNTERS;
   }
 
   ++count;
   mCounterRegistry.Put(key, count);
 
+  aCountLabel = label;
+  return count;
+}
+
+JS::Value
+Console::CreateCounterValue(JSContext* aCx, const nsAString& aCountLabel,
+                            uint32_t aCountValue) const
+{
+  AssertIsOnMainThread();
+
+  ClearException ce(aCx);
+
+  if (aCountValue == MAX_PAGE_COUNTERS) {
+    RootedDictionary<ConsoleCounterError> error(aCx);
+
+    JS::Rooted<JS::Value> value(aCx);
+    if (!ToJSValue(aCx, error, &value)) {
+      return JS::UndefinedValue();
+    }
+
+    return value;
+  }
+
   RootedDictionary<ConsoleCounter> data(aCx);
-  data.mLabel = label;
-  data.mCount = count;
+  data.mLabel = aCountLabel;
+  data.mCount = aCountValue;
 
   JS::Rooted<JS::Value> value(aCx);
   if (!ToJSValue(aCx, data, &value)) {
     return JS::UndefinedValue();
   }
 
   return value;
 }
@@ -1904,17 +2055,17 @@ Console::ShouldIncludeStackTrace(MethodN
     default:
       return false;
   }
 }
 
 JSObject*
 Console::GetOrCreateSandbox(JSContext* aCx, nsIPrincipal* aPrincipal)
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  AssertIsOnMainThread();
 
   if (!mSandbox) {
     nsIXPConnect* xpc = nsContentUtils::XPConnect();
     MOZ_ASSERT(xpc, "This should never be null!");
 
     JS::Rooted<JSObject*> sandbox(aCx);
     nsresult rv = xpc->CreateSandbox(aCx, aPrincipal, sandbox.address());
     if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -1925,21 +2076,32 @@ Console::GetOrCreateSandbox(JSContext* a
   }
 
   return mSandbox->GetJSObject();
 }
 
 void
 Console::RegisterConsoleCallData(ConsoleCallData* aData)
 {
+  AssertIsOnOwningThread();
+
   MOZ_ASSERT(!mConsoleCallDataArray.Contains(aData));
   mConsoleCallDataArray.AppendElement(aData);
 }
 
 void
 Console::UnregisterConsoleCallData(ConsoleCallData* aData)
 {
+  AssertIsOnOwningThread();
+
   MOZ_ASSERT(mConsoleCallDataArray.Contains(aData));
   mConsoleCallDataArray.RemoveElement(aData);
 }
 
+void
+Console::AssertIsOnOwningThread() const
+{
+  MOZ_ASSERT(mOwningThread);
+  MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+}
+
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Console.h
+++ b/dom/base/Console.h
@@ -176,64 +176,143 @@ private:
                    char aCh) const;
 
   // Stringify and Concat all the JS::Value in a single string using ' ' as
   // separator.
   void
   ComposeGroupName(JSContext* aCx, const Sequence<JS::Value>& aData,
                    nsAString& aName) const;
 
-  JS::Value
+  // StartTimer is called on the owning thread and populates aTimerLabel and
+  // aTimerValue. It returns false if a JS exception is thrown or if
+  // the max number of timers is reached.
+  // * aCx - the JSContext rooting aName.
+  // * aName - this is (should be) the name of the timer as JS::Value.
+  // * aTimestamp - the monotonicTimer for this context (taken from
+  //                window->performance.now() or from Now() -
+  //                workerPrivate->NowBaseTimeStamp() in workers.
+  // * aTimerLabel - This label will be populated with the aName converted to a
+  //                 string.
+  // * aTimerValue - the StartTimer value stored into (or taken from)
+  //                 mTimerRegistry.
+  bool
   StartTimer(JSContext* aCx, const JS::Value& aName,
-             DOMHighResTimeStamp aTimestamp);
+             DOMHighResTimeStamp aTimestamp,
+             nsAString& aTimerLabel,
+             DOMHighResTimeStamp* aTimerValue);
+
+  // CreateStartTimerValue is called on the main thread only and generates a
+  // ConsoleTimerStart dictionary exposed as JS::Value. If aTimerStatus is
+  // false, it generates a ConsoleTimerError instead. It's called only after
+  // the execution StartTimer on the owning thread.
+  // * aCx - this is the context that will root the returned value.
+  // * aTimerLabel - this label must be what StartTimer received as aTimerLabel.
+  // * aTimerValue - this is what StartTimer received as aTimerValue
+  // * aTimerStatus - the return value of StartTimer.
+  JS::Value
+  CreateStartTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
+                        DOMHighResTimeStamp aTimerValue,
+                        bool aTimerStatus) const;
 
-  JS::Value
+  // StopTimer follows the same pattern as StartTimer: it runs on the
+  // owning thread and populates aTimerLabel and aTimerDuration, used by
+  // CreateStopTimerValue on the main thread. It returns false if a JS
+  // exception is thrown or if the aName timer doesn't exist in mTimerRegistry.
+  // * aCx - the JSContext rooting aName.
+  // * aName - this is (should be) the name of the timer as JS::Value.
+  // * aTimestamp - the monotonicTimer for this context (taken from
+  //                window->performance.now() or from Now() -
+  //                workerPrivate->NowBaseTimeStamp() in workers.
+  // * aTimerLabel - This label will be populated with the aName converted to a
+  //                 string.
+  // * aTimerDuration - the difference between aTimestamp and when the timer
+  //                    started (see StartTimer).
+  bool
   StopTimer(JSContext* aCx, const JS::Value& aName,
-            DOMHighResTimeStamp aTimestamp);
+            DOMHighResTimeStamp aTimestamp,
+            nsAString& aTimerLabel,
+            double* aTimerDuration);
+
+  // Executed on the main thread and generates a ConsoleTimerEnd dictionary
+  // exposed as JS::Value, or a ConsoleTimerError dictionary if aTimerStatus is
+  // false. See StopTimer.
+  // * aCx - this is the context that will root the returned value.
+  // * aTimerLabel - this label must be what StopTimer received as aTimerLabel.
+  // * aTimerDuration - this is what StopTimer received as aTimerDuration
+  // * aTimerStatus - the return value of StopTimer.
+  JS::Value
+  CreateStopTimerValue(JSContext* aCx, const nsAString& aTimerLabel,
+                       double aTimerDuration,
+                       bool aTimerStatus) const;
 
   // The method populates a Sequence from an array of JS::Value.
   bool
   ArgumentsToValueList(const Sequence<JS::Value>& aData,
                        Sequence<JS::Value>& aSequence) const;
 
   void
   ProfileMethod(JSContext* aCx, const nsAString& aAction,
                 const Sequence<JS::Value>& aData);
 
-  JS::Value
+  // This method follows the same pattern as StartTimer: its runs on the owning
+  // thread and populates aCountLabel, used by CreateCounterValue on the
+  // main thread. Returns MAX_PAGE_COUNTERS in case of error otherwise the
+  // incremented counter value.
+  // * aCx - the JSContext rooting aData.
+  // * aFrame - the first frame of ConsoleCallData.
+  // * aData - the arguments received by the console.count() method.
+  // * aCountLabel - the label that will be populated by this method.
+  uint32_t
   IncreaseCounter(JSContext* aCx, const ConsoleStackEntry& aFrame,
-                  const Sequence<JS::Value>& aArguments);
+                  const Sequence<JS::Value>& aData,
+                  nsAString& aCountLabel);
+
+  // Executed on the main thread and generates a ConsoleCounter dictionary as
+  // JS::Value. If aCountValue is == MAX_PAGE_COUNTERS it generates a
+  // ConsoleCounterError instead. See IncreaseCounter.
+  // * aCx - this is the context that will root the returned value.
+  // * aCountLabel - this label must be what IncreaseCounter received as
+  //                 aTimerLabel.
+  // * aCountValue - the return value of IncreaseCounter.
+  JS::Value
+  CreateCounterValue(JSContext* aCx, const nsAString& aCountLabel,
+                     uint32_t aCountValue) const;
 
   bool
   ShouldIncludeStackTrace(MethodName aMethodName) const;
 
   JSObject*
   GetOrCreateSandbox(JSContext* aCx, nsIPrincipal* aPrincipal);
 
   void
   RegisterConsoleCallData(ConsoleCallData* aData);
 
   void
   UnregisterConsoleCallData(ConsoleCallData* aData);
 
-  // All these nsCOMPtr are touched on main-thread only.
+  void
+  AssertIsOnOwningThread() const;
+
+  // All these nsCOMPtr are touched on main thread only.
   nsCOMPtr<nsPIDOMWindowInner> mWindow;
   nsCOMPtr<nsIConsoleAPIStorage> mStorage;
   RefPtr<JSObjectHolder> mSandbox;
 
-  // Touched on main-thread only.
+  // Touched on the owner thread.
   nsDataHashtable<nsStringHashKey, DOMHighResTimeStamp> mTimerRegistry;
-
-  // Touched on main-thread only.
   nsDataHashtable<nsStringHashKey, uint32_t> mCounterRegistry;
 
   // Raw pointers because ConsoleCallData manages its own
   // registration/unregistration.
   nsTArray<ConsoleCallData*> mConsoleCallDataArray;
 
+#ifdef DEBUG
+  PRThread* mOwningThread;
+#endif
+
   uint64_t mOuterID;
   uint64_t mInnerID;
 
   friend class ConsoleCallData;
   friend class ConsoleRunnable;
   friend class ConsoleCallDataRunnable;
   friend class ConsoleProfileRunnable;
 };
--- a/dom/base/Element.cpp
+++ b/dom/base/Element.cpp
@@ -40,17 +40,16 @@
 #include "nsIDOMEvent.h"
 #include "nsDOMCID.h"
 #include "nsIServiceManager.h"
 #include "nsIDOMCSSStyleDeclaration.h"
 #include "nsDOMCSSAttrDeclaration.h"
 #include "nsNameSpaceManager.h"
 #include "nsContentList.h"
 #include "nsVariant.h"
-#include "nsDOMSettableTokenList.h"
 #include "nsDOMTokenList.h"
 #include "nsXBLPrototypeBinding.h"
 #include "nsError.h"
 #include "nsDOMString.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIDOMMutationEvent.h"
 #include "mozilla/dom/AnimatableBinding.h"
 #include "mozilla/AnimationComparator.h"
@@ -3061,21 +3060,21 @@ Element::PostHandleEventForLinks(EventCh
 
 void
 Element::GetLinkTarget(nsAString& aTarget)
 {
   aTarget.Truncate();
 }
 
 static void
-nsDOMSettableTokenListPropertyDestructor(void *aObject, nsIAtom *aProperty,
-                                         void *aPropertyValue, void *aData)
+nsDOMTokenListPropertyDestructor(void *aObject, nsIAtom *aProperty,
+                                 void *aPropertyValue, void *aData)
 {
-  nsDOMSettableTokenList* list =
-    static_cast<nsDOMSettableTokenList*>(aPropertyValue);
+  nsDOMTokenList* list =
+    static_cast<nsDOMTokenList*>(aPropertyValue);
   NS_RELEASE(list);
 }
 
 static nsIAtom** sPropertiesToTraverseAndUnlink[] =
   {
     &nsGkAtoms::microdataProperties,
     &nsGkAtoms::itemtype,
     &nsGkAtoms::itemref,
@@ -3087,57 +3086,57 @@ static nsIAtom** sPropertiesToTraverseAn
 
 // static
 nsIAtom***
 Element::HTMLSVGPropertiesToTraverseAndUnlink()
 {
   return sPropertiesToTraverseAndUnlink;
 }
 
-nsDOMSettableTokenList*
+nsDOMTokenList*
 Element::GetTokenList(nsIAtom* aAtom)
 {
 #ifdef DEBUG
   nsIAtom*** props =
     HTMLSVGPropertiesToTraverseAndUnlink();
   bool found = false;
   for (uint32_t i = 0; props[i]; ++i) {
     if (*props[i] == aAtom) {
       found = true;
       break;
     }
   }
   MOZ_ASSERT(found, "Trying to use an unknown tokenlist!");
 #endif
 
-  nsDOMSettableTokenList* list = nullptr;
+  nsDOMTokenList* list = nullptr;
   if (HasProperties()) {
-    list = static_cast<nsDOMSettableTokenList*>(GetProperty(aAtom));
+    list = static_cast<nsDOMTokenList*>(GetProperty(aAtom));
   }
   if (!list) {
-    list = new nsDOMSettableTokenList(this, aAtom);
+    list = new nsDOMTokenList(this, aAtom);
     NS_ADDREF(list);
-    SetProperty(aAtom, list, nsDOMSettableTokenListPropertyDestructor);
+    SetProperty(aAtom, list, nsDOMTokenListPropertyDestructor);
   }
   return list;
 }
 
 void
 Element::GetTokenList(nsIAtom* aAtom, nsIVariant** aResult)
 {
   nsISupports* itemType = GetTokenList(aAtom);
   nsCOMPtr<nsIWritableVariant> out = new nsVariant();
   out->SetAsInterface(NS_GET_IID(nsISupports), itemType);
   out.forget(aResult);
 }
 
 nsresult
 Element::SetTokenList(nsIAtom* aAtom, nsIVariant* aValue)
 {
-  nsDOMSettableTokenList* itemType = GetTokenList(aAtom);
+  nsDOMTokenList* itemType = GetTokenList(aAtom);
   nsAutoString string;
   aValue->GetAsAString(string);
   ErrorResult rv;
   itemType->SetValue(string, rv);
   return rv.StealNSResult();
 }
 
 Element*
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -39,17 +39,16 @@
 #include "Units.h"
 
 class nsIFrame;
 class nsIDOMMozNamedAttrMap;
 class nsIURI;
 class nsIScrollableFrame;
 class nsAttrValueOrString;
 class nsContentList;
-class nsDOMSettableTokenList;
 class nsDOMTokenList;
 struct nsRect;
 class nsFocusManager;
 class nsGlobalWindow;
 class nsICSSDeclaration;
 class nsISMILAttr;
 class nsDocument;
 
@@ -1293,17 +1292,17 @@ protected:
    *
    * Note: for HTML this gets the value of the 'target' attribute; for XLink
    * this gets the value of the xlink:_moz_target attribute, or failing that,
    * the value of xlink:show, converted to a suitably equivalent named target
    * (e.g. _blank).
    */
   virtual void GetLinkTarget(nsAString& aTarget);
 
-  nsDOMSettableTokenList* GetTokenList(nsIAtom* aAtom);
+  nsDOMTokenList* GetTokenList(nsIAtom* aAtom);
   void GetTokenList(nsIAtom* aAtom, nsIVariant** aResult);
   nsresult SetTokenList(nsIAtom* aAtom, nsIVariant* aValue);
 
 private:
   /**
    * Get this element's client area rect in app units.
    * @return the frame's client area
    */
--- a/dom/base/ProcessGlobal.cpp
+++ b/dom/base/ProcessGlobal.cpp
@@ -52,19 +52,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
   tmp->TraverseHostObjectURIs(cb);
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ProcessGlobal)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
-  for (uint32_t i = 0; i < tmp->mAnonymousGlobalScopes.Length(); ++i) {
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAnonymousGlobalScopes[i])
-  }
+  tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ProcessGlobal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousGlobalScopes)
   tmp->UnlinkHostObjectURIs();
--- a/dom/base/WebSocket.cpp
+++ b/dom/base/WebSocket.cpp
@@ -624,18 +624,18 @@ WebSocketImpl::Disconnect()
     // where to, exactly?
     rv.SuppressException();
   }
 
   // DontKeepAliveAnyMore() can release the object. So hold a reference to this
   // until the end of the method.
   RefPtr<WebSocketImpl> kungfuDeathGrip = this;
 
-  NS_ReleaseOnMainThread(mChannel);
-  NS_ReleaseOnMainThread(static_cast<nsIWebSocketEventService*>(mService.forget().take()));
+  NS_ReleaseOnMainThread(mChannel.forget());
+  NS_ReleaseOnMainThread(mService.forget());
 
   mWebSocket->DontKeepAliveAnyMore();
   mWebSocket->mImpl = nullptr;
 
   if (mWorkerPrivate && mWorkerFeature) {
     UnregisterFeature();
   }
 
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -276,17 +276,16 @@ UNIFIED_SOURCES += [
     'nsDocumentEncoder.cpp',
     'nsDOMAttributeMap.cpp',
     'nsDOMCaretPosition.cpp',
     'nsDOMClassInfo.cpp',
     'nsDOMMutationObserver.cpp',
     'nsDOMNavigationTiming.cpp',
     'nsDOMScriptObjectFactory.cpp',
     'nsDOMSerializer.cpp',
-    'nsDOMSettableTokenList.cpp',
     'nsDOMTokenList.cpp',
     'nsDOMWindowList.cpp',
     'nsFocusManager.cpp',
     'nsFrameLoader.cpp',
     'nsGenConImageContent.cpp',
     'nsGenericDOMDataNode.cpp',
     'nsGkAtoms.cpp',
     'nsGlobalWindowCommands.cpp',
deleted file mode 100644
--- a/dom/base/nsDOMSettableTokenList.cpp
+++ /dev/null
@@ -1,29 +0,0 @@
-/* -*- 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/. */
-
-/*
- * Implementation of DOMSettableTokenList specified by HTML5.
- */
-
-#include "nsDOMSettableTokenList.h"
-#include "mozilla/dom/DOMSettableTokenListBinding.h"
-#include "mozilla/dom/Element.h"
-
-void
-nsDOMSettableTokenList::SetValue(const nsAString& aValue, mozilla::ErrorResult& rv)
-{
-  if (!mElement) {
-    return;
-  }
-
-  rv = mElement->SetAttr(kNameSpaceID_None, mAttrAtom, aValue, true);
-}
-
-JSObject*
-nsDOMSettableTokenList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
-{
-  return mozilla::dom::DOMSettableTokenListBinding::Wrap(cx, this, aGivenProto);
-}
deleted file mode 100644
--- a/dom/base/nsDOMSettableTokenList.h
+++ /dev/null
@@ -1,35 +0,0 @@
-/* -*- 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/. */
-
-/*
- * Implementation of DOMSettableTokenList specified by HTML5.
- */
-
-#ifndef nsDOMSettableTokenList_h___
-#define nsDOMSettableTokenList_h___
-
-#include "nsDOMTokenList.h"
-
-class nsIAtom;
-
-// nsISupports must be on the primary inheritance chain
-// because nsDOMSettableTokenList is traversed by Element.
-class nsDOMSettableTokenList final : public nsDOMTokenList
-{
-public:
-
-  nsDOMSettableTokenList(mozilla::dom::Element* aElement, nsIAtom* aAttrAtom)
-    : nsDOMTokenList(aElement, aAttrAtom) {}
-
-  virtual JSObject* WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto) override;
-
-  // WebIDL
-  void GetValue(nsAString& aResult) { Stringify(aResult); }
-  void SetValue(const nsAString& aValue, mozilla::ErrorResult& rv);
-};
-
-#endif // nsDOMSettableTokenList_h___
-
--- a/dom/base/nsDOMTokenList.cpp
+++ b/dom/base/nsDOMTokenList.cpp
@@ -4,17 +4,16 @@
  * 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/. */
 
 /*
  * Implementation of DOMTokenList specified by HTML5.
  */
 
 #include "nsDOMTokenList.h"
-
 #include "nsAttrValue.h"
 #include "nsContentUtils.h"
 #include "nsError.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/DOMTokenListBinding.h"
 #include "mozilla/ErrorResult.h"
 
 using namespace mozilla;
@@ -69,16 +68,26 @@ nsDOMTokenList::IndexedGetter(uint32_t a
   if (attr && aIndex < static_cast<uint32_t>(attr->GetAtomCount())) {
     aFound = true;
     attr->AtomAt(aIndex)->ToString(aResult);
   } else {
     aFound = false;
   }
 }
 
+void
+nsDOMTokenList::SetValue(const nsAString& aValue, mozilla::ErrorResult& rv)
+{
+  if (!mElement) {
+    return;
+  }
+
+  rv = mElement->SetAttr(kNameSpaceID_None, mAttrAtom, aValue, true);
+}
+
 nsresult
 nsDOMTokenList::CheckToken(const nsAString& aStr)
 {
   if (aStr.IsEmpty()) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
   nsAString::const_iterator iter, end;
--- a/dom/base/nsDOMTokenList.h
+++ b/dom/base/nsDOMTokenList.h
@@ -21,17 +21,17 @@ namespace mozilla {
 class ErrorResult;
 
 } // namespace mozilla
 
 class nsAttrValue;
 class nsIAtom;
 
 // nsISupports must be on the primary inheritance chain
-// because nsDOMSettableTokenList is traversed by Element.
+
 class nsDOMTokenList : public nsISupports,
                        public nsWrapperCache
 {
 protected:
   typedef mozilla::dom::Element Element;
 
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
@@ -61,16 +61,19 @@ public:
   void Add(const nsTArray<nsString>& aTokens,
            mozilla::ErrorResult& aError);
   void Remove(const nsAString& aToken, mozilla::ErrorResult& aError);
   void Remove(const nsTArray<nsString>& aTokens,
               mozilla::ErrorResult& aError);
   bool Toggle(const nsAString& aToken,
               const mozilla::dom::Optional<bool>& force,
               mozilla::ErrorResult& aError);
+  
+  void GetValue(nsAString& aResult) { Stringify(aResult); }
+  void SetValue(const nsAString& aValue, mozilla::ErrorResult& rv);
   void Stringify(nsAString& aResult);
 
 protected:
   virtual ~nsDOMTokenList();
 
   nsresult CheckToken(const nsAString& aStr);
   nsresult CheckTokens(const nsTArray<nsString>& aStr);
   void AddInternal(const nsAttrValue* aAttr,
--- a/dom/base/nsFrameMessageManager.cpp
+++ b/dom/base/nsFrameMessageManager.cpp
@@ -1786,16 +1786,24 @@ nsMessageManagerScriptExecutor::TryCache
   const nsAString& aURL,
   bool aRunInGlobalScope)
 {
   AutoSafeJSContext cx;
   JS::Rooted<JSScript*> script(cx);
   TryCacheLoadAndCompileScript(aURL, aRunInGlobalScope, true, &script);
 }
 
+void
+nsMessageManagerScriptExecutor::Trace(const TraceCallbacks& aCallbacks, void* aClosure)
+{
+  for (size_t i = 0, length = mAnonymousGlobalScopes.Length(); i < length; ++i) {
+    aCallbacks.Trace(&mAnonymousGlobalScopes[i], "mAnonymousGlobalScopes[i]", aClosure);
+  }
+}
+
 bool
 nsMessageManagerScriptExecutor::InitChildGlobalInternal(
   nsISupports* aScope,
   const nsACString& aID)
 {
   AutoSafeJSContext cx;
   nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal));
 
--- a/dom/base/nsFrameMessageManager.h
+++ b/dom/base/nsFrameMessageManager.h
@@ -392,16 +392,17 @@ protected:
   void LoadScriptInternal(const nsAString& aURL, bool aRunInGlobalScope);
   void TryCacheLoadAndCompileScript(const nsAString& aURL,
                                     bool aRunInGlobalScope,
                                     bool aShouldCache,
                                     JS::MutableHandle<JSScript*> aScriptp);
   void TryCacheLoadAndCompileScript(const nsAString& aURL,
                                     bool aRunInGlobalScope);
   bool InitChildGlobalInternal(nsISupports* aScope, const nsACString& aID);
+  void Trace(const TraceCallbacks& aCallbacks, void* aClosure);
   nsCOMPtr<nsIXPConnectJSObjectHolder> mGlobal;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   AutoTArray<JS::Heap<JSObject*>, 2> mAnonymousGlobalScopes;
 
   static nsDataHashtable<nsStringHashKey, nsMessageManagerScriptHolder*>* sCachedScripts;
   static nsScriptCacheCleaner* sScriptCacheCleaner;
 };
 
--- a/dom/base/nsInProcessTabChildGlobal.cpp
+++ b/dom/base/nsInProcessTabChildGlobal.cpp
@@ -141,19 +141,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
                                                   DOMEventTargetHelper)
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
    tmp->TraverseHostObjectURIs(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(nsInProcessTabChildGlobal,
                                                DOMEventTargetHelper)
-  for (uint32_t i = 0; i < tmp->mAnonymousGlobalScopes.Length(); ++i) {
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAnonymousGlobalScopes[i])
-  }
+  tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsInProcessTabChildGlobal,
                                                 DOMEventTargetHelper)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousGlobalScopes)
    tmp->UnlinkHostObjectURIs();
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -777,27 +777,18 @@ nsScriptLoader::ProcessOffThreadRequest(
   nsresult rv = ProcessRequest(aRequest);
   mDocument->UnblockOnload(false);
   return rv;
 }
 
 NotifyOffThreadScriptLoadCompletedRunnable::~NotifyOffThreadScriptLoadCompletedRunnable()
 {
   if (MOZ_UNLIKELY(mRequest || mLoader) && !NS_IsMainThread()) {
-    nsCOMPtr<nsIThread> mainThread;
-    NS_GetMainThread(getter_AddRefs(mainThread));
-    if (mainThread) {
-      NS_ProxyRelease(mainThread, mRequest);
-      NS_ProxyRelease(mainThread, mLoader);
-    } else {
-      MOZ_ASSERT(false, "We really shouldn't leak!");
-      // Better to leak than crash.
-      Unused << mRequest.forget();
-      Unused << mLoader.forget();
-    }
+    NS_ReleaseOnMainThread(mRequest.forget());
+    NS_ReleaseOnMainThread(mLoader.forget());
   }
 }
 
 NS_IMETHODIMP
 NotifyOffThreadScriptLoadCompletedRunnable::Run()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
--- a/dom/base/test/test_bug346485.html
+++ b/dom/base/test/test_bug346485.html
@@ -17,17 +17,17 @@ https://bugzilla.mozilla.org/show_bug.cg
   <output id='o' for='a b'></output>
 </div>
 <pre id="test">
 <script type="application/javascript">
 
 /** Test for Bug 346485 **/
 
 /**
- * This test is testing DOMSettableTokenList used by the output element.
+ * This test is testing DOMTokenList used by the output element.
  */
 
 function checkHtmlFor(htmlFor, list, msg) {
   var length = htmlFor.length;
   is(length, list.length, htmlFor + ": incorrect htmlFor length (" + msg + ")");
   for (var i = 0; i < length; ++i) {
     is(htmlFor[i], list[i], htmlFor + ": wrong element at " + i + " (" + msg + ")");
   }
--- a/dom/base/test/test_bug422403-1.html
+++ b/dom/base/test/test_bug422403-1.html
@@ -30,24 +30,24 @@ function loadFileContent(aFile, aCharset
     var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
             .getService(SpecialPowers.Ci.nsIIOService);
     var chann = ios.newChannel2(aFile,
                                 aCharset,
                                 baseUri,
                                 null,      // aLoadingNode
                                 SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
                                 null,      // aTriggeringPrincipal
-                                SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL,
+                                SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                                 SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
 
     var cis = SpecialPowers.Ci.nsIConverterInputStream;
 
     var inputStream = SpecialPowers.Cc["@mozilla.org/intl/converter-input-stream;1"]
                        .createInstance(cis);
-    inputStream.init(chann.open(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
+    inputStream.init(chann.open2(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
     var str = {}, content = '';
     while (inputStream.readString(4096, str) != 0) {
         content += str.value;
     }
     return content;
 }
 
 
--- a/dom/base/test/test_bug422403-2.xhtml
+++ b/dom/base/test/test_bug422403-2.xhtml
@@ -30,24 +30,24 @@ function loadFileContent(aFile, aCharset
     var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
             .getService(SpecialPowers.Ci.nsIIOService);
     var chann = ios.newChannel2(aFile,
                                 aCharset,
                                 baseUri,
                                 null,      // aLoadingNode
                                 SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
                                 null,      // aTriggeringPrincipal
-                                SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL,
+                                SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                                 SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
 
     var cis = SpecialPowers.Ci.nsIConverterInputStream;
 
     var inputStream = SpecialPowers.Cc["@mozilla.org/intl/converter-input-stream;1"]
                        .createInstance(cis);
-    inputStream.init(chann.open(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
+    inputStream.init(chann.open2(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
     var str = {}, content = '';
     while (inputStream.readString(4096, str) != 0) {
         content += str.value;
     }
     return content;
 }
 
 
--- a/dom/base/test/test_bug424359-1.html
+++ b/dom/base/test/test_bug424359-1.html
@@ -30,24 +30,24 @@ function loadFileContent(aFile, aCharset
     var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
             .getService(SpecialPowers.Ci.nsIIOService);
     var chann = ios.newChannel2(aFile,
                                 aCharset,
                                 baseUri,
                                 null,      // aLoadingNode
                                 SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
                                 null,      // aTriggeringPrincipal
-                                SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL,
+                                SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                                 SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
 
     var cis = SpecialPowers.Ci.nsIConverterInputStream;
 
     var inputStream = SpecialPowers.Cc["@mozilla.org/intl/converter-input-stream;1"]
                        .createInstance(cis);
-    inputStream.init(chann.open(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
+    inputStream.init(chann.open2(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
     var str = {}, content = '';
     while (inputStream.readString(4096, str) != 0) {
         content += str.value;
     }
     return content;
 }
 
 function isRoughly(actual, expected, message) {
--- a/dom/base/test/test_bug424359-2.html
+++ b/dom/base/test/test_bug424359-2.html
@@ -30,24 +30,24 @@ function loadFileContent(aFile, aCharset
     var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
             .getService(SpecialPowers.Ci.nsIIOService);
     var chann = ios.newChannel2(aFile,
                                 aCharset,
                                 baseUri,
                                 null,      // aLoadingNode
                                 SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
                                 null,      // aTriggeringPrincipal
-                                SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL,
+                                SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                                 SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
 
     var cis = SpecialPowers.Ci.nsIConverterInputStream;
 
     var inputStream = SpecialPowers.Cc["@mozilla.org/intl/converter-input-stream;1"]
                        .createInstance(cis);
-    inputStream.init(chann.open(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
+    inputStream.init(chann.open2(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
     var str = {}, content = '';
     while (inputStream.readString(4096, str) != 0) {
         content += str.value;
     }
     return content;
 }
 
 function isRoughly(actual, expected, message) {
--- a/dom/base/test/test_bug498433.html
+++ b/dom/base/test/test_bug498433.html
@@ -29,24 +29,24 @@ function loadFileContent(aFile, aCharset
     var ios = SpecialPowers.Cc['@mozilla.org/network/io-service;1']
             .getService(SpecialPowers.Ci.nsIIOService);
     var chann = ios.newChannel2(aFile,
                                 aCharset,
                                 baseUri,
                                 null,      // aLoadingNode
                                 SpecialPowers.Services.scriptSecurityManager.getSystemPrincipal(),
                                 null,      // aTriggeringPrincipal
-                                SpecialPowers.Ci.nsILoadInfo.SEC_NORMAL,
+                                SpecialPowers.Ci.nsILoadInfo.SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                                 SpecialPowers.Ci.nsIContentPolicy.TYPE_OTHER);
 
     var cis = SpecialPowers.Ci.nsIConverterInputStream;
 
     var inputStream = SpecialPowers.Cc["@mozilla.org/intl/converter-input-stream;1"]
                        .createInstance(cis);
-    inputStream.init(chann.open(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
+    inputStream.init(chann.open2(), aCharset, 1024, cis.DEFAULT_REPLACEMENT_CHARACTER);
     var str = {}, content = '';
     while (inputStream.readString(4096, str) != 0) {
         content += str.value;
     }
     return content;
 }
 
 function isRoughly(actual, expected, message) {
--- a/dom/base/test/test_classList.html
+++ b/dom/base/test/test_classList.html
@@ -93,25 +93,30 @@ function checkModification(e, funcName, 
     is(gMutationEvents[0].newValue, after, "wrong new value " + contextMsg);
   }
 }
 
 function assignToClassListStrict(e) {
   "use strict";
   try {
     e.classList = "foo";
-    ok(false, "assigning to classList didn't throw");
-  } catch (e) { }
+    ok(true, "assigning to classList didn't throw");
+    e.removeAttribute("class");
+  } catch (e) { 
+    ok(false, "assigning to classList threw");
+  }
 }
 
 function assignToClassList(e) {
   try {
     var expect = e.classList;
     e.classList = "foo";
+    ok(true, "assigning to classList didn't throw");
     is(e.classList, expect, "classList should be unchanged after assignment");
+    e.removeAttribute("class");
   } catch (e) {
     ok(false, "assigning to classList threw");
   }
 }
 
 function testClassList(e) {
 
   // basic tests
@@ -155,18 +160,16 @@ function testClassList(e) {
 
   e.setAttribute("class", "a b c c b a a b c c");
   is(e.classList.length, 10, "wrong classList.length value");
 
   // [Stringifies]
 
   ok(DOMTokenList.prototype.hasOwnProperty("toString"),
      "Should have own toString on DOMTokenList")
-  ok(!DOMSettableTokenList.prototype.hasOwnProperty("toString"),
-     "Should not have own toString on DOMSettableTokenList")
 
   e.removeAttribute("class");
   is(e.classList.toString(), "", "wrong classList.toString() value");
   is(e.classList + "", "", "wrong classList string conversion value");
 
   e.setAttribute("class", "foo");
   is(e.classList.toString(), "foo", "wrong classList.toString() value");
   is(e.classList + "", "foo", "wrong classList string conversion value");
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -432,20 +432,16 @@ DOMInterfaces = {
 'DOMRectReadOnly': {
     'headerFile': 'mozilla/dom/DOMRect.h',
 },
 
 'DOMRequest': {
     'implicitJSContext': [ 'then' ],
 },
 
-'DOMSettableTokenList': {
-    'nativeType': 'nsDOMSettableTokenList',
-},
-
 'DOMStringMap': {
     'nativeType': 'nsDOMStringMap'
 },
 
 'DOMTokenList': {
     'nativeType': 'nsDOMTokenList',
 },
 
--- a/dom/cache/ManagerId.cpp
+++ b/dom/cache/ManagerId.cpp
@@ -57,17 +57,14 @@ ManagerId::~ManagerId()
   if (NS_IsMainThread()) {
     return;
   }
 
   // Otherwise we need to proxy to main thread to do the release
 
   // The PBackground worker thread shouldn't be running after the main thread
   // is stopped.  So main thread is guaranteed to exist here.
-  nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-  MOZ_ASSERT(mainThread);
-
-  NS_ProxyRelease(mainThread, mPrincipal.forget().take());
+  NS_ReleaseOnMainThread(mPrincipal.forget());
 }
 
 } // namespace cache
 } // namespace dom
 } // namespace mozilla
--- a/dom/canvas/CanvasRenderingContextHelper.cpp
+++ b/dom/canvas/CanvasRenderingContextHelper.cpp
@@ -134,27 +134,33 @@ CanvasRenderingContextHelper::CreateCont
     Telemetry::Accumulate(Telemetry::CANVAS_2D_USED, 1);
     ret = new CanvasRenderingContext2D();
     break;
 
   case CanvasContextType::WebGL1:
     Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
 
     ret = WebGL1Context::Create();
-    if (!ret)
+    if (!ret) {
+      Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_SUCCESS, 0);
       return nullptr;
+    }
+    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_SUCCESS, 1);
 
     break;
 
   case CanvasContextType::WebGL2:
     Telemetry::Accumulate(Telemetry::CANVAS_WEBGL_USED, 1);
 
     ret = WebGL2Context::Create();
-    if (!ret)
+    if (!ret) {
+      Telemetry::Accumulate(Telemetry::CANVAS_WEBGL2_SUCCESS, 0);
       return nullptr;
+    }
+    Telemetry::Accumulate(Telemetry::CANVAS_WEBGL2_SUCCESS, 1);
 
     break;
 
   case CanvasContextType::ImageBitmap:
     ret = new ImageBitmapRenderingContext();
 
     break;
   }
--- a/dom/devicestorage/DeviceStorageStatics.cpp
+++ b/dom/devicestorage/DeviceStorageStatics.cpp
@@ -855,17 +855,17 @@ DeviceStorageStatics::ListenerWrapper::L
   : mListener(do_GetWeakReference(static_cast<DOMEventTargetHelper*>(aListener)))
   , mOwningThread(NS_GetCurrentThread())
 {
 }
 
 DeviceStorageStatics::ListenerWrapper::~ListenerWrapper()
 {
   // Even weak pointers are not thread safe
-  NS_ProxyRelease(mOwningThread, mListener);
+  NS_ProxyRelease(mOwningThread, mListener.forget());
 }
 
 bool
 DeviceStorageStatics::ListenerWrapper::Equals(nsDOMDeviceStorage* aListener)
 {
   bool current = false;
   mOwningThread->IsOnCurrentThread(&current);
   if (current) {
--- a/dom/devicestorage/nsDeviceStorage.cpp
+++ b/dom/devicestorage/nsDeviceStorage.cpp
@@ -3711,18 +3711,17 @@ DeviceStorageRequestManager::~DeviceStor
   DS_LOG_INFO("%p pending %zu", this, mPending.Length());
 
   if (!mPending.IsEmpty()) {
     MOZ_ASSERT_UNREACHABLE("Should not destroy, still has pending requests");
     ListIndex i = mPending.Length();
     while (i > 0) {
       --i;
       DS_LOG_ERROR("terminate %u", mPending[i].mId);
-      NS_ProxyRelease(mOwningThread,
-        NS_ISUPPORTS_CAST(EventTarget*, mPending[i].mRequest.forget().take()));
+      NS_ProxyRelease(mOwningThread, mPending[i].mRequest.forget());
     }
   }
 }
 
 void
 DeviceStorageRequestManager::StorePermission(size_t aAccess, bool aAllow)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -867,17 +867,17 @@ nsresult nsGeolocationService::Init()
   }
 
   // geolocation service can be enabled -> now register observer
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   if (!obs) {
     return NS_ERROR_FAILURE;
   }
 
-  obs->AddObserver(this, "quit-application", false);
+  obs->AddObserver(this, "xpcom-shutdown", false);
   obs->AddObserver(this, "mozsettings-changed", false);
 
 #ifdef MOZ_ENABLE_QT5GEOPOSITION
   mProvider = new QTMLocationProvider();
 #endif
 
 #ifdef MOZ_WIDGET_ANDROID
   mProvider = new AndroidLocationProvider();
@@ -975,20 +975,20 @@ nsGeolocationService::HandleMozsettingVa
     }
 }
 
 NS_IMETHODIMP
 nsGeolocationService::Observe(nsISupports* aSubject,
                               const char* aTopic,
                               const char16_t* aData)
 {
-  if (!strcmp("quit-application", aTopic)) {
+  if (!strcmp("xpcom-shutdown", aTopic)) {
     nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
     if (obs) {
-      obs->RemoveObserver(this, "quit-application");
+      obs->RemoveObserver(this, "xpcom-shutdown");
       obs->RemoveObserver(this, "mozsettings-changed");
     }
 
     for (uint32_t i = 0; i< mGeolocators.Length(); i++) {
       mGeolocators[i]->Shutdown();
     }
     StopDevice();
 
--- a/dom/html/HTMLIFrameElement.h
+++ b/dom/html/HTMLIFrameElement.h
@@ -5,17 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_HTMLIFrameElement_h
 #define mozilla_dom_HTMLIFrameElement_h
 
 #include "mozilla/Attributes.h"
 #include "nsGenericHTMLFrameElement.h"
 #include "nsIDOMHTMLIFrameElement.h"
-#include "nsDOMSettableTokenList.h"
+#include "nsDOMTokenList.h"
 
 namespace mozilla {
 namespace dom {
 
 class HTMLIFrameElement final : public nsGenericHTMLFrameElement
                               , public nsIDOMHTMLIFrameElement
 {
 public:
@@ -79,17 +79,17 @@ public:
   void GetName(DOMString& aName)
   {
     GetHTMLAttr(nsGkAtoms::name, aName);
   }
   void SetName(const nsAString& aName, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::name, aName, aError);
   }
-  nsDOMSettableTokenList* Sandbox()
+  nsDOMTokenList* Sandbox()
   {
     return GetTokenList(nsGkAtoms::sandbox);
   }
   bool AllowFullscreen() const
   {
     return GetBoolAttr(nsGkAtoms::allowfullscreen);
   }
   void SetAllowFullscreen(bool aAllow, ErrorResult& aError)
--- a/dom/html/HTMLLinkElement.h
+++ b/dom/html/HTMLLinkElement.h
@@ -114,17 +114,17 @@ public:
   {
     SetHTMLAttr(nsGkAtoms::media, aMedia, aRv);
   }
   // XPCOM GetHreflang is fine.
   void SetHreflang(const nsAString& aHreflang, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::hreflang, aHreflang, aRv);
   }
-  nsDOMSettableTokenList* Sizes()
+  nsDOMTokenList* Sizes()
   {
     return GetTokenList(nsGkAtoms::sizes);
   }
   // XPCOM GetType is fine.
   void SetType(const nsAString& aType, ErrorResult& aRv)
   {
     SetHTMLAttr(nsGkAtoms::type, aType, aRv);
   }
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -1426,16 +1426,18 @@ NS_IMETHODIMP HTMLMediaElement::GetCurre
 {
   *aCurrentTime = CurrentTime();
   return NS_OK;
 }
 
 void
 HTMLMediaElement::FastSeek(double aTime, ErrorResult& aRv)
 {
+  LOG(LogLevel::Debug, ("Reporting telemetry VIDEO_FASTSEEK_USED"));
+  Telemetry::Accumulate(Telemetry::VIDEO_FASTSEEK_USED, 1);
   Seek(aTime, SeekTarget::PrevSyncPoint, aRv);
 }
 
 void
 HTMLMediaElement::SetCurrentTime(double aCurrentTime, ErrorResult& aRv)
 {
   Seek(aCurrentTime, SeekTarget::Accurate, aRv);
 }
--- a/dom/html/HTMLOutputElement.cpp
+++ b/dom/html/HTMLOutputElement.cpp
@@ -6,17 +6,17 @@
 
 #include "mozilla/dom/HTMLOutputElement.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/HTMLFormElement.h"
 #include "mozilla/dom/HTMLOutputElementBinding.h"
 #include "nsContentUtils.h"
-#include "nsDOMSettableTokenList.h"
+#include "nsDOMTokenList.h"
 #include "nsFormSubmission.h"
 
 NS_IMPL_NS_NEW_HTML_ELEMENT_CHECK_PARSER(Output)
 
 namespace mozilla {
 namespace dom {
 
 HTMLOutputElement::HTMLOutputElement(already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo,
@@ -161,21 +161,21 @@ void
 HTMLOutputElement::SetDefaultValue(const nsAString& aDefaultValue, ErrorResult& aRv)
 {
   mDefaultValue = aDefaultValue;
   if (mValueModeFlag == eModeDefault) {
     aRv = nsContentUtils::SetNodeTextContent(this, mDefaultValue, true);
   }
 }
 
-nsDOMSettableTokenList*
+nsDOMTokenList*
 HTMLOutputElement::HtmlFor()
 {
   if (!mTokenList) {
-    mTokenList = new nsDOMSettableTokenList(this, nsGkAtoms::_for);
+    mTokenList = new nsDOMTokenList(this, nsGkAtoms::_for);
   }
   return mTokenList;
 }
 
 void HTMLOutputElement::DescendantsChanged()
 {
   if (mIsDoneAddingChildren && mValueModeFlag == eModeDefault) {
     nsContentUtils::GetNodeTextContent(this, true, mDefaultValue);
--- a/dom/html/HTMLOutputElement.h
+++ b/dom/html/HTMLOutputElement.h
@@ -59,17 +59,17 @@ public:
   NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
 
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(HTMLOutputElement,
                                            nsGenericHTMLFormElement)
 
   virtual JSObject* WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   // WebIDL
-  nsDOMSettableTokenList* HtmlFor();
+  nsDOMTokenList* HtmlFor();
   // nsGenericHTMLFormElement::GetForm is fine.
   void GetName(nsAString& aName)
   {
     GetHTMLAttr(nsGkAtoms::name, aName);
   }
 
   void SetName(const nsAString& aName, ErrorResult& aRv)
   {
@@ -103,15 +103,15 @@ protected:
   enum ValueModeFlag {
     eModeDefault,
     eModeValue
   };
 
   ValueModeFlag                     mValueModeFlag;
   bool                              mIsDoneAddingChildren;
   nsString                          mDefaultValue;
-  RefPtr<nsDOMSettableTokenList>  mTokenList;
+  RefPtr<nsDOMTokenList>  mTokenList;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_HTMLOutputElement_h
--- a/dom/html/HTMLPropertiesCollection.cpp
+++ b/dom/html/HTMLPropertiesCollection.cpp
@@ -4,17 +4,17 @@
  * 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 "HTMLPropertiesCollection.h"
 #include "nsIDocument.h"
 #include "nsContentUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsVariant.h"
-#include "nsDOMSettableTokenList.h"
+#include "nsDOMTokenList.h"
 #include "nsAttrValue.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/dom/HTMLPropertiesCollectionBinding.h"
 #include "jsapi.h"
 #include "MainThreadUtils.h"
 #include "mozilla/Assertions.h"
 
 namespace mozilla {
--- a/dom/html/HTMLTableCellElement.h
+++ b/dom/html/HTMLTableCellElement.h
@@ -42,17 +42,17 @@ public:
   uint32_t RowSpan() const
   {
     return GetIntAttr(nsGkAtoms::rowspan, 1);
   }
   void SetRowSpan(uint32_t aRowSpan, ErrorResult& aError)
   {
     SetHTMLIntAttr(nsGkAtoms::rowspan, aRowSpan, aError);
   }
-  //already_AddRefed<nsDOMSettableTokenList> Headers() const;
+  //already_AddRefed<nsDOMTokenList> Headers() const;
   void GetHeaders(DOMString& aHeaders)
   {
     GetHTMLAttr(nsGkAtoms::headers, aHeaders);
   }
   void SetHeaders(const nsAString& aHeaders, ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::headers, aHeaders, aError);
   }
--- a/dom/html/nsGenericHTMLElement.cpp
+++ b/dom/html/nsGenericHTMLElement.cpp
@@ -91,17 +91,17 @@
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/FromParser.h"
 #include "mozilla/dom/Link.h"
 #include "mozilla/dom/UndoManager.h"
 #include "mozilla/BloomFilter.h"
 
 #include "HTMLPropertiesCollection.h"
 #include "nsVariant.h"
-#include "nsDOMSettableTokenList.h"
+#include "nsDOMTokenList.h"
 #include "nsThreadUtils.h"
 #include "nsTextFragment.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/TouchEvent.h"
 #include "mozilla/ErrorResult.h"
 #include "nsHTMLDocument.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/dom/HTMLBodyElement.h"
--- a/dom/html/nsGenericHTMLElement.h
+++ b/dom/html/nsGenericHTMLElement.h
@@ -15,17 +15,17 @@
 #include "nsGkAtoms.h"
 #include "nsContentCreatorFunctions.h"
 #include "mozilla/ErrorResult.h"
 #include "nsIDOMHTMLMenuElement.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/ValidityState.h"
 #include "mozilla/dom/ElementInlines.h"
 
-class nsDOMSettableTokenList;
+class nsDOMTokenList;
 class nsIDOMHTMLMenuElement;
 class nsIEditor;
 class nsIFormControlFrame;
 class nsIFrame;
 class nsILayoutHistoryState;
 class nsIURI;
 class nsPresState;
 struct nsSize;
@@ -99,33 +99,33 @@ public:
   bool ItemScope() const
   {
     return GetBoolAttr(nsGkAtoms::itemscope);
   }
   void SetItemScope(bool aItemScope, mozilla::ErrorResult& aError)
   {
     SetHTMLBoolAttr(nsGkAtoms::itemscope, aItemScope, aError);
   }
-  nsDOMSettableTokenList* ItemType()
+  nsDOMTokenList* ItemType()
   {
     return GetTokenList(nsGkAtoms::itemtype);
   }
   void GetItemId(nsString& aItemId)
   {
     GetHTMLURIAttr(nsGkAtoms::itemid, aItemId);
   }
   void SetItemId(const nsAString& aItemID, mozilla::ErrorResult& aError)
   {
     SetHTMLAttr(nsGkAtoms::itemid, aItemID, aError);
   }
-  nsDOMSettableTokenList* ItemRef()
+  nsDOMTokenList* ItemRef()
   {
     return GetTokenList(nsGkAtoms::itemref);
   }
-  nsDOMSettableTokenList* ItemProp()
+  nsDOMTokenList* ItemProp()
   {
     return GetTokenList(nsGkAtoms::itemprop);
   }
   mozilla::dom::HTMLPropertiesCollection* Properties();
   void GetItemValue(JSContext* aCx, JSObject* aScope,
                     JS::MutableHandle<JS::Value> aRetval,
                     mozilla::ErrorResult& aError);
   void GetItemValue(JSContext* aCx, JS::MutableHandle<JS::Value> aRetval,
--- a/dom/html/test/forms/test_output_element.html
+++ b/dom/html/test/forms/test_output_element.html
@@ -104,17 +104,17 @@ function checkFormIDLAttribute(element)
     "form IDL attribute is invalid");
 }
 
 function checkHtmlForIDLAttribute(element)
 {
   is(String(element.htmlFor), 'a b',
     "htmlFor IDL attribute should reflect the for content attribute");
 
-  // DOMSettableTokenList is tested in another bug so we just test assignation
+  // DOMTokenList is tested in another bug so we just test assignation
   element.htmlFor.value = 'a b c';
   is(String(element.htmlFor), 'a b c', "htmlFor should have changed");
 }
 
 function submitForm()
 {
   // Setting the values for the submit.
   document.getElementById('o').value = 'foo';
--- a/dom/html/test/test_bug845057.html
+++ b/dom/html/test/test_bug845057.html
@@ -21,18 +21,18 @@ https://bugzilla.mozilla.org/show_bug.cg
       attr = iframe.sandbox;
   // Security enforcement tests for iframe sandbox are in test_iframe_*
 
   function eq(a, b) {
     // check if two attributes are qual modulo permutation
     return ((a+'').split(" ").sort()+'') == ((b+'').split(" ").sort()+'');
   }
 
-  ok(attr instanceof DOMSettableTokenList,
-     "Iframe sandbox attribute is instace of DOMSettableTokenList");
+  ok(attr instanceof DOMTokenList,
+     "Iframe sandbox attribute is instace of DOMTokenList");
   ok(eq(attr, "allow-scripts") &&
      eq(iframe.getAttribute("sandbox"), "allow-scripts"),
      "Stringyfied sandbox attribute is same as that of the DOM element");
 
   ok(attr.contains("allow-scripts") && !attr.contains("allow-same-origin"),
      "Set membership of attribute elements is ok");
 
   attr.add("allow-same-origin");
--- a/dom/imptests/html/dom/test_interface-objects.html
+++ b/dom/imptests/html/dom/test_interface-objects.html
@@ -28,18 +28,17 @@ var interfaces = [
   "Comment",
   "NodeIterator",
   "TreeWalker",
   "NodeFilter",
   "NodeList",
   "HTMLCollection",
   "DOMStringList",
   "DOMTokenList",
-  "DOMSettableTokenList"
-];
+ ];
 test(function() {
   for (var p in window) {
     interfaces.forEach(function(i) {
       assert_not_equals(p, i)
     })
   }
 }, "Interface objects properties should not be Enumerable")
 interfaces.forEach(testInterfaceDeletable);
--- a/dom/imptests/html/dom/test_interfaces.html
+++ b/dom/imptests/html/dom/test_interfaces.html
@@ -244,16 +244,17 @@ interface DocumentType : Node {
 interface Element : Node {
   readonly attribute DOMString? namespaceURI;
   readonly attribute DOMString? prefix;
   readonly attribute DOMString localName;
   readonly attribute DOMString tagName;
 
            attribute DOMString id;
            attribute DOMString className;
+  [PutForwards=value]
   readonly attribute DOMTokenList classList;
 
   readonly attribute Attr[] attributes;
   DOMString? getAttribute(DOMString name);
   DOMString? getAttributeNS(DOMString? namespace, DOMString localName);
   void setAttribute(DOMString name, DOMString value);
   void setAttributeNS(DOMString? namespace, DOMString name, DOMString value);
   void removeAttribute(DOMString name);
@@ -409,20 +410,17 @@ interface HTMLCollection {
 interface DOMTokenList {
   readonly attribute unsigned long length;
   getter DOMString? item(unsigned long index);
   boolean contains(DOMString token);
   void add(DOMString... tokens);
   void remove(DOMString... tokens);
   boolean toggle(DOMString token, optional boolean force);
   stringifier;
-};
-
-interface DOMSettableTokenList : DOMTokenList {
-            attribute DOMString value;
+  attribute DOMString value;
 };
 </script>
 <script>
 "use strict";
 var xmlDoc, detachedRange, element;
 var idlArray;
 setup(function() {
 	xmlDoc = document.implementation.createDocument(null, "", null);
--- a/dom/indexedDB/test/helpers.js
+++ b/dom/indexedDB/test/helpers.js
@@ -374,17 +374,19 @@ function workerScript() {
                        diag: _diag_ });
   };
 
   self.info = function(_msg_) {
     self.postMessage({ op: "info", msg: _msg_ });
   };
 
   self.executeSoon = function(_fun_) {
-    setTimeout(_fun_, 0);
+    var channel = new MessageChannel();
+    channel.port1.postMessage("");
+    channel.port2.onmessage = function(event) { _fun_(); };
   };
 
   self.finishTest = function() {
     self.postMessage({ op: "done" });
   };
 
   self.grabEventAndContinueHandler = function(_event_) {
     testGenerator.send(_event_);
--- a/dom/indexedDB/test/unit/test_transaction_abort.js
+++ b/dom/indexedDB/test/unit/test_transaction_abort.js
@@ -1,21 +1,13 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-var disableWorkerTest =
-  "This test requires a precise 'executeSoon()' to complete reliably. On a " +
-  "worker 'executeSoon()' currently uses 'setTimeout()', and that switches " +
-  "to the timer thread and back before completing. That gives the IndexedDB " +
-  "transaction thread time to fully complete transactions and to place " +
-  "'complete' events in the worker thread's queue before the timer event, " +
-  "causing ordering problems in the spot marked 'Worker Fails Here' below.";
-
 var testGenerator = testSteps();
 
 var abortFired = false;
 
 function abortListener(evt)
 {
   abortFired = true;
   is(evt.target.error, null, "Expect a null error for an aborted transaction");
@@ -333,18 +325,16 @@ function testSteps()
       continueToNextStep();
     });
   };
   yield undefined;
 
   // During COMMITTING
   transaction = db.transaction("foo", "readwrite");
   transaction.objectStore("foo").put({hello: "world"}, 1).onsuccess = function(event) {
-    // Worker Fails Here! Due to the thread switching of 'executeSoon()' the
-    // transaction can commit and fire a 'complete' event before we continue.
     continueToNextStep();
   };
   yield undefined;
   try {
     transaction.abort();
     ok(false, "second abort should throw an error");
   }
   catch (ex) {
--- a/dom/indexedDB/test/unit/test_transaction_lifetimes.js
+++ b/dom/indexedDB/test/unit/test_transaction_lifetimes.js
@@ -1,21 +1,13 @@
 /**
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-var disableWorkerTest =
-  "This test requires a precise 'executeSoon()' to complete reliably. On a " +
-  "worker 'executeSoon()' currently uses 'setTimeout()', and that switches " +
-  "to the timer thread and back before completing. That gives the IndexedDB " +
-  "transaction thread time to fully complete transactions and to place " +
-  "'complete' events in the worker thread's queue before the timer event, " +
-  "causing ordering problems in the spot marked 'Worker Fails Here' below.";
-
 var testGenerator = testSteps();
 
 function testSteps()
 {
   let request = indexedDB.open(this.window ? window.location.pathname : "Splendid Test", 1);
   request.onerror = errorHandler;
   request.onupgradeneeded = grabEventAndContinueHandler;
   request.onsuccess = unexpectedSuccessHandler;
@@ -44,18 +36,16 @@ function testSteps()
   os = transaction.objectStore("foo");
   // Place a request to keep the transaction alive long enough for our
   // executeSoon.
   let requestComplete = false;
 
   let wasAbleToGrabObjectStoreOutsideOfCallback = false;
   let wasAbleToGrabIndexOutsideOfCallback = false;
   executeSoon(function() {
-    // Worker Fails Here! Due to the thread switching of 'executeSoon()' the
-    // transaction can commit and fire a 'complete' event before we continue.
     ok(!requestComplete, "Ordering is correct.");
     wasAbleToGrabObjectStoreOutsideOfCallback = !!transaction.objectStore("foo");
     wasAbleToGrabIndexOutsideOfCallback =
       !!transaction.objectStore("foo").index("bar");
   });
 
   request = os.add({});
   request.onerror = errorHandler;
--- a/dom/interfaces/html/nsIDOMHTMLElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLElement.idl
@@ -27,17 +27,17 @@ interface nsIDOMHTMLElement : nsIDOMElem
            attribute DOMString        lang;
            attribute DOMString        dir;
   readonly attribute nsISupports      dataset;
 
            attribute boolean                        itemScope;
            attribute nsIVariant                     itemType;
            attribute DOMString                      itemId;
   readonly attribute nsISupports                    properties;
-  // The following attributes are really nsDOMSettableTokenList, which has
+  // The following attributes are really nsDOMTokenList, which has
   // PutForwards, so we express them as nsIVariants to deal with this.
            attribute nsIVariant                     itemValue;
            attribute nsIVariant                     itemProp;
            attribute nsIVariant                     itemRef;
 
   // user interaction
   /**
    * Indicates that the element is not yet, or is no longer, relevant.
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -170,19 +170,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TabChildBase)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChildGlobal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebBrowserChrome)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(TabChildBase)
-  for (uint32_t i = 0; i < tmp->mAnonymousGlobalScopes.Length(); ++i) {
-    NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mAnonymousGlobalScopes[i])
-  }
+  tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase)
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -328,21 +328,18 @@ public:
           if (mAudioDevice) {
             mAudioDevice->GetSource()->Stop(source, kAudioTrack);
             mAudioDevice->GetSource()->Deallocate();
           }
           if (mVideoDevice) {
             mVideoDevice->GetSource()->Stop(source, kVideoTrack);
             mVideoDevice->GetSource()->Deallocate();
           }
-          // We consider ourselves finished if all tracks have been stopped, as
-          // there is no way to restart them from the JS APIs.
-          if (mBool || ((!mAudioDevice || mAudioDevice->GetSource()->IsAvailable()) &&
-                        (!mVideoDevice || mVideoDevice->GetSource()->IsAvailable()))) {
-            source->Finish();
+          if (mType == MEDIA_STOP) {
+            source->EndAllTrackAndFinish();
           }
 
           nsIRunnable *event =
             new GetUserMediaNotificationEvent(mListener,
                                               mType == MEDIA_STOP ?
                                               GetUserMediaNotificationEvent::STOPPING :
                                               GetUserMediaNotificationEvent::STOPPED_TRACK,
                                               mAudioDevice != nullptr,
@@ -681,36 +678,16 @@ public:
   {
     StopImpl();
 
     if (GetSourceStream()) {
       GetSourceStream()->Destroy();
     }
   }
 
-  // For gUM streams, we have a trackunion which assigns TrackIDs.  However, for a
-  // single-source trackunion like we have here, the TrackUnion will assign trackids
-  // that match the source's trackids, so we can avoid needing a mapping function.
-  // XXX This will not handle more complex cases well.
-  void StopTrack(TrackID aTrackID) override
-  {
-    if (GetSourceStream()) {
-      GetSourceStream()->EndTrack(aTrackID);
-      // We could override NotifyMediaStreamTrackEnded(), and maybe should, but it's
-      // risky to do late in a release since that will affect all track ends, and not
-      // just StopTrack()s.
-      RefPtr<dom::MediaStreamTrack> ownedTrack = FindOwnedDOMTrack(mOwnedStream, aTrackID);
-      if (ownedTrack) {
-        mListener->StopTrack(aTrackID, !!ownedTrack->AsAudioStreamTrack());
-      } else {
-        LOG(("StopTrack(%d) on non-existent track", aTrackID));
-      }
-    }
-  }
-
   already_AddRefed<Promise>
   ApplyConstraintsToTrack(TrackID aTrackID,
                           const MediaTrackConstraints& aConstraints,
                           ErrorResult &aRv) override
   {
     nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(mWindow);
     RefPtr<Promise> promise = Promise::Create(go, aRv);
 
@@ -2437,35 +2414,35 @@ StopSharingCallback(MediaManager *aThis,
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (aListeners) {
     auto length = aListeners->Length();
     for (size_t i = 0; i < length; ++i) {
       GetUserMediaCallbackMediaStreamListener *listener = aListeners->ElementAt(i);
 
       if (listener->Stream()) { // aka HasBeenActivate()ed
-        listener->Invalidate();
+        listener->Stop();
       }
       listener->Remove();
       listener->StopSharing();
     }
     aListeners->Clear();
     aThis->RemoveWindowID(aWindowID);
   }
 }
 
 
 void
 MediaManager::OnNavigation(uint64_t aWindowID)
 {
   MOZ_ASSERT(NS_IsMainThread());
   LOG(("OnNavigation for %llu", aWindowID));
 
-  // Invalidate this window. The runnables check this value before making
-  // a call to content.
+  // Stop the streams for this window. The runnables check this value before
+  // making a call to content.
 
   nsTArray<nsString>* callIDs;
   if (mCallIds.Get(aWindowID, &callIDs)) {
     for (auto& callID : *callIDs) {
       mActiveCallbacks.Remove(callID);
     }
     mCallIds.Remove(aWindowID);
   }
@@ -3087,56 +3064,49 @@ MediaManager::IsActivelyCapturingOrHasAP
       return false;
     }
   }
   return audio == nsIPermissionManager::ALLOW_ACTION ||
          video == nsIPermissionManager::ALLOW_ACTION;
 }
 
 void
-GetUserMediaCallbackMediaStreamListener::Invalidate()
+GetUserMediaCallbackMediaStreamListener::Stop()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread");
   if (mStopped) {
     return;
   }
 
   // We can't take a chance on blocking here, so proxy this to another
   // thread.
   // Pass a ref to us (which is threadsafe) so it can query us for the
   // source stream info.
   MediaManager::PostTask(FROM_HERE,
     new MediaOperationTask(MEDIA_STOP,
                            this, nullptr, nullptr,
                            !mAudioStopped ? mAudioDevice.get() : nullptr,
                            !mVideoStopped ? mVideoDevice.get() : nullptr,
-                           mFinished, mWindowID, nullptr));
+                           false, mWindowID, nullptr));
   mStopped = mAudioStopped = mVideoStopped = true;
 }
 
 // Doesn't kill audio
 void
 GetUserMediaCallbackMediaStreamListener::StopSharing()
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (mVideoDevice && !mStopped &&
+  if (mVideoDevice &&
       (mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Screen ||
        mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Application ||
        mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Window)) {
-    // Stop the whole stream if there's no audio; just the video track if we have both
-    if (!mAudioDevice) {
-      Invalidate();
-    } else if (!mVideoStopped) {
-      MediaManager::PostTask(FROM_HERE,
-        new MediaOperationTask(MEDIA_STOP_TRACK,
-                               this, nullptr, nullptr,
-                               nullptr, mVideoDevice,
-                               mFinished, mWindowID, nullptr));
-      mVideoStopped = true;
-    }
+    // We want to stop the whole stream if there's no audio;
+    // just the video track if we have both.
+    // StopTrack figures this out for us.
+    StopTrack(kVideoTrack);
   } else if (mAudioDevice &&
              mAudioDevice->GetMediaSource() == dom::MediaSourceEnum::AudioCapture) {
     nsCOMPtr<nsPIDOMWindowInner> window = nsGlobalWindow::GetInnerWindowWithId(mWindowID)->AsInner();
     MOZ_ASSERT(window);
     window->SetAudioCapture(false);
     MediaStreamGraph* graph =
       MediaStreamGraph::GetInstance(MediaStreamGraph::AUDIO_THREAD_DRIVER,
                                     dom::AudioChannel::Normal);
@@ -3236,46 +3206,61 @@ GetUserMediaCallbackMediaStreamListener:
     })));
   }));
   return p.forget();
 }
 
 // Stop backend for track
 
 void
-GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID, bool aIsAudio)
+GetUserMediaCallbackMediaStreamListener::StopTrack(TrackID aTrackID)
 {
   MOZ_ASSERT(NS_IsMainThread());
-  if (((aIsAudio && mAudioDevice) ||
-       (!aIsAudio && mVideoDevice)) && !mStopped)
+  MOZ_ASSERT(aTrackID == kAudioTrack || aTrackID == kVideoTrack);
+
+  // XXX to support multiple tracks of a type in a stream, this should key off
+  // the TrackID and not just hard coded values.
+
+  bool stopAudio = aTrackID == kAudioTrack;
+  bool stopVideo = aTrackID == kVideoTrack;
+
+  if (mStopped ||
+      (stopAudio && (mAudioStopped || !mAudioDevice)) ||
+      (stopVideo && (mVideoStopped || !mVideoDevice)))
   {
-    // XXX to support multiple tracks of a type in a stream, this should key off
-    // the TrackID and not just the type
-    bool stopAudio = aIsAudio && !mAudioStopped;
-    bool stopVideo = !aIsAudio && !mVideoStopped;
-    MediaManager::PostTask(FROM_HERE,
-      new MediaOperationTask(MEDIA_STOP_TRACK,
-                             this, nullptr, nullptr,
-                             stopAudio ? mAudioDevice.get() : nullptr,
-                             stopVideo ? mVideoDevice.get() : nullptr,
-                             mFinished, mWindowID, nullptr));
-    mAudioStopped |= stopAudio;
-    mVideoStopped |= stopVideo;
-  } else {
-    LOG(("gUM track %d ended, but we don't have type %s",
-         aTrackID, aIsAudio ? "audio" : "video"));
+    LOG(("Can't stop gUM track %d (%s), exists=%d, stopped=%d",
+         aTrackID,
+         aTrackID == kAudioTrack ? "audio" : "video",
+         aTrackID == kAudioTrack ? !!mAudioDevice : !!mVideoDevice,
+         aTrackID == kAudioTrack ? mAudioStopped : mVideoStopped));
+    return;
   }
+
+  if ((stopAudio || mAudioStopped || !mAudioDevice) &&
+      (stopVideo || mVideoStopped || !mVideoDevice)) {
+    Stop();
+    return;
+  }
+
+  MediaManager::PostTask(FROM_HERE,
+    new MediaOperationTask(MEDIA_STOP_TRACK,
+                           this, nullptr, nullptr,
+                           stopAudio ? mAudioDevice.get() : nullptr,
+                           stopVideo ? mVideoDevice.get() : nullptr,
+                           false , mWindowID, nullptr));
+  mAudioStopped |= stopAudio;
+  mVideoStopped |= stopVideo;
 }
 
 void
 GetUserMediaCallbackMediaStreamListener::NotifyFinished()
 {
   MOZ_ASSERT(NS_IsMainThread());
   mFinished = true;
-  Invalidate(); // we know it's been activated
+  Stop(); // we know it's been activated
 
   RefPtr<MediaManager> manager(MediaManager::GetInstance());
   manager->RemoveFromWindowList(mWindowID, this);
 }
 
 // Called from the MediaStreamGraph thread
 void
 GetUserMediaCallbackMediaStreamListener::NotifyDirectListeners(MediaStreamGraph* aGraph,
--- a/dom/media/MediaManager.h
+++ b/dom/media/MediaManager.h
@@ -161,17 +161,17 @@ public:
     if (!mStream) {
       return nullptr;
     }
     return mStream->AsSourceStream();
   }
 
   void StopSharing();
 
-  void StopTrack(TrackID aID, bool aIsAudio);
+  void StopTrack(TrackID aID);
 
   typedef media::Pledge<bool, dom::MediaStreamError*> PledgeVoid;
 
   already_AddRefed<PledgeVoid>
   ApplyConstraintsToTrack(nsPIDOMWindowInner* aWindow,
                           TrackID aID, bool aIsAudio,
                           const dom::MediaTrackConstraints& aConstraints);
 
@@ -220,17 +220,17 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
     return mVideoDevice && !mStopped &&
            mVideoDevice->GetSource()->IsAvailable() &&
            mVideoDevice->GetMediaSource() == dom::MediaSourceEnum::Browser;
   }
 
   // implement in .cpp to avoid circular dependency with MediaOperationTask
   // Can be invoked from EITHER MainThread or MSG thread
-  void Invalidate();
+  void Stop();
 
   void
   AudioConfig(bool aEchoOn, uint32_t aEcho,
               bool aAgcOn, uint32_t aAGC,
               bool aNoiseOn, uint32_t aNoise,
               int32_t aPlayoutDelay);
 
   void
@@ -313,17 +313,17 @@ private:
   // true after this listener has been removed from its MediaStream.
   // MainThread only.
   bool mRemoved;
 
   // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
   // MainThread only.
   bool mAudioStopped;
 
-  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mAudioDevice.
+  // true if we have sent MEDIA_STOP or MEDIA_STOP_TRACK for mVideoDevice.
   // MainThread only.
   bool mVideoStopped;
 
   // Set at Activate on MainThread
 
   // Accessed from MediaStreamGraph thread, MediaManager thread, and MainThread
   // No locking needed as they're only addrefed except on the MediaManager thread
   RefPtr<AudioDevice> mAudioDevice; // threadsafe refcnt
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -73,16 +73,27 @@ MediaStreamGraphImpl::~MediaStreamGraphI
 }
 
 void
 MediaStreamGraphImpl::FinishStream(MediaStream* aStream)
 {
   if (aStream->mFinished)
     return;
   STREAM_LOG(LogLevel::Debug, ("MediaStream %p will finish", aStream));
+#ifdef DEBUG
+  for (StreamBuffer::TrackIter track(aStream->mBuffer);
+         !track.IsEnded(); track.Next()) {
+    if (!track->IsEnded()) {
+      STREAM_LOG(LogLevel::Error,
+                 ("MediaStream %p will finish, but track %d has not ended.",
+                  aStream, track->GetID()));
+      NS_ASSERTION(false, "Finished stream cannot contain live track");
+    }
+  }
+#endif
   aStream->mFinished = true;
   aStream->mBuffer.AdvanceKnownTracksTime(STREAM_TIME_MAX);
 
   SetStreamOrderDirty();
 }
 
 void
 MediaStreamGraphImpl::AddStreamGraphThread(MediaStream* aStream)
@@ -969,17 +980,17 @@ MediaStreamGraphImpl::OpenAudioInput(Cub
       mGraph->OpenAudioInputImpl(mID, mListener);
     }
     MediaStreamGraphImpl *mGraph;
     // aID is a cubeb_devid, and we assume that opaque ptr is valid until
     // we close cubeb.
     CubebUtils::AudioDeviceID mID;
     RefPtr<AudioDataListener> mListener;
   };
-  this->AppendMessage(new Message(this, aID, aListener));
+  this->AppendMessage(MakeUnique<Message>(this, aID, aListener));
   return NS_OK;
 }
 
 void
 MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
 {
   mInputDeviceID = nullptr;
   mInputWanted = false;
@@ -1036,17 +1047,17 @@ MediaStreamGraphImpl::CloseAudioInput(Au
       ControlMessage(nullptr), mGraph(aGraph), mListener(aListener) {}
     virtual void Run()
     {
       mGraph->CloseAudioInputImpl(mListener);
     }
     MediaStreamGraphImpl *mGraph;
     RefPtr<AudioDataListener> mListener;
   };
-  this->AppendMessage(new Message(this, aListener));
+  this->AppendMessage(MakeUnique<Message>(this, aListener));
 }
 
 
 // All AudioInput listeners get the same speaker data (at least for now).
 void
 MediaStreamGraph::NotifyOutputData(AudioDataValue* aBuffer, size_t aFrames,
                                    uint32_t aChannels)
 {
@@ -1150,17 +1161,17 @@ MediaStreamGraphImpl::AllFinishedStreams
     if (stream->mFinished && !stream->mNotifiedFinished) {
       return false;
     }
   }
   return true;
 }
 
 void
-MediaStreamGraphImpl::RunMessageAfterProcessing(nsAutoPtr<ControlMessage> aMessage)
+MediaStreamGraphImpl::RunMessageAfterProcessing(UniquePtr<ControlMessage> aMessage)
 {
   MOZ_ASSERT(CurrentDriver()->OnThread());
 
   if (mFrontMessageQueue.IsEmpty()) {
     mFrontMessageQueue.AppendElement();
   }
 
   // Only one block is used for messages from the graph thread.
@@ -1170,17 +1181,17 @@ MediaStreamGraphImpl::RunMessageAfterPro
 
 void
 MediaStreamGraphImpl::RunMessagesInQueue()
 {
   // Calculate independent action times for each batch of messages (each
   // batch corresponding to an event loop task). This isolates the performance
   // of different scripts to some extent.
   for (uint32_t i = 0; i < mFrontMessageQueue.Length(); ++i) {
-    nsTArray<nsAutoPtr<ControlMessage> >& messages = mFrontMessageQueue[i].mMessages;
+    nsTArray<UniquePtr<ControlMessage>>& messages = mFrontMessageQueue[i].mMessages;
 
     for (uint32_t j = 0; j < messages.Length(); ++j) {
       messages[j]->Run();
     }
   }
   mFrontMessageQueue.Clear();
 }
 
@@ -1527,17 +1538,17 @@ void
 MediaStreamGraphImpl::RunInStableState(bool aSourceIsMSG)
 {
   NS_ASSERTION(NS_IsMainThread(), "Must be called on main thread");
 
   nsTArray<nsCOMPtr<nsIRunnable> > runnables;
   // When we're doing a forced shutdown, pending control messages may be
   // run on the main thread via RunDuringShutdown. Those messages must
   // run without the graph monitor being held. So, we collect them here.
-  nsTArray<nsAutoPtr<ControlMessage> > controlMessagesToRunDuringShutdown;
+  nsTArray<UniquePtr<ControlMessage>> controlMessagesToRunDuringShutdown;
 
   {
     MonitorAutoLock lock(mMonitor);
     if (aSourceIsMSG) {
       MOZ_ASSERT(mPostedRunInStableStateEvent);
       mPostedRunInStableStateEvent = false;
     }
 
@@ -1705,17 +1716,17 @@ MediaStreamGraphImpl::EnsureStableStateE
   if (mPostedRunInStableStateEvent)
     return;
   mPostedRunInStableStateEvent = true;
   nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable(this, true);
   NS_DispatchToMainThread(event.forget());
 }
 
 void
-MediaStreamGraphImpl::AppendMessage(ControlMessage* aMessage)
+MediaStreamGraphImpl::AppendMessage(UniquePtr<ControlMessage> aMessage)
 {
   MOZ_ASSERT(NS_IsMainThread(), "main thread only");
   MOZ_ASSERT(!aMessage->GetStream() ||
              !aMessage->GetStream()->IsDestroyed(),
              "Stream already destroyed");
 
   if (mDetectedNotRunning &&
       mLifecycleState > LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
@@ -1728,31 +1739,30 @@ MediaStreamGraphImpl::AppendMessage(Cont
 #ifdef DEBUG
     MOZ_ASSERT(mCanRunMessagesSynchronously);
     mCanRunMessagesSynchronously = false;
 #endif
     aMessage->RunDuringShutdown();
 #ifdef DEBUG
     mCanRunMessagesSynchronously = true;
 #endif
-    delete aMessage;
     if (IsEmpty() &&
         mLifecycleState >= LIFECYCLE_WAITING_FOR_STREAM_DESTRUCTION) {
 
       MediaStreamGraphImpl* graph;
       if (gGraphs.Get(uint32_t(mAudioChannel), &graph) && graph == this) {
         gGraphs.Remove(uint32_t(mAudioChannel));
       }
 
       Destroy();
     }
     return;
   }
 
-  mCurrentTaskMessageQueue.AppendElement(aMessage);
+  mCurrentTaskMessageQueue.AppendElement(Move(aMessage));
   EnsureRunInStableState();
 }
 
 MediaStream::MediaStream(DOMMediaStream* aWrapper)
   : mBufferStartTime(0)
   , mStartBlocking(GRAPH_TIME_MAX)
   , mSuspendedCount(0)
   , mFinished(false)
@@ -1920,17 +1930,17 @@ MediaStream::Destroy()
       auto graph = mStream->GraphImpl();
       mStream->DestroyImpl();
       graph->RemoveStreamGraphThread(mStream);
     }
     void RunDuringShutdown() override
     { Run(); }
   };
   mWrapper = nullptr;
-  GraphImpl()->AppendMessage(new Message(this));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this));
   // Message::RunDuringShutdown may have removed this stream from the graph,
   // but our kungFuDeathGrip above will have kept this stream alive if
   // necessary.
   mMainThreadDestroyed = true;
 }
 
 void
 MediaStream::AddAudioOutput(void* aKey)
@@ -1939,17 +1949,17 @@ MediaStream::AddAudioOutput(void* aKey)
   public:
     Message(MediaStream* aStream, void* aKey) : ControlMessage(aStream), mKey(aKey) {}
     void Run() override
     {
       mStream->AddAudioOutputImpl(mKey);
     }
     void* mKey;
   };
-  GraphImpl()->AppendMessage(new Message(this, aKey));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aKey));
 }
 
 void
 MediaStream::SetAudioOutputVolumeImpl(void* aKey, float aVolume)
 {
   for (uint32_t i = 0; i < mAudioOutputs.Length(); ++i) {
     if (mAudioOutputs[i].mKey == aKey) {
       mAudioOutputs[i].mVolume = aVolume;
@@ -1968,17 +1978,17 @@ MediaStream::SetAudioOutputVolume(void* 
       ControlMessage(aStream), mKey(aKey), mVolume(aVolume) {}
     void Run() override
     {
       mStream->SetAudioOutputVolumeImpl(mKey, mVolume);
     }
     void* mKey;
     float mVolume;
   };
-  GraphImpl()->AppendMessage(new Message(this, aKey, aVolume));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aKey, aVolume));
 }
 
 void
 MediaStream::AddAudioOutputImpl(void* aKey)
 {
   STREAM_LOG(LogLevel::Info, ("MediaStream %p Adding AudioOutput for key %p",
                               this, aKey));
   mAudioOutputs.AppendElement(AudioOutput(aKey));
@@ -2006,17 +2016,17 @@ MediaStream::RemoveAudioOutput(void* aKe
     Message(MediaStream* aStream, void* aKey) :
       ControlMessage(aStream), mKey(aKey) {}
     void Run() override
     {
       mStream->RemoveAudioOutputImpl(mKey);
     }
     void* mKey;
   };
-  GraphImpl()->AppendMessage(new Message(this, aKey));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aKey));
 }
 
 void
 MediaStream::AddVideoOutputImpl(already_AddRefed<VideoFrameContainer> aContainer)
 {
   RefPtr<VideoFrameContainer> container = aContainer;
   STREAM_LOG(LogLevel::Info, ("MediaStream %p Adding VideoFrameContainer %p as output",
                               this, container.get()));
@@ -2042,33 +2052,33 @@ MediaStream::AddVideoOutput(VideoFrameCo
     Message(MediaStream* aStream, VideoFrameContainer* aContainer) :
       ControlMessage(aStream), mContainer(aContainer) {}
     void Run() override
     {
       mStream->AddVideoOutputImpl(mContainer.forget());
     }
     RefPtr<VideoFrameContainer> mContainer;
   };
-  GraphImpl()->AppendMessage(new Message(this, aContainer));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aContainer));
 }
 
 void
 MediaStream::RemoveVideoOutput(VideoFrameContainer* aContainer)
 {
   class Message : public ControlMessage {
   public:
     Message(MediaStream* aStream, VideoFrameContainer* aContainer) :
       ControlMessage(aStream), mContainer(aContainer) {}
     void Run() override
     {
       mStream->RemoveVideoOutputImpl(mContainer);
     }
     RefPtr<VideoFrameContainer> mContainer;
   };
-  GraphImpl()->AppendMessage(new Message(this, aContainer));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aContainer));
 }
 
 void
 MediaStream::Suspend()
 {
   class Message : public ControlMessage {
   public:
     explicit Message(MediaStream* aStream) :
@@ -2079,17 +2089,17 @@ MediaStream::Suspend()
     }
   };
 
   // This can happen if this method has been called asynchronously, and the
   // stream has been destroyed since then.
   if (mMainThreadDestroyed) {
     return;
   }
-  GraphImpl()->AppendMessage(new Message(this));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this));
 }
 
 void
 MediaStream::Resume()
 {
   class Message : public ControlMessage {
   public:
     explicit Message(MediaStream* aStream) :
@@ -2100,17 +2110,17 @@ MediaStream::Resume()
     }
   };
 
   // This can happen if this method has been called asynchronously, and the
   // stream has been destroyed since then.
   if (mMainThreadDestroyed) {
     return;
   }
-  GraphImpl()->AppendMessage(new Message(this));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this));
 }
 
 void
 MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
 {
   MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
   listener->NotifyBlockingChanged(GraphImpl(),
     mNotifiedBlocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
@@ -2130,17 +2140,17 @@ MediaStream::AddListener(MediaStreamList
     Message(MediaStream* aStream, MediaStreamListener* aListener) :
       ControlMessage(aStream), mListener(aListener) {}
     void Run() override
     {
       mStream->AddListenerImpl(mListener.forget());
     }
     RefPtr<MediaStreamListener> mListener;
   };
-  GraphImpl()->AppendMessage(new Message(this, aListener));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener));
 }
 
 void
 MediaStream::RemoveListenerImpl(MediaStreamListener* aListener)
 {
   // wouldn't need this if we could do it in the opposite order
   RefPtr<MediaStreamListener> listener(aListener);
   mListeners.RemoveElement(aListener);
@@ -2158,17 +2168,17 @@ MediaStream::RemoveListener(MediaStreamL
     {
       mStream->RemoveListenerImpl(mListener);
     }
     RefPtr<MediaStreamListener> mListener;
   };
   // If the stream is destroyed the Listeners have or will be
   // removed.
   if (!IsDestroyed()) {
-    GraphImpl()->AppendMessage(new Message(this, aListener));
+    GraphImpl()->AppendMessage(MakeUnique<Message>(this, aListener));
   }
 }
 
 void
 MediaStream::RunAfterPendingUpdates(already_AddRefed<nsIRunnable> aRunnable)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MediaStreamGraphImpl* graph = GraphImpl();
@@ -2198,17 +2208,17 @@ MediaStream::RunAfterPendingUpdates(alre
       // assume that there are no remaining controlMessagesToRunDuringShutdown.
       MOZ_ASSERT(NS_IsMainThread());
       NS_DispatchToCurrentThread(mRunnable);
     }
   private:
     nsCOMPtr<nsIRunnable> mRunnable;
   };
 
-  graph->AppendMessage(new Message(this, runnable.forget()));
+  graph->AppendMessage(MakeUnique<Message>(this, runnable.forget()));
 }
 
 void
 MediaStream::SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled)
 {
   if (aEnabled) {
     mDisabledTrackIDs.RemoveElement(aTrackID);
   } else {
@@ -2227,17 +2237,17 @@ MediaStream::SetTrackEnabled(TrackID aTr
       ControlMessage(aStream), mTrackID(aTrackID), mEnabled(aEnabled) {}
     void Run() override
     {
       mStream->SetTrackEnabledImpl(mTrackID, mEnabled);
     }
     TrackID mTrackID;
     bool mEnabled;
   };
-  GraphImpl()->AppendMessage(new Message(this, aTrackID, aEnabled));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackID, aEnabled));
 }
 
 void
 MediaStream::ApplyTrackDisabling(TrackID aTrackID, MediaSegment* aSegment, MediaSegment* aRawSegment)
 {
   if (!mDisabledTrackIDs.Contains(aTrackID)) {
     return;
   }
@@ -2434,17 +2444,17 @@ SourceMediaStream::NotifyListenersEvent(
     Message(SourceMediaStream* aStream, MediaStreamListener::MediaStreamGraphEvent aEvent) :
       ControlMessage(aStream), mEvent(aEvent) {}
     void Run() override
       {
         mStream->AsSourceStream()->NotifyListenersEventImpl(mEvent);
       }
     MediaStreamListener::MediaStreamGraphEvent mEvent;
   };
-  GraphImpl()->AppendMessage(new Message(this, aNewEvent));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aNewEvent));
 }
 
 void
 SourceMediaStream::AddDirectListener(MediaStreamDirectListener* aListener)
 {
   bool wasEmpty;
   {
     MutexAutoLock lock(mMutex);
@@ -2606,17 +2616,17 @@ MediaInputPort::Destroy()
       NS_RELEASE(mPort);
     }
     void RunDuringShutdown() override
     {
       Run();
     }
     MediaInputPort* mPort;
   };
-  GraphImpl()->AppendMessage(new Message(this));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this));
 }
 
 MediaStreamGraphImpl*
 MediaInputPort::GraphImpl()
 {
   return mGraph;
 }
 
@@ -2656,17 +2666,17 @@ MediaInputPort::BlockTrackId(TrackID aTr
       Run();
     }
     RefPtr<MediaInputPort> mPort;
     TrackID mTrackId;
   };
 
   MOZ_ASSERT(aTrackId != TRACK_NONE && aTrackId != TRACK_INVALID && aTrackId != TRACK_ANY,
              "Only explicit TrackID is allowed");
-  GraphImpl()->AppendMessage(new Message(this, aTrackId));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aTrackId));
 }
 
 already_AddRefed<MediaInputPort>
 ProcessedMediaStream::AllocateInputPort(MediaStream* aStream, TrackID aTrackID,
                                         uint16_t aInputNumber, uint16_t aOutputNumber)
 {
   // This method creates two references to the MediaInputPort: one for
   // the main thread, and one for the MediaStreamGraph.
@@ -2690,49 +2700,49 @@ ProcessedMediaStream::AllocateInputPort(
   };
 
   MOZ_ASSERT(aStream->GraphImpl() == GraphImpl());
   MOZ_ASSERT(aTrackID != TRACK_NONE && aTrackID != TRACK_INVALID,
              "Only TRACK_ANY and explicit ID are allowed");
   RefPtr<MediaInputPort> port = new MediaInputPort(aStream, aTrackID, this,
                                                      aInputNumber, aOutputNumber);
   port->SetGraphImpl(GraphImpl());
-  GraphImpl()->AppendMessage(new Message(port));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(port));
   return port.forget();
 }
 
 void
 ProcessedMediaStream::Finish()
 {
   class Message : public ControlMessage {
   public:
     explicit Message(ProcessedMediaStream* aStream)
       : ControlMessage(aStream) {}
     void Run() override
     {
       mStream->GraphImpl()->FinishStream(mStream);
     }
   };
-  GraphImpl()->AppendMessage(new Message(this));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this));
 }
 
 void
 ProcessedMediaStream::SetAutofinish(bool aAutofinish)
 {
   class Message : public ControlMessage {
   public:
     Message(ProcessedMediaStream* aStream, bool aAutofinish)
       : ControlMessage(aStream), mAutofinish(aAutofinish) {}
     void Run() override
     {
       static_cast<ProcessedMediaStream*>(mStream)->SetAutofinishImpl(mAutofinish);
     }
     bool mAutofinish;
   };
-  GraphImpl()->AppendMessage(new Message(this, aAutofinish));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aAutofinish));
 }
 
 void
 ProcessedMediaStream::DestroyImpl()
 {
   for (int32_t i = mInputs.Length() - 1; i >= 0; --i) {
     mInputs[i]->Disconnect();
   }
@@ -3010,17 +3020,17 @@ MediaStreamGraph::CreateAudioCaptureStre
 }
 
 void
 MediaStreamGraph::AddStream(MediaStream* aStream)
 {
   NS_ADDREF(aStream);
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   aStream->SetGraphImpl(graph);
-  graph->AppendMessage(new CreateMessage(aStream));
+  graph->AppendMessage(MakeUnique<CreateMessage>(aStream));
 }
 
 class GraphStartedRunnable final : public nsRunnable
 {
 public:
   GraphStartedRunnable(AudioNodeStream* aStream, MediaStreamGraph* aGraph)
   : mStream(aStream)
   , mGraph(aGraph)
@@ -3067,17 +3077,17 @@ MediaStreamGraph::NotifyWhenGraphStarted
     }
     void RunDuringShutdown() override
     {
     }
   };
 
   if (!aStream->IsDestroyed()) {
     MediaStreamGraphImpl* graphImpl = static_cast<MediaStreamGraphImpl*>(this);
-    graphImpl->AppendMessage(new GraphStartedNotificationControlMessage(aStream));
+    graphImpl->AppendMessage(MakeUnique<GraphStartedNotificationControlMessage>(aStream));
   }
 }
 
 void
 MediaStreamGraphImpl::IncrementSuspendCount(MediaStream* aStream)
 {
   if (!aStream->IsSuspended()) {
     MOZ_ASSERT(mStreams.Contains(aStream));
@@ -3286,18 +3296,18 @@ MediaStreamGraph::ApplyAudioContextOpera
     // doesn't.
     nsTArray<MediaStream*> mStreams;
     AudioContextOperation mAudioContextOperation;
     void* mPromise;
   };
 
   MediaStreamGraphImpl* graphImpl = static_cast<MediaStreamGraphImpl*>(this);
   graphImpl->AppendMessage(
-    new AudioContextOperationControlMessage(aDestinationStream, aStreams,
-                                            aOperation, aPromise));
+    MakeUnique<AudioContextOperationControlMessage>(aDestinationStream, aStreams,
+                                                    aOperation, aPromise));
 }
 
 bool
 MediaStreamGraph::IsNonRealtime() const
 {
   const MediaStreamGraphImpl* impl = static_cast<const MediaStreamGraphImpl*>(this);
   MediaStreamGraphImpl* graph;
 
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -10,16 +10,17 @@
 
 #include "mozilla/Monitor.h"
 #include "mozilla/TimeStamp.h"
 #include "nsIMemoryReporter.h"
 #include "nsIThread.h"
 #include "nsIRunnable.h"
 #include "nsIAsyncShutdown.h"
 #include "Latency.h"
+#include "mozilla/UniquePtr.h"
 #include "mozilla/WeakPtr.h"
 #include "GraphDriver.h"
 #include "AudioMixer.h"
 
 namespace mozilla {
 
 template <typename T>
 class LinkedList;
@@ -75,17 +76,17 @@ protected:
   // a reference to the stream until the Destroy message is processed. The
   // last message referencing a stream is the Destroy message for that stream.
   MediaStream* mStream;
 };
 
 class MessageBlock
 {
 public:
-  nsTArray<nsAutoPtr<ControlMessage> > mMessages;
+  nsTArray<UniquePtr<ControlMessage>> mMessages;
 };
 
 /**
  * The implementation of a media stream graph. This class is private to this
  * file. It's not in the anonymous namespace because MediaStream needs to
  * be able to friend it.
  *
  * Currently we have one global instance per process, and one per each
@@ -134,17 +135,17 @@ public:
   /**
    * Called to apply a StreamUpdate to its stream.
    */
   void ApplyStreamUpdate(StreamUpdate* aUpdate);
   /**
    * Append a ControlMessage to the message queue. This queue is drained
    * during RunInStableState; the messages will run on the graph thread.
    */
-  void AppendMessage(ControlMessage* aMessage);
+  void AppendMessage(UniquePtr<ControlMessage> aMessage);
 
   // Shutdown helpers.
 
   static already_AddRefed<nsIAsyncShutdownClient>
   GetShutdownBarrier()
   {
     nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdown();
     MOZ_RELEASE_ASSERT(svc);
@@ -288,17 +289,17 @@ public:
                            bool* aEnsureNextIteration);
 
   /**
    * For use during ProcessedMediaStream::ProcessInput() or
    * MediaStreamListener callbacks, when graph state cannot be changed.
    * Schedules |aMessage| to run after processing, at a time when graph state
    * can be changed.  Graph thread.
    */
-  void RunMessageAfterProcessing(nsAutoPtr<ControlMessage> aMessage);
+  void RunMessageAfterProcessing(UniquePtr<ControlMessage> aMessage);
 
   /**
    * Called when a suspend/resume/close operation has been completed, on the
    * graph thread.
    */
   void AudioContextOperationCompleted(MediaStream* aStream,
                                       void* aPromise,
                                       dom::AudioContextOperation aOperation);
@@ -743,17 +744,17 @@ public:
   // Main thread only
 
   /**
    * Messages posted by the current event loop task. These are forwarded to
    * the media graph thread during RunInStableState. We can't forward them
    * immediately because we want all messages between stable states to be
    * processed as an atomic batch.
    */
-  nsTArray<nsAutoPtr<ControlMessage> > mCurrentTaskMessageQueue;
+  nsTArray<UniquePtr<ControlMessage>> mCurrentTaskMessageQueue;
   /**
    * True when RunInStableState has determined that mLifecycleState is >
    * LIFECYCLE_RUNNING. Since only the main thread can reset mLifecycleState to
    * LIFECYCLE_RUNNING, this can be relied on to not change unexpectedly.
    */
   bool mDetectedNotRunning;
   /**
    * True when a stable state runner has been posted to the appshell to run
--- a/dom/media/gmp/GMPParent.cpp
+++ b/dom/media/gmp/GMPParent.cpp
@@ -2,20 +2,16 @@
 /* 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 "GMPParent.h"
 #include "mozilla/Logging.h"
 #include "nsComponentManagerUtils.h"
 #include "nsComponentManagerUtils.h"
-#include "nsIInputStream.h"
-#include "nsILineInputStream.h"
-#include "nsNetUtil.h"
-#include "nsCharSeparatedTokenizer.h"
 #include "nsThreadUtils.h"
 #include "nsIRunnable.h"
 #include "nsIWritablePropertyBag2.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "mozilla/ipc/GeckoChildProcessHost.h"
 #include "mozilla/SyncRunnable.h"
 #include "mozilla/unused.h"
 #include "nsIObserverService.h"
@@ -729,117 +725,55 @@ bool
 GMPParent::DeallocPGMPTimerParent(PGMPTimerParent* aActor)
 {
   GMPTimerParent* p = static_cast<GMPTimerParent*>(aActor);
   p->Shutdown();
   mTimers.RemoveElement(p);
   return true;
 }
 
-nsresult
-ParseNextRecord(nsILineInputStream* aLineInputStream,
-                const nsCString& aPrefix,
-                nsCString& aResult,
-                bool& aMoreLines)
+bool
+ReadRequiredField(GMPInfoFileParser& aParser, const nsCString& aKey, nsACString& aOutValue)
 {
-  nsAutoCString record;
-  nsresult rv = aLineInputStream->ReadLine(record, &aMoreLines);
-  if (NS_FAILED(rv)) {
-    return rv;
+  if (!aParser.Contains(aKey) || aParser.Get(aKey).IsEmpty()) {
+    return false;
   }
-
-  if (record.Length() <= aPrefix.Length() ||
-      !Substring(record, 0, aPrefix.Length()).Equals(aPrefix)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  aResult = Substring(record, aPrefix.Length());
-  aResult.Trim("\b\t\r\n ");
-
-  return NS_OK;
+  aOutValue = aParser.Get(aKey);
+  return true;
 }
 
 nsresult
 GMPParent::ReadGMPMetaData()
 {
   MOZ_ASSERT(mDirectory, "Plugin directory cannot be NULL!");
   MOZ_ASSERT(!mName.IsEmpty(), "Plugin mName cannot be empty!");
 
   nsCOMPtr<nsIFile> infoFile;
   nsresult rv = mDirectory->Clone(getter_AddRefs(infoFile));
   if (NS_FAILED(rv)) {
     return rv;
   }
   infoFile->AppendRelativePath(mName + NS_LITERAL_STRING(".info"));
 
-  nsCOMPtr<nsIInputStream> inputStream;
-  rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), infoFile);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(inputStream, &rv);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-
-  nsCString value;
-  bool moreLines = false;
-
-  // 'Name:' record
-  nsCString prefix = NS_LITERAL_CSTRING("Name:");
-  rv = ParseNextRecord(lineInputStream, prefix, value, moreLines);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  if (value.IsEmpty()) {
-    // Not OK for name to be empty. Must have one non-whitespace character.
-    return NS_ERROR_FAILURE;
-  }
-  mDisplayName = value;
-
-  // 'Description:' record
-  if (!moreLines) {
+  GMPInfoFileParser parser;
+  if (!parser.Init(infoFile)) {
     return NS_ERROR_FAILURE;
   }
-  prefix = NS_LITERAL_CSTRING("Description:");
-  rv = ParseNextRecord(lineInputStream, prefix, value, moreLines);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  mDescription = value;
 
-  // 'Version:' record
-  if (!moreLines) {
+  nsAutoCString apis;
+  if (!ReadRequiredField(parser, NS_LITERAL_CSTRING("name"), mDisplayName) ||
+      !ReadRequiredField(parser, NS_LITERAL_CSTRING("description"), mDescription) ||
+      !ReadRequiredField(parser, NS_LITERAL_CSTRING("version"), mVersion) ||
+      !ReadRequiredField(parser, NS_LITERAL_CSTRING("apis"), apis)) {
     return NS_ERROR_FAILURE;
   }
-  prefix = NS_LITERAL_CSTRING("Version:");
-  rv = ParseNextRecord(lineInputStream, prefix, value, moreLines);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  mVersion = value;
 
-  // 'Capability:' record
-  if (!moreLines) {
-    return NS_ERROR_FAILURE;
-  }
-  prefix = NS_LITERAL_CSTRING("APIs:");
-  rv = ParseNextRecord(lineInputStream, prefix, value, moreLines);
-  if (NS_FAILED(rv)) {
-    return rv;
-  }
-  nsCCharSeparatedTokenizer apiTokens(value, ',');
-  while (apiTokens.hasMoreTokens()) {
-    nsAutoCString api(apiTokens.nextToken());
-    api.StripWhitespace();
-    if (api.IsEmpty()) {
-      continue;
-    }
-
+  nsTArray<nsCString> apiTokens;
+  SplitAt(", ", apis, apiTokens);
+  for (nsCString api : apiTokens) {
     int32_t tagsStart = api.FindChar('[');
     if (tagsStart == 0) {
       // Not allowed to be the first character.
       // API name must be at least one character.
       continue;
     }
 
     auto cap = new GMPCapability();
@@ -853,19 +787,19 @@ GMPParent::ReadGMPMetaData()
         delete cap;
         continue;
       }
 
       cap->mAPIName.Assign(Substring(api, 0, tagsStart));
 
       if ((tagsEnd - tagsStart) > 1) {
         const nsDependentCSubstring ts(Substring(api, tagsStart + 1, tagsEnd - tagsStart - 1));
-        nsCCharSeparatedTokenizer tagTokens(ts, ':');
-        while (tagTokens.hasMoreTokens()) {
-          const nsDependentCSubstring tag(tagTokens.nextToken());
+        nsTArray<nsCString> tagTokens;
+        SplitAt(":", ts, tagTokens);
+        for (nsCString tag : tagTokens) {
           cap->mAPITags.AppendElement(tag);
         }
       }
     }
 
     // We support the current GMPDecryptor version, and the previous.
     // We Adapt the previous to the current in the GMPContentChild.
     if (cap->mAPIName.EqualsLiteral(GMP_API_DECRYPTOR_BACKWARDS_COMPAT)) {
--- a/dom/media/gmp/GMPUtils.cpp
+++ b/dom/media/gmp/GMPUtils.cpp
@@ -164,25 +164,29 @@ GMPInfoFileParser::Init(nsIFile* aInfoFi
     return false;
   }
 
   // Note: we pass "\r\n" to SplitAt so that we'll split lines delimited
   // by \n (Unix), \r\n (Windows) and \r (old MacOSX).
   SplitAt("\r\n", info, lines);
 
   for (nsCString line : lines) {
-    nsTArray<nsCString> tokens;
-    SplitAt(":", line, tokens);
-    if (tokens.Length() < 2) {
+    // Field name is the string up to but not including the first ':'
+    // character on the line.
+    int32_t colon = line.FindChar(':');
+    if (colon <= 0) {
+      // Not allowed to be the first character.
+      // Info field name must be at least one character.
       continue;
     }
-    nsCString key = tokens[0];
+    nsAutoCString key(Substring(line, 0, colon));
     ToLowerCase(key);
     key.Trim(" ");
-    nsCString* value = new nsCString(tokens[1]);
+
+    nsCString* value = new nsCString(Substring(line, colon + 1));
     value->Trim(" ");
     mValues.Put(key, value); // Hashtable assumes ownership of value.
   }
 
   return true;
 }
 
 bool
--- a/dom/media/systemservices/CamerasChild.h
+++ b/dom/media/systemservices/CamerasChild.h
@@ -8,16 +8,17 @@
 #define mozilla_CamerasChild_h
 
 #include "mozilla/Move.h"
 #include "mozilla/Pair.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/camera/PCamerasChild.h"
 #include "mozilla/camera/PCamerasParent.h"
 #include "mozilla/Mutex.h"
+#include "base/singleton.h"
 #include "nsCOMPtr.h"
 
 // conflicts with #include of scoped_ptr.h
 #undef FF
 #include "webrtc/common.h"
 // Video Engine
 #include "webrtc/video_engine/include/vie_base.h"
 #include "webrtc/video_engine/include/vie_capture.h"
@@ -74,34 +75,31 @@ template <class T> class LockAndDispatch
 // something like device enumeration to complete.
 
 class CamerasSingleton {
 public:
   CamerasSingleton();
   ~CamerasSingleton();
 
   static OffTheBooksMutex& Mutex() {
-    return GetInstance().mCamerasMutex;
+    return gTheInstance.get()->mCamerasMutex;
   }
 
   static CamerasChild*& Child() {
     Mutex().AssertCurrentThreadOwns();
-    return GetInstance().mCameras;
+    return gTheInstance.get()->mCameras;
   }
 
   static nsCOMPtr<nsIThread>& Thread() {
     Mutex().AssertCurrentThreadOwns();
-    return GetInstance().mCamerasChildThread;
+    return gTheInstance.get()->mCamerasChildThread;
   }
 
 private:
-  static CamerasSingleton& GetInstance() {
-    static CamerasSingleton instance;
-    return instance;
-  }
+  static Singleton<CamerasSingleton> gTheInstance;
 
   // Reinitializing CamerasChild will change the pointers below.
   // We don't want this to happen in the middle of preparing IPC.
   // We will be alive on destruction, so this needs to be off the books.
   mozilla::OffTheBooksMutex mCamerasMutex;
 
   // This is owned by the IPC code, and the same code controls the lifetime.
   // It will set and clear this pointer as appropriate in setup/teardown.
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -87,12 +87,11 @@ load audiocontext-double-suspend.html
 load buffer-source-duration-1.html
 load buffer-source-ended-1.html
 load buffer-source-resampling-start-1.html
 load doppler-1.html
 HTTP load media-element-source-seek-1.html
 load offline-buffer-source-ended-1.html
 load oscillator-ended-1.html
 load oscillator-ended-2.html
-skip-if(winWidget) load video-replay-after-audio-end.html
 
 # This needs to run at the end to avoid leaking busted state into other tests.
 skip-if(B2G) load 691096-1.html # bug 852821
deleted file mode 100644
index 9532113d87ae59497ab5e9482615c57cc51c8672..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
--- a/dom/media/test/crashtests/video-replay-after-audio-end.html
+++ /dev/null
@@ -1,43 +0,0 @@
-<html class="reftest-wait">
-<head>
-  <title> Bug 1242774 : video crashed if pause and play again after audio track ends </title>
-</head>
-<body>
-<script type="text/javascript">
-function assert(value, msg) {
-  if (!value) {
-    dump("### Error : " + msg + "\n");
-  }
-}
-
-var AUDIO_END_TIME = 4.5;
-var video = document.createElement('video');
-video.src = "video-crash.webm";
-video.play();
-
-video.ontimeupdate = function () {
-  assert(AUDIO_END_TIME < video.duration,
-         "AUDIO_END_TIME should be smaller than the duration!");
-
-  if (video.currentTime > AUDIO_END_TIME) {
-    dump("### Pause video during silent part.\n");
-    video.ontimeupdate = null;
-    video.pause();
-  }
-
-  video.onpause = function () {
-    video.onpause = null;
-    setTimeout(function() {
-      dump("### Re-play after pausing during silent part.\n");
-      video.play();
-      video.onended = function () {
-        video.onended = null;
-        dump("### Video is ended.\n");
-        document.documentElement.removeAttribute("class");
-      }
-    }, 1000);
-  }
-}
-</script>
-</body>
-</html>
\ No newline at end of file
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -142,19 +142,19 @@ AudioNodeStream::SetStreamTimeParameter(
       static_cast<AudioNodeStream*>(mStream)->
           SetStreamTimeParameterImpl(mIndex, mRelativeToStream, mStreamTime);
     }
     double mStreamTime;
     MediaStream* mRelativeToStream;
     uint32_t mIndex;
   };
 
-  GraphImpl()->AppendMessage(new Message(this, aIndex,
-                                         aContext->DestinationStream(),
-                                         aStreamTime));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex,
+                                                 aContext->DestinationStream(),
+                                                 aStreamTime));
 }
 
 void
 AudioNodeStream::SetStreamTimeParameterImpl(uint32_t aIndex, MediaStream* aRelativeToStream,
                                             double aStreamTime)
 {
   StreamTime ticks = aRelativeToStream->SecondsToNearestStreamTime(aStreamTime);
   mEngine->SetStreamTimeParameter(aIndex, ticks);
@@ -173,17 +173,17 @@ AudioNodeStream::SetDoubleParameter(uint
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetDoubleParameter(mIndex, mValue);
     }
     double mValue;
     uint32_t mIndex;
   };
 
-  GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
 }
 
 void
 AudioNodeStream::SetInt32Parameter(uint32_t aIndex, int32_t aValue)
 {
   class Message final : public ControlMessage
   {
   public:
@@ -194,17 +194,17 @@ AudioNodeStream::SetInt32Parameter(uint3
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetInt32Parameter(mIndex, mValue);
     }
     int32_t mValue;
     uint32_t mIndex;
   };
 
-  GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
 }
 
 void
 AudioNodeStream::SendTimelineEvent(uint32_t aIndex,
                                    const AudioTimelineEvent& aEvent)
 {
   class Message final : public ControlMessage
   {
@@ -220,17 +220,17 @@ AudioNodeStream::SendTimelineEvent(uint3
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           RecvTimelineEvent(mIndex, mEvent);
     }
     AudioTimelineEvent mEvent;
     TrackRate mSampleRate;
     uint32_t mIndex;
   };
-  GraphImpl()->AppendMessage(new Message(this, aIndex, aEvent));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aEvent));
 }
 
 void
 AudioNodeStream::SetThreeDPointParameter(uint32_t aIndex, const ThreeDPoint& aValue)
 {
   class Message final : public ControlMessage
   {
   public:
@@ -241,17 +241,17 @@ AudioNodeStream::SetThreeDPointParameter
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetThreeDPointParameter(mIndex, mValue);
     }
     ThreeDPoint mValue;
     uint32_t mIndex;
   };
 
-  GraphImpl()->AppendMessage(new Message(this, aIndex, aValue));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aIndex, aValue));
 }
 
 void
 AudioNodeStream::SetBuffer(already_AddRefed<ThreadSharedFloatArrayBufferList>&& aBuffer)
 {
   class Message final : public ControlMessage
   {
   public:
@@ -262,17 +262,17 @@ AudioNodeStream::SetBuffer(already_AddRe
     void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->
           SetBuffer(mBuffer.forget());
     }
     RefPtr<ThreadSharedFloatArrayBufferList> mBuffer;
   };
 
-  GraphImpl()->AppendMessage(new Message(this, aBuffer));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aBuffer));
 }
 
 void
 AudioNodeStream::SetRawArrayData(nsTArray<float>& aData)
 {
   class Message final : public ControlMessage
   {
   public:
@@ -284,17 +284,17 @@ AudioNodeStream::SetRawArrayData(nsTArra
     }
     void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->Engine()->SetRawArrayData(mData);
     }
     nsTArray<float> mData;
   };
 
-  GraphImpl()->AppendMessage(new Message(this, aData));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aData));
 }
 
 void
 AudioNodeStream::SetChannelMixingParameters(uint32_t aNumberOfChannels,
                                             ChannelCountMode aChannelCountMode,
                                             ChannelInterpretation aChannelInterpretation)
 {
   class Message final : public ControlMessage
@@ -315,19 +315,19 @@ AudioNodeStream::SetChannelMixingParamet
         SetChannelMixingParametersImpl(mNumberOfChannels, mChannelCountMode,
                                        mChannelInterpretation);
     }
     uint32_t mNumberOfChannels;
     ChannelCountMode mChannelCountMode;
     ChannelInterpretation mChannelInterpretation;
   };
 
-  GraphImpl()->AppendMessage(new Message(this, aNumberOfChannels,
-                                         aChannelCountMode,
-                                         aChannelInterpretation));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aNumberOfChannels,
+                                                 aChannelCountMode,
+                                                 aChannelInterpretation));
 }
 
 void
 AudioNodeStream::SetPassThrough(bool aPassThrough)
 {
   class Message final : public ControlMessage
   {
   public:
@@ -336,17 +336,17 @@ AudioNodeStream::SetPassThrough(bool aPa
     {}
     void Run() override
     {
       static_cast<AudioNodeStream*>(mStream)->mPassThrough = mPassThrough;
     }
     bool mPassThrough;
   };
 
-  GraphImpl()->AppendMessage(new Message(this, aPassThrough));
+  GraphImpl()->AppendMessage(MakeUnique<Message>(this, aPassThrough));
 }
 
 void
 AudioNodeStream::SetChannelMixingParametersImpl(uint32_t aNumberOfChannels,
                                                 ChannelCountMode aChannelCountMode,
                                                 ChannelInterpretation aChannelInterpretation)
 {
   // Make sure that we're not clobbering any significant bits by fitting these
@@ -394,17 +394,17 @@ public:
 private:
   StreamTime mAdvance;
 };
 
 void
 AudioNodeStream::AdvanceAndResume(StreamTime aAdvance)
 {
   mMainThreadCurrentTime += aAdvance;
-  GraphImpl()->AppendMessage(new AdvanceAndResumeMessage(this, aAdvance));
+  GraphImpl()->AppendMessage(MakeUnique<AdvanceAndResumeMessage>(this, aAdvance));
 }
 
 void
 AudioNodeStream::ObtainInputBlock(AudioBlock& aTmpChunk,
                                   uint32_t aPortIndex)
 {
   uint32_t inputCount = mInputs.Length();
   uint32_t outputChannelCount = 1;
@@ -726,17 +726,17 @@ public:
 
 void
 AudioNodeStream::ScheduleCheckForInactive()
 {
   if (mActiveInputCount > 0 && !mMarkAsFinishedAfterThisBlock) {
     return;
   }
 
-  nsAutoPtr<CheckForInactiveMessage> message(new CheckForInactiveMessage(this));
+  auto message = MakeUnique<CheckForInactiveMessage>(this);
   GraphImpl()->RunMessageAfterProcessing(Move(message));
 }
 
 void
 AudioNodeStream::CheckForInactive()
 {
   if (((mActiveInputCount > 0 || mEngine->IsActive()) &&
        !mMarkAsFinishedAfterThisBlock) ||
--- a/dom/media/webspeech/recognition/SpeechStreamListener.cpp
+++ b/dom/media/webspeech/recognition/SpeechStreamListener.cpp
@@ -17,20 +17,17 @@ SpeechStreamListener::SpeechStreamListen
 {
 }
 
 SpeechStreamListener::~SpeechStreamListener()
 {
   nsCOMPtr<nsIThread> mainThread;
   NS_GetMainThread(getter_AddRefs(mainThread));
 
-  SpeechRecognition* forgottenRecognition = nullptr;
-  mRecognition.swap(forgottenRecognition);
-  NS_ProxyRelease(mainThread,
-                  static_cast<DOMEventTargetHelper*>(forgottenRecognition));
+  NS_ProxyRelease(mainThread, mRecognition.forget());
 }
 
 void
 SpeechStreamListener::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
                                                TrackID aID,
                                                StreamTime aTrackOffset,
                                                uint32_t aTrackEvents,
                                                const MediaSegment& aQueuedMedia,
--- a/dom/messagechannel/MessagePort.cpp
+++ b/dom/messagechannel/MessagePort.cpp
@@ -40,70 +40,57 @@
 #undef PostMessage
 #endif
 
 using namespace mozilla::dom::workers;
 
 namespace mozilla {
 namespace dom {
 
-class DispatchEventRunnable final : public nsICancelableRunnable
+class PostMessageRunnable final : public nsICancelableRunnable
 {
   friend class MessagePort;
 
 public:
   NS_DECL_ISUPPORTS
 
-  explicit DispatchEventRunnable(MessagePort* aPort)
-    : mPort(aPort)
-  { }
-
-  NS_IMETHOD
-  Run() override
-  {
-    MOZ_ASSERT(mPort);
-    MOZ_ASSERT(mPort->mDispatchRunnable == this);
-    mPort->mDispatchRunnable = nullptr;
-    mPort->Dispatch();
-
-    return NS_OK;
-  }
-
-  NS_IMETHOD
-  Cancel() override
-  {
-    mPort = nullptr;
-    return NS_OK;
-  }
-
-private:
-  ~DispatchEventRunnable()
-  {}
-
-  RefPtr<MessagePort> mPort;
-};
-
-NS_IMPL_ISUPPORTS(DispatchEventRunnable, nsICancelableRunnable, nsIRunnable)
-
-class PostMessageRunnable final : public nsICancelableRunnable
-{
-public:
-  NS_DECL_ISUPPORTS
-
   PostMessageRunnable(MessagePort* aPort, SharedMessagePortMessage* aData)
     : mPort(aPort)
     , mData(aData)
   {
     MOZ_ASSERT(aPort);
     MOZ_ASSERT(aData);
   }
 
   NS_IMETHOD
   Run() override
   {
+    MOZ_ASSERT(mPort);
+    MOZ_ASSERT(mPort->mPostMessageRunnable == this);
+
+    nsresult rv = DispatchMessage();
+
+    mPort->mPostMessageRunnable = nullptr;
+    mPort->Dispatch();
+
+    return rv;
+  }
+
+  NS_IMETHOD
+  Cancel() override
+  {
+    mPort = nullptr;
+    mData = nullptr;
+    return NS_OK;
+  }
+
+private:
+  nsresult
+  DispatchMessage() const
+  {
     nsCOMPtr<nsIGlobalObject> globalObject;
 
     if (NS_IsMainThread()) {
       globalObject = do_QueryInterface(mPort->GetParentObject());
     } else {
       WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
       MOZ_ASSERT(workerPrivate);
       globalObject = workerPrivate->GlobalScope();
@@ -152,51 +139,43 @@ public:
     mPort->DispatchEvent(static_cast<dom::Event*>(event.get()), &dummy);
 
     // We must check if we were waiting for this message in order to shutdown
     // the port.
     mPort->UpdateMustKeepAlive();
     return NS_OK;
   }
 
-  NS_IMETHOD
-  Cancel() override
-  {
-    mPort = nullptr;
-    mData = nullptr;
-    return NS_OK;
-  }
-
 private:
   ~PostMessageRunnable()
   {}
 
   RefPtr<MessagePort> mPort;
   RefPtr<SharedMessagePortMessage> mData;
 };
 
 NS_IMPL_ISUPPORTS(PostMessageRunnable, nsICancelableRunnable, nsIRunnable)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(MessagePort)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MessagePort,
                                                 DOMEventTargetHelper)
-  if (tmp->mDispatchRunnable) {
-    NS_IMPL_CYCLE_COLLECTION_UNLINK(mDispatchRunnable->mPort);
+  if (tmp->mPostMessageRunnable) {
+    NS_IMPL_CYCLE_COLLECTION_UNLINK(mPostMessageRunnable->mPort);
   }
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessages);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagesForTheOtherPort);
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mUnshippedEntangledPort);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MessagePort,
                                                   DOMEventTargetHelper)
-  if (tmp->mDispatchRunnable) {
-    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDispatchRunnable->mPort);
+  if (tmp->mPostMessageRunnable) {
+    NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPostMessageRunnable->mPort);
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mUnshippedEntangledPort);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(MessagePort)
   NS_INTERFACE_MAP_ENTRY(nsIIPCBackgroundChildCreateCallback)
   NS_INTERFACE_MAP_ENTRY(nsIObserver)
@@ -492,17 +471,17 @@ MessagePort::Start()
 
   mMessageQueueEnabled = true;
   Dispatch();
 }
 
 void
 MessagePort::Dispatch()
 {
-  if (!mMessageQueueEnabled || mMessages.IsEmpty() || mDispatchRunnable) {
+  if (!mMessageQueueEnabled || mMessages.IsEmpty() || mPostMessageRunnable) {
     return;
   }
 
   switch (mState) {
     case eStateUnshippedEntangled:
       // Everything is fine here. We have messages because the other
       // port populates our queue directly.
       break;
@@ -544,23 +523,19 @@ MessagePort::Dispatch()
       // If we are here is because the port has been closed. We can still
       // process the pending messages.
       break;
   }
 
   RefPtr<SharedMessagePortMessage> data = mMessages.ElementAt(0);
   mMessages.RemoveElementAt(0);
 
-  RefPtr<PostMessageRunnable> runnable = new PostMessageRunnable(this, data);
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(runnable)));
+  mPostMessageRunnable = new PostMessageRunnable(this, data);
 
-  mDispatchRunnable = new DispatchEventRunnable(this);
-
-  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(mDispatchRunnable)));
+  MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToCurrentThread(mPostMessageRunnable)));
 }
 
 void
 MessagePort::Close()
 {
   CloseInternal(true /* aSoftly */);
 }
 
--- a/dom/messagechannel/MessagePort.h
+++ b/dom/messagechannel/MessagePort.h
@@ -16,32 +16,30 @@
 #undef PostMessage
 #endif
 
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 namespace dom {
 
-class DispatchEventRunnable;
 class MessagePortChild;
 class MessagePortIdentifier;
 class MessagePortMessage;
 class PostMessageRunnable;
 class SharedMessagePortMessage;
 
 namespace workers {
 class WorkerFeature;
 } // namespace workers
 
 class MessagePort final : public DOMEventTargetHelper
                         , public nsIIPCBackgroundChildCreateCallback
                         , public nsIObserver
 {
-  friend class DispatchEventRunnable;
   friend class PostMessageRunnable;
 
 public:
   NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
   NS_DECL_NSIOBSERVER
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MessagePort,
                                            DOMEventTargetHelper)
@@ -161,17 +159,17 @@ private:
 
   bool IsCertainlyAliveForCC() const override
   {
     return mIsKeptAlive;
   }
 
   nsAutoPtr<workers::WorkerFeature> mWorkerFeature;
 
-  RefPtr<DispatchEventRunnable> mDispatchRunnable;
+  RefPtr<PostMessageRunnable> mPostMessageRunnable;
 
   RefPtr<MessagePortChild> mActor;
 
   RefPtr<MessagePort> mUnshippedEntangledPort;
 
   nsTArray<RefPtr<SharedMessagePortMessage>> mMessages;
   nsTArray<RefPtr<SharedMessagePortMessage>> mMessagesForTheOtherPort;
 
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -846,29 +846,36 @@ nsCSPContext::SendReports(nsISupports* a
       CSPCONTEXTLOG(("Could not create nsIURI for report URI %s",
                      reportURICstring.get()));
       logToConsole(MOZ_UTF16("triedToSendReport"), params, ArrayLength(params),
                    aSourceFile, aScriptSample, aLineNum, 0, nsIScriptError::errorFlag);
       continue; // don't return yet, there may be more URIs
     }
 
     // try to create a new channel for every report-uri
+    nsLoadFlags loadFlags = nsIRequest::LOAD_NORMAL | nsIChannel::LOAD_CLASSIFY_URI;
     if (doc) {
       rv = NS_NewChannel(getter_AddRefs(reportChannel),
                          reportURI,
                          doc,
                          nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
-                         nsIContentPolicy::TYPE_CSP_REPORT);
+                         nsIContentPolicy::TYPE_CSP_REPORT,
+                         nullptr, // aLoadGroup
+                         nullptr, // aCallbacks
+                         loadFlags);
     }
     else {
       rv = NS_NewChannel(getter_AddRefs(reportChannel),
                          reportURI,
                          mLoadingPrincipal,
                          nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
-                         nsIContentPolicy::TYPE_CSP_REPORT);
+                         nsIContentPolicy::TYPE_CSP_REPORT,
+                         nullptr, // aLoadGroup
+                         nullptr, // aCallbacks
+                         loadFlags);
     }
 
     if (NS_FAILED(rv)) {
       CSPCONTEXTLOG(("Could not create new channel for report URI %s",
                      reportURICstring.get()));
       continue; // don't return yet, there may be more URIs
     }
 
--- a/dom/svg/SVGTests.cpp
+++ b/dom/svg/SVGTests.cpp
@@ -10,18 +10,16 @@
 #include "mozilla/dom/SVGSwitchElement.h"
 #include "nsCharSeparatedTokenizer.h"
 #include "nsStyleUtil.h"
 #include "mozilla/Preferences.h"
 
 namespace mozilla {
 namespace dom {
 
-NS_IMPL_ISUPPORTS0(SVGTests)
-
 nsIAtom** SVGTests::sStringListNames[3] =
 {
   &nsGkAtoms::requiredFeatures,
   &nsGkAtoms::requiredExtensions,
   &nsGkAtoms::systemLanguage,
 };
 
 SVGTests::SVGTests()
--- a/dom/svg/SVGTests.h
+++ b/dom/svg/SVGTests.h
@@ -23,17 +23,16 @@ class DOMSVGStringList;
      {0x9b, 0x1b, 0xe0, 0x06, 0x0d, 0xb7, 0x3f, 0xc3 } }
 
 namespace dom {
 
 class SVGTests : public nsISupports
 {
 public:
   NS_DECLARE_STATIC_IID_ACCESSOR(MOZILLA_DOMSVGTESTS_IID)
-  NS_DECL_ISUPPORTS
 
   SVGTests();
 
   friend class mozilla::DOMSVGStringList;
   typedef mozilla::SVGStringList SVGStringList;
 
   /**
    * Compare the language name(s) in a systemLanguage attribute to the
--- a/dom/tests/mochitest/general/test_interfaces.html
+++ b/dom/tests/mochitest/general/test_interfaces.html
@@ -445,18 +445,16 @@ var interfaceNamesInGlobalScope =
     "DOMRect",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DOMRectList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DOMRectReadOnly",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DOMRequest",
 // IMPORTANT: Do not change this list without review from a DOM peer!
-    "DOMSettableTokenList",
-// IMPORTANT: Do not change this list without review from a DOM peer!
     "DOMStringList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DOMStringMap",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DOMTokenList",
 // IMPORTANT: Do not change this list without review from a DOM peer!
     "DOMTransactionEvent",
 // IMPORTANT: Do not change this list without review from a DOM peer!
--- a/dom/tests/mochitest/geolocation/network_geolocation.sjs
+++ b/dom/tests/mochitest/geolocation/network_geolocation.sjs
@@ -13,26 +13,26 @@ function parseQueryString(str)
       throw "Bad parameter in queryString!  '" + paramArray[i] + "'";
     params[decodeURIComponent(match[1])] = decodeURIComponent(match[2]);
   }
 
   return params;
 }
 
 function getPosition(action)
-{  
+{
   var response = {
     status: "OK",
     location: {
       lat: 37.41857,
       lng: -122.08769,
     },
     accuracy: (action == "worse-accuracy") ? 100 : 42,
   };
-  
+
   return JSON.stringify(response);
 }
 
 var timer;
 function handleRequest(request, response)
 {
   var params = parseQueryString(request.queryString);
 
--- a/dom/webidl/Console.webidl
+++ b/dom/webidl/Console.webidl
@@ -1,16 +1,16 @@
 /* -*- Mode: IDL; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=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/. */
 
 [ChromeOnly,
- Exposed=(Window,Worker)]
+ Exposed=(Window,Worker,WorkerDebugger)]
 interface Console {
   void log(any... data);
   void info(any... data);
   void warn(any... data);
   void error(any... data);
   void _exception(any... data);
   void debug(any... data);
   void table(any... data);
deleted file mode 100644
--- a/dom/webidl/DOMSettableTokenList.webidl
+++ /dev/null
@@ -1,16 +0,0 @@
-/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this file,
- * You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- * The origin of this IDL file is
- * http://www.w3.org/TR/2012/WD-dom-20120105/
- *
- * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
- * liability, trademark and document use rules apply.
- */
-
-interface DOMSettableTokenList : DOMTokenList {
-            [SetterThrows]
-            attribute DOMString value;
-};
--- a/dom/webidl/DOMTokenList.webidl
+++ b/dom/webidl/DOMTokenList.webidl
@@ -16,10 +16,12 @@ interface DOMTokenList {
   [Throws]
   boolean contains(DOMString token);
   [Throws]
   void add(DOMString... tokens);
   [Throws]
   void remove(DOMString... tokens);
   [Throws]
   boolean toggle(DOMString token, optional boolean force);
+  [SetterThrows]
+  attribute DOMString value;
   stringifier DOMString ();
 };
--- a/dom/webidl/Element.webidl
+++ b/dom/webidl/Element.webidl
@@ -25,17 +25,17 @@ interface Element : Node {
   // Not [Constant] because it depends on which document we're in
   [Pure]
   readonly attribute DOMString tagName;
 
   [Pure]
            attribute DOMString id;
   [Pure]
            attribute DOMString className;
-  [Constant]
+  [Constant, PutForwards=value]
   readonly attribute DOMTokenList classList;
 
   [SameObject]
   readonly attribute NamedNodeMap attributes;
   [Pure]
   sequence<DOMString> getAttributeNames();
   [Pure]
   DOMString? getAttribute(DOMString name);
--- a/dom/webidl/HTMLAnchorElement.webidl
+++ b/dom/webidl/HTMLAnchorElement.webidl
@@ -18,16 +18,17 @@ interface HTMLAnchorElement : HTMLElemen
            [SetterThrows]
            attribute DOMString download;
            [SetterThrows]
            attribute DOMString ping;
            [SetterThrows]
            attribute DOMString rel;
            [SetterThrows, Pref="network.http.enablePerElementReferrer"]
            attribute DOMString referrerPolicy;
+           [PutForwards=value]
   readonly attribute DOMTokenList relList;
            [SetterThrows]
            attribute DOMString hreflang;
            [SetterThrows]
            attribute DOMString type;
 
            [SetterThrows]
            attribute DOMString text;
--- a/dom/webidl/HTMLAreaElement.webidl
+++ b/dom/webidl/HTMLAreaElement.webidl
@@ -25,16 +25,17 @@ interface HTMLAreaElement : HTMLElement 
            [SetterThrows]
            attribute DOMString download;
            [SetterThrows]
            attribute DOMString ping;
            [SetterThrows]
            attribute DOMString rel;
            [SetterThrows, Pref="network.http.enablePerElementReferrer"]
            attribute DOMString referrerPolicy;
+           [PutForwards=value]
   readonly attribute DOMTokenList relList;
 };
 
 HTMLAreaElement implements HTMLHyperlinkElementUtils;
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLAreaElement {
            [SetterThrows]
--- a/dom/webidl/HTMLElement.webidl
+++ b/dom/webidl/HTMLElement.webidl
@@ -23,21 +23,21 @@ interface HTMLElement : Element {
   readonly attribute DOMStringMap dataset;
 
   [GetterThrows, Pure]
            attribute DOMString innerText;
 
   // microdata 
   [SetterThrows, Pure]
            attribute boolean itemScope;
-  [PutForwards=value,Constant] readonly attribute DOMSettableTokenList itemType;
+  [PutForwards=value,Constant] readonly attribute DOMTokenList itemType;
   [SetterThrows, Pure]
            attribute DOMString itemId;
-  [PutForwards=value,Constant] readonly attribute DOMSettableTokenList itemRef;
-  [PutForwards=value,Constant] readonly attribute DOMSettableTokenList itemProp;
+  [PutForwards=value,Constant] readonly attribute DOMTokenList itemRef;
+  [PutForwards=value,Constant] readonly attribute DOMTokenList itemProp;
   [Constant]
   readonly attribute HTMLPropertiesCollection properties;
   [Throws]
            attribute any itemValue;
 
   // user interaction
   [SetterThrows, Pure]
            attribute boolean hidden;
@@ -49,17 +49,17 @@ interface HTMLElement : Element {
   [Throws]
   void blur();
   [SetterThrows, Pure]
            attribute DOMString accessKey;
   [Pure]
   readonly attribute DOMString accessKeyLabel;
   [SetterThrows, Pure]
            attribute boolean draggable;
-  //[PutForwards=value] readonly attribute DOMSettableTokenList dropzone;
+  //[PutForwards=value] readonly attribute DOMTokenList dropzone;
   [SetterThrows, Pure]
            attribute DOMString contentEditable;
   [Pure]
   readonly attribute boolean isContentEditable;
   [Pure]
   readonly attribute HTMLMenuElement? contextMenu;
   //[SetterThrows]
   //         attribute HTMLMenuElement? contextMenu;
--- a/dom/webidl/HTMLIFrameElement.webidl
+++ b/dom/webidl/HTMLIFrameElement.webidl
@@ -13,17 +13,17 @@
 
 interface HTMLIFrameElement : HTMLElement {
   [SetterThrows, Pure]
            attribute DOMString src;
   [SetterThrows, Pure]
            attribute DOMString srcdoc;
   [SetterThrows, Pure]
            attribute DOMString name;
-  [PutForwards=value] readonly attribute DOMSettableTokenList sandbox;
+  [PutForwards=value] readonly attribute DOMTokenList sandbox;
            // attribute boolean seamless;
   [SetterThrows, Pure]
            attribute boolean allowFullscreen;
   [SetterThrows, Pure]
            attribute DOMString width;
   [SetterThrows, Pure]
            attribute DOMString height;
   [SetterThrows, Pure, Pref="network.http.enablePerElementReferrer"]
--- a/dom/webidl/HTMLLinkElement.webidl
+++ b/dom/webidl/HTMLLinkElement.webidl
@@ -16,24 +16,25 @@ interface HTMLLinkElement : HTMLElement 
   [Pure]
            attribute boolean disabled;
   [SetterThrows, Pure]
            attribute DOMString href;
   [SetterThrows, Pure]
            attribute DOMString? crossOrigin;
   [SetterThrows, Pure]
            attribute DOMString rel;
+  [PutForwards=value]
   readonly attribute DOMTokenList relList;
   [SetterThrows, Pure]
            attribute DOMString media;
   [SetterThrows, Pure]
            attribute DOMString hreflang;
   [SetterThrows, Pure]
            attribute DOMString type;
-  [PutForwards=value] readonly attribute DOMSettableTokenList sizes;
+  [PutForwards=value] readonly attribute DOMTokenList sizes;
 };
 HTMLLinkElement implements LinkStyle;
 
 // http://www.whatwg.org/specs/web-apps/current-work/#other-elements,-attributes-and-apis
 partial interface HTMLLinkElement {
   [SetterThrows, Pure]
            attribute DOMString charset;
   [SetterThrows, Pure]
--- a/dom/webidl/HTMLOutputElement.webidl
+++ b/dom/webidl/HTMLOutputElement.webidl
@@ -9,17 +9,17 @@
  * © Copyright 2004-2011 Apple Computer, Inc., Mozilla Foundation, and
  * Opera Software ASA. You are granted a license to use, reproduce
  * and create derivative works of this document.
  */
 
 // http://www.whatwg.org/specs/web-apps/current-work/#the-output-element
 interface HTMLOutputElement : HTMLElement {
   [PutForwards=value, Constant]
-  readonly attribute DOMSettableTokenList htmlFor;
+  readonly attribute DOMTokenList htmlFor;
   readonly attribute HTMLFormElement? form;
   [SetterThrows, Pure]
            attribute DOMString name;
 
   [Constant]
   readonly attribute DOMString type;
   [SetterThrows, Pure]
            attribute DOMString defaultValue;
--- a/dom/webidl/HTMLTableCellElement.webidl
+++ b/dom/webidl/HTMLTableCellElement.webidl
@@ -11,17 +11,17 @@
  * and create derivative works of this document.
  */
 
 interface HTMLTableCellElement : HTMLElement {
            [SetterThrows]
            attribute unsigned long colSpan;
            [SetterThrows]
            attribute unsigned long rowSpan;
-  //[PutForwards=value] readonly attribute DOMSettableTokenList headers;
+  //[PutForwards=value] readonly attribute DOMTokenList headers;
            [SetterThrows]
            attribute DOMString headers;
   readonly attribute long cellIndex;
 
   // Mozilla-specific extensions
            [SetterThrows]
            attribute DOMString abbr;
            [SetterThrows]
--- a/dom/webidl/WorkerDebuggerGlobalScope.webidl
+++ b/dom/webidl/WorkerDebuggerGlobalScope.webidl
@@ -24,9 +24,12 @@ interface WorkerDebuggerGlobalScope : Ev
   void setImmediate(Function handler);
 
   void reportError(DOMString message);
 };
 
 // So you can debug while you debug
 partial interface WorkerDebuggerGlobalScope {
   void dump(optional DOMString string);
+
+  [Throws, Replaceable]
+  readonly attribute Console console;
 };
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -130,17 +130,16 @@ WEBIDL_FILES = [
     'DOMMatrix.webidl',
     'DOMMobileMessageError.webidl',
     'DOMParser.webidl',
     'DOMPoint.webidl',
     'DOMQuad.webidl',
     'DOMRect.webidl',
     'DOMRectList.webidl',
     'DOMRequest.webidl',
-    'DOMSettableTokenList.webidl',
     'DOMStringList.webidl',
     'DOMStringMap.webidl',
     'DOMTokenList.webidl',
     'DOMTransaction.webidl',
     'Downloads.webidl',
     'DragEvent.webidl',
     'DummyBinding.webidl',
     'DynamicsCompressorNode.webidl',
--- a/dom/workers/ServiceWorkerEvents.cpp
+++ b/dom/workers/ServiceWorkerEvents.cpp
@@ -188,32 +188,40 @@ public:
       channelInfo = mInternalResponse->GetChannelInfo();
     } else {
       // We are dealing with a synthesized response here, so fall back to the
       // channel info for the worker script.
       channelInfo = mWorkerChannelInfo;
     }
     rv = mChannel->SetChannelInfo(&channelInfo);
     if (NS_WARN_IF(NS_FAILED(rv))) {
-      return rv;
+      mChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
+      return NS_OK;
     }
 
-    mChannel->SynthesizeStatus(mInternalResponse->GetUnfilteredStatus(),
-                               mInternalResponse->GetUnfilteredStatusText());
+    rv = mChannel->SynthesizeStatus(mInternalResponse->GetUnfilteredStatus(),
+                                    mInternalResponse->GetUnfilteredStatusText());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
+      return NS_OK;
+    }
 
     AutoTArray<InternalHeaders::Entry, 5> entries;
     mInternalResponse->UnfilteredHeaders()->GetEntries(entries);
     for (uint32_t i = 0; i < entries.Length(); ++i) {
        mChannel->SynthesizeHeader(entries[i].mName, entries[i].mValue);
     }
 
     loadInfo->MaybeIncreaseTainting(mInternalResponse->GetTainting());
 
     rv = mChannel->FinishSynthesizedResponse(mResponseURLSpec);
-    NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "Failed to finish synthesized response");
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      mChannel->Cancel(NS_ERROR_INTERCEPTION_FAILED);
+      return NS_OK;
+    }
 
     nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();
     if (obsService) {
       obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr);
     }
 
     return rv;
   }
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -1306,16 +1306,23 @@ public:
       return;
     }
 
     if (mJobType == RegisterJob) {
       MOZ_ASSERT(!mRegistration);
       mRegistration = swm->GetRegistration(mPrincipal, mScope);
 
       if (mRegistration) {
+        // If we are resurrecting an uninstalling registration, then persist
+        // it to disk again.  We preemptively removed it earlier during
+        // unregister so that closing the window by shutting down the browser
+        // results in the registration being gone on restart.
+        if (mRegistration->mPendingUninstall) {
+          swm->StoreRegistration(mPrincipal, mRegistration);
+        }
         mRegistration->mPendingUninstall = false;
         RefPtr<ServiceWorkerInfo> newest = mRegistration->Newest();
         if (newest && mScriptSpec.Equals(newest->ScriptSpec())) {
           Succeed();
 
           // Done() must always be called async from Start()
           nsCOMPtr<nsIRunnable> runnable =
             NS_NewRunnableMethodWithArg<nsresult>(
@@ -2462,17 +2469,22 @@ private:
     PrincipalInfo principalInfo;
     if (NS_WARN_IF(NS_FAILED(PrincipalToPrincipalInfo(mPrincipal,
                                                       &principalInfo)))) {
       return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK;
     }
 
     RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 
-    // Could it be that we are shutting down.
+    // Note, we send the message to remove the registration from disk now even
+    // though we may only set the mPendingUninstall flag below.  This is
+    // necessary to ensure the registration is removed if the controlled
+    // clients are closed by shutting down the browser.  If the registration
+    // is resurrected by clearing mPendingUninstall then it should be saved
+    // to disk again.
     if (swm->mActor) {
       swm->mActor->SendUnregister(principalInfo, NS_ConvertUTF8toUTF16(mScope));
     }
 
     nsAutoCString scopeKey;
     nsresult rv = swm->PrincipalToScopeKey(mPrincipal, scopeKey);
     if (NS_WARN_IF(NS_FAILED(rv))) {
       return mCallback ? mCallback->UnregisterSucceeded(false) : NS_OK;
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3621,27 +3621,18 @@ WorkerDebugger::WorkerDebugger(WorkerPri
   AssertIsOnMainThread();
 }
 
 WorkerDebugger::~WorkerDebugger()
 {
   MOZ_ASSERT(!mWorkerPrivate);
 
   if (!NS_IsMainThread()) {
-    nsCOMPtr<nsIThread> mainThread;
-    if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread)))) {
-      NS_WARNING("Failed to proxy release of listeners, leaking instead!");
-    }
-
     for (size_t index = 0; index < mListeners.Length(); ++index) {
-      nsIWorkerDebuggerListener* listener = nullptr;
-      mListeners[index].forget(&listener);
-      if (NS_FAILED(NS_ProxyRelease(mainThread, listener))) {
-        NS_WARNING("Failed to proxy release of listener, leaking instead!");
-      }
+      NS_ReleaseOnMainThread(mListeners[index].forget());
     }
   }
 }
 
 NS_IMPL_ISUPPORTS(WorkerDebugger, nsIWorkerDebugger)
 
 NS_IMETHODIMP
 WorkerDebugger::GetIsClosed(bool* aResult)
@@ -4153,21 +4144,17 @@ WorkerPrivate::GetLoadInfo(JSContext* aC
     // Now that we've spun the loop there's no guarantee that our parent is
     // still alive.  We may have received control messages initiating shutdown.
     {
       MutexAutoLock lock(aParent->mMutex);
       parentStatus = aParent->mStatus;
     }
 
     if (parentStatus > Running) {
-      nsCOMPtr<nsIThread> mainThread;
-      if (NS_FAILED(NS_GetMainThread(getter_AddRefs(mainThread))) ||
-          NS_FAILED(NS_ProxyRelease(mainThread, loadInfo.mChannel))) {
-        NS_WARNING("Failed to proxy release of channel, leaking instead!");
-      }
+      NS_ReleaseOnMainThread(loadInfo.mChannel.forget());
       return NS_ERROR_FAILURE;
     }
 
     loadInfo.mDomain = aParent->Domain();
     loadInfo.mFromWindow = aParent->IsFromWindow();
     loadInfo.mWindowID = aParent->WindowID();
     loadInfo.mStorageAllowed = aParent->IsStorageAllowed();
     loadInfo.mPrivateBrowsing = aParent->IsInPrivateBrowsing();
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -663,16 +663,35 @@ WorkerDebuggerGlobalScope::WorkerDebugge
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
 WorkerDebuggerGlobalScope::~WorkerDebuggerGlobalScope()
 {
   mWorkerPrivate->AssertIsOnWorkerThread();
 }
 
+NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerDebuggerGlobalScope)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerDebuggerGlobalScope,
+                                                  DOMEventTargetHelper)
+  tmp->mWorkerPrivate->AssertIsOnWorkerThread();
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerDebuggerGlobalScope,
+                                                DOMEventTargetHelper)
+  tmp->mWorkerPrivate->AssertIsOnWorkerThread();
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerDebuggerGlobalScope,
+                                               DOMEventTargetHelper)
+  tmp->mWorkerPrivate->AssertIsOnWorkerThread();
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
 NS_IMPL_ADDREF_INHERITED(WorkerDebuggerGlobalScope, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(WorkerDebuggerGlobalScope, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerDebuggerGlobalScope)
   NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
 
 bool
@@ -905,16 +924,29 @@ WorkerDebuggerGlobalScope::ReportError(J
 {
   JS::UniqueChars chars;
   uint32_t lineno = 0;
   JS::DescribeScriptedCaller(aCx, &chars, &lineno);
   nsString filename(NS_ConvertUTF8toUTF16(chars.get()));
   mWorkerPrivate->ReportErrorToDebugger(filename, lineno, aMessage);
 }
 
+Console*
+WorkerDebuggerGlobalScope::GetConsole(ErrorResult& aRv)
+{
+  mWorkerPrivate->AssertIsOnWorkerThread();
+
+  // Debugger console has its own console object.
+  if (!mConsole) {
+    mConsole = new Console(nullptr);
+  }
+
+  return mConsole;
+}
+
 void
 WorkerDebuggerGlobalScope::Dump(JSContext* aCx,
                                 const Optional<nsAString>& aString) const
 {
   return mWorkerPrivate->GetOrCreateGlobalScope(aCx)->Dump(aString);
 }
 
 nsIGlobalObject*
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -276,21 +276,24 @@ public:
   IMPL_EVENT_HANDLER(pushsubscriptionchange)
 
 };
 
 class WorkerDebuggerGlobalScope final : public DOMEventTargetHelper,
                                         public nsIGlobalObject
 {
   WorkerPrivate* mWorkerPrivate;
+  RefPtr<Console> mConsole;
 
 public:
   explicit WorkerDebuggerGlobalScope(WorkerPrivate* aWorkerPrivate);
 
   NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerDebuggerGlobalScope,
+                                                         DOMEventTargetHelper)
 
   virtual JSObject*
   WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
   {
     MOZ_CRASH("Shouldn't get here!");
   }
 
   virtual bool
@@ -328,16 +331,19 @@ public:
   IMPL_EVENT_HANDLER(message)
 
   void
   SetImmediate(JSContext* aCx, Function& aHandler, ErrorResult& aRv);
 
   void
   ReportError(JSContext* aCx, const nsAString& aMessage);
 
+  Console*
+  GetConsole(ErrorResult& aRv);
+
   void
   Dump(JSContext* aCx, const Optional<nsAString>& aString) const;
 
 private:
   virtual ~WorkerDebuggerGlobalScope();
 };
 
 END_WORKERS_NAMESPACE
--- a/extensions/gio/nsGIOProtocolHandler.cpp
+++ b/extensions/gio/nsGIOProtocolHandler.cpp
@@ -610,27 +610,20 @@ nsGIOInputStream::Close()
   {
     // Destroy the list of GIOFileInfo objects...
     g_list_foreach(mDirList, (GFunc) g_object_unref, nullptr);
     g_list_free(mDirList);
     mDirList = nullptr;
     mDirListPtr = nullptr;
   }
 
-  if (mChannel)
-  {
-    nsresult rv = NS_OK;
+  if (mChannel) {
+    NS_ReleaseOnMainThread(dont_AddRef(mChannel));
 
-    nsCOMPtr<nsIThread> thread = do_GetMainThread();
-    if (thread)
-      rv = NS_ProxyRelease(thread, mChannel);
-
-    NS_ASSERTION(thread && NS_SUCCEEDED(rv), "leaking channel reference");
     mChannel = nullptr;
-    (void) rv;
   }
 
   mSpec.Truncate(); // free memory
 
   // Prevent future reads from re-opening the handle.
   if (NS_SUCCEEDED(mStatus))
     mStatus = NS_BASE_STREAM_CLOSED;
 
--- a/extensions/spellcheck/src/mozPersonalDictionary.cpp
+++ b/extensions/spellcheck/src/mozPersonalDictionary.cpp
@@ -60,26 +60,17 @@ public:
   {
   }
 
   NS_IMETHOD Run() override
   {
     mDict->SyncLoad();
 
     // Release the dictionary on the main thread
-    mozPersonalDictionary *dict;
-    mDict.forget(&dict);
-
-    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-    if (mainThread) {
-      NS_ProxyRelease(mainThread, static_cast<mozIPersonalDictionary *>(dict));
-    } else {
-      // It's better to leak the dictionary than to release it on a wrong thread
-      NS_WARNING("Cannot get main thread, leaking mozPersonalDictionary.");
-    }
+    NS_ReleaseOnMainThread(mDict.forget());
 
     return NS_OK;
   }
 
 private:
   RefPtr<mozPersonalDictionary> mDict;
 };
 
@@ -140,26 +131,18 @@ public:
       mDict->mSavePending = false;
       mon.Notify();
 
       // Leaving the block where 'mon' was declared will call the destructor
       // and unlock.
     }
 
     // Release the dictionary on the main thread.
-    mozPersonalDictionary *dict;
-    mDict.forget(&dict);
+    NS_ReleaseOnMainThread(mDict.forget());
 
-    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-    if (mainThread) {
-      NS_ProxyRelease(mainThread, static_cast<mozIPersonalDictionary *>(dict));
-    } else {
-      // It's better to leak the dictionary than to release it on a wrong thread.
-      NS_WARNING("Cannot get main thread, leaking mozPersonalDictionary.");
-    }
     return NS_OK;
   }
 
 private:
   nsTArray<nsString> mDictWords;
   nsCOMPtr<nsIFile> mFile;
   RefPtr<mozPersonalDictionary> mDict;
 };
--- a/gfx/2d/BaseRect.h
+++ b/gfx/2d/BaseRect.h
@@ -540,16 +540,32 @@ struct BaseRect {
              std::max(aRect.y, y),
              std::min(aRect.width, width),
              std::min(aRect.height, height));
     rect.x = std::min(rect.XMost(), aRect.XMost()) - rect.width;
     rect.y = std::min(rect.YMost(), aRect.YMost()) - rect.height;
     return rect;
   }
 
+  // Returns the largest rectangle that can be represented with 32-bit
+  // signed integers, centered around a point at 0,0.  As BaseRect's represent
+  // the dimensions as a top-left point with a width and height, the width
+  // and height will be the largest positive 32-bit value.  The top-left
+  // position coordinate is divided by two to center the rectangle around a
+  // point at 0,0.
+  static Sub MaxIntRect()
+  {
+    return Sub(
+      -std::numeric_limits<int32_t>::max() * 0.5,
+      -std::numeric_limits<int32_t>::max() * 0.5,
+      std::numeric_limits<int32_t>::max(),
+      std::numeric_limits<int32_t>::max()
+    );
+  };
+
   friend std::ostream& operator<<(std::ostream& stream,
       const BaseRect<T, Sub, Point, SizeT, MarginT>& aRect) {
     return stream << '(' << aRect.x << ',' << aRect.y << ','
                   << aRect.width << ',' << aRect.height << ')';
   }
 
 private:
   // Do not use the default operator== or operator!= !
--- a/gfx/2d/DrawTargetD2D1.cpp
+++ b/gfx/2d/DrawTargetD2D1.cpp
@@ -27,17 +27,17 @@ ID2D1Factory1* DrawTargetD2D1::mFactory 
 
 ID2D1Factory1 *D2DFactory1()
 {
   return DrawTargetD2D1::factory();
 }
 
 DrawTargetD2D1::DrawTargetD2D1()
   : mPushedLayers(1)
-  , mPushedLayersSincePurge(0)
+  , mUsedCommandListsSincePurge(0)
 {
 }
 
 DrawTargetD2D1::~DrawTargetD2D1()
 {
   PopAllClips();
 
   if (mSnapshot) {
@@ -92,24 +92,24 @@ DrawTargetD2D1::Snapshot()
 // this can cause issues with memory usage (see bug 1238328). EndDraw/BeginDraw
 // are expensive though, especially relatively when little work is done, so
 // we try to reduce the amount of times we execute these purges.
 static const uint32_t kPushedLayersBeforePurge = 25;
 
 void
 DrawTargetD2D1::Flush()
 {
-  if ((mPushedLayersSincePurge >= kPushedLayersBeforePurge) &&
+  if ((mUsedCommandListsSincePurge >= kPushedLayersBeforePurge) &&
       mPushedLayers.size() == 1) {
     // It's important to pop all clips as otherwise layers can forget about
     // their clip when doing an EndDraw. When we have layers pushed we cannot
     // easily pop all underlying clips to delay the purge until we have no
     // layers pushed.
     PopAllClips();
-    mPushedLayersSincePurge = 0;
+    mUsedCommandListsSincePurge = 0;
     mDC->EndDraw();
     mDC->BeginDraw();
   } else {
     mDC->Flush();
   }
 
   // We no longer depend on any target.
   for (TargetSet::iterator iter = mDependingOnTargets.begin();
@@ -269,16 +269,17 @@ DrawTargetD2D1::ClearRect(const Rect &aR
     mDC->Clear();
     mDC->PopAxisAlignedClip();
 
     PopClip();
     return;
   }
 
   RefPtr<ID2D1CommandList> list;
+  mUsedCommandListsSincePurge++;
   mDC->CreateCommandList(getter_AddRefs(list));
   mDC->SetTarget(list);
 
   IntRect addClipRect;
   RefPtr<ID2D1Geometry> geom = GetClippedGeometry(&addClipRect);
 
   RefPtr<ID2D1SolidColorBrush> brush;
   mDC->CreateSolidColorBrush(D2D1::ColorF(D2D1::ColorF::White), getter_AddRefs(brush));
@@ -798,17 +799,17 @@ DrawTargetD2D1::PushLayer(bool aOpaque, 
   pushedLayer.mOldPermitSubpixelAA = mPermitSubpixelAA;
   mPermitSubpixelAA = aOpaque;
 
   mDC->CreateCommandList(getter_AddRefs(pushedLayer.mCurrentList));
   mPushedLayers.push_back(pushedLayer);
 
   mDC->SetTarget(CurrentTarget());
 
-  mPushedLayersSincePurge++;
+  mUsedCommandListsSincePurge++;
 }
 
 void
 DrawTargetD2D1::PopLayer()
 {
   MOZ_ASSERT(CurrentLayer().mPushedClips.size() == 0);
 
   RefPtr<ID2D1CommandList> list = CurrentLayer().mCurrentList;
@@ -1127,16 +1128,17 @@ DrawTargetD2D1::PrepareForDrawing(Compos
 
     return;
   }
 
   PopAllClips();
 
   mDC->CreateCommandList(getter_AddRefs(mCommandList));
   mDC->SetTarget(mCommandList);
+  mUsedCommandListsSincePurge++;
 
   PushAllClips();
   FlushTransformToDC();
 }
 
 void
 DrawTargetD2D1::FinalizeDrawing(CompositionOp aOp, const Pattern &aPattern)
 {
--- a/gfx/2d/DrawTargetD2D1.h
+++ b/gfx/2d/DrawTargetD2D1.h
@@ -261,17 +261,17 @@ private:
   // The latest snapshot of this surface. This needs to be told when this
   // target is modified. We keep it alive as a cache.
   RefPtr<SourceSurfaceD2D1> mSnapshot;
   // A list of targets we need to flush when we're modified.
   TargetSet mDependentTargets;
   // A list of targets which have this object in their mDependentTargets set
   TargetSet mDependingOnTargets;
 
-  uint32_t mPushedLayersSincePurge;
+  uint32_t mUsedCommandListsSincePurge;
 
   static ID2D1Factory1 *mFactory;
   static IDWriteFactory *mDWriteFactory;
 };
 
 }
 }
 
--- a/gfx/2d/Rect.h
+++ b/gfx/2d/Rect.h
@@ -151,32 +151,16 @@ struct RectTyped :
     RectTyped(const PointTyped<units, F>& aPos, const SizeTyped<units, F>& aSize) :
         Super(aPos, aSize) {}
     RectTyped(F _x, F _y, F _width, F _height) :
         Super(_x, _y, _width, _height) {}
     explicit RectTyped(const IntRectTyped<units>& rect) :
         Super(F(rect.x), F(rect.y),
               F(rect.width), F(rect.height)) {}
 
-    // Returns the largest rectangle that can be represented with 32-bit
-    // signed integers, centered around a point at 0,0.  As BaseRect's represent
-    // the dimensions as a top-left point with a width and height, the width
-    // and height will be the largest positive 32-bit value.  The top-left
-    // position coordinate is divided by two to center the rectangle around a
-    // point at 0,0.
-    static RectTyped<units, F> MaxIntRect()
-    {
-      return RectTyped<units, F>(
-        -std::numeric_limits<int32_t>::max() * 0.5,
-        -std::numeric_limits<int32_t>::max() * 0.5,
-        std::numeric_limits<int32_t>::max(),
-        std::numeric_limits<int32_t>::max()
-      );
-    };
-
     void NudgeToIntegers()
     {
       NudgeToInteger(&(this->x));
       NudgeToInteger(&(this->y));
       NudgeToInteger(&(this->width));
       NudgeToInteger(&(this->height));
     }
 
--- a/gfx/ipc/GfxMessageUtils.h
+++ b/gfx/ipc/GfxMessageUtils.h
@@ -783,27 +783,38 @@ struct ParamTraits<mozilla::layers::Text
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mParentBackend);
     WriteParam(aMsg, aParam.mSupportedBlendModes.serialize());
     WriteParam(aMsg, aParam.mMaxTextureSize);
     WriteParam(aMsg, aParam.mSupportsTextureBlitting);
     WriteParam(aMsg, aParam.mSupportsPartialUploads);
     WriteParam(aMsg, aParam.mSyncHandle);
+#ifdef XP_WIN
+    WriteParam(aMsg, (void*)aParam.mSwapChain);
+#endif
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     uint32_t supportedBlendModes = 0;
+    IDXGISwapChain* swapChain = nullptr;
     bool result = ReadParam(aMsg, aIter, &aResult->mParentBackend) &&
                   ReadParam(aMsg, aIter, &supportedBlendModes) &&
                   ReadParam(aMsg, aIter, &aResult->mMaxTextureSize) &&
                   ReadParam(aMsg, aIter, &aResult->mSupportsTextureBlitting) &&
                   ReadParam(aMsg, aIter, &aResult->mSupportsPartialUploads) &&
-                  ReadParam(aMsg, aIter, &aResult->mSyncHandle);
+                  ReadParam(aMsg, aIter, &aResult->mSyncHandle)
+#ifdef XP_WIN
+                  && ReadParam(aMsg, aIter, (void**)&swapChain)
+#endif
+      ;
+    if (XRE_IsParentProcess()) {
+      aResult->SetSwapChain(swapChain);
+    }
     aResult->mSupportedBlendModes.deserialize(supportedBlendModes);
     return result;
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::TextureInfo>
 {
--- a/gfx/layers/Compositor.cpp
+++ b/gfx/layers/Compositor.cpp
@@ -13,20 +13,58 @@
 
 #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 17
 #include "libdisplay/GonkDisplay.h"     // for GonkDisplay
 #include <ui/Fence.h>
 #include "nsWindow.h"
 #include "nsScreenManagerGonk.h"
 #endif
 
+#ifdef XP_WIN
+#include "dxgi.h"
+#endif
+
 namespace mozilla {
 
 namespace layers {
 
+TextureFactoryIdentifier&
+TextureFactoryIdentifier::operator=(const TextureFactoryIdentifier& aOther) = default;
+
+TextureFactoryIdentifier::TextureFactoryIdentifier(const TextureFactoryIdentifier& aOther)
+{
+  *this = aOther;
+}
+
+TextureFactoryIdentifier::TextureFactoryIdentifier(LayersBackend aLayersBackend,
+                                                   GeckoProcessType aParentProcessId,
+                                                   int32_t aMaxTextureSize,
+                                                   bool aSupportsTextureBlitting,
+                                                   bool aSupportsPartialUploads,
+                                                   SyncHandle aSyncHandle,
+                                                   IDXGISwapChain* aSwapChain)
+  : mParentBackend(aLayersBackend)
+  , mParentProcessId(aParentProcessId)
+  , mSupportedBlendModes(gfx::CompositionOp::OP_OVER)
+  , mMaxTextureSize(aMaxTextureSize)
+  , mSupportsTextureBlitting(aSupportsTextureBlitting)
+  , mSupportsPartialUploads(aSupportsPartialUploads)
+  , mSyncHandle(aSyncHandle)
+  , mSwapChain(aSwapChain)
+{}
+
+TextureFactoryIdentifier::~TextureFactoryIdentifier()
+{}
+
+void
+TextureFactoryIdentifier::SetSwapChain(IDXGISwapChain* aSwapChain)
+{
+  mSwapChain = aSwapChain;
+}
+
 /* static */ void
 Compositor::AssertOnCompositorThread()
 {
   MOZ_ASSERT(!CompositorParent::CompositorLoop() ||
              CompositorParent::CompositorLoop() == MessageLoop::current(),
              "Can only call this from the compositor thread!");
 }
 
--- a/gfx/layers/CompositorTypes.h
+++ b/gfx/layers/CompositorTypes.h
@@ -10,16 +10,18 @@
 #include <sys/types.h>                  // for int32_t
 #include "LayersTypes.h"                // for LayersBackend, etc
 #include "nsXULAppAPI.h"                // for GeckoProcessType, etc
 #include "mozilla/gfx/Types.h"
 #include "mozilla/EnumSet.h"
 
 #include "mozilla/TypedEnumBits.h"
 
+struct IDXGISwapChain;
+
 namespace mozilla {
 namespace layers {
 
 /**
  * Flags used by texture clients and texture hosts. These are passed from client
  * side to host side when textures and compositables are created. Usually set
  * by the compositableCient, they may be modified by either the compositable or
  * texture clients.
@@ -161,31 +163,41 @@ struct TextureFactoryIdentifier
 {
   LayersBackend mParentBackend;
   GeckoProcessType mParentProcessId;
   EnumSet<gfx::CompositionOp> mSupportedBlendModes;
   int32_t mMaxTextureSize;
   bool mSupportsTextureBlitting;
   bool mSupportsPartialUploads;
   SyncHandle mSyncHandle;
+  // This member is required to send the SwapChain to the main thread in order
+  // to workaround bug 1232042.
+#ifdef XP_WIN
+  RefPtr<IDXGISwapChain> mSwapChain;
+#else
+  void* mSwapChain;
+#endif
+
+  // We can't include dxgi.h here because WinUser.h conflicts with some parts
+  // of the tree. Therefor we compile all constructors & operators in a single
+  // compile unit.
+  TextureFactoryIdentifier& operator=(const TextureFactoryIdentifier& aOther);
+
+  TextureFactoryIdentifier(const TextureFactoryIdentifier& aOther);
 
   explicit TextureFactoryIdentifier(LayersBackend aLayersBackend = LayersBackend::LAYERS_NONE,
                                     GeckoProcessType aParentProcessId = GeckoProcessType_Default,
                                     int32_t aMaxTextureSize = 4096,
                                     bool aSupportsTextureBlitting = false,
                                     bool aSupportsPartialUploads = false,
-                                    SyncHandle aSyncHandle = 0)
-    : mParentBackend(aLayersBackend)
-    , mParentProcessId(aParentProcessId)
-    , mSupportedBlendModes(gfx::CompositionOp::OP_OVER)
-    , mMaxTextureSize(aMaxTextureSize)
-    , mSupportsTextureBlitting(aSupportsTextureBlitting)
-    , mSupportsPartialUploads(aSupportsPartialUploads)
-    , mSyncHandle(aSyncHandle)
-  {}
+                                    SyncHandle aSyncHandle = 0,
+                                    IDXGISwapChain* aSwapChain = nullptr);
+  ~TextureFactoryIdentifier();
+
+  void SetSwapChain(IDXGISwapChain* aSwapChain);
 };
 
 /**
  * Information required by the compositor from the content-side for creating or
  * using compositables and textures.
  * XXX - TextureInfo is a bad name: this information is useful for the compositable,
  * not the Texture. And ith new Textures, only the compositable type is really
  * useful. This may (should) be removed in the near future.
--- a/gfx/layers/LayerTreeInvalidation.cpp
+++ b/gfx/layers/LayerTreeInvalidation.cpp
@@ -226,23 +226,23 @@ struct LayerPropertiesBase : public Laye
         AddRegion(result, tmp);
       }
     }
 
     mLayer->ClearInvalidRect();
     return result;
   }
 
-  IntRect NewTransformedBounds()
+  virtual IntRect NewTransformedBounds()
   {
     return TransformRect(mLayer->GetVisibleRegion().ToUnknownRegion().GetBounds(),
                          GetTransformForInvalidation(mLayer));
   }
 
-  IntRect OldTransformedBounds()
+  virtual IntRect OldTransformedBounds()
   {
     return TransformRect(mVisibleRegion.ToUnknownRegion().GetBounds(), mTransform);
   }
 
   virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
                                             bool& aGeometryChanged)
   {
     return IntRect();
@@ -268,18 +268,18 @@ struct ContainerLayerProperties : public
     , mPreXScale(aLayer->GetPreXScale())
     , mPreYScale(aLayer->GetPreYScale())
   {
     for (Layer* child = aLayer->GetFirstChild(); child; child = child->GetNextSibling()) {
       mChildren.AppendElement(Move(CloneLayerTreePropertiesInternal(child)));
     }
   }
 
-  virtual nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
-                                            bool& aGeometryChanged)
+  nsIntRegion ComputeChangeInternal(NotifySubDocInvalidationFunc aCallback,
+                                    bool& aGeometryChanged) override
   {
     ContainerLayer* container = mLayer->AsContainerLayer();
     nsIntRegion invalidOfLayer; // Invalid regions of this layer.
     nsIntRegion result;         // Invliad regions for children only.
 
     bool childrenChanged = false;
 
     if (mPreXScale != container->GetPreXScale() ||
@@ -377,16 +377,41 @@ struct ContainerLayerProperties : public
     }
     // else, effective transforms have applied on children.
 
     result.OrWith(invalidOfLayer);
 
     return result;
   }
 
+  IntRect NewTransformedBounds() override
+  {
+    if (mLayer->Extend3DContext()) {
+      IntRect result;
+      for (UniquePtr<LayerPropertiesBase>& child : mChildren) {
+        result = result.Union(child->NewTransformedBounds());
+      }
+      return result;
+    }
+
+    return LayerPropertiesBase::NewTransformedBounds();
+  }
+
+  IntRect OldTransformedBounds() override
+  {
+    if (mLayer->Extend3DContext()) {
+      IntRect result;
+      for (UniquePtr<LayerPropertiesBase>& child : mChildren) {
+        result = result.Union(child->OldTransformedBounds());
+      }
+      return result;
+    }
+    return LayerPropertiesBase::OldTransformedBounds();
+  }
+
   // The old list of children:
   AutoTArray<UniquePtr<LayerPropertiesBase>,1> mChildren;
   float mPreXScale;
   float mPreYScale;
 };
 
 struct ColorLayerProperties : public LayerPropertiesBase
 {
--- a/gfx/layers/TreeTraversal.h
+++ b/gfx/layers/TreeTraversal.h
@@ -3,29 +3,139 @@
 /* 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_layers_TreeTraversal_h
 #define mozilla_layers_TreeTraversal_h
 
 #include <queue>
-#include <stack>
 
 namespace mozilla {
 namespace layers {
 
 
 /*
- * Returned by |aAction| in ForEachNode. If the action returns
- * TraversalFlag::Skip, the node's children are not added to the traverrsal
- * stack. Otherwise, a return value of TraversalFlag::Continue indicates that
- * ForEachNode should traverse each of the node's children.
+ * Returned by |aPostAction| and |aPreAction| in ForEachNode, indicates
+ * the behavior to follow either action:
+ *
+ * TraversalFlag::Skip - the node's children are not traversed. If this
+ * flag is returned by |aPreAction|, |aPostAction| is skipped for the
+ * current node, as well.
+ * TraversalFlag::Continue - traversal continues normally.
+ * TraversalFlag::Abort - traversal stops immediately.
+ */
+enum class TraversalFlag { Skip, Continue, Abort };
+
+/*
+ * Do a depth-first traversal of the tree rooted at |aRoot|, performing
+ * |aPreAction| before traversal of children and |aPostAction| after.
+ *
+ * Returns true if traversal aborted, false if continued normally. If
+ * TraversalFlag::Skip is returned in |aPreAction|, then |aPostAction|
+ * is not performed.
+ */
+template <typename Node, typename PreAction, typename PostAction>
+static auto ForEachNode(Node* aRoot, const PreAction& aPreAction, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value &&
+                  IsSame<decltype(aPostAction(aRoot)),TraversalFlag>::value, bool>::Type
+{
+  if (!aRoot) {
+    return false;
+  }
+
+  TraversalFlag result = aPreAction(aRoot);
+
+  if (result == TraversalFlag::Abort) {
+    return true;
+  }
+
+  if (result == TraversalFlag::Continue) {
+    for (Node* child = aRoot->GetLastChild();
+         child;
+         child = child->GetPrevSibling()) {
+      bool abort = ForEachNode(child, aPreAction, aPostAction);
+      if (abort) {
+        return true;
+      }
+    }
+
+    result = aPostAction(aRoot);
+
+    if (result == TraversalFlag::Abort) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+/*
+ * Do a depth-first traversal of the tree rooted at |aRoot|, performing
+ * |aPreAction| before traversal of children and |aPostAction| after.
  */
-enum class TraversalFlag { Skip, Continue };
+template <typename Node, typename PreAction, typename PostAction>
+static auto ForEachNode(Node* aRoot, const PreAction& aPreAction, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), void>::value &&
+                  IsSame<decltype(aPostAction(aRoot)),void>::value, void>::Type
+{
+  if (!aRoot) {
+    return;
+  }
+
+  aPreAction(aRoot);
+
+  for (Node* child = aRoot->GetLastChild();
+       child;
+       child = child->GetPrevSibling()) {
+    ForEachNode(child, aPreAction, aPostAction);
+  }
+
+  aPostAction(aRoot);
+}
+
+/*
+ * ForEachNode pre-order traversal, using TraversalFlag.
+ */
+template <typename Node, typename PreAction>
+auto ForEachNode(Node* aRoot, const PreAction& aPreAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), TraversalFlag>::value, bool>::Type
+{
+  return ForEachNode(aRoot, aPreAction, [](Node* aNode){ return TraversalFlag::Continue; });
+}
+
+/*
+ * ForEachNode pre-order, not using TraversalFlag.
+ */
+template <typename Node, typename PreAction>
+auto ForEachNode(Node* aRoot, const PreAction& aPreAction) ->
+typename EnableIf<IsSame<decltype(aPreAction(aRoot)), void>::value, void>::Type
+{
+  ForEachNode(aRoot, aPreAction, [](Node* aNode){});
+}
+
+/*
+ * ForEachNode post-order traversal, using TraversalFlag.
+ */
+template <typename Node, typename PostAction>
+auto ForEachNodePostOrder(Node* aRoot, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPostAction(aRoot)), TraversalFlag>::value, bool>::Type
+{
+  return ForEachNode(aRoot, [](Node* aNode){ return TraversalFlag::Continue; }, aPostAction);
+}
+
+/*
+ * ForEachNode post-order, not using TraversalFlag.
+ */
+template <typename Node, typename PostAction>
+auto ForEachNodePostOrder(Node* aRoot, const PostAction& aPostAction) ->
+typename EnableIf<IsSame<decltype(aPostAction(aRoot)), void>::value, void>::Type
+{
+  ForEachNode(aRoot, [](Node* aNode){}, aPostAction);
+}
 
 /*
  * Do a breadth-first search of the tree rooted at |aRoot|, and return the
  * first visited node that satisfies |aCondition|, or nullptr if no such node
  * was found.
  *
  * |Node| should have methods GetLastChild() and GetPrevSibling().
  */
@@ -52,105 +162,59 @@ Node* BreadthFirstSearch(Node* aRoot, co
       queue.push(child);
     }
   }
 
   return nullptr;
 }
 
 /*
- * Do a depth-first search of the tree rooted at |aRoot|, and return the
- * first visited node that satisfies |aCondition|, or nullptr if no such node
- * was found.
+ * Do a pre-order, depth-first search of the tree rooted at |aRoot|, and
+ * return the first visited node that satisfies |aCondition|, or nullptr
+ * if no such node was found.
  *
  * |Node| should have methods GetLastChild() and GetPrevSibling().
  */
 template <typename Node, typename Condition>
 Node* DepthFirstSearch(Node* aRoot, const Condition& aCondition)
 {
-  if (!aRoot) {
-    return nullptr;
-  }
-
-  std::stack<Node*> stack;
-  stack.push(aRoot);
-  while (!stack.empty()) {
-    Node* node = stack.top();
-    stack.pop();
+  Node* result = nullptr;
 
-    if (aCondition(node)) {
-        return node;
-    }
+  ForEachNode(aRoot,
+      [&aCondition, &result](Node* aNode)
+      {
+        if (aCondition(aNode)) {
+          result = aNode;
+          return TraversalFlag::Abort;
+        }
 
-    for (Node* child = node->GetLastChild();
-         child;
-         child = child->GetPrevSibling()) {
-      stack.push(child);
-    }
-  }
+        return TraversalFlag::Continue;
+      });
 
-  return nullptr;
+  return result;
 }
 
 /*
- * Do a depth-first traversal of the tree rooted at |aRoot|, performing
- * |aAction| for each node.  |aAction| can return a TraversalFlag to determine
- * whether or not to omit the children of a particular node.
- *
- * If |aAction| does not return a TraversalFlag, it must return nothing.  There
- * is no ForEachNode instance handling types other than void or TraversalFlag.
+ * Perform a post-order, depth-first search starting at aRoot.
  */
-template <typename Node, typename Action>
-auto ForEachNode(Node* aRoot, const Action& aAction) ->
-typename EnableIf<IsSame<decltype(aAction(aRoot)), TraversalFlag>::value, void>::Type
+template <typename Node, typename Condition>
+Node* DepthFirstSearchPostOrder(Node* aRoot, const Condition& aCondition)
 {
-  if (!aRoot) {
-    return;
-  }
-
-  std::stack<Node*> stack;
-  stack.push(aRoot);
-
-  while (!stack.empty()) {
-    Node* node = stack.top();
-    stack.pop();
-
-    TraversalFlag result = aAction(node);
+  Node* result = nullptr;
 
-    if (result == TraversalFlag::Continue) {
-      for (Node* child = node->GetLastChild();
-           child;
-           child = child->GetPrevSibling()) {
-        stack.push(child);
-      }
-    }
-  }
-}
+  ForEachNodePostOrder(aRoot,
+      [&aCondition, &result](Node* aNode)
+      {
+        if (aCondition(aNode)) {
+          result = aNode;
+          return TraversalFlag::Abort;
+        }
 
-template <typename Node, typename Action>
-auto ForEachNode(Node* aRoot, const Action& aAction) ->
-typename EnableIf<IsSame<decltype(aAction(aRoot)), void>::value, void>::Type
-{
-  if (!aRoot) {
-    return;
-  }
-
-  std::stack<Node*> stack;
-  stack.push(aRoot);
+        return TraversalFlag::Continue;
+      });
 
-  while (!stack.empty()) {
-    Node* node = stack.top();
-    stack.pop();
-
-    aAction(node);
-
-    for (Node* child = node->GetLastChild();
-         child;
-         child = child->GetPrevSibling()) {
-      stack.push(child);
-    }
-  }
+  return result;
 }
 
 }
 }
 
 #endif // mozilla_layers_TreeTraversal_h
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -1,13 +1,14 @@
 /* -*- Mode: C++; tab-width: 8; 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 <stack>
 #include "APZCTreeManager.h"
 #include "AsyncPanZoomController.h"
 #include "Compositor.h"                 // for Compositor
 #include "gfxPrefs.h"                   // for gfxPrefs
 #include "HitTestingTreeNode.h"         // for HitTestingTreeNode
 #include "InputBlockState.h"            // for InputBlockState
 #include "InputData.h"                  // for InputData, etc
 #include "Layers.h"                     // for Layer, etc
@@ -1507,17 +1508,30 @@ APZCTreeManager::GetTargetAPZC(const Scr
   return apzc.forget();
 }
 
 already_AddRefed<HitTestingTreeNode>
 APZCTreeManager::GetTargetNode(const ScrollableLayerGuid& aGuid,
                                GuidComparator aComparator)
 {
   mTreeLock.AssertCurrentThreadOwns();
-  RefPtr<HitTestingTreeNode> target = FindTargetNode(mRootNode, aGuid, aComparator);
+  RefPtr<HitTestingTreeNode> target = DepthFirstSearchPostOrder(mRootNode.get(),
+      [&aGuid, &aComparator](HitTestingTreeNode* node)
+      {
+        bool matches = false;
+        if (node->GetApzc()) {
+          if (aComparator) {
+            matches = aComparator(aGuid, node->GetApzc()->GetGuid());
+          } else {
+            matches = node->GetApzc()->Matches(aGuid);
+          }
+        }
+        return matches;
+      }
+  );
   return target.forget();
 }
 
 already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint, HitTestResult* aOutHitResult)
 {
   MutexAutoLock lock(mTreeLock);
   HitTestResult hitResult = HitNothing;
@@ -1617,46 +1631,16 @@ APZCTreeManager::BuildOverscrollHandoffC
 
 /* static */ void
 APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled)
 {
   APZThreadUtils::RunOnControllerThread(
     NewRunnableFunction(GestureEventListener::SetLongTapEnabled, aLongTapEnabled));
 }
 
-HitTestingTreeNode*
-APZCTreeManager::FindTargetNode(HitTestingTreeNode* aNode,
-                                const ScrollableLayerGuid& aGuid,
-                                GuidComparator aComparator)
-{
-  mTreeLock.AssertCurrentThreadOwns();
-
-  // This walks the tree in depth-first, reverse order, so that it encounters
-  // APZCs front-to-back on the screen.
-  for (HitTestingTreeNode* node = aNode; node; node = node->GetPrevSibling()) {
-    HitTestingTreeNode* match = FindTargetNode(node->GetLastChild(), aGuid, aComparator);
-    if (match) {
-      return match;
-    }
-
-    bool matches = false;
-    if (node->GetApzc()) {
-      if (aComparator) {
-        matches = aComparator(aGuid, node->GetApzc()->GetGuid());
-      } else {
-        matches = node->GetApzc()->Matches(aGuid);
-      }
-    }
-    if (matches) {
-      return node;
-    }
-  }
-  return nullptr;
-}
-
 RefPtr<HitTestingTreeNode>
 APZCTreeManager::FindScrollNode(const AsyncDragMetrics& aDragMetrics)
 {
   MutexAutoLock lock(mTreeLock);
 
   return DepthFirstSearch(mRootNode.get(),
       [&aDragMetrics](HitTestingTreeNode* aNode) {
         return aNode->MatchesScrollDragMetrics(aDragMetrics);
@@ -1667,61 +1651,70 @@ AsyncPanZoomController*
 APZCTreeManager::GetAPZCAtPoint(HitTestingTreeNode* aNode,
                                 const ParentLayerPoint& aHitTestPoint,
                                 HitTestResult* aOutHitResult)
 {
   mTreeLock.AssertCurrentThreadOwns();
 
   // This walks the tree in depth-first, reverse order, so that it encounters
   // APZCs front-to-back on the screen.
-  for (HitTestingTreeNode* node = aNode; node; node = node->GetPrevSibling()) {
-    if (node->IsOutsideClip(aHitTestPoint)) {
-      // If the point being tested is outside the clip region for this node
-      // then we don't need to test against this node or any of its children.
-      // Just skip it and move on.
-      APZCTM_LOG("Point %f %f outside clip for node %p\n",
-        aHitTestPoint.x, aHitTestPoint.y, node);
-      continue;
-    }
-
-    AsyncPanZoomController* result = nullptr;
-
-    // First check the subtree rooted at this node, because deeper nodes
-    // are more "in front".
-    Maybe<LayerPoint> hitTestPointForChildLayers = node->Untransform(aHitTestPoint);
-    if (hitTestPointForChildLayers) {
-      ParentLayerPoint childPoint = ViewAs<ParentLayerPixel>(hitTestPointForChildLayers.ref(),
-        PixelCastJustification::MovingDownToChildren);
-      result = GetAPZCAtPoint(node->GetLastChild(), childPoint, aOutHitResult);
-    }
+  HitTestingTreeNode* resultNode;
+  HitTestingTreeNode* root = aNode;
+  std::stack<ParentLayerPoint> hitTestPoints;
+  hitTestPoints.push(aHitTestPoint);
 
-    // If we didn't match anything in the subtree, check |node|.
-    if (*aOutHitResult == HitNothing) {
-      APZCTM_LOG("Testing ParentLayer point %s (Layer %s) against node %p\n",
-          Stringify(aHitTestPoint).c_str(),
-          hitTestPointForChildLayers ? Stringify(hitTestPointForChildLayers.ref()).c_str() : "nil",
-          node);
-      HitTestResult hitResult = node->HitTest(aHitTestPoint);
-      if (hitResult != HitTestResult::HitNothing) {
-        result = node->GetNearestContainingApzcWithSameLayersId();
-        if (!result) {
-          result = FindRootApzcForLayersId(node->GetLayersId());
-          MOZ_ASSERT(result);
+  ForEachNode(root,
+      [&hitTestPoints](HitTestingTreeNode* aNode) {
+        if (aNode->IsOutsideClip(hitTestPoints.top())) {
+          // If the point being tested is outside the clip region for this node
+          // then we don't need to test against this node or any of its children.
+          // Just skip it and move on.
+          APZCTM_LOG("Point %f %f outside clip for node %p\n",
+            hitTestPoints.top().x, hitTestPoints.top().y, aNode);
+          return TraversalFlag::Skip;
+        }
+        // First check the subtree rooted at this node, because deeper nodes
+        // are more "in front".
+        Maybe<LayerPoint> hitTestPointForChildLayers = aNode->Untransform(hitTestPoints.top());
+        APZCTM_LOG("Transformed ParentLayer point %s to layer %s\n",
+                Stringify(hitTestPoints.top()).c_str(),
+                hitTestPointForChildLayers ? Stringify(hitTestPointForChildLayers.ref()).c_str() : "nil");
+        if (!hitTestPointForChildLayers) {
+          return TraversalFlag::Skip;
         }
-        APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n",
-             result, node, hitResult);
-        MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion);
-        // If event regions are disabled, *aOutHitResult will be HitLayer
-        *aOutHitResult = hitResult;
+        hitTestPoints.push(ViewAs<ParentLayerPixel>(hitTestPointForChildLayers.ref(),
+            PixelCastJustification::MovingDownToChildren));
+        return TraversalFlag::Continue;
+      },
+      [&resultNode, &hitTestPoints, &aOutHitResult](HitTestingTreeNode* aNode) {
+        hitTestPoints.pop();
+        HitTestResult hitResult = aNode->HitTest(hitTestPoints.top());
+        APZCTM_LOG("Testing ParentLayer point %s against node %p\n",
+                Stringify(hitTestPoints.top()).c_str(), aNode);
+        if (hitResult != HitTestResult::HitNothing) {
+          resultNode = aNode;
+          MOZ_ASSERT(hitResult == HitLayer || hitResult == HitDispatchToContentRegion);
+          // If event regions are disabled, *aOutHitResult will be HitLayer
+          *aOutHitResult = hitResult;
+          return TraversalFlag::Abort;
+        }
+        return TraversalFlag::Continue;
       }
-    }
+  );
 
-    if (*aOutHitResult != HitNothing) {
+  if (*aOutHitResult != HitNothing) {
+      MOZ_ASSERT(resultNode);
+      AsyncPanZoomController* result = resultNode->GetNearestContainingApzcWithSameLayersId();
+      if (!result) {
+        result = FindRootApzcForLayersId(resultNode->GetLayersId());
+        MOZ_ASSERT(result);
+      }
+      APZCTM_LOG("Successfully matched APZC %p via node %p (hit result %d)\n",
+          result, aNode, *aOutHitResult);
       return result;
-    }
   }
 
   return nullptr;
 }
 
 AsyncPanZoomController*
 APZCTreeManager::FindRootApzcForLayersId(uint64_t aLayersId) const
 {
--- a/gfx/layers/apz/test/mochitest/test_wheel_transactions.html
+++ b/gfx/layers/apz/test/mochitest/test_wheel_transactions.html
@@ -2,17 +2,19 @@
 <html>
 <!--
 https://bugzilla.mozilla.org/show_bug.cgi?id=1175585
 -->
 <head>
   <title>Test for Bug 1175585</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/paint_listener.js"></script>
   <script type="application/javascript" src="apz_test_native_event_utils.js"></script>
+  <script type="application/javascript" src="apz_test_utils.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <style>
   #outer-frame {
       height: 500px;
       overflow: scroll;
       background: repeating-linear-gradient(#CCC, #CCC 100px, #BBB 100px, #BBB 200px);
   }
   #inner-frame {
@@ -141,23 +143,29 @@ function driveTest() {
   }
   var ret = gTestContinuation.next();
   if (ret.done) {
     SimpleTest.finish();
   }
 }
 
 function startTest() {
-  // Disable smooth scrolling because it makes the test flaky (we don't have a good
-  // way of detecting when the scrolling is finished).
-  SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false],
-                                     ["layers.dump", true],
-                                     ["apz.printtree", true]]}, driveTest);
+  waitForAllPaints(function() {
+    flushApzRepaints(driveTest);
+  });
 }
 
+// Disable smooth scrolling because it makes the test flaky (we don't have a good
+// way of detecting when the scrolling is finished).
+SpecialPowers.pushPrefEnv({"set": [["general.smoothScroll", false],
+                                   ["layers.dump", true],
+                                   ["apz.printtree", true]]},
+                          function() {
+                            SimpleTest.waitForFocus(startTest, window);
+                          });
+
 SimpleTest.waitForExplicitFinish();
-SimpleTest.waitForFocus(startTest, window);
 
 </script>
 </pre>
 
 </body>
 </html>
--- a/gfx/layers/d3d11/CompositorD3D11.cpp
+++ b/gfx/layers/d3d11/CompositorD3D11.cpp
@@ -455,16 +455,17 @@ CompositorD3D11::CreateDataTextureSource
 TextureFactoryIdentifier
 CompositorD3D11::GetTextureFactoryIdentifier()
 {
   TextureFactoryIdentifier ident;
   ident.mMaxTextureSize = GetMaxTextureSize();
   ident.mParentProcessId = XRE_GetProcessType();
   ident.mParentBackend = LayersBackend::LAYERS_D3D11;
   ident.mSyncHandle = mAttachments->mSyncHandle;
+  ident.mSwapChain = mSwapChain;
   for (uint8_t op = 0; op < uint8_t(gfx::CompositionOp::OP_COUNT); op++) {
     if (BlendOpIsMixBlendMode(gfx::CompositionOp(op))) {
       ident.mSupportedBlendModes += gfx::CompositionOp(op);
     }
   }
   return ident;
 }
 
new file mode 100644
--- /dev/null
+++ b/gfx/tests/crashtests/1216832-1.html
@@ -0,0 +1,13 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+<meta charset="UTF-8">
+</head>
+<body>
+    <div style="transform-style: preserve-3d; border-bottom-style: outset; width: 4787550px;">
+        <div style="margin-left: 4787550px;"></div>
+        <div style="will-change: contents, transform;"></div>
+    </div>
+</body>
+</html>
--- a/gfx/tests/crashtests/crashtests.list
+++ b/gfx/tests/crashtests/crashtests.list
@@ -119,9 +119,10 @@ load 893572-2.html
 load 893572-3.html
 load 893572-4.html
 pref(layers.force-active,true) load 914457-1.html
 load 944579.svg
 load 944579.html
 pref(security.fileuri.strict_origin_policy,false) load 950000.html
 load 1034403-1.html
 load balinese-letter-spacing.html
+load 1216832-1.html
 load 1225125-1.html
--- a/gfx/tests/gtest/TestTreeTraversal.cpp
+++ b/gfx/tests/gtest/TestTreeTraversal.cpp
@@ -116,25 +116,25 @@ TEST(TreeTraversal, DepthFirstSearchValu
     } else if (i < expectedNeedleTraversalRank) {
       nodeList.push_back(new SearchTestNode(SearchNodeType::Hay, i));
     } else {
       nodeList.push_back(new SearchTestNode(SearchNodeType::Hay));
     }
   }
 
   RefPtr<SearchTestNode> root = nodeList[0];
+  nodeList[0]->AddChild(nodeList[4]);
   nodeList[0]->AddChild(nodeList[1]);
-  nodeList[0]->AddChild(nodeList[4]);
+  nodeList[1]->AddChild(nodeList[3]);
   nodeList[1]->AddChild(nodeList[2]);
-  nodeList[1]->AddChild(nodeList[3]);
+  nodeList[4]->AddChild(nodeList[6]);
   nodeList[4]->AddChild(nodeList[5]);
-  nodeList[4]->AddChild(nodeList[6]);
   nodeList[6]->AddChild(nodeList[7]);
+  nodeList[7]->AddChild(nodeList[9]);
   nodeList[7]->AddChild(nodeList[8]);
-  nodeList[7]->AddChild(nodeList[9]);
 
   RefPtr<SearchTestNode> foundNode = DepthFirstSearch(root.get(),
       [&visitCount](SearchTestNode* aNode)
       {
         aNode->SetActualTraversalRank(visitCount);
         visitCount++;
         return aNode->GetType() == SearchNodeType::Needle;
       });
@@ -180,43 +180,166 @@ TEST(TreeTraversal, DepthFirstSearchValu
   int visitCount = 0;
   std::vector<RefPtr<SearchTestNode>> nodeList;
   for (int i = 0; i < 10; i++)
   {
       nodeList.push_back(new SearchTestNode(SearchNodeType::Hay, i));
   }
 
   RefPtr<SearchTestNode> root = nodeList[0];
+  nodeList[0]->AddChild(nodeList[4]);
   nodeList[0]->AddChild(nodeList[1]);
-  nodeList[0]->AddChild(nodeList[4]);
+  nodeList[1]->AddChild(nodeList[3]);
   nodeList[1]->AddChild(nodeList[2]);
-  nodeList[1]->AddChild(nodeList[3]);
+  nodeList[4]->AddChild(nodeList[6]);
   nodeList[4]->AddChild(nodeList[5]);
-  nodeList[4]->AddChild(nodeList[6]);
   nodeList[6]->AddChild(nodeList[7]);
+  nodeList[7]->AddChild(nodeList[9]);
   nodeList[7]->AddChild(nodeList[8]);
-  nodeList[7]->AddChild(nodeList[9]);
 
 
   RefPtr<SearchTestNode> foundNode = DepthFirstSearch(root.get(),
       [&visitCount](SearchTestNode* aNode)
       {
         aNode->SetActualTraversalRank(visitCount);
-	visitCount++;
+        visitCount++;
         return aNode->GetType() == SearchNodeType::Needle;
       });
 
   for (int i = 0; i < 10; i++)
   {
-    ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), 
+    ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+        nodeList[i]->GetActualTraversalRank())
+        << "Node at index " << i << " was hit out of order.";
+  }
+
+  ASSERT_EQ(foundNode.get(), nullptr)
+      << "Search found something that should not exist.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderNull)
+{
+  RefPtr<SearchTestNode> nullNode;
+  RefPtr<SearchTestNode> result = DepthFirstSearchPostOrder(nullNode.get(),
+      [](SearchTestNode* aNode)
+      {
+        return aNode->GetType() == SearchNodeType::Needle;
+      });
+  ASSERT_EQ(result.get(), nullptr) << "Null root did not return null search result.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueExists)
+{
+  int visitCount = 0;
+  size_t expectedNeedleTraversalRank = 7;
+  RefPtr<SearchTestNode> needleNode;
+  std::vector<RefPtr<SearchTestNode>> nodeList;
+  for (size_t i = 0; i < 10; i++)
+  {
+    if (i == expectedNeedleTraversalRank) {
+      needleNode = new SearchTestNode(SearchNodeType::Needle, i);
+      nodeList.push_back(needleNode);
+    } else if (i < expectedNeedleTraversalRank) {
+      nodeList.push_back(new SearchTestNode(SearchNodeType::Hay, i));
+    } else {
+      nodeList.push_back(new SearchTestNode(SearchNodeType::Hay));
+    }
+  }
+
+  RefPtr<SearchTestNode> root = nodeList[9];
+  nodeList[9]->AddChild(nodeList[8]);
+  nodeList[9]->AddChild(nodeList[2]);
+  nodeList[2]->AddChild(nodeList[1]);
+  nodeList[2]->AddChild(nodeList[0]);
+  nodeList[8]->AddChild(nodeList[7]);
+  nodeList[8]->AddChild(nodeList[6]);
+  nodeList[6]->AddChild(nodeList[5]);
+  nodeList[5]->AddChild(nodeList[4]);
+  nodeList[5]->AddChild(nodeList[3]);
+
+  RefPtr<SearchTestNode> foundNode = DepthFirstSearchPostOrder(root.get(),
+      [&visitCount](SearchTestNode* aNode)
+      {
+        aNode->SetActualTraversalRank(visitCount);
+        visitCount++;
+        return aNode->GetType() == SearchNodeType::Needle;
+      });
+
+  for (size_t i = 0; i < nodeList.size(); i++)
+  {
+    ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
         nodeList[i]->GetActualTraversalRank())
         << "Node at index " << i << " was hit out of order.";
   }
 
-  ASSERT_EQ(foundNode.get(), nullptr) 
+  ASSERT_EQ(foundNode, needleNode) << "Search did not return expected node.";
+  ASSERT_EQ(foundNode->GetType(), SearchNodeType::Needle)
+      << "Returned node does not match expected value (something odd happened).";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderRootIsNeedle)
+{
+  RefPtr<SearchTestNode> root = new SearchTestNode(SearchNodeType::Needle, 0);
+  RefPtr<SearchTestNode> childNode1= new SearchTestNode(SearchNodeType::Hay);
+  RefPtr<SearchTestNode> childNode2 = new SearchTestNode(SearchNodeType::Hay);
+  int visitCount = 0;
+  RefPtr<SearchTestNode> result = DepthFirstSearchPostOrder(root.get(),
+      [&visitCount](SearchTestNode* aNode)
+      {
+        aNode->SetActualTraversalRank(visitCount);
+        visitCount++;
+        return aNode->GetType() == SearchNodeType::Needle;
+      });
+  ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
+  ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
+      << "Search starting at needle did not return needle.";
+  ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
+      childNode1->GetActualTraversalRank())
+      << "Search starting at needle continued past needle.";
+  ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
+      childNode2->GetActualTraversalRank())
+      << "Search starting at needle continued past needle.";
+}
+
+TEST(TreeTraversal, DepthFirstSearchPostOrderValueDoesNotExist)
+{
+  int visitCount = 0;
+  std::vector<RefPtr<SearchTestNode>> nodeList;
+  for (int i = 0; i < 10; i++)
+  {
+      nodeList.push_back(new SearchTestNode(SearchNodeType::Hay, i));
+  }
+
+  RefPtr<SearchTestNode> root = nodeList[9];
+  nodeList[9]->AddChild(nodeList[8]);
+  nodeList[9]->AddChild(nodeList[2]);
+  nodeList[2]->AddChild(nodeList[1]);
+  nodeList[2]->AddChild(nodeList[0]);
+  nodeList[8]->AddChild(nodeList[7]);
+  nodeList[8]->AddChild(nodeList[6]);
+  nodeList[6]->AddChild(nodeList[5]);
+  nodeList[5]->AddChild(nodeList[4]);
+  nodeList[5]->AddChild(nodeList[3]);
+
+  RefPtr<SearchTestNode> foundNode = DepthFirstSearchPostOrder(root.get(),
+      [&visitCount](SearchTestNode* aNode)
+      {
+        aNode->SetActualTraversalRank(visitCount);
+        visitCount++;
+        return aNode->GetType() == SearchNodeType::Needle;
+      });
+
+  for (int i = 0; i < 10; i++)
+  {
+    ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
+        nodeList[i]->GetActualTraversalRank())
+        << "Node at index " << i << " was hit out of order.";
+  }
+
+  ASSERT_EQ(foundNode.get(), nullptr)
       << "Search found something that should not exist.";
 }
 
 TEST(TreeTraversal, BreadthFirstSearchNull)
 {
   RefPtr<SearchTestNode> nullNode;
   RefPtr<SearchTestNode> result = BreadthFirstSearch(nullNode.get(),
       [](SearchTestNode* aNode)
@@ -238,20 +361,20 @@ TEST(TreeTraversal, BreadthFirstSearchRo
         aNode->SetActualTraversalRank(visitCount);
         visitCount++;
         return aNode->GetType() == SearchNodeType::Needle;
       });
   ASSERT_EQ(result, root) << "Search starting at needle did not return needle.";
   ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
       << "Search starting at needle did not return needle.";
   ASSERT_EQ(childNode1->GetExpectedTraversalRank(),
-      childNode1->GetActualTraversalRank()) 
+      childNode1->GetActualTraversalRank())
       << "Search starting at needle continued past needle.";
   ASSERT_EQ(childNode2->GetExpectedTraversalRank(),
-      childNode2->GetActualTraversalRank()) 
+      childNode2->GetActualTraversalRank())
       << "Search starting at needle continued past needle.";
 }
 
 TEST(TreeTraversal, BreadthFirstSearchValueExists)
 {
   int visitCount = 0;
   size_t expectedNeedleTraversalRank = 7;
   RefPtr<SearchTestNode> needleNode;
@@ -278,17 +401,17 @@ TEST(TreeTraversal, BreadthFirstSearchVa
   nodeList[6]->AddChild(nodeList[7]);
   nodeList[7]->AddChild(nodeList[9]);
   nodeList[7]->AddChild(nodeList[8]);
 
   RefPtr<SearchTestNode> foundNode = BreadthFirstSearch(root.get(),
       [&visitCount](SearchTestNode* aNode)
       {
         aNode->SetActualTraversalRank(visitCount);
-	visitCount++;
+        visitCount++;
         return aNode->GetType() == SearchNodeType::Needle;
       });
 
   for (size_t i = 0; i < nodeList.size(); i++)
   {
     ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
         nodeList[i]->GetActualTraversalRank())
         << "Node at index " << i << " was hit out of order.";
@@ -319,17 +442,17 @@ TEST(TreeTraversal, BreadthFirstSearchVa
   nodeList[7]->AddChild(nodeList[9]);
   nodeList[7]->AddChild(nodeList[8]);
 
 
   RefPtr<SearchTestNode> foundNode = BreadthFirstSearch(root.get(),
       [&visitCount](SearchTestNode* aNode)
       {
         aNode->SetActualTraversalRank(visitCount);
-	visitCount++;
+        visitCount++;
         return aNode->GetType() == SearchNodeType::Needle;
       });
 
   for (size_t i = 0; i < nodeList.size(); i++)
   {
       ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), 
           nodeList[i]->GetActualTraversalRank())
           << "Node at index " << i << " was hit out of order.";
@@ -354,34 +477,34 @@ TEST(TreeTraversal, ForEachNodeAllEligib
   std::vector<RefPtr<ForEachTestNode>> nodeList;
   int visitCount = 0;
   for (int i = 0; i < 10; i++)
   {
     nodeList.push_back(new ForEachTestNode(ForEachNodeType::Continue,i));
   }
 
   RefPtr<ForEachTestNode> root = nodeList[0];
+  nodeList[0]->AddChild(nodeList[4]);
   nodeList[0]->AddChild(nodeList[1]);
-  nodeList[0]->AddChild(nodeList[4]);
+  nodeList[1]->AddChild(nodeList[3]);
   nodeList[1]->AddChild(nodeList[2]);
-  nodeList[1]->AddChild(nodeList[3]);
+  nodeList[4]->AddChild(nodeList[6]);
   nodeList[4]->AddChild(nodeList[5]);
-  nodeList[4]->AddChild(nodeList[6]);
   nodeList[6]->AddChild(nodeList[7]);
+  nodeList[7]->AddChild(nodeList[9]);
   nodeList[7]->AddChild(nodeList[8]);
-  nodeList[7]->AddChild(nodeList[9]);
 
 
   ForEachNode(root.get(),
       [&visitCount](ForEachTestNode* aNode)
       {
         aNode->SetActualTraversalRank(visitCount);
-	visitCount++;
+        visitCount++;
         return aNode->GetType() == ForEachNodeType::Continue
-	    ? TraversalFlag::Continue : TraversalFlag::Skip;
+            ? TraversalFlag::Continue : TraversalFlag::Skip;
       });
 
   for (size_t i = 0; i < nodeList.size(); i++)
   {
     ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), 
         nodeList[i]->GetActualTraversalRank())
         << "Node at index " << i << " was hit out of order.";
   }
@@ -399,31 +522,31 @@ TEST(TreeTraversal, ForEachNodeSomeIneli
   expectedVisitedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Skip, 3));
 
   expectedSkippedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Continue));
   expectedSkippedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Continue));
   expectedSkippedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Skip));
   expectedSkippedNodeList.push_back(new ForEachTestNode(ForEachNodeType::Skip));
 
   RefPtr<ForEachTestNode> root = expectedVisitedNodeList[0];
+  expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]);
   expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[1]);
-  expectedVisitedNodeList[0]->AddChild(expectedVisitedNodeList[2]);
-  expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]);
   expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[1]);
+  expectedVisitedNodeList[1]->AddChild(expectedSkippedNodeList[0]);
   expectedVisitedNodeList[2]->AddChild(expectedVisitedNodeList[3]);
+  expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]);
   expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[2]);
-  expectedVisitedNodeList[3]->AddChild(expectedSkippedNodeList[3]);
 
   ForEachNode(root.get(),
       [&visitCount](ForEachTestNode* aNode)
       {
         aNode->SetActualTraversalRank(visitCount);
-	visitCount++;
+        visitCount++;
         return aNode->GetType() == ForEachNodeType::Continue
-	    ? TraversalFlag::Continue : TraversalFlag::Skip;
+            ? TraversalFlag::Continue : TraversalFlag::Skip;
       });
 
   for (size_t i = 0; i < expectedVisitedNodeList.size(); i++)
   {
     ASSERT_EQ(expectedVisitedNodeList[i]->GetExpectedTraversalRank(), 
         expectedVisitedNodeList[i]->GetActualTraversalRank())
         << "Node at index " << i << " was hit out of order.";
   }
@@ -443,19 +566,19 @@ TEST(TreeTraversal, ForEachNodeIneligibl
   RefPtr<ForEachTestNode> root = new ForEachTestNode(ForEachNodeType::Skip, 0);
   RefPtr<ForEachTestNode> childNode1 = new ForEachTestNode(ForEachNodeType::Continue);
   RefPtr<ForEachTestNode> chlidNode2 = new ForEachTestNode(ForEachNodeType::Skip);
 
   ForEachNode(root.get(),
       [&visitCount](ForEachTestNode* aNode)
       {
         aNode->SetActualTraversalRank(visitCount);
-	visitCount++;
+        visitCount++;
         return aNode->GetType() == ForEachNodeType::Continue
-	    ? TraversalFlag::Continue : TraversalFlag::Skip;
+            ? TraversalFlag::Continue : TraversalFlag::Skip;
       });
 
   ASSERT_EQ(root->GetExpectedTraversalRank(), root->GetActualTraversalRank())
       << "Root was hit out of order.";
   ASSERT_EQ(childNode1->GetExpectedTraversalRank(), childNode1->GetActualTraversalRank())
       << "Eligible child was still hit.";
   ASSERT_EQ(chlidNode2->GetExpectedTraversalRank(), chlidNode2->GetActualTraversalRank())
       << "Ineligible child was still hit.";
@@ -471,33 +594,33 @@ TEST(TreeTraversal, ForEachNodeLeavesIne
     if (i == 1 || i == 9) {
       nodeList.push_back(new ForEachTestNode(ForEachNodeType::Skip, i));
     } else {
       nodeList.push_back(new ForEachTestNode(ForEachNodeType::Continue, i));
     }
   }
 
   RefPtr<ForEachTestNode> root = nodeList[0];
+  nodeList[0]->AddChild(nodeList[2]);
   nodeList[0]->AddChild(nodeList[1]);
-  nodeList[0]->AddChild(nodeList[2]);
+  nodeList[2]->AddChild(nodeList[4]);
   nodeList[2]->AddChild(nodeList[3]);
-  nodeList[2]->AddChild(nodeList[4]);
+  nodeList[4]->AddChild(nodeList[6]);
   nodeList[4]->AddChild(nodeList[5]);
-  nodeList[4]->AddChild(nodeList[6]);
   nodeList[6]->AddChild(nodeList[7]);
+  nodeList[7]->AddChild(nodeList[9]);
   nodeList[7]->AddChild(nodeList[8]);
-  nodeList[7]->AddChild(nodeList[9]);
 
   ForEachNode(root.get(),
       [&visitCount](ForEachTestNode* aNode)
       {
         aNode->SetActualTraversalRank(visitCount);
-	visitCount++;
+        visitCount++;
         return aNode->GetType() == ForEachNodeType::Continue
-	    ? TraversalFlag::Continue : TraversalFlag::Skip;
+            ? TraversalFlag::Continue : TraversalFlag::Skip;
       });
 
   for (size_t i = 0; i < nodeList.size(); i++)
   {
     ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(), 
         nodeList[i]->GetActualTraversalRank())
         << "Node at index " << i << " was hit out of order.";
   }
@@ -508,32 +631,32 @@ TEST(TreeTraversal, ForEachNodeLambdaRet
   std::vector<RefPtr<ForEachTestNode>> nodeList;
   int visitCount = 0;
   for (int i = 0; i < 10; i++)
   {
     nodeList.push_back(new ForEachTestNode(ForEachNodeType::Continue,i));
   }
 
   RefPtr<ForEachTestNode> root = nodeList[0];
+  nodeList[0]->AddChild(nodeList[4]);
   nodeList[0]->AddChild(nodeList[1]);
-  nodeList[0]->AddChild(nodeList[4]);
+  nodeList[1]->AddChild(nodeList[3]);
   nodeList[1]->AddChild(nodeList[2]);
-  nodeList[1]->AddChild(nodeList[3]);
+  nodeList[4]->AddChild(nodeList[6]);
   nodeList[4]->AddChild(nodeList[5]);
-  nodeList[4]->AddChild(nodeList[6]);
   nodeList[6]->AddChild(nodeList[7]);
+  nodeList[7]->AddChild(nodeList[9]);
   nodeList[7]->AddChild(nodeList[8]);
-  nodeList[7]->AddChild(nodeList[9]);
 
 
   ForEachNode(root.get(),
       [&visitCount](ForEachTestNode* aNode)
       {
         aNode->SetActualTraversalRank(visitCount);
-	visitCount++;
+        visitCount++;
       });
 
   for (size_t i = 0; i < nodeList.size(); i++)
   {
     ASSERT_EQ(nodeList[i]->GetExpectedTraversalRank(),
         nodeList[i]->GetActualTraversalRank())
         << "Node at index " << i << " was hit out of order.";
   }
--- a/gfx/thebes/gfxContext.cpp
+++ b/gfx/thebes/gfxContext.cpp
@@ -849,19 +849,21 @@ gfxContext::PushGroupAndCopyBackground(g
     Save();
 
     if (pushOpaqueWithCopiedBG) {
       mDT->PushLayer(true, aOpacity, aMask, aMaskTransform, IntRect(), true);
     } else {
       mDT->PushLayer(content == gfxContentType::COLOR, aOpacity, aMask, aMaskTransform, IntRect(), false);
     }
   } else {
-    if (pushOpaqueWithCopiedBG) {
+    RefPtr<SourceSurface> source;
+    // This snapshot can be nullptr if the DrawTarget is a cairo target that is currently
+    // in an error state.
+    if (pushOpaqueWithCopiedBG && (source = mDT->Snapshot())) {
       DrawTarget *oldDT = mDT;
-      RefPtr<SourceSurface> source = mDT->Snapshot();
       Point oldDeviceOffset = CurrentState().deviceOffset;
 
       PushNewDT(gfxContentType::COLOR);
 
       if (oldDT == mDT) {
         // Creating new DT failed.
         return;
       }
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -2508,16 +2508,22 @@ gfxWindowsPlatform::InitializeD3D11()
     }
 
     // If we still have no device by now, exit.
     if (!mD3D11Device) {
       MOZ_ASSERT(IsFeatureStatusFailure(mD3D11Status));
       return;
     }
 
+    RefPtr<ID3D10Multithread> multi;
+    HRESULT hr = mD3D11Device->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi));
+    if (SUCCEEDED(hr) && multi) {
+      multi->SetMultithreadProtected(TRUE);
+    }
+
     // Either device creation function should have returned Available.
     MOZ_ASSERT(mD3D11Status == FeatureStatus::Available);
   } else {
     // Child processes do not need a compositor, but they do need to know
     // whether the parent process is using WARP and whether or not texture
     // sharing works.
     mIsWARP = !canUseHardware;
     mCompositorD3D11TextureSharingWorks = GetParentDevicePrefs().d3d11TextureSharingWorks();
--- a/image/Decoder.cpp
+++ b/image/Decoder.cpp
@@ -50,26 +50,17 @@ Decoder::~Decoder()
              "Destroying Decoder without taking all its progress changes");
   MOZ_ASSERT(mInvalidRect.IsEmpty() || !mImage,
              "Destroying Decoder without taking all its invalidations");
   mInitialized = false;
 
   if (mImage && !NS_IsMainThread()) {
     // Dispatch mImage to main thread to prevent it from being destructed by the
     // decode thread.
-    nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
-    NS_WARN_IF_FALSE(mainThread, "Couldn't get the main thread!");
-    if (mainThread) {
-      // Handle ambiguous nsISupports inheritance.
-      RasterImage* rawImg = nullptr;
-      mImage.swap(rawImg);
-      DebugOnly<nsresult> rv =
-        NS_ProxyRelease(mainThread, NS_ISUPPORTS_CAST(ImageResource*, rawImg));
-      MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed to proxy release to main thread");
-    }
+    NS_ReleaseOnMainThread(mImage.forget());
   }
 }
 
 /*
  * Common implementation of the decoder interface.
  */
 
 void
--- a/image/decoders/icon/mac/nsIconChannelCocoa.mm
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -33,22 +33,17 @@
 // nsIconChannel methods
 nsIconChannel::nsIconChannel()
 {
 }
 
 nsIconChannel::~nsIconChannel()
 {
   if (mLoadInfo) {
-    nsCOMPtr<nsIThread> mainThread;
-    NS_GetMainThread(getter_AddRefs(mainThread));
-
-    nsILoadInfo* forgetableLoadInfo;
-    mLoadInfo.forget(&forgetableLoadInfo);
-    NS_ProxyRelease(mainThread, forgetableLoadInfo, false);
+    NS_ReleaseOnMainThread(mLoadInfo.forget());
   }
 }
 
 NS_IMPL_ISUPPORTS(nsIconChannel,
                   nsIChannel,
                   nsIRequest,
                   nsIRequestObserver,
                   nsIStreamListener)
--- a/image/decoders/icon/win/nsIconChannel.cpp
+++ b/image/decoders/icon/win/nsIconChannel.cpp
@@ -71,22 +71,17 @@ GetStockIconIDForName(const nsACString& 
 // nsIconChannel methods
 nsIconChannel::nsIconChannel()
 {
 }
 
 nsIconChannel::~nsIconChannel()
 {
   if (mLoadInfo) {
-    nsCOMPtr<nsIThread> mainThread;
-    NS_GetMainThread(getter_AddRefs(mainThread));
-
-    nsILoadInfo* forgetableLoadInfo;
-    mLoadInfo.forget(&forgetableLoadInfo);
-    NS_ProxyRelease(mainThread, forgetableLoadInfo, false);
+    NS_ReleaseOnMainThread(mLoadInfo.forget());
   }
 }
 
 NS_IMPL_ISUPPORTS(nsIconChannel,
                   nsIChannel,
                   nsIRequest,
                   nsIRequestObserver,
                   nsIStreamListener)
--- a/js/public/MemoryMetrics.h
+++ b/js/public/MemoryMetrics.h
@@ -161,20 +161,21 @@ struct CStringHashPolicy
 
 namespace JS {
 
 struct ClassInfo
 {
 #define FOR_EACH_SIZE(macro) \
     macro(Objects, GCHeapUsed, objectsGCHeap) \
     macro(Objects, MallocHeap, objectsMallocHeapSlots) \
-    macro(Objects, MallocHeap, objectsMallocHeapElementsNonAsmJS) \
+    macro(Objects, MallocHeap, objectsMallocHeapElementsNormal) \
     macro(Objects, MallocHeap, objectsMallocHeapElementsAsmJS) \
+    macro(Objects, NonHeap,    objectsNonHeapElementsNormal) \
     macro(Objects, NonHeap,    objectsNonHeapElementsAsmJS) \
-    macro(Objects, NonHeap,    objectsNonHeapElementsMapped) \
+    macro(Objects, NonHeap,    objectsNonHeapElementsShared) \
     macro(Objects, NonHeap,    objectsNonHeapCodeAsmJS) \
     macro(Objects, MallocHeap, objectsMallocHeapMisc) \
     \
     macro(Other,   GCHeapUsed, shapesGCHeapTree) \
     macro(Other,   GCHeapUsed, shapesGCHeapDict) \
     macro(Other,   GCHeapUsed, shapesGCHeapBase) \
     macro(Other,   MallocHeap, shapesMallocHeapTreeTables) \
     macro(Other,   MallocHeap, shapesMallocHeapDictTables) \
--- a/js/src/asmjs/AsmJS.cpp
+++ b/js/src/asmjs/AsmJS.cpp
@@ -1563,23 +1563,16 @@ class MOZ_STACK_CLASS ModuleValidator
           : name(name), type(type)
         {}
 
         PropertyName* name;
         Scalar::Type type;
     };
 
   private:
-    struct SigHashPolicy
-    {
-        typedef const Sig& Lookup;
-        static HashNumber hash(Lookup sig) { return sig.hash(); }
-        static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; }
-    };
-    typedef HashMap<const DeclaredSig*, uint32_t, SigHashPolicy> SigMap;
     class NamedSig
     {
         PropertyName* name_;
         const DeclaredSig* sig_;
 
       public:
         NamedSig(PropertyName* name, const DeclaredSig& sig)
           : name_(name), sig_(&sig)
@@ -1600,16 +1593,17 @@ class MOZ_STACK_CLASS ModuleValidator
         static HashNumber hash(Lookup l) {
             return HashGeneric(l.name, l.sig.hash());
         }
         static bool match(NamedSig lhs, Lookup rhs) {
             return lhs.name_ == rhs.name && *lhs.sig_ == rhs.sig;
         }
     };
     typedef HashMap<NamedSig, uint32_t, NamedSig> ImportMap;
+    typedef HashMap<const DeclaredSig*, uint32_t, SigHashPolicy> SigMap;
     typedef HashMap<PropertyName*, Global*> GlobalMap;
     typedef HashMap<PropertyName*, MathBuiltin> MathNameMap;
     typedef HashMap<PropertyName*, AsmJSAtomicsBuiltinFunction> AtomicsNameMap;
     typedef HashMap<PropertyName*, SimdOperation> SimdOperationNameMap;
     typedef Vector<ArrayView> ArrayViewVector;
 
     ExclusiveContext*     cx_;
     AsmJSParser&          parser_;
--- a/js/src/asmjs/Wasm.cpp
+++ b/js/src/asmjs/Wasm.cpp
@@ -342,16 +342,43 @@ DecodeIfElse(FunctionDecoder& f, bool ha
     return DecodeExpr(f, ExprType::I32) &&
            DecodeExpr(f, expected) &&
            (hasElse
             ? DecodeExpr(f, expected)
             : CheckType(f, ExprType::Void, expected));
 }
 
 static bool
+DecodeLoadStoreAddress(FunctionDecoder &f)
+{
+    uint32_t offset, align;
+    return DecodeExpr(f, ExprType::I32) &&
+           f.d().readVarU32(&offset) &&
+           f.d().readVarU32(&align) &&
+           mozilla::IsPowerOfTwo(align) &&
+           (offset == 0 || f.fail("NYI: address offsets")) &&
+           f.fail("NYI: wasm loads and stores");
+}
+
+static bool
+DecodeLoad(FunctionDecoder& f, ExprType expected, ExprType type)
+{
+    return DecodeLoadStoreAddress(f) &&
+           CheckType(f, type, expected);
+}
+
+static bool
+DecodeStore(FunctionDecoder& f, ExprType expected, ExprType type)
+{
+    return DecodeLoadStoreAddress(f) &&
+           DecodeExpr(f, expected) &&
+           CheckType(f, type, expected);
+}
+
+static bool
 DecodeExpr(FunctionDecoder& f, ExprType expected)
 {
     Expr expr;
     if (!f.d().readExpr(&expr))
         return f.fail("unable to read expression");
 
     switch (expr) {
       case Expr::Nop:
@@ -534,16 +561,48 @@ DecodeExpr(FunctionDecoder& f, ExprType 
         return DecodeConversionOperator(f, expected, ExprType::F64, ExprType::I32);
       case Expr::F64ConvertSI64:
       case Expr::F64ConvertUI64:
       case Expr::F64ReinterpretI64:
         return f.fail("NYI: i64") &&
                DecodeConversionOperator(f, expected, ExprType::F64, ExprType::I64);
       case Expr::F64PromoteF32:
         return DecodeConversionOperator(f, expected, ExprType::F64, ExprType::F32);
+      case Expr::I32LoadMem:
+      case Expr::I32LoadMem8S:
+      case Expr::I32LoadMem8U:
+      case Expr::I32LoadMem16S:
+      case Expr::I32LoadMem16U:
+        return DecodeLoad(f, expected, ExprType::I32);
+      case Expr::I64LoadMem:
+      case Expr::I64LoadMem8S:
+      case Expr::I64LoadMem8U:
+      case Expr::I64LoadMem16S:
+      case Expr::I64LoadMem16U:
+      case Expr::I64LoadMem32S:
+      case Expr::I64LoadMem32U:
+        return DecodeLoad(f, expected, ExprType::I64);
+      case Expr::F32LoadMem:
+        return DecodeLoad(f, expected, ExprType::F32);
+      case Expr::F64LoadMem:
+        return DecodeLoad(f, expected, ExprType::F64);
+      case Expr::I32StoreMem:
+      case Expr::I32StoreMem8:
+      case Expr::I32StoreMem16:
+        return DecodeStore(f, expected, ExprType::I32);
+      case Expr::I64StoreMem:
+      case Expr::I64StoreMem8:
+      case Expr::I64StoreMem16:
+      case Expr::I64StoreMem32:
+        return f.fail("NYI: i64") &&
+               DecodeStore(f, expected, ExprType::I64);
+      case Expr::F32StoreMem:
+        return DecodeStore(f, expected, ExprType::F32);
+      case Expr::F64StoreMem:
+        return DecodeStore(f, expected, ExprType::F64);
       default:
         break;
     }
 
     return f.fail("bad expression code");
 }
 
 static bool
@@ -581,16 +640,18 @@ struct ImportName
     {}
 };
 
 typedef Vector<ImportName, 0, SystemAllocPolicy> ImportNameVector;
 
 /*****************************************************************************/
 // wasm decoding and generation
 
+typedef HashSet<const DeclaredSig*, SigHashPolicy> SigSet;
+
 static bool
 DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
 {
     if (!d.readCStringIf(SigSection))
         return true;
 
     uint32_t sectionStart;
     if (!d.startSection(&sectionStart))
@@ -601,16 +662,20 @@ DecodeSignatureSection(JSContext* cx, De
         return Fail(cx, d, "expected number of signatures");
 
     if (numSigs > MaxSigs)
         return Fail(cx, d, "too many signatures");
 
     if (!init->sigs.resize(numSigs))
         return false;
 
+    SigSet dupSet(cx);
+    if (!dupSet.init())
+        return false;
+
     for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
         uint32_t numArgs;
         if (!d.readVarU32(&numArgs))
             return Fail(cx, d, "bad number of signature args");
 
         if (numArgs > MaxArgsPerFunc)
             return Fail(cx, d, "too many arguments in signature");
 
@@ -623,16 +688,23 @@ DecodeSignatureSection(JSContext* cx, De
             return false;
 
         for (uint32_t i = 0; i < numArgs; i++) {
             if (!DecodeValType(cx, d, &args[i]))
                 return false;
         }
 
         init->sigs[sigIndex] = Sig(Move(args), result);
+
+        SigSet::AddPtr p = dupSet.lookupForAdd(init->sigs[sigIndex]);
+        if (p)
+            return Fail(cx, d, "duplicate signature");
+
+        if (!dupSet.add(p, &init->sigs[sigIndex]))
+            return false;
     }
 
     if (!d.finishSection(sectionStart))
         return Fail(cx, d, "decls section byte size mismatch");
 
     return true;
 }
 
@@ -1167,55 +1239,33 @@ ImportFunctions(JSContext* cx, HandleObj
 
         if (!imports.append(&v.toObject().as<JSFunction>()))
             return false;
     }
 
     return true;
 }
 
-static bool
-WasmEval(JSContext* cx, unsigned argc, Value* vp)
+bool
+wasm::Eval(JSContext* cx, Handle<ArrayBufferObject*> code,
+           HandleObject importObj, MutableHandleObject exportObj)
 {
     if (!CheckCompilerSupport(cx))
         return false;
 
-    CallArgs args = CallArgsFromVp(argc, vp);
-    RootedObject callee(cx, &args.callee());
-
-    if (args.length() < 1 || args.length() > 2) {
-        ReportUsageError(cx, callee, "Wrong number of arguments");
-        return false;
-    }
-
-    if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
-        ReportUsageError(cx, callee, "First argument must be an ArrayBuffer");
-        return false;
-    }
-
-    Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
     const uint8_t* bytes = code->dataPointer();
     uint32_t length = code->byteLength();
 
     Vector<uint8_t> copy(cx);
     if (code->hasInlineData()) {
         if (!copy.append(bytes, length))
             return false;
         bytes = copy.begin();
     }
 
-    RootedObject importObj(cx);
-    if (!args.get(1).isUndefined()) {
-        if (!args.get(1).isObject()) {
-            ReportUsageError(cx, callee, "Second argument, if present, must be an Object");
-            return false;
-        }
-        importObj = &args[1].toObject();
-    }
-
     UniqueChars file;
     if (!DescribeScriptedCaller(cx, &file))
         return false;
 
     ImportNameVector importNames;
     UniqueExportMap exportMap;
     Rooted<ArrayBufferObject*> heap(cx);
     Rooted<WasmModuleObject*> moduleObj(cx);
@@ -1224,18 +1274,52 @@ WasmEval(JSContext* cx, unsigned argc, V
             ReportOutOfMemory(cx);
         return false;
     }
 
     Rooted<FunctionVector> imports(cx, FunctionVector(cx));
     if (!ImportFunctions(cx, importObj, importNames, &imports))
         return false;
 
+    if (!moduleObj->module().dynamicallyLink(cx, moduleObj, heap, imports, *exportMap, exportObj))
+        return false;
+
+    return true;
+}
+
+
+static bool
+WasmEval(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    RootedObject callee(cx, &args.callee());
+
+    if (args.length() < 1 || args.length() > 2) {
+        ReportUsageError(cx, callee, "Wrong number of arguments");
+        return false;
+    }
+
+    if (!args[0].isObject() || !args[0].toObject().is<ArrayBufferObject>()) {
+        ReportUsageError(cx, callee, "First argument must be an ArrayBuffer");
+        return false;
+    }
+
+    RootedObject importObj(cx);
+    if (!args.get(1).isUndefined()) {
+        if (!args.get(1).isObject()) {
+            ReportUsageError(cx, callee, "Second argument, if present, must be an Object");
+            return false;
+        }
+        importObj = &args[1].toObject();
+    }
+
+    Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
+
     RootedObject exportObj(cx);
-    if (!moduleObj->module().dynamicallyLink(cx, moduleObj, heap, imports, *exportMap, &exportObj))
+    if (!Eval(cx, code, importObj, &exportObj))
         return false;
 
     args.rval().setObject(*exportObj);
     return true;
 }
 
 static bool
 WasmTextToBinary(JSContext* cx, unsigned argc, Value* vp)
--- a/js/src/asmjs/Wasm.h
+++ b/js/src/asmjs/Wasm.h
@@ -17,16 +17,19 @@
  */
 
 #ifndef wasm_h
 #define wasm_h
 
 #include "gc/Rooting.h"
 
 namespace js {
+
+class ArrayBufferObject;
+
 namespace wasm {
 
 // Add wasm testing JS functions to the given JS global object.
 bool
 DefineTestingFunctions(JSContext* cx, JS::HandleObject globalObj);
 
 // Return whether WebAssembly can be compiled on this platform.
 bool
@@ -39,12 +42,18 @@ static const unsigned PageSize = 64 * 10
 // When signal handling is used for bounds checking, MappedSize bytes are
 // reserved and the subrange [0, memory_size) is given readwrite permission.
 // See also static asserts in MIRGenerator::foldableOffsetRange.
 #ifdef ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB
 static const uint64_t Uint32Range = uint64_t(UINT32_MAX) + 1;
 static const uint64_t MappedSize = 2 * Uint32Range + PageSize;
 #endif
 
+// Compiles the given binary wasm module given the ArrayBufferObject
+// and links the module's imports with the given import object.
+bool
+Eval(JSContext* cx, JS::Handle<ArrayBufferObject*> code,
+     JS::HandleObject importObj, JS::MutableHandleObject exportObj);
+
 }  // namespace wasm
 }  // namespace js
 
 #endif // namespace wasm_h
--- a/js/src/asmjs/WasmText.cpp
+++ b/js/src/asmjs/WasmText.cpp
@@ -42,16 +42,18 @@ using mozilla::Maybe;
 using mozilla::PositiveInfinity;
 using mozilla::SpecificNaN;
 
 static const unsigned AST_LIFO_DEFAULT_CHUNK_SIZE = 4096;
 
 /*****************************************************************************/
 // wasm AST
 
+namespace {
+
 class WasmAstExpr;
 
 template <class T>
 using WasmAstVector = mozilla::Vector<T, 0, LifoAllocPolicy<Fallible>>;
 
 template <class K, class V, class HP>
 using WasmAstHashMap = HashMap<K, V, HP, LifoAllocPolicy<Fallible>>;
 
@@ -103,18 +105,20 @@ enum class WasmAstExprKind
     BinaryOperator,
     Block,
     Call,
     ComparisonOperator,
     Const,
     ConversionOperator,
     GetLocal,
     IfElse,
+    Load,
     Nop,
     SetLocal,
+    Store,
     UnaryOperator,
 };
 
 class WasmAstExpr : public WasmAstNode
 {
     const WasmAstExprKind kind_;
 
   protected:
@@ -238,16 +242,73 @@ class WasmAstIfElse : public WasmAstExpr
 
     bool hasElse() const { return expr_ == Expr::IfElse; }
     Expr expr() const { return expr_; }
     WasmAstExpr& cond() const { return *cond_; }
     WasmAstExpr& ifBody() const { return *ifBody_; }
     WasmAstExpr& elseBody() const { return *elseBody_; }
 };
 
+class WasmAstLoadStoreAddress
+{
+    WasmAstExpr* base_;
+    int32_t offset_;
+    int32_t align_;
+
+  public:
+    explicit WasmAstLoadStoreAddress(WasmAstExpr* base, int32_t offset,
+                                     int32_t align)
+      : base_(base),
+        offset_(offset),
+        align_(align)
+    {}
+
+    WasmAstExpr& base() const { return *base_; }
+    int32_t offset() const { return offset_; }
+    int32_t align() const { return align_; }
+};
+
+class WasmAstLoad : public WasmAstExpr
+{
+    Expr expr_;
+    WasmAstLoadStoreAddress address_;
+
+  public:
+    static const WasmAstExprKind Kind = WasmAstExprKind::Load;
+    explicit WasmAstLoad(Expr expr, const WasmAstLoadStoreAddress &address)
+      : WasmAstExpr(Kind),
+        expr_(expr),
+        address_(address)
+    {}
+
+    Expr expr() const { return expr_; }
+    const WasmAstLoadStoreAddress& address() const { return address_; }
+};
+
+class WasmAstStore : public WasmAstExpr
+{
+    Expr expr_;
+    WasmAstLoadStoreAddress address_;
+    WasmAstExpr* value_;
+
+  public:
+    static const WasmAstExprKind Kind = WasmAstExprKind::Store;
+    explicit WasmAstStore(Expr expr, const WasmAstLoadStoreAddress &address,
+                          WasmAstExpr* value)
+      : WasmAstExpr(Kind),
+        expr_(expr),
+        address_(address),
+        value_(value)
+    {}
+
+    Expr expr() const { return expr_; }
+    const WasmAstLoadStoreAddress& address() const { return address_; }
+    WasmAstExpr& value() const { return *value_; }
+};
+
 class WasmAstFunc : public WasmAstNode
 {
     const uint32_t sigIndex_;
     WasmAstValTypeVector varTypes_;
     WasmAstExpr* const maybeBody_;
 
   public:
     WasmAstFunc(uint32_t sigIndex, WasmAstValTypeVector&& varTypes, WasmAstExpr* maybeBody)
@@ -462,62 +523,71 @@ class WasmAstConversionOperator final : 
       : WasmAstExpr(Kind),
         expr_(expr), op_(op)
     {}
 
     Expr expr() const { return expr_; }
     WasmAstExpr* op() const { return op_; }
 };
 
+} // end anonymous namespace
+
 /*****************************************************************************/
 // wasm text token stream
 
+namespace {
+
 class WasmToken
 {
   public:
     enum FloatLiteralKind
     {
         HexNumber,
         DecNumber,
         Infinity,
         NaN
     };
 
     enum Kind
     {
+        Align,
         BinaryOpcode,
         Block,
         Call,
         CallImport,
         CloseParen,
         ComparisonOpcode,
         Const,
         ConversionOpcode,
         EndOfFile,
+        Equal,
         Error,
         Export,
         Float,
         Func,
         GetLocal,
         If,
         IfElse,
         Import,
         Index,
         UnsignedInteger,
         SignedInteger,
         Memory,
+        Load,
         Local,
         Module,
         Name,
         Nop,
+        Offset,
         OpenParen,
         Param,
         Result,
         Segment,
         SetLocal,
+        Store,
         Text,
         UnaryOpcode,
         ValueType
     };
   private:
     Kind kind_;
     const char16_t* begin_;
     const char16_t* end_;
@@ -583,17 +653,17 @@ class WasmToken
     }
     explicit WasmToken(Kind kind, Expr expr, const char16_t* begin, const char16_t* end)
       : kind_(kind),
         begin_(begin),
         end_(end)
     {
         MOZ_ASSERT(begin != end);
         MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == ComparisonOpcode ||
-                   kind_ == ConversionOpcode);
+                   kind_ == ConversionOpcode || kind_ == Load || kind_ == Store);
         u.expr_ = expr;
     }
     explicit WasmToken(const char16_t* begin)
       : kind_(Error),
         begin_(begin),
         end_(begin)
     {}
     Kind kind() const {
@@ -629,17 +699,17 @@ class WasmToken
         return u.floatLiteralKind_;
     }
     ValType valueType() const {
         MOZ_ASSERT(kind_ == ValueType || kind_ == Const);
         return u.valueType_;
     }
     Expr expr() const {
         MOZ_ASSERT(kind_ == UnaryOpcode || kind_ == BinaryOpcode || kind_ == ComparisonOpcode ||
-                   kind_ == ConversionOpcode);
+                   kind_ == ConversionOpcode || kind_ == Load || kind_ == Store);
         return u.expr_;
     }
 };
 
 static bool
 IsWasmNewLine(char16_t c)
 {
     return c == '\n';
@@ -833,596 +903,17 @@ class WasmTokenStream
                 return false;
         }
         cur_ = p;
         return true;
     }
     WasmToken fail(const char16_t* begin) const {
         return WasmToken(begin);
     }
-    WasmToken next() {
-        while (cur_ != end_ && IsWasmSpace(*cur_)) {
-            if (IsWasmNewLine(*cur_++)) {
-                lineStart_ = cur_;
-                line_++;
-            }
-        }
-
-        if (cur_ == end_)
-            return WasmToken(WasmToken::EndOfFile, cur_, cur_);
-
-        const char16_t* begin = cur_;
-        switch (*begin) {
-          case '"':
-            cur_++;
-            while (true) {
-                if (cur_ == end_)
-                    return fail(begin);
-                if (*cur_ == '"')
-                    break;
-                if (!ConsumeTextByte(&cur_, end_))
-                    return fail(begin);
-            }
-            cur_++;
-            return WasmToken(WasmToken::Text, begin, cur_);
-
-          case '$':
-            cur_++;
-            while (cur_ != end_ && IsNameAfterDollar(*cur_))
-                cur_++;
-            return WasmToken(WasmToken::Name, begin, cur_);
-
-          case '(':
-            cur_++;
-            return WasmToken(WasmToken::OpenParen, begin, cur_);
-
-          case ')':
-            cur_++;
-            return WasmToken(WasmToken::CloseParen, begin, cur_);
-
-          case '+': case '-':
-            cur_++;
-            if (consume(MOZ_UTF16("infinity")))
-                goto infinity;
-            if (consume(MOZ_UTF16("nan")))
-                goto nan;
-            if (!IsWasmDigit(*cur_))
-                break;
-            MOZ_FALLTHROUGH;
-          case '0': case '1': case '2': case '3': case '4':
-          case '5': case '6': case '7': case '8': case '9': {
-            CheckedInt<uint64_t> u = 0;
-            if (consume(MOZ_UTF16("0x"))) {
-                if (cur_ == end_)
-                    return fail(begin);
-                do {
-                    if (*cur_ == '.' || *cur_ == 'p')
-                        return LexHexFloatLiteral(begin, end_, &cur_);
-                    uint8_t digit;
-                    if (!IsHexDigit(*cur_, &digit))
-                        break;
-                    u *= 16;
-                    u += digit;
-                    if (!u.isValid())
-                        return fail(begin);
-                    cur_++;
-                } while (cur_ != end_);
-            } else {
-                while (cur_ != end_) {
-                    if (*cur_ == '.' || *cur_ == 'e')
-                        return LexDecFloatLiteral(begin, end_, &cur_);
-                    if (!IsWasmDigit(*cur_))
-                        break;
-                    u *= 10;
-                    u += *cur_ - '0';
-                    if (!u.isValid())
-                        return fail(begin);
-                    cur_++;
-                }
-            }
-
-            uint64_t value = u.value();
-            if (*begin == '-') {
-                if (value > uint64_t(INT64_MIN))
-                    return fail(begin);
-                value = -value;
-                return WasmToken(int64_t(value), begin, cur_);
-            }
-
-            CheckedInt<uint32_t> index = u.value();
-            if (index.isValid())
-                return WasmToken(index.value(), begin, cur_);
-
-            return WasmToken(value, begin, cur_);
-          }
-
-          case 'b':
-            if (consume(MOZ_UTF16("block")))
-                return WasmToken(WasmToken::Block, begin, cur_);
-            break;
-
-          case 'c':
-            if (consume(MOZ_UTF16("call"))) {
-                if (consume(MOZ_UTF16("_import")))
-                    return WasmToken(WasmToken::CallImport, begin, cur_);
-                return WasmToken(WasmToken::Call, begin, cur_);
-            }
-            break;
-
-          case 'e':
-            if (consume(MOZ_UTF16("export")))
-                return WasmToken(WasmToken::Export, begin, cur_);
-            break;
-
-          case 'f':
-            if (consume(MOZ_UTF16("func")))
-                return WasmToken(WasmToken::Func, begin, cur_);
-
-            if (consume(MOZ_UTF16("f32"))) {
-                if (!consume(MOZ_UTF16(".")))
-                    return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_);
-
-                switch (*cur_) {
-                  case 'a':
-                    if (consume(MOZ_UTF16("abs")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F32Abs, begin, cur_);
-                    if (consume(MOZ_UTF16("add")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F32Add, begin, cur_);
-                    break;
-                  case 'c':
-                    if (consume(MOZ_UTF16("ceil")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F32Ceil, begin, cur_);
-                    if (consume(MOZ_UTF16("const")))
-                        return WasmToken(WasmToken::Const, ValType::F32, begin, cur_);
-                    if (consume(MOZ_UTF16("convert_s/i32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertSI32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("convert_u/i32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertUI32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("copysign")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F32CopySign, begin, cur_);
-                    break;
-                  case 'd':
-                    if (consume(MOZ_UTF16("demote/f64")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::F32DemoteF64,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("div")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F32Div, begin, cur_);
-                    break;
-                  case 'e':
-                    if (consume(MOZ_UTF16("eq")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Eq, begin, cur_);
-                    break;
-                  case 'f':
-                    if (consume(MOZ_UTF16("floor")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F32Floor, begin, cur_);
-                    break;
-                  case 'g':
-                    if (consume(MOZ_UTF16("ge")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ge, begin, cur_);
-                    if (consume(MOZ_UTF16("gt")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Gt, begin, cur_);
-                    break;
-                  case 'l':
-                    if (consume(MOZ_UTF16("le")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Le, begin, cur_);
-                    if (consume(MOZ_UTF16("lt")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Lt, begin, cur_);
-                    break;
-                  case 'm':
-                    if (consume(MOZ_UTF16("max")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F32Max, begin, cur_);
-                    if (consume(MOZ_UTF16("min")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F32Min, begin, cur_);
-                    if (consume(MOZ_UTF16("mul")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F32Mul, begin, cur_);
-                    break;
-                  case 'n':
-                    if (consume(MOZ_UTF16("nearest")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F32Nearest, begin, cur_);
-                    if (consume(MOZ_UTF16("neg")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F32Neg, begin, cur_);
-                    if (consume(MOZ_UTF16("ne")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ne, begin, cur_);
-                    break;
-                  case 'r':
-                    if (consume(MOZ_UTF16("reinterpret/i32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::F32ReinterpretI32,
-                                         begin, cur_);
-                    break;
-                  case 's':
-                    if (consume(MOZ_UTF16("sqrt")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F32Sqrt, begin, cur_);
-                    if (consume(MOZ_UTF16("sub")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F32Sub, begin, cur_);
-                    break;
-                  case 't':
-                    if (consume(MOZ_UTF16("trunc")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F32Trunc, begin, cur_);
-                    break;
-                }
-                break;
-            }
-            if (consume(MOZ_UTF16("f64"))) {
-                if (!consume(MOZ_UTF16(".")))
-                    return WasmToken(WasmToken::ValueType, ValType::F64, begin, cur_);
-
-                switch (*cur_) {
-                  case 'a':
-                    if (consume(MOZ_UTF16("abs")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F64Abs, begin, cur_);
-                    if (consume(MOZ_UTF16("add")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F64Add, begin, cur_);
-                    break;
-                  case 'c':
-                    if (consume(MOZ_UTF16("ceil")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F64Ceil, begin, cur_);
-                    if (consume(MOZ_UTF16("const")))
-                        return WasmToken(WasmToken::Const, ValType::F64, begin, cur_);
-                    if (consume(MOZ_UTF16("convert_s/i32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertSI32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("convert_u/i32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertUI32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("copysign")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F64CopySign, begin, cur_);
-                    break;
-                  case 'd':
-                    if (consume(MOZ_UTF16("div")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F64Div, begin, cur_);
-                    break;
-                  case 'e':
-                    if (consume(MOZ_UTF16("eq")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Eq, begin, cur_);
-                    break;
-                  case 'f':
-                    if (consume(MOZ_UTF16("floor")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F64Floor, begin, cur_);
-                    break;
-                  case 'g':
-                    if (consume(MOZ_UTF16("ge")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ge, begin, cur_);
-                    if (consume(MOZ_UTF16("gt")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Gt, begin, cur_);
-                    break;
-                  case 'l':
-                    if (consume(MOZ_UTF16("le")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Le, begin, cur_);
-                    if (consume(MOZ_UTF16("lt")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Lt, begin, cur_);
-                    break;
-                  case 'm':
-                    if (consume(MOZ_UTF16("max")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F64Max, begin, cur_);
-                    if (consume(MOZ_UTF16("min")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F64Min, begin, cur_);
-                    if (consume(MOZ_UTF16("mul")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F64Mul, begin, cur_);
-                    break;
-                  case 'n':
-                    if (consume(MOZ_UTF16("nearest")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F64Nearest, begin, cur_);
-                    if (consume(MOZ_UTF16("neg")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F64Neg, begin, cur_);
-                    if (consume(MOZ_UTF16("ne")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ne, begin, cur_);
-                    break;
-                  case 'p':
-                    if (consume(MOZ_UTF16("promote/f32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::F64PromoteF32,
-                                         begin, cur_);
-                    break;
-                  case 's':
-                    if (consume(MOZ_UTF16("sqrt")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F64Sqrt, begin, cur_);
-                    if (consume(MOZ_UTF16("sub")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::F64Sub, begin, cur_);
-                    break;
-                  case 't':
-                    if (consume(MOZ_UTF16("trunc")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::F64Trunc, begin, cur_);
-                    break;
-                }
-                break;
-            }
-            break;
-
-          case 'g':
-            if (consume(MOZ_UTF16("get_local")))
-                return WasmToken(WasmToken::GetLocal, begin, cur_);
-            break;
-
-          case 'i':
-            if (consume(MOZ_UTF16("i32"))) {
-                if (!consume(MOZ_UTF16(".")))
-                    return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_);
-
-                switch (*cur_) {
-                  case 'a':
-                    if (consume(MOZ_UTF16("add")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32Add, begin, cur_);
-                    if (consume(MOZ_UTF16("and")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32And, begin, cur_);
-                    break;
-                  case 'c':
-                    if (consume(MOZ_UTF16("const")))
-                        return WasmToken(WasmToken::Const, ValType::I32, begin, cur_);
-                    if (consume(MOZ_UTF16("clz")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::I32Clz, begin, cur_);
-                    if (consume(MOZ_UTF16("ctz")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::I32Ctz, begin, cur_);
-                    break;
-                  case 'd':
-                    if (consume(MOZ_UTF16("div_s")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivS, begin, cur_);
-                    if (consume(MOZ_UTF16("div_u")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivU, begin, cur_);
-                    break;
-                  case 'e':
-                    if (consume(MOZ_UTF16("eq")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Eq, begin, cur_);
-                    break;
-                  case 'g':
-                    if (consume(MOZ_UTF16("ge_s")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeS, begin, cur_);
-                    if (consume(MOZ_UTF16("ge_u")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeU, begin, cur_);
-                    if (consume(MOZ_UTF16("gt_s")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtS, begin, cur_);
-                    if (consume(MOZ_UTF16("gt_u")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtU, begin, cur_);
-                    break;
-                  case 'l':
-                    if (consume(MOZ_UTF16("le_s")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeS, begin, cur_);
-                    if (consume(MOZ_UTF16("le_u")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeU, begin, cur_);
-                    if (consume(MOZ_UTF16("lt_s")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtS, begin, cur_);
-                    if (consume(MOZ_UTF16("lt_u")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtU, begin, cur_);
-                    break;
-                  case 'm':
-                    if (consume(MOZ_UTF16("mul")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32Mul, begin, cur_);
-                    break;
-                  case 'n':
-                    if (consume(MOZ_UTF16("ne")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Ne, begin, cur_);
-                    break;
-                  case 'o':
-                    if (consume(MOZ_UTF16("or")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32Or, begin, cur_);
-                    break;
-                  case 'p':
-                    if (consume(MOZ_UTF16("popcnt")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::I32Popcnt, begin, cur_);
-                    break;
-                  case 'r':
-                    if (consume(MOZ_UTF16("reinterpret/f32")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::I32ReinterpretF32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("rem_s")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemS, begin, cur_);
-                    if (consume(MOZ_UTF16("rem_u")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemU, begin, cur_);
-                    break;
-                  case 's':
-                    if (consume(MOZ_UTF16("sub")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32Sub, begin, cur_);
-                    if (consume(MOZ_UTF16("shl")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32Shl, begin, cur_);
-                    if (consume(MOZ_UTF16("shr_s")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrS, begin, cur_);
-                    if (consume(MOZ_UTF16("shr_u")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrU, begin, cur_);
-                    break;
-                  case 't':
-                    if (consume(MOZ_UTF16("trunc_s/f32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("trunc_s/f64")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF64,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("trunc_u/f32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("trunc_u/f64")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF64,
-                                         begin, cur_);
-                    break;
-                  case 'w':
-                    if (consume(MOZ_UTF16("wrap/i64")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I32WrapI64,
-                                         begin, cur_);
-                    break;
-                  case 'x':
-                    if (consume(MOZ_UTF16("xor")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I32Xor, begin, cur_);
-                    break;
-                }
-                break;
-            }
-            if (consume(MOZ_UTF16("i64"))) {
-                if (!consume(MOZ_UTF16(".")))
-                    return WasmToken(WasmToken::ValueType, ValType::I64, begin, cur_);
-
-                switch (*cur_) {
-                  case 'a':
-                    if (consume(MOZ_UTF16("add")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64Add, begin, cur_);
-                    if (consume(MOZ_UTF16("and")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64And, begin, cur_);
-                    break;
-                  case 'c':
-                    if (consume(MOZ_UTF16("const")))
-                        return WasmToken(WasmToken::Const, ValType::I64, begin, cur_);
-                    if (consume(MOZ_UTF16("clz")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::I64Clz, begin, cur_);
-                    if (consume(MOZ_UTF16("ctz")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::I64Ctz, begin, cur_);
-                    break;
-                  case 'd':
-                    if (consume(MOZ_UTF16("div_s")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivS, begin, cur_);
-                    if (consume(MOZ_UTF16("div_u")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivU, begin, cur_);
-                    break;
-                  case 'e':
-                    if (consume(MOZ_UTF16("eq")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Eq, begin, cur_);
-                    if (consume(MOZ_UTF16("extend_s/i32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendSI32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("extend_u/i32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendUI32,
-                                         begin, cur_);
-                    break;
-                  case 'g':
-                    if (consume(MOZ_UTF16("ge_s")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeS, begin, cur_);
-                    if (consume(MOZ_UTF16("ge_u")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeU, begin, cur_);
-                    if (consume(MOZ_UTF16("gt_s")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtS, begin, cur_);
-                    if (consume(MOZ_UTF16("gt_u")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtU, begin, cur_);
-                    break;
-                  case 'l':
-                    if (consume(MOZ_UTF16("le_s")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeS, begin, cur_);
-                    if (consume(MOZ_UTF16("le_u")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeU, begin, cur_);
-                    if (consume(MOZ_UTF16("lt_s")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtS, begin, cur_);
-                    if (consume(MOZ_UTF16("lt_u")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtU, begin, cur_);
-                    break;
-                  case 'm':
-                    if (consume(MOZ_UTF16("mul")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64Mul, begin, cur_);
-                    break;
-                  case 'n':
-                    if (consume(MOZ_UTF16("ne")))
-                        return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Ne, begin, cur_);
-                    break;
-                  case 'o':
-                    if (consume(MOZ_UTF16("or")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64Or, begin, cur_);
-                    break;
-                  case 'p':
-                    if (consume(MOZ_UTF16("popcnt")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::I64Popcnt, begin, cur_);
-                    break;
-                  case 'r':
-                    if (consume(MOZ_UTF16("reinterpret/f64")))
-                        return WasmToken(WasmToken::UnaryOpcode, Expr::I64ReinterpretF64,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("rem_s")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemS, begin, cur_);
-                    if (consume(MOZ_UTF16("rem_u")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemU, begin, cur_);
-                    break;
-                  case 's':
-                    if (consume(MOZ_UTF16("sub")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64Sub, begin, cur_);
-                    if (consume(MOZ_UTF16("shl")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64Shl, begin, cur_);
-                    if (consume(MOZ_UTF16("shr_s")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrS, begin, cur_);
-                    if (consume(MOZ_UTF16("shr_u")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrU, begin, cur_);
-                    break;
-                  case 't':
-                    if (consume(MOZ_UTF16("trunc_s/f32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("trunc_s/f64")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF64,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("trunc_u/f32")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF32,
-                                         begin, cur_);
-                    if (consume(MOZ_UTF16("trunc_u/f64")))
-                        return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF64,
-                                         begin, cur_);
-                    break;
-                  case 'x':
-                    if (consume(MOZ_UTF16("xor")))
-                        return WasmToken(WasmToken::BinaryOpcode, Expr::I64Xor, begin, cur_);
-                    break;
-                }
-                break;
-            }
-            if (consume(MOZ_UTF16("import")))
-                return WasmToken(WasmToken::Import, begin, cur_);
-            if (consume(MOZ_UTF16("infinity"))) {
-            infinity:
-                return WasmToken(WasmToken::Infinity, begin, cur_);
-            }
-            if (consume(MOZ_UTF16("if"))) {
-                if (consume(MOZ_UTF16("_else")))
-                    return WasmToken(WasmToken::IfElse, begin, cur_);
-                return WasmToken(WasmToken::If, begin, cur_);
-            }
-            break;
-
-          case 'l':
-            if (consume(MOZ_UTF16("local")))
-                return WasmToken(WasmToken::Local, begin, cur_);
-            break;
-
-          case 'm':
-            if (consume(MOZ_UTF16("module")))
-                return WasmToken(WasmToken::Module, begin, cur_);
-            if (consume(MOZ_UTF16("memory")))
-                return WasmToken(WasmToken::Memory, begin, cur_);
-            break;
-
-          case 'n':
-            if (consume(MOZ_UTF16("nan"))) {
-            nan:
-                if (consume(MOZ_UTF16(":"))) {
-                    if (!consume(MOZ_UTF16("0x")))
-                        break;
-                    uint8_t digit;
-                    while (cur_ != end_ && IsHexDigit(*cur_, &digit))
-                        cur_++;
-                }
-                return WasmToken(WasmToken::NaN, begin, cur_);
-            }
-            if (consume(MOZ_UTF16("nop")))
-                return WasmToken(WasmToken::Nop, begin, cur_);
-            break;
-
-          case 'p':
-            if (consume(MOZ_UTF16("param")))
-                return WasmToken(WasmToken::Param, begin, cur_);
-            break;
-
-          case 'r':
-            if (consume(MOZ_UTF16("result")))
-                return WasmToken(WasmToken::Result, begin, cur_);
-            break;
-
-          case 's':
-            if (consume(MOZ_UTF16("set_local")))
-                return WasmToken(WasmToken::SetLocal, begin, cur_);
-            if (consume(MOZ_UTF16("segment")))
-                return WasmToken(WasmToken::Segment, begin, cur_);
-            break;
-
-          default:
-            break;
-        }
-
-        return fail(begin);
-    }
+    WasmToken next();
 
   public:
     WasmTokenStream(const char16_t* text, UniqueChars* error)
       : cur_(text),
         end_(text + js_strlen(text)),
         lineStart_(text),
         line_(0),
         lookaheadIndex_(0),
@@ -1473,20 +964,677 @@ class WasmTokenStream
         return false;
     }
     bool match(WasmToken::Kind expect, UniqueChars* error) {
         WasmToken token;
         return match(expect, &token, error);
     }
 };
 
+WasmToken WasmTokenStream::next()
+{
+    while (cur_ != end_ && IsWasmSpace(*cur_)) {
+        if (IsWasmNewLine(*cur_++)) {
+            lineStart_ = cur_;
+            line_++;
+        }
+    }
+
+    if (cur_ == end_)
+        return WasmToken(WasmToken::EndOfFile, cur_, cur_);
+
+    const char16_t* begin = cur_;
+    switch (*begin) {
+      case '"':
+        cur_++;
+        while (true) {
+            if (cur_ == end_)
+                return fail(begin);
+            if (*cur_ == '"')
+                break;
+            if (!ConsumeTextByte(&cur_, end_))
+                return fail(begin);
+        }
+        cur_++;
+        return WasmToken(WasmToken::Text, begin, cur_);
+
+      case '$':
+        cur_++;
+        while (cur_ != end_ && IsNameAfterDollar(*cur_))
+            cur_++;
+        return WasmToken(WasmToken::Name, begin, cur_);
+
+      case '(':
+        cur_++;
+        return WasmToken(WasmToken::OpenParen, begin, cur_);
+
+      case ')':
+        cur_++;
+        return WasmToken(WasmToken::CloseParen, begin, cur_);
+
+      case '=':
+        cur_++;
+        return WasmToken(WasmToken::Equal, begin, cur_);
+
+      case '+': case '-':
+        cur_++;
+        if (consume(MOZ_UTF16("infinity")))
+            goto infinity;
+        if (consume(MOZ_UTF16("nan")))
+            goto nan;
+        if (!IsWasmDigit(*cur_))
+            break;
+        MOZ_FALLTHROUGH;
+      case '0': case '1': case '2': case '3': case '4':
+      case '5': case '6': case '7': case '8': case '9': {
+        CheckedInt<uint64_t> u = 0;
+        if (consume(MOZ_UTF16("0x"))) {
+            if (cur_ == end_)
+                return fail(begin);
+            do {
+                if (*cur_ == '.' || *cur_ == 'p')
+                    return LexHexFloatLiteral(begin, end_, &cur_);
+                uint8_t digit;
+                if (!IsHexDigit(*cur_, &digit))
+                    break;
+                u *= 16;
+                u += digit;
+                if (!u.isValid())
+                    return fail(begin);
+                cur_++;
+            } while (cur_ != end_);
+        } else {
+            while (cur_ != end_) {
+                if (*cur_ == '.' || *cur_ == 'e')
+                    return LexDecFloatLiteral(begin, end_, &cur_);
+                if (!IsWasmDigit(*cur_))
+                    break;
+                u *= 10;
+                u += *cur_ - '0';
+                if (!u.isValid())
+                    return fail(begin);
+                cur_++;
+            }
+        }
+
+        uint64_t value = u.value();
+        if (*begin == '-') {
+            if (value > uint64_t(INT64_MIN))
+                return fail(begin);
+            value = -value;
+            return WasmToken(int64_t(value), begin, cur_);
+        }
+
+        CheckedInt<uint32_t> index = u.value();
+        if (index.isValid())
+            return WasmToken(index.value(), begin, cur_);
+
+        return WasmToken(value, begin, cur_);
+      }
+
+      case 'a':
+        if (consume(MOZ_UTF16("align")))
+            return WasmToken(WasmToken::Align, begin, cur_);
+        break;
+
+      case 'b':
+        if (consume(MOZ_UTF16("block")))
+            return WasmToken(WasmToken::Block, begin, cur_);
+        break;
+
+      case 'c':
+        if (consume(MOZ_UTF16("call"))) {
+            if (consume(MOZ_UTF16("_import")))
+                return WasmToken(WasmToken::CallImport, begin, cur_);
+            return WasmToken(WasmToken::Call, begin, cur_);
+        }
+        break;
+
+      case 'e':
+        if (consume(MOZ_UTF16("export")))
+            return WasmToken(WasmToken::Export, begin, cur_);
+        break;
+
+      case 'f':
+        if (consume(MOZ_UTF16("func")))
+            return WasmToken(WasmToken::Func, begin, cur_);
+
+        if (consume(MOZ_UTF16("f32"))) {
+            if (!consume(MOZ_UTF16(".")))
+                return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_);
+
+            switch (*cur_) {
+              case 'a':
+                if (consume(MOZ_UTF16("abs")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F32Abs, begin, cur_);
+                if (consume(MOZ_UTF16("add")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F32Add, begin, cur_);
+                break;
+              case 'c':
+                if (consume(MOZ_UTF16("ceil")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F32Ceil, begin, cur_);
+                if (consume(MOZ_UTF16("const")))
+                    return WasmToken(WasmToken::Const, ValType::F32, begin, cur_);
+                if (consume(MOZ_UTF16("convert_s/i32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertSI32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("convert_u/i32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::F32ConvertUI32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("copysign")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F32CopySign, begin, cur_);
+                break;
+              case 'd':
+                if (consume(MOZ_UTF16("demote/f64")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::F32DemoteF64,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("div")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F32Div, begin, cur_);
+                break;
+              case 'e':
+                if (consume(MOZ_UTF16("eq")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Eq, begin, cur_);
+                break;
+              case 'f':
+                if (consume(MOZ_UTF16("floor")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F32Floor, begin, cur_);
+                break;
+              case 'g':
+                if (consume(MOZ_UTF16("ge")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ge, begin, cur_);
+                if (consume(MOZ_UTF16("gt")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Gt, begin, cur_);
+                break;
+              case 'l':
+                if (consume(MOZ_UTF16("le")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Le, begin, cur_);
+                if (consume(MOZ_UTF16("lt")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Lt, begin, cur_);
+                if (consume(MOZ_UTF16("load")))
+                    return WasmToken(WasmToken::Load, Expr::F32LoadMem, begin, cur_);
+                break;
+              case 'm':
+                if (consume(MOZ_UTF16("max")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F32Max, begin, cur_);
+                if (consume(MOZ_UTF16("min")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F32Min, begin, cur_);
+                if (consume(MOZ_UTF16("mul")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F32Mul, begin, cur_);
+                break;
+              case 'n':
+                if (consume(MOZ_UTF16("nearest")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F32Nearest, begin, cur_);
+                if (consume(MOZ_UTF16("neg")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F32Neg, begin, cur_);
+                if (consume(MOZ_UTF16("ne")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F32Ne, begin, cur_);
+                break;
+              case 'r':
+                if (consume(MOZ_UTF16("reinterpret/i32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::F32ReinterpretI32,
+                                     begin, cur_);
+                break;
+              case 's':
+                if (consume(MOZ_UTF16("sqrt")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F32Sqrt, begin, cur_);
+                if (consume(MOZ_UTF16("sub")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F32Sub, begin, cur_);
+                if (consume(MOZ_UTF16("store")))
+                    return WasmToken(WasmToken::Store, Expr::F32StoreMem, begin, cur_);
+                break;
+              case 't':
+                if (consume(MOZ_UTF16("trunc")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F32Trunc, begin, cur_);
+                break;
+            }
+            break;
+        }
+        if (consume(MOZ_UTF16("f64"))) {
+            if (!consume(MOZ_UTF16(".")))
+                return WasmToken(WasmToken::ValueType, ValType::F64, begin, cur_);
+
+            switch (*cur_) {
+              case 'a':
+                if (consume(MOZ_UTF16("abs")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F64Abs, begin, cur_);
+                if (consume(MOZ_UTF16("add")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F64Add, begin, cur_);
+                break;
+              case 'c':
+                if (consume(MOZ_UTF16("ceil")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F64Ceil, begin, cur_);
+                if (consume(MOZ_UTF16("const")))
+                    return WasmToken(WasmToken::Const, ValType::F64, begin, cur_);
+                if (consume(MOZ_UTF16("convert_s/i32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertSI32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("convert_u/i32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::F64ConvertUI32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("copysign")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F64CopySign, begin, cur_);
+                break;
+              case 'd':
+                if (consume(MOZ_UTF16("div")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F64Div, begin, cur_);
+                break;
+              case 'e':
+                if (consume(MOZ_UTF16("eq")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Eq, begin, cur_);
+                break;
+              case 'f':
+                if (consume(MOZ_UTF16("floor")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F64Floor, begin, cur_);
+                break;
+              case 'g':
+                if (consume(MOZ_UTF16("ge")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ge, begin, cur_);
+                if (consume(MOZ_UTF16("gt")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Gt, begin, cur_);
+                break;
+              case 'l':
+                if (consume(MOZ_UTF16("le")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Le, begin, cur_);
+                if (consume(MOZ_UTF16("lt")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Lt, begin, cur_);
+                if (consume(MOZ_UTF16("load")))
+                    return WasmToken(WasmToken::Load, Expr::F64LoadMem, begin, cur_);
+                break;
+              case 'm':
+                if (consume(MOZ_UTF16("max")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F64Max, begin, cur_);
+                if (consume(MOZ_UTF16("min")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F64Min, begin, cur_);
+                if (consume(MOZ_UTF16("mul")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F64Mul, begin, cur_);
+                break;
+              case 'n':
+                if (consume(MOZ_UTF16("nearest")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F64Nearest, begin, cur_);
+                if (consume(MOZ_UTF16("neg")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F64Neg, begin, cur_);
+                if (consume(MOZ_UTF16("ne")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::F64Ne, begin, cur_);
+                break;
+              case 'p':
+                if (consume(MOZ_UTF16("promote/f32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::F64PromoteF32,
+                                     begin, cur_);
+                break;
+              case 's':
+                if (consume(MOZ_UTF16("sqrt")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F64Sqrt, begin, cur_);
+                if (consume(MOZ_UTF16("sub")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::F64Sub, begin, cur_);
+                if (consume(MOZ_UTF16("store")))
+                    return WasmToken(WasmToken::Store, Expr::F64StoreMem, begin, cur_);
+                break;
+              case 't':
+                if (consume(MOZ_UTF16("trunc")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::F64Trunc, begin, cur_);
+                break;
+            }
+            break;
+        }
+        break;
+
+      case 'g':
+        if (consume(MOZ_UTF16("get_local")))
+            return WasmToken(WasmToken::GetLocal, begin, cur_);
+        break;
+
+      case 'i':
+        if (consume(MOZ_UTF16("i32"))) {
+            if (!consume(MOZ_UTF16(".")))
+                return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_);
+
+            switch (*cur_) {
+              case 'a':
+                if (consume(MOZ_UTF16("add")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32Add, begin, cur_);
+                if (consume(MOZ_UTF16("and")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32And, begin, cur_);
+                break;
+              case 'c':
+                if (consume(MOZ_UTF16("const")))
+                    return WasmToken(WasmToken::Const, ValType::I32, begin, cur_);
+                if (consume(MOZ_UTF16("clz")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::I32Clz, begin, cur_);
+                if (consume(MOZ_UTF16("ctz")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::I32Ctz, begin, cur_);
+                break;
+              case 'd':
+                if (consume(MOZ_UTF16("div_s")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivS, begin, cur_);
+                if (consume(MOZ_UTF16("div_u")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32DivU, begin, cur_);
+                break;
+              case 'e':
+                if (consume(MOZ_UTF16("eq")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Eq, begin, cur_);
+                break;
+              case 'g':
+                if (consume(MOZ_UTF16("ge_s")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeS, begin, cur_);
+                if (consume(MOZ_UTF16("ge_u")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GeU, begin, cur_);
+                if (consume(MOZ_UTF16("gt_s")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtS, begin, cur_);
+                if (consume(MOZ_UTF16("gt_u")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32GtU, begin, cur_);
+                break;
+              case 'l':
+                if (consume(MOZ_UTF16("le_s")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeS, begin, cur_);
+                if (consume(MOZ_UTF16("le_u")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LeU, begin, cur_);
+                if (consume(MOZ_UTF16("lt_s")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtS, begin, cur_);
+                if (consume(MOZ_UTF16("lt_u")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32LtU, begin, cur_);
+                if (consume(MOZ_UTF16("load"))) {
+                    if (IsWasmSpace(*cur_))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem, begin, cur_);
+                    if (consume(MOZ_UTF16("8_s")))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem8S, begin, cur_);
+                    if (consume(MOZ_UTF16("8_u")))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem8U, begin, cur_);
+                    if (consume(MOZ_UTF16("16_s")))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem16S, begin, cur_);
+                    if (consume(MOZ_UTF16("16_u")))
+                        return WasmToken(WasmToken::Load, Expr::I32LoadMem16U, begin, cur_);
+                    break;
+                }
+                break;
+              case 'm':
+                if (consume(MOZ_UTF16("mul")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32Mul, begin, cur_);
+                break;
+              case 'n':
+                if (consume(MOZ_UTF16("ne")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I32Ne, begin, cur_);
+                break;
+              case 'o':
+                if (consume(MOZ_UTF16("or")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32Or, begin, cur_);
+                break;
+              case 'p':
+                if (consume(MOZ_UTF16("popcnt")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::I32Popcnt, begin, cur_);
+                break;
+              case 'r':
+                if (consume(MOZ_UTF16("reinterpret/f32")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::I32ReinterpretF32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("rem_s")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemS, begin, cur_);
+                if (consume(MOZ_UTF16("rem_u")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32RemU, begin, cur_);
+                break;
+              case 's':
+                if (consume(MOZ_UTF16("sub")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32Sub, begin, cur_);
+                if (consume(MOZ_UTF16("shl")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32Shl, begin, cur_);
+                if (consume(MOZ_UTF16("shr_s")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrS, begin, cur_);
+                if (consume(MOZ_UTF16("shr_u")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32ShrU, begin, cur_);
+                if (consume(MOZ_UTF16("store"))) {
+                    if (IsWasmSpace(*cur_))
+                        return WasmToken(WasmToken::Store, Expr::I32StoreMem, begin, cur_);
+                    if (consume(MOZ_UTF16("8")))
+                        return WasmToken(WasmToken::Store, Expr::I32StoreMem8, begin, cur_);
+                    if (consume(MOZ_UTF16("16")))
+                        return WasmToken(WasmToken::Store, Expr::I32StoreMem16, begin, cur_);
+                    break;
+                }
+                break;
+              case 't':
+                if (consume(MOZ_UTF16("trunc_s/f32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("trunc_s/f64")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncSF64,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("trunc_u/f32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("trunc_u/f64")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I32TruncUF64,
+                                     begin, cur_);
+                break;
+              case 'w':
+                if (consume(MOZ_UTF16("wrap/i64")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I32WrapI64,
+                                     begin, cur_);
+                break;
+              case 'x':
+                if (consume(MOZ_UTF16("xor")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I32Xor, begin, cur_);
+                break;
+            }
+            break;
+        }
+        if (consume(MOZ_UTF16("i64"))) {
+            if (!consume(MOZ_UTF16(".")))
+                return WasmToken(WasmToken::ValueType, ValType::I64, begin, cur_);
+
+            switch (*cur_) {
+              case 'a':
+                if (consume(MOZ_UTF16("add")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64Add, begin, cur_);
+                if (consume(MOZ_UTF16("and")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64And, begin, cur_);
+                break;
+              case 'c':
+                if (consume(MOZ_UTF16("const")))
+                    return WasmToken(WasmToken::Const, ValType::I64, begin, cur_);
+                if (consume(MOZ_UTF16("clz")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::I64Clz, begin, cur_);
+                if (consume(MOZ_UTF16("ctz")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::I64Ctz, begin, cur_);
+                break;
+              case 'd':
+                if (consume(MOZ_UTF16("div_s")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivS, begin, cur_);
+                if (consume(MOZ_UTF16("div_u")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64DivU, begin, cur_);
+                break;
+              case 'e':
+                if (consume(MOZ_UTF16("eq")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Eq, begin, cur_);
+                if (consume(MOZ_UTF16("extend_s/i32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendSI32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("extend_u/i32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I64ExtendUI32,
+                                     begin, cur_);
+                break;
+              case 'g':
+                if (consume(MOZ_UTF16("ge_s")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeS, begin, cur_);
+                if (consume(MOZ_UTF16("ge_u")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GeU, begin, cur_);
+                if (consume(MOZ_UTF16("gt_s")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtS, begin, cur_);
+                if (consume(MOZ_UTF16("gt_u")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64GtU, begin, cur_);
+                break;
+              case 'l':
+                if (consume(MOZ_UTF16("le_s")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeS, begin, cur_);
+                if (consume(MOZ_UTF16("le_u")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LeU, begin, cur_);
+                if (consume(MOZ_UTF16("lt_s")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtS, begin, cur_);
+                if (consume(MOZ_UTF16("lt_u")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64LtU, begin, cur_);
+                if (consume(MOZ_UTF16("load"))) {
+                    if (IsWasmSpace(*cur_))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem, begin, cur_);
+                    if (consume(MOZ_UTF16("8_s")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem8S, begin, cur_);
+                    if (consume(MOZ_UTF16("8_u")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem8U, begin, cur_);
+                    if (consume(MOZ_UTF16("16_s")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem16S, begin, cur_);
+                    if (consume(MOZ_UTF16("16_u")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem16U, begin, cur_);
+                    if (consume(MOZ_UTF16("32_s")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem32S, begin, cur_);
+                    if (consume(MOZ_UTF16("32_u")))
+                        return WasmToken(WasmToken::Load, Expr::I64LoadMem32U, begin, cur_);
+                    break;
+                }
+                break;
+              case 'm':
+                if (consume(MOZ_UTF16("mul")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64Mul, begin, cur_);
+                break;
+              case 'n':
+                if (consume(MOZ_UTF16("ne")))
+                    return WasmToken(WasmToken::ComparisonOpcode, Expr::I64Ne, begin, cur_);
+                break;
+              case 'o':
+                if (consume(MOZ_UTF16("or")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64Or, begin, cur_);
+                break;
+              case 'p':
+                if (consume(MOZ_UTF16("popcnt")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::I64Popcnt, begin, cur_);
+                break;
+              case 'r':
+                if (consume(MOZ_UTF16("reinterpret/f64")))
+                    return WasmToken(WasmToken::UnaryOpcode, Expr::I64ReinterpretF64,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("rem_s")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemS, begin, cur_);
+                if (consume(MOZ_UTF16("rem_u")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64RemU, begin, cur_);
+                break;
+              case 's':
+                if (consume(MOZ_UTF16("sub")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64Sub, begin, cur_);
+                if (consume(MOZ_UTF16("shl")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64Shl, begin, cur_);
+                if (consume(MOZ_UTF16("shr_s")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrS, begin, cur_);
+                if (consume(MOZ_UTF16("shr_u")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64ShrU, begin, cur_);
+                if (consume(MOZ_UTF16("store"))) {
+                    if (IsWasmSpace(*cur_))
+                        return WasmToken(WasmToken::Store, Expr::I64StoreMem, begin, cur_);
+                    if (consume(MOZ_UTF16("8")))
+                        return WasmToken(WasmToken::Store, Expr::I64StoreMem8, begin, cur_);
+                    if (consume(MOZ_UTF16("16")))
+                        return WasmToken(WasmToken::Store, Expr::I64StoreMem16, begin, cur_);
+                    if (consume(MOZ_UTF16("32")))
+                        return WasmToken(WasmToken::Store, Expr::I64StoreMem32, begin, cur_);
+                    break;
+                }
+                break;
+              case 't':
+                if (consume(MOZ_UTF16("trunc_s/f32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("trunc_s/f64")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncSF64,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("trunc_u/f32")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF32,
+                                     begin, cur_);
+                if (consume(MOZ_UTF16("trunc_u/f64")))
+                    return WasmToken(WasmToken::ConversionOpcode, Expr::I64TruncUF64,
+                                     begin, cur_);
+                break;
+              case 'x':
+                if (consume(MOZ_UTF16("xor")))
+                    return WasmToken(WasmToken::BinaryOpcode, Expr::I64Xor, begin, cur_);
+                break;
+            }
+            break;
+        }
+        if (consume(MOZ_UTF16("import")))
+            return WasmToken(WasmToken::Import, begin, cur_);
+        if (consume(MOZ_UTF16("infinity"))) {
+        infinity:
+            return WasmToken(WasmToken::Infinity, begin, cur_);
+        }
+        if (consume(MOZ_UTF16("if"))) {
+            if (consume(MOZ_UTF16("_else")))
+                return WasmToken(WasmToken::IfElse, begin, cur_);
+            return WasmToken(WasmToken::If, begin, cur_);
+        }
+        break;
+
+      case 'l':
+        if (consume(MOZ_UTF16("local")))
+            return WasmToken(WasmToken::Local, begin, cur_);
+        break;
+
+      case 'm':
+        if (consume(MOZ_UTF16("module")))
+            return WasmToken(WasmToken::Module, begin, cur_);
+        if (consume(MOZ_UTF16("memory")))
+            return WasmToken(WasmToken::Memory, begin, cur_);
+        break;
+
+      case 'n':
+        if (consume(MOZ_UTF16("nan"))) {
+        nan:
+            if (consume(MOZ_UTF16(":"))) {
+                if (!consume(MOZ_UTF16("0x")))
+                    break;
+                uint8_t digit;
+                while (cur_ != end_ && IsHexDigit(*cur_, &digit))
+                    cur_++;
+            }
+            return WasmToken(WasmToken::NaN, begin, cur_);
+        }
+        if (consume(MOZ_UTF16("nop")))
+            return WasmToken(WasmToken::Nop, begin, cur_);
+        break;
+
+      case 'o':
+        if (consume(MOZ_UTF16("offset")))
+            return WasmToken(WasmToken::Offset, begin, cur_);
+        break;
+
+      case 'p':
+        if (consume(MOZ_UTF16("param")))
+            return WasmToken(WasmToken::Param, begin, cur_);
+        break;
+
+      case 'r':
+        if (consume(MOZ_UTF16("result")))
+            return WasmToken(WasmToken::Result, begin, cur_);
+        break;
+
+      case 's':
+        if (consume(MOZ_UTF16("set_local")))
+            return WasmToken(WasmToken::SetLocal, begin, cur_);
+        if (consume(MOZ_UTF16("segment")))
+            return WasmToken(WasmToken::Segment, begin, cur_);
+        break;
+
+      default:
+        break;
+    }
+
+    return fail(begin);
+}
+
+} // end anonymous namespace
 
 /*****************************************************************************/
 // wasm text format parser
 
+namespace {
+
 struct WasmParseContext
 {
     WasmTokenStream ts;
     LifoAlloc& lifo;
     UniqueChars* error;
     DtoaState* dtoaState;
 
     WasmParseContext(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
@@ -1754,37 +1902,42 @@ ParseFloatLiteral(WasmParseContext& c, W
     if (*cur == '-' || *cur == '+')
         isNegated = *cur++ == '-';
 
     switch (token.floatLiteralKind()) {
       case WasmToken::Infinity:
         *result = PositiveInfinity<Float>();
         break;
       case WasmToken::NaN:
-        if (!ParseNaNLiteral(cur, end, result))
+        if (!ParseNaNLiteral(cur, end, result)) {
+            c.ts.generateError(token, c.error);
             return false;
+        }
         break;
       case WasmToken::HexNumber:
-        if (!ParseHexFloatLiteral(cur, end, result))
+        if (!ParseHexFloatLiteral(cur, end, result)) {
+            c.ts.generateError(token, c.error);
             return false;
+        }
         break;
       case WasmToken::DecNumber: {
         // Call into JS' strtod. Tokenization has already required that the
         // string is well-behaved.
         LifoAlloc::Mark mark = c.lifo.mark();
         char* buffer = c.lifo.newArray<char>(end - begin + 1);
         if (!buffer)
             return false;
         for (ptrdiff_t i = 0; i < end - cur; ++i)
             buffer[i] = char(cur[i]);
         char* strtod_end;
         int err;
         Float d = (Float)js_strtod_harder(c.dtoaState, buffer, &strtod_end, &err);
         if (err != 0 || strtod_end == buffer) {
             c.lifo.release(mark);
+            c.ts.generateError(token, c.error);
             return false;
         }
         c.lifo.release(mark);
         *result = d;
         break;
       }
     }
 
@@ -1798,64 +1951,61 @@ static WasmAstConst*
 ParseConst(WasmParseContext& c, WasmToken constToken)
 {
     WasmToken val = c.ts.get();
     switch (constToken.valueType()) {
       case ValType::I32: {
         switch (val.kind()) {
           case WasmToken::Index:
             return new(c.lifo) WasmAstConst(Val(val.index()));
-          case WasmToken::UnsignedInteger: {
-            CheckedInt<uint32_t> uint = val.uint();
-            if (uint.isValid())
-                return new(c.lifo) WasmAstConst(Val(uint.value()));
-            return nullptr;
-          }
           case WasmToken::SignedInteger: {
             CheckedInt<int32_t> sint = val.sint();
-            if (sint.isValid())
-                return new(c.lifo) WasmAstConst(Val(uint32_t(sint.value())));
-            return nullptr;
+            if (!sint.isValid())
+                break;
+            return new(c.lifo) WasmAstConst(Val(uint32_t(sint.value())));
           }
           default:
-            return nullptr;
+            break;
         }
+        break;
       }
       case ValType::I64: {
         switch (val.kind()) {
           case WasmToken::Index:
             return new(c.lifo) WasmAstConst(Val(val.index()));
           case WasmToken::UnsignedInteger:
             return new(c.lifo) WasmAstConst(Val(val.uint()));
           case WasmToken::SignedInteger:
             return new(c.lifo) WasmAstConst(Val(uint64_t(val.sint())));
           default:
-            return nullptr;
+            break;
         }
+        break;
       }
       case ValType::F32: {
         if (val.kind() != WasmToken::Float)
-            return nullptr;
+            break;
         float result;
         if (!ParseFloatLiteral(c, val, &result))
-            return nullptr;
+            break;
         return new(c.lifo) WasmAstConst(Val(result));
       }
       case ValType::F64: {
         if (val.kind() != WasmToken::Float)
-            return nullptr;
+            break;
         double result;
         if (!ParseFloatLiteral(c, val, &result))
-            return nullptr;
+            break;
         return new(c.lifo) WasmAstConst(Val(result));
       }
       default:
-        c.ts.generateError(constToken, c.error);
-        return nullptr;
+        break;
     }
+    c.ts.generateError(constToken, c.error);
+    return nullptr;
 }
 
 static WasmAstGetLocal*
 ParseGetLocal(WasmParseContext& c)
 {
     WasmToken localIndex;
     if (!c.ts.match(WasmToken::Index, &localIndex, c.error))
         return nullptr;
@@ -1941,16 +2091,145 @@ ParseIfElse(WasmParseContext& c, Expr ex
         elseBody = ParseExpr(c);
         if (!elseBody)
             return nullptr;
     }
 
     return new(c.lifo) WasmAstIfElse(expr, cond, ifBody, elseBody);
 }
 
+static bool
+ParseLoadStoreAddress(WasmParseContext& c, WasmAstExpr** base,
+                      int32_t* offset, int32_t* align)
+{
+    *base = ParseExpr(c);
+    if (!*base)
+        return false;
+
+    WasmToken token = c.ts.get();
+
+    *offset = 0;
+    if (token.kind() == WasmToken::Offset) {
+        if (!c.ts.match(WasmToken::Equal, c.error))
+            return false;
+        WasmToken val = c.ts.get();
+        switch (val.kind()) {
+          case WasmToken::Index:
+            *offset = val.index();
+            break;
+          default:
+            c.ts.generateError(val, c.error);
+            return false;
+        }
+
+        token = c.ts.get();
+    }
+
+    *align = 0;
+    if (token.kind() == WasmToken::Align) {
+        if (!c.ts.match(WasmToken::Equal, c.error))
+            return false;
+        WasmToken val = c.ts.get();
+        switch (val.kind()) {
+          case WasmToken::Index:
+            *align = val.index();
+            break;
+          default:
+            c.ts.generateError(val, c.error);
+            return false;
+        }
+
+        token = c.ts.get();
+    }
+
+    c.ts.unget(token);
+    return true;
+}
+
+static WasmAstLoad*
+ParseLoad(WasmParseContext& c, Expr expr)
+{
+    WasmAstExpr* base;
+    int32_t offset;
+    int32_t align;
+    if (!ParseLoadStoreAddress(c, &base, &offset, &align))
+        return nullptr;
+
+    if (align == 0) {
+        switch (expr) {
+          case Expr::I32LoadMem8S:
+          case Expr::I32LoadMem8U:
+          case Expr::I64LoadMem8S:
+          case Expr::I64LoadMem8U:
+            align = 1;
+            break;
+          case Expr::I32LoadMem16S:
+          case Expr::I32LoadMem16U:
+          case Expr::I64LoadMem16S:
+          case Expr::I64LoadMem16U:
+            align = 2;
+            break;
+          case Expr::I32LoadMem:
+          case Expr::F32LoadMem:
+          case Expr::I64LoadMem32S:
+          case Expr::I64LoadMem32U:
+            align = 4;
+            break;
+          case Expr::I64LoadMem:
+          case Expr::F64LoadMem:
+            align = 8;
+            break;
+          default:
+            MOZ_CRASH("Bad load expr");
+        }
+    }
+
+    return new(c.lifo) WasmAstLoad(expr, WasmAstLoadStoreAddress(base, offset, align));
+}
+
+static WasmAstStore*
+ParseStore(WasmParseContext& c, Expr expr)
+{
+    WasmAstExpr* base;
+    int32_t offset;
+    int32_t align;
+    if (!ParseLoadStoreAddress(c, &base, &offset, &align))
+        return nullptr;
+
+    if (align == 0) {
+        switch (expr) {
+          case Expr::I32StoreMem8:
+          case Expr::I64StoreMem8:
+            align = 1;
+            break;
+          case Expr::I32StoreMem16:
+          case Expr::I64StoreMem16:
+            align = 2;
+            break;
+          case Expr::I32StoreMem:
+          case Expr::F32StoreMem:
+          case Expr::I64StoreMem32:
+            align = 4;
+            break;
+          case Expr::I64StoreMem:
+          case Expr::F64StoreMem:
+            align = 8;
+            break;
+          default:
+            MOZ_CRASH("Bad load expr");
+        }
+    }
+
+    WasmAstExpr* value = ParseExpr(c);
+    if (!value)
+        return nullptr;
+
+    return new(c.lifo) WasmAstStore(expr, WasmAstLoadStoreAddress(base, offset, align), value);
+}
+
 static WasmAstExpr*
 ParseExprInsideParens(WasmParseContext& c)
 {
     WasmToken token = c.ts.get();
 
     switch (token.kind()) {
       case WasmToken::Nop:
         return new(c.lifo) WasmAstNop;
@@ -1969,18 +2248,22 @@ ParseExprInsideParens(WasmParseContext& 
       case WasmToken::ConversionOpcode:
         return ParseConversionOperator(c, token.expr());
       case WasmToken::If:
         return ParseIfElse(c, Expr::If);
       case WasmToken::IfElse:
         return ParseIfElse(c, Expr::IfElse);
       case WasmToken::GetLocal:
         return ParseGetLocal(c);
+      case WasmToken::Load:
+        return ParseLoad(c, token.expr());
       case WasmToken::SetLocal:
         return ParseSetLocal(c);
+      case WasmToken::Store:
+        return ParseStore(c, token.expr());
       case WasmToken::UnaryOpcode:
         return ParseUnaryOperator(c, token.expr());
       default:
         c.ts.generateError(token, c.error);
         return nullptr;
     }
 }
 
@@ -2205,16 +2488,18 @@ ParseModule(const char16_t* text, LifoAl
     if (!c.ts.match(WasmToken::CloseParen, c.error))
         return nullptr;
     if (!c.ts.match(WasmToken::EndOfFile, c.error))
         return nullptr;
 
     return module;
 }
 
+} // end anonymous namespace
+
 /*****************************************************************************/
 // wasm function body serialization
 
 static bool
 EncodeExpr(Encoder& e, WasmAstExpr& expr);
 
 static bool
 EncodeBlock(Encoder& e, WasmAstBlock& b)
@@ -2323,16 +2608,39 @@ EncodeIfElse(Encoder& e, WasmAstIfElse& 
 {
     return e.writeExpr(ie.expr()) &&
            EncodeExpr(e, ie.cond()) &&
            EncodeExpr(e, ie.ifBody()) &&
            (!ie.hasElse() || EncodeExpr(e, ie.elseBody()));
 }
 
 static bool
+EncodeLoadStoreAddress(Encoder &e, const WasmAstLoadStoreAddress &address)
+{
+    return EncodeExpr(e, address.base()) &&
+           e.writeVarU32(address.offset()) &&
+           e.writeVarU32(address.align());
+}
+
+static bool
+EncodeLoad(Encoder& e, WasmAstLoad& l)
+{
+    return e.writeExpr(l.expr()) &&
+           EncodeLoadStoreAddress(e, l.address());
+}
+
+static bool
+EncodeStore(Encoder& e, WasmAstStore& s)
+{
+    return e.writeExpr(s.expr()) &&
+           EncodeLoadStoreAddress(e, s.address()) &&
+           EncodeExpr(e, s.value());
+}
+
+static bool
 EncodeExpr(Encoder& e, WasmAstExpr& expr)
 {
     switch (expr.kind()) {
       case WasmAstExprKind::Nop:
         return e.writeExpr(Expr::Nop);
       case WasmAstExprKind::BinaryOperator:
         return EncodeBinaryOperator(e, expr.as<WasmAstBinaryOperator>());
       case WasmAstExprKind::Block:
@@ -2344,18 +2652,22 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr
       case WasmAstExprKind::Const:
         return EncodeConst(e, expr.as<WasmAstConst>());
       case WasmAstExprKind::ConversionOperator:
         return EncodeConversionOperator(e, expr.as<WasmAstConversionOperator>());
       case WasmAstExprKind::GetLocal:
         return EncodeGetLocal(e, expr.as<WasmAstGetLocal>());
       case WasmAstExprKind::IfElse:
         return EncodeIfElse(e, expr.as<WasmAstIfElse>());
+      case WasmAstExprKind::Load:
+        return EncodeLoad(e, expr.as<WasmAstLoad>());
       case WasmAstExprKind::SetLocal:
         return EncodeSetLocal(e, expr.as<WasmAstSetLocal>());
+      case WasmAstExprKind::Store:
+        return EncodeStore(e, expr.as<WasmAstStore>());
       case WasmAstExprKind::UnaryOperator:
         return EncodeUnaryOperator(e, expr.as<WasmAstUnaryOperator>());
       default:;
     }
     MOZ_CRASH("Bad expr kind");
 }
 
 /*****************************************************************************/
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -240,16 +240,23 @@ class Sig
     bool operator==(const Sig& rhs) const {
         return ret() == rhs.ret() && EqualContainers(args(), rhs.args());
     }
     bool operator!=(const Sig& rhs) const {
         return !(*this == rhs);
     }
 };
 
+struct SigHashPolicy
+{
+    typedef const Sig& Lookup;
+    static HashNumber hash(Lookup sig) { return sig.hash(); }
+    static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; }
+};
+
 // A "declared" signature is a Sig object that is created and owned by the
 // ModuleGenerator. These signature objects are read-only and have the same
 // lifetime as the ModuleGenerator. This type is useful since some uses of Sig
 // need this extended lifetime and want to statically distinguish from the
 // common stack-allocated Sig objects that get passed around.
 
 struct DeclaredSig : Sig
 {
--- a/js/src/builtin/Reflect.cpp
+++ b/js/src/builtin/Reflect.cpp
@@ -172,43 +172,16 @@ Reflect_deleteProperty(JSContext* cx, un
     // Step 4.
     ObjectOpResult result;
     if (!DeleteProperty(cx, target, key, result))
         return false;
     args.rval().setBoolean(bool(result));
     return true;
 }
 
-#if 0
-/*
- * ES6 26.1.5 Reflect.enumerate(target)
- *
- * TODO:
- * - redefine enumeration in terms of iterators without losing performance
- * - support iterators in Proxies
- */
-static bool
-Reflect_enumerate(JSContext* cx, unsigned argc, Value* vp)
-{
-    CallArgs args = CallArgsFromVp(argc, vp);
-
-    // Step 1.
-    RootedObject obj(cx, NonNullObject(cx, args.get(0)));
-    if (!obj)
-        return false;
-
-    // Step 2.
-    RootedObject iterator(cx);
-    if (!Enumerate(cx, obj, &iterator))
-        return false;
-    args.rval().setObject(*iterator);
-    return true;
-}
-#endif
-
 /* ES6 26.1.6 Reflect.get(target, propertyKey [, receiver]) */
 static bool
 Reflect_get(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     // Step 1.
     RootedObject obj(cx, NonNullObject(cx, args.get(0)));
@@ -375,17 +348,16 @@ Reflect_setPrototypeOf(JSContext* cx, un
     return true;
 }
 
 static const JSFunctionSpec methods[] = {
     JS_FN("apply", Reflect_apply, 3, 0),
     JS_FN("construct", Reflect_construct, 2, 0),
     JS_FN("defineProperty", Reflect_defineProperty, 3, 0),
     JS_FN("deleteProperty", Reflect_deleteProperty, 2, 0),
-    // JS_FN("enumerate", Reflect_enumerate, 1, 0),
     JS_FN("get", Reflect_get, 2, 0),
     JS_FN("getOwnPropertyDescriptor", Reflect_getOwnPropertyDescriptor, 2, 0),
     JS_FN("getPrototypeOf", Reflect_getPrototypeOf, 1, 0),
     JS_SELF_HOSTED_FN("has", "Reflect_has", 2, 0),
     JS_FN("isExtensible", Reflect_isExtensible, 1, 0),
     JS_FN("ownKeys", Reflect_ownKeys, 1, 0),
     JS_FN("preventExtensions", Reflect_preventExtensions, 1, 0),
     JS_FN("set", Reflect_set, 3, 0),
--- a/js/src/devtools/automation/cgc-jittest-timeouts.txt
+++ b/js/src/devtools/automation/cgc-jittest-timeouts.txt
@@ -8,16 +8,17 @@ basic/bug632964-regexp.js
 basic/bug656261.js
 basic/bug677957-2.js
 basic/bug753283.js
 basic/testBug614653.js
 basic/testBug686274.js
 basic/testManyVars.js
 basic/testTypedArrayInit.js
 gc/bug-1014972.js
+gc/bug-1246593.js
 gc/bug-906236.js
 gc/bug-906241.js
 ion/bug787921.js
 parallel/alloc-many-objs.js
 parallel/alloc-too-many-objs.js
 self-test/assertDeepEq.js
 v8-v5/check-earley-boyer.js
 v8-v5/check-raytrace.js
--- a/js/src/ds/Fifo.h
+++ b/js/src/ds/Fifo.h
@@ -31,17 +31,17 @@ template <typename T,
 class Fifo
 {
     static_assert(MinInlineCapacity % 2 == 0, "MinInlineCapacity must be even!");
 
   protected:
     // An element A is "younger" than an element B if B was inserted into the
     // |Fifo| before A was.
     //
-    // Invariant 1: Every element within |front_| is younger than every element
+    // Invariant 1: Every element within |front_| is older than every element
     // within |rear_|.
     // Invariant 2: Entries within |front_| are sorted from younger to older.
     // Invariant 3: Entries within |rear_| are sorted from older to younger.
     // Invariant 4: If the |Fifo| is not empty, then |front_| is not empty.
     Vector<T, MinInlineCapacity / 2, AllocPolicy> front_;
     Vector<T, MinInlineCapacity / 2, AllocPolicy> rear_;
 
   private:
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1242812.js
@@ -0,0 +1,5 @@
+if (!('oomTest' in this))
+   quit();
+
+var lfcode = new Array();
+oomTest(() => { let a = [2147483651]; [-1, 0, 1, 31, 32].sort(); });
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/gc/bug-1246593.js
@@ -0,0 +1,4 @@
+length = 10000;
+array = Array();
+for (i = length;i > -100000; i--)
+  array[i] = {};
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/wasm/basic-memory.js
@@ -0,0 +1,111 @@
+load(libdir + "wasm.js");
+
+quit(); // TODO: Loads and stores are NYI.
+
+if (!wasmIsSupported())
+    quit();
+
+function mismatchError(actual, expect) {
+    var str = "type mismatch: expression has type " + actual + " but expected " + expect;
+    return RegExp(str);
+}
+
+function testLoad(type, ext, base, offset, align, expect) {
+    print('(module' +
+    '  (memory 0x10000' +
+    '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
+    '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
+    '  )' +
+    '  (func (param i32) (result ' + type + ')' +
+    '    (' + type + '.load' + ext + ' (get_local 0)' +
+    '     offset=' + offset +
+    '     ' + (align != 0 ? 'align=' + align : '') +
+    '    )' +
+    '  ) (export "" 0))');
+  assertEq(wasmEvalText(
+    '(module' +
+    '  (memory 0x10000' +
+    '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
+    '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
+    '  )' +
+    '  (func (param i32) (result ' + type + ')' +
+    '    (' + type + '.load' + ext + ' (get_local 0)' +
+    '     offset=' + offset +
+    '     ' + (align != 0 ? 'align=' + align : '') +
+    '    )' +
+    '  ) (export "" 0))'
+  )(base), expect);
+}
+
+function testStore(type, ext, base, offset, align, value) {
+  assertEq(wasmEvalText(
+    '(module' +
+    '  (memory 0x10000)' +
+    '    (segment 0 "\\00\\01\\02\\03\\04\\05\\06\\07\\08\\09\\0a\\0b\\0c\\0d\\0e\\0f")' +
+    '    (segment 16 "\\f0\\f1\\f2\\f3\\f4\\f5\\f6\\f7\\f8\\f9\\fa\\fb\\fc\\fd\\fe\\ff")' +
+    '  )' +
+    '  (func (param i32) (param ' + type + ') (result ' + type + ')' +
+    '    (' + type + '.store' + ext + ' (get_local 0)' +
+    '     offset=' + offset +
+    '     ' + (align != 0 ? 'align=' + align : '') +
+    '     (get_local 1)' +
+    '    )' +
+    '  ) (export "" 0))'
+  )(base, value), value);
+}
+
+function testConstError(type, str) {
+  // For now at least, we don't distinguish between parse errors and OOMs.
+  assertErrorMessage(() => wasmEvalText('(module (func (result ' + type + ') (' + type + '.const ' + str + ')) (export "" 0))')(), Error, /out of memory/);
+}
+
+testLoad('i32', '', 0, 0, 0, 0x00010203);
+testLoad('i32', '', 1, 0, 0, 0x01020304);
+//testLoad('i32', '', 0, 1, 0, 0x01020304); // TODO: NYI
+//testLoad('i32', '', 1, 1, 4, 0x02030405); // TODO: NYI
+//testLoad('i64', '', 0, 0, 0, 0x0001020304050607); // TODO: NYI
+//testLoad('i64', '', 1, 0, 0, 0x0102030405060708); // TODO: NYI
+//testLoad('i64', '', 0, 1, 0, 0x0102030405060708); // TODO: NYI