Merge mozilla-central to fx-team
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 09 Feb 2016 13:10:45 +0100
changeset 329842 683c1c3ca8321e064f7bcd2f690557ef6562bffb
parent 329841 7a0ebe3fccacc655c38e7f32948626af4d3d3269 (current diff)
parent 329801 2dfb45d74f42d2a0010696f5fd47c7a7da94cedb (diff)
child 329843 0c51994fbafe65be49c253ed58390f1ef4de691c
child 330091 f4d2a93f3c497edba0ae4321b362d04ecf9bfe19
push id10617
push userdtownsend@mozilla.com
push dateTue, 09 Feb 2016 16:30:19 +0000
milestone47.0a1
Merge mozilla-central to fx-team
build/unix/build-clang/README
build/unix/build-clang/build-clang.py
build/unix/build-clang/clang-static-analysis-linux64-centos6.json
build/unix/build-clang/clang-static-analysis-linux64.json
build/unix/build-clang/clang-static-analysis-macosx64.json
build/unix/build-clang/llvm-debug-frame.patch
build/unix/build-clang/query-selector-visibility.patch
build/unix/build-clang/return-empty-string-non-mangled.patch
mobile/android/modules/MulticastDNS.jsm
toolkit/crashreporter/Makefile.in
toolkit/crashreporter/breakpad-logging/BreakpadLogging.cpp
toolkit/crashreporter/breakpad-logging/BreakpadLogging.h
toolkit/crashreporter/breakpad-logging/moz.build
toolkit/crashreporter/breakpad-patches/00-module-api-extras.patch
toolkit/crashreporter/breakpad-patches/02-cfi-rule-repr.patch
toolkit/crashreporter/breakpad-patches/03-unique-string.patch
toolkit/crashreporter/breakpad-patches/04-uniquestringmap.patch
toolkit/crashreporter/breakpad-patches/08-dwarf2reader-dynamic-cast.patch
toolkit/crashreporter/breakpad-patches/09-bug779291.patch
toolkit/crashreporter/breakpad-patches/10-logging.patch
toolkit/crashreporter/breakpad-patches/11-readsymboldatainternal-proto.patch
toolkit/crashreporter/breakpad-patches/12-bug863475.patch
toolkit/crashreporter/breakpad-patches/13-bug836829.patch
toolkit/crashreporter/breakpad-patches/14-bug883126.patch
toolkit/crashreporter/breakpad-patches/15-bug859745.patch
toolkit/crashreporter/breakpad-patches/16-sht-arm-exidx-define.patch
toolkit/crashreporter/breakpad-patches/17-bug942407-usersig.patch
toolkit/crashreporter/breakpad-patches/18-bug945498-expose-handle-signal.patch
toolkit/crashreporter/breakpad-patches/19-bug942290-breakpad-exidx-merge.patch
toolkit/crashreporter/breakpad-patches/20-bug1068410-no-procfs-search.patch
toolkit/crashreporter/breakpad-patches/21-bug1068410-crash-server-pipe.patch
toolkit/crashreporter/google-breakpad/COPYING
toolkit/crashreporter/google-breakpad/README
toolkit/crashreporter/google-breakpad/SVN-INFO
toolkit/crashreporter/google-breakpad/src/client/linux/crash_generation/moz.build
toolkit/crashreporter/google-breakpad/src/client/linux/handler/moz.build
toolkit/crashreporter/google-breakpad/src/client/linux/minidump_writer/moz.build
toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.h
toolkit/crashreporter/google-breakpad/src/client/mac/tests/SimpleStringDictionaryTest.mm
toolkit/crashreporter/google-breakpad/src/client/windows/build/common.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/external_code.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/internal/release_defaults.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/internal/release_impl.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/internal/release_impl_official.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/build/release.gypi
toolkit/crashreporter/google-breakpad/src/client/windows/crash_generation/minidump_generator.h
toolkit/crashreporter/google-breakpad/src/client/windows/unittests/exception_handler_nesting_test.cc
toolkit/crashreporter/google-breakpad/src/client/windows/unittests/exception_handler_test.h
toolkit/crashreporter/google-breakpad/src/common/android/include/sys/stat.h
toolkit/crashreporter/google-breakpad/src/common/android/include/sys/ucontext.h
toolkit/crashreporter/google-breakpad/src/common/logging.cc
toolkit/crashreporter/google-breakpad/src/common/logging.h
toolkit/crashreporter/google-breakpad/src/common/mac/GTMGarbageCollection.h
toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.h
toolkit/crashreporter/google-breakpad/src/common/mac/SimpleStringDictionary.mm
toolkit/crashreporter/google-breakpad/src/common/pathname_stripper.cc
toolkit/crashreporter/google-breakpad/src/common/pathname_stripper.h
toolkit/crashreporter/google-breakpad/src/common/pathname_stripper_unittest.cc
toolkit/crashreporter/google-breakpad/src/common/unique_string.cc
toolkit/crashreporter/google-breakpad/src/common/unique_string.h
toolkit/crashreporter/google-breakpad/src/processor/binarystream.cc
toolkit/crashreporter/google-breakpad/src/processor/binarystream.h
toolkit/crashreporter/google-breakpad/src/processor/binarystream_unittest.cc
toolkit/crashreporter/google-breakpad/src/processor/minidump.cc
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_block_write.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_clobber_write.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_conditional.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_then_jmp.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_read_av_xchg_write.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_write_av.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/ascii_write_av_arg_to_call.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/exec_av_on_stack.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/linux_test_app.cc
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.dump.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.machine_readable.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/minidump2.stackwalk.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module0.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module1.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module2.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module3_bad.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/module4_bad.out
toolkit/crashreporter/google-breakpad/src/processor/testdata/null_read_av.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/null_write_av.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/read_av_clobber_write.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/read_av_conditional.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/read_av_non_null.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/stack_exhaustion.dmp
toolkit/crashreporter/google-breakpad/src/processor/testdata/symbols/kernel32.pdb/BCE8785C57B44245A669896B6A19B9542/kernel32.sym
toolkit/crashreporter/google-breakpad/src/processor/testdata/symbols/test_app.pdb/5A9832E5287241C1838ED98914E9B7FF1/test_app.sym
toolkit/crashreporter/google-breakpad/src/processor/testdata/test_app.cc
toolkit/crashreporter/google-breakpad/src/processor/testdata/write_av_non_null.dmp
toolkit/crashreporter/google-breakpad/src/third_party/glog/AUTHORS
toolkit/crashreporter/google-breakpad/src/third_party/glog/COPYING
toolkit/crashreporter/google-breakpad/src/third_party/glog/ChangeLog
toolkit/crashreporter/google-breakpad/src/third_party/glog/INSTALL
toolkit/crashreporter/google-breakpad/src/third_party/glog/Makefile.am
toolkit/crashreporter/google-breakpad/src/third_party/glog/NEWS
toolkit/crashreporter/google-breakpad/src/third_party/glog/README
toolkit/crashreporter/google-breakpad/src/third_party/glog/README.windows
toolkit/crashreporter/google-breakpad/src/third_party/glog/aclocal.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/compile
toolkit/crashreporter/google-breakpad/src/third_party/glog/config.guess
toolkit/crashreporter/google-breakpad/src/third_party/glog/config.sub
toolkit/crashreporter/google-breakpad/src/third_party/glog/configure
toolkit/crashreporter/google-breakpad/src/third_party/glog/configure.ac
toolkit/crashreporter/google-breakpad/src/third_party/glog/depcomp
toolkit/crashreporter/google-breakpad/src/third_party/glog/doc/designstyle.css
toolkit/crashreporter/google-breakpad/src/third_party/glog/doc/glog.html
toolkit/crashreporter/google-breakpad/src/third_party/glog/google-glog.sln
toolkit/crashreporter/google-breakpad/src/third_party/glog/install-sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/libglog.pc.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/ltmain.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ac_have_attribute.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ac_have_builtin_expect.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ac_have_sync_val_compare_and_swap.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ac_rwlock.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/acx_pthread.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/google_namespace.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/libtool.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ltoptions.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ltsugar.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/ltversion.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/lt~obsolete.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/namespaces.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/pc_from_ucontext.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/stl_namespace.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/m4/using_operator.m4
toolkit/crashreporter/google-breakpad/src/third_party/glog/missing
toolkit/crashreporter/google-breakpad/src/third_party/glog/mkinstalldirs
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/README
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/changelog
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/compat
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/control
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/copyright
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/docs
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/libgoogle-glog-dev.dirs
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/libgoogle-glog-dev.install
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/libgoogle-glog0.dirs
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/libgoogle-glog0.install
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/deb/rules
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/rpm.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/packages/rpm/rpm.spec
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/base/commandlineflags.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/base/googleinit.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/base/mutex.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/config.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/config_for_unittests.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle_unittest.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/demangle_unittest.txt
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/log_severity.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/logging.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/raw_logging.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/stl_logging.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/glog/vlog_is_on.h.in
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/googletest.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_striplog_test.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_striptest10.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_striptest2.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_striptest_main.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/logging_unittest.err
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/mock-log.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/mock-log_test.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/raw_logging.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/signalhandler.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/signalhandler_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/signalhandler_unittest.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_generic-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_libunwind-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_powerpc-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_x86-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stacktrace_x86_64-inl.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/stl_logging_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/symbolize.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/symbolize.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/symbolize_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/utilities.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/utilities.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/utilities_unittest.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/vlog_is_on.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/config.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/log_severity.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/logging.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/raw_logging.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/stl_logging.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/glog/vlog_is_on.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/port.cc
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/port.h
toolkit/crashreporter/google-breakpad/src/third_party/glog/src/windows/preprocess.sh
toolkit/crashreporter/google-breakpad/src/third_party/glog/vsprojects/libglog/libglog.vcproj
toolkit/crashreporter/google-breakpad/src/third_party/glog/vsprojects/libglog_static/libglog_static.vcproj
toolkit/crashreporter/google-breakpad/src/third_party/glog/vsprojects/logging_unittest/logging_unittest.vcproj
toolkit/crashreporter/google-breakpad/src/third_party/glog/vsprojects/logging_unittest_static/logging_unittest_static.vcproj
toolkit/crashreporter/google-breakpad/src/tools/windows/dump_syms/testdata/dump_syms_regtest.cc
toolkit/crashreporter/google-breakpad/src/tools/windows/dump_syms/testdata/dump_syms_regtest.pdb
toolkit/crashreporter/google-breakpad/src/tools/windows/dump_syms/testdata/dump_syms_regtest.sym
toolkit/crashreporter/google-breakpad/src/tools/windows/symupload/symupload.vcproj
tools/profiler/core/shim_mac_dump_syms.h
tools/profiler/core/shim_mac_dump_syms.mm
tools/profiler/gecko/local_debug_info_symbolizer.cc
tools/profiler/gecko/local_debug_info_symbolizer.h
--- a/browser/components/preferences/cookies.js
+++ b/browser/components/preferences/cookies.js
@@ -500,29 +500,37 @@ var gCookiesWindow = {
                                      date.getDate(),
                                      date.getHours(),
                                      date.getMinutes(),
                                      date.getSeconds());
     }
     return this._bundle.getString("expireAtEndOfSession");
   },
 
+  _getUserContextString: function(aUserContextId) {
+    if (parseInt(aUserContextId) == 0) {
+      return this._bundle.getString("noUserContextLabel");
+    }
+
+    return UserContextUI.getUserContextLabel(aUserContextId);
+  },
+
   _updateCookieData: function (aItem) {
     var seln = this._view.selection;
     var ids = ["name", "value", "host", "path", "isSecure", "expires", "userContext"];
     var properties;
 
     if (aItem && !aItem.container && seln.count > 0) {
       properties = { name: aItem.name, value: aItem.value, host: aItem.host,
                      path: aItem.path, expires: this.formatExpiresString(aItem.expires),
                      isDomain: aItem.isDomain ? this._bundle.getString("domainColon")
                                               : this._bundle.getString("hostColon"),
                      isSecure: aItem.isSecure ? this._bundle.getString("forSecureOnly")
                                               : this._bundle.getString("forAnyConnection"),
-                     userContext: UserContextUI.getUserContextLabel(aItem.originAttributes.userContextId) };
+                     userContext: this._getUserContextString(aItem.originAttributes.userContextId) };
       for (var i = 0; i < ids.length; ++i) {
         document.getElementById(ids[i]).disabled = false;
       }
     }
     else {
       var noneSelected = this._bundle.getString("noCookieSelected");
       properties = { name: noneSelected, value: noneSelected, host: noneSelected,
                      path: noneSelected, expires: noneSelected,
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/win32/clang.manifest
@@ -0,0 +1,25 @@
+[
+{
+"clang_version": "r259916"
+},
+{
+"size": 266240,
+"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+"algorithm": "sha512",
+"filename": "mozmake.exe"
+},
+{
+"size": 167175,
+"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+"algorithm": "sha512",
+"filename": "sccache.tar.bz2",
+"unpack": true
+},
+{
+"size": 164660139,
+"digest": "7db5f16921c10023152e6a7765c6d31dfefc91a8742a14b6279bd253f86a683d8d951b17608aa200978f8cd433ead32e0ae9b53778566d2994e1db7af1df4cc4",
+"algorithm": "sha512",
+"filename": "clang32.tar.bz2",
+"unpack": true
+}
+]
new file mode 100644
--- /dev/null
+++ b/browser/config/tooltool-manifests/win64/clang.manifest
@@ -0,0 +1,25 @@
+[
+{
+"clang_version": "r259916"
+},
+{
+"size": 266240,
+"digest": "bb345b0e700ffab4d09436981f14b5de84da55a3f18a7f09ebc4364a4488acdeab8d46f447b12ac70f2da1444a68b8ce8b8675f0dae2ccf845e966d1df0f0869",
+"algorithm": "sha512",
+"filename": "mozmake.exe"
+},
+{
+"size": 167175,
+"digest": "0b71a936edf5bd70cf274aaa5d7abc8f77fe8e7b5593a208f805cc9436fac646b9c4f0b43c2b10de63ff3da671497d35536077ecbc72dba7f8159a38b580f831",
+"algorithm": "sha512",
+"filename": "sccache.tar.bz2",
+"unpack": true
+},
+{
+"size": 168551971,
+"digest": "af127aba2ed881bad6e83fd162125038f2450f7b6de64efe84d1281052225ec92db6d96d6ade6ac2642f1b00761b3be5725f79c41953dc885a81185097cc81a5",
+"algorithm": "sha512",
+"filename": "clang64.tar.bz2",
+"unpack": true
+}
+]
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/chrome/locale/jar.mn
@@ -0,0 +1,37 @@
+#filter substitution
+# 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/.
+
+# These are used for the big if statement, as the preprocessor can't handle
+# dashes.
+#define bn_BD bn-BD
+#define bn_IN bn-IN
+#define en_GB en-GB
+#define en_US en-US
+#define es_CL es-CL
+#define es_ES es-ES
+#define es_MX es-MX
+#define fy_NL fy-NL
+#define gu_IN gu-IN
+#define hi_IN hi-IN
+#define hy_AM hy-AM
+#define nb_NO nb-NO
+#define ne_NP ne-NP
+#define pa_IN pa-IN
+#define pt_BR pt-BR
+#define pt_PT pt-PT
+#define sv_SE sv-SE
+#define zh_CN zh-CN
+#define zh_TW zh-TW
+
+[features/loop@mozilla.org] @AB_CD@.jar:
+% locale loop @AB_CD@ %locale/@AB_CD@/
+  # For locales we support, include the file from the locale's directory in the
+  # source tree.
+  # For other locales (and en-US) fallback to the en-US directory.
+#if AB_CD == af || AB_CD == ar || AB_CD == as || AB_CD == ast || AB_CD == az || AB_CD == be || AB_CD == bg || AB_CD == bn_BD || AB_CD == bn_IN || AB_CD == bs || AB_CD == ca || AB_CD == cs || AB_CD == cy || AB_CD == da || AB_CD == de || AB_CD == dsb || AB_CD == el || AB_CD == en_GB || AB_CD == en_US || AB_CD == eo || AB_CD == es_CL || AB_CD == es_ES || AB_CD == es_MX || AB_CD == et || AB_CD == eu || AB_CD == fa || AB_CD == ff || AB_CD == fi || AB_CD == fr || AB_CD == fy || AB_CD == fy_NL || AB_CD == ga || AB_CD == gd || AB_CD == gl || AB_CD == gu_IN || AB_CD == he || AB_CD == hi_IN || AB_CD == hr || AB_CD == hsb || AB_CD == ht || AB_CD == hu || AB_CD == hy_AM || AB_CD == id || AB_CD == it || AB_CD == ja || AB_CD == kk || AB_CD == km || AB_CD == kn || AB_CD == ko || AB_CD == ku || AB_CD == lij || AB_CD == lt || AB_CD == lv || AB_CD == mk || AB_CD == ml || AB_CD == mn || AB_CD == ms || AB_CD == my || AB_CD == nb_NO || AB_CD == ne_NP || AB_CD == nl || AB_CD == or || AB_CD == pa || AB_CD == pa_IN || AB_CD == pl || AB_CD == pt || AB_CD == pt_BR || AB_CD == pt_PT || AB_CD == rm || AB_CD == ro || AB_CD == ru || AB_CD == si || AB_CD == sk || AB_CD == sl || AB_CD == son || AB_CD == sq || AB_CD == sr || AB_CD == sv_SE || AB_CD == ta || AB_CD == te || AB_CD == th || AB_CD == tr || AB_CD == uk || AB_CD == ur || AB_CD == vi || AB_CD == xh || AB_CD == zh_CN || AB_CD == zh_TW || AB_CD == zu
+  locale/@AB_CD@/                (@AB_CD@/*)
+#else
+  locale/@AB_CD@/                (en-US/*)
+#endif
new file mode 100644
--- /dev/null
+++ b/browser/extensions/loop/chrome/locale/moz.build
@@ -0,0 +1,7 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+JAR_MANIFESTS += ['jar.mn']
--- a/browser/extensions/loop/jar.mn
+++ b/browser/extensions/loop/jar.mn
@@ -1,39 +1,16 @@
 #filter substitution
 # 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/.
 
-# These are used for the big if statement, as the preprocessor can't handle
-# dashes.
-#define bn_BD bn-BD
-#define bn_IN bn-IN
-#define en_GB en-GB
-#define en_US en-US
-#define es_CL es-CL
-#define es_ES es-ES
-#define es_MX es-MX
-#define fy_NL fy-NL
-#define gu_IN gu-IN
-#define hi_IN hi-IN
-#define hy_AM hy-AM
-#define nb_NO nb-NO
-#define ne_NP ne-NP
-#define pa_IN pa-IN
-#define pt_BR pt-BR
-#define pt_PT pt-PT
-#define sv_SE sv-SE
-#define zh_CN zh-CN
-#define zh_TW zh-TW
-
 [features/loop@mozilla.org] chrome.jar:
 % content loop %content/ contentaccessible=yes
 % content loop-locale-fallback %content/locale-fallback/en-US/
-% locale loop @AB_CD@ %locale/@AB_CD@/
 % skin loop classic/1.0 %skin/linux/ os=Linux
 % skin loop classic/1.0 %skin/osx/ os=Darwin
 % skin loop classic/1.0 %skin/windows/ os=WINNT
 % skin loop-shared classic/1.0 %skin/shared/
 % override chrome://loop/skin/menuPanel.png       chrome://loop/skin/menuPanel-yosemite.png       os=Darwin osversion>=10.10
 % override chrome://loop/skin/menuPanel@2x.png    chrome://loop/skin/menuPanel-yosemite@2x.png    os=Darwin osversion>=10.10
 % override chrome://loop/skin/toolbar.png         chrome://loop/skin/toolbar-yosemite.png         os=Darwin osversion>=10.10
 % override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-yosemite@2x.png      os=Darwin osversion>=10.10
@@ -49,24 +26,16 @@
 % override chrome://loop/skin/toolbar.png         chrome://loop/skin/toolbar-win8.png            os=WINNT osversion=6.3
 % override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-XP@2x.png            os=WINNT osversion<6
 % override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-aero@2x.png          os=WINNT osversion=6
 % override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-aero@2x.png          os=WINNT osversion=6.1
 % override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-win8@2x.png         os=WINNT osversion=6.2
 % override chrome://loop/skin/toolbar@2x.png      chrome://loop/skin/toolbar-win8@2x.png         os=WINNT osversion=6.3
   # Due to the way Loop's L10n works, we include a fallback to en-US.
   content/locale-fallback/en-US/          (chrome/locale/en-US/*)
-  # For locales we support, include the file from the locale's directory in the
-  # source tree.
-  # For other locales (and en-US) fallback to the en-US directory.
-#if AB_CD == af || AB_CD == ar || AB_CD == as || AB_CD == ast || AB_CD == az || AB_CD == be || AB_CD == bg || AB_CD == bn_BD || AB_CD == bn_IN || AB_CD == bs || AB_CD == ca || AB_CD == cs || AB_CD == cy || AB_CD == da || AB_CD == de || AB_CD == dsb || AB_CD == el || AB_CD == en_GB || AB_CD == en_US || AB_CD == eo || AB_CD == es_CL || AB_CD == es_ES || AB_CD == es_MX || AB_CD == et || AB_CD == eu || AB_CD == fa || AB_CD == ff || AB_CD == fi || AB_CD == fr || AB_CD == fy || AB_CD == fy_NL || AB_CD == ga || AB_CD == gd || AB_CD == gl || AB_CD == gu_IN || AB_CD == he || AB_CD == hi_IN || AB_CD == hr || AB_CD == hsb || AB_CD == ht || AB_CD == hu || AB_CD == hy_AM || AB_CD == id || AB_CD == it || AB_CD == ja || AB_CD == kk || AB_CD == km || AB_CD == kn || AB_CD == ko || AB_CD == ku || AB_CD == lij || AB_CD == lt || AB_CD == lv || AB_CD == mk || AB_CD == ml || AB_CD == mn || AB_CD == ms || AB_CD == my || AB_CD == nb_NO || AB_CD == ne_NP || AB_CD == nl || AB_CD == or || AB_CD == pa || AB_CD == pa_IN || AB_CD == pl || AB_CD == pt || AB_CD == pt_BR || AB_CD == pt_PT || AB_CD == rm || AB_CD == ro || AB_CD == ru || AB_CD == si || AB_CD == sk || AB_CD == sl || AB_CD == son || AB_CD == sq || AB_CD == sr || AB_CD == sv_SE || AB_CD == ta || AB_CD == te || AB_CD == th || AB_CD == tr || AB_CD == uk || AB_CD == ur || AB_CD == vi || AB_CD == xh || AB_CD == zh_CN || AB_CD == zh_TW || AB_CD == zu
-  locale/@AB_CD@/                (chrome/locale/@AB_CD@/*)
-#else
-  locale/@AB_CD@/                (chrome/locale/en-US/*)
-#endif
   skin/                          (chrome/skin/*)
   content/modules/               (chrome/content/modules/*)
 # We don't package the test/ directory for panels, so do these separately.
   content/panels/                (chrome/content/panels/*.html)
   content/panels/css/            (chrome/content/panels/css/*)
   content/panels/js/             (chrome/content/panels/js/*)
   content/panels/vendor/         (chrome/content/panels/vendor/*)
 * content/preferences/prefs.js   (chrome/content/preferences/prefs.js)
--- a/browser/extensions/loop/moz.build
+++ b/browser/extensions/loop/moz.build
@@ -1,14 +1,16 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
+DIRS += ['chrome/locale']
+
 FINAL_TARGET_FILES.features['loop@mozilla.org'] += [
  'bootstrap.js'
 ]
 
 FINAL_TARGET_PP_FILES.features['loop@mozilla.org'] += [
   'install.rdf.in'
 ]
 
--- a/browser/extensions/loop/test/functional/hanging_threads.py
+++ b/browser/extensions/loop/test/functional/hanging_threads.py
@@ -10,18 +10,16 @@ import time
 # reduced down from <https://gist.github.com/niccokunzmann/6038331>; importing
 # this class just so happens to allow mach to exit rather than hanging after
 # our tests are run.
 
 
 def monitor():
     while 1:
         time.sleep(1.)
-        _ = sys._current_frames()
+        sys._current_frames()
 
 
 def start_monitoring():
     thread = threading.Thread(target=monitor)
     thread.daemon = True
     thread.start()
     return thread
-
-start_monitoring()
--- a/browser/extensions/loop/test/functional/serversetup.py
+++ b/browser/extensions/loop/test/functional/serversetup.py
@@ -9,27 +9,30 @@ from mozprocess import processhandler
 # XXX We want to convince ourselves that the race condition that importing
 # hanging_threads.py works around, tracked by bug 1046873, is gone, and get
 # rid of this inclusion entirely before we hook our functional tests up to
 # Tbpl, so that we don't introduce an intermittent failure.
 import sys
 import os
 sys.path.append(os.path.dirname(__file__))
 import hanging_threads
-from config import *
+from config import CONTENT_SERVER_PORT, LOOP_SERVER_PORT, LOOP_SERVER_URL, \
+    FIREFOX_PREFERENCES
+
+hanging_threads.start_monitoring()
 
 CONTENT_SERVER_COMMAND = ["make", "runserver"]
 CONTENT_SERVER_ENV = os.environ.copy()
 # Set PORT so that it does not interfere with any other
 # development server that might be running
 CONTENT_SERVER_ENV.update({"PORT": str(CONTENT_SERVER_PORT),
                            "LOOP_SERVER_URL": LOOP_SERVER_URL})
 
 ROOMS_WEB_APP_URL = "http://localhost:" + str(CONTENT_SERVER_PORT) + \
-  "/content/{token}"
+    "/content/{token}"
 
 LOOP_SERVER_COMMAND = ["make", "runserver"]
 LOOP_SERVER_ENV = os.environ.copy()
 # Set PORT so that it does not interfere with any other
 # development server that might be running
 LOOP_SERVER_ENV.update({"NODE_ENV": "dev",
                         "PORT": str(LOOP_SERVER_PORT),
                         "SERVER_ADDRESS": LOOP_SERVER_URL,
@@ -57,18 +60,18 @@ class LoopTestServers:
                                           env=LOOP_SERVER_ENV)
         p.run()
         return p
 
     @staticmethod
     def start_content_server():
         content_server_location = os.environ.get('STANDALONE_SERVER')
         if content_server_location is None:
-          content_server_location = os.path.join(os.path.dirname(__file__),
-                                                 "../../standalone")
+            content_server_location = os.path.join(os.path.dirname(__file__),
+                                                   "../../standalone")
         os.chdir(content_server_location)
 
         p = processhandler.ProcessHandler(CONTENT_SERVER_COMMAND,
                                           env=CONTENT_SERVER_ENV)
         p.run()
 
         # Give the content server time to start.
         import time
@@ -79,9 +82,8 @@ class LoopTestServers:
 
         return p
 
     def shutdown(self):
         if hasattr(self, "content_server"):
             self.content_server.kill()
         if hasattr(self, "loop_server"):
             self.loop_server.kill()
-
--- a/browser/extensions/loop/test/functional/test_1_browser_call.py
+++ b/browser/extensions/loop/test/functional/test_1_browser_call.py
@@ -7,17 +7,17 @@ from marionette import MarionetteTestCas
 import os
 import sys
 import urlparse
 sys.path.insert(1, os.path.dirname(os.path.abspath(__file__)))
 
 import pyperclip
 
 from serversetup import LoopTestServers
-from config import *
+from config import FIREFOX_PREFERENCES
 
 
 class Test1BrowserCall(MarionetteTestCase):
     # XXX Move this to setup class so it doesn't restart the server
     # after every test.
     def setUp(self):
         # start server
         self.loop_test_servers = LoopTestServers()
@@ -76,17 +76,17 @@ class Test1BrowserCall(MarionetteTestCas
     def switch_to_chatbox(self):
         self.marionette.set_context("chrome")
         self.marionette.switch_to_frame()
 
         # XXX should be using wait_for_element_displayed, but need to wait
         # for Marionette bug 1094246 to be fixed.
         chatbox = self.wait_for_element_exists(By.TAG_NAME, 'chatbox')
         script = ("return document.getAnonymousElementByAttribute("
-                  "arguments[0], 'class', 'chat-frame');")
+                  "arguments[0], 'anonid', 'content');")
         frame = self.marionette.execute_script(script, [chatbox])
         self.marionette.switch_to_frame(frame)
 
     def switch_to_standalone(self):
         self.marionette.set_context("content")
 
     def local_start_a_conversation(self):
         button = self.wait_for_element_displayed(By.CSS_SELECTOR, ".new-room-view .btn-info")
@@ -125,17 +125,17 @@ class Test1BrowserCall(MarionetteTestCas
         # Join the room
         join_button = self.wait_for_element_displayed(By.CLASS_NAME,
                                                       "btn-join")
         join_button.click()
 
     # Assumes the standalone or the conversation window is selected first.
     def check_video(self, selector):
         video = self.wait_for_element_displayed(By.CSS_SELECTOR,
-                                                        selector, 20)
+                                                selector, 20)
         self.wait_for_element_attribute_to_be_false(video, "paused")
         self.assertEqual(video.get_attribute("ended"), "false")
 
     def standalone_check_remote_video(self):
         self.switch_to_standalone()
         self.check_video(".remote-video")
 
     def local_check_remote_video(self):
@@ -211,18 +211,18 @@ class Test1BrowserCall(MarionetteTestCas
         self.marionette.set_context("chrome")
         self.marionette.switch_to_frame()
 
         # XXX should be using wait_for_element_displayed, but need to wait
         # for Marionette bug 1094246 to be fixed.
         chatbox = self.wait_for_element_exists(By.TAG_NAME, 'chatbox')
         script = '''
             let chatBrowser = document.getAnonymousElementByAttribute(
-              arguments[0], 'class',
-              'chat-frame')
+              arguments[0], 'anonid',
+              'content')
 
             // note that using wrappedJSObject waives X-ray vision, which
             // has security implications, but because we trust the code
             // running in the chatbox, it should be reasonably safe
             let chatGlobal = chatBrowser.contentWindow.wrappedJSObject;
 
             return chatGlobal.''' + expr
 
--- a/browser/locales/Makefile.in
+++ b/browser/locales/Makefile.in
@@ -100,17 +100,17 @@ libs-%:
 	$(NSINSTALL) -D $(DIST)/install
 	@$(MAKE) -C ../../toolkit/locales libs-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
 	@$(MAKE) -C ../../services/sync/locales AB_CD=$* XPI_NAME=locale-$*
 ifdef MOZ_WEBAPP_RUNTIME
 	@$(MAKE) -C ../../webapprt/locales AB_CD=$* XPI_NAME=locale-$*
 endif
 	@$(MAKE) -C ../../extensions/spellcheck/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../extensions/pocket/locales AB_CD=$* XPI_NAME=locale-$*
-	@$(MAKE) -C ../extensions/loop/ AB_CD=$* XPI_NAME=locale-$*
+	@$(MAKE) -C ../extensions/loop/chrome/locale AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../../intl/locales AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) -C ../../devtools/client/locales AB_CD=$* XPI_NAME=locale-$* XPI_ROOT_APPID='$(XPI_ROOT_APPID)'
 	@$(MAKE) -B searchplugins AB_CD=$* XPI_NAME=locale-$*
 	@$(MAKE) libs AB_CD=$* XPI_NAME=locale-$* PREF_DIR=$(PREF_DIR)
 	@$(MAKE) -C $(DEPTH)/$(MOZ_BRANDING_DIRECTORY)/locales AB_CD=$* XPI_NAME=locale-$*
 
 repackage-win32-installer: WIN32_INSTALLER_OUT=$(ABS_DIST)/$(PKG_INST_PATH)$(PKG_INST_BASENAME).exe
 repackage-win32-installer: $(call ESCAPE_WILDCARD,$(WIN32_INSTALLER_IN)) $(SUBMAKEFILES) libs-$(AB_CD)
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -740,17 +740,17 @@ e10s.accessibilityNotice.acceptButton.la
 e10s.accessibilityNotice.acceptButton.accesskey = O
 e10s.accessibilityNotice.enableAndRestart.label = Enable (Requires Restart)
 e10s.accessibilityNotice.enableAndRestart.accesskey = E
 
 # LOCALIZATION NOTE (usercontext.personal.label,
 #                    usercontext.work.label,
 #                    usercontext.shopping.label,
 #                    usercontext.banking.label):
-# These strings specify the four default contexts included in support of the
+# These strings specify the four predefined contexts included in support of the
 # Contextual Identity / Containers project. Each context is meant to represent
 # the context that the user is in when interacting with the site. Different
 # contexts will store cookies and other information from those sites in
 # different, isolated locations. You can enable the feature by typing
 # about:config in the URL bar and changing privacy.userContext.enabled to true.
 # Once enabled, you can open a new tab in a specific context by clicking
 # File > New Container Tab > (1 of 4 contexts). Once opened, you will see these
 # strings on the right-hand side of the URL bar.
--- a/browser/locales/en-US/chrome/browser/preferences/preferences.properties
+++ b/browser/locales/en-US/chrome/browser/preferences/preferences.properties
@@ -124,16 +124,17 @@ cookiesFiltered=The following cookies ma
 # LOCALIZATION NOTE (removeSelectedCookies):
 # Semicolon-separated list of plural forms. See:
 # http://developer.mozilla.org/en/docs/Localization_and_Plurals
 # If you need to display the number of selected elements in your language,
 # you can use #1 in your localization as a placeholder for the number.
 # For example this is the English string with numbers:   
 # removeSelectedCookied=Remove #1 Selected;Remove #1 Selected
 removeSelectedCookies=Remove Selected;Remove Selected
+noUserContextLabel=None
 
 #### Offline apps
 offlineAppsList.height=7em
 offlineAppRemoveTitle=Remove offline website data
 offlineAppRemovePrompt=After removing this data, %S will not be available offline.  Are you sure you want to remove this offline website?
 offlineAppRemoveConfirm=Remove offline data
 
 # LOCALIZATION NOTE: The next string is for the disk usage of the
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -391,17 +391,17 @@ class Automation(object):
         if os.path.exists(crashinject):
           status = subprocess.Popen([crashinject, str(processPID)]).wait()
           printstatus("crashinject", status)
           if status == 0:
             return
     self.log.info("Can't trigger Breakpad, just killing process")
     self.killPid(processPID)
 
-  def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath):
+  def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, outputHandler=None):
     """ Look for timeout or crashes and return the status after the process terminates """
     stackFixerFunction = None
     didTimeout = False
     hitMaxTime = False
     if proc.stdout is None:
       self.log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection")
     else:
       logsource = proc.stdout
@@ -431,17 +431,22 @@ class Automation(object):
       # The metro test harness hands back the real browser process id via log output which we need to
       # pick up on and parse out. This variable tracks the real browser process id if we find it.
       browserProcessId = -1
 
       (line, didTimeout) = self.readWithTimeout(logsource, timeout)
       while line != "" and not didTimeout:
         if stackFixerFunction:
           line = stackFixerFunction(line)
-        self.log.info(line.rstrip().decode("UTF-8", "ignore"))
+
+        if outputHandler is None:
+            self.log.info(line.rstrip().decode("UTF-8", "ignore"))
+        else:
+            outputHandler(line)
+
         if "TEST-START" in line and "|" in line:
           self.lastTestSeen = line.split("|")[1].strip()
         if not debuggerInfo and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line:
           self.dumpScreen(utilityPath)
 
         (line, didTimeout) = self.readWithTimeout(logsource, timeout)
 
         if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime):
@@ -525,17 +530,17 @@ class Automation(object):
   def checkForCrashes(self, minidumpDir, symbolsPath):
     return mozcrash.check_for_crashes(minidumpDir, symbolsPath, test_name=self.lastTestSeen)
 
   def runApp(self, testURL, env, app, profileDir, extraArgs, utilityPath = None,
              xrePath = None, certPath = None,
              debuggerInfo = None, symbolsPath = None,
              timeout = -1, maxTime = None, onLaunch = None,
              detectShutdownLeaks = False, screenshotOnFail=False, testPath=None, bisectChunk=None,
-             valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None):
+             valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None, outputHandler=None):
     """
     Run the app, log the duration it took to execute, return the status code.
     Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds.
     """
 
     if utilityPath == None:
       utilityPath = self.DIST_BIN
     if xrePath == None:
@@ -574,17 +579,18 @@ class Automation(object):
                  stderr = subprocess.STDOUT)
     self.log.info("INFO | automation.py | Application pid: %d", proc.pid)
 
     if onLaunch is not None:
       # Allow callers to specify an onLaunch callback to be fired after the
       # app is launched.
       onLaunch()
 
-    status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath)
+    status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath,
+                                outputHandler=outputHandler)
     self.log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime))
 
     # Do a final check for zombie child processes.
     zombieProcesses = self.checkForZombies(processLog, utilityPath, debuggerInfo)
 
     crashed = self.checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath)
 
     if crashed or zombieProcesses:
new file mode 100644
--- /dev/null
+++ b/build/build-clang/README
@@ -0,0 +1,44 @@
+build-clang.py
+==============
+
+A script to build clang from source.
+
+```
+usage: build-clang.py [-h] -c CONFIG [--clean]
+
+optional arguments:
+  -h, --help            show this help message and exit
+  -c CONFIG, --config CONFIG
+                        Clang configuration file
+  --clean               Clean the build directory
+```
+
+Pre-requisites
+--------------
+* Working build toolchain.
+* Subversion
+* CMake
+* Ninja
+* Python 2.7
+
+Please use the latest available CMake for your platform to avoid surprises.
+
+Config file format
+------------------
+
+build-clang.py accepts a JSON config format with the following fields:
+
+* llvm_revision: The LLVM SVN revision to build.
+* stages: Use 1, 2, or 3 to select different compiler stages.  The default is 3.
+* llvm_repo: SVN path to the LLVM repo.
+* clang_repo: SVN path to the Clang repo.
+* compiler_repo: SVN path to the compiler-rt repo.
+* libcxx_repo: SVN path to the libcxx repo.
+* python_path: Path to the Python 2.7 installation on the machine building clang.
+* gcc_dir: Path to the gcc toolchain installation, only required on Linux.
+* cc: Path to the bootsraping C Compiler.
+* cxx: Path to the bootsraping C++ Compiler.
+* patches: Optional list of patches to apply per platform.  Supported platforms: macosx64, linux32, linux64.  The default is Release.
+* build_type: The type of build to make.  Supported types: Release, Debug, RelWithDebInfo or MinSizeRel.
+* build_libcxx: Whether to build with libcxx.  The default is false.
+* assertions: Whether to enable LLVM assertions.  The default is false.
new file mode 100755
--- /dev/null
+++ b/build/build-clang/build-clang.py
@@ -0,0 +1,405 @@
+#!/usr/bin/python2.7
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+import os
+import os.path
+import shutil
+import subprocess
+import platform
+import json
+import argparse
+import tempfile
+import glob
+import errno
+from contextlib import contextmanager
+import sys
+
+DEBUG = os.getenv("DEBUG")
+
+
+def symlink(source, link_name):
+    os_symlink = getattr(os, "symlink", None)
+    if callable(os_symlink):
+        os_symlink(source, link_name)
+    else:
+        if os.path.isdir(source):
+            # Fall back to copying the directory :(
+            copy_dir_contents(source, link_name)
+
+
+def check_run(args):
+    global DEBUG
+    if DEBUG:
+        print >> sys.stderr, ' '.join(args)
+    r = subprocess.call(args)
+    assert r == 0
+
+
+def run_in(path, args):
+    d = os.getcwd()
+    global DEBUG
+    if DEBUG:
+        print >> sys.stderr, 'cd "%s"' % path
+    os.chdir(path)
+    check_run(args)
+    if DEBUG:
+        print >> sys.stderr, 'cd "%s"' % d
+    os.chdir(d)
+
+
+def patch(patch, srcdir):
+    patch = os.path.realpath(patch)
+    check_run(['patch', '-d', srcdir, '-p1', '-i', patch, '--fuzz=0',
+               '-s'])
+
+
+def build_package(package_build_dir, run_cmake, cmake_args):
+    if not os.path.exists(package_build_dir):
+        os.mkdir(package_build_dir)
+    if run_cmake:
+        run_in(package_build_dir, ["cmake"] + cmake_args)
+    run_in(package_build_dir, ["ninja", "install"])
+
+
+@contextmanager
+def updated_env(env):
+    old_env = os.environ.copy()
+    os.environ.update(env)
+    yield
+    os.environ.clear()
+    os.environ.update(old_env)
+
+
+def build_tar_package(tar, name, base, directory):
+    name = os.path.realpath(name)
+    # On Windows, we have to convert this into an msys path so that tar can
+    # understand it.
+    if is_windows():
+        name = name.replace('\\', '/').replace('c:', '/c')
+    run_in(base, [tar,
+                  "-c",
+                  "-%s" % ("J" if ".xz" in name else "j"),
+                  "-f",
+                  name, directory])
+
+
+def copy_dir_contents(src, dest):
+    for f in glob.glob("%s/*" % src):
+        try:
+            destname = "%s/%s" % (dest, os.path.basename(f))
+            if os.path.isdir(f):
+                shutil.copytree(f, destname)
+            else:
+                shutil.copy2(f, destname)
+        except OSError as e:
+            if e.errno == errno.ENOTDIR:
+                shutil.copy2(f, destname)
+            elif e.errno == errno.EEXIST:
+                if os.path.isdir(f):
+                    copy_dir_contents(f, destname)
+                else:
+                    os.remove(destname)
+                    shutil.copy2(f, destname)
+            else:
+                raise Exception('Directory not copied. Error: %s' % e)
+
+
+def mkdir_p(path):
+    try:
+        os.makedirs(path)
+    except OSError as e:
+        if e.errno != errno.EEXIST or not os.path.isdir(path):
+            raise
+
+
+def build_and_use_libgcc(env, clang_dir):
+    with updated_env(env):
+        tempdir = tempfile.mkdtemp()
+        gcc_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
+                               "..", "build-gcc")
+        run_in(gcc_dir, ["./build-gcc.sh", tempdir, "libgcc"])
+        run_in(tempdir, ["tar", "-xf", "gcc.tar.xz"])
+        libgcc_dir = glob.glob(os.path.join(tempdir,
+                                            "gcc", "lib", "gcc",
+                                            "x86_64-unknown-linux-gnu",
+                                            "[0-9]*"))[0]
+        clang_lib_dir = os.path.join(clang_dir, "lib", "gcc",
+                                     "x86_64-unknown-linux-gnu",
+                                     os.path.basename(libgcc_dir))
+        mkdir_p(clang_lib_dir)
+        copy_dir_contents(libgcc_dir, clang_lib_dir)
+        libgcc_dir = os.path.join(tempdir, "gcc", "lib64")
+        clang_lib_dir = os.path.join(clang_dir, "lib")
+        copy_dir_contents(libgcc_dir, clang_lib_dir)
+        include_dir = os.path.join(tempdir, "gcc", "include")
+        clang_include_dir = os.path.join(clang_dir, "include")
+        copy_dir_contents(include_dir, clang_include_dir)
+        shutil.rmtree(tempdir)
+
+
+def svn_co(source_dir, url, directory, revision):
+    run_in(source_dir, ["svn", "co", "-r", revision, url, directory])
+
+
+def svn_update(directory, revision):
+    run_in(directory, ["svn", "update", "-r", revision])
+
+
+def build_one_stage(cc, cxx, src_dir, stage_dir, build_libcxx,
+                    build_type, assertions, python_path):
+    build_one_stage_aux(cc, cxx, src_dir, stage_dir, build_libcxx,
+                        build_type, assertions, python_path)
+
+
+def get_platform():
+    p = platform.system()
+    if p == "Darwin":
+        return "macosx64"
+    elif p == "Linux":
+        if platform.architecture() == "AMD64":
+            return "linux64"
+        else:
+            return "linux32"
+    elif p == "Windows":
+        if platform.architecture() == "AMD64":
+            return "win64"
+        else:
+            return "win32"
+    else:
+        raise NotImplementedError("Not supported platform")
+
+
+def is_darwin():
+    return platform.system() == "Darwin"
+
+
+def is_linux():
+    return platform.system() == "Linux"
+
+
+def is_windows():
+    return platform.system() == "Windows"
+
+
+def build_one_stage_aux(cc, cxx, src_dir, stage_dir, build_libcxx,
+                        build_type, assertions, python_path):
+    if not os.path.exists(stage_dir):
+        os.mkdir(stage_dir)
+
+    build_dir = stage_dir + "/build"
+    inst_dir = stage_dir + "/clang"
+
+    run_cmake = True
+    if os.path.exists(build_dir + "/build.ninja"):
+        run_cmake = False
+
+    cmake_args = ["-GNinja",
+                  "-DCMAKE_C_COMPILER=%s" % cc,
+                  "-DCMAKE_CXX_COMPILER=%s" % cxx,
+                  "-DCMAKE_BUILD_TYPE=%s" % build_type,
+                  "-DLLVM_TARGETS_TO_BUILD=X86;ARM",
+                  "-DLLVM_ENABLE_ASSERTIONS=%s" % ("ON" if assertions else "OFF"),
+                  "-DPYTHON_EXECUTABLE=%s" % python_path,
+                  "-DCMAKE_INSTALL_PREFIX=%s" % inst_dir,
+                  "-DLLVM_TOOL_LIBCXX_BUILD=%s" % ("ON" if build_libcxx else "OFF"),
+                  "-DLIBCXX_LIBCPPABI_VERSION=\"\"",
+                  src_dir];
+    build_package(build_dir, run_cmake, cmake_args)
+
+if __name__ == "__main__":
+    # The directories end up in the debug info, so the easy way of getting
+    # a reproducible build is to run it in a know absolute directory.
+    # We use a directory in /builds/slave because the mozilla infrastructure
+    # cleans it up automatically.
+    base_dir = "/builds/slave/moz-toolchain"
+    if is_windows():
+        base_dir = "c:%s" % base_dir
+
+    source_dir = base_dir + "/src"
+    build_dir = base_dir + "/build"
+
+    llvm_source_dir = source_dir + "/llvm"
+    clang_source_dir = source_dir + "/clang"
+    compiler_rt_source_dir = source_dir + "/compiler-rt"
+    libcxx_source_dir = source_dir + "/libcxx"
+
+    if is_darwin():
+        os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.7'
+
+    exe_ext = ""
+    if is_windows():
+        exe_ext = ".exe"
+
+    cc_name = "clang"
+    cxx_name = "clang++"
+    if is_windows():
+        cc_name = "clang-cl"
+        cxx_name = "clang-cl"
+
+    parser = argparse.ArgumentParser()
+    parser.add_argument('-c', '--config', required=True,
+                        type=argparse.FileType('r'),
+                        help="Clang configuration file")
+    parser.add_argument('--clean', required=False,
+                        action='store_true',
+                        help="Clean the build directory")
+
+    args = parser.parse_args()
+    config = json.load(args.config)
+
+    if args.clean:
+        shutil.rmtree(build_dir)
+        os.sys.exit(0)
+
+    llvm_revision = config["llvm_revision"]
+    llvm_repo = config["llvm_repo"]
+    clang_repo = config["clang_repo"]
+    compiler_repo = config["compiler_repo"]
+    libcxx_repo = config["libcxx_repo"]
+    stages = 3
+    if "stages" in config:
+        stages = int(config["stages"])
+        if stages not in (1, 2, 3):
+            raise ValueError("We only know how to build 1, 2, or 3 stages")
+    build_type = "Release"
+    if "build_type" in config:
+        build_type = config["build_type"]
+        if build_type not in ("Release", "Debug", "RelWithDebInfo", "MinSizeRel"):
+            raise ValueError("We only know how to do Release, Debug, RelWithDebInfo or MinSizeRel builds")
+    build_libcxx = False
+    if "build_libcxx" in config:
+        build_libcxx = config["build_libcxx"]
+        if build_libcxx not in (True, False):
+            raise ValueError("Only boolean values are accepted for build_libcxx.")
+    assertions = False
+    if "assertions" in config:
+        assertions = config["assertions"]
+        if assertions not in (True, False):
+            raise ValueError("Only boolean values are accepted for assertions.")
+    python_path = None
+    if "python_path" not in config:
+        raise ValueError("Config file needs to set python_path")
+    python_path = config["python_path"]
+    gcc_dir = None
+    if "gcc_dir" in config:
+        gcc_dir = config["gcc_dir"]
+        if not os.path.exists(gcc_dir):
+            raise ValueError("gcc_dir must point to an existing path")
+    if is_linux() and gcc_dir is None:
+        raise ValueError("Config file needs to set gcc_dir")
+    cc = None
+    if "cc" in config:
+        cc = config["cc"]
+        if not os.path.exists(cc):
+            raise ValueError("cc must point to an existing path")
+    else:
+        raise ValueError("Config file needs to set cc")
+    cxx = None
+    if "cxx" in config:
+        cxx = config["cxx"]
+        if not os.path.exists(cxx):
+            raise ValueError("cxx must point to an existing path")
+    else:
+        raise ValueError("Config file needs to set cxx")
+
+    if not os.path.exists(source_dir):
+        os.makedirs(source_dir)
+        svn_co(source_dir, llvm_repo, llvm_source_dir, llvm_revision)
+        svn_co(source_dir, clang_repo, clang_source_dir, llvm_revision)
+        svn_co(source_dir, compiler_repo, compiler_rt_source_dir, llvm_revision)
+        svn_co(source_dir, libcxx_repo, libcxx_source_dir, llvm_revision)
+        for p in config.get("patches", {}).get(get_platform(), []):
+            patch(p, source_dir)
+    else:
+        svn_update(llvm_source_dir, llvm_revision)
+        svn_update(clang_source_dir, llvm_revision)
+        svn_update(compiler_rt_source_dir, llvm_revision)
+        svn_update(libcxx_source_dir, llvm_revision)
+
+    symlinks = [(source_dir + "/clang",
+                 llvm_source_dir + "/tools/clang"),
+                (source_dir + "/compiler-rt",
+                 llvm_source_dir + "/projects/compiler-rt"),
+                (source_dir + "/libcxx",
+                 llvm_source_dir + "/projects/libcxx")]
+    for l in symlinks:
+        # On Windows, we have to re-copy the whole directory every time.
+        if not is_windows() and os.path.islink(l[1]):
+            continue
+        if os.path.isdir(l[1]):
+            shutil.rmtree(l[1])
+        elif os.path.exists(l[1]):
+            os.unlink(l[1])
+        symlink(l[0], l[1])
+
+    if not os.path.exists(build_dir):
+        os.makedirs(build_dir)
+
+    stage1_dir = build_dir + '/stage1'
+    stage1_inst_dir = stage1_dir + '/clang'
+
+    final_stage_dir = stage1_dir
+
+    if is_darwin():
+        extra_cflags = ""
+        extra_cxxflags = "-stdlib=libc++"
+        extra_cflags2 = ""
+        extra_cxxflags2 = "-stdlib=libc++"
+    elif is_linux():
+        extra_cflags = "-static-libgcc"
+        extra_cxxflags = "-static-libgcc -static-libstdc++"
+        extra_cflags2 = "-fPIC --gcc-toolchain=%s" % gcc_dir
+        extra_cxxflags2 = "-fPIC --gcc-toolchain=%s" % gcc_dir
+
+        if os.environ.has_key('LD_LIBRARY_PATH'):
+            os.environ['LD_LIBRARY_PATH'] = '%s/lib64/:%s' % (gcc_dir, os.environ['LD_LIBRARY_PATH']);
+        else:
+            os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir
+    elif is_windows():
+        extra_cflags = ""
+        extra_cxxflags = ""
+        extra_cflags2 = ""
+        extra_cxxflags2 = ""
+
+    build_one_stage(
+        cc + " %s" % extra_cflags,
+        cxx + " %s" % extra_cxxflags,
+        llvm_source_dir, stage1_dir, build_libcxx,
+        build_type, assertions, python_path)
+
+    if stages > 1:
+        stage2_dir = build_dir + '/stage2'
+        stage2_inst_dir = stage2_dir + '/clang'
+        final_stage_dir = stage2_dir
+        build_one_stage(
+            stage1_inst_dir + "/bin/%s%s %s" %
+                (cc_name, exe_ext, extra_cflags2),
+            stage1_inst_dir + "/bin/%s%s %s" %
+                (cxx_name, exe_ext, extra_cxxflags2),
+            llvm_source_dir, stage2_dir, build_libcxx,
+            build_type, assertions, python_path)
+
+        if stages > 2:
+            stage3_dir = build_dir + '/stage3'
+            final_stage_dir = stage3_dir
+            build_one_stage(
+                stage2_inst_dir + "/bin/%s%s %s" %
+                    (cc_name, exe_ext, extra_cflags2),
+                stage2_inst_dir + "/bin/%s%s %s" %
+                    (cxx_name, exe_ext, extra_cxxflags2),
+                llvm_source_dir, stage3_dir, build_libcxx,
+                build_type, assertions, python_path)
+
+    if is_linux():
+        final_stage_inst_dir = final_stage_dir + '/clang'
+        build_and_use_libgcc(
+            {"CC": cc + " %s" % extra_cflags,
+             "CXX": cxx + " %s" % extra_cxxflags},
+            final_stage_inst_dir)
+
+    if is_darwin() or is_windows():
+        build_tar_package("tar", "clang.tar.bz2", final_stage_dir, "clang")
+    else:
+        build_tar_package("tar", "clang.tar.xz", final_stage_dir, "clang")
rename from build/unix/build-clang/clang-static-analysis-linux64-centos6.json
rename to build/build-clang/clang-static-analysis-linux64-centos6.json
rename from build/unix/build-clang/clang-static-analysis-linux64.json
rename to build/build-clang/clang-static-analysis-linux64.json
rename from build/unix/build-clang/clang-static-analysis-macosx64.json
rename to build/build-clang/clang-static-analysis-macosx64.json
new file mode 100644
--- /dev/null
+++ b/build/build-clang/clang-static-analysis-win32.json
@@ -0,0 +1,16 @@
+{
+    "llvm_revision": "259916",
+    "stages": "3",
+    "build_libcxx": false,
+    "build_type": "Release",
+    "assertions": false,
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
+    "python_path": "c:/mozilla-build/python/python.exe",
+    "cc": "c:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/BIN/amd64_x86/cl.exe",
+    "cxx": "c:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/BIN/amd64_x86/cl.exe",
+    "patches": {
+    }
+}
new file mode 100644
--- /dev/null
+++ b/build/build-clang/clang-static-analysis-win64.json
@@ -0,0 +1,16 @@
+{
+    "llvm_revision": "259916",
+    "stages": "3",
+    "build_libcxx": false,
+    "build_type": "Release",
+    "assertions": false,
+    "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk",
+    "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk",
+    "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk",
+    "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk",
+    "python_path": "c:/mozilla-build/python/python.exe",
+    "cc": "c:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/BIN/amd64/cl.exe",
+    "cxx": "c:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/BIN/amd64/cl.exe",
+    "patches": {
+    }
+}
rename from build/unix/build-clang/llvm-debug-frame.patch
rename to build/build-clang/llvm-debug-frame.patch
rename from build/unix/build-clang/query-selector-visibility.patch
rename to build/build-clang/query-selector-visibility.patch
rename from build/unix/build-clang/return-empty-string-non-mangled.patch
rename to build/build-clang/return-empty-string-non-mangled.patch
--- a/build/mobile/b2gautomation.py
+++ b/build/mobile/b2gautomation.py
@@ -204,33 +204,41 @@ class B2GRemoteAutomation(Automation):
         if (self._remoteProfile):
             profileDir = self._remoteProfile
 
         cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs)
 
         return app, args
 
     def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime,
-                      debuggerInfo, symbolsPath):
+                      debuggerInfo, symbolsPath, outputHandler=None):
         """ Wait for tests to finish (as evidenced by a signature string
             in logcat), or for a given amount of time to elapse with no
             output.
         """
         timeout = timeout or 120
         while True:
-            currentlog = proc.getStdoutLines(timeout)
-            if currentlog:
-                print currentlog
+            lines = proc.getStdoutLines(timeout)
+            if lines:
+                currentlog = '\n'.join(lines)
+
+                if outputHandler:
+                    for line in lines:
+                        outputHandler(line)
+                else:
+                    print(currentlog)
+
                 # Match the test filepath from the last TEST-START line found in the new
                 # log content. These lines are in the form:
                 # ... INFO TEST-START | /filepath/we/wish/to/capture.html\n
                 testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", currentlog)
                 if testStartFilenames:
                     self.lastTestSeen = testStartFilenames[-1]
-                if hasattr(self, 'logFinish') and self.logFinish in currentlog:
+                if (outputHandler and outputHandler.suite_finished) or (
+                        hasattr(self, 'logFinish') and self.logFinish in currentlog):
                     return 0
             else:
                 self.log.info("TEST-UNEXPECTED-FAIL | %s | application timed "
                               "out after %d seconds with no output",
                               self.lastTestSeen, int(timeout))
                 self._devicemanager.killProcess('/system/b2g/b2g', sig=signal.SIGABRT)
 
                 timeout = 10 # seconds
@@ -429,21 +437,22 @@ class B2GRemoteAutomation(Automation):
             # get all of the lines that are currently available
             while True:
                 try:
                     lines.append(self.queue.get_nowait())
                 except Queue.Empty:
                     break
 
             # wait 'timeout' for any additional lines
-            try:
-                lines.append(self.queue.get(True, timeout))
-            except Queue.Empty:
-                pass
-            return '\n'.join(lines)
+            if not lines:
+                try:
+                    lines.append(self.queue.get(True, timeout))
+                except Queue.Empty:
+                    pass
+            return lines
 
         def wait(self, timeout=None):
             # this should never happen
             raise Exception("'wait' called on B2GInstance")
 
         def kill(self):
             # this should never happen
             raise Exception("'kill' called on B2GInstance")
--- a/build/mobile/remoteautomation.py
+++ b/build/mobile/remoteautomation.py
@@ -90,17 +90,17 @@ class RemoteAutomation(Automation):
         # standard WebRTC setting for NSPR_LOG_MODULES is not available.
         # env.setdefault('NSPR_LOG_MODULES', 'signaling:5,mtransport:5,datachannel:5,jsep:5,MediaPipelineFactory:5')
         env.setdefault('R_LOG_LEVEL', '6')
         env.setdefault('R_LOG_DESTINATION', 'stderr')
         env.setdefault('R_LOG_VERBOSE', '1')
 
         return env
 
-    def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath):
+    def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, outputHandler=None):
         """ Wait for tests to finish.
             If maxTime seconds elapse or no output is detected for timeout
             seconds, kill the process and fail the test.
         """
         # maxTime is used to override the default timeout, we should honor that
         status = proc.wait(timeout = maxTime, noOutputTimeout = timeout)
         self.lastTestSeen = proc.getLastTestSeen
 
@@ -302,58 +302,60 @@ class RemoteAutomation(Automation):
             # running processes for the remote case, but for now we'll assume
             # that this method can be called when nothing exists and it is not
             # an error
             if pid is None:
                 return 0
             return pid
 
         def read_stdout(self):
-            """ Fetch the full remote log file using devicemanager and return just
-                the new log entries since the last call (as a list of messages or lines).
+            """
+            Fetch the full remote log file using devicemanager, process them and
+            return whether there were any new log entries since the last call.
             """
             if not self.dm.fileExists(self.proc):
-                return []
+                return False
             try:
                 newLogContent = self.dm.pullFile(self.proc, self.stdoutlen)
             except DMError:
                 # we currently don't retry properly in the pullFile
                 # function in dmSUT, so an error here is not necessarily
                 # the end of the world
-                return []
+                return False
             if not newLogContent:
-                return []
+                return False
 
             self.stdoutlen += len(newLogContent)
 
             if self.messageLogger is None:
                 testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", newLogContent)
                 if testStartFilenames:
                     self.lastTestSeen = testStartFilenames[-1]
                 print newLogContent
-                return [newLogContent]
+                return True
 
             self.logBuffer += newLogContent
             lines = self.logBuffer.split('\n')
-            if not lines:
-                return
 
-            # We only keep the last (unfinished) line in the buffer
-            self.logBuffer = lines[-1]
-            del lines[-1]
-            messages = []
+            if lines:
+                # We only keep the last (unfinished) line in the buffer
+                self.logBuffer = lines[-1]
+                del lines[-1]
+
+            if not lines:
+                return False
+
             for line in lines:
                 # This passes the line to the logger (to be logged or buffered)
                 # and returns a list of structured messages (dict)
                 parsed_messages = self.messageLogger.write(line)
                 for message in parsed_messages:
-                    if message['action'] == 'test_start':
+                    if isinstance(message, dict) and message.get('action') == 'test_start':
                         self.lastTestSeen = message['test']
-                messages += parsed_messages
-            return messages
+            return True
 
         @property
         def getLastTestSeen(self):
             return self.lastTestSeen
 
         # Wait for the remote process to end (or for its activity to go to background).
         # While waiting, periodically retrieve the process output and print it.
         # If the process is still running after *timeout* seconds, return 1;
@@ -369,20 +371,20 @@ class RemoteAutomation(Automation):
             status = 0
             top = self.procName
             slowLog = False
             while (top == self.procName):
                 # Get log updates on each interval, but if it is taking
                 # too long, only do it every 60 seconds
                 if (not slowLog) or (timer % 60 == 0):
                     startRead = datetime.datetime.now()
-                    messages = self.read_stdout()
+                    hasOutput = self.read_stdout()
                     if (datetime.datetime.now() - startRead) > datetime.timedelta(seconds=5):
                         slowLog = True
-                    if messages:
+                    if hasOutput:
                         noOutputTimer = 0
                 time.sleep(interval)
                 timer += interval
                 noOutputTimer += interval
                 if (timer > timeout):
                     status = 1
                     break
                 if (noOutputTimeout and noOutputTimer > noOutputTimeout):
new file mode 100644
--- /dev/null
+++ b/build/mozconfig.clang-cl
@@ -0,0 +1,7 @@
+CLANG_DIR=`cd "$topsrcdir/clang/bin" ; pwd`
+export PATH="${CLANG_DIR}:${PATH}"
+
+mk_export_correct_style PATH
+
+export CC=clang-cl
+export CXX=clang-cl
deleted file mode 100644
--- a/build/unix/build-clang/README
+++ /dev/null
@@ -1,34 +0,0 @@
-build-clang.py
-==============
-
-A script to build clang from source.
-
-```
-usage: build-clang.py [-h] -c CONFIG [--clean]
-
-optional arguments:
-  -h, --help            show this help message and exit
-  -c CONFIG, --config CONFIG
-                        Clang configuration file
-  --clean               Clean the build directory
-```
-
-Config file format
-------------------
-
-build-clang.py accepts a JSON config format with the following fields:
-
-* llvm_revision: The LLVM SVN revision to build.
-* stages: Use 1, 2, or 3 to select different compiler stages.  The default is 3.
-* llvm_repo: SVN path to the LLVM repo.
-* clang_repo: SVN path to the Clang repo.
-* compiler_repo: SVN path to the compiler-rt repo.
-* libcxx_repo: SVN path to the libcxx repo.
-* python_path: Path to the Python 2.7 installation on the machine building clang.
-* gcc_dir: Path to the gcc toolchain installation, only required on Linux.
-* cc: Path to the bootsraping C Compiler.
-* cxx: Path to the bootsraping C++ Compiler.
-* patches: Optional list of patches to apply per platform.  Supported platforms: macosx64, linux32, linux64.  The default is Release.
-* build_type: The type of build to make.  Supported types: Release, Debug, RelWithDebInfo or MinSizeRel.
-* build_libcxx: Whether to build with libcxx.  The default is false.
-* assertions: Whether to enable LLVM assertions.  The default is false.
deleted file mode 100755
--- a/build/unix/build-clang/build-clang.py
+++ /dev/null
@@ -1,341 +0,0 @@
-#!/usr/bin/python2.7
-# This Source Code Form is subject to the terms of the Mozilla Public
-# License, v. 2.0. If a copy of the MPL was not distributed with this
-# file, You can obtain one at http://mozilla.org/MPL/2.0/.
-
-import os
-import os.path
-import shutil
-import subprocess
-import platform
-import json
-import argparse
-import tempfile
-import glob
-import errno
-from contextlib import contextmanager
-import sys
-
-DEBUG = os.getenv("DEBUG")
-
-def check_run(args):
-    global DEBUG
-    if DEBUG:
-        print >> sys.stderr, ' '.join(args)
-    r = subprocess.call(args)
-    assert r == 0
-
-
-def run_in(path, args):
-    d = os.getcwd()
-    global DEBUG
-    if DEBUG:
-        print >> sys.stderr, 'cd "%s"' % path
-    os.chdir(path)
-    check_run(args)
-    if DEBUG:
-        print >> sys.stderr, 'cd "%s"' % d
-    os.chdir(d)
-
-
-def patch(patch, srcdir):
-    patch = os.path.realpath(patch)
-    check_run(['patch', '-d', srcdir, '-p1', '-i', patch, '--fuzz=0',
-               '-s'])
-
-
-def build_package(package_build_dir, run_cmake, cmake_args):
-    if not os.path.exists(package_build_dir):
-        os.mkdir(package_build_dir)
-    if run_cmake:
-        run_in(package_build_dir, ["cmake"] + cmake_args)
-    run_in(package_build_dir, ["ninja", "install"])
-
-
-@contextmanager
-def updated_env(env):
-    old_env = os.environ.copy()
-    os.environ.update(env)
-    yield
-    os.environ.clear()
-    os.environ.update(old_env)
-
-
-def build_tar_package(tar, name, base, directory):
-    name = os.path.realpath(name)
-    run_in(base, [tar,
-                  "-c",
-                  "-%s" % ("J" if ".xz" in name else "j"),
-                  "-f",
-                  name, directory])
-
-
-def copy_dir_contents(src, dest):
-    for f in glob.glob("%s/*" % src):
-        try:
-            destname = "%s/%s" % (dest, os.path.basename(f))
-            shutil.copytree(f, destname)
-        except OSError as e:
-            if e.errno == errno.ENOTDIR:
-                shutil.copy2(f, destname)
-            elif e.errno == errno.EEXIST:
-                if os.path.isdir(f):
-                    copy_dir_contents(f, destname)
-                else:
-                    os.remove(destname)
-                    shutil.copy2(f, destname)
-            else:
-                raise Exception('Directory not copied. Error: %s' % e)
-
-
-def mkdir_p(path):
-    try:
-        os.makedirs(path)
-    except OSError as e:
-        if e.errno != errno.EEXIST or not os.path.isdir(path):
-            raise
-
-
-def build_and_use_libgcc(env, clang_dir):
-    with updated_env(env):
-        tempdir = tempfile.mkdtemp()
-        gcc_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)),
-                               "..", "build-gcc")
-        run_in(gcc_dir, ["./build-gcc.sh", tempdir, "libgcc"])
-        run_in(tempdir, ["tar", "-xf", "gcc.tar.xz"])
-        libgcc_dir = glob.glob(os.path.join(tempdir,
-                                            "gcc", "lib", "gcc",
-                                            "x86_64-unknown-linux-gnu",
-                                            "[0-9]*"))[0]
-        clang_lib_dir = os.path.join(clang_dir, "lib", "gcc",
-                                     "x86_64-unknown-linux-gnu",
-                                     os.path.basename(libgcc_dir))
-        mkdir_p(clang_lib_dir)
-        copy_dir_contents(libgcc_dir, clang_lib_dir)
-        libgcc_dir = os.path.join(tempdir, "gcc", "lib64")
-        clang_lib_dir = os.path.join(clang_dir, "lib")
-        copy_dir_contents(libgcc_dir, clang_lib_dir)
-        include_dir = os.path.join(tempdir, "gcc", "include")
-        clang_include_dir = os.path.join(clang_dir, "include")
-        copy_dir_contents(include_dir, clang_include_dir)
-        shutil.rmtree(tempdir)
-
-
-def svn_co(url, directory, revision):
-    check_run(["svn", "co", "-r", revision, url, directory])
-
-
-def svn_update(directory, revision):
-    run_in(directory, ["svn", "update", "-r", revision])
-
-
-def build_one_stage(env, src_dir, stage_dir, build_libcxx, build_type, assertions,
-                    python_path):
-    with updated_env(env):
-        build_one_stage_aux(src_dir, stage_dir, build_libcxx, build_type,
-                            assertions, python_path)
-
-
-def get_platform():
-    p = platform.system()
-    if p == "Darwin":
-        return "macosx64"
-    elif p == "Linux":
-        if platform.processor() == "x86_64":
-            return "linux64"
-        else:
-            return "linux32"
-    else:
-        raise NotImplementedError("Not supported platform")
-
-
-def is_darwin():
-    return platform.system() == "Darwin"
-
-
-def build_one_stage_aux(src_dir, stage_dir, build_libcxx,
-                        build_type, assertions, python_path):
-    if not os.path.exists(stage_dir):
-        os.mkdir(stage_dir)
-
-    build_dir = stage_dir + "/build"
-    inst_dir = stage_dir + "/clang"
-
-    run_cmake = True
-    if os.path.exists(build_dir):
-        run_cmake = False
-
-    cmake_args = ["-GNinja",
-                  "-DCMAKE_BUILD_TYPE=%s" % build_type,
-                  "-DLLVM_TARGETS_TO_BUILD=X86;ARM",
-                  "-DLLVM_ENABLE_ASSERTIONS=%s" % ("ON" if assertions else "OFF"),
-                  "-DPYTHON_EXECUTABLE=%s" % python_path,
-                  "-DCMAKE_INSTALL_PREFIX=%s" % inst_dir,
-                  "-DLLVM_TOOL_LIBCXX_BUILD=%s" % ("ON" if build_libcxx else "OFF"),
-                  "-DLIBCXX_LIBCPPABI_VERSION=\"\"",
-                  src_dir];
-    build_package(build_dir, run_cmake, cmake_args)
-
-if __name__ == "__main__":
-    # The directories end up in the debug info, so the easy way of getting
-    # a reproducible build is to run it in a know absolute directory.
-    # We use a directory in /builds/slave because the mozilla infrastructure
-    # cleans it up automatically.
-    base_dir = "/builds/slave/moz-toolchain"
-
-    source_dir = base_dir + "/src"
-    build_dir = base_dir + "/build"
-
-    llvm_source_dir = source_dir + "/llvm"
-    clang_source_dir = source_dir + "/clang"
-    compiler_rt_source_dir = source_dir + "/compiler-rt"
-    libcxx_source_dir = source_dir + "/libcxx"
-
-    if is_darwin():
-        os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.7'
-
-    parser = argparse.ArgumentParser()
-    parser.add_argument('-c', '--config', required=True,
-                        type=argparse.FileType('r'),
-                        help="Clang configuration file")
-    parser.add_argument('--clean', required=False,
-                        action='store_true',
-                        help="Clean the build directory")
-
-    args = parser.parse_args()
-    config = json.load(args.config)
-
-    if args.clean:
-        shutil.rmtree(build_dir)
-        os.sys.exit(0)
-
-    llvm_revision = config["llvm_revision"]
-    llvm_repo = config["llvm_repo"]
-    clang_repo = config["clang_repo"]
-    compiler_repo = config["compiler_repo"]
-    libcxx_repo = config["libcxx_repo"]
-    stages = 3
-    if "stages" in config:
-        stages = int(config["stages"])
-        if stages not in (1, 2, 3):
-            raise ValueError("We only know how to build 1, 2, or 3 stages")
-    build_type = "Release"
-    if "build_type" in config:
-        build_type = config["build_type"]
-        if build_type not in ("Release", "Debug", "RelWithDebInfo", "MinSizeRel"):
-            raise ValueError("We only know how to do Release, Debug, RelWithDebInfo or MinSizeRel builds")
-    build_libcxx = False
-    if "build_libcxx" in config:
-        build_libcxx = config["build_libcxx"]
-        if build_libcxx not in (True, False):
-            raise ValueError("Only boolean values are accepted for build_libcxx.")
-    assertions = False
-    if "assertions" in config:
-        assertions = config["assertions"]
-        if assertions not in (True, False):
-            raise ValueError("Only boolean values are accepted for assertions.")
-    python_path = None
-    if "python_path" not in config:
-        raise ValueError("Config file needs to set python_path")
-    python_path = config["python_path"]
-    gcc_dir = None
-    if "gcc_dir" in config:
-        gcc_dir = config["gcc_dir"]
-        if not os.path.exists(gcc_dir):
-            raise ValueError("gcc_dir must point to an existing path")
-    if not is_darwin() and gcc_dir is None:
-        raise ValueError("Config file needs to set gcc_dir")
-    cc = None
-    if "cc" in config:
-        cc = config["cc"]
-        if not os.path.exists(cc):
-            raise ValueError("cc must point to an existing path")
-    else:
-        raise ValueError("Config file needs to set cc")
-    cxx = None
-    if "cxx" in config:
-        cxx = config["cxx"]
-        if not os.path.exists(cxx):
-            raise ValueError("cxx must point to an existing path")
-    else:
-        raise ValueError("Config file needs to set cxx")
-
-    if not os.path.exists(source_dir):
-        os.makedirs(source_dir)
-        svn_co(llvm_repo, llvm_source_dir, llvm_revision)
-        svn_co(clang_repo, clang_source_dir, llvm_revision)
-        svn_co(compiler_repo, compiler_rt_source_dir, llvm_revision)
-        svn_co(libcxx_repo, libcxx_source_dir, llvm_revision)
-        os.symlink("../../clang", llvm_source_dir + "/tools/clang")
-        os.symlink("../../compiler-rt",
-                   llvm_source_dir + "/projects/compiler-rt")
-        os.symlink("../../libcxx",
-                   llvm_source_dir + "/projects/libcxx")
-        for p in config.get("patches", {}).get(get_platform(), []):
-            patch(p, source_dir)
-    else:
-        svn_update(llvm_source_dir, llvm_revision)
-        svn_update(clang_source_dir, llvm_revision)
-        svn_update(compiler_rt_source_dir, llvm_revision)
-        svn_update(libcxx_source_dir, llvm_revision)
-
-    if not os.path.exists(build_dir):
-        os.makedirs(build_dir)
-
-    stage1_dir = build_dir + '/stage1'
-    stage1_inst_dir = stage1_dir + '/clang'
-
-    final_stage_dir = stage1_dir
-
-    if is_darwin():
-        extra_cflags = ""
-        extra_cxxflags = "-stdlib=libc++"
-        extra_cflags2 = ""
-        extra_cxxflags2 = "-stdlib=libc++"
-    else:
-        extra_cflags = "-static-libgcc"
-        extra_cxxflags = "-static-libgcc -static-libstdc++"
-        extra_cflags2 = "-fPIC --gcc-toolchain=%s" % gcc_dir
-        extra_cxxflags2 = "-fPIC --gcc-toolchain=%s" % gcc_dir
-
-        if os.environ.has_key('LD_LIBRARY_PATH'):
-            os.environ['LD_LIBRARY_PATH'] = '%s/lib64/:%s' % (gcc_dir, os.environ['LD_LIBRARY_PATH']);
-        else:
-            os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir
-
-    build_one_stage(
-        {"CC": cc + " %s" % extra_cflags,
-         "CXX": cxx + " %s" % extra_cxxflags},
-        llvm_source_dir, stage1_dir, build_libcxx,
-        build_type, assertions, python_path)
-
-    if stages > 1:
-        stage2_dir = build_dir + '/stage2'
-        stage2_inst_dir = stage2_dir + '/clang'
-        final_stage_dir = stage2_dir
-        build_one_stage(
-            {"CC": stage1_inst_dir + "/bin/clang %s" % extra_cflags2,
-             "CXX": stage1_inst_dir + "/bin/clang++ %s" % extra_cxxflags2},
-            llvm_source_dir, stage2_dir, build_libcxx,
-            build_type, assertions, python_path)
-
-        if stages > 2:
-            stage3_dir = build_dir + '/stage3'
-            final_stage_dir = stage3_dir
-            build_one_stage(
-                {"CC": stage2_inst_dir + "/bin/clang %s" % extra_cflags2,
-                 "CXX": stage2_inst_dir + "/bin/clang++ %s" % extra_cxxflags2},
-                llvm_source_dir, stage3_dir, build_libcxx,
-                build_type, assertions, python_path)
-
-    if not is_darwin():
-        final_stage_inst_dir = final_stage_dir + '/clang'
-        build_and_use_libgcc(
-            {"CC": cc + " %s" % extra_cflags,
-             "CXX": cxx + " %s" % extra_cxxflags},
-            final_stage_inst_dir)
-
-    if is_darwin():
-        build_tar_package("tar", "clang.tar.bz2", final_stage_dir, "clang")
-    else:
-        build_tar_package("tar", "clang.tar.xz", final_stage_dir, "clang")
--- a/config/external/nss/Makefile.in
+++ b/config/external/nss/Makefile.in
@@ -170,17 +170,17 @@ DEFAULT_GMAKE_FLAGS += NSDISTMODE=absolu
 endif
 ifdef MACOS_SDK_DIR
 DEFAULT_GMAKE_FLAGS += MACOS_SDK_DIR=$(MACOS_SDK_DIR)
 endif
 endif
 
 # Turn off TLS compression support because it requires system zlib.
 # See bug 580679 comment 18.
-DEFAULT_GMAKE_FLAGS += NSS_ENABLE_ZLIB=
+DEFAULT_GMAKE_FLAGS += NSS_SSL_ENABLE_ZLIB=
 
 # Disable building of the test programs in security/nss/lib/zlib
 DEFAULT_GMAKE_FLAGS += PROGRAMS=
 
 # Disable creating .chk files. They will be generated from packager.mk
 # When bug 681624 lands, we can replace CHECKLOC= with SKIP_SHLIBSIGN=1
 DEFAULT_GMAKE_FLAGS += CHECKLOC=
 
--- a/devtools/shared/apps/tests/mochitest.ini
+++ b/devtools/shared/apps/tests/mochitest.ini
@@ -1,8 +1,7 @@
 [DEFAULT]
+skip-if = (buildapp != 'b2g' && buildapp != 'mulet')
 support-files =
   debugger-protocol-helper.js
   redirect.sjs
 
 [test_webapps_actor.html]
-# The mochitest doesn't work on fennec yet
-skip-if = toolkit == 'android'
--- a/dom/activities/tests/mochi/mochitest.ini
+++ b/dom/activities/tests/mochi/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = e10s || os == 'android'
+skip-if = (buildapp != 'b2g' && buildapp != 'mulet')
 support-files =
   common.js
   system.webapp
   system.webapp^headers^
   manifest.webapp
   manifest.webapp^headers^
 
 [test_dev_mode_activity.html]
--- a/dom/apps/tests/chrome.ini
+++ b/dom/apps/tests/chrome.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = buildapp == 'b2g' || os == 'android'
+skip-if = buildapp != 'mulet'
 support-files =
   apps/*
   asmjs/*
   common.js
   cross_origin.html
   file_bug_945152.html
   file_bug_945152.sjs
   head.js
--- a/dom/apps/tests/mochitest.ini
+++ b/dom/apps/tests/mochitest.ini
@@ -1,9 +1,10 @@
 [DEFAULT]
+skip-if = buildapp != 'b2g' && buildapp != 'mulet'
 support-files =
   addons/application.zip
   addons/invalid.webapp
   addons/invalid.webapp^headers^
   addons/update.webapp
   addons/update.webapp^headers^
   addons/index.html
   chromeAddCert.js
--- a/dom/base/test/test_navigator_resolve_identity.html
+++ b/dom/base/test/test_navigator_resolve_identity.html
@@ -14,23 +14,16 @@ https://bugzilla.mozilla.org/show_bug.cg
 
   // Test WebIDL NavigatorProperty objects
   var x = navigator.mozContacts;
   is(typeof x, "object", "Should have a mozContacts object");
   delete navigator.mozContacts;
   var y = navigator.mozContacts;
   is(x, y, "Should have gotten the same mozContacts object again");
 
-  // Test Javascript-navigator-property objects
-  x = navigator.mozApps;
-  is(typeof x, "object", "Should have a mozApps object");
-  delete navigator.mozApps;
-  y = navigator.mozApps;
-  is(x, y, "Should have gotten the same mozApps object again");
-
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=985827">Mozilla Bug 985827</a>
 <p id="display"></p>
 <div id="content" style="display: none">
 
 </div>
--- a/dom/base/test/test_navigator_resolve_identity_xrays.xul
+++ b/dom/base/test/test_navigator_resolve_identity_xrays.xul
@@ -27,18 +27,13 @@ https://bugzilla.mozilla.org/show_bug.cg
     var nav = $("t").contentWindow.navigator;
     ok(Components.utils.isXrayWrapper(nav), "Should have an Xray here");
 
     // Test WebIDL NavigatorProperty objects
     is(typeof nav.mozContacts, "object", "Should have a mozContacts object");
     is(nav.mozContacts, nav.mozContacts,
        "Should have gotten the same mozContacts object again");
 
-    // Test Javascript-navigator-property objects
-    is(typeof nav.mozApps, "object", "Should have a mozApps object");
-    is(nav.mozApps, nav.mozApps,
-       "Should have gotten the same mozApps object again");
-
     SimpleTest.finish();
   });
   ]]>
   </script>
 </window>
--- a/dom/bindings/test/test_bug707564-chrome.html
+++ b/dom/bindings/test/test_bug707564-chrome.html
@@ -21,29 +21,29 @@ https://bugzilla.mozilla.org/show_bug.cg
 
 /** Test for Bug 775543 **/
 function test()
 {
   var nav = document.getElementById("t1").contentWindow.navigator;
   is(nav.foopy, undefined, "We should have an Xray now");
   is(nav.wrappedJSObject.foopy, 5, "We should have the right navigator object");
   var props = Object.getOwnPropertyNames(nav);
-  isnot(props.indexOf("mozApps"), -1,
-        "Should enumerate a mozApps property on navigator xray");
+  isnot(props.indexOf("mozContacts"), -1,
+        "Should enumerate a mozContacts property on navigator xray");
 
   var nav = document.getElementById("t2").contentWindow.navigator;
   is(nav.foopy, undefined, "We should have an Xray now again");
   is(nav.wrappedJSObject.foopy, 5, "We should have the right navigator object again");
   var found = false;
   for (var name in nav) {
-    if (name == "mozApps") {
+    if (name == "mozContacts") {
       found = true;
     }
   }
-  ok(found, "Should enumerate a mozApps property on navigator xray via for...in");
+  ok(found, "Should enumerate a mozContacts property on navigator xray via for...in");
 
   SimpleTest.finish();
 }
 
 addLoadEvent(test);
 
 SimpleTest.waitForExplicitFinish();
 </script>
--- a/dom/bindings/test/test_bug707564.html
+++ b/dom/bindings/test/test_bug707564.html
@@ -10,27 +10,27 @@ https://bugzilla.mozilla.org/show_bug.cg
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
   <script type="application/javascript">
 
   /** Test for Bug 707564 **/
   SimpleTest.waitForExplicitFinish();
 
   addLoadEvent(function() {
     var props = Object.getOwnPropertyNames(frames[0].navigator);
-    isnot(props.indexOf("mozApps"), -1,
-          "Should enumerate a mozApps property on navigator");
+    isnot(props.indexOf("mozContacts"), -1,
+          "Should enumerate a mozContacts property on navigator");
 
     // Now enumerate a different navigator object
     var found = false;
     for (var name in frames[1].navigator) {
-      if (name == "mozApps") {
+      if (name == "mozContacts") {
         found = true;
       }
     }
-    ok(found, "Should enumerate a mozApps property on navigator via for...in");
+    ok(found, "Should enumerate a mozContacts property on navigator via for...in");
     SimpleTest.finish();
   });
   </script>
 </head>
 <body>
 <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=707564">Mozilla Bug 707564</a>
 <p id="display"></p>
 <div id="content" style="display: none">
--- a/dom/broadcastchannel/tests/mochitest.ini
+++ b/dom/broadcastchannel/tests/mochitest.ini
@@ -15,12 +15,12 @@ support-files =
 [test_broadcastchannel_any.html]
 [test_broadcastchannel_basic.html]
 [test_broadcastchannel_close.html]
 [test_broadcastchannel_self.html]
 [test_broadcastchannel_sharedWorker.html]
 [test_broadcastchannel_worker.html]
 [test_broadcastchannel_worker_alive.html]
 [test_broadcastchannel_mozbrowser.html]
-skip-if = buildapp == 'b2g'
+skip-if = buildapp != 'mulet'
 [test_broadcastchannel_mozbrowser2.html]
-skip-if = buildapp == 'b2g'
+skip-if = buildapp != 'mulet'
 [test_bfcache.html]
--- a/dom/browser-element/mochitest/chrome.ini
+++ b/dom/browser-element/mochitest/chrome.ini
@@ -1,18 +1,18 @@
 [DEFAULT]
-skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
+skip-if = (buildapp != 'b2g' && buildapp != 'mulet') || (buildapp == 'b2g' && (toolkit != 'gonk' || debug))
 
 support-files =
   audio.ogg
   browserElement_MultipleAudioChannels.js
   browserElement_NotifyChannel.js
   file_browserElement_MultipleAudioChannels.html
   file_browserElement_NotifyChannel.html
   manifest.webapp
   manifest.webapp^headers^
   multipleAudioChannels_manifest.webapp
   multipleAudioChannels_manifest.webapp^headers^
 
 [test_browserElement_MultipleAudioChannels.html]
 tags = audiochannel
 [test_browserElement_NotifyChannel.html]
-tags = audiochannel
\ No newline at end of file
+tags = audiochannel
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -19,19 +19,19 @@ tags = audiochannel
 [test_browserElement_oop_ThemeColor.html]
 [test_browserElement_inproc_ErrorSecurity.html]
 skip-if = toolkit=='gonk'
 [test_browserElement_inproc_OpenMixedProcess.html]
 skip-if = toolkit=='gonk' || (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_Alert.html]
 [test_browserElement_oop_AlertInFrame.html]
 [test_browserElement_oop_AllowEmbedAppsInNestedOOIframe.html]
-skip-if = toolkit=='gonk'
+skip-if = toolkit == 'gonk' || buildapp != 'b2g'
 [test_browserElement_oop_AppFramePermission.html]
-skip-if = (toolkit == 'gonk' && !debug)
+skip-if = (toolkit == 'gonk' && !debug) || buildapp != 'b2g'
 [test_browserElement_oop_AppWindowNamespace.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_AudioChannelMutedByDefault.html]
 tags = audiochannel
 [test_browserElement_oop_Auth.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BackForward.html]
 [test_browserElement_oop_BadScreenshot.html]
@@ -39,16 +39,17 @@ skip-if = (toolkit == 'gonk' && !debug)
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_BrowserWindowResize.html]
 [test_browserElement_oop_Close.html]
 [test_browserElement_oop_CookiesNotThirdParty.html]
 [test_browserElement_oop_CopyPaste.html]
 [test_browserElement_oop_DOMRequestError.html]
 [test_browserElement_oop_DataURI.html]
 [test_browserElement_oop_DisallowEmbedAppsInOOP.html]
+skip-if = buildapp != 'b2g'
 [test_browserElement_oop_DocumentFirstPaint.html]
 [test_browserElement_oop_Download.html]
 disabled = bug 1022281
 [test_browserElement_oop_ErrorSecurity.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_ExecuteScript.html]
 [test_browserElement_oop_Find.html]
 [test_browserElement_oop_FirstPaint.html]
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = buildapp == 'mulet' || (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s
+skip-if = (buildapp == 'b2g' && (toolkit != 'gonk' || debug)) || e10s
 support-files =
   audio.ogg
   ../../../dom/media/test/short-video.ogv
   async.js
   browserElementTestHelpers.js
   browserElement_ActiveStateChangeOnChangingMutedOrVolume.js
   browserElement_Alert.js
   browserElement_AlertInFrame.js
@@ -161,17 +161,17 @@ support-files =
 [test_browserElement_inproc_Alert.html]
 [test_browserElement_inproc_AudioChannelSeeking.html]
 tags = audiochannel
 [test_browserElement_inproc_Viewmode.html]
 [test_browserElement_inproc_ThemeColor.html]
 skip-if = buildapp == 'b2g'
 [test_browserElement_inproc_AlertInFrame.html]
 [test_browserElement_inproc_AppFramePermission.html]
-skip-if = toolkit == 'android' || buildapp == 'b2g'
+skip-if = toolkit == 'android' || buildapp != 'mulet'
 [test_browserElement_inproc_AppWindowNamespace.html]
 skip-if = toolkit == 'android' || buildapp == 'b2g' # android(TIMED_OUT, bug 783509) androidx86(TIMED_OUT, bug 783509)
 [test_browserElement_inproc_AudioChannelMutedByDefault.html]
 tags = audiochannel
 skip-if = toolkit == 'android'
 [test_browserElement_inproc_AudioPlayback.html]
 [test_browserElement_inproc_Auth.html]
 skip-if = buildapp == 'b2g'
@@ -187,17 +187,17 @@ skip-if = toolkit == 'android' || builda
 skip-if = buildapp == 'b2g'
 [test_browserElement_inproc_ContextmenuEvents.html]
 [test_browserElement_inproc_CookiesNotThirdParty.html]
 [test_browserElement_inproc_CopyPaste.html]
 skip-if = (os == "android") # Disabled on Android, see bug 1230421
 [test_browserElement_inproc_DOMRequestError.html]
 [test_browserElement_inproc_DataURI.html]
 [test_browserElement_inproc_DisallowEmbedAppsInOOP.html]
-skip-if = os == "android" || toolkit == 'gonk' # embed-apps doesn't work in the mochitest app
+skip-if = os == "android" || toolkit == 'gonk' || buildapp != 'mulet' # embed-apps doesn't work in the mochitest app
 [test_browserElement_inproc_DocumentFirstPaint.html]
 [test_browserElement_inproc_Download.html]
 disabled = bug 1022281
 [test_browserElement_inproc_ExecuteScript.html]
 [test_browserElement_inproc_ExposableURI.html]
 [test_browserElement_inproc_Find.html]
 [test_browserElement_inproc_FirstPaint.html]
 [test_browserElement_inproc_ForwardName.html]
@@ -227,17 +227,17 @@ skip-if = (toolkit == 'gonk' && !debug)
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_Opensearch.html]
 [test_browserElement_inproc_PrivateBrowsing.html]
 [test_browserElement_inproc_PromptCheck.html]
 [test_browserElement_inproc_PromptConfirm.html]
 # Disabled on B2G Emulator because permission cannot be asserted in content process,
 # need to fix either bug 1094055 or bug 1020135.
 [test_browserElement_inproc_Proxy.html]
-skip-if = (toolkit == 'gonk')
+skip-if = toolkit == 'gonk' || buildapp == 'mulet'
 [test_browserElement_inproc_PurgeHistory.html]
 [test_browserElement_inproc_ReloadPostRequest.html]
 [test_browserElement_inproc_RemoveBrowserElement.html]
 [test_browserElement_inproc_ScrollEvent.html]
 [test_browserElement_inproc_SecurityChange.html]
 skip-if = toolkit == 'android' || (toolkit == 'gonk' && !debug) # android(TIMED_OUT, bug 766586) androidx86(TIMED_OUT, bug 766586)
 [test_browserElement_inproc_SendEvent.html]
 # The setInputMethodActive() tests will timed out on Android
--- a/dom/cache/test/mochitest/mochitest.ini
+++ b/dom/cache/test/mochitest/mochitest.ini
@@ -38,12 +38,12 @@ skip-if = e10s && debug && os == 'win'
 [test_cache_requestCache.html]
 [test_cache_delete.html]
 [test_cache_put_reorder.html]
 [test_cache_https.html]
   skip-if = buildapp == 'b2g' # bug 1162353
 [test_cache_restart.html]
 [test_cache_shrink.html]
 [test_cache_clear_on_app_uninstall.html]
-  skip-if = e10s || buildapp == 'b2g' # bug 1178685
+  skip-if = buildapp != 'mulet' || e10s # bug 1178685
 [test_cache_orphaned_cache.html]
 [test_cache_orphaned_body.html]
 [test_cache_untrusted.html]
--- a/dom/datastore/tests/mochitest.ini
+++ b/dom/datastore/tests/mochitest.ini
@@ -1,10 +1,10 @@
 [DEFAULT]
-skip-if = e10s
+skip-if = (buildapp != 'b2g' && buildapp != 'mulet')
 support-files =
   file_app_install.html
   file_readonly.html
   file_basic.html
   file_basic_worker.html
   file_basic_worker.js
   file_changes.html
   file_changes2.html
--- a/dom/indexedDB/test/mochitest.ini
+++ b/dom/indexedDB/test/mochitest.ini
@@ -356,19 +356,19 @@ skip-if = (buildapp == 'b2g' && toolkit 
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_transaction_ordering.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_unique_index_update.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') # Bug 931116
 [test_webapp_clearBrowserData_inproc_inproc.html]
 # The clearBrowserData tests are only supposed to run in the main process.
 # They currently time out on android.
-skip-if = buildapp == 'b2g' || e10s || toolkit == 'android'
+skip-if = buildapp != 'mulet'
 [test_webapp_clearBrowserData_inproc_oop.html]
 # The clearBrowserData tests are only supposed to run in the main process.
 # They currently time out on android.
-skip-if = buildapp == 'b2g' || e10s || toolkit == 'android'
+skip-if = buildapp != 'mulet'
 [test_webapp_clearBrowserData_oop_inproc.html]
 # The clearBrowserData tests are only supposed to run in the main process.
 # They currently time out on android.
-skip-if = buildapp == 'b2g' || e10s || toolkit == 'android'
+skip-if = buildapp != 'mulet'
 [test_serviceworker.html]
 skip-if = buildapp == 'b2g'
--- a/dom/ipc/tests/mochitest.ini
+++ b/dom/ipc/tests/mochitest.ini
@@ -17,43 +17,43 @@ skip-if = buildapp == 'b2g' || buildapp 
 skip-if = toolkit != 'gonk'
 [test_NuwaProcessDeadlock.html]
 skip-if = toolkit != 'gonk'
 [test_child_docshell.html]
 skip-if = toolkit == 'cocoa' # disabled due to hangs, see changeset 6852e7c47edf
 [test_CrashService_crash.html]
 skip-if = !(crashreporter && !e10s && (toolkit == 'gtk2' || toolkit == 'gtk3' || toolkit == 'cocoa' || toolkit == 'windows') && (buildapp != 'b2g' || toolkit == 'gonk') && (buildapp != 'mulet')) # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
 [test_permission_for_in_process_app.html]
-skip-if = e10s || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
+skip-if = e10s || (buildapp != 'b2g' && buildapp != 'mulet') || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 support-files =
   test_permission_helper.js
 [test_permission_for_oop_app.html]
-skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
+skip-if = (buildapp != 'b2g' && buildapp != 'mulet') || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 support-files =
   file_app.sjs
   file_app.template.webapp
   test_permission_helper.js
   test_permission_embed.html
   test_permission_framescript.js
 [test_permission_for_nested_oop_app.html]
-skip-if =  os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
+skip-if = (buildapp != 'b2g' && buildapp != 'mulet') || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 support-files =
   file_app.sjs
   file_app.template.webapp
   test_permission_helper.js
   test_permission_embed.html
   test_permission_framescript.js
 [test_permission_for_two_oop_apps.html]
-skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
+skip-if = (buildapp != 'b2g' && buildapp != 'mulet') || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 support-files =
   file_app.sjs
   file_app.template.webapp
   test_permission_helper.js
   test_permission_embed.html
   test_permission_framescript.js
 [test_permission_when_oop_app_crashes.html]
-skip-if = buildapp == 'mulet' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
+skip-if = buildapp != 'b2g' || os == "android" || toolkit == "gonk" # embed-apps doesn't work in mochitest app
 support-files =
   file_app.sjs
   file_app.template.webapp
   test_permission_helper.js
   test_permission_embed.html
   test_permission_framescript.js
--- a/dom/locales/en-US/chrome/layout/HtmlForm.properties
+++ b/dom/locales/en-US/chrome/layout/HtmlForm.properties
@@ -33,8 +33,12 @@ ColorPicker=Choose a color
 # LOCALIZATION NOTE (AndNMoreFiles): Semi-colon list of plural forms. 
 # See: http://developer.mozilla.org/en/docs/Localization_and_Plurals 
 # This string is shown at the end of the tooltip text for <input type='file'
 # multiple> when there are more than 21 files selected (when we will only list
 # the first 20, plus an "and X more" line). #1 represents the number of files
 # minus 20 and will always be a number equal to or greater than 2. So the
 # singular case will never be used.
 AndNMoreFiles=and one more;and #1 more
+# LOCALIZATION NOTE (DefaultSummary): this string is shown on a <details> when
+# it has no direct <summary> child. Google Chrome should already have this
+# string translated.
+DefaultSummary=Details
--- a/dom/media/ADTSDemuxer.cpp
+++ b/dom/media/ADTSDemuxer.cpp
@@ -14,18 +14,18 @@
 
 #ifdef PR_LOGGING
 mozilla::LazyLogModule gADTSDemuxerLog("ADTSDemuxer");
 #define ADTSLOG(msg, ...) \
   MOZ_LOG(gADTSDemuxerLog, LogLevel::Debug, ("ADTSDemuxer " msg, ##__VA_ARGS__))
 #define ADTSLOGV(msg, ...) \
   MOZ_LOG(gADTSDemuxerLog, LogLevel::Verbose, ("ADTSDemuxer " msg, ##__VA_ARGS__))
 #else
-#define ADTSLOG(msg, ...) do {} while (false)
-#define ADTSLOG(msg, ...) do {} while (false)
+#define ADTSLOG(msg, ...)  do {} while (false)
+#define ADTSLOGV(msg, ...) do {} while (false)
 #endif
 
 namespace mozilla {
 namespace adts {
 
 // adts::FrameHeader - Holds the ADTS frame header and its parsing
 // state.
 //
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -789,17 +789,17 @@ MediaFormatReader::UpdateReceivedNewData
     // We do not want to clear mWaitingForData or mDemuxEOS while
     // a drain is in progress in order to properly complete the operation.
     return false;
   }
 
   bool hasLastEnd;
   media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd);
   if (hasLastEnd) {
-    if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() > lastEnd) {
+    if (decoder.mLastTimeRangesEnd && decoder.mLastTimeRangesEnd.ref() < lastEnd) {
       // New data was added after our previous end, we can clear the EOS flag.
       decoder.mDemuxEOS = false;
     }
     decoder.mLastTimeRangesEnd = Some(lastEnd);
   }
 
   decoder.mReceivedNewData = false;
   if (decoder.mTimeThreshold) {
@@ -1109,18 +1109,20 @@ MediaFormatReader::Update(TrackType aTra
             !decoder.mNextStreamSourceID) {
           // We have completed draining the decoder following WaitingForData.
           // Set up the internal seek machinery to be able to resume from the
           // last sample decoded.
           LOG("Seeking to last sample time: %lld",
               decoder.mLastSampleTime.ref().ToMicroseconds());
           InternalSeek(aTrack, InternalSeekTarget(decoder.mLastSampleTime.ref(), true));
         }
-        LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack));
-        decoder.RejectPromise(WAITING_FOR_DATA, __func__);
+        if (!decoder.mReceivedNewData) {
+          LOG("Rejecting %s promise: WAITING_FOR_DATA", TrackTypeToStr(aTrack));
+          decoder.RejectPromise(WAITING_FOR_DATA, __func__);
+        }
       }
       // Now that draining has completed, we check if we have received
       // new data again as the result may now be different from the earlier
       // run.
       if (UpdateReceivedNewData(aTrack)) {
         LOGV("Nothing more to do");
         return;
       }
--- a/dom/media/gmp/GMPChild.cpp
+++ b/dom/media/gmp/GMPChild.cpp
@@ -123,28 +123,31 @@ GetPluginFile(const nsAString& aPluginPa
   nsAutoString binaryName =                            baseName + NS_LITERAL_STRING(".dll");
 #else
 #error not defined
 #endif
   aLibFile->AppendRelativePath(binaryName);
   return true;
 }
 
-#ifdef XP_WIN
 static bool
 GetInfoFile(const nsAString& aPluginPath,
             nsCOMPtr<nsIFile>& aInfoFile)
 {
   nsAutoString baseName;
+#if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
+  nsCOMPtr<nsIFile> unusedLibDir;
+  GetFileBase(aPluginPath, unusedLibDir, aInfoFile, baseName);
+#else
   GetFileBase(aPluginPath, aInfoFile, baseName);
+#endif
   nsAutoString infoFileName = baseName + NS_LITERAL_STRING(".info");
   aInfoFile->AppendRelativePath(infoFileName);
   return true;
 }
-#endif
 
 #if defined(XP_MACOSX) && defined(MOZ_GMP_SANDBOX)
 static bool
 GetPluginPaths(const nsAString& aPluginPath,
                nsCString &aPluginDirectoryPath,
                nsCString &aPluginFilePath)
 {
   nsCOMPtr<nsIFile> libDirectory, libFile;
@@ -261,16 +264,25 @@ GMPChild::Init(const nsAString& aPluginP
   }
 
 #ifdef MOZ_CRASHREPORTER
   SendPCrashReporterConstructor(CrashReporter::CurrentThreadId());
 #endif
 
   mPluginPath = aPluginPath;
   mSandboxVoucherPath = aVoucherPath;
+
+  nsCOMPtr<nsIFile> infoFile;
+  if (!GetInfoFile(mPluginPath, infoFile) || !infoFile) {
+    return false;
+  }
+  if (!mInfoParser.Init(infoFile)) {
+    return false;
+  }
+
   return true;
 }
 
 bool
 GMPChild::RecvSetNodeId(const nsCString& aNodeId)
 {
   LOGD("%s nodeId=%s", __FUNCTION__, aNodeId.Data());
 
@@ -285,114 +297,47 @@ GMPErr
 GMPChild::GetAPI(const char* aAPIName, void* aHostAPI, void** aPluginAPI)
 {
   if (!mGMPLoader) {
     return GMPGenericErr;
   }
   return mGMPLoader->GetAPI(aAPIName, aHostAPI, aPluginAPI);
 }
 
-static bool
-ReadIntoArray(nsIFile* aFile,
-              nsTArray<uint8_t>& aOutDst,
-              size_t aMaxLength)
-{
-  if (!FileExists(aFile)) {
-    return false;
-  }
-
-  PRFileDesc* fd = nullptr;
-  nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
-
-  int32_t length = PR_Seek(fd, 0, PR_SEEK_END);
-  PR_Seek(fd, 0, PR_SEEK_SET);
-
-  if (length < 0 || (size_t)length > aMaxLength) {
-    NS_WARNING("EME file is longer than maximum allowed length");
-    PR_Close(fd);
-    return false;
-  }
-  aOutDst.SetLength(length);
-  int32_t bytesRead = PR_Read(fd, aOutDst.Elements(), length);
-  PR_Close(fd);
-  return (bytesRead == length);
-}
-
 #ifdef XP_WIN
-static bool
-ReadIntoString(nsIFile* aFile,
-               nsCString& aOutDst,
-               size_t aMaxLength)
-{
-  nsTArray<uint8_t> buf;
-  bool rv = ReadIntoArray(aFile, buf, aMaxLength);
-  if (rv) {
-    buf.AppendElement(0); // Append null terminator, required by nsC*String.
-    aOutDst = nsDependentCString((const char*)buf.Elements(), buf.Length() - 1);
-  }
-  return rv;
-}
-
 // Pre-load DLLs that need to be used by the EME plugin but that can't be
 // loaded after the sandbox has started
 bool
-GMPChild::PreLoadLibraries(const nsAString& aPluginPath)
+GMPChild::PreLoadLibraries()
 {
-  // This must be in sorted order and lowercase!
+  // Items in this must be lowercase!
   static const char* whitelist[] = {
     "d3d9.dll", // Create an `IDirect3D9` to get adapter information
     "dxva2.dll", // Get monitor information
     "evr.dll", // MFGetStrideForBitmapInfoHeader
     "mfh264dec.dll", // H.264 decoder (on Windows Vista)
     "mfheaacdec.dll", // AAC decoder (on Windows Vista)
     "mfplat.dll", // MFCreateSample, MFCreateAlignedMemoryBuffer, MFCreateMediaType
     "msauddecmft.dll", // AAC decoder (on Windows 8)
     "msmpeg2adec.dll", // AAC decoder (on Windows 7)
     "msmpeg2vdec.dll", // H.264 decoder
   };
 
-  nsCOMPtr<nsIFile> infoFile;
-  GetInfoFile(aPluginPath, infoFile);
-
-  static const size_t MAX_GMP_INFO_FILE_LENGTH = 5 * 1024;
-  nsAutoCString info;
-  if (!ReadIntoString(infoFile, info, MAX_GMP_INFO_FILE_LENGTH)) {
-    NS_WARNING("Failed to read info file in GMP process.");
+  if (!mInfoParser.Contains(NS_LITERAL_CSTRING("libraries"))) {
     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).
-  nsTArray<nsCString> lines;
-  SplitAt("\r\n", info, lines);
-  for (nsCString line : lines) {
-    // Make lowercase.
-    std::transform(line.BeginWriting(),
-                   line.EndWriting(),
-                   line.BeginWriting(),
-                   tolower);
-
-    const char* libraries = "libraries:";
-    int32_t offset = line.Find(libraries, false, 0);
-    if (offset == kNotFound) {
-      continue;
-    }
-    // Line starts with "libraries:".
-    nsTArray<nsCString> libs;
-    SplitAt(",", Substring(line, offset + strlen(libraries)), libs);
-    for (nsCString lib : libs) {
-      lib.Trim(" ");
-      for (const char* whiteListedLib : whitelist) {
-        if (lib.EqualsASCII(whiteListedLib)) {
-          LoadLibraryA(lib.get());
-          break;
-        }
+  nsTArray<nsCString> libs;
+  SplitAt(", ", mInfoParser.Get(NS_LITERAL_CSTRING("libraries")), libs);
+  for (nsCString lib : libs) {
+    ToLowerCase(lib);
+    for (const char* whiteListedLib : whitelist) {
+      if (lib.EqualsASCII(whiteListedLib)) {
+        LoadLibraryA(lib.get());
+        break;
       }
     }
   }
 
   return true;
 }
 #endif
 
@@ -426,17 +371,17 @@ GMPChild::GetUTF8LibPath(nsACString& aOu
 }
 
 bool
 GMPChild::AnswerStartPlugin()
 {
   LOGD("%s", __FUNCTION__);
 
 #if defined(XP_WIN)
-  PreLoadLibraries(mPluginPath);
+  PreLoadLibraries();
 #endif
   if (!PreLoadPluginVoucher()) {
     NS_WARNING("Plugin voucher failed to load!");
     return false;
   }
   PreLoadSandboxVoucher();
 
   nsCString libPath;
--- a/dom/media/gmp/GMPChild.h
+++ b/dom/media/gmp/GMPChild.h
@@ -8,16 +8,17 @@
 
 #include "mozilla/gmp/PGMPChild.h"
 #include "GMPTimerChild.h"
 #include "GMPStorageChild.h"
 #include "GMPLoader.h"
 #include "gmp-async-shutdown.h"
 #include "gmp-entrypoints.h"
 #include "prlink.h"
+#include "GMPUtils.h"
 
 namespace mozilla {
 namespace gmp {
 
 class GMPContentChild;
 
 class GMPChild : public PGMPChild
                , public GMPAsyncShutdownHost
@@ -27,17 +28,17 @@ public:
   virtual ~GMPChild();
 
   bool Init(const nsAString& aPluginPath,
             const nsAString& aVoucherPath,
             base::ProcessId aParentPid,
             MessageLoop* aIOLoop,
             IPC::Channel* aChannel);
 #ifdef XP_WIN
-  bool PreLoadLibraries(const nsAString& aPluginPath);
+  bool PreLoadLibraries();
 #endif
   MessageLoop* GMPMessageLoop();
 
   // Main thread only.
   GMPTimerChild* GetGMPTimers();
   GMPStorageChild* GetGMPStorage();
 
   // GMPAsyncShutdownHost
@@ -88,14 +89,15 @@ private:
 
   MessageLoop* mGMPMessageLoop;
   nsString mPluginPath;
   nsString mSandboxVoucherPath;
   nsCString mNodeId;
   GMPLoader* mGMPLoader;
   nsTArray<uint8_t> mPluginVoucher;
   nsTArray<uint8_t> mSandboxVoucher;
+  GMPInfoFileParser mInfoParser;
 };
 
 } // namespace gmp
 } // namespace mozilla
 
 #endif // GMPChild_h_
--- a/dom/media/gmp/GMPUtils.cpp
+++ b/dom/media/gmp/GMPUtils.cpp
@@ -104,9 +104,104 @@ DirectoryEnumerator::Next()
         continue;
       }
     }
     return path.forget();
   }
   return nullptr;
 }
 
+bool
+ReadIntoArray(nsIFile* aFile,
+              nsTArray<uint8_t>& aOutDst,
+              size_t aMaxLength)
+{
+  if (!FileExists(aFile)) {
+    return false;
+  }
+
+  PRFileDesc* fd = nullptr;
+  nsresult rv = aFile->OpenNSPRFileDesc(PR_RDONLY, 0, &fd);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+
+  int32_t length = PR_Seek(fd, 0, PR_SEEK_END);
+  PR_Seek(fd, 0, PR_SEEK_SET);
+
+  if (length < 0 || (size_t)length > aMaxLength) {
+    NS_WARNING("EME file is longer than maximum allowed length");
+    PR_Close(fd);
+    return false;
+  }
+  aOutDst.SetLength(length);
+  int32_t bytesRead = PR_Read(fd, aOutDst.Elements(), length);
+  PR_Close(fd);
+  return (bytesRead == length);
+}
+
+static bool
+ReadIntoString(nsIFile* aFile,
+               nsCString& aOutDst,
+               size_t aMaxLength)
+{
+  nsTArray<uint8_t> buf;
+  bool rv = ReadIntoArray(aFile, buf, aMaxLength);
+  if (rv) {
+    buf.AppendElement(0); // Append null terminator, required by nsC*String.
+    aOutDst = nsDependentCString((const char*)buf.Elements(), buf.Length() - 1);
+  }
+  return rv;
+}
+
+bool
+GMPInfoFileParser::Init(nsIFile* aInfoFile)
+{
+  nsTArray<nsCString> lines;
+  static const size_t MAX_GMP_INFO_FILE_LENGTH = 5 * 1024;
+
+  nsAutoCString info;
+  if (!ReadIntoString(aInfoFile, info, MAX_GMP_INFO_FILE_LENGTH)) {
+    NS_WARNING("Failed to read info file in GMP process.");
+    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) {
+      continue;
+    }
+    nsCString key = tokens[0];
+    ToLowerCase(key);
+    key.Trim(" ");
+    nsCString* value = new nsCString(tokens[1]);
+    value->Trim(" ");
+    mValues.Put(key, value); // Hashtable assumes ownership of value.
+  }
+
+  return true;
+}
+
+bool
+GMPInfoFileParser::Contains(const nsCString& aKey) const {
+  nsCString key(aKey);
+  ToLowerCase(key);
+  return mValues.Contains(key);
+}
+
+nsCString
+GMPInfoFileParser::Get(const nsCString& aKey) const {
+  MOZ_ASSERT(Contains(aKey));
+  nsCString key(aKey);
+  ToLowerCase(key);
+  nsCString* p = nullptr;
+  if (mValues.Get(key, &p)) {
+    return nsCString(*p);
+  }
+  return EmptyCString();
+}
+
 } // namespace mozilla
--- a/dom/media/gmp/GMPUtils.h
+++ b/dom/media/gmp/GMPUtils.h
@@ -4,16 +4,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GMPUtils_h_
 #define GMPUtils_h_
 
 #include "mozilla/UniquePtr.h"
 #include "nsTArray.h"
 #include "nsCOMPtr.h"
+#include "nsClassHashtable.h"
 
 class nsIFile;
 class nsCString;
 class nsISimpleEnumerator;
 
 namespace mozilla {
 
 template<typename T>
@@ -55,11 +56,25 @@ public:
 
   already_AddRefed<nsIFile> Next();
 
 private:
   Mode mMode;
   nsCOMPtr<nsISimpleEnumerator> mIter;
 };
 
+class GMPInfoFileParser {
+public:
+  bool Init(nsIFile* aFile);
+  bool Contains(const nsCString& aKey) const;
+  nsCString Get(const nsCString& aKey) const;
+private:
+  nsClassHashtable<nsCStringHashKey, nsCString> mValues;
+};
+
+bool
+ReadIntoArray(nsIFile* aFile,
+              nsTArray<uint8_t>& aOutDst,
+              size_t aMaxLength);
+
 } // namespace mozilla
 
 #endif
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp
+++ b/dom/media/mediasource/MediaSourceDemuxer.cpp
@@ -7,16 +7,17 @@
 #include <algorithm>
 #include <limits>
 #include <stdint.h>
 
 #include "MediaSourceDemuxer.h"
 #include "MediaSourceUtils.h"
 #include "SourceBufferList.h"
 #include "nsPrintfCString.h"
+#include "OpusDecoder.h"
 
 namespace mozilla {
 
 typedef TrackInfo::TrackType TrackType;
 using media::TimeUnit;
 using media::TimeIntervals;
 
 MediaSourceDemuxer::MediaSourceDemuxer()
@@ -291,16 +292,20 @@ MediaSourceDemuxer::GetMozDebugReaderDat
 MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent,
                                                  TrackInfo::TrackType aType,
                                                  TrackBuffersManager* aManager)
   : mParent(aParent)
   , mManager(aManager)
   , mType(aType)
   , mMonitor("MediaSourceTrackDemuxer")
   , mReset(true)
+  , mPreRoll(
+      TimeUnit::FromMicroseconds(
+        OpusDataDecoder::IsOpus(mParent->GetTrackInfo(mType)->mMimeType)
+          ? 80000 : 0))
 {
 }
 
 UniquePtr<TrackInfo>
 MediaSourceTrackDemuxer::GetInfo() const
 {
   return mParent->GetTrackInfo(mType)->Clone();
 }
@@ -374,24 +379,34 @@ MediaSourceTrackDemuxer::BreakCycles()
   mParent->GetTaskQueue()->Dispatch(task.forget());
 }
 
 RefPtr<MediaSourceTrackDemuxer::SeekPromise>
 MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime)
 {
   TimeIntervals buffered = mManager->Buffered(mType);
   buffered.SetFuzz(MediaSourceDemuxer::EOS_FUZZ);
+  TimeUnit seekTime = std::max(aTime - mPreRoll, TimeUnit::FromMicroseconds(0));
 
-  if (!buffered.Contains(aTime)) {
-    // We don't have the data to seek to.
-    return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
-                                        __func__);
+  if (!buffered.Contains(seekTime)) {
+    if (!buffered.Contains(aTime)) {
+      // We don't have the data to seek to.
+      return SeekPromise::CreateAndReject(DemuxerFailureReason::WAITING_FOR_DATA,
+                                          __func__);
+    }
+    // Theorically we should reject the promise with WAITING_FOR_DATA,
+    // however, to avoid unwanted regressions we assume that if at this time
+    // we don't have the wanted data it won't come later.
+    // Instead of using the pre-rolled time, use the earliest time available in
+    // the interval.
+    TimeIntervals::IndexType index = buffered.Find(aTime);
+    MOZ_ASSERT(index != TimeIntervals::NoIndex);
+    seekTime = buffered[index].mStart;
   }
-  TimeUnit seekTime =
-    mManager->Seek(mType, aTime, MediaSourceDemuxer::EOS_FUZZ);
+  seekTime = mManager->Seek(mType, seekTime, MediaSourceDemuxer::EOS_FUZZ);
   bool error;
   RefPtr<MediaRawData> sample =
     mManager->GetSample(mType,
                         media::TimeUnit(),
                         error);
   MOZ_ASSERT(!error && sample);
   mNextSample = Some(sample);
   mReset = false;
--- a/dom/media/mediasource/MediaSourceDemuxer.h
+++ b/dom/media/mediasource/MediaSourceDemuxer.h
@@ -128,13 +128,17 @@ private:
   TrackInfo::TrackType mType;
   // Monitor protecting members below accessed from multiple threads.
   Monitor mMonitor;
   media::TimeUnit mNextRandomAccessPoint;
   Maybe<RefPtr<MediaRawData>> mNextSample;
   // Set to true following a reset. Ensure that the next sample demuxed
   // is available at position 0.
   bool mReset;
+
+  // Amount of pre-roll time when seeking.
+  // Set to 80ms if track is Opus.
+  const media::TimeUnit mPreRoll;
 };
 
 } // namespace mozilla
 
 #endif
--- a/dom/media/test/crashtests/crashtests.list
+++ b/dom/media/test/crashtests/crashtests.list
@@ -73,17 +73,17 @@ load 1015662.html
 load 1020205.html
 skip-if(Android||B2G) test-pref(media.navigator.permission.disabled,true) load 1028458.html # bug 1048863
 load 1041466.html
 load 1045650.html
 load 1080986.html
 load 1122218.html
 load 1127188.html
 load 1157994.html
-load 1158427.html
+skip-if(!B2G) load 1158427.html
 load 1185176.html
 load 1185192.html
 load 1228484.html
 load analyser-channels-1.html
 load audiocontext-double-suspend.html
 load buffer-source-duration-1.html
 load buffer-source-ended-1.html
 load buffer-source-resampling-start-1.html
--- a/dom/messages/test/chrome.ini
+++ b/dom/messages/test/chrome.ini
@@ -1,11 +1,13 @@
 [DEFAULT]
-skip-if = (buildapp != "browser") || e10s
+skip-if = e10s
 support-files =
   file_hasPendingMessage.html
   invalid_manifest.webapp
   invalid_manifest.webapp^headers^
   manifest.webapp
   manifest.webapp^headers^
 
 [test_hasPendingMessage.html]
+skip-if = buildapp != "browser"
 [test_sysmsg_registration.html]
+skip-if = buildapp != 'mulet'
--- a/dom/permission/tests/mochitest.ini
+++ b/dom/permission/tests/mochitest.ini
@@ -14,16 +14,17 @@ skip-if = buildapp == 'b2g' || toolkit =
 [test_permissions.html]
 [test_permissions_api.html]
 [test_power.html]
 [test_presentation-device-manage.html]
 [test_systemXHR.html]
 [test_tcp-socket.html]
 [test_udp-socket.html]
 [test_webapps-manage.html]
+skip-if = buildapp != 'b2g' && buildapp != 'mulet'
 [test_camera.html]
 disabled = disabled until bug 859593 is fixed
 [test_keyboard.html]
 skip-if = toolkit == 'android'
 [test_input-manage.html]
 skip-if = toolkit == 'android'
 [test_wifi-manage.html]
 skip-if = (buildapp != 'b2g') || (buildapp == 'b2g' && toolkit != 'gonk') #b2g-desktop(Bug 931116, b2g desktop specific, initial triage)
--- a/dom/requestsync/tests/mochitest.ini
+++ b/dom/requestsync/tests/mochitest.ini
@@ -9,17 +9,17 @@ support-files =
 
 [test_webidl.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_minInterval.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_basic.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_basic_app.html]
-skip-if = os == "android" || buildapp == 'b2g' || e10s # mozapps
+skip-if = buildapp != 'mulet' || e10s # mozapps
 [test_wakeUp.html]
 run-if = buildapp == 'b2g' && toolkit == 'gonk'
 [test_runNow.html]
 run-if = buildapp == 'b2g' && toolkit == 'gonk'
 [test_promise.html]
 skip-if = os == "android" || toolkit == "gonk"
 [test_bug1151082.html]
 skip-if = os == "android" || toolkit == "gonk"
--- a/dom/security/test/csp/mochitest.ini
+++ b/dom/security/test/csp/mochitest.ini
@@ -182,16 +182,17 @@ skip-if = (buildapp == 'b2g' && (toolkit
 skip-if = buildapp == 'b2g' # http-on-opening-request observers are not available in child processes
 [test_hash_source.html]
 skip-if = buildapp == 'b2g' # can't compute hashes in child process (bug 958702)
 [test_scheme_relative_sources.html]
 skip-if = buildapp == 'b2g' #no ssl support
 [test_ignore_unsafe_inline.html]
 [test_self_none_as_hostname_confusion.html]
 [test_bug949549.html]
+skip-if = buildapp != 'b2g' && buildapp != 'mulet'
 [test_path_matching.html]
 [test_path_matching_redirect.html]
 [test_report_uri_missing_in_report_only_header.html]
 [test_report.html]
 [test_301_redirect.html]
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
 [test_302_redirect.html]
 skip-if = buildapp == 'b2g' # intermittent orange (bug 1028490)
--- a/dom/security/test/mixedcontentblocker/mochitest.ini
+++ b/dom/security/test/mixedcontentblocker/mochitest.ini
@@ -9,13 +9,13 @@ support-files =
   file_frameNavigation_secure.html
   file_frameNavigation_secure_grandchild.html
   file_main.html
   file_main_bug803225.html
   file_main_bug803225_websocket_wsh.py
   file_server.sjs
 
 [test_main.html]
-skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #TIMED_OUT, SSL_REQUIRED # Bug 1141029 Mulet parity with B2G Desktop for TC
+skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT, SSL_REQUIRED # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_bug803225.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' || e10s #TIMED_OUT, SSL_REQUIRED # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_frameNavigation.html]
 skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT, SSL_REQUIRED # Bug 1141029 Mulet parity with B2G Desktop for TC
--- a/dom/security/test/mixedcontentblocker/test_bug803225.html
+++ b/dom/security/test/mixedcontentblocker/test_bug803225.html
@@ -1,88 +1,84 @@
 <!DOCTYPE HTML>
 <html>
 <!--
-Testing Whitelist of Resource Schemed for Mixed Content Blocker
+Testing Whitelist of Resource Scheme for Mixed Content Blocker
 https://bugzilla.mozilla.org/show_bug.cgi?id=803225
 -->
 <head>
   <meta charset="utf-8">
   <title>Tests for Bug 803225</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script>
-
-  var origBlockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
-  var origBlockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
-
   var counter = 0;
   var settings = [ [true, true], [true, false], [false, true], [false, false] ];
 
   var blockActive;
   var blockDisplay;
 
   //Cycle through 4 different preference settings.
-  function changePrefs(x) {
-    SpecialPowers.setBoolPref("security.mixed_content.block_display_content", settings[x][0]);
-    SpecialPowers.setBoolPref("security.mixed_content.block_active_content", settings[x][1]);
-    blockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
-    blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+  function changePrefs(callback) {
+    let newPrefs = [["security.mixed_content.block_display_content", settings[counter][0]],
+                    ["security.mixed_content.block_active_content", settings[counter][1]]];
+
+    SpecialPowers.pushPrefEnv({"set": newPrefs}, function () {
+      blockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
+      blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+      counter++;
+      callback();
+    });
   }
 
-  //Set the first set of settings (true, true) and increment the counter.
-  changePrefs(counter);
-  counter++;
-
   var testsToRun = {
     /* https - Tests already run as part of bug 62178. */
     about: false,
     mozicon: false,
     resource: false,
     unsafe_about: false,
     data_protocol: false,
     javascript: false,
     mailto: false,
     wss: false,
   };
 
   function log(msg) {
     document.getElementById("log").textContent += "\n" + msg;
   }
 
+  function reloadFrame() {
+    document.getElementById('framediv').innerHTML = '<iframe id="testHarness" src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_main_bug803225.html"></iframe>';
+  }
+
   function checkTestsCompleted() {
     for (var prop in testsToRun) {
       // some test hasn't run yet so we're not done
       if (!testsToRun[prop])
         return;
     }
     //if the testsToRun are all completed, change the pref and run the tests again until we have cycled through all the prefs.
     if(counter < 4) {
        for (var prop in testsToRun) {
          testsToRun[prop] = false;
        }
       //call to change the preferences
-      changePrefs(counter);
-      counter++;
-      log("\nblockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
-      document.getElementById('framediv').innerHTML = '<iframe id="testHarness" src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_main_bug803225.html"></iframe>';
+      changePrefs(function() {
+        log("\nblockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
+        reloadFrame();
+      });
     }
     else {
-      //set the prefs back to what they were set to originally
-      SpecialPowers.setBoolPref("security.mixed_content.block_display_content", origBlockDisplay);
-      SpecialPowers.setBoolPref("security.mixed_content.block_active_content", origBlockActive);
       SimpleTest.finish();
     }
   }
 
   var firstTest = true;
 
-  // listen for a messages from the mixed content test harness
-  window.addEventListener("message", receiveMessage, false);
   function receiveMessage(event) {
     if(firstTest) {
       log("blockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
       firstTest = false;
     }
 
     log("test: "+event.data.test+", msg: "+event.data.msg + " logging message.");
     // test that the load type matches the pref for this type of content
@@ -130,19 +126,27 @@ https://bugzilla.mozilla.org/show_bug.cg
       case "mailto":
         ok(event.data.msg == "resource with mailto protocol loaded", "resource with mailto protocol did not load");
         testsToRun["mailto"] = true;
         break;
     }
     checkTestsCompleted();
   }
 
+  function startTest() {
+    //Set the first set of settings (true, true) and increment the counter.
+    changePrefs(function() {
+      // listen for a messages from the mixed content test harness
+      window.addEventListener("message", receiveMessage, false);
+
+      reloadFrame();
+    });
+  }
+
   SimpleTest.waitForExplicitFinish();
   </script>
 </head>
 
-<body>
-  <div id="framediv">
-    <iframe id="testHarness" src="https://example.com/tests/dom/security/test/mixedcontentblocker/file_main_bug803225.html"></iframe>
-  </div>
+<body onload='startTest()'>
+  <div id="framediv"></div>
   <pre id="log"></pre>
 </body>
 </html>
--- a/dom/security/test/mixedcontentblocker/test_main.html
+++ b/dom/security/test/mixedcontentblocker/test_main.html
@@ -8,37 +8,36 @@ https://bugzilla.mozilla.org/show_bug.cg
   <meta charset="utf-8">
   <title>Tests for Bug 62178</title>
   <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
 
   <script>
   SpecialPowers.setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
 
-  var origBlockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
-  var origBlockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
-
   var counter = 0;
   var settings = [ [true, true], [true, false], [false, true], [false, false] ];
 
   var blockActive;
   var blockDisplay;
 
   //Cycle through 4 different preference settings.
-  function changePrefs(x) {
-    SpecialPowers.setBoolPref("security.mixed_content.block_display_content", settings[x][0]);
-    SpecialPowers.setBoolPref("security.mixed_content.block_active_content", settings[x][1]);
-    blockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
-    blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+  function changePrefs(otherPrefs, callback) {
+    let basePrefs = [["security.mixed_content.block_display_content", settings[counter][0]],
+                     ["security.mixed_content.block_active_content", settings[counter][1]]];
+    let newPrefs = basePrefs.concat(otherPrefs);
+
+    SpecialPowers.pushPrefEnv({"set": newPrefs}, function () {
+      blockDisplay = SpecialPowers.getBoolPref("security.mixed_content.block_display_content");
+      blockActive = SpecialPowers.getBoolPref("security.mixed_content.block_active_content");
+      counter++;
+      callback();
+    });
   }
 
-  //Set the first set of settings (true, true) and increment the counter.
-  changePrefs(counter);
-  counter++;
-
   var testsToRun = {
     iframe: false,
     image: false,
     imageSrcset: false,
     imageSrcsetFallback: false,
     imagePicture: false,
     imageJoinPicture: false,
     imageLeavePicture: false,
@@ -64,33 +63,28 @@ https://bugzilla.mozilla.org/show_bug.cg
         return;
     }
     //if the testsToRun are all completed, chnage the pref and run the tests again until we have cycled through all the prefs.
     if(counter < 4) {
        for (var prop in testsToRun) {
          testsToRun[prop] = false;
        }
       //call to change the preferences
-      changePrefs(counter);
-      counter++;
-      log("\nblockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
-      reloadFrame();
+      changePrefs([], function() {
+        log("\nblockDisplay set to "+blockDisplay+", blockActive set to "+blockActive+".");
+        reloadFrame();
+      });
     }
     else {
-      //set the prefs back to what they were set to originally
-      SpecialPowers.setBoolPref("security.mixed_content.block_display_content", origBlockDisplay);
-      SpecialPowers.setBoolPref("security.mixed_content.block_active_content", origBlockActive);
       SimpleTest.finish();
     }
   }
 
   var firstTest = true;
 
-  // listen for a messages from the mixed content test harness
-  window.addEventListener("message", receiveMessage, false);
   function receiveMessage(event) {
     if(firstTest) {
       log("blockActive set to "+blockActive+", blockDisplay set to "+blockDisplay+".");
       firstTest = false;
     }
 
     log("test: "+event.data.test+", msg: "+event.data.msg + " logging message.");
     // test that the load type matches the pref for this type of content
@@ -162,26 +156,32 @@ https://bugzilla.mozilla.org/show_bug.cg
         ok(blockDisplay == (event.data.msg == "insecure image blocked"), "imageLeavePicture did not follow block_display_content pref");
         testsToRun["imageLeavePicture"] = true;
         break;
 
     }
     checkTestsCompleted();
   }
 
-  // Enable <picture> and <img srcset> for test
-  SpecialPowers.pushPrefEnv({'set': [[ "dom.image.srcset.enabled", true ],
-                                     [ "dom.image.picture.enabled", true ]] },
-                            function() {
-    // Kick off test
-    reloadFrame();
-  });
+  function startTest() {
+    //Set the first set of mixed content settings and increment the counter.
+    //Enable <picture> and <img srcset> for the test.
+    changePrefs([[ "dom.image.srcset.enabled", true ], [ "dom.image.picture.enabled", true ]],
+      function() {
+        //listen for a messages from the mixed content test harness
+        window.addEventListener("message", receiveMessage, false);
+
+        //Kick off test
+        reloadFrame();
+      }
+    );
+  }
 
   SimpleTest.waitForExplicitFinish();
 
   </script>
 </head>
 
-<body>
+<body onload='startTest()'>
   <div id="framediv"></div>
   <pre id="log"></pre>
 </body>
 </html>
--- a/dom/tests/mochitest/fetch/mochitest.ini
+++ b/dom/tests/mochitest/fetch/mochitest.ini
@@ -20,17 +20,17 @@ support-files =
   empty.js^headers^
 
 [test_headers.html]
 [test_headers_sw_reroute.html]
 skip-if = buildapp == 'b2g' # Bug 1137683
 [test_headers_mainthread.html]
 skip-if = (e10s && debug && os == 'win')
 [test_fetch_app_protocol.html]
-skip-if = (e10s && debug && os == 'win')
+skip-if = (buildapp != 'b2g' && buildapp != 'mulet')
 [test_fetch_basic.html]
 skip-if = (e10s && debug && os == 'win')
 [test_fetch_basic_sw_reroute.html]
 skip-if = buildapp == 'b2g' || (e10s && debug && os == 'win') # Bug 1137683
 [test_fetch_basic_sw_empty_reroute.html]
 skip-if = buildapp == 'b2g'
 [test_fetch_basic_http.html]
 skip-if = (e10s && debug && os == 'win')
--- a/dom/tests/mochitest/localstorage/chrome.ini
+++ b/dom/tests/mochitest/localstorage/chrome.ini
@@ -1,12 +1,14 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' || os == 'android'
 support-files =
   frame_clear_browser_data.html
   page_blank.html
 
 [test_app_uninstall.html]
+skip-if = buildapp != 'mulet'
 [test_clear_browser_data.html]
+skip-if = buildapp != 'mulet'
 [test_localStorageBasePrivateBrowsing_perwindowpb.html]
 skip-if = true # bug 1156725
 [test_localStorageFromChrome.xhtml]
 [test_localStorageQuotaPrivateBrowsing_perwindowpb.html]
--- a/dom/tests/mochitest/localstorage/test_app_uninstall.html
+++ b/dom/tests/mochitest/localstorage/test_app_uninstall.html
@@ -147,17 +147,17 @@ function browserLoadEvent() {
   gBrowserStorage.localStorage = window.frames[1].frames[0].localStorage;
   gBrowserStorage.sessionStorage = window.frames[1].frames[0].sessionStorage;
 
   setupStorage(gAppStorage.localStorage);
   setupStorage(gAppStorage.sessionStorage);
   setupStorage(gBrowserStorage.localStorage);
   setupStorage(gBrowserStorage.sessionStorage);
 
-  navigator.mozApps.mgmt.getNotInstalled().onsuccess = function() {
+  navigator.mozApps.mgmt.getAll().onsuccess = function() {
     for (i in this.result) {
       var app = this.result[i];
       if (app.manifestURL == gManifestURL) {
         navigator.mozApps.mgmt.uninstall(app).onsuccess = function() {
           /*
            * Now that the app is uninstalled, we should not find any more
            * localStorage data from the app or its browsers. However,
            * sessionStorage is expected to stay.
--- a/dom/tests/mochitest/notification/mochitest.ini
+++ b/dom/tests/mochitest/notification/mochitest.ini
@@ -3,11 +3,11 @@
 support-files =
   MockServices.js
   NotificationTest.js
 
 [test_notification_basics.html]
 [test_notification_storage.html]
 [test_bug931307.html]
 [test_notification_resend.html]
-skip-if = e10s # On e10s, faking the app seems to be failing
+skip-if = (buildapp != 'b2g' && buildapp != 'mulet') || e10s # On e10s, faking the app seems to be failing
 [test_notification_noresend.html]
 skip-if = (toolkit == 'gonk') # Mochitest on Gonk registers an app manifest that messes with the logic
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -24,17 +24,16 @@ WEBIDL_FILES = [
     'Animation.webidl',
     'AnimationEffectReadOnly.webidl',
     'AnimationEffectTimingReadOnly.webidl',
     'AnimationEvent.webidl',
     'AnimationTimeline.webidl',
     'AnonymousContent.webidl',
     'AppInfo.webidl',
     'AppNotificationServiceOptions.webidl',
-    'Apps.webidl',
     'APZTestData.webidl',
     'ArchiveReader.webidl',
     'ArchiveRequest.webidl',
     'Attr.webidl',
     'AudioBuffer.webidl',
     'AudioBufferSourceNode.webidl',
     'AudioChannel.webidl',
     'AudioContext.webidl',
@@ -668,17 +667,16 @@ if CONFIG['MOZ_GAMEPAD']:
 
 WEBIDL_FILES += [
     'CloseEvent.webidl',
     'CustomEvent.webidl',
     'DeviceOrientationEvent.webidl',
     'DeviceStorageChangeEvent.webidl',
     'DOMTransactionEvent.webidl',
     'HashChangeEvent.webidl',
-    'MozApplicationEvent.webidl',
     'MozSettingsEvent.webidl',
     'PageTransitionEvent.webidl',
     'PopStateEvent.webidl',
     'PopupBlockedEvent.webidl',
     'ProgressEvent.webidl',
     'RecordErrorEvent.webidl',
     'StyleRuleChangeEvent.webidl',
     'StyleSheetApplicableStateChangeEvent.webidl',
@@ -791,17 +789,16 @@ GENERATED_EVENTS_WEBIDL_FILES = [
     'DownloadEvent.webidl',
     'ErrorEvent.webidl',
     'ExternalAppEvent.webidl',
     'FontFaceSetLoadEvent.webidl',
     'HashChangeEvent.webidl',
     'IccChangeEvent.webidl',
     'ImageCaptureErrorEvent.webidl',
     'MediaStreamEvent.webidl',
-    'MozApplicationEvent.webidl',
     'MozCellBroadcastEvent.webidl',
     'MozClirModeEvent.webidl',
     'MozContactChangeEvent.webidl',
     'MozEmergencyCbModeEvent.webidl',
     'MozInterAppMessageEvent.webidl',
     'MozMessageDeletedEvent.webidl',
     'MozMmsEvent.webidl',
     'MozOtaStatusEvent.webidl',
@@ -915,10 +912,15 @@ if CONFIG['MOZ_EME']:
 
 if CONFIG['MOZ_PAY']:
     WEBIDL_FILES += [
         'MozPaymentProvider.webidl'
     ]
 
 if CONFIG['MOZ_B2G']:
     WEBIDL_FILES += [
-        'Identity.webidl'
+        'Apps.webidl',
+        'Identity.webidl',
+        'MozApplicationEvent.webidl'
     ]
+    GENERATED_EVENTS_WEBIDL_FILES += [
+        'MozApplicationEvent.webidl'
+    ]
--- a/dom/xslt/xslt/txInstructions.cpp
+++ b/dom/xslt/xslt/txInstructions.cpp
@@ -93,16 +93,19 @@ txAttribute::txAttribute(nsAutoPtr<Expr>
                          txNamespaceMap* aMappings)
     : mName(Move(aName)), mNamespace(Move(aNamespace)), mMappings(aMappings)
 {
 }
 
 nsresult
 txAttribute::execute(txExecutionState& aEs)
 {
+    nsAutoPtr<txTextHandler> handler(
+        static_cast<txTextHandler*>(aEs.popResultHandler()));
+
     nsAutoString name;
     nsresult rv = mName->evaluateToString(aEs.getEvalContext(), name);
     NS_ENSURE_SUCCESS(rv, rv);
 
     const char16_t* colon;
     if (!XMLUtils::isValidQName(name, &colon) ||
         TX_StringEqualsAtom(name, nsGkAtoms::xmlns)) {
         return NS_OK;
@@ -125,19 +128,16 @@ txAttribute::execute(txExecutionState& a
         if (!nspace.IsEmpty()) {
             nsId = txNamespaceManager::getNamespaceID(nspace);
         }
     }
     else if (colon) {
         nsId = mMappings->lookupNamespace(prefix);
     }
 
-    nsAutoPtr<txTextHandler> handler(
-        static_cast<txTextHandler*>(aEs.popResultHandler()));
-
     // add attribute if everything was ok
     return nsId != kNameSpaceID_Unknown ?
            aEs.mResultHandler->attribute(prefix, Substring(name, lnameStart),
                                          nsId, handler->mValue) :
            NS_OK;
 }
 
 txCallTemplate::txCallTemplate(const txExpandedName& aName)
--- a/embedding/components/printingui/ipc/PrintDataUtils.cpp
+++ b/embedding/components/printingui/ipc/PrintDataUtils.cpp
@@ -17,17 +17,17 @@ namespace embedding {
 /**
  * MockWebBrowserPrint is a mostly useless implementation of nsIWebBrowserPrint,
  * but wraps a PrintData so that it's able to return information to print
  * settings dialogs that need an nsIWebBrowserPrint to interrogate.
  */
 
 NS_IMPL_ISUPPORTS(MockWebBrowserPrint, nsIWebBrowserPrint);
 
-MockWebBrowserPrint::MockWebBrowserPrint(PrintData aData)
+MockWebBrowserPrint::MockWebBrowserPrint(const PrintData &aData)
   : mData(aData)
 {
   MOZ_COUNT_CTOR(MockWebBrowserPrint);
 }
 
 MockWebBrowserPrint::~MockWebBrowserPrint()
 {
   MOZ_COUNT_DTOR(MockWebBrowserPrint);
--- a/embedding/components/printingui/ipc/PrintDataUtils.h
+++ b/embedding/components/printingui/ipc/PrintDataUtils.h
@@ -18,17 +18,17 @@
  */
 
 namespace mozilla {
 namespace embedding {
 
 class MockWebBrowserPrint final : public nsIWebBrowserPrint
 {
 public:
-  explicit MockWebBrowserPrint(PrintData aData);
+  explicit MockWebBrowserPrint(const PrintData &aData);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIWEBBROWSERPRINT
 
 private:
   ~MockWebBrowserPrint();
   PrintData mData;
 };
--- a/extensions/cookie/test/chrome.ini
+++ b/extensions/cookie/test/chrome.ini
@@ -1,7 +1,10 @@
 [DEFAULT]
 skip-if = buildapp == 'b2g' || os == 'android'
 support-files = channel_utils.js
 
 [test_app_uninstall_cookies.html]
+skip-if = buildapp != 'mulet'
 [test_app_uninstall_permissions.html]
+skip-if = buildapp != 'mulet'
+
 [test_permissionmanager_app_isolation.html]
--- a/extensions/cookie/test/test_app_uninstall_cookies.html
+++ b/extensions/cookie/test/test_app_uninstall_cookies.html
@@ -146,18 +146,17 @@ function onInstall() {
 }
 
 function checkCookie() {
   var appCookiesCount = getCookiesCountForApp(gTestAppId);
   is(appCookiesCount, 2, "App should have two cookies");
 
   gCurrentCookiesCount = getCookiesCount() - appCookiesCount;
 
-  // Not installed means not installed as native app.
-  navigator.mozApps.mgmt.getNotInstalled().onsuccess = function() {
+  navigator.mozApps.mgmt.getAll().onsuccess = function() {
     for (i in this.result) {
       var app = this.result[i];
       if (app.manifestURL == gManifestURL) {
         navigator.mozApps.mgmt.uninstall(app).onsuccess = function() {
           is(getCookiesCountForApp(gTestAppId), 0, "App should have no cookies");
 
           is(getCookiesCount(), gCurrentCookiesCount,
              "Number of cookies should not have changed");
--- a/extensions/cookie/test/test_app_uninstall_permissions.html
+++ b/extensions/cookie/test/test_app_uninstall_permissions.html
@@ -55,22 +55,27 @@ permManager.addFromPrincipal(window.docu
 
 SimpleTest.registerCleanupFunction(() =>
   permManager.removeFromPrincipal(window.document.nodePrincipal, "webapps-manage",
                                   Ci.nsIPermissionManager.ALLOW_ACTION)
 );
 
 var gManifestURL = "http://www.example.com/chrome/dom/apps/tests/apps/basic.webapp";
 
+// Get the permission count before installing the app so we can compare it to
+// the permission count after uninstalling the app.
+var currentPermissionCount = getPermissionCountForApp(-1);
+
 function onInstall() {
   var testAppId = appsService.getAppLocalIdByManifestURL(gManifestURL);
 
-  is(getPermissionCountForApp(testAppId), 0, "App should have no permission");
-
-  var currentPermissionCount = getPermissionCountForApp(-1);
+  // PermissionsManager.installPermissions gets run during the install process,
+  // and it adds the "indexedDB" permission by default, so the app should have
+  // that permission.
+  is(getPermissionCountForApp(testAppId), 1, "App should have 1 permission");
 
   var attrs = {appId: testAppId};
   var principal = secMan.createCodebasePrincipal(ioService.newURI("http://www.example.com", null, null),
                                                  attrs);
 
   permManager.addFromPrincipal(principal, "foobar", Ci.nsIPermissionManager.ALLOW_ACTION);
   permManager.addFromPrincipal(principal, "foo", Ci.nsIPermissionManager.DENY_ACTION);
   permManager.addFromPrincipal(principal, "bar", Ci.nsIPermissionManager.ALLOW_ACTION, Ci.nsIPermissionManager.EXPIRE_SESSION, 0);
@@ -80,20 +85,19 @@ function onInstall() {
                                              attrs);
   permManager.addFromPrincipal(principal, "foobar", Ci.nsIPermissionManager.ALLOW_ACTION);
 
   attrs = {appId: testAppId};
   principal = secMan.createCodebasePrincipal(ioService.newURI("http://www.example.org", null, null),
                                              attrs);
   permManager.addFromPrincipal(principal, "foobar", Ci.nsIPermissionManager.ALLOW_ACTION);
 
-  is(getPermissionCountForApp(testAppId), 5, "App should have 5 permissions");
+  is(getPermissionCountForApp(testAppId), 6, "App should have 6 permissions");
 
-  // Not installed means not installed as native app.
-  navigator.mozApps.mgmt.getNotInstalled().onsuccess = function() {
+  navigator.mozApps.mgmt.getAll().onsuccess = function() {
     for (i in this.result) {
       var app = this.result[i];
       if (app.manifestURL == gManifestURL) {
         navigator.mozApps.mgmt.uninstall(app).onsuccess = function() {
           is(getPermissionCountForApp(testAppId), 0, "App should have no permissions");
 
           is(getPermissionCountForApp(-1), currentPermissionCount,
              "Number of permissions should not have changed");
--- a/gfx/gl/GLTextureImage.cpp
+++ b/gfx/gl/GLTextureImage.cpp
@@ -7,16 +7,17 @@
 #include "GLContext.h"
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxUtils.h"
 #include "gfx2DGlue.h"
 #include "mozilla/gfx/2D.h"
 #include "ScopedGLHelpers.h"
 #include "GLUploadHelpers.h"
+#include "GfxTexturesReporter.h"
 
 #include "TextureImageEGL.h"
 #ifdef XP_MACOSX
 #include "TextureImageCGL.h"
 #endif
 
 using namespace mozilla::gfx;
 
@@ -90,16 +91,26 @@ TextureImage::UpdateFromDataSource(gfx::
 gfx::IntRect TextureImage::GetTileRect() {
     return gfx::IntRect(gfx::IntPoint(0,0), mSize);
 }
 
 gfx::IntRect TextureImage::GetSrcTileRect() {
     return GetTileRect();
 }
 
+void
+TextureImage::UpdateUploadSize(size_t amount)
+{
+    if (mUploadSize > 0) {
+        GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryFreed, mUploadSize);
+    }
+    mUploadSize = amount;
+    GfxTexturesReporter::UpdateAmount(GfxTexturesReporter::MemoryAllocated, mUploadSize);
+}
+
 BasicTextureImage::~BasicTextureImage()
 {
     GLContext *ctx = mGLContext;
     if (ctx->IsDestroyed() || !ctx->IsOwningThreadCurrent()) {
         ctx = ctx->GetSharedContext();
     }
 
     // If we have a context, then we need to delete the texture;
@@ -157,26 +168,31 @@ BasicTextureImage::EndUpdate()
     NS_ASSERTION(!!mUpdateDrawTarget, "EndUpdate() without BeginUpdate()?");
 
     // FIXME: this is the slow boat.  Make me fast (with GLXPixmap?).
 
     RefPtr<gfx::SourceSurface> updateSnapshot = mUpdateDrawTarget->Snapshot();
     RefPtr<gfx::DataSourceSurface> updateData = updateSnapshot->GetDataSurface();
 
     bool relative = FinishedSurfaceUpdate();
-
+    bool needInit = mTextureState == Created;
+    size_t uploadSize;
     mTextureFormat =
         UploadSurfaceToTexture(mGLContext,
                                updateData,
                                mUpdateRegion,
                                mTexture,
-                               mTextureState == Created,
+                               &uploadSize,
+                               needInit,
                                mUpdateOffset,
                                relative);
     FinishedSurfaceUpload();
+    if (uploadSize > 0) {
+        UpdateUploadSize(uploadSize);
+    }
 
     mUpdateDrawTarget = nullptr;
     mTextureState = Valid;
 }
 
 void
 BasicTextureImage::BindTexture(GLenum aTextureUnit)
 {
@@ -209,24 +225,30 @@ BasicTextureImage::DirectUpdate(gfx::Dat
     nsIntRegion region;
     if (mTextureState != Valid) {
         bounds = IntRect(0, 0, mSize.width, mSize.height);
         region = nsIntRegion(bounds);
     } else {
         region = aRegion;
     }
 
+    size_t uploadSize;
+    bool needInit = mTextureState == Created;
     mTextureFormat =
         UploadSurfaceToTexture(mGLContext,
                                aSurf,
                                region,
                                mTexture,
-                               mTextureState == Created,
+                               &uploadSize,
+                               needInit,
                                bounds.TopLeft() + IntPoint(aFrom.x, aFrom.y),
                                false);
+    if (uploadSize > 0) {
+        UpdateUploadSize(uploadSize);
+    }
     mTextureState = Valid;
     return true;
 }
 
 void
 BasicTextureImage::Resize(const gfx::IntSize& aSize)
 {
     NS_ASSERTION(!mUpdateDrawTarget, "Resize() while in update?");
@@ -268,16 +290,17 @@ TextureImage::TextureImage(const gfx::In
              GLenum aWrapMode, ContentType aContentType,
              Flags aFlags)
     : mSize(aSize)
     , mWrapMode(aWrapMode)
     , mContentType(aContentType)
     , mTextureFormat(gfx::SurfaceFormat::UNKNOWN)
     , mFilter(Filter::GOOD)
     , mFlags(aFlags)
+    , mUploadSize(0)
 {}
 
 BasicTextureImage::BasicTextureImage(GLuint aTexture,
                   const gfx::IntSize& aSize,
                   GLenum aWrapMode,
                   ContentType aContentType,
                   GLContext* aContext,
                   TextureImage::Flags aFlags)
--- a/gfx/gl/GLTextureImage.h
+++ b/gfx/gl/GLTextureImage.h
@@ -198,37 +198,42 @@ public:
     virtual bool InUpdate() const = 0;
     GLenum GetWrapMode() const { return mWrapMode; }
 
     void SetFilter(gfx::Filter aFilter) { mFilter = aFilter; }
 
 protected:
     friend class GLContext;
 
+    void UpdateUploadSize(size_t amount);
+
     /**
      * After the ctor, the TextureImage is invalid.  Implementations
      * must allocate resources successfully before returning the new
      * TextureImage from GLContext::CreateTextureImage().  That is,
      * clients must not be given partially-constructed TextureImages.
      */
     TextureImage(const gfx::IntSize& aSize,
                  GLenum aWrapMode, ContentType aContentType,
                  Flags aFlags = NoFlags);
 
     // Protected destructor, to discourage deletion outside of Release():
-    virtual ~TextureImage() {}
+    virtual ~TextureImage() {
+        UpdateUploadSize(0);
+    }
 
     virtual gfx::IntRect GetSrcTileRect();
 
     gfx::IntSize mSize;
     GLenum mWrapMode;
     ContentType mContentType;
     gfx::SurfaceFormat mTextureFormat;
     gfx::Filter mFilter;
     Flags mFlags;
+    size_t mUploadSize;
 };
 
 /**
  * BasicTextureImage is the baseline TextureImage implementation ---
  * it updates its texture by allocating a scratch buffer for the
  * client to draw into, then using glTexSubImage2D() to upload the new
  * pixels.  Platforms must provide the code to create a new surface
  * into which the updated pixels will be drawn, and the code to
--- a/gfx/gl/GLUploadHelpers.cpp
+++ b/gfx/gl/GLUploadHelpers.cpp
@@ -4,16 +4,18 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GLUploadHelpers.h"
 
 #include "GLContext.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Tools.h"  // For BytesPerPixel
 #include "nsRegion.h"
+#include "GfxTexturesReporter.h"
+#include "mozilla/gfx/Logging.h"
 
 namespace mozilla {
 
 using namespace gfx;
 
 namespace gl {
 
 /* These two techniques are suggested by "Bit Twiddling Hacks"
@@ -374,29 +376,75 @@ TexImage2DHelper(GLContext *gl,
                         format,
                         type,
                         pixels);
         gl->fPixelStorei(LOCAL_GL_UNPACK_ROW_LENGTH, 0);
         gl->fPixelStorei(LOCAL_GL_UNPACK_ALIGNMENT, 4);
     }
 }
 
+static uint32_t
+GetBytesPerTexel(GLenum format, GLenum type)
+{
+    // If there is no defined format or type, we're not taking up any memory
+    if (!format || !type) {
+        return 0;
+    }
+
+    if (format == LOCAL_GL_DEPTH_COMPONENT) {
+        if (type == LOCAL_GL_UNSIGNED_SHORT)
+            return 2;
+        else if (type == LOCAL_GL_UNSIGNED_INT)
+            return 4;
+    } else if (format == LOCAL_GL_DEPTH_STENCIL) {
+        if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
+            return 4;
+    }
+
+    if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT || type == LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV) {
+        uint32_t multiplier = type == LOCAL_GL_UNSIGNED_BYTE ? 1 : 4;
+        switch (format) {
+            case LOCAL_GL_ALPHA:
+            case LOCAL_GL_LUMINANCE:
+                return 1 * multiplier;
+            case LOCAL_GL_LUMINANCE_ALPHA:
+                return 2 * multiplier;
+            case LOCAL_GL_RGB:
+                return 3 * multiplier;
+            case LOCAL_GL_RGBA:
+                return 4 * multiplier;
+            default:
+                break;
+        }
+    } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
+               type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
+               type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
+    {
+        return 2;
+    }
+
+    gfxCriticalError() << "Unknown texture type " << type << " or format " << format;
+    MOZ_CRASH();
+    return 0;
+}
+
 SurfaceFormat
 UploadImageDataToTexture(GLContext* gl,
                          unsigned char* aData,
                          int32_t aStride,
                          SurfaceFormat aFormat,
                          const nsIntRegion& aDstRegion,
                          GLuint& aTexture,
-                         bool aOverwrite,
+                         size_t* aOutUploadSize,
+                         bool aNeedInit,
                          bool aPixelBuffer,
                          GLenum aTextureUnit,
                          GLenum aTextureTarget)
 {
-    bool textureInited = aOverwrite ? false : true;
+    bool textureInited = aNeedInit ? false : true;
     gl->MakeCurrent();
     gl->fActiveTexture(aTextureUnit);
 
     if (!aTexture) {
         gl->fGenTextures(1, &aTexture);
         gl->fBindTexture(aTextureTarget, aTexture);
         gl->fTexParameteri(aTextureTarget,
                            LOCAL_GL_TEXTURE_MIN_FILTER,
@@ -500,16 +548,20 @@ UploadImageDataToTexture(GLContext* gl,
         default:
             NS_ASSERTION(false, "Unhandled image surface format!");
     }
 
 
     // Top left point of the region's bounding rectangle.
     IntPoint topLeft = paintRegion.GetBounds().TopLeft();
 
+    if (aOutUploadSize) {
+        *aOutUploadSize = 0;
+    }
+
     for (auto iter = paintRegion.RectIter(); !iter.Done(); iter.Next()) {
         const IntRect& rect = iter.Get();
         // The inital data pointer is at the top left point of the region's
         // bounding rectangle. We need to find the offset of this rect
         // within the region and adjust the data pointer accordingly.
         unsigned char *rectData =
             aData + DataOffset(rect.TopLeft() - topLeft, aStride, aFormat);
 
@@ -539,39 +591,45 @@ UploadImageDataToTexture(GLContext* gl,
                              aStride,
                              pixelSize,
                              0,
                              format,
                              type,
                              rectData);
         }
 
+        if (aOutUploadSize && !textureInited) {
+            uint32_t texelSize = GetBytesPerTexel(internalFormat, type);
+            size_t numTexels = size_t(rect.width) * size_t(rect.height);
+            *aOutUploadSize += texelSize * numTexels;
+        }
     }
 
     return surfaceFormat;
 }
 
 SurfaceFormat
 UploadSurfaceToTexture(GLContext* gl,
-                       DataSourceSurface *aSurface,
+                       DataSourceSurface* aSurface,
                        const nsIntRegion& aDstRegion,
                        GLuint& aTexture,
-                       bool aOverwrite,
+                       size_t* aOutUploadSize,
+                       bool aNeedInit,
                        const gfx::IntPoint& aSrcPoint,
                        bool aPixelBuffer,
                        GLenum aTextureUnit,
                        GLenum aTextureTarget)
 {
     unsigned char* data = aPixelBuffer ? nullptr : aSurface->GetData();
     int32_t stride = aSurface->Stride();
     SurfaceFormat format = aSurface->GetFormat();
     data += DataOffset(aSrcPoint, stride, format);
     return UploadImageDataToTexture(gl, data, stride, format,
-                                    aDstRegion, aTexture, aOverwrite,
-                                    aPixelBuffer, aTextureUnit,
+                                    aDstRegion, aTexture, aOutUploadSize,
+                                    aNeedInit, aPixelBuffer, aTextureUnit,
                                     aTextureTarget);
 }
 
 bool
 CanUploadNonPowerOfTwo(GLContext* gl)
 {
     if (!gl->WorkAroundDriverBugs())
         return true;
--- a/gfx/gl/GLUploadHelpers.h
+++ b/gfx/gl/GLUploadHelpers.h
@@ -35,16 +35,17 @@ class GLContext;
   * texture memory block allocated.
   *
   * The aDstPoint parameter is ignored if no texture was provided
   * or aOverwrite is true.
   *
   * \param aData Image data to upload.
   * \param aDstRegion Region of texture to upload to.
   * \param aTexture Texture to use, or 0 to have one created for you.
+  * \param aOutUploadSize if set, the number of bytes the texture requires will be returned here
   * \param aOverwrite Over an existing texture with a new one.
   * \param aSrcPoint Offset into aSrc where the region's bound's
   *  TopLeft() sits.
   * \param aPixelBuffer Pass true to upload texture data with an
   *  offset from the base data (generally for pixel buffer objects),
   *  otherwise textures are upload with an absolute pointer to the data.
   * \param aTextureUnit, the texture unit used temporarily to upload the
   *  surface. This testure may be overridden, clients should not rely on
@@ -54,30 +55,32 @@ class GLContext;
   */
 gfx::SurfaceFormat
 UploadImageDataToTexture(GLContext* gl,
                          unsigned char* aData,
                          int32_t aStride,
                          gfx::SurfaceFormat aFormat,
                          const nsIntRegion& aDstRegion,
                          GLuint& aTexture,
-                         bool aOverwrite = false,
+                         size_t* aOutUploadSize = nullptr,
+                         bool aNeedInit = false,
                          bool aPixelBuffer = false,
                          GLenum aTextureUnit = LOCAL_GL_TEXTURE0,
                          GLenum aTextureTarget = LOCAL_GL_TEXTURE_2D);
 
 /**
   * Convenience wrapper around UploadImageDataToTexture for gfx::DataSourceSurface's.
   */
 gfx::SurfaceFormat
 UploadSurfaceToTexture(GLContext* gl,
                        gfx::DataSourceSurface *aSurface,
                        const nsIntRegion& aDstRegion,
                        GLuint& aTexture,
-                       bool aOverwrite = false,
+                       size_t* aOutUploadSize = nullptr,
+                       bool aNeedInit = false,
                        const gfx::IntPoint& aSrcPoint = gfx::IntPoint(0, 0),
                        bool aPixelBuffer = false,
                        GLenum aTextureUnit = LOCAL_GL_TEXTURE0,
                        GLenum aTextureTarget = LOCAL_GL_TEXTURE_2D);
 
 bool CanUploadSubTextures(GLContext* gl);
 bool CanUploadNonPowerOfTwo(GLContext* gl);
 
--- a/gfx/gl/GfxTexturesReporter.cpp
+++ b/gfx/gl/GfxTexturesReporter.cpp
@@ -1,99 +1,34 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
 /* vim: set ts=8 sts=4 et sw=4 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "GfxTexturesReporter.h"
-#include "GLDefs.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
 
 using namespace mozilla;
 using namespace mozilla::gl;
 
 NS_IMPL_ISUPPORTS(GfxTexturesReporter, nsIMemoryReporter)
 
-Atomic<int32_t> GfxTexturesReporter::sAmount(0);
-Atomic<int32_t> GfxTexturesReporter::sTileWasteAmount(0);
+Atomic<size_t> GfxTexturesReporter::sAmount(0);
+Atomic<size_t> GfxTexturesReporter::sTileWasteAmount(0);
 
-static uint32_t
-GetBitsPerTexel(GLenum format, GLenum type)
+/* static */ void
+GfxTexturesReporter::UpdateAmount(MemoryUse action, size_t amount)
 {
-    // If there is no defined format or type, we're not taking up any memory
-    if (!format || !type) {
-        return 0;
-    }
-
-    if (format == LOCAL_GL_DEPTH_COMPONENT) {
-        if (type == LOCAL_GL_UNSIGNED_SHORT)
-            return 2*8;
-        else if (type == LOCAL_GL_UNSIGNED_INT)
-            return 4*8;
-    } else if (format == LOCAL_GL_DEPTH_STENCIL) {
-        if (type == LOCAL_GL_UNSIGNED_INT_24_8_EXT)
-            return 4*8;
+    if (action == MemoryFreed) {
+        MOZ_RELEASE_ASSERT(amount <= sAmount);
+        sAmount -= amount;
+    } else {
+        sAmount += amount;
     }
 
-    if (type == LOCAL_GL_UNSIGNED_BYTE || type == LOCAL_GL_FLOAT) {
-        uint32_t multiplier = type == LOCAL_GL_FLOAT ? 32 : 8;
-        switch (format) {
-            case LOCAL_GL_ALPHA:
-            case LOCAL_GL_LUMINANCE:
-                return 1 * multiplier;
-            case LOCAL_GL_LUMINANCE_ALPHA:
-                return 2 * multiplier;
-            case LOCAL_GL_RGB:
-                return 3 * multiplier;
-            case LOCAL_GL_RGBA:
-                return 4 * multiplier;
-            case LOCAL_GL_COMPRESSED_RGB_PVRTC_2BPPV1:
-            case LOCAL_GL_COMPRESSED_RGBA_PVRTC_2BPPV1:
-                return 2;
-            case LOCAL_GL_COMPRESSED_RGB_S3TC_DXT1_EXT:
-            case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT1_EXT:
-            case LOCAL_GL_ATC_RGB:
-            case LOCAL_GL_COMPRESSED_RGB_PVRTC_4BPPV1:
-            case LOCAL_GL_COMPRESSED_RGBA_PVRTC_4BPPV1:
-            case LOCAL_GL_ETC1_RGB8_OES:
-            case LOCAL_GL_COMPRESSED_RGB8_ETC2:
-            case LOCAL_GL_COMPRESSED_SRGB8_ETC2:
-            case LOCAL_GL_COMPRESSED_RGB8_PUNCHTHROUGH_ALPHA1_ETC2:
-            case LOCAL_GL_COMPRESSED_SRGB8_PUNCHTHROUGH_ALPHA1_ETC2:
-            case LOCAL_GL_COMPRESSED_R11_EAC:
-            case LOCAL_GL_COMPRESSED_SIGNED_R11_EAC:
-                return 4;
-            case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT3_EXT:
-            case LOCAL_GL_COMPRESSED_RGBA_S3TC_DXT5_EXT:
-            case LOCAL_GL_ATC_RGBA_EXPLICIT_ALPHA:
-            case LOCAL_GL_ATC_RGBA_INTERPOLATED_ALPHA:
-            case LOCAL_GL_COMPRESSED_RGBA8_ETC2_EAC:
-            case LOCAL_GL_COMPRESSED_SRGB8_ALPHA8_ETC2_EAC:
-            case LOCAL_GL_COMPRESSED_RG11_EAC:
-            case LOCAL_GL_COMPRESSED_SIGNED_RG11_EAC:
-                return 8;
-            default:
-                break;
-        }
-    } else if (type == LOCAL_GL_UNSIGNED_SHORT_4_4_4_4 ||
-               type == LOCAL_GL_UNSIGNED_SHORT_5_5_5_1 ||
-               type == LOCAL_GL_UNSIGNED_SHORT_5_6_5)
-    {
-        return 2*8;
-    }
-
-    MOZ_ASSERT(false);
-    return 0;
+#ifdef MOZ_CRASHREPORTER
+    CrashReporter::AnnotateTexturesSize(sAmount);
+#endif
 }
-
-/* static */ void
-GfxTexturesReporter::UpdateAmount(MemoryUse action, GLenum format,
-                                  GLenum type, int32_t tileWidth,
-                                  int32_t tileHeight)
-{
-    int64_t bitsPerTexel = GetBitsPerTexel(format, type);
-    int64_t bytes = int64_t(tileWidth) * int64_t(tileHeight) * bitsPerTexel/8;
-    if (action == MemoryFreed) {
-        sAmount -= bytes;
-    } else {
-        sAmount += bytes;
-    }
-}
--- a/gfx/gl/GfxTexturesReporter.h
+++ b/gfx/gl/GfxTexturesReporter.h
@@ -36,38 +36,37 @@ public:
         // when memory being allocated is reported to a memory reporter
         MemoryAllocated,
         // when memory being freed is reported to a memory reporter
         MemoryFreed
     };
 
     // When memory is used/freed for tile textures, call this method to update
     // the value reported by this memory reporter.
-    static void UpdateAmount(MemoryUse action, GLenum format, GLenum type,
-                             int32_t tileWidth, int32_t tileHeight);
+    static void UpdateAmount(MemoryUse action, size_t amount);
 
-    static void UpdateWasteAmount(int32_t delta) {
+    static void UpdateWasteAmount(size_t delta) {
       sTileWasteAmount += delta;
     }
 
     NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                               nsISupports* aData, bool aAnonymize) override
     {
         MOZ_COLLECT_REPORT("gfx-tiles-waste", KIND_OTHER, UNITS_BYTES,
-            sTileWasteAmount,
+            int64_t(sTileWasteAmount),
             "Memory lost due to tiles extending past content boundaries");
         return MOZ_COLLECT_REPORT(
-            "gfx-textures", KIND_OTHER, UNITS_BYTES, sAmount,
+            "gfx-textures", KIND_OTHER, UNITS_BYTES, int64_t(sAmount),
             "Memory used for storing GL textures.");
     }
 
 private:
-    static Atomic<int32_t> sAmount;
+    static Atomic<size_t> sAmount;
     // Count the amount of memory lost to tile waste
-    static Atomic<int32_t> sTileWasteAmount;
+    static Atomic<size_t> sTileWasteAmount;
 };
 
 class GfxTextureWasteTracker {
 public:
   GfxTextureWasteTracker()
     : mBytes(0)
   {
     MOZ_COUNT_CTOR(GfxTextureWasteTracker);
--- a/gfx/gl/TextureImageEGL.cpp
+++ b/gfx/gl/TextureImageEGL.cpp
@@ -202,24 +202,30 @@ TextureImageEGL::DirectUpdate(gfx::DataS
     nsIntRegion region;
     if (mTextureState != Valid) {
         bounds = gfx::IntRect(0, 0, mSize.width, mSize.height);
         region = nsIntRegion(bounds);
     } else {
         region = aRegion;
     }
 
+    bool needInit = mTextureState == Created;
+    size_t uploadSize = 0;
     mTextureFormat =
       UploadSurfaceToTexture(mGLContext,
                              aSurf,
                              region,
                              mTexture,
-                             mTextureState == Created,
+                             &uploadSize,
+                             needInit,
                              bounds.TopLeft() + gfx::IntPoint(aFrom.x, aFrom.y),
                              false);
+    if (uploadSize > 0) {
+        UpdateUploadSize(uploadSize);
+    }
 
     mTextureState = Valid;
     return true;
 }
 
 void
 TextureImageEGL::BindTexture(GLenum aTextureUnit)
 {
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -347,17 +347,17 @@ ContentClientRemoteBuffer::GetUpdatedReg
   if (mIsNewBuffer || aDidSelfCopy) {
     // A buffer reallocation clears both buffers. The front buffer has all the
     // content by now, but the back buffer is still clear. Here, in effect, we
     // are saying to copy all of the pixels of the front buffer to the back.
     // Also when we self-copied in the buffer, the buffer space
     // changes and some changed buffer content isn't reflected in the
     // draw or invalidate region (on purpose!).  When this happens, we
     // need to read back the entire buffer too.
-    updatedRegion = aVisibleRegion;
+    updatedRegion = aVisibleRegion.GetBounds();
     mIsNewBuffer = false;
   } else {
     updatedRegion = aRegionToDraw;
   }
 
   NS_ASSERTION(BufferRect().Contains(aRegionToDraw.GetBounds()),
                "Update outside of buffer rect!");
   MOZ_ASSERT(mTextureClient, "should have a back buffer by now");
--- a/js/src/asmjs/WasmFrameIterator.cpp
+++ b/js/src/asmjs/WasmFrameIterator.cpp
@@ -90,17 +90,16 @@ FrameIterator::settle()
         MOZ_ASSERT(callsite_);
         break;
       case CodeRange::Entry:
         fp_ = nullptr;
         MOZ_ASSERT(done());
         break;
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
-      case CodeRange::Interrupt:
       case CodeRange::Inline:
         MOZ_CRASH("Should not encounter an exit during iteration");
     }
 }
 
 JSAtom*
 FrameIterator::functionDisplayAtom() const
 {
@@ -187,17 +186,17 @@ PushRetAddr(MacroAssembler& masm)
 #endif
 }
 
 // Generate a prologue that maintains WasmActivation::fp as the virtual frame
 // pointer so that ProfilingFrameIterator can walk the stack at any pc in
 // generated code.
 static void
 GenerateProfilingPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
-                          ProfilingOffsets* offsets, Label* maybeEntry = nullptr)
+                          ProfilingOffsets* offsets)
 {
 #if !defined (JS_CODEGEN_ARM)
     Register scratch = ABIArgGenerator::NonArg_VolatileReg;
 #else
     // Unfortunately, there are no unused non-arg volatile registers on ARM --
     // the MacroAssembler claims both lr and ip -- so we use the second scratch
     // register (lr) and be very careful not to call any methods that use it.
     Register scratch = lr;
@@ -210,18 +209,16 @@ GenerateProfilingPrologue(MacroAssembler
     // this requires AutoForbidPools to prevent a constant pool from being
     // randomly inserted between two instructions.
     {
 #if defined(JS_CODEGEN_ARM)
         AutoForbidPools afp(&masm, /* number of instructions in scope = */ 5);
 #endif
 
         offsets->begin = masm.currentOffset();
-        if (maybeEntry)
-            masm.bind(maybeEntry);
 
         PushRetAddr(masm);
         MOZ_ASSERT_IF(!masm.oom(), PushedRetAddr == masm.currentOffset() - offsets->begin);
 
         masm.loadWasmActivation(scratch);
         masm.push(Address(scratch, WasmActivation::offsetOfFP()));
         MOZ_ASSERT_IF(!masm.oom(), PushedFP == masm.currentOffset() - offsets->begin);
 
@@ -380,20 +377,20 @@ wasm::GenerateFunctionEpilogue(MacroAsse
 
     // Profiling epilogue:
     offsets->profilingEpilogue = masm.currentOffset();
     GenerateProfilingEpilogue(masm, framePushed, ExitReason::None, offsets);
 }
 
 void
 wasm::GenerateExitPrologue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
-                           ProfilingOffsets* offsets, Label* maybeEntry)
+                           ProfilingOffsets* offsets)
 {
     masm.haltingAlign(CodeAlignment);
-    GenerateProfilingPrologue(masm, framePushed, reason, offsets, maybeEntry);
+    GenerateProfilingPrologue(masm, framePushed, reason, offsets);
     masm.setFramePushed(framePushed);
 }
 
 void
 wasm::GenerateExitEpilogue(MacroAssembler& masm, unsigned framePushed, ExitReason reason,
                            ProfilingOffsets* offsets)
 {
     // Inverse of GenerateExitPrologue:
@@ -488,17 +485,16 @@ ProfilingFrameIterator::initFromFP(const
       case CodeRange::Function:
         fp = CallerFPFromFP(fp);
         callerPC_ = ReturnAddressFromFP(fp);
         callerFP_ = CallerFPFromFP(fp);
         AssertMatchesCallSite(*module_, callerPC_, callerFP_, fp);
         break;
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
-      case CodeRange::Interrupt:
       case CodeRange::Inline:
         MOZ_CRASH("Unexpected CodeRange kind");
     }
 
     // The iterator inserts a pretend innermost frame for non-None ExitReasons.
     // This allows the variety of exit reasons to show up in the callstack.
     exitReason_ = activation.exitReason();
 
@@ -541,18 +537,17 @@ ProfilingFrameIterator::ProfilingFrameIt
 
     // Note: fp may be null while entering and leaving the activation.
     uint8_t* fp = activation.fp();
 
     const CodeRange* codeRange = module_->lookupCodeRange(state.pc);
     switch (codeRange->kind()) {
       case CodeRange::Function:
       case CodeRange::ImportJitExit:
-      case CodeRange::ImportInterpExit:
-      case CodeRange::Interrupt: {
+      case CodeRange::ImportInterpExit: {
         // When the pc is inside the prologue/epilogue, the innermost
         // call's AsmJSFrame is not complete and thus fp points to the the
         // second-to-innermost call's AsmJSFrame. Since fp can only tell you
         // about its caller (via ReturnAddressFromFP(fp)), naively unwinding
         // while pc is in the prologue/epilogue would skip the second-to-
         // innermost call. To avoid this problem, we use the static structure of
         // the code in the prologue and epilogue to do the Right Thing.
         MOZ_ASSERT(module_->containsCodePC(state.pc));
@@ -654,17 +649,16 @@ ProfilingFrameIterator::operator++()
     switch (codeRange->kind()) {
       case CodeRange::Entry:
         MOZ_ASSERT(callerFP_ == nullptr);
         callerPC_ = nullptr;
         break;
       case CodeRange::Function:
       case CodeRange::ImportJitExit:
       case CodeRange::ImportInterpExit:
-      case CodeRange::Interrupt:
       case CodeRange::Inline:
         stackAddress_ = callerFP_;
         callerPC_ = ReturnAddressFromFP(callerFP_);
         AssertMatchesCallSite(*module_, callerPC_, CallerFPFromFP(callerFP_), callerFP_);
         callerFP_ = CallerFPFromFP(callerFP_);
         break;
     }
 
@@ -696,17 +690,16 @@ ProfilingFrameIterator::label() const
         return nativeDescription;
     }
 
     switch (codeRange_->kind()) {
       case CodeRange::Function:         return module_->profilingLabel(codeRange_->funcIndex());
       case CodeRange::Entry:            return "entry trampoline (in asm.js)";
       case CodeRange::ImportJitExit:    return importJitDescription;
       case CodeRange::ImportInterpExit: return importInterpDescription;
-      case CodeRange::Interrupt:        return nativeDescription;
       case CodeRange::Inline:           return "inline stub (in asm.js)";
     }
 
     MOZ_CRASH("bad code range kind");
 }
 
 /*****************************************************************************/
 // Runtime patching to enable/disable profiling
--- a/js/src/asmjs/WasmFrameIterator.h
+++ b/js/src/asmjs/WasmFrameIterator.h
@@ -93,17 +93,17 @@ class ProfilingFrameIterator
 
     void* stackAddress() const { MOZ_ASSERT(!done()); return stackAddress_; }
     const char* label() const;
 };
 
 // Prologue/epilogue code generation
 void
 GenerateExitPrologue(jit::MacroAssembler& masm, unsigned framePushed, ExitReason reason,
-                     ProfilingOffsets* offsets, jit::Label* maybeEntry = nullptr);
+                     ProfilingOffsets* offsets);
 void
 GenerateExitEpilogue(jit::MacroAssembler& masm, unsigned framePushed, ExitReason reason,
                      ProfilingOffsets* offsets);
 void
 GenerateFunctionPrologue(jit::MacroAssembler& masm, unsigned framePushed, FuncOffsets* offsets);
 void
 GenerateFunctionEpilogue(jit::MacroAssembler& masm, unsigned framePushed, FuncOffsets* offsets);
 
--- a/js/src/asmjs/WasmGenerator.cpp
+++ b/js/src/asmjs/WasmGenerator.cpp
@@ -168,34 +168,43 @@ ModuleGenerator::finishOutstandingTask()
 
             HelperThreadState().wait(GlobalHelperThreadState::CONSUMER);
         }
     }
 
     return finishTask(task);
 }
 
+static const uint32_t BadEntry = UINT32_MAX;
+
+bool
+ModuleGenerator::funcIsDefined(uint32_t funcIndex) const
+{
+    return funcIndex < funcEntryOffsets_.length() &&
+           funcEntryOffsets_[funcIndex] != BadEntry;
+}
+
 bool
 ModuleGenerator::finishTask(IonCompileTask* task)
 {
     const FuncBytecode& func = task->func();
     FuncCompileResults& results = task->results();
 
     // Offset the recorded FuncOffsets by the offset of the function in the
     // whole module's code segment.
     uint32_t offsetInWhole = masm_.size();
     results.offsets().offsetBy(offsetInWhole);
 
     // Record the non-profiling entry for whole-module linking later.
     // Cannot simply append because funcIndex order is nonlinear.
     if (func.index() >= funcEntryOffsets_.length()) {
-        if (!funcEntryOffsets_.resize(func.index() + 1))
+        if (!funcEntryOffsets_.appendN(BadEntry, func.index() - funcEntryOffsets_.length() + 1))
             return false;
     }
-    MOZ_ASSERT(funcEntryOffsets_[func.index()] == 0);
+    MOZ_ASSERT(!funcIsDefined(func.index()));
     funcEntryOffsets_[func.index()] = results.offsets().nonProfilingEntry;
 
     // Merge the compiled results into the whole-module masm.
     DebugOnly<size_t> sizeBefore = masm_.size();
     if (!masm_.asmMergeWith(results.masm()))
         return false;
     MOZ_ASSERT(masm_.size() == offsetInWhole + results.masm().size());
 
@@ -398,17 +407,19 @@ uint32_t
 ModuleGenerator::exportFuncIndex(uint32_t index) const
 {
     return exportMap_->exportFuncIndices[index];
 }
 
 uint32_t
 ModuleGenerator::exportEntryOffset(uint32_t index) const
 {
-    return funcEntryOffsets_[exportMap_->exportFuncIndices[index]];
+    uint32_t funcIndex = exportMap_->exportFuncIndices[index];
+    MOZ_ASSERT(funcIsDefined(funcIndex));
+    return funcEntryOffsets_[funcIndex];
 }
 
 const Sig&
 ModuleGenerator::exportSig(uint32_t index) const
 {
     return module_->exports[index].sig();
 }
 
@@ -545,16 +556,19 @@ ModuleGenerator::finishFuncDefs()
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(!finishedFuncs_);
 
     while (outstanding_ > 0) {
         if (!finishOutstandingTask())
             return false;
     }
 
+    for (uint32_t funcIndex = 0; funcIndex < funcEntryOffsets_.length(); funcIndex++)
+        MOZ_ASSERT(funcIsDefined(funcIndex));
+
     // During codegen, all wasm->wasm (internal) calls use AsmJSInternalCallee
     // as the call target, which contains the function-index of the target.
     // These get recorded in a CallSiteAndTargetVector in the MacroAssembler
     // so that we can patch them now that all the function entry offsets are
     // known.
 
     for (CallSiteAndTarget& cs : masm_.callSites()) {
         if (!cs.isInternal())
@@ -606,63 +620,57 @@ ModuleGenerator::funcPtrTableGlobalDataO
 void
 ModuleGenerator::defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& elemFuncIndices)
 {
     MOZ_ASSERT(finishedFuncs_);
 
     StaticLinkData::FuncPtrTable& table = link_->funcPtrTables[index];
     MOZ_ASSERT(table.elemOffsets.length() == elemFuncIndices.length());
 
-    for (size_t i = 0; i < elemFuncIndices.length(); i++)
-        table.elemOffsets[i] = funcEntryOffsets_[elemFuncIndices[i]];
+    for (size_t i = 0; i < elemFuncIndices.length(); i++) {
+        uint32_t funcIndex = elemFuncIndices[i];
+        MOZ_ASSERT(funcIsDefined(funcIndex));
+        table.elemOffsets[i] = funcEntryOffsets_[funcIndex];
+    }
 }
 
 bool
 ModuleGenerator::defineInlineStub(Offsets offsets)
 {
     MOZ_ASSERT(finishedFuncs_);
     return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
 }
 
-bool
-ModuleGenerator::defineSyncInterruptStub(ProfilingOffsets offsets)
-{
-    MOZ_ASSERT(finishedFuncs_);
-    return module_->codeRanges.emplaceBack(CodeRange::Interrupt, offsets);
-}
-
-bool
-ModuleGenerator::defineAsyncInterruptStub(Offsets offsets)
+void
+ModuleGenerator::defineInterruptExit(uint32_t offset)
 {
     MOZ_ASSERT(finishedFuncs_);
-    link_->pod.interruptOffset = offsets.begin;
-    return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
+    link_->pod.interruptOffset = offset;
 }
 
-bool
-ModuleGenerator::defineOutOfBoundsStub(Offsets offsets)
+void
+ModuleGenerator::defineOutOfBoundsExit(uint32_t offset)
 {
     MOZ_ASSERT(finishedFuncs_);
-    link_->pod.outOfBoundsOffset = offsets.begin;
-    return module_->codeRanges.emplaceBack(CodeRange::Inline, offsets);
+    link_->pod.outOfBoundsOffset = offset;
 }
 
 bool
 ModuleGenerator::finish(CacheableCharsVector&& prettyFuncNames,
                         UniqueModuleData* module,
                         UniqueStaticLinkData* linkData,
                         UniqueExportMap* exportMap,
                         SlowFunctionVector* slowFuncs)
 {
     MOZ_ASSERT(!activeFunc_);
     MOZ_ASSERT(finishedFuncs_);
 
     module_->prettyFuncNames = Move(prettyFuncNames);
 
-    if (!GenerateStubs(*this, UsesHeap(module_->heapUsage)))
+    if (!GenerateStubs(*this))
         return false;
 
     masm_.finish();
     if (masm_.oom())
         return false;
 
     // Start global data on a new page so JIT code may be given independent
     // protection flags. Note assumption that global data starts right after
--- a/js/src/asmjs/WasmGenerator.h
+++ b/js/src/asmjs/WasmGenerator.h
@@ -152,16 +152,17 @@ class MOZ_STACK_CLASS ModuleGenerator
     Vector<IonCompileTask>          tasks_;
     Vector<IonCompileTask*>         freeTasks_;
 
     // Assertions
     DebugOnly<FunctionGenerator*>   activeFunc_;
     DebugOnly<bool>                 finishedFuncs_;
 
     bool finishOutstandingTask();
+    bool funcIsDefined(uint32_t funcIndex) const;
     bool finishTask(IonCompileTask* task);
     bool addImport(const Sig& sig, uint32_t globalDataOffset);
     bool startedFuncDefs() const { return !!threadView_; }
     bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
 
   public:
     explicit ModuleGenerator(ExclusiveContext* cx);
     ~ModuleGenerator();
@@ -213,19 +214,18 @@ class MOZ_STACK_CLASS ModuleGenerator
 
     // Function-pointer tables:
     bool declareFuncPtrTable(uint32_t numElems, uint32_t* index);
     uint32_t funcPtrTableGlobalDataOffset(uint32_t index) const;
     void defineFuncPtrTable(uint32_t index, const Vector<uint32_t>& elemFuncIndices);
 
     // Stubs:
     bool defineInlineStub(Offsets offsets);
-    bool defineSyncInterruptStub(ProfilingOffsets offsets);
-    bool defineAsyncInterruptStub(Offsets offsets);
-    bool defineOutOfBoundsStub(Offsets offsets);
+    void defineInterruptExit(uint32_t offset);
+    void defineOutOfBoundsExit(uint32_t offset);
 
     // Return a ModuleData object which may be used to construct a Module, the
     // StaticLinkData required to call Module::staticallyLink, and the list of
     // functions that took a long time to compile.
     bool finish(CacheableCharsVector&& prettyFuncNames,
                 UniqueModuleData* module,
                 UniqueStaticLinkData* staticLinkData,
                 UniqueExportMap* exportMap,
--- a/js/src/asmjs/WasmIonCompile.cpp
+++ b/js/src/asmjs/WasmIonCompile.cpp
@@ -584,18 +584,25 @@ class FunctionCompiler
         curBlock_->add(MAsmJSStoreGlobalVar::New(alloc(), globalDataOffset, v));
     }
 
     void addInterruptCheck()
     {
         if (inDeadCode())
             return;
 
+        // WasmHandleExecutionInterrupt takes 0 arguments and the stack is
+        // always ABIStackAlignment-aligned, but don't forget to account for
+        // ShadowStackSpace and any other ABI warts.
+        ABIArgGenerator abi;
+        if (abi.stackBytesConsumedSoFar() > mirGen_.maxAsmJSStackArgBytes())
+            mirGen_.setAsmJSMaxStackArgBytes(abi.stackBytesConsumedSoFar());
+
         CallSiteDesc callDesc(0, CallSiteDesc::Relative);
-        curBlock_->add(MAsmJSInterruptCheck::New(alloc(), masm().asmSyncInterruptLabel(), callDesc));
+        curBlock_->add(MAsmJSInterruptCheck::New(alloc()));
     }
 
     MDefinition* extractSimdElement(SimdLane lane, MDefinition* base, MIRType type)
     {
         if (inDeadCode())
             return nullptr;
 
         MOZ_ASSERT(IsSimdType(base->type()));
--- a/js/src/asmjs/WasmModule.cpp
+++ b/js/src/asmjs/WasmModule.cpp
@@ -370,17 +370,17 @@ CodeRange::CodeRange(Kind kind, Profilin
     profilingReturn_(offsets.profilingReturn),
     end_(offsets.end)
 {
     PodZero(&u);  // zero padding for Valgrind
     u.kind_ = kind;
 
     MOZ_ASSERT(begin_ < profilingReturn_);
     MOZ_ASSERT(profilingReturn_ < end_);
-    MOZ_ASSERT(u.kind_ == ImportJitExit || u.kind_ == ImportInterpExit || u.kind_ == Interrupt);
+    MOZ_ASSERT(u.kind_ == ImportJitExit || u.kind_ == ImportInterpExit);
 }
 
 CodeRange::CodeRange(uint32_t funcIndex, uint32_t funcLineOrBytecode, FuncOffsets offsets)
   : funcIndex_(funcIndex),
     funcLineOrBytecode_(funcLineOrBytecode)
 {
     PodZero(&u);  // zero padding for Valgrind
     u.kind_ = Function;
--- a/js/src/asmjs/WasmModule.h
+++ b/js/src/asmjs/WasmModule.h
@@ -208,17 +208,17 @@ class CodeRange
             uint8_t profilingEpilogueToProfilingReturn_;
         } func;
         uint8_t kind_;
     } u;
 
     void assertValid();
 
   public:
-    enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Interrupt, Inline };
+    enum Kind { Function, Entry, ImportJitExit, ImportInterpExit, Inline };
 
     CodeRange() = default;
     CodeRange(Kind kind, Offsets offsets);
     CodeRange(Kind kind, ProfilingOffsets offsets);
     CodeRange(uint32_t funcIndex, uint32_t lineOrBytecode, FuncOffsets offsets);
 
     // All CodeRanges have a begin and end.
 
--- a/js/src/asmjs/WasmStubs.cpp
+++ b/js/src/asmjs/WasmStubs.cpp
@@ -90,17 +90,17 @@ static const unsigned FramePushedAfterSa
 #endif
 static const unsigned FramePushedForEntrySP = FramePushedAfterSave + sizeof(void*);
 
 // Generate a stub that enters wasm from a C++ caller via the native ABI.
 // The signature of the entry point is Module::CodePtr. The exported wasm
 // function has an ABI derived from its specific signature, so this function
 // must map from the ABI of CodePtr to the export's signature's ABI.
 static bool
-GenerateEntry(ModuleGenerator& mg, unsigned exportIndex, bool usesHeap)
+GenerateEntry(ModuleGenerator& mg, unsigned exportIndex)
 {
     MacroAssembler& masm = mg.masm();
     const Sig& sig = mg.exportSig(exportIndex);
 
     masm.haltingAlign(CodeAlignment);
 
     Offsets offsets;
     offsets.begin = masm.currentOffset();
@@ -126,17 +126,17 @@ GenerateEntry(ModuleGenerator& mg, unsig
 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
     masm.movePtr(IntArgReg1, GlobalReg);
     masm.addPtr(Imm32(AsmJSGlobalRegBias), GlobalReg);
 #endif
 
     // ARM, MIPS/MIPS64 and x64 have a globally-pinned HeapReg (x86 uses immediates in
     // effective addresses). Loading the heap register depends on the global
     // register already having been loaded.
-    if (usesHeap)
+    if (mg.usesHeap())
         masm.loadAsmJSHeapRegisterFromGlobalData();
 
     // Put the 'argv' argument into a non-argument/return register so that we
     // can use 'argv' while we fill in the arguments for the asm.js callee.
     // Also, save 'argv' on the stack so that we can recover it after the call.
     // Use a second non-argument/return register as temporary scratch.
     Register argv = ABIArgGenerator::NonArgReturnReg0;
     Register scratch = ABIArgGenerator::NonArgReturnReg1;
@@ -329,18 +329,17 @@ FillArgumentArray(MacroAssembler& masm, 
         }
     }
 }
 
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into an appropriate InvokeImport C++
 // function, having boxed all the ABI arguments into a homogeneous Value array.
 static bool
-GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, Label* throwLabel,
-                       ProfilingOffsets* offsets)
+GenerateInterpExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets* offsets)
 {
     MacroAssembler& masm = mg.masm();
     const Sig& sig = *mg.import(importIndex).sig;
 
     masm.setFramePushed(0);
 
     // Argument types for InvokeImport_*:
     static const MIRType typeArray[] = { MIRType_Pointer,   // ImportExit
@@ -393,30 +392,30 @@ GenerateInterpExitStub(ModuleGenerator& 
     i++;
     MOZ_ASSERT(i.done());
 
     // Make the call, test whether it succeeded, and extract the return value.
     AssertStackAlignment(masm, ABIStackAlignment);
     switch (sig.ret()) {
       case ExprType::Void:
         masm.call(SymbolicAddress::InvokeImport_Void);
-        masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+        masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
         break;
       case ExprType::I32:
         masm.call(SymbolicAddress::InvokeImport_I32);
-        masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+        masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
         masm.unboxInt32(argv, ReturnReg);
         break;
       case ExprType::I64:
         MOZ_CRASH("no int64 in asm.js");
       case ExprType::F32:
         MOZ_CRASH("Float32 shouldn't be returned from a FFI");
       case ExprType::F64:
         masm.call(SymbolicAddress::InvokeImport_F64);
-        masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+        masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
         masm.loadDouble(argv, ReturnDoubleReg);
         break;
       case ExprType::I32x4:
       case ExprType::F32x4:
       case ExprType::B32x4:
         MOZ_CRASH("SIMD types shouldn't be returned from a FFI");
       case ExprType::Limit:
         MOZ_CRASH("Limit");
@@ -436,18 +435,17 @@ static const unsigned MaybeSavedGlobalRe
 #else
 static const unsigned MaybeSavedGlobalReg = 0;
 #endif
 
 // Generate a stub that is called via the internal ABI derived from the
 // signature of the import and calls into a compatible JIT function,
 // having boxed all the ABI arguments into the JIT stack frame layout.
 static bool
-GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, bool usesHeap,
-                    Label* throwLabel, ProfilingOffsets* offsets)
+GenerateJitExitStub(ModuleGenerator& mg, unsigned importIndex, ProfilingOffsets* offsets)
 {
     MacroAssembler& masm = mg.masm();
     const Sig& sig = *mg.import(importIndex).sig;
 
     masm.setFramePushed(0);
 
     // JIT calls use the following stack layout (sp grows to the left):
     //   | retaddr | descriptor | callee | argc | this | arg1..N |
@@ -642,17 +640,17 @@ GenerateJitExitStub(ModuleGenerator& mg,
     //   (sp + sizeof(void*)) % JitStackAlignment == 0
     // But now we possibly want to call one of several different C++ functions,
     // so subtract the sizeof(void*) so that sp is aligned for an ABI call.
     static_assert(ABIStackAlignment <= JitStackAlignment, "subsumes");
     masm.reserveStack(sizeOfRetAddr);
     unsigned nativeFramePushed = masm.framePushed();
     AssertStackAlignment(masm, ABIStackAlignment);
 
-    masm.branchTestMagic(Assembler::Equal, JSReturnOperand, throwLabel);
+    masm.branchTestMagic(Assembler::Equal, JSReturnOperand, JumpTarget::Throw);
 
     Label oolConvert;
     switch (sig.ret()) {
       case ExprType::Void:
         break;
       case ExprType::I32:
         masm.convertValueToInt32(JSReturnOperand, ReturnDoubleReg, ReturnReg, &oolConvert,
                                  /* -0 check */ false);
@@ -672,17 +670,17 @@ GenerateJitExitStub(ModuleGenerator& mg,
         MOZ_CRASH("Limit");
     }
 
     Label done;
     masm.bind(&done);
 
     // Ion code does not respect system callee-saved register conventions so
     // reload the heap register.
-    if (usesHeap)
+    if (mg.usesHeap())
         masm.loadAsmJSHeapRegisterFromGlobalData();
 
     GenerateExitEpilogue(masm, masm.framePushed(), ExitReason::ImportJit, offsets);
 
     if (oolConvert.used()) {
         masm.bind(&oolConvert);
         masm.setFramePushed(nativeFramePushed);
 
@@ -709,22 +707,22 @@ GenerateJitExitStub(ModuleGenerator& mg,
         i++;
         MOZ_ASSERT(i.done());
 
         // Call coercion function
         AssertStackAlignment(masm, ABIStackAlignment);
         switch (sig.ret()) {
           case ExprType::I32:
             masm.call(SymbolicAddress::CoerceInPlace_ToInt32);
-            masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+            masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
             masm.unboxInt32(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnReg);
             break;
           case ExprType::F64:
             masm.call(SymbolicAddress::CoerceInPlace_ToNumber);
-            masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, throwLabel);
+            masm.branchTest32(Assembler::Zero, ReturnReg, ReturnReg, JumpTarget::Throw);
             masm.loadDouble(Address(masm.getStackPointer(), offsetToCoerceArgv), ReturnDoubleReg);
             break;
           default:
             MOZ_CRASH("Unsupported convert type");
         }
 
         masm.jump(&done);
         masm.setFramePushed(0);
@@ -734,28 +732,42 @@ GenerateJitExitStub(ModuleGenerator& mg,
 
     if (masm.oom())
         return false;
 
     offsets->end = masm.currentOffset();
     return true;
 }
 
+static void
+BindJumps(MacroAssembler& masm, JumpTarget target)
+{
+    for (uint32_t offset : masm.jumpSites()[target]) {
+        RepatchLabel label;
+        label.use(offset);
+        masm.bind(&label);
+    }
+}
+
 // Generate a stub that is called immediately after the prologue when there is a
 // stack overflow. This stub calls a C++ function to report the error and then
 // jumps to the throw stub to pop the activation.
 static bool
-GenerateStackOverflowStub(ModuleGenerator& mg, Label* throwLabel)
+GenerateStackOverflowStub(ModuleGenerator& mg)
 {
     MacroAssembler& masm = mg.masm();
+    masm.haltingAlign(CodeAlignment);
 
-    masm.haltingAlign(CodeAlignment);
+    if (masm.jumpSites()[JumpTarget::StackOverflow].empty())
+        return true;
+
+    BindJumps(masm, JumpTarget::StackOverflow);
+
     Offsets offsets;
     offsets.begin = masm.currentOffset();
-    masm.bind(masm.asmStackOverflowLabel());
 
     // If we reach here via the non-profiling prologue, WasmActivation::fp has
     // not been updated. To enable stack unwinding from C++, store to it now. If
     // we reached here via the profiling prologue, we'll just store the same
     // value again. Do not update AsmJSFrame::callerFP as it is not necessary in
     // the non-profiling case (there is no return path from this point) and, in
     // the profiling case, it is already correct.
     Register activation = ABIArgGenerator::NonArgReturnReg0;
@@ -764,130 +776,115 @@ GenerateStackOverflowStub(ModuleGenerato
 
     // Prepare the stack for calling C++.
     if (uint32_t d = StackDecrementForCall(ABIStackAlignment, sizeof(AsmJSFrame), ShadowStackSpace))
         masm.subFromStackPtr(Imm32(d));
 
     // No need to restore the stack; the throw stub pops everything.
     masm.assertStackAlignment(ABIStackAlignment);
     masm.call(SymbolicAddress::ReportOverRecursed);
-    masm.jump(throwLabel);
-
-    if (masm.oom())
-        return false;
-
-    offsets.end = masm.currentOffset();
-    return mg.defineInlineStub(offsets);
-}
-
-// Generate a stub that is called from the synchronous, inline interrupt checks
-// when the interrupt flag is set. This stub calls the C++ function to handle
-// the interrupt which returns whether execution has been interrupted.
-static bool
-GenerateSyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
-{
-    MacroAssembler& masm = mg.masm();
-
-    masm.setFramePushed(0);
-    unsigned framePushed = StackDecrementForCall(masm, ABIStackAlignment, ShadowStackSpace);
-
-    ProfilingOffsets offsets;
-    GenerateExitPrologue(masm, framePushed, ExitReason::Native, &offsets,
-                         masm.asmSyncInterruptLabel());
-
-    AssertStackAlignment(masm, ABIStackAlignment);
-    masm.call(SymbolicAddress::HandleExecutionInterrupt);
-    masm.branchIfFalseBool(ReturnReg, throwLabel);
-
-    GenerateExitEpilogue(masm, framePushed, ExitReason::Native, &offsets);
-
-    if (masm.oom())
-        return false;
-
-    offsets.end = masm.currentOffset();
-    return mg.defineSyncInterruptStub(offsets);
-}
-
-// Generate a stub that is jumped to from an out-of-bounds heap access when
-// there are throwing semantics. This stub calls a C++ function to report an
-// error and then jumps to the throw stub to pop the activation.
-static bool
-GenerateConversionErrorStub(ModuleGenerator& mg, Label* throwLabel)
-{
-    MacroAssembler& masm = mg.masm();
-
-    masm.haltingAlign(CodeAlignment);
-    Offsets offsets;
-    offsets.begin = masm.currentOffset();
-    masm.bind(masm.asmOnConversionErrorLabel());
-
-    // sp can be anything at this point, so ensure it is aligned when calling
-    // into C++.  We unconditionally jump to throw so don't worry about restoring sp.
-    masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
-
-    // OnImpreciseConversion always throws.
-    masm.assertStackAlignment(ABIStackAlignment);
-    masm.call(SymbolicAddress::OnImpreciseConversion);
-    masm.jump(throwLabel);
+    masm.jump(JumpTarget::Throw);
 
     if (masm.oom())
         return false;
 
     offsets.end = masm.currentOffset();
     return mg.defineInlineStub(offsets);
 }
 
 // Generate a stub that is jumped to from an out-of-bounds heap access when
 // there are throwing semantics. This stub calls a C++ function to report an
 // error and then jumps to the throw stub to pop the activation.
 static bool
-GenerateOutOfBoundsStub(ModuleGenerator& mg, Label* throwLabel)
+GenerateConversionErrorStub(ModuleGenerator& mg)
 {
     MacroAssembler& masm = mg.masm();
+    masm.haltingAlign(CodeAlignment);
 
-    masm.haltingAlign(CodeAlignment);
+    if (masm.jumpSites()[JumpTarget::ConversionError].empty())
+        return true;
+
+    BindJumps(masm, JumpTarget::ConversionError);
+
     Offsets offsets;
     offsets.begin = masm.currentOffset();
-    masm.bind(masm.asmOnOutOfBoundsLabel());
+
+    // sp can be anything at this point, so ensure it is aligned when calling
+    // into C++.  We unconditionally jump to throw so don't worry about restoring sp.
+    masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
+
+    // OnImpreciseConversion always throws.
+    masm.assertStackAlignment(ABIStackAlignment);
+    masm.call(SymbolicAddress::OnImpreciseConversion);
+    masm.jump(JumpTarget::Throw);
+
+    if (masm.oom())
+        return false;
+
+    offsets.end = masm.currentOffset();
+    return mg.defineInlineStub(offsets);
+}
+
+// Generate a stub that is jumped to from an out-of-bounds heap access when
+// there are throwing semantics. This stub calls a C++ function to report an
+// error and then jumps to the throw stub to pop the activation.
+static bool
+GenerateOutOfBoundsStub(ModuleGenerator& mg)
+{
+    MacroAssembler& masm = mg.masm();
+    masm.haltingAlign(CodeAlignment);
+
+    // Generate the out-of-bounds stub unconditionally since it may always be
+    // used by the signal handler.
+    mg.defineOutOfBoundsExit(masm.currentOffset());
+
+    BindJumps(masm, JumpTarget::OutOfBounds);
+
+    Offsets offsets;
+    offsets.begin = masm.currentOffset();
 
     // sp can be anything at this point, so ensure it is aligned when calling
     // into C++.  We unconditionally jump to throw so don't worry about restoring sp.
     masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
 
     // OnOutOfBounds always throws.
     masm.assertStackAlignment(ABIStackAlignment);
     masm.call(SymbolicAddress::OnOutOfBounds);
-    masm.jump(throwLabel);
+    masm.jump(JumpTarget::Throw);
 
     if (masm.oom())
         return false;
 
     offsets.end = masm.currentOffset();
-    return mg.defineOutOfBoundsStub(offsets);
+    return mg.defineInlineStub(offsets);
 }
 
 static const LiveRegisterSet AllRegsExceptSP(
     GeneralRegisterSet(Registers::AllMask&
                        ~(uint32_t(1) << Registers::StackPointer)),
     FloatRegisterSet(FloatRegisters::AllMask));
 
 // The async interrupt-callback exit is called from arbitrarily-interrupted asm.js
 // code. That means we must first save *all* registers and restore *all*
 // registers (except the stack pointer) when we resume. The address to resume to
 // (assuming that js::HandleExecutionInterrupt doesn't indicate that the
 // execution should be aborted) is stored in WasmActivation::resumePC_.
 // Unfortunately, loading this requires a scratch register which we don't have
 // after restoring all registers. To hack around this, push the resumePC on the
 // stack so that it can be popped directly into PC.
 static bool
-GenerateAsyncInterruptStub(ModuleGenerator& mg, Label* throwLabel)
+GenerateInterruptStub(ModuleGenerator& mg)
 {
     MacroAssembler& masm = mg.masm();
+    masm.haltingAlign(CodeAlignment);
 
-    masm.haltingAlign(CodeAlignment);
+    // Generate the interrupt stub unconditionally since it may always be used
+    // by the signal handler.
+    mg.defineInterruptExit(masm.currentOffset());
+
     Offsets offsets;
     offsets.begin = masm.currentOffset();
 
 #if defined(JS_CODEGEN_X86) || defined(JS_CODEGEN_X64)
     // Be very careful here not to perturb the machine state before saving it
     // to the stack. In particular, add/sub instructions may set conditions in
     // the flags register.
     masm.push(Imm32(0));            // space for resumePC
@@ -907,17 +904,17 @@ GenerateAsyncInterruptStub(ModuleGenerat
     masm.moveStackPtrTo(ABIArgGenerator::NonVolatileReg);
     masm.andToStackPtr(Imm32(~(ABIStackAlignment - 1)));
     if (ShadowStackSpace)
         masm.subFromStackPtr(Imm32(ShadowStackSpace));
 
     masm.assertStackAlignment(ABIStackAlignment);
     masm.call(SymbolicAddress::HandleExecutionInterrupt);
 
-    masm.branchIfFalseBool(ReturnReg, throwLabel);
+    masm.branchIfFalseBool(ReturnReg, JumpTarget::Throw);
 
     // Restore the StackPointer to its position before the call.
     masm.moveToStackPtr(ABIArgGenerator::NonVolatileReg);
 
     // Restore the machine state to before the interrupt.
     masm.PopRegsInMask(AllRegsExceptSP); // restore all GP/FP registers (except SP)
     masm.popFlags();              // after this, nothing that sets conditions
     masm.ret();                   // pop resumePC into PC
@@ -945,17 +942,17 @@ GenerateAsyncInterruptStub(ModuleGenerat
     // MIPS ABI requires rewserving stack for registes $a0 to $a3.
     masm.subFromStackPtr(Imm32(4 * sizeof(intptr_t)));
 
     masm.assertStackAlignment(ABIStackAlignment);
     masm.call(SymbolicAddress::HandleExecutionInterrupt);
 
     masm.addToStackPtr(Imm32(4 * sizeof(intptr_t)));
 
-    masm.branchIfFalseBool(ReturnReg, throwLabel);
+    masm.branchIfFalseBool(ReturnReg, JumpTarget::Throw);
 
     // This will restore stack to the address before the call.
     masm.moveToStackPtr(s0);
     masm.PopRegsInMask(AllRegsExceptSP);
 
     // Pop resumePC into PC. Clobber HeapReg to make the jump and restore it
     // during jump delay slot.
     masm.pop(HeapReg);
@@ -988,17 +985,17 @@ GenerateAsyncInterruptStub(ModuleGenerat
     // Save all FP registers
     JS_STATIC_ASSERT(!SupportsSimd);
     masm.PushRegsInMask(LiveRegisterSet(GeneralRegisterSet(0),
                                         FloatRegisterSet(FloatRegisters::AllDoubleMask)));
 
     masm.assertStackAlignment(ABIStackAlignment);
     masm.call(SymbolicAddress::HandleExecutionInterrupt);
 
-    masm.branchIfFalseBool(ReturnReg, throwLabel);
+    masm.branchIfFalseBool(ReturnReg, JumpTarget::Throw);
 
     // Restore the machine state to before the interrupt. this will set the pc!
 
     // Restore all FP registers
     masm.PopRegsInMask(LiveRegisterSet(GeneralRegisterSet(0),
                                        FloatRegisterSet(FloatRegisters::AllDoubleMask)));
     masm.mov(r6,sp);
     masm.as_vmsr(r5);
@@ -1028,33 +1025,37 @@ GenerateAsyncInterruptStub(ModuleGenerat
 #else
 # error "Unknown architecture!"
 #endif
 
     if (masm.oom())
         return false;
 
     offsets.end = masm.currentOffset();
-    return mg.defineAsyncInterruptStub(offsets);
+    return mg.defineInlineStub(offsets);
 }
 
 // If an exception is thrown, simply pop all frames (since asm.js does not
 // contain try/catch). To do this:
 //  1. Restore 'sp' to it's value right after the PushRegsInMask in GenerateEntry.
 //  2. PopRegsInMask to restore the caller's non-volatile registers.
 //  3. Return (to CallAsmJS).
 static bool
-GenerateThrowStub(ModuleGenerator& mg, Label* throwLabel)
+GenerateThrowStub(ModuleGenerator& mg)
 {
     MacroAssembler& masm = mg.masm();
+    masm.haltingAlign(CodeAlignment);
 
-    masm.haltingAlign(CodeAlignment);
+    if (masm.jumpSites()[JumpTarget::Throw].empty())
+        return true;
+
+    BindJumps(masm, JumpTarget::Throw);
+
     Offsets offsets;
     offsets.begin = masm.currentOffset();
-    masm.bind(throwLabel);
 
     // We are about to pop all frames in this WasmActivation. Set fp to null to
     // maintain the invariant that fp is either null or pointing to a valid
     // frame.
     Register scratch = ABIArgGenerator::NonArgReturnReg0;
     masm.loadWasmActivation(scratch);
     masm.storePtr(ImmWord(0), Address(scratch, WasmActivation::offsetOfFP()));
 
@@ -1070,62 +1071,44 @@ GenerateThrowStub(ModuleGenerator& mg, L
     if (masm.oom())
         return false;
 
     offsets.end = masm.currentOffset();
     return mg.defineInlineStub(offsets);
 }
 
 bool
-wasm::GenerateStubs(ModuleGenerator& mg, bool usesHeap)
+wasm::GenerateStubs(ModuleGenerator& mg)
 {
     for (unsigned i = 0; i < mg.numExports(); i++) {
-        if (!GenerateEntry(mg, i, usesHeap))
+        if (!GenerateEntry(mg, i))
             return false;
     }
 
-    Label onThrow;
-
     for (size_t i = 0; i < mg.numImports(); i++) {
         ProfilingOffsets interp;
-        if (!GenerateInterpExitStub(mg, i, &onThrow, &interp))
+        if (!GenerateInterpExitStub(mg, i, &interp))
             return false;
 
         ProfilingOffsets jit;
-        if (!GenerateJitExitStub(mg, i, usesHeap, &onThrow, &jit))
+        if (!GenerateJitExitStub(mg, i, &jit))
             return false;
 
         if (!mg.defineImport(i, interp, jit))
             return false;
     }
 
-    if (mg.masm().asmStackOverflowLabel()->used()) {
-        if (!GenerateStackOverflowStub(mg, &onThrow))
-            return false;
-    }
+    if (!GenerateStackOverflowStub(mg))
+        return false;
 
-    if (mg.masm().asmSyncInterruptLabel()->used()) {
-        if (!GenerateSyncInterruptStub(mg, &onThrow))
-            return false;
-    }
-
-    if (mg.masm().asmOnConversionErrorLabel()->used()) {
-        if (!GenerateConversionErrorStub(mg, &onThrow))
-            return false;
-    }
-
-    // Generate unconditionally: the out-of-bounds exit may be used later even
-    // if signal handling isn't used for out-of-bounds at the moment.
-    if (!GenerateOutOfBoundsStub(mg, &onThrow))
+    if (!GenerateConversionErrorStub(mg))
         return false;
 
-    // Generate unconditionally: the async interrupt may be taken at any time.
-    if (!GenerateAsyncInterruptStub(mg, &onThrow))
+    if (!GenerateOutOfBoundsStub(mg))
         return false;
 
-    if (onThrow.used()) {
-        if (!GenerateThrowStub(mg, &onThrow))
-            return false;
-    }
+    if (!GenerateInterruptStub(mg))
+        return false;
 
-    return true;
+    // The throw stub must go last since the other stubs use it.
+    return GenerateThrowStub(mg);
 }
 
--- a/js/src/asmjs/WasmStubs.h
+++ b/js/src/asmjs/WasmStubs.h
@@ -20,14 +20,14 @@
 #define wasm_stubs_h
 
 #include "asmjs/WasmGenerator.h"
 
 namespace js {
 namespace wasm {
 
 bool
-GenerateStubs(ModuleGenerator& mg, bool usesHeap);
+GenerateStubs(ModuleGenerator& mg);
 
 } // namespace wasm
 } // namespace js
 
 #endif // wasm_stubs_h
--- a/js/src/asmjs/WasmTypes.h
+++ b/js/src/asmjs/WasmTypes.h
@@ -15,16 +15,17 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_types_h
 #define wasm_types_h
 
 #include "mozilla/DebugOnly.h"
+#include "mozilla/EnumeratedArray.h"
 #include "mozilla/HashFunctions.h"
 #include "mozilla/Move.h"
 
 #include "NamespaceImports.h"
 
 #include "ds/LifoAlloc.h"
 #include "jit/IonTypes.h"
 #include "js/UniquePtr.h"
@@ -551,16 +552,32 @@ enum class SymbolicAddress
     CoerceInPlace_ToInt32,
     CoerceInPlace_ToNumber,
     Limit
 };
 
 void*
 AddressOf(SymbolicAddress imm, ExclusiveContext* cx);
 
+// A wasm::JumpTarget represents one of a special set of stubs that can be
+// jumped to from any function. Because wasm modules can be larger than the
+// range of a plain jump, these potentially out-of-range jumps must be recorded
+// and patched specially by the MacroAssembler and ModuleGenerator.
+
+enum class JumpTarget
+{
+    StackOverflow,
+    OutOfBounds,
+    ConversionError,
+    Throw,
+    Limit
+};
+
+typedef mozilla::EnumeratedArray<JumpTarget, JumpTarget::Limit, Uint32Vector> JumpSiteArray;
+
 // The CompileArgs struct captures global parameters that affect all wasm code
 // generation. It also currently is the single source of truth for whether or
 // not to use signal handlers for different purposes.
 
 struct CompileArgs
 {
     bool useSignalHandlersForOOB;
     bool useSignalHandlersForInterrupt;
--- a/js/src/builtin/TestingFunctions.cpp
+++ b/js/src/builtin/TestingFunctions.cpp
@@ -3171,16 +3171,25 @@ GetModuleEnvironmentValue(JSContext* cx,
     RootedString name(cx, args[1].toString());
     RootedId id(cx);
     if (!JS_StringToId(cx, name, &id))
         return false;
 
     return GetProperty(cx, env, env, id, args.rval());
 }
 
+static bool
+EnableMatchFlagArgument(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    cx->runtime()->options().setMatchFlagArgument(true);
+    args.rval().setUndefined();
+    return true;
+}
+
 static const JSFunctionSpecWithHelp TestingFunctions[] = {
     JS_FN_HELP("gc", ::GC, 0, 0,
 "gc([obj] | 'compartment' [, 'shrinking'])",
 "  Run the garbage collector. When obj is given, GC only its compartment.\n"
 "  If 'compartment' is given, GC any compartments that were scheduled for\n"
 "  GC via schedulegc.\n"
 "  If 'shrinking' is passed as the optional second argument, perform a\n"
 "  shrinking GC rather than a normal GC."),
@@ -3656,16 +3665,21 @@ gc::ZealModeHelpText),
     JS_FN_HELP("getModuleEnvironmentNames", GetModuleEnvironmentNames, 1, 0,
 "getModuleEnvironmentNames(module)",
 "  Get the list of a module environment's bound names for a specified module.\n"),
 
     JS_FN_HELP("getModuleEnvironmentValue", GetModuleEnvironmentValue, 2, 0,
 "getModuleEnvironmentValue(module, name)",
 "  Get the value of a bound name in a module environment.\n"),
 
+    JS_FN_HELP("enableMatchFlagArgument", EnableMatchFlagArgument, 0, 0,
+"enableMatchFlagArgument()",
+"  Enables the deprecated, non-standard flag argument of\n"
+"  String.prototype.{match,search,replace}.\n"),
+
     JS_FS_HELP_END
 };
 
 static const JSPropertySpec TestingProperties[] = {
     JS_PSG("timesAccessed", TimesAccessed, 0),
     JS_PS_END
 };
 
--- a/js/src/jit-test/tests/asm.js/testExpressions.js
+++ b/js/src/jit-test/tests/asm.js/testExpressions.js
@@ -257,16 +257,17 @@ assertEq(asmLink(asmCompile(USE_ASM + "f
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (3 / 2)|0 } return f"))(), 1);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (4 % 2)|0 } return f"))(), 0);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (3 % 2)|0 } return f"))(), 1);
 
 assertAsmTypeFail(USE_ASM + "function f() { var i=42,j=1.1; return +(i?i:j) } return f");
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { return 0; 1 ? 1 : 1; return 0; } return f"))(), 0);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42,j=1.1; return +(i?+(i|0):j) } return f"))(), 42);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42,j=1; return (i?i:j)|0 } return f"))(), 42);
+assertEq(asmLink(asmCompile(USE_ASM + "function f() { var i=42,j=1; return 13; return (i?i:j)|0 } return f"))(), 13);
 
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { return (0 > (-(~~1) >>> 0)) | 0; } return f"))(), 0);
 assertEq(asmLink(asmCompile(USE_ASM + "function f() { return 0 < 4294967294 | 0; } return f"))(), 1);
 
 var f = asmLink(asmCompile(USE_ASM + "function f(i,j) { i=i|0;j=j|0; return ((i|0)>(j|0)?(i+10)|0:(j+100)|0)|0 } return f"));
 assertEq(f(2, 4), 104);
 assertEq(f(-2, -4), 8);
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/bug1122581.js
@@ -0,0 +1,10 @@
+function f(x, y) {
+    for (var i=0; i<50; i++) {
+	if (i % 10 === 0) {
+	    var stack = getBacktrace({args: true, locals: true, thisprops: true});
+	    assertEq(stack.includes("f(x = "), true);
+	    foo = arguments;
+	}
+    }
+}
+f(1, 2);
--- a/js/src/jit-test/tests/basic/bug593663-regexp.js
+++ b/js/src/jit-test/tests/basic/bug593663-regexp.js
@@ -7,16 +7,18 @@ function isPatternSyntaxError(pattern) {
     try {
         new RegExp(pattern);
         return false;
     } catch (e if e instanceof SyntaxError) {
         return true;
     }
 }
 
+enableMatchFlagArgument();
+
 // Bug example.
 assertEq("1+2".replace("1+2", "$&+3", "g"), "1+2+3");
 assertEq("1112".replace("1+2", "", "g"), "1112");
 
 // ^
 assertEq("leading text^my hat".replace("^my hat", "", "g"), "leading text");
 assertEq("my hat".replace("^my hat", "", "g"), "my hat");
 
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/basic/evalInWorker-interrupt.js
@@ -0,0 +1,3 @@
+if (helperThreadCount() === 0)
+    quit();
+evalInWorker("setInterruptCallback(function() {}); timeout(1000);");
--- a/js/src/jit-test/tests/ion/bug977966.js
+++ b/js/src/jit-test/tests/ion/bug977966.js
@@ -96,16 +96,18 @@ function split_join_pattern(i) {
     var s = i + "-" + i;
     assertEq(s.split("-").join("$`$&$'"), i + "$`$&$'" + i);
     assertEq(s.replace("-", "$`$&$'"), "" + i + i + "-" + i + i);
 }
 
 // Check that, as opposed to String.replace, we are doing a global replacement
 // as String.split does.
 function split_join_multiple(i) {
+    enableMatchFlagArgument();
+
     var s1 = i + "-\n-" + i + "-\n-" + i;
     assertEq(s1.split("-\n-").join("-")  , i + "-" + i + "-" + i);
     assertEq(s1.replace("-\n-", "-")     , i + "-" + i + "-\n-" + i);
     // SpiderMonkey extension
     assertEq(s1.replace("-\n-", "-", "g"), i + "-" + i + "-" + i);
 
     var s2 = "abc";
     assertEq(s2.split("").join("" + i)   , "a" + i + "b" + i + "c");
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/ion/fold-in.js
@@ -0,0 +1,32 @@
+// Singleton
+function f() {
+    var res = 0;
+    for (var i=0; i<500; i++)
+	res += ("abcd" in Math);
+    return res;
+}
+assertEq(f(), 0);
+Math.abcd = 3;
+assertEq(f(), 500);
+delete Math.abcd;
+assertEq(f(), 0);
+
+// Non-singleton
+function O(x) { if (x) this.x = 1; }
+
+var arr = [];
+for (var i=0; i<4; i++)
+    arr.push(new O(i % 2));
+
+function g(arr) {
+    var res = 0;
+    for (var i=0; i<500; i++) {
+	var o = arr[i % arr.length];
+	res += "x" in o;
+	res += "abcd" in o;
+    }
+    return res;
+}
+assertEq(g(arr), 250);
+arr[0].abcd = 3;
+assertEq(g(arr), 375);
--- a/js/src/jit/BaselineJIT.cpp
+++ b/js/src/jit/BaselineJIT.cpp
@@ -1184,21 +1184,22 @@ jit::ToggleBaselineTraceLoggerEngine(JSR
 static void
 MarkActiveBaselineScripts(JSRuntime* rt, const JitActivationIterator& activation)
 {
     for (jit::JitFrameIterator iter(activation); !iter.done(); ++iter) {
         switch (iter.type()) {
           case JitFrame_BaselineJS:
             iter.script()->baselineScript()->setActive();
             break;
-          case JitFrame_LazyLink: {
-            LazyLinkExitFrameLayout* ll = iter.exitFrame()->as<LazyLinkExitFrameLayout>();
-            ScriptFromCalleeToken(ll->jsFrame()->calleeToken())->baselineScript()->setActive();
+          case JitFrame_Exit:
+            if (iter.exitFrame()->is<LazyLinkExitFrameLayout>()) {
+                LazyLinkExitFrameLayout* ll = iter.exitFrame()->as<LazyLinkExitFrameLayout>();
+                ScriptFromCalleeToken(ll->jsFrame()->calleeToken())->baselineScript()->setActive();
+            }
             break;
-          }
           case JitFrame_Bailout:
           case JitFrame_IonJS: {
             // Keep the baseline script around, since bailouts from the ion
             // jitcode might need to re-enter into the baseline jitcode.
             iter.script()->baselineScript()->setActive();
             for (InlineFrameIterator inlineIter(rt, &iter); inlineIter.more(); ++inlineIter)
                 inlineIter.script()->baselineScript()->setActive();
             break;
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -8283,37 +8283,39 @@ CodeGenerator::generateAsmJS(wasm::FuncO
 
     wasm::GenerateFunctionPrologue(masm, frameSize(), offsets);
 
     // Overflow checks are omitted by CodeGenerator in some cases (leaf
     // functions with small framePushed). Perform overflow-checking after
     // pushing framePushed to catch cases with really large frames.
     Label onOverflow;
     if (!omitOverRecursedCheck()) {
-        // See comment below.
-        Label* target = frameSize() > 0 ? &onOverflow : masm.asmStackOverflowLabel();
         masm.branchPtr(Assembler::AboveOrEqual,
                        wasm::SymbolicAddress::StackLimit,
                        masm.getStackPointer(),
-                       target);
+                       &onOverflow);
     }
 
     if (!generateBody())
         return false;
 
     masm.bind(&returnLabel_);
     wasm::GenerateFunctionEpilogue(masm, frameSize(), offsets);
 
-    if (onOverflow.used()) {
-        // The stack overflow stub assumes that only sizeof(AsmJSFrame) bytes have
-        // been pushed. The overflow check occurs after incrementing by
+    if (!omitOverRecursedCheck()) {
+        // The stack overflow stub assumes that only sizeof(AsmJSFrame) bytes
+        // have been pushed. The overflow check occurs after incrementing by
         // framePushed, so pop that before jumping to the overflow exit.
-        masm.bind(&onOverflow);
-        masm.addToStackPtr(Imm32(frameSize()));
-        masm.jump(masm.asmStackOverflowLabel());
+        if (frameSize() > 0) {
+            masm.bind(&onOverflow);
+            masm.addToStackPtr(Imm32(frameSize()));
+            masm.jump(wasm::JumpTarget::StackOverflow);
+        } else {
+            masm.bindLater(&onOverflow, wasm::JumpTarget::StackOverflow);
+        }
     }
 
 #if defined(JS_ION_PERF)
     // Note the end of the inline code and start of the OOL code.
     gen->perfSpewer().noteEndInlineCode(masm);
 #endif
 
     if (!generateOutOfLineCode())
@@ -10605,23 +10607,21 @@ CodeGenerator::visitInterruptCheck(LInte
 void
 CodeGenerator::visitAsmJSInterruptCheck(LAsmJSInterruptCheck* lir)
 {
     Label rejoin;
     masm.branch32(Assembler::Equal,
                   wasm::SymbolicAddress::RuntimeInterruptUint32,
                   Imm32(0),
                   &rejoin);
-    {
-        uint32_t stackFixup = ComputeByteAlignment(masm.framePushed() + sizeof(AsmJSFrame),
-                                                   ABIStackAlignment);
-        masm.reserveStack(stackFixup);
-        masm.call(lir->funcDesc(), lir->interruptExit());
-        masm.freeStack(stackFixup);
-    }
+
+    MOZ_ASSERT((sizeof(AsmJSFrame) + masm.framePushed()) % ABIStackAlignment == 0);
+    masm.call(wasm::SymbolicAddress::HandleExecutionInterrupt);
+    masm.branchIfFalseBool(ReturnReg, wasm::JumpTarget::Throw);
+
     masm.bind(&rejoin);
 }
 
 typedef bool (*RecompileFn)(JSContext*);
 static const VMFunction RecompileFnInfo = FunctionInfo<RecompileFn>(Recompile);
 
 typedef bool (*ForcedRecompileFn)(JSContext*);
 static const VMFunction ForcedRecompileFnInfo = FunctionInfo<ForcedRecompileFn>(ForcedRecompile);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -547,37 +547,16 @@ FinishAllOffThreadCompilations(JSCompart
         IonBuilder* builder = finished[i];
         if (builder->compartment == CompileCompartment::get(comp)) {
             FinishOffThreadBuilder(nullptr, builder);
             HelperThreadState().remove(finished, &i);
         }
     }
 }
 
-class MOZ_RAII AutoLazyLinkExitFrame
-{
-    JitActivation* jitActivation_;
-    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
-
-  public:
-    explicit AutoLazyLinkExitFrame(JitActivation* jitActivation
-                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
-      : jitActivation_(jitActivation)
-    {
-        MOZ_GUARD_OBJECT_NOTIFIER_INIT;
-        MOZ_ASSERT(!jitActivation_->isLazyLinkExitFrame(),
-                   "Cannot stack multiple lazy-link frames.");
-        jitActivation_->setLazyLinkExitFrame(true);
-    }
-
-    ~AutoLazyLinkExitFrame() {
-        jitActivation_->setLazyLinkExitFrame(false);
-    }
-};
-
 static bool
 LinkCodeGen(JSContext* cx, IonBuilder* builder, CodeGenerator *codegen,
             MutableHandle<ScriptVector> scripts, OnIonCompilationInfo* info)
 {
     RootedScript script(cx, builder->script());
     TraceLoggerThread* logger = TraceLoggerForMainThread(cx->runtime());
     TraceLoggerEvent event(logger, TraceLogger_AnnotateScripts, script);
     AutoTraceLog logScript(logger, event);
@@ -649,21 +628,18 @@ jit::LazyLink(JSContext* cx, HandleScrip
 
     if (info.filled())
         Debugger::onIonCompilation(cx, debugScripts, info.graph);
 }
 
 uint8_t*
 jit::LazyLinkTopActivation(JSContext* cx)
 {
-    JitActivationIterator iter(cx->runtime());
-    AutoLazyLinkExitFrame lazyLinkExitFrame(iter->asJit());
-
     // First frame should be an exit frame.
-    JitFrameIterator it(iter);
+    JitFrameIterator it(cx);
     LazyLinkExitFrameLayout* ll = it.exitFrame()->as<LazyLinkExitFrameLayout>();
     RootedScript calleeScript(cx, ScriptFromCalleeToken(ll->jsFrame()->calleeToken()));
 
     LazyLink(cx, calleeScript);
 
     MOZ_ASSERT(calleeScript->hasBaselineScript());
     MOZ_ASSERT(calleeScript->baselineOrIonRawPointer());
 
@@ -2972,17 +2948,16 @@ InvalidateActivation(FreeOp* fop, const 
     size_t frameno = 1;
 
     for (JitFrameIterator it(activations); !it.done(); ++it, ++frameno) {
         MOZ_ASSERT_IF(frameno == 1, it.isExitFrame() || it.type() == JitFrame_Bailout);
 
 #ifdef JS_JITSPEW
         switch (it.type()) {
           case JitFrame_Exit:
-          case JitFrame_LazyLink:
             JitSpew(JitSpew_IonInvalidate, "#%d exit frame @ %p", frameno, it.fp());
             break;
           case JitFrame_BaselineJS:
           case JitFrame_IonJS:
           case JitFrame_Bailout:
           {
             MOZ_ASSERT(it.isScripted());
             const char* type = "Unknown";
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -195,24 +195,24 @@ FlagAllOperandsAsHavingRemovedUses(MBasi
                     continue;
                 rp->getOperand(i)->setUseRemovedUnchecked();
             }
         }
     }
 
     // Flag observable operands of the entry resume point as having removed uses.
     MResumePoint* rp = block->entryResumePoint();
-    do {
+    while (rp) {
         for (size_t i = 0, e = rp->numOperands(); i < e; i++) {
             if (!rp->isObservableOperand(i))
                 continue;
             rp->getOperand(i)->setUseRemovedUnchecked();
         }
         rp = rp->caller();
-    } while (rp);
+    }
 
     // Flag Phi inputs of the successors has having removed uses.
     MPhiVector worklist;
     for (size_t i = 0, e = block->numSuccessors(); i < e; i++) {
         if (!FlagPhiInputsAsHavingRemovedUses(block, block->getSuccessor(i), worklist))
             return false;
     }
 
@@ -1916,16 +1916,27 @@ bool
 jit::RemoveUnmarkedBlocks(MIRGenerator* mir, MIRGraph& graph, uint32_t numMarkedBlocks)
 {
     if (numMarkedBlocks == graph.numBlocks()) {
         // If all blocks are marked, no blocks need removal. Just clear the
         // marks. We'll still need to update the dominator tree below though,
         // since we may have removed edges even if we didn't remove any blocks.
         graph.unmarkBlocks();
     } else {
+        // As we are going to remove edges and basic blocks, we have to mark
+        // instructions which would be needed by baseline if we were to
+        // bailout.
+        for (PostorderIterator it(graph.poBegin()); it != graph.poEnd();) {
+            MBasicBlock* block = *it++;
+            if (!block->isMarked())
+                continue;
+
+            FlagAllOperandsAsHavingRemovedUses(block);
+        }
+
         // Find unmarked blocks and remove them.
         for (ReversePostorderIterator iter(graph.rpoBegin()); iter != graph.rpoEnd();) {
             MBasicBlock* block = *iter++;
 
             if (block->isMarked()) {
                 block->unmark();
                 continue;
             }
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -13314,43 +13314,51 @@ IonBuilder::jsop_setaliasedvar(ScopeCoor
 }
 
 bool
 IonBuilder::jsop_in()
 {
     MDefinition* obj = convertUnboxedObjects(current->pop());
     MDefinition* id = current->pop();
 
-    do {
-        if (shouldAbortOnPreliminaryGroups(obj))
-            break;
-
-        JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id);
-        if (unboxedType == JSVAL_TYPE_MAGIC) {
-            if (!ElementAccessIsDenseNative(constraints(), obj, id))
-                break;
-        }
-
-        if (ElementAccessHasExtraIndexedProperty(this, obj))
-            break;
-
-        return jsop_in_dense(obj, id, unboxedType);
-    } while (false);
+    bool emitted = false;
+
+    if (!inTryDense(&emitted, obj, id) || emitted)
+        return emitted;
+
+    if (!inTryFold(&emitted, obj, id) || emitted)
+        return emitted;
 
     MIn* ins = MIn::New(alloc(), id, obj);
 
     current->add(ins);
     current->push(ins);
 
     return resumeAfter(ins);
 }
 
 bool
-IonBuilder::jsop_in_dense(MDefinition* obj, MDefinition* id, JSValueType unboxedType)
-{
+IonBuilder::inTryDense(bool* emitted, MDefinition* obj, MDefinition* id)
+{
+    MOZ_ASSERT(!*emitted);
+
+    if (shouldAbortOnPreliminaryGroups(obj))
+        return true;
+
+    JSValueType unboxedType = UnboxedArrayElementType(constraints(), obj, id);
+    if (unboxedType == JSVAL_TYPE_MAGIC) {
+        if (!ElementAccessIsDenseNative(constraints(), obj, id))
+            return true;
+    }
+
+    if (ElementAccessHasExtraIndexedProperty(this, obj))
+        return true;
+
+    *emitted = true;
+
     bool needsHoleCheck = !ElementAccessIsPacked(constraints(), obj);
 
     // Ensure id is an integer.
     MInstruction* idInt32 = MToInt32::New(alloc(), id);
     current->add(idInt32);
     id = idInt32;
 
     // Get the elements vector.
@@ -13371,16 +13379,77 @@ IonBuilder::jsop_in_dense(MDefinition* o
 
     current->add(ins);
     current->push(ins);
 
     return true;
 }
 
 bool
+IonBuilder::inTryFold(bool* emitted, MDefinition* obj, MDefinition* id)
+{
+    // Fold |id in obj| to |false|, if we know the object (or an object on its
+    // prototype chain) does not have this property.
+
+    MOZ_ASSERT(!*emitted);
+
+    jsid propId;
+    if (!id->isConstantValue() || !ValueToIdPure(id->constantValue(), &propId))
+        return true;
+
+    if (propId != IdToTypeId(propId))
+        return true;
+
+    TemporaryTypeSet* types = obj->resultTypeSet();
+    if (!types || types->unknownObject())
+        return true;
+
+    for (unsigned i = 0, count = types->getObjectCount(); i < count; i++) {
+        TypeSet::ObjectKey* key = types->getObject(i);
+        if (!key)
+            continue;
+
+        while (true) {
+            if (!key->hasStableClassAndProto(constraints()) || key->unknownProperties())
+                return true;
+
+            const Class* clasp = key->clasp();
+            if (!ClassHasEffectlessLookup(clasp) || ObjectHasExtraOwnProperty(compartment, key, propId))
+                return true;
+
+            // If the object is a singleton, we can do a lookup now to avoid
+            // unnecessary invalidations later on, in case the property types
+            // have not yet been instantiated.
+            if (key->isSingleton() &&
+                key->singleton()->is<NativeObject>() &&
+                key->singleton()->as<NativeObject>().lookupPure(propId))
+            {
+                return true;
+            }
+
+            HeapTypeSetKey property = key->property(propId);
+            if (property.isOwnProperty(constraints()))
+                return true;
+
+            JSObject* proto = checkNurseryObject(key->proto().toObjectOrNull());
+            if (!proto)
+                break;
+            key = TypeSet::ObjectKey::get(proto);
+        }
+    }
+
+    *emitted = true;
+
+    pushConstant(BooleanValue(false));
+    obj->setImplicitlyUsedUnchecked();
+    id->setImplicitlyUsedUnchecked();
+    return true;
+}
+
+bool
 IonBuilder::hasOnProtoChain(TypeSet::ObjectKey* key, JSObject* protoObject, bool* hasOnProto)
 {
     MOZ_ASSERT(protoObject);
 
     while (true) {
         if (!key->hasStableClassAndProto(constraints()) || !key->clasp()->isNative())
             return false;
 
--- a/js/src/jit/IonBuilder.h
+++ b/js/src/jit/IonBuilder.h
@@ -497,23 +497,27 @@ class IonBuilder
     bool binaryArithTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
     bool binaryArithTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
                                                       MDefinition* right);
     bool arithTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
 
     // jsop_bitnot helpers.
     bool bitnotTrySpecialized(bool* emitted, MDefinition* input);
 
-    // jsop_compare helpes.
+    // jsop_compare helpers.
     bool compareTrySpecialized(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
     bool compareTryBitwise(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
     bool compareTrySpecializedOnBaselineInspector(bool* emitted, JSOp op, MDefinition* left,
                                                   MDefinition* right);
     bool compareTrySharedStub(bool* emitted, JSOp op, MDefinition* left, MDefinition* right);
 
+    // jsop_in helpers.
+    bool inTryDense(bool* emitted, MDefinition* obj, MDefinition* id);
+    bool inTryFold(bool* emitted, MDefinition* obj, MDefinition* id);
+
     // binary data lookup helpers.
     TypedObjectPrediction typedObjectPrediction(MDefinition* typedObj);
     TypedObjectPrediction typedObjectPrediction(TemporaryTypeSet* types);
     bool typedObjectHasField(MDefinition* typedObj,
                              PropertyName* name,
                              size_t* fieldOffset,
                              TypedObjectPrediction* fieldTypeReprs,
                              size_t* fieldIndex);
@@ -727,17 +731,16 @@ class IonBuilder
     bool jsop_globalthis();
     bool jsop_typeof();
     bool jsop_toid();
     bool jsop_iter(uint8_t flags);
     bool jsop_itermore();
     bool jsop_isnoiter();
     bool jsop_iterend();
     bool jsop_in();
-    bool jsop_in_dense(MDefinition* obj, MDefinition* id, JSValueType unboxedType);
     bool jsop_instanceof();
     bool jsop_getaliasedvar(ScopeCoordinate sc);
     bool jsop_setaliasedvar(ScopeCoordinate sc);
     bool jsop_debugger();
     bool jsop_newtarget();
     bool jsop_checkobjcoercible();
 
     /* Inlining. */
--- a/js/src/jit/JitFrameIterator.h
+++ b/js/src/jit/JitFrameIterator.h
@@ -55,23 +55,16 @@ enum FrameType
     // JitActivation.
     JitFrame_Exit,
 
     // A bailout frame is a special IonJS jit frame after a bailout, and before
     // the reconstruction of the BaselineJS frame. From within C++, a bailout
     // frame is always the last frame in a JitActivation iff the bailout frame
     // information is recorded on the JitActivation.
     JitFrame_Bailout,
-
-    // A lazy link frame is a special exit frame where a IonJS frame is reused
-    // for linking the newly compiled code.  A special frame is needed to
-    // work-around the fact that we can make stack patterns which are similar to
-    // unwound frames. As opposed to unwound frames, we still have to mark all
-    // the arguments of the original IonJS frame.
-    JitFrame_LazyLink
 };
 
 enum ReadFrameArgsBehavior {
     // Only read formals (i.e. [0 ... callee()->nargs]
     ReadFrame_Formals,
 
     // Only read overflown args (i.e. [callee()->nargs ... numActuals()]
     ReadFrame_Overflown,
@@ -136,17 +129,17 @@ class JitFrameIterator
     inline ExitFrameLayout* exitFrame() const;
 
     // Returns whether the JS frame has been invalidated and, if so,
     // places the invalidated Ion script in |ionScript|.
     bool checkInvalidation(IonScript** ionScript) const;
     bool checkInvalidation() const;
 
     bool isExitFrame() const {
-        return type_ == JitFrame_Exit || type_ == JitFrame_LazyLink;
+        return type_ == JitFrame_Exit;
     }
     bool isScripted() const {
         return type_ == JitFrame_BaselineJS || type_ == JitFrame_IonJS || type_ == JitFrame_Bailout;
     }
     bool isBaselineJS() const {
         return type_ == JitFrame_BaselineJS;
     }
     bool isIonScripted() const {
--- a/js/src/jit/JitFrames.cpp
+++ b/js/src/jit/JitFrames.cpp
@@ -113,37 +113,31 @@ JitFrameIterator::JitFrameIterator(JSCon
     frameSize_(0),
     cachedSafepointIndex_(nullptr),
     activation_(cx->runtime()->activation()->asJit())
 {
     if (activation_->bailoutData()) {
         current_ = activation_->bailoutData()->fp();
         frameSize_ = activation_->bailoutData()->topFrameSize();
         type_ = JitFrame_Bailout;
-    } else if (activation_->isLazyLinkExitFrame()) {
-        type_ = JitFrame_LazyLink;
-        MOZ_ASSERT(isExitFrameLayout<LazyLinkExitFrameLayout>());
     }
 }
 
 JitFrameIterator::JitFrameIterator(const ActivationIterator& activations)
   : current_(activations.jitTop()),
     type_(JitFrame_Exit),
     returnAddressToFp_(nullptr),
     frameSize_(0),
     cachedSafepointIndex_(nullptr),
     activation_(activations->asJit())
 {
     if (activation_->bailoutData()) {
         current_ = activation_->bailoutData()->fp();
         frameSize_ = activation_->bailoutData()->topFrameSize();
         type_ = JitFrame_Bailout;
-    } else if (activation_->isLazyLinkExitFrame()) {
-        type_ = JitFrame_LazyLink;
-        MOZ_ASSERT(isExitFrameLayout<LazyLinkExitFrameLayout>());
     }
 }
 
 bool
 JitFrameIterator::checkInvalidation() const
 {
     IonScript* dummy;
     return checkInvalidation(&dummy);
@@ -1414,17 +1408,16 @@ MarkJitActivation(JSTracer* trc, const J
 #endif
 
     activation->markRematerializedFrames(trc);
     activation->markIonRecovery(trc);
 
     for (JitFrameIterator frames(activations); !frames.done(); ++frames) {
         switch (frames.type()) {
           case JitFrame_Exit:
-          case JitFrame_LazyLink:
             MarkJitExitFrame(trc, frames);
             break;
           case JitFrame_BaselineJS:
             frames.baselineFrame()->trace(trc, frames);
             break;
           case JitFrame_IonJS:
             MarkIonJSFrame(trc, frames);
             break;
@@ -2686,19 +2679,16 @@ JitFrameIterator::dump() const
         break;
       case JitFrame_IonAccessorIC:
         fprintf(stderr, " Ion scripted accessor IC\n");
         fprintf(stderr, "  Frame size: %u\n", unsigned(current()->prevFrameLocalSize()));
         break;
       case JitFrame_Exit:
         fprintf(stderr, " Exit frame\n");
         break;
-      case JitFrame_LazyLink:
-        fprintf(stderr, " Lazy link frame\n");
-        break;
     };
     fputc('\n', stderr);
 }
 
 #ifdef DEBUG
 bool
 JitFrameIterator::verifyReturnAddressUsingNativeToBytecodeMap()
 {
--- a/js/src/jit/Lowering.cpp
+++ b/js/src/jit/Lowering.cpp
@@ -2360,20 +2360,17 @@ LIRGenerator::visitInterruptCheck(MInter
     add(lir, ins);
     assignSafepoint(lir, ins);
 }
 
 void
 LIRGenerator::visitAsmJSInterruptCheck(MAsmJSInterruptCheck* ins)
 {
     gen->setPerformsCall();
-
-    LAsmJSInterruptCheck* lir = new(alloc()) LAsmJSInterruptCheck(ins->interruptExit(),
-                                                                  ins->funcDesc());
-    add(lir, ins);
+    add(new(alloc()) LAsmJSInterruptCheck, ins);
 }
 
 void
 LIRGenerator::visitStoreSlot(MStoreSlot* ins)
 {
     LInstruction* lir;
 
     switch (ins->value()->type()) {
--- a/js/src/jit/MIR.h
+++ b/js/src/jit/MIR.h
@@ -7365,36 +7365,20 @@ class MInterruptCheck : public MNullaryI
 };
 
 // Check whether we need to fire the interrupt handler at loop headers and
 // function prologues in asm.js. Generated only if we can't use implicit
 // interrupt checks with signal handlers.
 class MAsmJSInterruptCheck
   : public MNullaryInstruction
 {
-    Label* interruptExit_;
-    wasm::CallSiteDesc funcDesc_;
-
-    MAsmJSInterruptCheck(Label* interruptExit, const wasm::CallSiteDesc& funcDesc)
-      : interruptExit_(interruptExit), funcDesc_(funcDesc)
-    {}
-
   public:
     INSTRUCTION_HEADER(AsmJSInterruptCheck)
-
-    static MAsmJSInterruptCheck* New(TempAllocator& alloc, Label* interruptExit,
-                                     const wasm::CallSiteDesc& funcDesc)
-    {
-        return new(alloc) MAsmJSInterruptCheck(interruptExit, funcDesc);
-    }
-    Label* interruptExit() const {
-        return interruptExit_;
-    }
-    const wasm::CallSiteDesc& funcDesc() const {
-        return funcDesc_;
+    static MAsmJSInterruptCheck* New(TempAllocator& alloc) {
+        return new(alloc) MAsmJSInterruptCheck;
     }
 };
 
 // Checks if a value is JS_UNINITIALIZED_LEXICAL, bailout out if so, leaving
 // it to baseline to throw at the correct pc.
 class MLexicalCheck
   : public MUnaryInstruction,
     public BoxPolicy<0>::Data
--- a/js/src/jit/MacroAssembler.cpp
+++ b/js/src/jit/MacroAssembler.cpp
@@ -2000,31 +2000,16 @@ MacroAssembler::convertTypedOrValueToInt
       case MIRType_Object:
         jump(fail);
         break;
       default:
         MOZ_CRASH("Bad MIRType");
     }
 }
 
-bool
-MacroAssembler::asmMergeWith(MacroAssembler& other)
-{
-    size_t sizeBeforeMerge = size();
-
-    if (!MacroAssemblerSpecific::asmMergeWith(other))
-        return false;
-
-    retargetWithOffset(sizeBeforeMerge, other.asmSyncInterruptLabel(), asmSyncInterruptLabel());
-    retargetWithOffset(sizeBeforeMerge, other.asmStackOverflowLabel(), asmStackOverflowLabel());
-    retargetWithOffset(sizeBeforeMerge, other.asmOnOutOfBoundsLabel(), asmOnOutOfBoundsLabel());
-    retargetWithOffset(sizeBeforeMerge, other.asmOnConversionErrorLabel(), asmOnConversionErrorLabel());
-    return true;
-}
-
 void
 MacroAssembler::finish()
 {
     if (failureLabel_.used()) {
         bind(&failureLabel_);
         handleFailure();
     }
 
--- a/js/src/jit/MacroAssembler.h
+++ b/js/src/jit/MacroAssembler.h
@@ -334,22 +334,16 @@ class MacroAssembler : public MacroAssem
     mozilla::Maybe<AutoRooter> autoRooter_;
     mozilla::Maybe<JitContext> jitContext_;
     mozilla::Maybe<AutoJitContextAlloc> alloc_;
 
   private:
     // Labels for handling exceptions and failures.
     NonAssertingLabel failureLabel_;
 
-    // Asm failure labels
-    NonAssertingLabel asmStackOverflowLabel_;
-    NonAssertingLabel asmSyncInterruptLabel_;
-    NonAssertingLabel asmOnConversionErrorLabel_;
-    NonAssertingLabel asmOnOutOfBoundsLabel_;
-
   public:
     MacroAssembler()
       : framePushed_(0),
 #ifdef DEBUG
         inCall_(false),
 #endif
         emitProfilingInstrumentation_(false)
     {
@@ -505,16 +499,22 @@ class MacroAssembler : public MacroAssem
 
     inline void call(const wasm::CallSiteDesc& desc, const Register reg);
     inline void call(const wasm::CallSiteDesc& desc, Label* label);
     inline void call(const wasm::CallSiteDesc& desc, AsmJSInternalCallee callee);
 
     CodeOffset callWithPatch() PER_SHARED_ARCH;
     void patchCall(uint32_t callerOffset, uint32_t calleeOffset) PER_SHARED_ARCH;
 
+    // Thunks provide the ability to jump to any uint32_t offset from any other
+    // uint32_t offset without using a constant pool (thus returning a simple
+    // CodeOffset instead of a CodeOffsetJump).
+    CodeOffset thunkWithPatch() PER_SHARED_ARCH;
+    void patchThunk(uint32_t thunkOffset, uint32_t targetOffset) PER_SHARED_ARCH;
+
     // Push the return address and make a call. On platforms where this function
     // is not defined, push the link register (pushReturnAddress) at the entry
     // point of the callee.
     void callAndPushReturnAddress(Register reg) DEFINED_ON(mips_shared, x86_shared);
     void callAndPushReturnAddress(Label* label) DEFINED_ON(mips_shared, x86_shared);
 
     void pushReturnAddress() DEFINED_ON(arm, arm64);
 
@@ -865,17 +865,18 @@ class MacroAssembler : public MacroAssem
           case MIRType_MagicIsConstructing:
           case MIRType_MagicHole: return branchTestMagic(cond, val, label);
           default:
             MOZ_CRASH("Bad MIRType");
         }
     }
 
     // Branches to |label| if |reg| is false. |reg| should be a C++ bool.
-    void branchIfFalseBool(Register reg, Label* label) {
+    template <class L>
+    void branchIfFalseBool(Register reg, L label) {
         // Note that C++ bool is only 1 byte, so ignore the higher-order bits.
         branchTest32(Assembler::Zero, reg, Imm32(0xFF), label);
     }
 
     // Branches to |label| if |reg| is true. |reg| should be a C++ bool.
     void branchIfTrueBool(Register reg, Label* label) {
         // Note that C++ bool is only 1 byte, so ignore the higher-order bits.
         branchTest32(Assembler::NonZero, reg, Imm32(0xFF), label);
@@ -1404,42 +1405,16 @@ class MacroAssembler : public MacroAssem
         // Exceptions are currently handled the same way as sequential failures.
         return &failureLabel_;
     }
 
     Label* failureLabel() {
         return &failureLabel_;
     }
 
-    Label* asmSyncInterruptLabel() {
-        return &asmSyncInterruptLabel_;
-    }
-    const Label* asmSyncInterruptLabel() const {
-        return &asmSyncInterruptLabel_;
-    }
-    Label* asmStackOverflowLabel() {
-        return &asmStackOverflowLabel_;
-    }
-    const Label* asmStackOverflowLabel() const {
-        return &asmStackOverflowLabel_;
-    }
-    Label* asmOnOutOfBoundsLabel() {
-        return &asmOnOutOfBoundsLabel_;
-    }
-    const Label* asmOnOutOfBoundsLabel() const {
-        return &asmOnOutOfBoundsLabel_;
-    }
-    Label* asmOnConversionErrorLabel() {
-        return &asmOnConversionErrorLabel_;
-    }
-    const Label* asmOnConversionErrorLabel() const {
-        return &asmOnConversionErrorLabel_;
-    }
-
-    bool asmMergeWith(MacroAssembler& masm);
     void finish();
     void link(JitCode* code);
 
     void assumeUnreachable(const char* output);
 
     template<typename T>
     void assertTestInt32(Condition cond, const T& value, const char* output);
 
--- a/js/src/jit/ValueNumbering.cpp
+++ b/js/src/jit/ValueNumbering.cpp
@@ -456,16 +456,17 @@ ValueNumberer::fixupOSROnlyLoop(MBasicBl
     if (!block->addPredecessorWithoutPhis(fake))
         return false;
 
     // Restore |backedge| as |block|'s loop backedge.
     block->clearLoopHeader();
     block->setLoopHeader(backedge);
 
     JitSpew(JitSpew_GVN, "        Created fake block%u", fake->id());
+    hasOSRFixups_ = true;
     return true;
 }
 
 // Remove the CFG edge between |pred| and |block|, after releasing the phi
 // operands on that edge and discarding any definitions consequently made dead.
 bool
 ValueNumberer::removePredecessorAndDoDCE(MBasicBlock* block, MBasicBlock* pred, size_t predIndex)
 {
@@ -1070,27 +1071,74 @@ ValueNumberer::visitGraph()
             // This block a dominator tree root. Proceed to the next one.
             ++iter;
         }
     }
     totalNumVisited_ = 0;
     return true;
 }
 
+// OSR fixups serve the purpose of representing the non-OSR entry into a loop
+// when the only real entry is an OSR entry into the middle. However, if the
+// entry into the middle is subsequently folded away, the loop may actually
+// have become unreachable. Mark-and-sweep all blocks to remove all such code.
+bool ValueNumberer::cleanupOSRFixups()
+{
+    // Mark.
+    Vector<MBasicBlock*, 0, JitAllocPolicy> worklist(graph_.alloc());
+    unsigned numMarked = 2;
+    graph_.entryBlock()->mark();
+    graph_.osrBlock()->mark();
+    if (!worklist.append(graph_.entryBlock()) || !worklist.append(graph_.osrBlock()))
+        return false;
+    while (!worklist.empty()) {
+        MBasicBlock* block = worklist.popCopy();
+        for (size_t i = 0, e = block->numSuccessors(); i != e; ++i) {
+            MBasicBlock* succ = block->getSuccessor(i);
+            if (!succ->isMarked()) {
+                ++numMarked;
+                succ->mark();
+                if (!worklist.append(succ))
+                    return false;
+            }
+        }
+        // The one special thing we do during this mark pass is to mark
+        // loop predecessors of reachable blocks as reachable. These blocks are
+        // the OSR fixups blocks which need to remain if the loop remains,
+        // though they can be removed if the loop is removed.
+        if (block->isLoopHeader()) {
+            MBasicBlock* pred = block->loopPredecessor();
+            if (!pred->isMarked() && pred->numPredecessors() == 0) {
+                MOZ_ASSERT(pred->numSuccessors() == 1,
+                           "OSR fixup block should have exactly one successor");
+                MOZ_ASSERT(pred != graph_.entryBlock(),
+                           "OSR fixup block shouldn't be the entry block");
+                MOZ_ASSERT(pred != graph_.osrBlock(),
+                           "OSR fixup block shouldn't be the OSR entry block");
+                pred->mark();
+            }
+        }
+    }
+
+    // And sweep.
+    return RemoveUnmarkedBlocks(mir_, graph_, numMarked);
+}
+
 ValueNumberer::ValueNumberer(MIRGenerator* mir, MIRGraph& graph)
   : mir_(mir), graph_(graph),
     values_(graph.alloc()),
     deadDefs_(graph.alloc()),
     remainingBlocks_(graph.alloc()),
     nextDef_(nullptr),
     totalNumVisited_(0),
     rerun_(false),
     blocksRemoved_(false),
     updateAliasAnalysis_(false),
-    dependenciesBroken_(false)
+    dependenciesBroken_(false),
+    hasOSRFixups_(false)
 {}
 
 bool
 ValueNumberer::init()
 {
     // Initialize the value set. It's tempting to pass in a size here of some
     // function of graph_.getNumInstructionIds(), however if we start out with a
     // large capacity, it will be far larger than the actual element count for
@@ -1157,10 +1205,15 @@ ValueNumberer::run(UpdateAliasAnalysisFl
             JitSpew(JitSpew_GVN, "Re-run cutoff of %d reached. Terminating GVN!", runs);
             break;
         }
 
         JitSpew(JitSpew_GVN, "Re-running GVN on graph (run %d, now with %llu blocks)",
                 runs, uint64_t(graph_.numBlocks()));
     }
 
+    if (MOZ_UNLIKELY(hasOSRFixups_)) {
+        cleanupOSRFixups();
+        hasOSRFixups_ = false;
+    }
+
     return true;
 }
--- a/js/src/jit/ValueNumbering.h
+++ b/js/src/jit/ValueNumbering.h
@@ -66,16 +66,17 @@ class ValueNumberer
     DefWorklist deadDefs_;            // Worklist for deleting values
     BlockWorklist remainingBlocks_;   // Blocks remaining with fewer preds
     MDefinition* nextDef_;            // The next definition; don't discard
     size_t totalNumVisited_;          // The number of blocks visited
     bool rerun_;                      // Should we run another GVN iteration?
     bool blocksRemoved_;              // Have any blocks been removed?
     bool updateAliasAnalysis_;        // Do we care about AliasAnalysis?
     bool dependenciesBroken_;         // Have we broken AliasAnalysis?
+    bool hasOSRFixups_;               // Have we created any OSR fixup blocks?
 
     enum UseRemovedOption {
         DontSetUseRemoved,
         SetUseRemoved
     };
 
     bool handleUseReleased(MDefinition* def, UseRemovedOption useRemovedOption);
     bool discardDefsRecursively(MDefinition* def);
@@ -95,16 +96,17 @@ class ValueNumberer
     bool loopHasOptimizablePhi(MBasicBlock* header) const;
 
     bool visitDefinition(MDefinition* def);
     bool visitControlInstruction(MBasicBlock* block, const MBasicBlock* root);
     bool visitUnreachableBlock(MBasicBlock* block);
     bool visitBlock(MBasicBlock* block, const MBasicBlock* root);
     bool visitDominatorTree(MBasicBlock* root);
     bool visitGraph();
+    bool cleanupOSRFixups();
 
   public:
     ValueNumberer(MIRGenerator* mir, MIRGraph& graph);
     bool init();
 
     enum UpdateAliasAnalysisFlag {
         DontUpdateAliasAnalysis,
         UpdateAliasAnalysis
--- a/js/src/jit/arm/Assembler-arm.cpp
+++ b/js/src/jit/arm/Assembler-arm.cpp
@@ -2363,16 +2363,25 @@ Assembler::as_b(Label* l, Condition c)
         return BufferOffset();
 
     DebugOnly<int32_t> check = l->use(ret.getOffset());
     MOZ_ASSERT(check == old);
     return ret;
 }
 
 BufferOffset
+Assembler::as_b(wasm::JumpTarget target, Condition c)
+{
+    Label l;
+    BufferOffset ret = as_b(&l, c);
+    bindLater(&l, target);
+    return ret;
+}
+
+BufferOffset
 Assembler::as_b(BOffImm off, Condition c, BufferOffset inst)
 {
     // JS_DISASM_ARM NOTE: Can't disassemble here, because numerous callers use this to
     // patchup old code.  Must disassemble in caller where it makes sense.  Not many callers.
     *editSrc(inst) = InstBImm(off, c);
     return inst;
 }
 
@@ -2782,16 +2791,28 @@ Assembler::bind(Label* label, BufferOffs
                 MOZ_CRASH("crazy fixup!");
             b = next;
         } while (more);
     }
     label->bind(nextOffset().getOffset());
 }
 
 void
+Assembler::bindLater(Label* label, wasm::JumpTarget target)
+{
+    if (label->used()) {
+        BufferOffset b(label);
+        do {
+            append(target, b.getOffset());
+        } while (nextLink(b, &b));
+    }
+    label->reset();
+}
+
+void
 Assembler::bind(RepatchLabel* label)
 {
     // It does not seem to be useful to record this label for
     // disassembly, as the value that is bound to the label is often
     // effectively garbage and is replaced by something else later.
     BufferOffset dest = nextOffset();
     if (label->used() && !oom()) {
         // If the label has a use, then change this use to refer to the bound
@@ -2848,50 +2869,16 @@ Assembler::retarget(Label* label, Label*
             DebugOnly<uint32_t> prev = target->use(label->offset());
             MOZ_ASSERT((int32_t)prev == Label::INVALID_OFFSET);
         }
     }
     label->reset();
 
 }
 
-void
-Assembler::retargetWithOffset(size_t baseOffset, const LabelBase* label, LabelBase* target)
-{
-    if (!label->used())
-        return;
-
-    MOZ_ASSERT(!target->bound());
-    bool more;
-    BufferOffset labelBranchOffset(label->offset() + baseOffset);
-    do {
-        BufferOffset next;
-        more = nextLink(labelBranchOffset, &next);
-
-        Instruction branch = *editSrc(labelBranchOffset);
-        Condition c = branch.extractCond();
-        int32_t prev = target->use(labelBranchOffset.getOffset());
-
-        MOZ_RELEASE_ASSERT(prev == Label::INVALID_OFFSET || unsigned(prev) < size());
-
-        BOffImm newOffset;
-        if (prev != Label::INVALID_OFFSET)
-            newOffset = BOffImm(prev);
-
-        if (branch.is<InstBImm>())
-            as_b(newOffset, c, labelBranchOffset);
-        else if (branch.is<InstBLImm>())
-            as_bl(newOffset, c, labelBranchOffset);
-        else
-            MOZ_CRASH("crazy fixup!");
-
-        labelBranchOffset = BufferOffset(next.getOffset() + baseOffset);
-    } while (more);
-}
-
 static int stopBKPT = -1;
 void
 Assembler::as_bkpt()
 {
     // This is a count of how many times a breakpoint instruction has been
     // generated. It is embedded into the instruction for debugging
     // purposes. Gdb will print "bkpt xxx" when you attempt to dissassemble a
     // breakpoint with the number xxx embedded into it. If this breakpoint is
--- a/js/src/jit/arm/Assembler-arm.h
+++ b/js/src/jit/arm/Assembler-arm.h
@@ -1592,16 +1592,17 @@ class Assembler : public AssemblerShared
     // bx can *only* branch to a register never to an immediate.
     BufferOffset as_bx(Register r, Condition c = Always);
 
     // Branch can branch to an immediate *or* to a register. Branches to
     // immediates are pc relative, branches to registers are absolute.
     BufferOffset as_b(BOffImm off, Condition c, Label* documentation = nullptr);
 
     BufferOffset as_b(Label* l, Condition c = Always);
+    BufferOffset as_b(wasm::JumpTarget target, Condition c = Always);
     BufferOffset as_b(BOffImm off, Condition c, BufferOffset inst);
 
     // blx can go to either an immediate or a register. When blx'ing to a
     // register, we change processor mode depending on the low bit of the
     // register when blx'ing to an immediate, we *always* change processor
     // state.
     BufferOffset as_blx(Label* l);
 
@@ -1699,20 +1700,20 @@ class Assembler : public AssemblerShared
 
     BufferOffset as_vmrs(Register r, Condition c = Always);
     BufferOffset as_vmsr(Register r, Condition c = Always);
 
     // Label operations.
     bool nextLink(BufferOffset b, BufferOffset* next);
     void bind(Label* label, BufferOffset boff = BufferOffset());
     void bind(RepatchLabel* label);
+    void bindLater(Label* label, wasm::JumpTarget target);
     uint32_t currentOffset() {
         return nextOffset().getOffset();
     }
-    void retargetWithOffset(size_t baseOffset, const LabelBase* label, LabelBase* target);
     void retarget(Label* label, Label* target);
     // I'm going to pretend this doesn't exist for now.
     void retarget(Label* label, void* target, Relocation::Kind reloc);
 
     void Bind(uint8_t* rawCode, CodeOffset* label, const void* address);
 
     // See Bind
     size_t labelToPatchOffset(CodeOffset label) {
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -2176,17 +2176,17 @@ CodeGeneratorARM::visitAsmJSLoadHeap(LAs
         } else {
             masm.ma_vldr(Address(GlobalReg, wasm::NaN64GlobalDataOffset - AsmJSGlobalRegBias),
                          vd, Assembler::AboveOrEqual);
             masm.ma_vldr(vd, HeapReg, ptrReg, 0, Assembler::Below);
         }
     } else {
         Register d = ToRegister(ins->output());
         if (mir->isAtomicAccess())
-            masm.ma_b(masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
+            masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
         else
             masm.ma_mov(Imm32(0), d, Assembler::AboveOrEqual);
         masm.ma_dataTransferN(IsLoad, size, isSigned, HeapReg, ptrReg, d, Offset, Assembler::Below);
     }
     memoryBarrier(mir->barrierAfter());
     masm.append(wasm::HeapAccess(bo.getOffset()));
 }
 
@@ -2250,17 +2250,17 @@ CodeGeneratorARM::visitAsmJSStoreHeap(LA
     if (isFloat) {
         VFPRegister vd(ToFloatRegister(ins->value()));
         if (size == 32)
             masm.ma_vstr(vd.singleOverlay(), HeapReg, ptrReg, 0, 0, Assembler::Below);
         else
             masm.ma_vstr(vd, HeapReg, ptrReg, 0, 0, Assembler::Below);
     } else {
         if (mir->isAtomicAccess())
-            masm.ma_b(masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
+            masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
         masm.ma_dataTransferN(IsStore, size, isSigned, HeapReg, ptrReg,
                               ToRegister(ins->value()), Offset, Assembler::Below);
     }
     memoryBarrier(mir->barrierAfter());
     masm.append(wasm::HeapAccess(bo.getOffset()));
 }
 
 void
@@ -2275,17 +2275,17 @@ CodeGeneratorARM::visitAsmJSCompareExcha
 
     Register oldval = ToRegister(ins->oldValue());
     Register newval = ToRegister(ins->newValue());
 
     uint32_t maybeCmpOffset = 0;
     if (mir->needsBoundsCheck()) {
         BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
         maybeCmpOffset = bo.getOffset();
-        masm.ma_b(masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
+        masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
     }
     masm.compareExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                         srcAddr, oldval, newval, InvalidReg,
                                         ToAnyRegister(ins->output()));
     if (mir->needsBoundsCheck())
         masm.append(wasm::HeapAccess(maybeCmpOffset));
 }
 
@@ -2321,17 +2321,17 @@ CodeGeneratorARM::visitAsmJSAtomicExchan
     Register value = ToRegister(ins->value());
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
 
     uint32_t maybeCmpOffset = 0;
     if (mir->needsBoundsCheck()) {
         BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
         maybeCmpOffset = bo.getOffset();
-        masm.ma_b(masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
+        masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
     }
 
     masm.atomicExchangeToTypedIntArray(vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                        srcAddr, value, InvalidReg, ToAnyRegister(ins->output()));
 
     if (mir->needsBoundsCheck())
         masm.append(wasm::HeapAccess(maybeCmpOffset));
 }
@@ -2372,17 +2372,17 @@ CodeGeneratorARM::visitAsmJSAtomicBinopH
     AtomicOp op = mir->operation();
 
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
 
     uint32_t maybeCmpOffset = 0;
     if (mir->needsBoundsCheck()) {
         BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
         maybeCmpOffset = bo.getOffset();
-        masm.ma_b(masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
+        masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
     }
 
     if (value->isConstant())
         atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
                                    Imm32(ToInt32(value)), srcAddr, flagTemp, InvalidReg,
                                    ToAnyRegister(ins->output()));
     else
         atomicBinopToTypedIntArray(op, vt == Scalar::Uint32 ? Scalar::Int32 : vt,
@@ -2407,17 +2407,17 @@ CodeGeneratorARM::visitAsmJSAtomicBinopH
     AtomicOp op = mir->operation();
 
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne);
 
     uint32_t maybeCmpOffset = 0;
     if (mir->needsBoundsCheck()) {
         BufferOffset bo = masm.ma_BoundsCheck(ptrReg);
         maybeCmpOffset = bo.getOffset();
-        masm.ma_b(masm.asmOnOutOfBoundsLabel(), Assembler::AboveOrEqual);
+        masm.ma_b(wasm::JumpTarget::OutOfBounds, Assembler::AboveOrEqual);
     }
 
     if (value->isConstant())
         atomicBinopToTypedIntArray(op, vt, Imm32(ToInt32(value)), srcAddr, flagTemp);
     else
         atomicBinopToTypedIntArray(op, vt, ToRegister(value), srcAddr, flagTemp);
 
     if (mir->needsBoundsCheck())
--- a/js/src/jit/arm/MacroAssembler-arm.cpp
+++ b/js/src/jit/arm/MacroAssembler-arm.cpp
@@ -1399,16 +1399,22 @@ MacroAssemblerARM::ma_dsb(BarrierOption 
 
 // Branches when done from within arm-specific code.
 BufferOffset
 MacroAssemblerARM::ma_b(Label* dest, Assembler::Condition c)
 {
     return as_b(dest, c);
 }
 
+BufferOffset
+MacroAssemblerARM::ma_b(wasm::JumpTarget target, Assembler::Condition c)
+{
+    return as_b(target, c);
+}
+
 void
 MacroAssemblerARM::ma_bx(Register dest, Assembler::Condition c)
 {
     as_bx(dest, c);
 }
 
 void
 MacroAssemblerARM::ma_b(void* target, Assembler::Condition c)
@@ -4930,27 +4936,71 @@ MacroAssembler::call(JitCode* c)
     ScratchRegisterScope scratch(*this);
     ma_movPatchable(ImmPtr(c->raw()), scratch, Always);
     callJitNoProfiler(scratch);
 }
 
 CodeOffset
 MacroAssembler::callWithPatch()
 {
-    // For now, assume that it'll be nearby.
+    // The caller ensures that the call is always in range using thunks (below)
+    // as necessary.
     as_bl(BOffImm(), Always, /* documentation */ nullptr);
     return CodeOffset(currentOffset());
 }
+
 void
 MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
 {
     BufferOffset inst(callerOffset - 4);
     as_bl(BufferOffset(calleeOffset).diffB<BOffImm>(inst), Always, inst);
 }
 
+CodeOffset
+MacroAssembler::thunkWithPatch()
+{
+    // The goal of the thunk is to be able to jump to any address without the
+    // usual 32MiB branch range limitation. Additionally, to make the thunk
+    // simple to use, the thunk does not use the constant pool or require
+    // patching an absolute address. Instead, a relative offset is used which
+    // can be patched during compilation.
+
+    // Inhibit pools since these three words must be contiguous so that the offset
+    // calculations below are valid.
+    AutoForbidPools afp(this, 3);
+
+    // When pc is used, the read value is the address of the instruction + 8.
+    // This is exactly the address of the uint32 word we want to load.
+    ScratchRegisterScope scratch(*this);
+    ma_ldr(Address(pc, 0), scratch);
+
+    // Branch by making pc the destination register.
+    ma_add(pc, scratch, pc, LeaveCC, Always);
+
+    // Allocate space which will be patched by patchThunk().
+    CodeOffset u32Offset(currentOffset());
+    writeInst(UINT32_MAX);
+
+    return u32Offset;
+}
+
+void
+MacroAssembler::patchThunk(uint32_t u32Offset, uint32_t targetOffset)
+{
+    uint32_t* u32 = reinterpret_cast<uint32_t*>(editSrc(BufferOffset(u32Offset)));
+    MOZ_ASSERT(*u32 == UINT32_MAX);
+
+    uint32_t addOffset = u32Offset - 4;
+    MOZ_ASSERT(editSrc(BufferOffset(addOffset))->is<InstALU>());
+
+    // When pc is read as the operand of the add, its value is the address of
+    // the add instruction + 8.
+    *u32 = (targetOffset - addOffset) - 8;
+}
+
 void
 MacroAssembler::pushReturnAddress()
 {
     push(lr);
 }
 
 // ===============================================================
 // ABI function calls.
--- a/js/src/jit/arm/MacroAssembler-arm.h
+++ b/js/src/jit/arm/MacroAssembler-arm.h
@@ -318,16 +318,17 @@ class MacroAssemblerARM : public Assembl
     void ma_vpush(VFPRegister r);
 
     // Barriers.
     void ma_dmb(BarrierOption option=BarrierSY);
     void ma_dsb(BarrierOption option=BarrierSY);
 
     // Branches when done from within arm-specific code.
     BufferOffset ma_b(Label* dest, Condition c = Always);
+    BufferOffset ma_b(wasm::JumpTarget target, Condition c = Always);
     void ma_b(void* target, Condition c = Always);
     void ma_bx(Register dest, Condition c = Always);
 
     // This is almost NEVER necessary, we'll basically never be calling a label
     // except, possibly in the crazy bailout-table case.
     void ma_bl(Label* dest, Condition c = Always);
 
     void ma_blx(Register dest, Condition c = Always);
@@ -603,16 +604,19 @@ class MacroAssemblerARMCompat : public M
     void jump(Register reg) {
         ma_bx(reg);
     }
     void jump(const Address& addr) {
         ScratchRegisterScope scratch(asMasm());
         ma_ldr(addr, scratch);
         ma_bx(scratch);
     }
+    void jump(wasm::JumpTarget target) {
+        as_b(target);
+    }
 
     void negl(Register reg) {
         ma_neg(reg, reg, SetCC);
     }
     void test32(Register lhs, Register rhs) {
         ma_tst(lhs, rhs);
     }
     void test32(Register lhs, Imm32 imm) {
@@ -860,18 +864,18 @@ class MacroAssemblerARMCompat : public M
         Condition c = testUndefined(cond, t);
         ma_b(label, c);
     }
     template <typename T>
     void branchTestNumber(Condition cond, const T& t, Label* label) {
         cond = testNumber(cond, t);
         ma_b(label, cond);
     }
-    template <typename T>
-    void branchTestMagic(Condition cond, const T& t, Label* label) {
+    template <typename T, class L>
+    void branchTestMagic(Condition cond, const T& t, L label) {
         cond = testMagic(cond, t);
         ma_b(label, cond);
     }
     void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
                               Label* label) {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         branchTestValue(cond, val, MagicValue(why), label);
     }
@@ -886,27 +890,29 @@ class MacroAssemblerARMCompat : public M
     void branchTestDoubleTruthy(bool truthy, FloatRegister reg, Label* label) {
         Condition c = testDoubleTruthy(truthy, reg);
         ma_b(label, c);
     }
     void branchTestStringTruthy(bool truthy, const ValueOperand& value, Label* label) {
         Condition c = testStringTruthy(truthy, value);
         ma_b(label, c);
     }
-    void branchTest32(Condition cond, Register lhs, Register rhs, Label* label) {
+    template <class L>
+    void branchTest32(Condition cond, Register lhs, Register rhs, L label) {
         MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
         // x86 likes test foo, foo rather than cmp foo, #0.
         // Convert the former into the latter.
         if (lhs == rhs && (cond == Zero || cond == NonZero))
             ma_cmp(lhs, Imm32(0));
         else
             ma_tst(lhs, rhs);
         ma_b(label, cond);
     }
-    void branchTest32(Condition cond, Register lhs, Imm32 imm, Label* label) {
+    template <class L>
+    void branchTest32(Condition cond, Register lhs, Imm32 imm, L label) {
         MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
         ma_tst(lhs, imm);
         ma_b(label, cond);
     }
     void branchTest32(Condition cond, const Address& address, Imm32 imm, Label* label) {
         // branchTest32 will use ScratchRegister.
         AutoRegisterScope scratch2(asMasm(), secondScratchReg_);
         load32(address, scratch2);
--- a/js/src/jit/arm64/Assembler-arm64.h
+++ b/js/src/jit/arm64/Assembler-arm64.h
@@ -208,16 +208,19 @@ class Assembler : public vixl::Assembler
     BufferOffset immPool64Branch(RepatchLabel* label, ARMBuffer::PoolEntry* pe, vixl::Condition c);
     BufferOffset fImmPool(ARMFPRegister dest, uint8_t* value, vixl::LoadLiteralOp op);
     BufferOffset fImmPool64(ARMFPRegister dest, double value);
     BufferOffset fImmPool32(ARMFPRegister dest, float value);
 
     void bind(Label* label) { bind(label, nextOffset()); }
     void bind(Label* label, BufferOffset boff);
     void bind(RepatchLabel* label);
+    void bindLater(Label* label, wasm::JumpTarget target) {
+        MOZ_CRASH("NYI");
+    }
 
     bool oom() const {
         return AssemblerShared::oom() ||
             armbuffer_.oom() ||
             jumpRelocations_.oom() ||
             dataRelocations_.oom() ||
             preBarriers_.oom();
     }
@@ -258,19 +261,16 @@ class Assembler : public vixl::Assembler
         }
     }
 
     void Bind(uint8_t* rawCode, CodeOffset* label, const void* address) {
         *reinterpret_cast<const void**>(rawCode + label->offset()) = address;
     }
 
     void retarget(Label* cur, Label* next);
-    void retargetWithOffset(size_t baseOffset, const LabelBase* label, LabelBase* target) {
-        MOZ_CRASH("NYI");
-    }
 
     // The buffer is about to be linked. Ensure any constant pools or
     // excess bookkeeping has been flushed to the instruction stream.
     void flush() {
         armbuffer_.flushPool();
     }
 
     int actualIndex(int curOffset) {
--- a/js/src/jit/arm64/MacroAssembler-arm64.cpp
+++ b/js/src/jit/arm64/MacroAssembler-arm64.cpp
@@ -561,16 +561,28 @@ MacroAssembler::callWithPatch()
     return CodeOffset();
 }
 void
 MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
 {
     MOZ_CRASH("NYI");
 }
 
+CodeOffset
+MacroAssembler::thunkWithPatch()
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+MacroAssembler::patchThunk(uint32_t thunkOffset, uint32_t targetOffset)
+{
+    MOZ_CRASH("NYI");
+}
+
 void
 MacroAssembler::pushReturnAddress()
 {
     push(lr);
 }
 
 // ===============================================================
 // ABI function calls.
--- a/js/src/jit/arm64/MacroAssembler-arm64.h
+++ b/js/src/jit/arm64/MacroAssembler-arm64.h
@@ -551,16 +551,24 @@ class MacroAssemblerCompat : public vixl
 
     void convertFloat32ToDouble(FloatRegister src, FloatRegister dest) {
         Fcvt(ARMFPRegister(dest, 64), ARMFPRegister(src, 32));
     }
     void convertDoubleToFloat32(FloatRegister src, FloatRegister dest) {
         Fcvt(ARMFPRegister(dest, 32), ARMFPRegister(src, 64));
     }
 
+    using vixl::MacroAssembler::B;
+    void B(wasm::JumpTarget) {
+        MOZ_CRASH("NYI");
+    }
+    void B(wasm::JumpTarget, Condition cond) {
+        MOZ_CRASH("NYI");
+    }
+
     void branchTruncateDouble(FloatRegister src, Register dest, Label* fail) {
         vixl::UseScratchRegisterScope temps(this);
         const ARMRegister scratch64 = temps.AcquireX();
 
         // An out of range integer will be saturated to the destination size.
         ARMFPRegister src64(src, 64);
         ARMRegister dest64(dest, 64);
 
@@ -751,16 +759,19 @@ class MacroAssemblerCompat : public vixl
     }
     void jump(Register reg) {
         Br(ARMRegister(reg, 64));
     }
     void jump(const Address& addr) {
         loadPtr(addr, ip0);
         Br(vixl::ip0);
     }
+    void jump(wasm::JumpTarget target) {
+        MOZ_CRASH("NYI");
+    }
 
     void align(int alignment) {
         armbuffer_.align(alignment);
     }
 
     void haltingAlign(int alignment) {
         // TODO: Implement a proper halting align.
         // ARM doesn't have one either.
@@ -1405,27 +1416,29 @@ class MacroAssemblerCompat : public vixl
         vixl::UseScratchRegisterScope temps(this);
         const ARMRegister scratch32 = temps.AcquireW();
         MOZ_ASSERT(scratch32.asUnsized() != lhs.base);
         MOZ_ASSERT(scratch32.asUnsized() != lhs.index);
         doBaseIndex(scratch32, lhs, vixl::LDR_w);
         branch32(cond, scratch32.asUnsized(), rhs, label);
     }
 
-    void branchTest32(Condition cond, Register lhs, Register rhs, Label* label) {
+    template <class L>
+    void branchTest32(Condition cond, Register lhs, Register rhs, L label) {
         MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
         // x86 prefers |test foo, foo| to |cmp foo, #0|.
         // Convert the former to the latter for ARM.
         if (lhs == rhs && (cond == Zero || cond == NonZero))
             cmp32(lhs, Imm32(0));
         else
             test32(lhs, rhs);
         B(label, cond);
     }
-    void branchTest32(Condition cond, Register lhs, Imm32 imm, Label* label) {
+    template <class L>
+    void branchTest32(Condition cond, Register lhs, Imm32 imm, L label) {
         MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
         test32(lhs, imm);
         B(label, cond);
     }
     void branchTest32(Condition cond, const Address& address, Imm32 imm, Label* label) {
         vixl::UseScratchRegisterScope temps(this);
         const Register scratch = temps.AcquireX().asUnsized();
         MOZ_ASSERT(scratch != address.base);
@@ -1749,18 +1762,18 @@ class MacroAssemblerCompat : public vixl
         Condition c = testGCThing(cond, src);
         B(label, c);
     }
     template <typename T>
     void branchTestPrimitive(Condition cond, const T& t, Label* label) {
         Condition c = testPrimitive(cond, t);
         B(label, c);
     }
-    template <typename T>
-    void branchTestMagic(Condition cond, const T& t, Label* label) {
+    template <typename T, typename L>
+    void branchTestMagic(Condition cond, const T& t, L label) {
         Condition c = testMagic(cond, t);
         B(label, c);
     }
     void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why, Label* label) {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         branchTestValue(cond, val, MagicValue(why), label);
     }
     void branchTestValue(Condition cond, const ValueOperand& value, const Value& v, Label* label) {
--- a/js/src/jit/mips-shared/Assembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.cpp
@@ -1322,16 +1322,22 @@ AssemblerMIPSShared::bind(Label* label, 
 
             b = BufferOffset(next);
         } while (next != LabelBase::INVALID_OFFSET);
     }
     label->bind(dest.getOffset());
 }
 
 void
+AssemblerMIPSShared::bindLater(Label* label, wasm::JumpTarget target)
+{
+    MOZ_CRASH("NYI");
+}
+
+void
 AssemblerMIPSShared::retarget(Label* label, Label* target)
 {
     if (label->used() && !oom()) {
         if (target->bound()) {
             bind(label, BufferOffset(target));
         } else if (target->used()) {
             // The target is not bound but used. Prepend label's branch list
             // onto target's.
@@ -1357,38 +1363,16 @@ AssemblerMIPSShared::retarget(Label* lab
             // the list hanging off of label, and dump that into target.
             DebugOnly<uint32_t> prev = target->use(label->offset());
             MOZ_ASSERT((int32_t)prev == Label::INVALID_OFFSET);
         }
     }
     label->reset();
 }
 
-void
-AssemblerMIPSShared::retargetWithOffset(size_t baseOffset, const LabelBase* label, Label* target)
-{
-    if (!label->used())
-        return;
-
-    MOZ_ASSERT(!target->bound());
-    int32_t next;
-    BufferOffset labelBranchOffset(label->offset() + baseOffset);
-    do {
-        Instruction* inst = editSrc(labelBranchOffset);
-        int32_t prev = target->use(labelBranchOffset.getOffset());
-
-        MOZ_RELEASE_ASSERT(prev == Label::INVALID_OFFSET || unsigned(prev) < size());
-
-        next = inst[1].encode();
-        inst[1].setData(prev);
-
-        labelBranchOffset = BufferOffset(next + baseOffset);
-    } while (next != LabelBase::INVALID_OFFSET);
-}
-
 void dbg_break() {}
 void
 AssemblerMIPSShared::as_break(uint32_t code)
 {
     MOZ_ASSERT(code <= MAX_BREAK_CODE);
     writeInst(op_special | code << FunctionBits | ff_break);
 }
 
--- a/js/src/jit/mips-shared/Assembler-mips-shared.h
+++ b/js/src/jit/mips-shared/Assembler-mips-shared.h
@@ -1049,26 +1049,26 @@ class AssemblerMIPSShared : public Assem
                          FPConditionBit fcc = FCC0);
     BufferOffset as_cole(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                          FPConditionBit fcc = FCC0);
     BufferOffset as_cule(FloatFormat fmt, FloatRegister fs, FloatRegister ft,
                          FPConditionBit fcc = FCC0);
 
     // label operations
     void bind(Label* label, BufferOffset boff = BufferOffset());
+    void bindLater(Label* label, wasm::JumpTarget target);
     virtual void bind(InstImm* inst, uintptr_t branch, uintptr_t target) = 0;
     virtual void Bind(uint8_t* rawCode, CodeOffset* label, const void* address) = 0;
     void bind(CodeOffset* label) {
         label->bind(currentOffset());
     }
     uint32_t currentOffset() {
         return nextOffset().getOffset();
     }
     void retarget(Label* label, Label* target);
-    void retargetWithOffset(size_t baseOffset, const LabelBase* label, Label* target);
 
     // See Bind
     size_t labelToPatchOffset(CodeOffset label) { return label.offset(); }
 
     void call(Label* label);
     void call(void* target);
 
     void as_break(uint32_t code);
--- a/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
+++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared.cpp
@@ -1151,16 +1151,28 @@ MacroAssembler::callWithPatch()
 void
 MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
 {
     BufferOffset li(callerOffset - Assembler::PatchWrite_NearCallSize());
     Assembler::PatchInstructionImmediate((uint8_t*)editSrc(li),
                                          PatchedImmPtr((const void*)calleeOffset));
 }
 
+CodeOffset
+MacroAssembler::thunkWithPatch()
+{
+    MOZ_CRASH("NYI");
+}
+
+void
+MacroAssembler::patchThunk(uint32_t callerOffset, uint32_t calleeOffset)
+{
+    MOZ_CRASH("NYI");
+}
+
 void
 MacroAssembler::call(wasm::SymbolicAddress target)
 {
     movePtr(target, CallReg);
     call(CallReg);
 }
 
 void
--- a/js/src/jit/none/MacroAssembler-none.h
+++ b/js/src/jit/none/MacroAssembler-none.h
@@ -168,17 +168,16 @@ class MacroAssemblerNone : public Assemb
     size_t bytesNeeded() const { MOZ_CRASH(); }
     size_t jumpRelocationTableBytes() const { MOZ_CRASH(); }
     size_t dataRelocationTableBytes() const { MOZ_CRASH(); }
     size_t preBarrierTableBytes() const { MOZ_CRASH(); }
 
     size_t numCodeLabels() const { MOZ_CRASH(); }
     CodeLabel codeLabel(size_t) { MOZ_CRASH(); }
 
-    void retargetWithOffset(size_t, const LabelBase*, LabelBase*) { MOZ_CRASH(); }
     bool asmMergeWith(const MacroAssemblerNone&) { MOZ_CRASH(); }
 
     void trace(JSTracer*) { MOZ_CRASH(); }
     static void TraceJumpRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); }
     static void TraceDataRelocations(JSTracer*, JitCode*, CompactBufferReader&) { MOZ_CRASH(); }
 
     static bool SupportsFloatingPoint() { return false; }
     static bool SupportsSimd() { return false; }
@@ -187,16 +186,17 @@ class MacroAssemblerNone : public Assemb
     void copyJumpRelocationTable(uint8_t*) { MOZ_CRASH(); }
     void copyDataRelocationTable(uint8_t*) { MOZ_CRASH(); }
     void copyPreBarrierTable(uint8_t*) { MOZ_CRASH(); }
     void processCodeLabels(uint8_t*) { MOZ_CRASH(); }
 
     void flushBuffer() { MOZ_CRASH(); }
 
     template <typename T> void bind(T) { MOZ_CRASH(); }
+    void bindLater(Label*, wasm::JumpTarget) { MOZ_CRASH(); }
     template <typename T> void j(Condition, T) { MOZ_CRASH(); }
     template <typename T> void jump(T) { MOZ_CRASH(); }
     void haltingAlign(size_t) { MOZ_CRASH(); }
     void nopAlign(size_t) { MOZ_CRASH(); }
     void checkStackAlignment() { MOZ_CRASH(); }
     uint32_t currentOffset() { MOZ_CRASH(); }
     uint32_t labelToPatchOffset(CodeOffset) { MOZ_CRASH(); }
     CodeOffset labelForPatch() { MOZ_CRASH(); }
@@ -240,17 +240,17 @@ class MacroAssemblerNone : public Assemb
     void testNullSet(Condition, ValueOperand, Register) { MOZ_CRASH(); }
     void testObjectSet(Condition, ValueOperand, Register) { MOZ_CRASH(); }
     void testUndefinedSet(Condition, ValueOperand, Register) { MOZ_CRASH(); }
 
     template <typename T, typename S> void cmpPtrSet(Condition, T, S, Register) { MOZ_CRASH(); }
     template <typename T, typename S> void cmp32Set(Condition, T, S, Register) { MOZ_CRASH(); }
 
     template <typename T, typename S> void branch32(Condition, T, S, Label*) { MOZ_CRASH(); }
-    template <typename T, typename S> void branchTest32(Condition, T, S, Label*) { MOZ_CRASH(); }
+    template <typename T, typename S, typename L> void branchTest32(Condition, T, S, L) { MOZ_CRASH(); }
     template <typename T, typename S> void branchAdd32(Condition, T, S, Label*) { MOZ_CRASH(); }
     template <typename T, typename S> void branchSub32(Condition, T, S, Label*) { MOZ_CRASH(); }
     template <typename T, typename S> void branchPtr(Condition, T, S, Label*) { MOZ_CRASH(); }
     template <typename T, typename S> void branchTestPtr(Condition, T, S, Label*) { MOZ_CRASH(); }
     template <typename T, typename S> void branchDouble(DoubleCondition, T, S, Label*) { MOZ_CRASH(); }
     template <typename T, typename S> void branchFloat(DoubleCondition, T, S, Label*) { MOZ_CRASH(); }
     template <typename T, typename S> void branchPrivatePtr(Condition, T, S, Label*) { MOZ_CRASH(); }
     template <typename T, typename S> void decBranchPtr(Condition, T, S, Label*) { MOZ_CRASH(); }
@@ -364,17 +364,17 @@ class MacroAssemblerNone : public Assemb
     template <typename T> void branchTestDouble(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestNull(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestString(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestSymbol(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestObject(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestNumber(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestGCThing(Condition, T, Label*) { MOZ_CRASH(); }
     template <typename T> void branchTestPrimitive(Condition, T, Label*) { MOZ_CRASH(); }
-    template <typename T> void branchTestMagic(Condition, T, Label*) { MOZ_CRASH(); }
+    template <typename T, typename L> void branchTestMagic(Condition, T, L) { MOZ_CRASH(); }
     template <typename T> void branchTestMagicValue(Condition, T, JSWhyMagic, Label*) { MOZ_CRASH(); }
     void boxDouble(FloatRegister, ValueOperand) { MOZ_CRASH(); }
     void boxNonDouble(JSValueType, Register, ValueOperand) { MOZ_CRASH(); }
     template <typename T> void unboxInt32(T, Register) { MOZ_CRASH(); }
     template <typename T> void unboxBoolean(T, Register) { MOZ_CRASH(); }
     template <typename T> void unboxString(T, Register) { MOZ_CRASH(); }
     template <typename T> void unboxSymbol(T, Register) { MOZ_CRASH(); }
     template <typename T> void unboxObject(T, Register) { MOZ_CRASH(); }
--- a/js/src/jit/shared/Assembler-shared.h
+++ b/js/src/jit/shared/Assembler-shared.h
@@ -703,16 +703,17 @@ struct AsmJSInternalCallee
       : index(calleeIndex)
     {}
 };
 
 // The base class of all Assemblers for all archs.
 class AssemblerShared
 {
     wasm::CallSiteAndTargetVector callsites_;
+    wasm::JumpSiteArray jumpsites_;
     wasm::HeapAccessVector heapAccesses_;
     Vector<AsmJSGlobalAccess, 0, SystemAllocPolicy> asmJSGlobalAccesses_;
     Vector<AsmJSAbsoluteAddress, 0, SystemAllocPolicy> asmJSAbsoluteAddresses_;
 
   protected:
     Vector<CodeLabel, 0, SystemAllocPolicy> codeLabels_;
 
     bool enoughMemory_;
@@ -735,26 +736,31 @@ class AssemblerShared
     bool oom() const {
         return !enoughMemory_;
     }
 
     bool embedsNurseryPointers() const {
         return embedsNurseryPointers_;
     }
 
-    void append(const wasm::CallSiteDesc& desc, CodeOffset label, size_t framePushed,
+    void append(const wasm::CallSiteDesc& desc, CodeOffset retAddr, size_t framePushed,
                 uint32_t targetIndex = wasm::CallSiteAndTarget::NOT_INTERNAL)
     {
         // framePushed does not include sizeof(AsmJSFrame), so add it in here (see
         // CallSite::stackDepth).
-        wasm::CallSite callsite(desc, label.offset(), framePushed + sizeof(AsmJSFrame));
+        wasm::CallSite callsite(desc, retAddr.offset(), framePushed + sizeof(AsmJSFrame));
         enoughMemory_ &= callsites_.append(wasm::CallSiteAndTarget(callsite, targetIndex));
     }
     wasm::CallSiteAndTargetVector& callSites() { return callsites_; }
 
+    void append(wasm::JumpTarget target, uint32_t offset) {
+        enoughMemory_ &= jumpsites_[target].append(offset);
+    }
+    const wasm::JumpSiteArray& jumpSites() const { return jumpsites_; }
+
     void append(wasm::HeapAccess access) { enoughMemory_ &= heapAccesses_.append(access); }
     wasm::HeapAccessVector&& extractHeapAccesses() { return Move(heapAccesses_); }
 
     void append(AsmJSGlobalAccess access) { enoughMemory_ &= asmJSGlobalAccesses_.append(access); }
     size_t numAsmJSGlobalAccesses() const { return asmJSGlobalAccesses_.length(); }
     AsmJSGlobalAccess asmJSGlobalAccess(size_t i) const { return asmJSGlobalAccesses_[i]; }
 
     void append(AsmJSAbsoluteAddress link) { enoughMemory_ &= asmJSAbsoluteAddresses_.append(link); }
@@ -776,16 +782,24 @@ class AssemblerShared
     // Merge this assembler with the other one, invalidating it, by shifting all
     // offsets by a delta.
     bool asmMergeWith(size_t delta, const AssemblerShared& other) {
         size_t i = callsites_.length();
         enoughMemory_ &= callsites_.appendAll(other.callsites_);
         for (; i < callsites_.length(); i++)
             callsites_[i].offsetReturnAddressBy(delta);
 
+        for (wasm::JumpTarget target : mozilla::MakeEnumeratedRange(wasm::JumpTarget::Limit)) {
+            wasm::Uint32Vector& offsets = jumpsites_[target];
+            i = offsets.length();
+            enoughMemory_ &= offsets.appendAll(other.jumpsites_[target]);
+            for (; i < offsets.length(); i++)
+                offsets[i] += delta;
+        }
+
         i = heapAccesses_.length();
         enoughMemory_ &= heapAccesses_.appendAll(other.heapAccesses_);
         for (; i < heapAccesses_.length(); i++)
             heapAccesses_[i].offsetInsnOffsetBy(delta);
 
         i = asmJSGlobalAccesses_.length();
         enoughMemory_ &= asmJSGlobalAccesses_.appendAll(other.asmJSGlobalAccesses_);
         for (; i < asmJSGlobalAccesses_.length(); i++)
--- a/js/src/jit/shared/CodeGenerator-shared.cpp
+++ b/js/src/jit/shared/CodeGenerator-shared.cpp
@@ -1496,21 +1496,21 @@ void
 CodeGeneratorShared::emitAsmJSCall(LAsmJSCall* ins)
 {
     MAsmJSCall* mir = ins->mir();
 
     if (mir->spIncrement())
         masm.freeStack(mir->spIncrement());
 
     MOZ_ASSERT((sizeof(AsmJSFrame) + masm.framePushed()) % AsmJSStackAlignment == 0);
-
-#ifdef DEBUG
     static_assert(AsmJSStackAlignment >= ABIStackAlignment &&
                   AsmJSStackAlignment % ABIStackAlignment == 0,
                   "The asm.js stack alignment should subsume the ABI-required alignment");
+
+#ifdef DEBUG
     Label ok;
     masm.branchTestStackPtr(Assembler::Zero, Imm32(AsmJSStackAlignment - 1), &ok);
     masm.breakpoint();
     masm.bind(&ok);
 #endif
 
     MAsmJSCall::Callee callee = mir->callee();
     switch (callee.which()) {
--- a/js/src/jit/shared/LIR-shared.h
+++ b/js/src/jit/shared/LIR-shared.h
@@ -1201,37 +1201,25 @@ class LCheckOverRecursed : public LInstr
 
     MCheckOverRecursed* mir() const {
         return mir_->toCheckOverRecursed();
     }
 };
 
 class LAsmJSInterruptCheck : public LInstructionHelper<0, 0, 0>
 {
-    Label* interruptExit_;
-    const wasm::CallSiteDesc& funcDesc_;
-
   public:
     LIR_HEADER(AsmJSInterruptCheck);
 
-    LAsmJSInterruptCheck(Label* interruptExit, const wasm::CallSiteDesc& funcDesc)
-      : interruptExit_(interruptExit), funcDesc_(funcDesc)
-    {
-    }
+    LAsmJSInterruptCheck()
+    { }
 
     bool isCall() const {
         return true;
     }
-
-    Label* interruptExit() const {
-        return interruptExit_;
-    }
-    const wasm::CallSiteDesc& funcDesc() const {
-        return funcDesc_;
-    }
 };
 
 class LInterruptCheck : public LInstructionHelper<0, 0, 0>
 {
     Label* oolEntry_;
 
     // Whether this is an implicit interrupt check. Implicit interrupt checks
     // use a patchable backedge and signal handlers instead of an explicit
--- a/js/src/jit/x64/CodeGenerator-x64.cpp
+++ b/js/src/jit/x64/CodeGenerator-x64.cpp
@@ -295,20 +295,17 @@ CodeGeneratorX64::emitSimdLoad(LAsmJSLoa
     const MAsmJSLoadHeap* mir = ins->mir();
     Scalar::Type type = mir->accessType();
     FloatRegister out = ToFloatRegister(ins->output());
     const LAllocation* ptr = ins->ptr();
     Operand srcAddr = ptr->isBogus()
                       ? Operand(HeapReg, mir->offset())
                       : Operand(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
 
-    uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
-    if (gen->needsAsmJSBoundsCheckBranch(mir))
-        maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr),
-                                                    masm.asmOnOutOfBoundsLabel());
+    uint32_t maybeCmpOffset = maybeEmitThrowingAsmJSBoundsCheck(mir, mir, ptr);
 
     unsigned numElems = mir->numSimdElems();
     if (numElems == 3) {
         MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4);
 
         Operand srcAddrZ =
             ptr->isBogus()
             ? Operand(HeapReg, 2 * sizeof(float) + mir->offset())
@@ -358,29 +355,19 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAs
 
     const LAllocation* ptr = ins->ptr();
     const LDefinition* out = ins->output();
     Operand srcAddr = ptr->isBogus()
                       ? Operand(HeapReg, mir->offset())
                       : Operand(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
 
     memoryBarrier(mir->barrierBefore());
-    OutOfLineLoadTypedArrayOutOfBounds* ool = nullptr;
-    uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
-    if (gen->needsAsmJSBoundsCheckBranch(mir)) {
-        Label* jumpTo = nullptr;
-        if (mir->isAtomicAccess()) {
-            jumpTo = masm.asmOnOutOfBoundsLabel();
-        } else {
-            ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), accessType);
-            addOutOfLineCode(ool, mir);
-            jumpTo = ool->entry();
-        }
-        maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), jumpTo);
-    }
+
+    OutOfLineLoadTypedArrayOutOfBounds* ool;
+    uint32_t maybeCmpOffset = maybeEmitAsmJSLoadBoundsCheck(mir, ins, &ool);
 
     uint32_t before = masm.size();
     switch (accessType) {
       case Scalar::Int8:      masm.movsbl(srcAddr, ToRegister(out)); break;
       case Scalar::Uint8:     masm.movzbl(srcAddr, ToRegister(out)); break;
       case Scalar::Int16:     masm.movswl(srcAddr, ToRegister(out)); break;
       case Scalar::Uint16:    masm.movzwl(srcAddr, ToRegister(out)); break;
       case Scalar::Int32:
@@ -389,22 +376,26 @@ CodeGeneratorX64::visitAsmJSLoadHeap(LAs
       case Scalar::Float64:   masm.loadDouble(srcAddr, ToFloatRegister(out)); break;
       case Scalar::Float32x4:
       case Scalar::Int32x4:   MOZ_CRASH("SIMD loads should be handled in emitSimdLoad");
       case Scalar::Uint8Clamped:
       case Scalar::MaxTypedArrayViewType:
           MOZ_CRASH("unexpected array type");
     }
     uint32_t after = masm.size();
+
     verifyHeapAccessDisassembly(before, after, /*isLoad=*/true, accessType, 0, srcAddr, *out->output());
+
     if (ool) {
         cleanupAfterAsmJSBoundsCheckBranch(mir, ToRegister(ptr));
         masm.bind(ool->rejoin());
     }
+
     memoryBarrier(mir->barrierAfter());
+
     masm.append(wasm::HeapAccess(before, wasm::HeapAccess::CarryOn, maybeCmpOffset));
 }
 
 void
 CodeGeneratorX64::storeSimd(Scalar::Type type, unsigned numElems, FloatRegister in,
                             const Operand& dstAddr)
 {
     switch (type) {
@@ -450,20 +441,17 @@ CodeGeneratorX64::emitSimdStore(LAsmJSSt
     const MAsmJSStoreHeap* mir = ins->mir();
     Scalar::Type type = mir->accessType();
     FloatRegister in = ToFloatRegister(ins->value());
     const LAllocation* ptr = ins->ptr();
     Operand dstAddr = ptr->isBogus()
                       ? Operand(HeapReg, mir->offset())
                       : Operand(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
 
-    uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
-    if (gen->needsAsmJSBoundsCheckBranch(mir))
-        maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr),
-                                                    masm.asmOnOutOfBoundsLabel());
+    uint32_t maybeCmpOffset = maybeEmitThrowingAsmJSBoundsCheck(mir, mir, ptr);
 
     unsigned numElems = mir->numSimdElems();
     if (numElems == 3) {
         MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4);
 
         Operand dstAddrZ =
             ptr->isBogus()
             ? Operand(HeapReg, 2 * sizeof(float) + mir->offset())
@@ -511,26 +499,19 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LA
 
     const LAllocation* value = ins->value();
     const LAllocation* ptr = ins->ptr();
     Operand dstAddr = ptr->isBogus()
                       ? Operand(HeapReg, mir->offset())
                       : Operand(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
 
     memoryBarrier(mir->barrierBefore());
-    Label* rejoin = nullptr;
-    uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
-    if (gen->needsAsmJSBoundsCheckBranch(mir)) {
-        Label* jumpTo = nullptr;
-        if (mir->isAtomicAccess())
-            jumpTo = masm.asmOnOutOfBoundsLabel();
-        else
-            rejoin = jumpTo = alloc().lifoAlloc()->newInfallible<Label>();
-        maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), jumpTo);
-    }
+
+    Label* rejoin;
+    uint32_t maybeCmpOffset = maybeEmitAsmJSStoreBoundsCheck(mir, ins, &rejoin);
 
     uint32_t before = masm.size();
     if (value->isConstant()) {
         switch (accessType) {
           case Scalar::Int8:
           case Scalar::Uint8:        masm.movb(Imm32(ToInt32(value)), dstAddr); break;
           case Scalar::Int16:
           case Scalar::Uint16:       masm.movw(Imm32(ToInt32(value)), dstAddr); break;
@@ -557,22 +538,26 @@ CodeGeneratorX64::visitAsmJSStoreHeap(LA
           case Scalar::Float32x4:
           case Scalar::Int32x4:      MOZ_CRASH("SIMD stores must be handled in emitSimdStore");
           case Scalar::Uint8Clamped:
           case Scalar::MaxTypedArrayViewType:
               MOZ_CRASH("unexpected array type");
         }
     }
     uint32_t after = masm.size();
+
     verifyHeapAccessDisassembly(before, after, /*isLoad=*/false, accessType, 0, dstAddr, *value);
+
     if (rejoin) {
         cleanupAfterAsmJSBoundsCheckBranch(mir, ToRegister(ptr));
         masm.bind(rejoin);
     }
+
     memoryBarrier(mir->barrierAfter());
+
     masm.append(wasm::HeapAccess(before, wasm::HeapAccess::CarryOn, maybeCmpOffset));
 }
 
 void
 CodeGeneratorX64::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
 {
     MAsmJSCompareExchangeHeap* mir = ins->mir();
     Scalar::Type accessType = mir->accessType();
@@ -580,23 +565,22 @@ CodeGeneratorX64::visitAsmJSCompareExcha
 
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
     MOZ_ASSERT(ptr->isRegister());
     BaseIndex srcAddr(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
 
     Register oldval = ToRegister(ins->oldValue());
     Register newval = ToRegister(ins->newValue());
 
-    // Note that we can't use
-    // needsAsmJSBoundsCheckBranch/emitAsmJSBoundsCheckBranch/cleanupAfterAsmJSBoundsCheckBranch
+    // Note that we can't use the same machinery as normal asm.js loads/stores
     // since signal-handler bounds checking is not yet implemented for atomic accesses.
     uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
     if (mir->needsBoundsCheck()) {
         maybeCmpOffset = masm.cmp32WithPatch(ToRegister(ptr), Imm32(-mir->endOffset())).offset();
-        masm.j(Assembler::Above, masm.asmOnOutOfBoundsLabel());
+        masm.j(Assembler::Above, wasm::JumpTarget::OutOfBounds);
     }
     uint32_t before = masm.size();
     masm.compareExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
                                         srcAddr,
                                         oldval,
                                         newval,
                                         InvalidReg,
                                         ToAnyRegister(ins->output()));
@@ -615,23 +599,22 @@ CodeGeneratorX64::visitAsmJSAtomicExchan
 
     MOZ_ASSERT(ins->addrTemp()->isBogusTemp());
     MOZ_ASSERT(ptr->isRegister());
     MOZ_ASSERT(accessType <= Scalar::Uint32);
 
     BaseIndex srcAddr(HeapReg, ToRegister(ptr), TimesOne, mir->offset());
     Register value = ToRegister(ins->value());
 
-    // Note that we can't use
-    // needsAsmJSBoundsCheckBranch/emitAsmJSBoundsCheckBranch/cleanupAfterAsmJSBoundsCheckBranch
+    // Note that we can't use the same machinery as normal asm.js loads/stores
     // since signal-handler bounds checking is not yet implemented for atomic accesses.
     uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
     if (mir->needsBoundsCheck()) {
         maybeCmpOffset = masm.cmp32WithPatch(ToRegister(ptr), Imm32(-mir->endOffset())).offset();
-        masm.j(Assembler::Above, masm.asmOnOutOfBoundsLabel());
+        masm.j(Assembler::Above, wasm::JumpTarget::OutOfBounds);
     }
     uint32_t before = masm.size();
     masm.atomicExchangeToTypedIntArray(accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
                                        srcAddr,
                                        value,
                                        InvalidReg,
                                        ToAnyRegister(ins->output()));
     MOZ_ASSERT(mir->offset() == 0,
@@ -650,23 +633,22 @@ CodeGeneratorX64::visitAsmJSAtomicBinopH
     Scalar::Type accessType = mir->accessType();
     Register ptrReg = ToRegister(ins->ptr());
     Register temp = ins->temp()->isBogusTemp() ? InvalidReg : ToRegister(ins->temp());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
 
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->offset());
 
-    // Note that we can't use
-    // needsAsmJSBoundsCheckBranch/emitAsmJSBoundsCheckBranch/cleanupAfterAsmJSBoundsCheckBranch
+    // Note that we can't use the same machinery as normal asm.js loads/stores
     // since signal-handler bounds checking is not yet implemented for atomic accesses.
     uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
     if (mir->needsBoundsCheck()) {
         maybeCmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(-mir->endOffset())).offset();
-        masm.j(Assembler::Above, masm.asmOnOutOfBoundsLabel());
+        masm.j(Assembler::Above, wasm::JumpTarget::OutOfBounds);
     }
     uint32_t before = masm.size();
     if (value->isConstant()) {
         atomicBinopToTypedIntArray(op, accessType == Scalar::Uint32 ? Scalar::Int32 : accessType,
                                    Imm32(ToInt32(value)),
                                    srcAddr,
                                    temp,
                                    InvalidReg,
@@ -694,23 +676,22 @@ CodeGeneratorX64::visitAsmJSAtomicBinopH
     MAsmJSAtomicBinopHeap* mir = ins->mir();
     Scalar::Type accessType = mir->accessType();
     Register ptrReg = ToRegister(ins->ptr());
     const LAllocation* value = ins->value();
     AtomicOp op = mir->operation();
 
     BaseIndex srcAddr(HeapReg, ptrReg, TimesOne, mir->offset());
 
-    // Note that we can't use
-    // needsAsmJSBoundsCheckBranch/emitAsmJSBoundsCheckBranch/cleanupAfterAsmJSBoundsCheckBranch
+    // Note that we can't use the same machinery as normal asm.js loads/stores
     // since signal-handler bounds checking is not yet implemented for atomic accesses.
     uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
     if (mir->needsBoundsCheck()) {
         maybeCmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(-mir->endOffset())).offset();
-        masm.j(Assembler::Above, masm.asmOnOutOfBoundsLabel());
+        masm.j(Assembler::Above, wasm::JumpTarget::OutOfBounds);
     }
 
     uint32_t before = masm.size();
     if (value->isConstant())
         atomicBinopToTypedIntArray(op, accessType, Imm32(ToInt32(value)), srcAddr);
     else
         atomicBinopToTypedIntArray(op, accessType, ToRegister(value), srcAddr);
     MOZ_ASSERT(mir->offset() == 0,
--- a/js/src/jit/x64/MacroAssembler-x64.h
+++ b/js/src/jit/x64/MacroAssembler-x64.h
@@ -972,18 +972,18 @@ class MacroAssemblerX64 : public MacroAs
         cond = testGCThing(cond, src);
         j(cond, label);
     }
     template <typename T>
     void branchTestPrimitive(Condition cond, const T& t, Label* label) {
         cond = testPrimitive(cond, t);
         j(cond, label);
     }
-    template <typename T>
-    void branchTestMagic(Condition cond, const T& t, Label* label) {
+    template <typename T, class L>
+    void branchTestMagic(Condition cond, const T& t, L label) {
         cond = testMagic(cond, t);
         j(cond, label);
     }
     void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
                               Label* label)
     {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         branchTestValue(cond, val, MagicValue(why), label);
--- a/js/src/jit/x86-shared/Assembler-x86-shared.h
+++ b/js/src/jit/x86-shared/Assembler-x86-shared.h
@@ -880,16 +880,27 @@ class AssemblerX86Shared : public Assemb
   public:
     void nop() { masm.nop(); }
     void twoByteNop() { masm.twoByteNop(); }
     void j(Condition cond, Label* label) { jSrc(cond, label); }
     void jmp(Label* label) { jmpSrc(label); }
     void j(Condition cond, RepatchLabel* label) { jSrc(cond, label); }
     void jmp(RepatchLabel* label) { jmpSrc(label); }
 
+    void j(Condition cond, wasm::JumpTarget target) {
+        Label l;
+        j(cond, &l);
+        bindLater(&l, target);
+    }
+    void jmp(wasm::JumpTarget target) {
+        Label l;
+        jmp(&l);
+        bindLater(&l, target);
+    }
+
     void jmp(const Operand& op) {
         switch (op.kind()) {
           case Operand::MEM_REG_DISP:
             masm.jmp_m(op.disp(), op.base());
             break;
           case Operand::MEM_SCALE:
             masm.jmp_m(op.disp(), op.base(), op.index(), op.scale());
             break;
@@ -910,53 +921,59 @@ class AssemblerX86Shared : public Assemb
                 JmpSrc next;
                 more = masm.nextJump(jmp, &next);
                 masm.linkJump(jmp, dst);
                 jmp = next;
             } while (more);
         }
         label->bind(dst.offset());
     }
+    void bindLater(Label* label, wasm::JumpTarget target) {
+        if (label->used()) {
+            JmpSrc jmp(label->offset());
+            do {
+                append(target, jmp.offset());
+            } while (masm.nextJump(jmp, &jmp));
+        }
+        label->reset();
+    }
     void bind(RepatchLabel* label) {
         JmpDst dst(masm.label());
         if (label->used()) {
             JmpSrc jmp(label->offset());
             masm.linkJump(jmp, dst);
         }
         label->bind(dst.offset());
     }
     void use(CodeOffset* label) {
         label->bind(currentOffset());
     }
     uint32_t currentOffset() {
         return masm.label().offset();
     }
 
     // Re-routes pending jumps to a new label.
-    void retargetWithOffset(size_t baseOffset, const LabelBase* label, LabelBase* target) {
+    void retarget(Label* label, Label* target) {
         if (!label->used())
             return;
         bool more;
-        JmpSrc jmp(label->offset() + baseOffset);
+        JmpSrc jmp(label->offset());
         do {
             JmpSrc next;
             more = masm.nextJump(jmp, &next);
             if (target->bound()) {
                 // The jump can be immediately patched to the correct destination.
                 masm.linkJump(jmp, JmpDst(target->offset()));
             } else {
                 // Thread the jump list through the unpatched jump targets.
                 JmpSrc prev(target->use(jmp.offset()));
                 masm.setNextJump(jmp, prev);
             }
-            jmp = JmpSrc(next.offset() + baseOffset);
+            jmp = JmpSrc(next.offset());
         } while (more);
-    }
-    void retarget(Label* label, Label* target) {
-        retargetWithOffset(0, label, target);
         label->reset();
     }
 
     static void Bind(uint8_t* raw, CodeOffset* label, const void* address) {
         if (label->bound()) {
             intptr_t offset = label->offset();
             X86Encoding::SetPointer(raw + offset, address);
         }
@@ -1003,16 +1020,23 @@ class AssemblerX86Shared : public Assemb
 
     CodeOffset callWithPatch() {
         return CodeOffset(masm.call().offset());
     }
     void patchCall(uint32_t callerOffset, uint32_t calleeOffset) {
         unsigned char* code = masm.data();
         X86Encoding::SetRel32(code + callerOffset, code + calleeOffset);
     }
+    CodeOffset thunkWithPatch() {
+        return CodeOffset(masm.jmp().offset());
+    }
+    void patchThunk(uint32_t jumpOffset, uint32_t targetOffset) {
+        unsigned char* code = masm.data();
+        X86Encoding::SetRel32(code + jumpOffset, code + targetOffset);
+    }
 
     void breakpoint() {
         masm.int3();
     }
 
     static bool HasSSE2() { return CPUInfo::IsSSE2Present(); }
     static bool HasSSE3() { return CPUInfo::IsSSE3Present(); }
     static bool HasSSE41() { return CPUInfo::IsSSE41Present(); }
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.cpp
@@ -333,62 +333,116 @@ CodeGeneratorX86Shared::visitOffsetBound
 {
     // The access is heap[ptr + offset]. The inline code checks that
     // ptr < heap.length - offset. We get here when that fails. We need to check
     // for the case where ptr + offset >= 0, in which case the access is still
     // in bounds.
     MOZ_ASSERT(oolCheck->offset() != 0,
                "An access without a constant offset doesn't need a separate OffsetBoundsCheck");
     masm.cmp32(oolCheck->ptrReg(), Imm32(-uint32_t(oolCheck->offset())));
-    masm.j(Assembler::Below, oolCheck->outOfBounds());
+    if (oolCheck->maybeOutOfBounds())
+        masm.j(Assembler::Below, oolCheck->maybeOutOfBounds());
+    else
+        masm.j(Assembler::Below, wasm::JumpTarget::OutOfBounds);
 
 #ifdef JS_CODEGEN_X64
     // In order to get the offset to wrap properly, we must sign-extend the
     // pointer to 32-bits. We'll zero out the sign extension immediately
     // after the access to restore asm.js invariants.
     masm.movslq(oolCheck->ptrReg(), oolCheck->ptrReg());
 #endif
 
     masm.jmp(oolCheck->rejoin());
 }
 
 uint32_t
 CodeGeneratorX86Shared::emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access,
                                                    const MInstruction* mir,
-                                                   Register ptr, Label* fail)
+                                                   Register ptr, Label* maybeFail)
 {
     // Emit a bounds-checking branch for |access|.
 
     MOZ_ASSERT(gen->needsAsmJSBoundsCheckBranch(access));
 
     Label* pass = nullptr;
 
     // If we have a non-zero offset, it's possible that |ptr| itself is out of
     // bounds, while adding the offset computes an in-bounds address. To catch
     // this case, we need a second branch, which we emit out of line since it's
     // unlikely to be needed in normal programs.
     if (access->offset() != 0) {
-        OffsetBoundsCheck* oolCheck = new(alloc()) OffsetBoundsCheck(fail, ptr, access->offset());
-        fail = oolCheck->entry();
+        auto oolCheck = new(alloc()) OffsetBoundsCheck(maybeFail, ptr, access->offset());
+        maybeFail = oolCheck->entry();
         pass = oolCheck->rejoin();
         addOutOfLineCode(oolCheck, mir);
     }
 
     // The bounds check is a comparison with an immediate value. The asm.js
     // module linking process will add the length of the heap to the immediate
     // field, so -access->endOffset() will turn into
     // (heapLength - access->endOffset()), allowing us to test whether the end
     // of the access is beyond the end of the heap.
-    uint32_t maybeCmpOffset = masm.cmp32WithPatch(ptr, Imm32(-access->endOffset())).offset();
-    masm.j(Assembler::Above, fail);
+    uint32_t cmpOffset = masm.cmp32WithPatch(ptr, Imm32(-access->endOffset())).offset();
+    if (maybeFail)
+        masm.j(Assembler::Above, maybeFail);
+    else
+        masm.j(Assembler::Above, wasm::JumpTarget::OutOfBounds);
 
     if (pass)
         masm.bind(pass);
 
-    return maybeCmpOffset;
+    return cmpOffset;
+}
+
+uint32_t
+CodeGeneratorX86Shared::maybeEmitThrowingAsmJSBoundsCheck(const MAsmJSHeapAccess* access,
+                                                          const MInstruction* mir,
+                                                          const LAllocation* ptr)
+{
+    if (!gen->needsAsmJSBoundsCheckBranch(access))
+        return wasm::HeapAccess::NoLengthCheck;
+
+    return emitAsmJSBoundsCheckBranch(access, mir, ToRegister(ptr), nullptr);
+}
+
+uint32_t
+CodeGeneratorX86Shared::maybeEmitAsmJSLoadBoundsCheck(const MAsmJSLoadHeap* mir, LAsmJSLoadHeap* ins,
+                                                      OutOfLineLoadTypedArrayOutOfBounds** ool)
+{
+    MOZ_ASSERT(!Scalar::isSimdType(mir->accessType()));
+    *ool = nullptr;
+
+    if (!gen->needsAsmJSBoundsCheckBranch(mir))
+        return wasm::HeapAccess::NoLengthCheck;
+
+    if (mir->isAtomicAccess())
+        return maybeEmitThrowingAsmJSBoundsCheck(mir, mir, ins->ptr());
+
+    *ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(ins->output()),
+                                                           mir->accessType());
+
+    addOutOfLineCode(*ool, mir);
+    return emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ins->ptr()), (*ool)->entry());
+}
+
+uint32_t
+CodeGeneratorX86Shared::maybeEmitAsmJSStoreBoundsCheck(const MAsmJSStoreHeap* mir, LAsmJSStoreHeap* ins,
+                                                       Label** rejoin)
+{
+    MOZ_ASSERT(!Scalar::isSimdType(mir->accessType()));
+    *rejoin = nullptr;
+
+    if (!gen->needsAsmJSBoundsCheckBranch(mir))
+        return wasm::HeapAccess::NoLengthCheck;
+
+    if (mir->isAtomicAccess())
+        return maybeEmitThrowingAsmJSBoundsCheck(mir, mir, ins->ptr());
+
+    *rejoin = alloc().lifoAlloc()->newInfallible<Label>();
+    return emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ins->ptr()), *rejoin);
 }
 
 void
 CodeGeneratorX86Shared::cleanupAfterAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* access,
                                                            Register ptr)
 {
     // Clean up after performing a heap access checked by a branch.
 
@@ -2274,39 +2328,40 @@ CodeGeneratorX86Shared::visitFloat32x4To
 }
 
 void
 CodeGeneratorX86Shared::visitOutOfLineSimdFloatToIntCheck(OutOfLineSimdFloatToIntCheck *ool)
 {
     static const SimdConstant Int32MaxX4 = SimdConstant::SplatX4(2147483647.f);
     static const SimdConstant Int32MinX4 = SimdConstant::SplatX4(-2147483648.f);
 
-    Label bail;
-    Label* onConversionError = gen->compilingAsmJS() ? masm.asmOnConversionErrorLabel() : &bail;
+    Label onConversionError;
 
     FloatRegister input = ool->input();
     Register temp = ool->temp();
 
     ScratchSimd128Scope scratch(masm);
     masm.loadConstantFloat32x4(Int32MinX4, scratch);
     masm.vcmpleps(Operand(input), scratch, scratch);
     masm.vmovmskps(scratch, temp);
     masm.cmp32(temp, Imm32(15));
-    masm.j(Assembler::NotEqual, onConversionError);
+    masm.j(Assembler::NotEqual, &onConversionError);
 
     masm.loadConstantFloat32x4(Int32MaxX4, scratch);
     masm.vcmpleps(Operand(input), scratch, scratch);
     masm.vmovmskps(scratch, temp);
     masm.cmp32(temp, Imm32(0));
-    masm.j(Assembler::NotEqual, onConversionError);
+    masm.j(Assembler::NotEqual, &onConversionError);
 
     masm.jump(ool->rejoin());
 
-    if (bail.used()) {
-        masm.bind(&bail);
+    if (gen->compilingAsmJS()) {
+        masm.bindLater(&onConversionError, wasm::JumpTarget::ConversionError);
+    } else {
+        masm.bind(&onConversionError);
         bailout(ool->ins()->snapshot());
     }
 }
 
 // Convert Float32x4 to Uint32x4.
 //
 // If any input lane value is out of range or NaN, bail out.
 void
--- a/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
+++ b/js/src/jit/x86-shared/CodeGenerator-x86-shared.h
@@ -48,25 +48,25 @@ class CodeGeneratorX86Shared : public Co
         void accept(CodeGeneratorX86Shared* codegen) {
             codegen->visitOutOfLineLoadTypedArrayOutOfBounds(this);
         }
     };
 
     // Additional bounds checking for heap accesses with constant offsets.
     class OffsetBoundsCheck : public OutOfLineCodeBase<CodeGeneratorX86Shared>
     {
-        Label* outOfBounds_;
+        Label* maybeOutOfBounds_;
         Register ptrReg_;
         int32_t offset_;
       public:
-        OffsetBoundsCheck(Label* outOfBounds, Register ptrReg, int32_t offset)
-          : outOfBounds_(outOfBounds), ptrReg_(ptrReg), offset_(offset)
+        OffsetBoundsCheck(Label* maybeOutOfBounds, Register ptrReg, int32_t offset)
+          : maybeOutOfBounds_(maybeOutOfBounds), ptrReg_(ptrReg), offset_(offset)
         {}
 
-        Label* outOfBounds() const { return outOfBounds_; }
+        Label* maybeOutOfBounds() const { return maybeOutOfBounds_; }
         Register ptrReg() const { return ptrReg_; }
         int32_t offset() const { return offset_; }
         void accept(CodeGeneratorX86Shared* codegen) {
             codegen->visitOffsetBoundsCheck(this);
         }
     };
 
     // Additional bounds check for vector Float to Int conversion, when the
@@ -86,20 +86,37 @@ class CodeGeneratorX86Shared : public Co
         FloatRegister input() const { return input_; }
         LInstruction* ins() const { return ins_; }
 
         void accept(CodeGeneratorX86Shared* codegen) {
             codegen->visitOutOfLineSimdFloatToIntCheck(this);
         }
     };
 
-    // Functions for emitting bounds-checking code with branches.
-    MOZ_WARN_UNUSED_RESULT
-    uint32_t emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, const MInstruction* ins,
-                                        Register ptr, Label* fail);
+  private:
+    MOZ_WARN_UNUSED_RESULT uint32_t
+    emitAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, const MInstruction* ins,
+                               Register ptr, Label* fail);
+
+  public:
+    // For SIMD and atomic loads and stores (which throw on out-of-bounds):
+    MOZ_WARN_UNUSED_RESULT uint32_t
+    maybeEmitThrowingAsmJSBoundsCheck(const MAsmJSHeapAccess* mir, const MInstruction* ins,
+                                      const LAllocation* ptr);
+
+    // For asm.js plain and atomic loads that possibly require a bounds check:
+    MOZ_WARN_UNUSED_RESULT uint32_t
+    maybeEmitAsmJSLoadBoundsCheck(const MAsmJSLoadHeap* mir, LAsmJSLoadHeap* ins,
+                                  OutOfLineLoadTypedArrayOutOfBounds** ool);
+
+    // For asm.js plain and atomic stores that possibly require a bounds check:
+    MOZ_WARN_UNUSED_RESULT uint32_t
+    maybeEmitAsmJSStoreBoundsCheck(const MAsmJSStoreHeap* mir, LAsmJSStoreHeap* ins,
+                                   Label** rejoin);
+
     void cleanupAfterAsmJSBoundsCheckBranch(const MAsmJSHeapAccess* mir, Register ptr);
 
     NonAssertingLabel deoptLabel_;
 
     MoveOperand toMoveOperand(LAllocation a) const;
 
     void bailoutIf(Assembler::Condition condition, LSnapshot* snapshot);
     void bailoutIf(Assembler::DoubleCondition condition, LSnapshot* snapshot);
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.cpp
@@ -539,16 +539,28 @@ MacroAssembler::callWithPatch()
     return Assembler::callWithPatch();
 }
 void
 MacroAssembler::patchCall(uint32_t callerOffset, uint32_t calleeOffset)
 {
     Assembler::patchCall(callerOffset, calleeOffset);
 }
 
+CodeOffset
+MacroAssembler::thunkWithPatch()
+{
+    return Assembler::thunkWithPatch();
+}
+
+void
+MacroAssembler::patchThunk(uint32_t jumpOffset, uint32_t targetOffset)
+{
+    Assembler::patchThunk(jumpOffset, targetOffset);
+}
+
 void
 MacroAssembler::callAndPushReturnAddress(Register reg)
 {
     call(reg);
 }
 
 void
 MacroAssembler::callAndPushReturnAddress(Label* label)
--- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
+++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h
@@ -90,24 +90,24 @@ class MacroAssemblerX86Shared : public A
 
     template<class T, class Map>
     T* getConstant(const typename T::Pod& value, Map& map, Vector<T, 0, SystemAllocPolicy>& vec);
 
     Float* getFloat(float f);
     Double* getDouble(double d);
     SimdData* getSimdData(const SimdConstant& v);
 
-    bool asmMergeWith(const MacroAssemblerX86Shared& other);
-
   public:
     using Assembler::call;
 
     MacroAssemblerX86Shared()
     { }
 
+    bool asmMergeWith(const MacroAssemblerX86Shared& other);
+
     void compareDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs) {
         if (cond & DoubleConditionBitInvert)
             vucomisd(lhs, rhs);
         else
             vucomisd(rhs, lhs);
     }
     void branchDouble(DoubleCondition cond, FloatRegister lhs, FloatRegister rhs, Label* label)
     {
@@ -599,22 +599,24 @@ class MacroAssemblerX86Shared : public A
     void branch32(Condition cond, Register lhs, Register rhs, Label* label) {
         cmp32(lhs, rhs);
         j(cond, label);
     }
     void branchTest16(Condition cond, Register lhs, Register rhs, Label* label) {
         testw(rhs, lhs);
         j(cond, label);
     }
-    void branchTest32(Condition cond, Register lhs, Register rhs, Label* label) {
+    template <class L>
+    void branchTest32(Condition cond, Register lhs, Register rhs, L label) {
         MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
         test32(lhs, rhs);
         j(cond, label);
     }
-    void branchTest32(Condition cond, Register lhs, Imm32 imm, Label* label) {
+    template <class L>
+    void branchTest32(Condition cond, Register lhs, Imm32 imm, L label) {
         MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
         test32(lhs, imm);
         j(cond, label);
     }
     void branchTest32(Condition cond, const Address& address, Imm32 imm, Label* label) {
         MOZ_ASSERT(cond == Zero || cond == NonZero || cond == Signed || cond == NotSigned);
         test32(Operand(address), imm);
         j(cond, label);
@@ -635,16 +637,19 @@ class MacroAssemblerX86Shared : public A
         jmp(label);
     }
     void jump(Register reg) {
         jmp(Operand(reg));
     }
     void jump(const Address& addr) {
         jmp(Operand(addr));
     }
+    void jump(wasm::JumpTarget target) {
+        jmp(target);
+    }
 
     void convertInt32ToDouble(Register src, FloatRegister dest) {
         // vcvtsi2sd and friends write only part of their output register, which
         // causes slowdowns on out-of-order processors. Explicitly break
         // dependencies with vxorpd (and vxorps elsewhere), which are handled
         // specially in modern CPUs, for this purpose. See sections 8.14, 9.8,
         // 10.8, 12.9, 13.16, 14.14, and 15.8 of Agner's Microarchitecture
         // document.
--- a/js/src/jit/x86/CodeGenerator-x86.cpp
+++ b/js/src/jit/x86/CodeGenerator-x86.cpp
@@ -390,20 +390,17 @@ CodeGeneratorX86::emitSimdLoad(LAsmJSLoa
     const MAsmJSLoadHeap* mir = ins->mir();
     Scalar::Type type = mir->accessType();
     FloatRegister out = ToFloatRegister(ins->output());
     const LAllocation* ptr = ins->ptr();
     Operand srcAddr = ptr->isBogus()
                       ? Operand(PatchedAbsoluteAddress(mir->offset()))
                       : Operand(ToRegister(ptr), mir->offset());
 
-    uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
-    if (gen->needsAsmJSBoundsCheckBranch(mir))
-        maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr),
-                                                    masm.asmOnOutOfBoundsLabel());
+    uint32_t maybeCmpOffset = maybeEmitThrowingAsmJSBoundsCheck(mir, mir, ptr);
 
     unsigned numElems = mir->numSimdElems();
     if (numElems == 3) {
         MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4);
 
         Operand srcAddrZ =
             ptr->isBogus()
             ? Operand(PatchedAbsoluteAddress(2 * sizeof(float) + mir->offset()))
@@ -447,38 +444,31 @@ CodeGeneratorX86::visitAsmJSLoadHeap(LAs
 
     const LAllocation* ptr = ins->ptr();
     const LDefinition* out = ins->output();
     Operand srcAddr = ptr->isBogus()
                       ? Operand(PatchedAbsoluteAddress(mir->offset()))
                       : Operand(ToRegister(ptr), mir->offset());
 
     memoryBarrier(mir->barrierBefore());
-    OutOfLineLoadTypedArrayOutOfBounds* ool = nullptr;
-    uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
-    if (gen->needsAsmJSBoundsCheckBranch(mir)) {
-        Label* jumpTo = nullptr;
-        if (mir->isAtomicAccess()) {
-            jumpTo = masm.asmOnOutOfBoundsLabel();
-        } else {
-            ool = new(alloc()) OutOfLineLoadTypedArrayOutOfBounds(ToAnyRegister(out), accessType);
-            addOutOfLineCode(ool, mir);
-            jumpTo = ool->entry();
-        }
-        maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), jumpTo);
-    }
+
+    OutOfLineLoadTypedArrayOutOfBounds* ool;
+    uint32_t maybeCmpOffset = maybeEmitAsmJSLoadBoundsCheck(mir, ins, &ool);
 
     uint32_t before = masm.size();
     load(accessType, srcAddr, out);
     uint32_t after = masm.size();
+
     if (ool) {
         cleanupAfterAsmJSBoundsCheckBranch(mir, ToRegister(ptr));
         masm.bind(ool->rejoin());
     }
+
     memoryBarrier(mir->barrierAfter());
+
     masm.append(wasm::HeapAccess(before, after, maybeCmpOffset));
 }
 
 void
 CodeGeneratorX86::store(Scalar::Type accessType, const LAllocation* value, const Operand& dstAddr)
 {
     switch (accessType) {
       case Scalar::Int8:
@@ -568,20 +558,17 @@ CodeGeneratorX86::emitSimdStore(LAsmJSSt
     const MAsmJSStoreHeap* mir = ins->mir();
     Scalar::Type type = mir->accessType();
     FloatRegister in = ToFloatRegister(ins->value());
     const LAllocation* ptr = ins->ptr();
     Operand dstAddr = ptr->isBogus()
                       ? Operand(PatchedAbsoluteAddress(mir->offset()))
                       : Operand(ToRegister(ptr), mir->offset());
 
-    uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
-    if (gen->needsAsmJSBoundsCheckBranch(mir))
-        maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr),
-                                                    masm.asmOnOutOfBoundsLabel());
+    uint32_t maybeCmpOffset = maybeEmitThrowingAsmJSBoundsCheck(mir, mir, ptr);
 
     unsigned numElems = mir->numSimdElems();
     if (numElems == 3) {
         MOZ_ASSERT(type == Scalar::Int32x4 || type == Scalar::Float32x4);
 
         Operand dstAddrZ =
             ptr->isBogus()
             ? Operand(PatchedAbsoluteAddress(2 * sizeof(float) + mir->offset()))
@@ -624,35 +611,31 @@ CodeGeneratorX86::visitAsmJSStoreHeap(LA
 
     const LAllocation* value = ins->value();
     const LAllocation* ptr = ins->ptr();
     Operand dstAddr = ptr->isBogus()
                       ? Operand(PatchedAbsoluteAddress(mir->offset()))
                       : Operand(ToRegister(ptr), mir->offset());
 
     memoryBarrier(mir->barrierBefore());
-    Label* rejoin = nullptr;
-    uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
-    if (gen->needsAsmJSBoundsCheckBranch(mir)) {
-        Label* jumpTo = nullptr;
-        if (mir->isAtomicAccess())
-            jumpTo = masm.asmOnOutOfBoundsLabel();
-        else
-            rejoin = jumpTo = alloc().lifoAlloc()->newInfallible<Label>();
-        maybeCmpOffset = emitAsmJSBoundsCheckBranch(mir, mir, ToRegister(ptr), jumpTo);
-    }
+
+    Label* rejoin;
+    uint32_t maybeCmpOffset = maybeEmitAsmJSStoreBoundsCheck(mir, ins, &rejoin);
 
     uint32_t before = masm.size();
     store(accessType, value, dstAddr);
     uint32_t after = masm.size();
+
     if (rejoin) {
         cleanupAfterAsmJSBoundsCheckBranch(mir, ToRegister(ptr));
         masm.bind(rejoin);
     }
+
     memoryBarrier(mir->barrierAfter());
+
     masm.append(wasm::HeapAccess(before, after, maybeCmpOffset));
 }
 
 void
 CodeGeneratorX86::visitAsmJSCompareExchangeHeap(LAsmJSCompareExchangeHeap* ins)
 {
     MAsmJSCompareExchangeHeap* mir = ins->mir();
     Scalar::Type accessType = mir->accessType();
@@ -680,17 +663,17 @@ CodeGeneratorX86::visitAsmJSCompareExcha
 void
 CodeGeneratorX86::asmJSAtomicComputeAddress(Register addrTemp, Register ptrReg, bool boundsCheck,
                                             int32_t offset, int32_t endOffset)
 {
     uint32_t maybeCmpOffset = wasm::HeapAccess::NoLengthCheck;
 
     if (boundsCheck) {
         maybeCmpOffset = masm.cmp32WithPatch(ptrReg, Imm32(-endOffset)).offset();
-        masm.j(Assembler::Above, masm.asmOnOutOfBoundsLabel());
+        masm.j(Assembler::Above, wasm::JumpTarget::OutOfBounds);
     }
 
     // Add in the actual heap pointer explicitly, to avoid opening up
     // the abstraction that is atomicBinopToTypedIntArray at this time.
     masm.movl(ptrReg, addrTemp);
     uint32_t before = masm.size();
     masm.addlWithPatch(Imm32(offset), addrTemp);
     uint32_t after = masm.size();
--- a/js/src/jit/x86/MacroAssembler-x86.h
+++ b/js/src/jit/x86/MacroAssembler-x86.h
@@ -792,18 +792,18 @@ class MacroAssemblerX86 : public MacroAs
         cond = testGCThing(cond, t);
         j(cond, label);
     }
     template <typename T>
     void branchTestPrimitive(Condition cond, const T& t, Label* label) {
         cond = testPrimitive(cond, t);
         j(cond, label);
     }
-    template <typename T>
-    void branchTestMagic(Condition cond, const T& t, Label* label) {
+    template <typename T, class L>
+    void branchTestMagic(Condition cond, const T& t, L label) {
         cond = testMagic(cond, t);
         j(cond, label);
     }
     void branchTestMagicValue(Condition cond, const ValueOperand& val, JSWhyMagic why,
                               Label* label)
     {
         MOZ_ASSERT(cond == Equal || cond == NotEqual);
         branchTestValue(cond, val, MagicValue(why), label);
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -289,16 +289,17 @@ MSG_DEF(JSMSG_NAME_AFTER_DOT,          0
 MSG_DEF(JSMSG_NAME_AFTER_IMPORT_STAR_AS, 0, JSEXN_SYNTAXERR, "missing name after import * as")
 MSG_DEF(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT, 0, JSEXN_SYNTAXERR, "expected named imports or namespace import after comma")
 MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default")
 MSG_DEF(JSMSG_NO_BINDING_NAME,        0, JSEXN_SYNTAXERR, "missing binding name")
 MSG_DEF(JSMSG_NO_CLASS_CONSTRUCTOR,    0, JSEXN_TYPEERR,   "class default constructors not yet implemented")
 MSG_DEF(JSMSG_NO_EXPORT_NAME,          0, JSEXN_SYNTAXERR, "missing export name")
 MSG_DEF(JSMSG_NO_IMPORT_NAME,          0, JSEXN_SYNTAXERR, "missing import name")
 MSG_DEF(JSMSG_NO_VARIABLE_NAME,        0, JSEXN_SYNTAXERR, "missing variable name")
+MSG_DEF(JSMSG_OBSOLETE_FLAGS_ARG,      0, JSEXN_NONE, "flags argument of String.prototype.{search,match,replace} is no longer supported")
 MSG_DEF(JSMSG_OF_AFTER_FOR_NAME,       0, JSEXN_SYNTAXERR, "missing 'of' after for")
 MSG_DEF(JSMSG_PAREN_AFTER_ARGS,        0, JSEXN_SYNTAXERR, "missing ) after argument list")
 MSG_DEF(JSMSG_PAREN_AFTER_CATCH,       0, JSEXN_SYNTAXERR, "missing ) after catch")
 MSG_DEF(JSMSG_PAREN_AFTER_COND,        0, JSEXN_SYNTAXERR, "missing ) after condition")
 MSG_DEF(JSMSG_PAREN_AFTER_FOR,         0, JSEXN_SYNTAXERR, "missing ( after for")
 MSG_DEF(JSMSG_PAREN_AFTER_FORMAL,      0, JSEXN_SYNTAXERR, "missing ) after formal parameters")
 MSG_DEF(JSMSG_PAREN_AFTER_FOR_CTRL,    0, JSEXN_SYNTAXERR, "missing ) after for-loop control")
 MSG_DEF(JSMSG_PAREN_AFTER_FOR_OF_ITERABLE, 0, JSEXN_SYNTAXERR, "missing ) after for-of iterable")
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1105,17 +1105,22 @@ class JS_PUBLIC_API(RuntimeOptions) {
         ion_(true),
         asmJS_(true),
         throwOnAsmJSValidationFailure_(false),
         nativeRegExp_(true),
         unboxedArrays_(false),
         asyncStack_(true),
         werror_(false),
         strictMode_(false),
-        extraWarnings_(false)
+        extraWarnings_(false),
+#ifdef RELEASE_BUILD
+        matchFlagArgument_(true)
+#else
+        matchFlagArgument_(false)
+#endif
     {
     }
 
     bool baseline() const { return baseline_; }
     RuntimeOptions& setBaseline(bool flag) {
         baseline_ = flag;
         return *this;
     }
@@ -1197,27 +1202,34 @@ class JS_PUBLIC_API(RuntimeOptions) {
         extraWarnings_ = flag;
         return *this;
     }
     RuntimeOptions& toggleExtraWarnings() {
         extraWarnings_ = !extraWarnings_;
         return *this;
     }
 
+    bool matchFlagArgument() const { return matchFlagArgument_; }
+    RuntimeOptions& setMatchFlagArgument(bool flag) {
+        matchFlagArgument_ = flag;
+        return *this;
+    }
+
   private:
     bool baseline_ : 1;
     bool ion_ : 1;
     bool asmJS_ : 1;
     bool throwOnAsmJSValidationFailure_ : 1;
     bool nativeRegExp_ : 1;
     bool unboxedArrays_ : 1;
     bool asyncStack_ : 1;
     bool werror_ : 1;
     bool strictMode_ : 1;
     bool extraWarnings_ : 1;
+    bool matchFlagArgument_ : 1;
 };
 
 JS_PUBLIC_API(RuntimeOptions&)
 RuntimeOptionsRef(JSRuntime* rt);
 
 JS_PUBLIC_API(RuntimeOptions&)
 RuntimeOptionsRef(JSContext* cx);
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -754,23 +754,23 @@ FormatFrame(JSContext* cx, const ScriptF
             RootedValue arg(cx);
             if (i < iter.numFormalArgs() && script->formalIsAliased(i)) {
                 for (AliasedFormalIter fi(script); ; fi++) {
                     if (fi.frameIndex() == i) {
                         arg = iter.callObj(cx).aliasedVar(fi);
                         break;
                     }
                 }
-            } else if (script->argsObjAliasesFormals() && iter.hasArgsObj()) {
-                arg = iter.argsObj().arg(i);
+            } else if (iter.hasUsableAbstractFramePtr()) {
+                if (script->argsObjAliasesFormals() && iter.hasArgsObj())
+                    arg = iter.argsObj().arg(i);
+                else
+                    arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
             } else {
-                if (iter.hasUsableAbstractFramePtr())
-                    arg = iter.unaliasedActual(i, DONT_CHECK_ALIASING);
-                else
-                    arg = MagicValue(JS_OPTIMIZED_OUT);
+                arg = MagicValue(JS_OPTIMIZED_OUT);
             }
 
             JSAutoByteString valueBytes;
             const char* value = FormatValue(cx, arg, valueBytes);
             if (!value) {
                 if (cx->isThrowingOutOfMemory())
                     return nullptr;
                 cx->clearPendingException();
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2179,33 +2179,42 @@ class MOZ_STACK_CLASS StringRegExpGuard
     bool normalizeRegExp(JSContext* cx, bool flat, unsigned optarg, const CallArgs& args)
     {
         if (re_.initialized())
             return true;
 
         /* Build RegExp from pattern string. */
         RootedString opt(cx);
         if (optarg < args.length()) {
+            // flag argument is enabled only in release build by default.
+            // In non-release build, both telemetry and warning are still
+            // enabled, but the value of flag argument is ignored.
+
             if (JSScript* script = cx->currentScript()) {
                 const char* filename = script->filename();
                 cx->compartment()->addTelemetry(filename, JSCompartment::DeprecatedFlagsArgument);
             }
 
+            bool flagArgumentEnabled = cx->runtime()->options().matchFlagArgument();
             if (!cx->compartment()->warnedAboutFlagsArgument) {
                 if (!JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING, GetErrorMessage, nullptr,
-                                                  JSMSG_DEPRECATED_FLAGS_ARG))
+                                                  flagArgumentEnabled
+                                                  ? JSMSG_DEPRECATED_FLAGS_ARG
+                                                  : JSMSG_OBSOLETE_FLAGS_ARG))
+                {
                     return false;
+                }
                 cx->compartment()->warnedAboutFlagsArgument = true;
             }
 
-            opt = ToString<CanGC>(cx, args[optarg]);
-            if (!opt)
-                return false;
-        } else {
-            opt = nullptr;
+            if (flagArgumentEnabled) {
+                opt = ToString<CanGC>(cx, args[optarg]);
+                if (!opt)
+                    return false;
+            }
         }
 
         Rooted<JSAtom*> pat(cx);
         if (flat) {
             pat = flattenPattern(cx, fm.pat_);
             if (!pat)
                 return false;
         } else {
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -134,17 +134,17 @@ static const double MAX_TIMEOUT_INTERVAL
 # define SHARED_MEMORY_DEFAULT 1
 #else
 # define SHARED_MEMORY_DEFAULT 0
 #endif
 
 // Per-runtime shell state.
 struct ShellRuntime
 {
-    ShellRuntime();
+    explicit ShellRuntime(JSRuntime* rt);
 
     bool isWorker;
     double timeoutInterval;
     Atomic<bool> serviceInterrupt;
     Atomic<bool> haveInterruptFunc;
     JS::PersistentRootedValue interruptFunc;
     bool lastWarningEnabled;
     JS::PersistentRootedValue lastWarning;
@@ -282,22 +282,24 @@ ShellPrincipals ShellPrincipals::fullyTr
 
 #ifdef EDITLINE
 extern "C" {
 extern JS_EXPORT_API(char*) readline(const char* prompt);
 extern JS_EXPORT_API(void)   add_history(char* line);
 } // extern "C"
 #endif
 
-ShellRuntime::ShellRuntime()
+ShellRuntime::ShellRuntime(JSRuntime* rt)
   : isWorker(false),
     timeoutInterval(-1.0),
     serviceInterrupt(false),
     haveInterruptFunc(false),
+    interruptFunc(rt, NullValue()),
     lastWarningEnabled(false),
+    lastWarning(rt, NullValue()),
     watchdogLock(nullptr),
     watchdogWakeup(nullptr),
     watchdogThread(nullptr),
     watchdogHasTimeout(false),
     watchdogTimeout(0),
     sleepWakeup(nullptr),
     exitCode(0),
     quitting(false),
@@ -2766,17 +2768,17 @@ WorkerMain(void* arg)
     WorkerInput* input = (WorkerInput*) arg;
 
     JSRuntime* rt = JS_NewRuntime(8L * 1024L * 1024L, 2L * 1024L * 1024L, input->runtime);
     if (!rt) {
         js_delete(input);
         return;
     }
 
-    UniquePtr<ShellRuntime> sr = MakeUnique<ShellRuntime>();
+    UniquePtr<ShellRuntime> sr = MakeUnique<ShellRuntime>(rt);
     if (!sr) {
         JS_DestroyRuntime(rt);
         js_delete(input);
         return;
     }
 
     sr->isWorker = true;
     JS_SetRuntimePrivate(rt, sr.get());
@@ -2820,16 +2822,19 @@ WorkerMain(void* arg)
             break;
         RootedValue result(cx);
         JS_ExecuteScript(cx, script, &result);
     } while (0);
 
     JS::SetLargeAllocationFailureCallback(rt, nullptr, nullptr);
 
     DestroyContext(cx, false);
+
+    KillWatchdog(rt);
+
     JS_DestroyRuntime(rt);
 
     js_delete(input);
 }
 
 // Workers can spawn other workers, so we need a lock to access workerThreads.
 static PRLock* workerThreadsLock = nullptr;
 static Vector<PRThread*, 0, SystemAllocPolicy> workerThreads;
@@ -6989,31 +6994,28 @@ main(int argc, char** argv, char** envp)
     size_t nurseryBytes = JS::DefaultNurseryBytes;
     nurseryBytes = op.getIntOption("nursery-size") * 1024L * 1024L;
 
     /* Use the same parameters as the browser in xpcjsruntime.cpp. */
     rt = JS_NewRuntime(JS::DefaultHeapMaxBytes, nurseryBytes);
     if (!rt)
         return 1;
 
-    UniquePtr<ShellRuntime> sr = MakeUnique<ShellRuntime>();
+    UniquePtr<ShellRuntime> sr = MakeUnique<ShellRuntime>(rt);
     if (!sr)
         return 1;
 
     JS_SetRuntimePrivate(rt, sr.get());
     // Waiting is allowed on the shell's main thread, for now.
     JS_SetFutexCanWait(rt);
     JS_SetErrorReporter(rt, my_ErrorReporter);
     JS::SetOutOfMemoryCallback(rt, my_OOMCallback, nullptr);
     if (!SetRuntimeOptions(rt, op))
         return 1;
 
-    sr->interruptFunc.init(rt, NullValue());
-    sr->lastWarning.init(rt, NullValue());
-
     JS_SetGCParameter(rt, JSGC_MAX_BYTES, 0xffffffff);
 
     size_t availMem = op.getIntOption("available-memory");
     if (availMem > 0)
         JS_SetGCParametersBasedOnAvailableMemory(rt, availMem);
 
     JS_SetTrustedPrincipals(rt, &ShellPrincipals::fullyTrusted);
     JS_SetSecurityCallbacks(rt, &ShellPrincipals::securityCallbacks);
--- a/js/src/tests/js1_5/Regress/regress-179524.js
+++ b/js/src/tests/js1_5/Regress/regress-179524.js
@@ -1,8 +1,9 @@
+// |reftest| skip-if(!xulRuntime.shell)
 /* -*- indent-tabs-mode: nil; js-indent-level: 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/. */
 
 /*
  *
  * Date:    11 Nov 2002
@@ -23,16 +24,17 @@ var BUGNUMBER = 179524;
 var summary = "Don't crash on extraneous arguments to str.match(), etc.";
 var status = '';
 var statusitems = [];
 var actual = '';
 var actualvalues = [];
 var expect= '';
 var expectedvalues = [];
 
+enableMatchFlagArgument();
 
 str = 'ABC abc';
 var re = /z/ig;
 
 status = inSection(1);
 actual = str.match(re);
 expect = null;
 addThis();
--- a/js/src/vm/Runtime.cpp
+++ b/js/src/vm/Runtime.cpp
@@ -575,17 +575,17 @@ InvokeInterruptCallback(JSContext* cx)
     if (!cb)
         return true;
 
     if (cb(cx)) {
         // Debugger treats invoking the interrupt callback as a "step", so
         // invoke the onStep handler.
         if (cx->compartment()->isDebuggee()) {
             ScriptFrameIter iter(cx);
-            if (iter.script()->stepModeEnabled()) {
+            if (!iter.done() && iter.script()->stepModeEnabled()) {
                 RootedValue rval(cx);
                 switch (Debugger::onSingleStep(cx, &rval)) {
                   case JSTRAP_ERROR:
                     return false;
                   case JSTRAP_CONTINUE:
                     return true;
                   case JSTRAP_RETURN:
                     // See note in Debugger::propagateForcedReturn.
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -56,18 +56,18 @@ InterpreterFrame::initExecuteFrame(JSCon
     Value* dstvp = (Value*)this - 1;
     dstvp[0] = newTarget;
 
     scopeChain_ = scopeChain.get();
     prev_ = nullptr;
     prevpc_ = nullptr;
     prevsp_ = nullptr;
 
+    evalInFramePrev_ = evalInFramePrev;
     MOZ_ASSERT_IF(evalInFramePrev, isDebuggerEvalFrame());
-    evalInFramePrev_ = evalInFramePrev;
 
     if (script->isDebuggee())
         setIsDebuggee();
 
 #ifdef DEBUG
     Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
 #endif
 }
@@ -1409,17 +1409,16 @@ ActivationEntryMonitor::ActivationEntryM
     }
 }
 
 /*****************************************************************************/
 
 jit::JitActivation::JitActivation(JSContext* cx, bool active)
   : Activation(cx, Jit),
     active_(active),
-    isLazyLinkExitFrame_(false),
     rematerializedFrames_(nullptr),
     ionRecovery_(cx),
     bailoutData_(nullptr),
     lastProfilingFrame_(nullptr),
     lastProfilingCallSite_(nullptr)
 {
     if (active) {
         prevJitTop_ = cx->runtime()->jitTop;
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -1383,25 +1383,16 @@ class BailoutFrameInfo;
 // A JitActivation is used for frames running in Baseline or Ion.
 class JitActivation : public Activation
 {
     uint8_t* prevJitTop_;
     JitActivation* prevJitActivation_;
     JSContext* prevJitJSContext_;
     bool active_;
 
-    // The lazy link stub reuse the frame pushed for calling a function as an
-    // exit frame. In a few cases, such as after calls from asm.js, we might
-    // have an entry frame followed by an exit frame. This pattern can be
-    // assimilated as a fake exit frame (unwound frame), in which case we skip
-    // marking during a GC. To ensure that we do mark the stack as expected we
-    // have to keep a flag set by the LazyLink VM function to safely mark the
-    // stack if a GC happens during the link phase.
-    bool isLazyLinkExitFrame_;
-
     // Rematerialized Ion frames which has info copied out of snapshots. Maps
     // frame pointers (i.e. jitTop) to a vector of rematerializations of all
     // inline frames associated with that frame.
     //
     // This table is lazily initialized by calling getRematerializedFrame.
     typedef Vector<RematerializedFrame*> RematerializedFrameVector;
     typedef HashMap<uint8_t*, RematerializedFrameVector> RematerializedFrameTable;
     RematerializedFrameTable* rematerializedFrames_;
@@ -1527,24 +1518,16 @@ class JitActivation : public Activation
     const BailoutFrameInfo* bailoutData() const { return bailoutData_; }
 
     // Register the bailout data when it is constructed.
     void setBailoutData(BailoutFrameInfo* bailoutData);
 
     // Unregister the bailout data when the frame is reconstructed.
     void cleanBailoutData();
 
-    // Return the bailout information if it is registered.
-    bool isLazyLinkExitFrame() const { return isLazyLinkExitFrame_; }
-
-    // Register the bailout data when it is constructed.
-    void setLazyLinkExitFrame(bool isExitFrame) {
-        isLazyLinkExitFrame_ = isExitFrame;
-    }
-
     static size_t offsetOfLastProfilingFrame() {
         return offsetof(JitActivation, lastProfilingFrame_);
     }
     void* lastProfilingFrame() {
         return lastProfilingFrame_;
     }
     void setLastProfilingFrame(void* ptr) {
         lastProfilingFrame_ = ptr;
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -26,21 +26,21 @@ namespace js {
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  *
  * (If you're wondering, 0xb973c0de is used because it looks like "bytecode".)
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 344;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 345;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 442,
+static_assert(JSErr_Limit == 443,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)
--- a/js/xpconnect/tests/chrome/test_bug853283.xul
+++ b/js/xpconnect/tests/chrome/test_bug853283.xul
@@ -25,17 +25,17 @@ https://bugzilla.mozilla.org/show_bug.cg
     // This chrome document already has Xrays to the content scope, but we use a
     // a sandbox anyway to make sure that the global in play here isn't an
     // nsIDOMWindow. Otherwise, the resolve hook might just end up squeaking by
     // with the chrome window.
     var iwin = $('ifr').contentWindow;
     var sb = new Cu.Sandbox(window);
     sb.iwin = iwin;
     sb.ok = ok;
-    Cu.evalInSandbox('try {iwin.navigator.mozApps; ok(true, "Didnt throw"); } catch (e) { ok(false, "Threw: " + e);}', sb);
+    Cu.evalInSandbox('try {iwin.navigator.mozContacts; ok(true, "Didnt throw"); } catch (e) { ok(false, "Threw: " + e);}', sb);
     SimpleTest.finish();
   }
 
 
   ]]>
   </script>
   <iframe id="ifr" onload="go();" src="http://example.org/tests/js/xpconnect/tests/mochitest/file_empty.html" />
 </window>
--- a/layout/base/AccessibleCaretEventHub.cpp
+++ b/layout/base/AccessibleCaretEventHub.cpp
@@ -334,16 +334,22 @@ public:
   {
     aContext->SetState(aContext->NoActionState());
 
     // Do not consume the release since the press is not consumed in
     // PressNoCaretState either.
     return nsEventStatus_eIgnore;
   }
 
+  virtual void OnScrollStart(AccessibleCaretEventHub* aContext) override
+  {
+    aContext->mManager->OnScrollStart();
+    aContext->SetState(aContext->ScrollState());
+  }
+
   virtual void OnReflow(AccessibleCaretEventHub* aContext) override
   {
     aContext->mManager->OnReflow();
   }
 };
 
 // -----------------------------------------------------------------------------
 // Implementation of AccessibleCaretEventHub methods
--- a/layout/base/AccessibleCaretManager.cpp
+++ b/layout/base/AccessibleCaretManager.cpp
@@ -62,16 +62,18 @@ std::ostream& operator<<(std::ostream& a
   }
   return aStream;
 }
 #undef AC_PROCESS_ENUM_TO_STREAM
 
 /*static*/ bool
 AccessibleCaretManager::sSelectionBarEnabled = false;
 /*static*/ bool
+AccessibleCaretManager::sCaretShownWhenLongTappingOnEmptyContent = false;
+/*static*/ bool
 AccessibleCaretManager::sCaretsExtendedVisibility = false;
 /*static*/ bool
 AccessibleCaretManager::sCaretsScriptUpdates = false;
 /*static*/ bool
 AccessibleCaretManager::sHapticFeedback = false;
 
 AccessibleCaretManager::AccessibleCaretManager(nsIPresShell* aPresShell)
   : mPresShell(aPresShell)
@@ -84,16 +86,18 @@ AccessibleCaretManager::AccessibleCaretM
   mSecondCaret = MakeUnique<AccessibleCaret>(mPresShell);
 
   mCaretTimeoutTimer = do_CreateInstance("@mozilla.org/timer;1");
 
   static bool addedPrefs = false;
   if (!addedPrefs) {
     Preferences::AddBoolVarCache(&sSelectionBarEnabled,
                                  "layout.accessiblecaret.bar.enabled");
+    Preferences::AddBoolVarCache(&sCaretShownWhenLongTappingOnEmptyContent,
+      "layout.accessiblecaret.caret_shown_when_long_tapping_on_empty_content");
     Preferences::AddBoolVarCache(&sCaretsExtendedVisibility,
                                  "layout.accessiblecaret.extendedvisibility");
     Preferences::AddBoolVarCache(&sCaretsScriptUpdates,
       "layout.accessiblecaret.allow_script_change_updates");
     Preferences::AddBoolVarCache(&sHapticFeedback,
                                  "layout.accessiblecaret.hapticfeedback");
     addedPrefs = true;
   }
@@ -262,24 +266,40 @@ AccessibleCaretManager::UpdateCaretsForC
       // Do nothing
       break;
 
     case PositionChangedResult::Changed:
       switch (aHint) {
         case UpdateCaretsHint::Default:
           if (HasNonEmptyTextContent(GetEditingHostForFrame(frame))) {
             mFirstCaret->SetAppearance(Appearance::Normal);
+          } else if (sCaretShownWhenLongTappingOnEmptyContent) {
+            if (mFirstCaret->IsLogicallyVisible()) {
+              // Possible cases are: 1) SelectWordOrShortcut() sets the
+              // appearance to Normal. 2) When the caret is out of viewport and
+              // now scrolling into viewport, it has appearance NormalNotShown.
+              mFirstCaret->SetAppearance(Appearance::Normal);
+            } else {
+              // Possible cases are: a) Single tap on current empty content;
+              // OnSelectionChanged() sets the appearance to None due to
+              // MOUSEDOWN_REASON. b) Single tap on other empty content;
+              // OnBlur() sets the appearance to None.
+              //
+              // Do nothing to make the appearance remains None so that it can
+              // be distinguished from case 2). Also do not set the appearance
+              // to NormalNotShown here like the default update behavior.
+            }
           } else {
             mFirstCaret->SetAppearance(Appearance::NormalNotShown);
           }
           break;
 
         case UpdateCaretsHint::RespectOldAppearance:
-          // Do nothing to prevent the appearance of the caret being
-          // changed from NormalNotShown to Normal.
+          // Do nothing to preserve the appearance of the caret set by the
+          // caller.
           break;
       }
       break;
 
     case PositionChangedResult::Invisible:
       mFirstCaret->SetAppearance(Appearance::NormalNotShown);
       break;
   }
@@ -479,16 +499,20 @@ AccessibleCaretManager::SelectWordOrShor
          focusableFrame ? focusableFrame->ListTag().get() : "no frame");
 #endif
 
   // Firstly check long press on an empty editable content.
   Element* newFocusEditingHost = GetEditingHostForFrame(ptFrame);
   if (focusableFrame && newFocusEditingHost &&
       !HasNonEmptyTextContent(newFocusEditingHost)) {
     ChangeFocusToOrClearOldFocus(focusableFrame);
+
+    if (sCaretShownWhenLongTappingOnEmptyContent) {
+      mFirstCaret->SetAppearance(Appearance::Normal);
+    }
     // We need to update carets to get correct information before dispatching
     // CaretStateChangedEvent.
     UpdateCaretsWithHapticFeedback();
     DispatchCaretStateChangedEvent(CaretChangedReason::Longpressonemptycontent);
     return NS_OK;
   }
 
   bool selectable = false;
--- a/layout/base/AccessibleCaretManager.h
+++ b/layout/base/AccessibleCaretManager.h
@@ -246,16 +246,22 @@ protected:
   // boundary by 61 app units, which is 1 pixel + 1 app unit as defined in
   // AppUnit.h.
   static const int32_t kBoundaryAppUnits = 61;
 
   // Preference to show selection bars at the two ends in selection mode. The
   // selection bar is always disabled in cursor mode.
   static bool sSelectionBarEnabled;
 
+  // Preference to show caret in cursor mode when long tapping on an empty
+  // content. This also changes the default update behavior in cursor mode,
+  // which is based on the emptiness of the content, into something more
+  // heuristic. See UpdateCaretsForCursorMode() for the details.
+  static bool sCaretShownWhenLongTappingOnEmptyContent;
+
   // Android specific visibility extensions correct compatibility issues
   // with caret-drag and ActionBar visibility during page scroll.
   static bool sCaretsExtendedVisibility;
 
   // By default, javascript content selection changes closes AccessibleCarets and
   // UI interactions. Optionally, we can try to maintain the active UI, keeping
   // carets and ActionBar available.
   static bool sCaretsScriptUpdates;
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -4287,17 +4287,21 @@ FrameLayerBuilder::ComputeGeometryChange
     // background images so we can scroll and just blit them when they are flattened into
     // the same layer as scrolling content. NotifyRenderingChanged is only used to tell
     // the canvas bg image item to purge this cache. We want to be careful not to accidentally
     // purge the cache if we are just invalidating due to scrolling (ie the background image
     // moves on the scrolling layer but it's rendering stays the same) so if
     // AddOffsetAndComputeDifference is the only thing that will invalidate we skip the
     // NotifyRenderingChanged call (ComputeInvalidationRegion for background images also calls
     // NotifyRenderingChanged if anything changes).
-    if (!combined.IsEmpty()) {
+    // Only allocate a new geometry object if something actually changed, otherwise the existing
+    // one should be fine. We always reallocate for inactive layers, since these types don't
+    // implement ComputeInvalidateRegion (and rely on the ComputeDifferences call in
+    // AddPaintedDisplayItem instead).
+    if (!combined.IsEmpty() || aData->mLayerState == LAYER_INACTIVE) {
       geometry = item->AllocateGeometry(mDisplayListBuilder);
     } else if (aData->mClip == clip && invalid.IsEmpty() && changedFrames.Length() == 0) {
       notifyRenderingChanged = false;
     }
     aData->mClip.AddOffsetAndComputeDifference(entry->mCommonClipCount,
                                                shift, aData->mGeometry->ComputeInvalidationRegion(),
                                                clip, entry->mLastCommonClipCount,
                                                geometry ? geometry->ComputeInvalidationRegion() :
--- a/layout/base/GeometryUtils.cpp
+++ b/layout/base/GeometryUtils.cpp
@@ -170,21 +170,32 @@ public:
                          const nsPoint& aRelativeToBoxTopLeft,
                          CSSBoxType aBoxType)
     : mParentObject(aParentObject)
     , mResult(aResult)
     , mRelativeToFrame(aRelativeToFrame)
     , mRelativeToBoxTopLeft(aRelativeToBoxTopLeft)
     , mBoxType(aBoxType)
   {
+    if (mBoxType == CSSBoxType::Margin) {
+      // Don't include the caption margin when computing margins for a
+      // table
+      mIncludeCaptionBoxForTable = false;
+    }
   }
 
   virtual void AddBox(nsIFrame* aFrame) override
   {
     nsIFrame* f = aFrame;
+    if (mBoxType == CSSBoxType::Margin &&
+        f->GetType() == nsGkAtoms::tableFrame) {
+      // Margin boxes for table frames should be taken from the outer table
+      // frame, since that has the margin.
+      f = f->GetParent();
+    }
     nsRect box = GetBoxRectFor