merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 12 Feb 2016 15:15:17 +0100
changeset 320433 218d16a9ddcc3699be2a19bcab7d5f6b7f8e7515
parent 320274 d992fbeb2b2696cb3854cb3a1f0f89ae09b483e2 (current diff)
parent 320432 375dae281f7b1868a7866ad7e07a8281c49a8d60 (diff)
child 320434 d719ac4bcbec13e0ba13a41547788e3bf365c679
child 320446 1b8a0036c771be89a2cc470ddb92616b10f96e40
child 320493 bbe59290c8257dafec58e13b8f656309ca882054
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone47.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
browser/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 + ')' +
+