Merge m-c to inbound a=merge CLOSED TREE
authorWes Kocher <wkocher@mozilla.com>
Thu, 19 Mar 2015 19:30:20 -0700
changeset 263424 4d2d97b3ba34d95b0d29ce099f8f5dc0a2ae722d
parent 263423 ae2217c25ceee746befcc7a1abeb0acb94bbd089 (current diff)
parent 263408 02ca416c13a5d084f20b10147635bdc6c4f3932a (diff)
child 263425 b2e71f32548fa5aebaa8a55ffb00e26ee3f1046d
child 263443 cdec6c4e2995b280a7975754f877e83983d2af87
child 263487 349a36b7e97ef2c6360f3602375a96536c35bf42
child 263513 ceaac3da2eff663f326dbf863029756ac8b93dff
push id4718
push userraliiev@mozilla.com
push dateMon, 11 May 2015 18:39:53 +0000
treeherdermozilla-beta@c20c4ef55f08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone39.0a1
first release with
nightly linux32
4d2d97b3ba34 / 39.0a1 / 20150320030211 / files
nightly linux64
4d2d97b3ba34 / 39.0a1 / 20150320030211 / files
nightly mac
4d2d97b3ba34 / 39.0a1 / 20150320030211 / files
nightly win32
4d2d97b3ba34 / 39.0a1 / 20150320030211 / files
nightly win64
4d2d97b3ba34 / 39.0a1 / 20150320030211 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-c to inbound a=merge CLOSED TREE
testing/docker/tester/hgrc
--- a/b2g/chrome/content/devtools/debugger.js
+++ b/b2g/chrome/content/devtools/debugger.js
@@ -18,16 +18,21 @@ XPCOMUtils.defineLazyGetter(this, "devto
 });
 
 XPCOMUtils.defineLazyGetter(this, "B2GTabList", function() {
   const { B2GTabList } =
     devtools.require("resource://gre/modules/DebuggerActors.js");
   return B2GTabList;
 });
 
+// Load the discovery module eagerly, so that it can set a device name at
+// startup.  This does not cause discovery to start listening for packets, as
+// that only happens once DevTools is enabled.
+devtools.require("devtools/toolkit/discovery/discovery");
+
 let RemoteDebugger = {
   _listening: false,
 
   /**
    * Prompt the user to accept or decline the incoming connection.
    *
    * @param session object
    *        The session object will contain at least the following fields:
--- a/b2g/components/DirectoryProvider.js
+++ b/b2g/components/DirectoryProvider.js
@@ -10,16 +10,17 @@ const Cr = Components.results;
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const XRE_OS_UPDATE_APPLY_TO_DIR = "OSUpdApplyToD"
 const UPDATE_ARCHIVE_DIR = "UpdArchD"
 const LOCAL_DIR = "/data/local";
 const UPDATES_DIR = "updates/0";
 const FOTA_DIR = "updates/fota";
+const COREAPPSDIR_PREF = "b2g.coreappsdir"
 
 XPCOMUtils.defineLazyServiceGetter(Services, "env",
                                    "@mozilla.org/process/environment;1",
                                    "nsIEnvironment");
 
 XPCOMUtils.defineLazyServiceGetter(Services, "um",
                                    "@mozilla.org/updates/update-manager;1",
                                    "nsIUpdateManager");
@@ -92,22 +93,37 @@ DirectoryProvider.prototype = {
     if (prop == XRE_OS_UPDATE_APPLY_TO_DIR) {
       // getUpdateDir will set persistent to false since it may toggle between
       // /data/local/ and /mnt/sdcard based on free space and/or availability
       // of the sdcard.
       // before apply, check if free space is 1.1 times of update.mar
       return this.getUpdateDir(persistent, FOTA_DIR, 1.1);
     }
 #else
-    // In desktop builds, coreAppsDir is the same as the profile directory.
-    // We just need to get the path from the parent, and it is then used to
-    // build jar:remoteopenfile:// uris.
+    // In desktop builds, coreAppsDir is the same as the profile
+    // directory unless otherwise specified. We just need to get the
+    // path from the parent, and it is then used to build
+    // jar:remoteopenfile:// uris.
     if (prop == "coreAppsDir") {
-      let appsDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
-      appsDir.append("webapps");
+      let coreAppsDirPref;
+      try {
+        coreAppsDirPref = Services.prefs.getCharPref(COREAPPSDIR_PREF);
+      } catch (e) {
+        // coreAppsDirPref may not exist if we're on an older version
+        // of gaia, so just fail silently.
+      }
+      let appsDir;
+      // If pref doesn't exist or isn't set, default to old value
+      if (!coreAppsDirPref || coreAppsDirPref == "") {
+        appsDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
+        appsDir.append("webapps");
+      } else {
+        appsDir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile)
+        appsDir.initWithPath(coreAppsDirPref);
+      }
       persistent.value = true;
       return appsDir;
     } else if (prop == "ProfD") {
       let inParent = Cc["@mozilla.org/xre/app-info;1"]
                        .getService(Ci.nsIXULRuntime)
                        .processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
       if (inParent) {
         // Just bail out to use the default from toolkit.
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="40f5ce12859076b63307480ff2f0bbdf42bbf258"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="acca7d20b79978cff975e755e5e0bac9f731a6f7"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
@@ -133,12 +133,12 @@
   <project name="platform/frameworks/av" path="frameworks/av" revision="fd359e3a74a658d9eaab1c683440ba5412535777"/>
   <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
   <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
   <project name="kernel/common" path="kernel" revision="df636d2c31ad4434a7de2565359ad982b3767118"/>
   <project name="platform/system/core" path="system/core" revision="a626f6c0ef9e88586569331bd7387b569eaa5ed2"/>
   <project name="u-boot" path="u-boot" revision="f1502910977ac88f43da7bf9277c3523ad4b0b2f"/>
   <project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="4c59900937dc2e978b7b14b7f1ea617e3d5d550e"/>
-  <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="1344e9e5b9d0b6c8f7909731f9d14fabeb1bdc8f"/>
+  <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="8a81f598126f4153b80c1a2425343174b6cffd79"/>
   <project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
   <project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
 </manifest>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="40f5ce12859076b63307480ff2f0bbdf42bbf258"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="acca7d20b79978cff975e755e5e0bac9f731a6f7"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="40f5ce12859076b63307480ff2f0bbdf42bbf258"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="acca7d20b79978cff975e755e5e0bac9f731a6f7"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="40f5ce12859076b63307480ff2f0bbdf42bbf258"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="acca7d20b79978cff975e755e5e0bac9f731a6f7"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="fb3845864573857677f9b500040a8f011eaf5078"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="354496e8eddd28c743d8e02c02eeab02958367e6"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="b37c91354272b7413a0dc058b7445e677921d39e"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="a227c92e0170bcf2296a63386956946b0dd78ca7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="884626610186b6dbea52cec5194b1c4bcfe1cb98"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="29f9b82faa1af9730f52e933dca848546cbea84c"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="93f9ba577f68d772093987c2f1c0a4ae293e1802"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="527d1c939ee57deb7192166e56e2a3fffa8cb087"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="40f5ce12859076b63307480ff2f0bbdf42bbf258"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="acca7d20b79978cff975e755e5e0bac9f731a6f7"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="40f5ce12859076b63307480ff2f0bbdf42bbf258"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="acca7d20b79978cff975e755e5e0bac9f731a6f7"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "c39e15f631de80c69467fda0d4ea0bcda9e194ca", 
+        "git_revision": "8837f94418d69a0b06c1f4843b0779e2bb72165a", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "a7732a117f9499b15cc27c022a11cd75ef71f477", 
+    "revision": "e034a24fa2dcff4623dce2d91f7828d582abf50d", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="40f5ce12859076b63307480ff2f0bbdf42bbf258"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="acca7d20b79978cff975e755e5e0bac9f731a6f7"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="52775e03a2d8532429dff579cb2cd56718e488c3">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="c39e15f631de80c69467fda0d4ea0bcda9e194ca"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="8837f94418d69a0b06c1f4843b0779e2bb72165a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="86cd7486d8e50eaac8ef6fe2f51f09d25194577b"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ed2cf97a6c37a4bbd0bbbbffe06ec7136d8c79ff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="40f5ce12859076b63307480ff2f0bbdf42bbf258"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="acca7d20b79978cff975e755e5e0bac9f731a6f7"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="50d1ca4ab8add54523b7bc692860d57e8ee4c0d1"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="fb3845864573857677f9b500040a8f011eaf5078"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="354496e8eddd28c743d8e02c02eeab02958367e6"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="b37c91354272b7413a0dc058b7445e677921d39e"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="a227c92e0170bcf2296a63386956946b0dd78ca7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="884626610186b6dbea52cec5194b1c4bcfe1cb98"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="29f9b82faa1af9730f52e933dca848546cbea84c"/>
@@ -137,17 +137,16 @@
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="ea531874885eed7f68802048218ed86dde927f58"/>
   <project name="platform_frameworks_base" path="frameworks/base" remote="b2g" revision="df7e0cfbbc7e954ed26c73ac17832a5ff035f046"/>
   <project name="platform_frameworks_wilhelm" path="frameworks/wilhelm" remote="b2g" revision="73f7e7f12c8c5459f7a39e2fa343f083c942864d"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="4df51d9abf6cc9a6ec49b965e621699e0e6dc4fb"/>
   <default remote="caf" revision="refs/tags/android-5.0.0_r6" sync-j="4"/>
   <!-- Nexus 5 specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="ba62cc8b78c30d36181b8060a2016cc8da166236"/>
   <project name="device-hammerhead" path="device/lge/hammerhead" remote="b2g" revision="c37663f828891cf7a49451a04f3f1ce7f7e5c054"/>
-  <project name="device/qcom/common" path="device/qcom/common" remote="caf" revision="3697e5acf25629b82658334e3f8ee3b6df5becab"/>
   <project name="device_lge_hammerhead-kernel" path="device/lge/hammerhead-kernel" remote="b2g" revision="1268f640184df5ef759ada669f101a613451673a"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="0cb8574d338bf9f15b45ace7c08ad6deae9673ee"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="16abda2258c9aa1ed78f00bb0a9b2b43b4cb919e"/>
   <project name="platform/hardware/broadcom/libbt" path="hardware/broadcom/libbt" revision="3e856528121ae0af0ca26c97cb563160c7e16d85"/>
   <project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="d9e716e14e685f4fc124ae591a1cd0899bc4d7b5"/>
   <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="505aa92674337b76ce7d038800f2a6d7dc340ac9"/>
   <project name="hardware_qcom_display" path="hardware/qcom/display" remote="b2g" revision="c43952000d57f08b93a0e4fb77052871ce587976"/>
   <project name="platform/hardware/qcom/keymaster" path="hardware/qcom/keymaster" revision="028649652cd8f8f18cfb47d34bd78c435eb030ca"/>
--- a/browser/base/content/browser-loop.js
+++ b/browser/base/content/browser-loop.js
@@ -364,17 +364,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
         this._tabChangeListeners = new Set();
         gBrowser.addEventListener("select", this);
       }
 
       this._tabChangeListeners.add(listener);
       this._maybeShowBrowserSharingInfoBar();
 
       // Get the first window Id for the listener.
-      listener(null, gBrowser.selectedTab.linkedBrowser.outerWindowID);
+      listener(null, gBrowser.selectedBrowser.outerWindowID);
     },
 
     /**
      * Removes a listener from browser sharing.
      *
      * @param {Function} listener The listener to remove from the list.
      */
     removeBrowserSharingListener: function(listener) {
@@ -458,17 +458,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
     /**
      * Hides the infobar, permanantly if requested.
      *
      * @param {Boolean} permanently Flag that determines if the infobar will never
      *                              been shown again. Defaults to `false`.
      * @return {Boolean} |true| if the infobar was hidden here.
      */
     _hideBrowserSharingInfoBar: function(permanently = false, browser) {
-      browser = browser || gBrowser.selectedTab.linkedBrowser;
+      browser = browser || gBrowser.selectedBrowser;
       let box = gBrowser.getNotificationBox(browser);
       let notification = box.getNotificationWithValue(kBrowserSharingNotificationId);
       let removed = false;
       if (notification) {
         box.removeNotification(notification);
         removed = true;
       }
 
@@ -492,17 +492,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
       // Hide the infobar from the previous tab.
       if (event.fromTab) {
         wasVisible = this._hideBrowserSharingInfoBar(false, event.fromTab.linkedBrowser);
       }
 
       // We've changed the tab, so get the new window id.
       for (let listener of this._tabChangeListeners) {
         try {
-          listener(null, gBrowser.selectedTab.linkedBrowser.outerWindowID);
+          listener(null, gBrowser.selectedBrowser.outerWindowID);
         } catch (ex) {
           Cu.reportError("Tab switch caused an error: " + ex.message);
         }
       };
 
       if (wasVisible) {
         // If the infobar was visible before, we should show it again after the
         // switch.
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -480,17 +480,17 @@ let AboutHomeListener = {
 AboutHomeListener.init(this);
 
 let AboutReaderListener = {
 
   _articlePromise: null,
 
   init: function() {
     addEventListener("AboutReaderContentLoaded", this, false, true);
-    addEventListener("pageshow", this, false);
+    addEventListener("DOMContentLoaded", this, false);
     addEventListener("pagehide", this, false);
     addMessageListener("Reader:ParseDocument", this);
   },
 
   receiveMessage: function(message) {
     switch (message.name) {
       case "Reader:ParseDocument":
         this._articlePromise = ReaderMode.parseDocument(content.document).catch(Cu.reportError);
@@ -520,17 +520,17 @@ let AboutReaderListener = {
           new AboutReader(global, content, this._articlePromise);
         }
         break;
 
       case "pagehide":
         sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: false });
         break;
 
-      case "pageshow":
+      case "DOMContentLoaded":
         if (!ReaderMode.isEnabledForParseOnLoad || this.isAboutReader) {
           return;
         }
 
         let isArticle = ReaderMode.isProbablyReaderable(content.document);
         sendAsyncMessage("Reader:UpdateReaderButton", { isArticle: isArticle });
     }
   }
--- a/browser/base/content/test/general/browser_autocomplete_enter_race.js
+++ b/browser/base/content/test/general/browser_autocomplete_enter_race.js
@@ -17,12 +17,12 @@ add_task(function*() {
   yield new Promise(resolve => waitForFocus(resolve, window));
 
   yield promiseAutocompleteResultPopup("keyword bear");
   gURLBar.focus();
   EventUtils.synthesizeKey("d", {});
   EventUtils.synthesizeKey("VK_RETURN", {});
 
   yield promiseTabLoadEvent(gBrowser.selectedTab);
-  is(gBrowser.selectedTab.linkedBrowser.currentURI.spec,
+  is(gBrowser.selectedBrowser.currentURI.spec,
      "http://example.com/?q=beard",
      "Latest typed characters should have been used");
 });
--- a/browser/components/loop/MozLoopAPI.jsm
+++ b/browser/components/loop/MozLoopAPI.jsm
@@ -278,17 +278,17 @@ function injectLoopAPI(targetWindow) {
      *
      * @param {Function} listener The listener to handle the windowId changes.
      */
     addBrowserSharingListener: {
       enumerable: true,
       writable: true,
       value: function(listener) {
         let win = Services.wm.getMostRecentWindow("navigator:browser");
-        let browser = win && win.gBrowser.selectedTab.linkedBrowser;
+        let browser = win && win.gBrowser.selectedBrowser;
         if (!win || !browser) {
           // This may happen when an undocked conversation window is the only
           // window left.
           let err = new Error("No tabs available to share.");
           MozLoopService.log.error(err);
           listener(cloneValueInto(err, targetWindow));
           return;
         }
--- a/browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js
+++ b/browser/components/loop/test/mochitest/browser_mozLoop_sharingListeners.js
@@ -137,17 +137,17 @@ add_task(function* test_infoBar() {
   yield promiseWindowIdReceivedNewTab();
   Assert.strictEqual(gBrowser.selectedTab, createdTabs[1],
     "The second tab created should be selected now");
 
   // Add one sharing listener, which should show the infobar.
   yield promiseWindowIdReceivedOnAdd(handlers[0]);
 
   let getInfoBar = function() {
-    let box = gBrowser.getNotificationBox(gBrowser.selectedTab.linkedBrowser);
+    let box = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
     return box.getNotificationWithValue(kBrowserSharingNotificationId);
   };
 
   let testBarProps = function() {
     let bar = getInfoBar();
 
     // Start with some basic assertions for the bar.
     Assert.ok(bar, "The notification bar should be visible");
--- a/browser/components/preferences/in-content/content.js
+++ b/browser/components/preferences/in-content/content.js
@@ -21,17 +21,17 @@ var gContentPane = {
 
     // Show translation preferences if we may:
     const prefName = "browser.translation.ui.show";
     if (Services.prefs.getBoolPref(prefName)) {
       let row = document.getElementById("translationBox");
       row.removeAttribute("hidden");
     }
 
-    setEventListener("font.language.group", "change",
+    setEventListener("font.language.group", "blur",
       gContentPane._rebuildFonts);
     setEventListener("popupPolicyButton", "command",
       gContentPane.showPopupExceptions);
     setEventListener("advancedFonts", "command",
       gContentPane.configureFonts);
     setEventListener("colors", "command",
       gContentPane.configureColors);
     setEventListener("chooseLanguage", "command",
--- a/browser/components/sessionstore/ContentRestore.jsm
+++ b/browser/components/sessionstore/ContentRestore.jsm
@@ -275,93 +275,35 @@ ContentRestoreInternal.prototype = {
         this.resetRestore();
 
         finishCallback();
       }
     });
   },
 
   /**
-   * Accumulates a list of frames that need to be restored for the given browser
-   * element. A frame is only restored if its current URL matches the one saved
-   * in the session data. Each frame to be restored is returned along with its
-   * associated session data.
-   *
-   * @param browser the browser being restored
-   * @return an array of [frame, data] pairs
-   */
-  getFramesToRestore: function (content, data) {
-    function hasExpectedURL(aDocument, aURL) {
-      return !aURL || aURL.replace(/#.*/, "") == aDocument.location.href.replace(/#.*/, "");
-    }
-
-    let frameList = [];
-
-    function enumerateFrame(content, data) {
-      // Skip the frame if the user has navigated away before loading finished.
-      if (!hasExpectedURL(content.document, data.url)) {
-        return;
-      }
-
-      frameList.push([content, data]);
-
-      for (let i = 0; i < content.frames.length; i++) {
-        if (data.children && data.children[i]) {
-          enumerateFrame(content.frames[i], data.children[i]);
-        }
-      }
-    }
-
-    enumerateFrame(content, data);
-
-    return frameList;
-  },
-
-  /**
    * Finish restoring the tab by filling in form data and setting the scroll
    * position. The restore is complete when this function exits. It should be
    * called when the "load" event fires for the restoring tab.
    */
   restoreDocument: function () {
     this._epoch = 0;
 
     if (!this._restoringDocument) {
       return;
     }
     let {entry, pageStyle, formdata, scrollPositions} = this._restoringDocument;
     this._restoringDocument = null;
 
-    let window = this.docShell.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindow);
-    let frameList = this.getFramesToRestore(window, entry);
+    let window = this.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                               .getInterface(Ci.nsIDOMWindow);
 
-    // Support the old pageStyle format.
-    if (typeof(pageStyle) === "string") {
-      PageStyle.restore(this.docShell, frameList, pageStyle);
-    } else {
-      PageStyle.restoreTree(this.docShell, pageStyle);
-    }
-
+    PageStyle.restoreTree(this.docShell, pageStyle);
     FormData.restoreTree(window, formdata);
     ScrollPosition.restoreTree(window, scrollPositions);
-
-    // We need to support the old form and scroll data for a while at least.
-    for (let [frame, data] of frameList) {
-      if (data.hasOwnProperty("formdata") || data.hasOwnProperty("innerHTML")) {
-        let formdata = data.formdata || {};
-        formdata.url = data.url;
-
-        if (data.hasOwnProperty("innerHTML")) {
-          formdata.innerHTML = data.innerHTML;
-        }
-
-        FormData.restore(frame, formdata);
-      }
-
-      ScrollPosition.restore(frame, data.scroll || "");
-    }
   },
 
   /**
    * Cancel an ongoing restore. This function can be called any time between
    * restoreHistory and restoreDocument.
    *
    * This function is called externally (if a restore is canceled) and
    * internally (when the loads for a restore have finished). In the latter
--- a/browser/components/sessionstore/PageStyle.jsm
+++ b/browser/components/sessionstore/PageStyle.jsm
@@ -11,20 +11,16 @@ const Ci = Components.interfaces;
 /**
  * The external API exported by this module.
  */
 this.PageStyle = Object.freeze({
   collect: function (docShell, frameTree) {
     return PageStyleInternal.collect(docShell, frameTree);
   },
 
-  restore: function (docShell, frameList, pageStyle) {
-    PageStyleInternal.restore(docShell, frameList, pageStyle);
-  },
-
   restoreTree: function (docShell, data) {
     PageStyleInternal.restoreTree(docShell, data);
   }
 });
 
 // Signifies that author style level is disabled for the page.
 const NO_STYLE = "_nostyle";
 
@@ -51,39 +47,16 @@ let PageStyleInternal = {
       result = result || {};
       result.disabled = true;
     }
 
     return result && Object.keys(result).length ? result : null;
   },
 
   /**
-   * Restore the selected style sheet of all the frames in frameList
-   * to match |pageStyle|.
-   * @param docShell the root docshell of all the frames
-   * @param frameList a list of [frame, data] pairs, where frame is a
-   * DOM window and data is the session restore data associated with
-   * it.
-   * @param pageStyle the title of the style sheet to apply
-   */
-  restore: function (docShell, frameList, pageStyle) {
-    let disabled = pageStyle == NO_STYLE;
-
-    let markupDocumentViewer =
-      docShell.contentViewer;
-    markupDocumentViewer.authorStyleDisabled = disabled;
-
-    for (let [frame, data] of frameList) {
-      Array.forEach(frame.document.styleSheets, function(aSS) {
-        aSS.disabled = aSS.title && aSS.title != pageStyle;
-      });
-    }
-  },
-
-  /**
    * Restores pageStyle data for the current frame hierarchy starting at the
    * |docShell's| current DOMWindow using the given pageStyle |data|.
    *
    * Warning: If the current frame hierarchy doesn't match that of the given
    * |data| object we will silently discard data for unreachable frames. We may
    * as well assign page styles to the wrong frames if some were reordered or
    * removed.
    *
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -130,19 +130,16 @@ skip-if = true
 [browser_466937.js]
 [browser_467409-backslashplosion.js]
 [browser_477657.js]
 [browser_480893.js]
 [browser_485482.js]
 [browser_485563.js]
 [browser_490040.js]
 [browser_491168.js]
-# Disabled for too many intermittent failures.
-# Can be re-enabled once bug 930202 lands.
-skip-if = true
 [browser_491577.js]
 [browser_495495.js]
 [browser_500328.js]
 [browser_514751.js]
 [browser_522375.js]
 [browser_522545.js]
 [browser_524745.js]
 [browser_528776.js]
--- a/browser/components/sessionstore/test/browser_467409-backslashplosion.js
+++ b/browser/components/sessionstore/test/browser_467409-backslashplosion.js
@@ -14,24 +14,24 @@
 // 2b. Check that formdata doesn't require JSON.parse
 //
 // 3.  [backwards compat] Use a stringified state as formdata when opening about:sessionrestore
 // 3a. Make sure there are nodes in the tree on about:sessionrestore (skipped, checking formdata is sufficient)
 // 3b. Check that there are no backslashes in the formdata
 // 3c. Check that formdata doesn't require JSON.parse
 
 const CRASH_STATE = {windows: [{tabs: [{entries: [{url: "about:mozilla" }]}]}]};
-const STATE = {entries: [createEntry(CRASH_STATE)]};
-const STATE2 = {entries: [createEntry({windows: [{tabs: [STATE]}]})]};
-const STATE3 = {entries: [createEntry(JSON.stringify(CRASH_STATE))]};
+const STATE = createEntries(CRASH_STATE);
+const STATE2 = createEntries({windows: [{tabs: [STATE]}]});
+const STATE3 = createEntries(JSON.stringify(CRASH_STATE));
 
-function createEntry(sessionData) {
+function createEntries(sessionData) {
   return {
-    url: "about:sessionrestore",
-    formdata: {id: {sessionData: sessionData}}
+    entries: [{url: "about:sessionrestore"}],
+    formdata: {id: {sessionData: sessionData}, url: "about:sessionrestore"}
   };
 }
 
 add_task(function test_nested_about_sessionrestore() {
   // Prepare a blank tab.
   let tab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
   yield promiseBrowserLoaded(browser);
--- a/browser/components/sessionstore/test/browser_491168.js
+++ b/browser/components/sessionstore/test/browser_491168.js
@@ -1,41 +1,42 @@
-/* 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/. */
-
-function test() {
-  /** Test for Bug 491168 **/
+"use strict";
 
-  waitForExplicitFinish();
+const REFERRER1 = "http://example.org/?" + Date.now();
+const REFERRER2 = "http://example.org/?" + Math.random();
 
-  const REFERRER1 = "http://example.org/?" + Date.now();
-  const REFERRER2 = "http://example.org/?" + Math.random();
-
-  let tab = gBrowser.addTab();
-  gBrowser.selectedTab = tab;
-
+add_task(function* () {
+  // Add a new tab.
+  let tab = gBrowser.selectedTab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
-  promiseBrowserLoaded(browser).then(() => {
-    let tabState = JSON.parse(ss.getTabState(tab));
-    is(tabState.entries[0].referrer,  REFERRER1,
-       "Referrer retrieved via getTabState matches referrer set via loadURI.");
-
-    tabState.entries[0].referrer = REFERRER2;
-
-    promiseTabState(tab, tabState).then(() => {
-      is(window.content.document.referrer, REFERRER2, "document.referrer matches referrer set via setTabState.");
+  yield promiseBrowserLoaded(browser);
 
-      gBrowser.removeTab(tab);
-
-      let newTab = ss.undoCloseTab(window, 0);
-      promiseTabRestored(newTab).then(() => {
-        is(window.content.document.referrer, REFERRER2, "document.referrer is still correct after closing and reopening the tab.");
-        gBrowser.removeTab(newTab);
-
-        finish();
-      });
-    });
-  });
-
+  // Load a new URI with a specific referrer.
   let referrerURI = Services.io.newURI(REFERRER1, null, null);
   browser.loadURI("http://example.org", referrerURI, null);
+  yield promiseBrowserLoaded(browser);
+
+  TabState.flush(browser);
+  let tabState = JSON.parse(ss.getTabState(tab));
+  is(tabState.entries[0].referrer,  REFERRER1,
+     "Referrer retrieved via getTabState matches referrer set via loadURI.");
+
+  tabState.entries[0].referrer = REFERRER2;
+  yield promiseTabState(tab, tabState);
+
+  is((yield promiseDocumentReferrer()), REFERRER2,
+     "document.referrer matches referrer set via setTabState.");
+  gBrowser.removeCurrentTab();
+
+  // Restore the closed tab.
+  tab = ss.undoCloseTab(window, 0);
+  yield promiseTabRestored(tab);
+
+  is((yield promiseDocumentReferrer()), REFERRER2,
+     "document.referrer is still correct after closing and reopening the tab.");
+  gBrowser.removeCurrentTab();
+});
+
+function promiseDocumentReferrer() {
+  return ContentTask.spawn(gBrowser.selectedBrowser, null, function* () {
+    return content.document.referrer;
+  });
 }
--- a/browser/components/sessionstore/test/browser_590563.js
+++ b/browser/components/sessionstore/test/browser_590563.js
@@ -1,25 +1,23 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
-  let oldState = {
+  let sessionData = {
     windows: [{
       tabs: [
         { entries: [{ url: "about:mozilla" }], hidden: true },
         { entries: [{ url: "about:blank" }], hidden: false }
       ]
     }]
   };
-  let pageData = {
-    url: "about:sessionrestore",
-    formdata: { id: { "sessionData": oldState } }
-  };
-  let state = { windows: [{ tabs: [{ entries: [pageData] }] }] };
+  let url = "about:sessionrestore";
+  let formdata = {id: {sessionData}, url};
+  let state = { windows: [{ tabs: [{ entries: [{url}], formdata }] }] };
 
   waitForExplicitFinish();
 
   newWindowWithState(state, function (win) {
     registerCleanupFunction(function () win.close());
 
     is(gBrowser.tabs.length, 1, "The total number of tabs should be 1");
     is(gBrowser.visibleTabs.length, 1, "The total number of visible tabs should be 1");
--- a/browser/components/sessionstore/test/browser_662743.js
+++ b/browser/components/sessionstore/test/browser_662743.js
@@ -56,17 +56,19 @@ function test() {
     testTabRestoreData(formData[i], expectedValues[i], callback);
   }
 }
 
 function testTabRestoreData(aFormData, aExpectedValue, aCallback) {
   let testURL =
     getRootDirectory(gTestPath) + "browser_662743_sample.html";
   let tab = gBrowser.addTab(testURL);
-  let tabState = { entries: [{ url: testURL, formdata: aFormData}] };
+
+  aFormData.url = testURL;
+  let tabState = { entries: [{ url: testURL, }], formdata: aFormData };
 
   promiseBrowserLoaded(tab.linkedBrowser).then(() => {
     promiseTabState(tab, tabState).then(() => {
       let doc = tab.linkedBrowser.contentDocument;
       let select = doc.getElementById("select_id");
       let value = select.options[select.selectedIndex].value;
 
       // Flush to make sure we have the latest form data.
--- a/browser/components/sessionstore/test/browser_739805.js
+++ b/browser/components/sessionstore/test/browser_739805.js
@@ -1,13 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+let url = "data:text/html;charset=utf-8,<input%20id='foo'>";
 let tabState = {
-  entries: [{url: "data:text/html;charset=utf-8,<input%20id='foo'>", formdata: { id: { "foo": "bar" } } }]
+  entries: [{ url }], formdata: { id: { "foo": "bar" }, url }
 };
 
 function test() {
   waitForExplicitFinish();
   Services.prefs.setBoolPref("browser.sessionstore.restore_on_demand", true);
 
   registerCleanupFunction(function () {
     if (gBrowser.tabs.length > 1)
@@ -19,18 +20,17 @@ function test() {
   let browser = tab.linkedBrowser;
 
   promiseBrowserLoaded(browser).then(() => {
     isnot(gBrowser.selectedTab, tab, "newly created tab is not selected");
 
     ss.setTabState(tab, JSON.stringify(tabState));
     is(browser.__SS_restoreState, TAB_STATE_NEEDS_RESTORE, "tab needs restoring");
 
-    let state = JSON.parse(ss.getTabState(tab));
-    let formdata = state.entries[0].formdata;
+    let {formdata} = JSON.parse(ss.getTabState(tab));
     is(formdata && formdata.id["foo"], "bar", "tab state's formdata is valid");
 
     promiseTabRestored(tab).then(() => {
       let input = browser.contentDocument.getElementById("foo");
       is(input.value, "bar", "formdata has been restored correctly");
       finish();
     });
 
--- a/browser/components/sessionstore/test/browser_aboutSessionRestore.js
+++ b/browser/components/sessionstore/test/browser_aboutSessionRestore.js
@@ -2,19 +2,20 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const CRASH_SHENTRY = {url: "about:mozilla"};
 const CRASH_TAB = {entries: [CRASH_SHENTRY]};
 const CRASH_STATE = {windows: [{tabs: [CRASH_TAB]}]};
 
-const TAB_FORMDATA = {id: {sessionData: CRASH_STATE}};
-const TAB_SHENTRY = {url: "about:sessionrestore", formdata: TAB_FORMDATA};
-const TAB_STATE = {entries: [TAB_SHENTRY]};
+const TAB_URL = "about:sessionrestore";
+const TAB_FORMDATA = {url: TAB_URL, id: {sessionData: CRASH_STATE}};
+const TAB_SHENTRY = {url: TAB_URL};
+const TAB_STATE = {entries: [TAB_SHENTRY], formdata: TAB_FORMDATA};
 
 const FRAME_SCRIPT = "data:," +
                      "content.document.getElementById('errorTryAgain').click()";
 
 add_task(function* () {
   // Prepare a blank tab.
   let tab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
--- a/browser/components/sessionstore/test/browser_formdata.js
+++ b/browser/components/sessionstore/test/browser_formdata.js
@@ -54,64 +54,16 @@ add_task(function test_formdata() {
   [{state: {formdata}}] = JSON.parse(ss.getClosedTabData(window));
   ok(!formdata, "form data has *not* been stored");
 
   // Restore the default privacy level.
   Services.prefs.clearUserPref("browser.sessionstore.privacy_level");
 });
 
 /**
- * This test ensures that we maintain backwards compatibility with the form
- * data format used pre Fx 29.
- */
-add_task(function test_old_format() {
-  const URL = "data:text/html;charset=utf-8,<input%20id=input>";
-  const VALUE = "value-" + Math.random();
-
-  // Create a tab with an iframe containing an input field.
-  let tab = gBrowser.addTab(URL);
-  let browser = tab.linkedBrowser;
-  yield promiseBrowserLoaded(browser);
-
-  // Check that the form value is restored.
-  let state = {entries: [{url: URL, formdata: {id: {input: VALUE}}}]};
-  yield promiseTabState(tab, state);
-  is((yield getInputValue(browser, "input")), VALUE, "form data restored");
-
-  // Cleanup.
-  gBrowser.removeTab(tab);
-});
-
-/**
- * This test ensures that we maintain backwards compatibility with the form
- * data form used pre Fx 29, esp. the .innerHTML property for editable docs.
- */
-add_task(function test_old_format_inner_html() {
-  const URL = "data:text/html;charset=utf-8,<h1>mozilla</h1>" +
-              "<script>document.designMode='on'</script>";
-  const VALUE = "<h1>value-" + Math.random() + "</h1>";
-
-  // Create a tab with an iframe containing an input field.
-  let tab = gBrowser.addTab(URL);
-  let browser = tab.linkedBrowser;
-  yield promiseBrowserLoaded(browser);
-
-  // Restore the tab state.
-  let state = {entries: [{url: URL, innerHTML: VALUE}]};
-  yield promiseTabState(tab, state);
-
-  // Check that the innerHTML value was restored.
-  let html = yield getInnerHTML(browser);
-  is(html, VALUE, "editable document has been restored correctly");
-
-  // Cleanup.
-  gBrowser.removeTab(tab);
-});
-
-/**
  * This test ensures that a malicious website can't trick us into restoring
  * form data into a wrong website and that we always check the stored URL
  * before doing so.
  */
 add_task(function test_url_check() {
   const URL = "data:text/html;charset=utf-8,<input%20id=input>";
   const VALUE = "value-" + Math.random();
 
--- a/browser/components/sessionstore/test/browser_formdata_format.js
+++ b/browser/components/sessionstore/test/browser_formdata_format.js
@@ -66,17 +66,19 @@ function test() {
     testTabRestoreData(formData[i], expectedValues[i], callback);
   }
 }
 
 function testTabRestoreData(aFormData, aExpectedValue, aCallback) {
   let URL = ROOT + "browser_formdata_format_sample.html";
   let tab = gBrowser.addTab("about:blank");
   let browser = tab.linkedBrowser;
-  let tabState = { entries: [{ url: URL, formdata: aFormData}] };
+
+  aFormData.url = URL;
+  let tabState = { entries: [{ url: URL }], formdata: aFormData };
 
   Task.spawn(function () {
     yield promiseBrowserLoaded(tab.linkedBrowser);
     yield promiseTabState(tab, tabState);
 
     TabState.flush(tab.linkedBrowser);
     let restoredTabState = JSON.parse(ss.getTabState(tab));
     let restoredFormData = restoredTabState.formdata;
--- a/browser/components/sessionstore/test/browser_scrollPositions.js
+++ b/browser/components/sessionstore/test/browser_scrollPositions.js
@@ -97,39 +97,15 @@ add_task(function test_scroll_nested() {
   yield sendMessage(browser, "ss-test:setScrollPosition", {x: 0, y: 0, frame: 1});
   checkScroll(tab, null, "no scroll stored");
 
   // Cleanup.
   gBrowser.removeTab(tab);
   gBrowser.removeTab(tab2);
 });
 
-/**
- * This test ensures that by moving scroll positions out of tabData.entries[]
- * we still support the old scroll data format stored per shistory entry.
- */
-add_task(function test_scroll_old_format() {
-  const TAB_STATE = { entries: [{url: URL, scroll: SCROLL_STR}] };
-
-  // Add a blank tab.
-  let tab = gBrowser.addTab("about:blank");
-  let browser = tab.linkedBrowser;
-  yield promiseBrowserLoaded(browser);
-
-  // Apply the tab state with the old format.
-  yield promiseTabState(tab, TAB_STATE);
-
-  // Check that the scroll positions has been applied.
-  let scroll = yield sendMessage(browser, "ss-test:getScrollPosition");
-  is(JSON.stringify(scroll), JSON.stringify({x: SCROLL_X, y: SCROLL_Y}),
-    "scroll position has been restored correctly");
-
-  // Cleanup.
-  gBrowser.removeTab(tab);
-});
-
 function checkScroll(tab, expected, msg) {
   let browser = tab.linkedBrowser;
   TabState.flush(browser);
 
   let scroll = JSON.parse(ss.getTabState(tab)).scroll || null;
   is(JSON.stringify(scroll), JSON.stringify(expected), msg);
 }
--- a/browser/devtools/responsivedesign/test/browser_responsive_cmd.js
+++ b/browser/devtools/responsivedesign/test/browser_responsive_cmd.js
@@ -5,17 +5,17 @@
 //
 // Whitelisting this test.
 // As part of bug 1077403, the leaking uncaught rejection should be fixed.
 //
 thisTestLeaksUncaughtRejectionsAndShouldBeFixed("destroy");
 
 function test() {
   function isOpen() {
-    return gBrowser.getBrowserContainer(gBrowser.selectedTab.linkedBrowser)
+    return gBrowser.getBrowserContainer(gBrowser.selectedBrowser)
                    .hasAttribute("responsivemode");
   }
 
   helpers.addTabWithToolbar("data:text/html;charset=utf-8,hi", function(options) {
     return helpers.audit(options, [
       {
         setup: "resize toggle",
         check: {
--- a/browser/devtools/styleinspector/css-parsing-utils.js
+++ b/browser/devtools/styleinspector/css-parsing-utils.js
@@ -10,24 +10,36 @@ const cssTokenizer  = require("devtools/
 
 /**
  * Returns the string enclosed in quotes
  */
 function quoteString(string) {
   let hasDoubleQuotes = string.contains('"');
   let hasSingleQuotes = string.contains("'");
 
+  let quote = '"';
   if (hasDoubleQuotes && !hasSingleQuotes) {
-    // In this case, no escaping required, just enclose in single-quotes
-    return "'" + string + "'";
+    quote = "'";
   }
 
-  // In all other cases, enclose in double-quotes, and escape any double-quote
-  // that may be in the string
-  return '"' + string.replace(/"/g, '\"') + '"';
+  // Quote special characters as specified by the CSS grammar.
+  // See http://www.w3.org/TR/CSS2/syndata.html#tokenization
+  // and http://www.w3.org/TR/CSS2/syndata.html#strings
+  return quote +
+    string.replace(/[\\"]/g, match => {
+      switch (match) {
+      case '\\':
+        return '\\\\';
+      case '"':
+        if (quote == '"')
+          return '\\"';
+        return match;
+      }
+    }) +
+    quote;
 }
 
 /**
  * Returns an array of CSS declarations given an string.
  * For example, parseDeclarations("width: 1px; height: 1px") would return
  * [{name:"width", value: "1px"}, {name: "height", "value": "1px"}]
  *
  * The input string is assumed to only contain declarations so { and } characters
--- a/browser/devtools/styleinspector/test/unit/test_parseDeclarations.js
+++ b/browser/devtools/styleinspector/test/unit/test_parseDeclarations.js
@@ -150,17 +150,17 @@ const TEST_DATA = [
   {input: "color: #333", expected: [{name: "color", value: "#333", priority: ""}]},
   {input: "color: #456789", expected: [{name: "color", value: "#456789", priority: ""}]},
   {input: "wat: #XYZ", expected: [{name: "wat", value: "#XYZ", priority: ""}]},
   // Test string/url quotes escaping
   {input: "content: \"this is a 'string'\"", expected: [{name: "content", value: "\"this is a 'string'\"", priority: ""}]},
   {input: 'content: "this is a \\"string\\""', expected: [{name: "content", value: '\'this is a "string"\'', priority: ""}]},
   {input: "content: 'this is a \"string\"'", expected: [{name: "content", value: '\'this is a "string"\'', priority: ""}]},
   {input: "content: 'this is a \\'string\\'", expected: [{name: "content", value: '"this is a \'string\'"', priority: ""}]},
-  {input: "content: 'this \\' is a \" really strange string'", expected: [{name: "content", value: '"this \' is a \" really strange string"', priority: ""}]},
+  {input: "content: 'this \\' is a \" really strange string'", expected: [{name: "content", value: '"this \' is a \\\" really strange string"', priority: ""}]},
   {
     input: "content: \"a not s\\\
           o very long title\"",
     expected: [
       {name: "content", value: '"a not s\
           o very long title"', priority: ""}
     ]
   },
--- a/browser/devtools/styleinspector/test/unit/test_parseSingleValue.js
+++ b/browser/devtools/styleinspector/test/unit/test_parseSingleValue.js
@@ -42,16 +42,23 @@ const TEST_DATA = [
     }
   },
   {
     input: "\"content!important\"",
     expected: {
       value: "\"content!important\"",
       priority: ""
     }
+  },
+  {
+    input: "\"all the \\\"'\\\\ special characters\"",
+    expected: {
+      value: "\"all the \\\"'\\\\ special characters\"",
+      priority: ""
+    }
   }
 ];
 
 function run_test() {
   for (let test of TEST_DATA) {
     do_print("Test input value " + test.input);
     try {
       let output = parseSingleValue(test.input);
--- a/browser/devtools/webide/modules/runtimes.js
+++ b/browser/devtools/webide/modules/runtimes.js
@@ -448,16 +448,20 @@ WiFiRuntime.prototype = {
   type: RuntimeTypes.WIFI,
   connect: function(connection) {
     let service = discovery.getRemoteService("devtools", this.deviceName);
     if (!service) {
       return promise.reject(new Error("Can't find device: " + this.name));
     }
     connection.advertisement = service;
     connection.authenticator.sendOOB = this.sendOOB;
+    // Disable the default connection timeout, since QR scanning can take an
+    // unknown amount of time.  This prevents spurious errors (even after
+    // eventual success) from being shown.
+    connection.timeoutDelay = 0;
     connection.connect();
     return promise.resolve();
   },
   get id() {
     return this.deviceName;
   },
   get name() {
     return this.deviceName;
--- a/dom/apps/AppsUtils.jsm
+++ b/dom/apps/AppsUtils.jsm
@@ -920,16 +920,20 @@ ManifestHelper.prototype = {
     }
     return icon;
   },
 
   fullLaunchPath: function(aStartPoint) {
     // If no start point is specified, we use the root launch path.
     // In all error cases, we just return null.
     if ((aStartPoint || "") === "") {
+      // W3C start_url takes precedence over mozApps launch_path
+      if (this._localeProp("start_url")) {
+        return this._baseURI.resolve(this._localeProp("start_url") || "/");
+      }
       return this._baseURI.resolve(this._localeProp("launch_path") || "/");
     }
 
     // Search for the l10n entry_points property.
     let entryPoints = this._localeProp("entry_points");
     if (!entryPoints) {
       return null;
     }
--- a/dom/apps/Webapps.jsm
+++ b/dom/apps/Webapps.jsm
@@ -774,17 +774,17 @@ this.DOMApplicationRegistry = {
               appMigrator.observe(null, "webapps-before-update-merge", null);
           } catch(e) {
             debug("Exception running app migration: ");
             debug(e.name + " " + e.message);
             debug("Skipping app migration.");
           }
         }
 
-#ifdef MOZ_WIDGET_GONK
+#ifdef MOZ_B2G
         yield this.installSystemApps();
 #endif
 
         // At first run, install preloaded apps and set up their permissions.
         for (let id in this.webapps) {
           let isPreinstalled = this.installPreinstalledApp(id);
           this.removeIfHttpsDuplicate(id);
           if (!this.webapps[id]) {
new file mode 100644
--- /dev/null
+++ b/dom/apps/tests/unit/test_manifestHelper.js
@@ -0,0 +1,23 @@
+// Test that W3C start_url takes precedence over mozApps launch_path
+function run_test() {
+  Components.utils.import("resource:///modules/AppsUtils.jsm");
+
+  let manifest1 = {
+    launch_path: "other.html"
+  };
+
+  let manifest2 = {
+    start_url: "start.html",
+    launch_path: "other.html"
+  };
+
+  var helper = new ManifestHelper(manifest1, "http://foo.com",
+    "http://foo.com/manifest.json");
+  var path = helper.fullLaunchPath();
+  do_check_true(path == "http://foo.com/other.html");
+
+  helper = new ManifestHelper(manifest2, "http://foo.com",
+    "http://foo.com/manifest.json");
+  path = helper.fullLaunchPath();
+  do_check_true(path == "http://foo.com/start.html");
+}
--- a/dom/apps/tests/unit/xpcshell.ini
+++ b/dom/apps/tests/unit/xpcshell.ini
@@ -1,7 +1,8 @@
 [DEFAULT]
 head =
 tail =
 
 [test_has_widget_criterion.js]
 [test_inter_app_comm_service.js]
 [test_manifestSanitizer.js]
+[test_manifestHelper.js]
--- a/dom/mobilemessage/tests/marionette/test_getthreads.js
+++ b/dom/mobilemessage/tests/marionette/test_getthreads.js
@@ -1,407 +1,182 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
-MARIONETTE_TIMEOUT = 40000;
-
-SpecialPowers.addPermission("sms", true, document);
-SpecialPowers.setBoolPref("dom.sms.enabled", true);
-
-let manager = window.navigator.mozMobileMessage;
-ok(manager instanceof MozMobileMessageManager,
-   "manager is instance of " + manager.constructor);
+MARIONETTE_TIMEOUT = 90000;
+MARIONETTE_HEAD_JS = 'head.js';
 
-let pendingEmulatorCmdCount = 0;
-function sendSmsToEmulator(from, text, callback) {
-  ++pendingEmulatorCmdCount;
+/**
+ * Create messages to be tested.
+ *
+ * @param aMessages
+ *        An array of
+ *          { 'incoming': [false|true],
+ *            'address': [Phone Number]
+ *            'text': [Text Body] };
+ *
+ * @return A deferred promise.
+ */
+function createMessages(aMessages) {
+  let promise = Promise.resolve();
+  aMessages.forEach((aMessage) => {
+    promise = promise.then((aMessage.incoming) ?
+      () => sendTextSmsToEmulatorAndWait(aMessage.address, aMessage.text) :
+      () => sendSmsWithSuccess(aMessage.address, aMessage.text));
+  });
 
-  let cmd = "sms send " + from + " " + text;
-  runEmulatorCmd(cmd, function(result) {
-    --pendingEmulatorCmdCount;
-
-    callback(result[0] == "OK");
-  });
+  return promise;
 }
 
-let tasks = {
-  // List of test fuctions. Each of them should call |tasks.next()| when
-  // completed or |tasks.finish()| to jump to the last one.
-  _tasks: [],
-  _nextTaskIndex: 0,
-
-  push: function(func) {
-    this._tasks.push(func);
-  },
-
-  next: function() {
-    let index = this._nextTaskIndex++;
-    let task = this._tasks[index];
-    try {
-      task.apply(null, Array.slice(arguments));
-    } catch (ex) {
-      ok(false, "test task[" + index + "] throws: " + ex);
-      // Run last task as clean up if possible.
-      if (index != this._tasks.length - 1) {
-        this.finish();
-      }
-    }
-  },
-
-  finish: function() {
-    this._tasks[this._tasks.length - 1]();
-  },
-
-  run: function() {
-    this.next();
-  }
-};
-
-function getAllMessages(callback, filter, reverse) {
-  let messages = [];
-  let request = manager.getMessages(filter, reverse || false);
-  request.onsuccess = function(event) {
-    if (!request.done) {
-      messages.push(request.result);
-      request.continue();
-      return;
-    }
-
-    window.setTimeout(callback.bind(null, messages), 0);
-  }
-}
+function checkThreads(aMessages, aNotMerged) {
+  return getAllThreads().then((aThreads) => {
+    let threadCount = aThreads.length;
 
-function deleteAllMessages() {
-  getAllMessages(function deleteAll(messages) {
-    let message = messages.shift();
-    if (!message) {
-      ok(true, "all messages deleted");
-      tasks.next();
-      return;
-    }
-
-    let request = manager.delete(message.id);
-    request.onsuccess = deleteAll.bind(null, messages);
-    request.onerror = function(event) {
-      ok(false, "failed to delete all messages");
-      tasks.finish();
-    }
-  });
-}
+    if (aNotMerged) {
+      // Threads are retrieved in reversed order of 'lastTimestamp'.
+      aThreads.reverse();
+      is(threadCount, aMessages.length, "Number of Threads.");
+      for (let i = 0; i < threadCount; i++) {
+        let thread = aThreads[i];
+        let message = aMessages[i];
+        is(thread.unreadCount, message.incoming ? 1 : 0, "Unread Count.");
+        is(thread.participants.length, 1, "Number of Participants.");
+        is(thread.participants[0], message.address, "Participants.");
+        is(thread.body, message.text, "Thread Body.");
+      }
 
-function sendMessage(to, body) {
-  manager.onsent = function() {
-    manager.onsent = null;
-    tasks.next();
-  };
-  let request = manager.send(to, body);
-  request.onerror = tasks.finish.bind(tasks);
-}
-
-function receiveMessage(from, body) {
-  manager.onreceived = function() {
-    manager.onreceived = null;
-    tasks.next();
-  };
-  sendSmsToEmulator(from, body, function(success) {
-    if (!success) {
-      tasks.finish();
-    }
-  });
-}
-
-function getAllThreads(callback) {
-  let threads = [];
-
-  let cursor = manager.getThreads();
-  ok(cursor instanceof DOMCursor,
-     "cursor is instanceof " + cursor.constructor);
-
-  cursor.onsuccess = function(event) {
-    if (!cursor.done) {
-      threads.push(cursor.result);
-      cursor.continue();
       return;
     }
 
-    window.setTimeout(callback.bind(null, threads), 0);
-  };
+    let lastBody = aMessages[aMessages.length - 1].text;
+    let unreadCount = 0;
+    let mergedThread = aThreads[0];
+    aMessages.forEach((aMessage) => {
+      if (aMessage.incoming) {
+        unreadCount++;
+      }
+    });
+    is(threadCount, 1, "Number of Threads.");
+    is(mergedThread.unreadCount, unreadCount, "Unread Count.");
+    is(mergedThread.participants.length, 1, "Number of Participants.");
+    is(mergedThread.participants[0], aMessages[0].address, "Participants.");
+    // Thread is updated according to the device 'timestamp' of the message record
+    // instead of the one from SMSC, so 'mergedThread.body' is expected to be the
+    // same to the body for the last saved SMS.
+    // See https://hg.mozilla.org/mozilla-central/annotate/436686833af0/dom/mobilemessage/gonk/MobileMessageDB.jsm#l2247
+    is(mergedThread.body, lastBody, "Thread Body.");
+  });
 }
 
-function checkThread(bodies, lastBody, unreadCount, participants,
-                     thread, callback) {
-  log("Validating MozMobileMessageThread attributes " +
-      JSON.stringify([bodies, lastBody, unreadCount, participants]));
-
-  ok(thread, "current thread should be valid.");
-
-  ok(thread.id, "thread id", "thread.id");
-  log("Got thread " + thread.id);
-
-  if (lastBody != null) {
-    is(thread.body, lastBody, "thread.body");
-  }
-
-  is(thread.unreadCount, unreadCount, "thread.unreadCount");
-
-  ok(Array.isArray(thread.participants), "thread.participants is array");
-  is(thread.participants.length, participants.length,
-     "thread.participants.length");
-  for (let i = 0; i < participants.length; i++) {
-    is(thread.participants[i], participants[i],
-       "thread.participants[" + i + "]");
-  }
-
-  // Check whether the thread does contain all the messages it supposed to have.
-  let filter = { threadId: thread.id };
-  getAllMessages(function(messages) {
-    is(messages.length, bodies.length, "messages.length and bodies.length");
-
-    for (let message of messages) {
-      let index = bodies.indexOf(message.body);
-      ok(index >= 0, "message.body '" + message.body +
-         "' should be found in bodies array.");
-      bodies.splice(index, 1);
-    }
-
-    is(bodies.length, 0, "bodies array length");
-
-    window.setTimeout(callback, 0);
-  }, filter, false);
+function testGetThreads(aMessages, aNotMerged) {
+  aNotMerged = !!aNotMerged;
+  log("aMessages: " + JSON.stringify(aMessages));
+  log("aNotMerged: " + aNotMerged);
+  return createMessages(aMessages)
+    .then(() => checkThreads(aMessages, aNotMerged))
+    .then(() => deleteAllMessages());
 }
 
-tasks.push(deleteAllMessages);
-
-tasks.push(getAllThreads.bind(null, function(threads) {
-  is(threads.length, 0, "Empty thread list at beginning.");
-  tasks.next();
-}));
-
-// Populate MobileMessageDB with messages.
-let checkFuncs = [];
-
-// [Thread 1]
-// One message only, body = "thread 1";
-// All sent message, unreadCount = 0;
-// One participant only, participants = ["5555211001"].
-tasks.push(sendMessage.bind(null, "5555211001", "thread 1"));
-checkFuncs.push(checkThread.bind(null, ["thread 1"],
-                                 "thread 1", 0, ["5555211001"]));
-
-// [Thread 2]
-// Two messages, body = "thread 2-2";
-// All sent message, unreadCount = 0;
-// One participant with two aliased addresses, participants = ["5555211002"].
-tasks.push(sendMessage.bind(null, "5555211002",   "thread 2-1"));
-tasks.push(sendMessage.bind(null, "+15555211002", "thread 2-2"));
-checkFuncs.push(checkThread.bind(null, ["thread 2-1", "thread 2-2"],
-                                 "thread 2-2", 0, ["5555211002"]));
-
-// [Thread 3]
-// Two messages, body = "thread 3-2";
-// All sent message, unreadCount = 0;
-// One participant with two aliased addresses, participants = ["+15555211003"].
-tasks.push(sendMessage.bind(null, "+15555211003", "thread 3-1"));
-tasks.push(sendMessage.bind(null, "5555211003",   "thread 3-2"));
-checkFuncs.push(checkThread.bind(null, ["thread 3-1", "thread 3-2"],
-                                 "thread 3-2", 0, ["+15555211003"]));
-
-// [Thread 4]
-// One message only, body = "thread 4";
-// All received message, unreadCount = 1;
-// One participant only, participants = ["5555211004"].
-tasks.push(receiveMessage.bind(null, "5555211004", "thread 4"));
-checkFuncs.push(checkThread.bind(null, ["thread 4"],
-                                 "thread 4", 1, ["5555211004"]));
-
-// [Thread 5]
-//
-// Thread body should be set to text body of the last message in this
-// thread. However due to BUG 840051, we're having SMSC time as message
-// timestamp and SMSC time resolution is 1 second. So it's likely that the two
-// messages have the same timestamp and we just can't tell which is the later
-// one.
-//
-// All received message, unreadCount = 2;
-// One participant with two aliased addresses, participants = ["5555211005"].
-tasks.push(receiveMessage.bind(null, "5555211005",   "thread 5-1"));
-tasks.push(receiveMessage.bind(null, "+15555211005", "thread 5-2"));
-checkFuncs.push(checkThread.bind(null, ["thread 5-1", "thread 5-2"],
-                                 null, 2, ["5555211005"]));
-
-// [Thread 6]
-//
-// Thread body should be set to text body of the last message in this
-// thread. However due to BUG 840051, we're having SMSC time as message
-// timestamp and SMSC time resolution is 1 second. So it's likely that the two
-// messages have the same timestamp and we just can't tell which is the later
-// one.
-//
-// All received message, unreadCount = 2;
-// One participant with two aliased addresses, participants = ["+15555211006"].
-tasks.push(receiveMessage.bind(null, "+15555211006", "thread 6-1"));
-tasks.push(receiveMessage.bind(null, "5555211006",   "thread 6-2"));
-checkFuncs.push(checkThread.bind(null, ["thread 6-1", "thread 6-2"],
-                                 null, 2, ["+15555211006"]));
-
-// [Thread 7]
-//
-// Thread body should be set to text body of the last message in this
-// thread. However due to BUG 840051, there might be time difference between
-// SMSC and device time. So the result of comparing the timestamps of sent and
-// received message may not follow the order of requests and may result in
-// UNEXPECTED-FAIL in following tests.
-//
-// Two received message, unreadCount = 2;
-// One participant with two aliased addresses, participants = ["5555211007"].
-tasks.push(sendMessage.bind(null,    "5555211007",   "thread 7-1"));
-tasks.push(sendMessage.bind(null,    "+15555211007", "thread 7-2"));
-tasks.push(receiveMessage.bind(null, "5555211007",   "thread 7-3"));
-tasks.push(receiveMessage.bind(null, "+15555211007", "thread 7-4"));
-checkFuncs.push(checkThread.bind(null, ["thread 7-1", "thread 7-2",
-                                        "thread 7-3", "thread 7-4"],
-                                 null, 2, ["5555211007"]));
-
-// [Thread 8]
-//
-// Thread body should be set to text body of the last message in this
-// thread. However due to BUG 840051, there might be time difference between
-// SMSC and device time. So the result of comparing the timestamps of sent and
-// received message may not follow the order of requests and may result in
-// UNEXPECTED-FAIL in following tests.
-//
-// Two received message, unreadCount = 2;
-// One participant with two aliased addresses, participants = ["5555211008"].
-tasks.push(receiveMessage.bind(null, "5555211008",   "thread 8-1"));
-tasks.push(receiveMessage.bind(null, "+15555211008", "thread 8-2"));
-tasks.push(sendMessage.bind(null,    "5555211008",   "thread 8-3"));
-tasks.push(sendMessage.bind(null,    "+15555211008", "thread 8-4"));
-checkFuncs.push(checkThread.bind(null, ["thread 8-1", "thread 8-2",
-                                        "thread 8-3", "thread 8-4"],
-                                 null, 2, ["5555211008"]));
-
-// [Thread 9]
-// Three sent message, unreadCount = 0;
-// One participant with three aliased addresses, participants = ["+15555211009"].
-tasks.push(sendMessage.bind(null, "+15555211009",  "thread 9-1"));
-tasks.push(sendMessage.bind(null, "01115555211009", "thread 9-2"));
-tasks.push(sendMessage.bind(null, "5555211009",    "thread 9-3"));
-checkFuncs.push(checkThread.bind(null, ["thread 9-1", "thread 9-2",
-                                        "thread 9-3"],
-                                 "thread 9-3", 0, ["+15555211009"]));
-
-// [Thread 10]
-// Three sent message, unreadCount = 0;
-// One participant with three aliased addresses, participants = ["+15555211010"].
-tasks.push(sendMessage.bind(null, "+15555211010",  "thread 10-1"));
-tasks.push(sendMessage.bind(null, "5555211010",    "thread 10-2"));
-tasks.push(sendMessage.bind(null, "01115555211010", "thread 10-3"));
-checkFuncs.push(checkThread.bind(null, ["thread 10-1", "thread 10-2",
-                                        "thread 10-3"],
-                                 "thread 10-3", 0, ["+15555211010"]));
-
-// [Thread 11]
-// Three sent message, unreadCount = 0;
-// One participant with three aliased addresses, participants = ["01115555211011"].
-tasks.push(sendMessage.bind(null, "01115555211011", "thread 11-1"));
-tasks.push(sendMessage.bind(null, "5555211011",    "thread 11-2"));
-tasks.push(sendMessage.bind(null, "+15555211011",  "thread 11-3"));
-checkFuncs.push(checkThread.bind(null, ["thread 11-1", "thread 11-2",
-                                        "thread 11-3"],
-                                 "thread 11-3", 0, ["01115555211011"]));
-
-// [Thread 12]
-// Three sent message, unreadCount = 0;
-// One participant with three aliased addresses, participants = ["01115555211012"].
-tasks.push(sendMessage.bind(null, "01115555211012", "thread 12-1"));
-tasks.push(sendMessage.bind(null, "+15555211012",  "thread 12-2"));
-tasks.push(sendMessage.bind(null, "5555211012",    "thread 12-3"));
-checkFuncs.push(checkThread.bind(null, ["thread 12-1", "thread 12-2",
-                                        "thread 12-3"],
-                                 "thread 12-3", 0, ["01115555211012"]));
-
-// [Thread 13]
-// Three sent message, unreadCount = 0;
-// One participant with three aliased addresses, participants = ["5555211013"].
-tasks.push(sendMessage.bind(null, "5555211013",    "thread 13-1"));
-tasks.push(sendMessage.bind(null, "+15555211013",  "thread 13-2"));
-tasks.push(sendMessage.bind(null, "01115555211013", "thread 13-3"));
-checkFuncs.push(checkThread.bind(null, ["thread 13-1", "thread 13-2",
-                                        "thread 13-3"],
-                                 "thread 13-3", 0, ["5555211013"]));
-
-// [Thread 14]
-// Three sent message, unreadCount = 0;
-// One participant with three aliased addresses, participants = ["5555211014"].
-tasks.push(sendMessage.bind(null, "5555211014",     "thread 14-1"));
-tasks.push(sendMessage.bind(null, "01115555211014", "thread 14-2"));
-tasks.push(sendMessage.bind(null, "+15555211014",   "thread 14-3"));
-checkFuncs.push(checkThread.bind(null, ["thread 14-1", "thread 14-2",
-                                        "thread 14-3"],
-                                 "thread 14-3", 0, ["5555211014"]));
-
-//[Thread 15]
-//Three sent message, unreadCount = 0;
-//One participant but might be merged to 555211015, participants = ["5555211015"].
-tasks.push(sendMessage.bind(null, "5555211015", "thread 15-1"));
-checkFuncs.push(checkThread.bind(null, ["thread 15-1"],
-                                 "thread 15-1", 0, ["5555211015"]));
-
-//[Thread 16]
-//Three sent message, unreadCount = 0;
-//One participant but might be merged to 5555211015, participants = ["555211015"].
-tasks.push(sendMessage.bind(null, "555211015", "thread 16-1"));
-checkFuncs.push(checkThread.bind(null, ["thread 16-1"],
-                                 "thread 16-1", 0, ["555211015"]));
-
-//[Thread 17]
-//Three sent message, unreadCount = 0;
-//One participant with two aliased addresses, participants = ["555211017"].
-tasks.push(sendMessage.bind(null, "+5511555211017", "thread 17-1"));
-tasks.push(sendMessage.bind(null, "555211017",      "thread 17-2"));
-checkFuncs.push(checkThread.bind(null, ["thread 17-1", "thread 17-2"],
-                                 "thread 17-2", 0, ["+5511555211017"]));
-
-//[Thread 18]
-//Three sent message, unreadCount = 0;
-//One participant with two aliased addresses, participants = ["555211018"].
-tasks.push(sendMessage.bind(null, "555211018",      "thread 18-1"));
-tasks.push(sendMessage.bind(null, "+5511555211018", "thread 18-2"));
-checkFuncs.push(checkThread.bind(null, ["thread 18-1", "thread 18-2"],
-                                 "thread 18-2", 0, ["555211018"]));
-
-// Check threads.
-tasks.push(getAllThreads.bind(null, function(threads) {
-  is(threads.length, checkFuncs.length, "number of threads got");
-
-  // Reverse threads as we iterate over them in reverse order
-  threads.reverse();
-
-  (function callback() {
-    if (!threads.length) {
-      tasks.next();
-      return;
-    }
-
-    checkFuncs.shift()(threads.shift(), callback);
-  })();
-}));
-
-tasks.push(deleteAllMessages);
-
-tasks.push(getAllThreads.bind(null, function(threads) {
-  is(threads.length, 0, "Empty thread list at the end.");
-  tasks.next();
-}));
-
-// WARNING: All tasks should be pushed before this!!!
-tasks.push(function cleanUp() {
-  if (pendingEmulatorCmdCount) {
-    window.setTimeout(cleanUp, 100);
-    return;
-  }
-
-  SpecialPowers.removePermission("sms", document);
-  SpecialPowers.clearUserPref("dom.sms.enabled");
-  finish();
+startTestCommon(function testCaseMain() {
+  // [Thread 1]
+  // One message only, body = "thread 1";
+  // All sent message, unreadCount = 0;
+  // One participant only, participants = ["5555211001"].
+  return testGetThreads([{ incoming: false, address: "5555211001", text: "thread 1" }])
+  // [Thread 2]
+  // Two messages, body = "thread 2-2";
+  // All sent message, unreadCount = 0;
+  // One participant with two aliased addresses, participants = ["5555211002"].
+    .then(() => testGetThreads([{ incoming: false, address: "5555211002", text: "thread 2-1" },
+                                { incoming: false, address: "+15555211002", text: "thread 2-2" }]))
+  // [Thread 3]
+  // Two messages, body = "thread 3-2";
+  // All sent message, unreadCount = 0;
+  // One participant with two aliased addresses, participants = ["+15555211003"].
+    .then(() => testGetThreads([{ incoming: false, address: "+15555211003", text: "thread 3-1" },
+                                { incoming: false, address: "5555211003", text: "thread 3-2" }]))
+  // [Thread 4]
+  // One message only, body = "thread 4";
+  // All received message, unreadCount = 1;
+  // One participant only, participants = ["5555211004"].
+    .then(() => testGetThreads([{ incoming: true, address: "5555211004", text: "thread 4" }]))
+  // [Thread 5]
+  // All received messages, unreadCount = 2;
+  // One participant with two aliased addresses, participants = ["5555211005"].
+    .then(() => testGetThreads([{ incoming: true, address: "5555211005", text: "thread 5-1" },
+                                { incoming: true, address: "+15555211005", text: "thread 5-2" },]))
+  // [Thread 6]
+  // All received messages, unreadCount = 2;
+  // One participant with two aliased addresses, participants = ["+15555211006"].
+    .then(() => testGetThreads([{ incoming: true, address: "+15555211006", text: "thread 6-1" },
+                                { incoming: true, address: "5555211006", text: "thread 6-2" }]))
+  // [Thread 7]
+  // 2 sent and then 2 received messages, unreadCount = 2;
+  // One participant with two aliased addresses, participants = ["5555211007"].
+    .then(() => testGetThreads([{ incoming: false, address: "5555211007", text: "thread 7-1" },
+                                { incoming: false, address: "+15555211007", text: "thread 7-2" },
+                                { incoming: true, address: "5555211007", text: "thread 7-3" },
+                                { incoming: true, address: "+15555211007", text: "thread 7-4" }]))
+  // [Thread 8]
+  // 2 received and then 2 sent messages, unreadCount = 2;
+  // One participant with two aliased addresses, participants = ["5555211008"].
+    .then(() => testGetThreads([{ incoming: true, address: "5555211008", text: "thread 8-1" },
+                                { incoming: true, address: "+15555211008", text: "thread 8-2" },
+                                { incoming: false, address: "5555211008", text: "thread 8-3" },
+                                { incoming: false, address: "+15555211008", text: "thread 8-4" }]))
+  // [Thread 9]
+  // Three sent message, unreadCount = 0;
+  // One participant with three aliased addresses, participants = ["+15555211009"].
+    .then(() => testGetThreads([{ incoming: false, address: "+15555211009", text: "thread 9-1" },
+                                { incoming: false, address: "01115555211009", text: "thread 9-2" },
+                                { incoming: false, address: "5555211009", text: "thread 9-3" }]))
+  // [Thread 10]
+  // Three sent message, unreadCount = 0;
+  // One participant with three aliased addresses, participants = ["+15555211010"].
+    .then(() => testGetThreads([{ incoming: false, address: "+15555211010", text: "thread 10-1" },
+                                { incoming: false, address: "5555211010", text: "thread 10-2" },
+                                { incoming: false, address: "01115555211010", text: "thread 10-3" }]))
+  // [Thread 11]
+  // Three sent message, unreadCount = 0;
+  // One participant with three aliased addresses, participants = ["01115555211011"].
+    .then(() => testGetThreads([{ incoming: false, address: "01115555211011", text: "thread 11-1" },
+                                { incoming: false, address: "5555211011", text: "thread 11-2" },
+                                { incoming: false, address: "+15555211011", text: "thread 11-3" }]))
+  // [Thread 12]
+  // Three sent message, unreadCount = 0;
+  // One participant with three aliased addresses, participants = ["01115555211012"].
+    .then(() => testGetThreads([{ incoming: false, address: "01115555211012", text: "thread 12-1" },
+                                { incoming: false, address: "+15555211012", text: "thread 12-2" },
+                                { incoming: false, address: "5555211012", text: "thread 12-3" }]))
+  // [Thread 13]
+  // Three sent message, unreadCount = 0;
+  // One participant with three aliased addresses, participants = ["5555211013"].
+    .then(() => testGetThreads([{ incoming: false, address: "5555211013", text: "thread 13-1" },
+                                { incoming: false, address: "+15555211013", text: "thread 13-2" },
+                                { incoming: false, address: "01115555211013", text: "thread 13-3" }]))
+  // [Thread 14]
+  // Three sent message, unreadCount = 0;
+  // One participant with three aliased addresses, participants = ["5555211014"].
+    .then(() => testGetThreads([{ incoming: false, address: "5555211014", text: "thread 14-1" },
+                                { incoming: false, address: "01115555211014", text: "thread 14-2" },
+                                { incoming: false, address: "+15555211014", text: "thread 14-3" }]))
+  // [Thread 15]
+  // One sent message, unreadCount = 0;
+  // One participant but might be merged to 555211015, participants = ["5555211015"].
+  // [Thread 16]
+  // One sent message, unreadCount = 0;
+  // One participant but might be merged to 5555211015, participants = ["555211015"].
+    .then(() => testGetThreads([{ incoming: false, address: "5555211015", text: "thread 15-1" },
+                                { incoming: false, address: "555211015", text: "thread 16-1" }],
+                                true))
+  // [Thread 17]
+  // Brazil number format: +55-aa-nnnnnnnn. (2-digit area code and 8-digit number)
+  // Two sent messages, unreadCount = 0;
+  // One participant with two aliased addresses, participants = ["+551155211017"].
+    .then(() => testGetThreads([{ incoming: false, address: "+551155211017", text: "thread 17-1" },
+                                { incoming: false, address: "1155211017", text: "thread 17-2" }]))
+  // [Thread 18]
+  // Brazil number format: +55-aa-nnnnnnnn. (2-digit area code and 8-digit number)
+  // All sent messages, unreadCount = 0;
+  // One participant with two aliased addresses, participants = ["1155211018"].
+    .then(() => testGetThreads([{ incoming: false, address: "1155211018", text: "thread 18-1" },
+                                { incoming: false, address: "+551155211018", text: "thread 18-2" }]));
 });
-
-tasks.run();
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -2977,23 +2977,24 @@ public class BrowserApp extends GeckoApp
             MenuUtils.safeSetEnabled(aMenu, R.id.site_settings, false);
             MenuUtils.safeSetEnabled(aMenu, R.id.add_to_launcher, false);
 
             return true;
         }
 
         final boolean inGuestMode = GeckoProfile.get(this).inGuestMode();
 
-        bookmark.setEnabled(!AboutPages.isAboutReader(tab.getURL()));
+        final boolean isAboutReader = AboutPages.isAboutReader(tab.getURL());
+        bookmark.setEnabled(!isAboutReader);
         bookmark.setVisible(!inGuestMode);
         bookmark.setCheckable(true);
         bookmark.setChecked(tab.isBookmark());
         bookmark.setIcon(resolveBookmarkIconID(tab.isBookmark()));
 
-        reader.setEnabled(true);
+        reader.setEnabled(isAboutReader || !AboutPages.isAboutPage(tab.getURL()));
         reader.setVisible(!inGuestMode);
         reader.setCheckable(true);
         final boolean isPageInReadingList = tab.isInReadingList();
         reader.setChecked(isPageInReadingList);
         reader.setIcon(resolveReadingListIconID(isPageInReadingList));
         reader.setTitle(resolveReadingListTitleID(isPageInReadingList));
 
         back.setEnabled(tab.canDoBack());
--- a/mobile/android/base/GeckoProfile.java
+++ b/mobile/android/base/GeckoProfile.java
@@ -1,27 +1,31 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
  * 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/. */
 
 package org.mozilla.gecko;
 
+import java.io.BufferedWriter;
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileReader;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.nio.charset.Charset;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.Hashtable;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
+import org.json.JSONException;
+import org.json.JSONArray;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
 import org.mozilla.gecko.GeckoProfileDirectories.NoSuchProfileException;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.LocalBrowserDB;
 import org.mozilla.gecko.db.StubBrowserDB;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.firstrun.FirstrunPane;
@@ -609,16 +613,52 @@ public final class GeckoProfile {
                 return readFile(sessionFile);
             }
         } catch (IOException ioe) {
             Log.e(LOGTAG, "Unable to read session file", ioe);
         }
         return null;
     }
 
+    public void writeFile(final String filename, final String data) {
+        File file = new File(getDir(), filename);
+        BufferedWriter bufferedWriter = null;
+        try {
+            bufferedWriter = new BufferedWriter(new FileWriter(file, false));
+            bufferedWriter.write(data);
+        } catch (IOException e) {
+            Log.e(LOGTAG, "Unable to write to file", e);
+        } finally {
+            try {
+                if (bufferedWriter != null) {
+                    bufferedWriter.close();
+                }
+            } catch (IOException e) {
+                Log.e(LOGTAG, "Error closing writer while writing to file", e);
+            }
+        }
+    }
+
+    public JSONArray readJSONArrayFromFile(final String filename) {
+        String fileContent;
+        try {
+            fileContent = readFile(filename);
+        } catch (IOException expected) {
+            return new JSONArray();
+        }
+
+        JSONArray jsonArray;
+        try {
+            jsonArray = new JSONArray(fileContent);
+        } catch (JSONException e) {
+            jsonArray = new JSONArray();
+        }
+        return jsonArray;
+    }
+
     public String readFile(String filename) throws IOException {
         File dir = getDir();
         if (dir == null) {
             throw new IOException("No profile directory found");
         }
         File target = new File(dir, filename);
         return readFile(target);
     }
--- a/mobile/android/base/ReadingListHelper.java
+++ b/mobile/android/base/ReadingListHelper.java
@@ -222,17 +222,16 @@ public final class ReadingListHelper imp
      * A page can be removed from the ReadingList by panel context menu,
      * or by tapping the readinglist-remove icon in the ReaderMode banner.
      */
     private void handleRemoveFromList(final String url) {
         ThreadUtils.postToBackgroundThread(new Runnable() {
             @Override
             public void run() {
                 readingListAccessor.removeReadingListItemWithURL(context.getContentResolver(), url);
-                GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:Removed", url));
                 showToast(R.string.page_removed, Toast.LENGTH_SHORT);
             }
         });
     }
 
     /**
      * Gecko (ReaderMode) requests the page ReadingList status, to display
      * the proper ReaderMode banner icon (readinglist-add / readinglist-remove).
--- a/mobile/android/base/db/LocalReadingListAccessor.java
+++ b/mobile/android/base/db/LocalReadingListAccessor.java
@@ -7,16 +7,20 @@ package org.mozilla.gecko.db;
 import android.content.ContentResolver;
 import android.content.ContentUris;
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.net.Uri;
 import android.util.Log;
+import org.mozilla.gecko.AboutPages;
+import org.mozilla.gecko.GeckoAppShell;
+import org.mozilla.gecko.GeckoEvent;
+import org.mozilla.gecko.ReaderModeUtils;
 import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 
 
 @RobocopTarget
 public class LocalReadingListAccessor implements ReadingListAccessor {
     private static final String LOG_TAG = "GeckoReadingListAcc";
 
@@ -64,17 +68,19 @@ public class LocalReadingListAccessor im
         return cr.query(mReadingListUriWithProfile,
                         new String[] { ReadingListItems._ID, ReadingListItems.URL },
                         ITEMS_TO_FETCH,
                         null,
                         SORT_ORDER_RECENT_FIRST);
     }
 
     @Override
-    public boolean isReadingListItem(ContentResolver cr, String uri) {
+    public boolean isReadingListItem(final ContentResolver cr, String uri) {
+        uri = stripURI(uri);
+
         final Cursor c = cr.query(mReadingListUriWithProfile,
                                   new String[] { ReadingListItems._ID },
                                   ReadingListItems.URL + " = ? OR " + ReadingListItems.RESOLVED_URL + " = ?",
                                   new String[] { uri, uri },
                                   null);
 
         if (c == null) {
             Log.e(LOG_TAG, "Null cursor in isReadingListItem");
@@ -93,23 +99,31 @@ public class LocalReadingListAccessor im
     public long addReadingListItem(ContentResolver cr, ContentValues values) {
         // Check that required fields are present.
         for (String field: ReadingListItems.REQUIRED_FIELDS) {
             if (!values.containsKey(field)) {
                 throw new IllegalArgumentException("Missing required field for reading list item: " + field);
             }
         }
 
+        // URL is a required field so no key check needed.
+        final String url = stripURI(values.getAsString(ReadingListItems.URL));
+        values.put(ReadingListItems.URL, url);
+
         // We're adding locally, so we can specify these.
         values.put(ReadingListItems.ADDED_ON, System.currentTimeMillis());
         values.put(ReadingListItems.ADDED_BY, ReadingListProvider.PLACEHOLDER_THIS_DEVICE);
 
         // We never un-delete (and we can't; we wipe as we go).
         // Re-add if necessary and allow the server to resolve conflicts.
-        return ContentUris.parseId(cr.insert(mReadingListUriWithProfile, values));
+        final long id = ContentUris.parseId(cr.insert(mReadingListUriWithProfile, values));
+
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:Added", url));
+
+        return id;
     }
 
     @Override
     public long addBasicReadingListItem(ContentResolver cr, String url, String title) {
         if (url == null) {
             throw new IllegalArgumentException("URL must not be null.");
         }
         final ContentValues values = new ContentValues();
@@ -124,33 +138,43 @@ public class LocalReadingListAccessor im
     }
 
     @Override
     public void updateReadingListItem(ContentResolver cr, ContentValues values) {
         if (!values.containsKey(ReadingListItems._ID)) {
             throw new IllegalArgumentException("Cannot update reading list item without an ID");
         }
 
+        if (values.containsKey(ReadingListItems.URL)) {
+            values.put(ReadingListItems.URL, stripURI(values.getAsString(ReadingListItems.URL)));
+        }
+
         final int updated = cr.update(mReadingListUriWithProfile,
                                       values,
                                       ReadingListItems._ID + " = ? ",
                                       new String[] { values.getAsString(ReadingListItems._ID) });
 
         Log.d(LOG_TAG, "Updated " + updated + " reading list rows.");
     }
 
     @Override
-    public void removeReadingListItemWithURL(ContentResolver cr, String uri) {
+    public void removeReadingListItemWithURL(final ContentResolver cr, String uri) {
+        uri = stripURI(uri);
         cr.delete(mReadingListUriWithProfile,
                   ReadingListItems.URL + " = ? OR " + ReadingListItems.RESOLVED_URL + " = ?",
                   new String[]{ uri, uri });
+
+        GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:Removed", uri));
     }
 
     @Override
     public void deleteItem(ContentResolver cr, long itemID) {
+        // TODO: For completness, we should send a "Reader:Removed"
+        // GeckoEvent, but we don't have the uri. Luckily, this is
+        // only called in testing at the moment.
         cr.delete(ContentUris.appendId(mReadingListUriWithProfile.buildUpon(), itemID).build(),
                   null, null);
     }
 
     @Override
     public void registerContentObserver(Context context, ContentObserver observer) {
         context.getContentResolver().registerContentObserver(mReadingListUriWithProfile, false, observer);
     }
@@ -172,9 +196,16 @@ public class LocalReadingListAccessor im
         values.put(ReadingListItems.CONTENT_STATUS, ReadingListItems.STATUS_FETCHED_ARTICLE);
         values.put(ReadingListItems.RESOLVED_URL, resolvedURL);
         values.put(ReadingListItems.RESOLVED_TITLE, resolvedTitle);
         values.put(ReadingListItems.EXCERPT, excerpt);
 
         // The ContentProvider will take care of updating the sync metadata.
         cr.update(mReadingListUriWithProfile, values, ReadingListItems._ID + " = " + itemID, null);
     }
+
+    /**
+     * Gets the URI from an about:reader URI if applicable, else returns the URI.
+     */
+    private String stripURI(final String uri) {
+        return !AboutPages.isAboutReader(uri) ? uri : ReaderModeUtils.getUrlFromAboutReader(uri);
+    }
 }
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -417,16 +417,17 @@ gbjar.sources += [
     'sqlite/ByteBufferInputStream.java',
     'sqlite/MatrixBlobCursor.java',
     'sqlite/SQLiteBridge.java',
     'sqlite/SQLiteBridgeException.java',
     'SuggestClient.java',
     'SurfaceBits.java',
     'Tab.java',
     'tabqueue/TabQueueDispatcher.java',
+    'tabqueue/TabQueueHelper.java',
     'tabqueue/TabQueueService.java',
     'Tabs.java',
     'tabs/PrivateTabsPanel.java',
     'tabs/TabCurve.java',
     'tabs/TabHistoryController.java',
     'tabs/TabHistoryFragment.java',
     'tabs/TabHistoryItemRow.java',
     'tabs/TabHistoryPage.java',
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tabqueue/TabQueueHelper.java
@@ -0,0 +1,40 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * 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/. */
+
+package org.mozilla.gecko.tabqueue;
+
+import org.mozilla.gecko.GeckoProfile;
+import org.mozilla.gecko.util.ThreadUtils;
+
+import android.text.TextUtils;
+import android.util.Log;
+import org.json.JSONArray;
+import org.json.JSONException;
+
+import java.io.IOException;
+
+public class TabQueueHelper {
+    private static final String LOGTAG = "Gecko" + TabQueueHelper.class.getSimpleName();
+
+    public static final String FILE_NAME = "tab_queue_url_list.json";
+
+    /**
+     * Reads file and converts any content to JSON, adds passed in URL to the data and writes back to the file,
+     * creating the file if it doesn't already exist.  This should not be run on the UI thread.
+     *
+     * @param profile
+     * @param url      URL to add
+     * @param filename filename to add URL to
+     */
+    public static void queueURL(final GeckoProfile profile, final String url, final String filename) {
+        ThreadUtils.assertNotOnUiThread();
+
+        JSONArray jsonArray = profile.readJSONArrayFromFile(filename);
+
+        jsonArray.put(url);
+
+        profile.writeFile(filename, jsonArray.toString());
+    }
+}
\ No newline at end of file
--- a/mobile/android/base/tabqueue/TabQueueService.java
+++ b/mobile/android/base/tabqueue/TabQueueService.java
@@ -15,20 +15,25 @@ import android.os.IBinder;
 import android.util.Log;
 import android.view.Gravity;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.WindowManager;
 import android.widget.Button;
 import android.widget.TextView;
 import org.mozilla.gecko.BrowserApp;
+import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.mozglue.ContextUtils;
 
 
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+
 /**
  * On launch this Service displays a View over the currently running process with an action to open the url in Fennec
  * immediately.  If the user takes no action, allowing the runnable to be processed after the specified
  * timeout (TOAST_TIMEOUT), the url is added to a file which is then read in Fennec on next launch, this allows the
  * user to quickly queue urls to open without having to open Fennec each time. If the Service receives an Intent whilst
  * the created View is still active, the old url is immediately processed and the View is re-purposed with the new
  * Intent data.
  * <p/>
@@ -41,34 +46,38 @@ import org.mozilla.gecko.mozglue.Context
  * meaning that we can't quickly queue the current data and re-purpose the View.  The asynchronous nature of the
  * IntentService is another prohibitive factor.
  * <p/>
  * General approach taken is similar to the FB chat heads functionality:
  * http://stackoverflow.com/questions/15975988/what-apis-in-android-is-facebook-using-to-create-chat-heads
  */
 public class TabQueueService extends Service {
     private static final String LOGTAG = "Gecko" + TabQueueService.class.getSimpleName();
+
     private static final long TOAST_TIMEOUT = 3000;
+
     private WindowManager windowManager;
     private View toastLayout;
     private Button openNowButton;
     private Handler tabQueueHandler;
     private WindowManager.LayoutParams toastLayoutParams;
     private volatile StopServiceRunnable stopServiceRunnable;
     private HandlerThread handlerThread;
+    private ExecutorService executorService;
 
     @Override
     public IBinder onBind(Intent intent) {
         // Not used
         return null;
     }
 
     @Override
     public void onCreate() {
         super.onCreate();
+        executorService = Executors.newSingleThreadExecutor();
 
         handlerThread = new HandlerThread("TabQueueHandlerThread");
         handlerThread.start();
         tabQueueHandler = new Handler(handlerThread.getLooper());
 
         windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
 
         LayoutInflater layoutInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
@@ -103,24 +112,24 @@ public class TabQueueService extends Ser
             stopServiceRunnable.run(false);
         } else {
             windowManager.addView(toastLayout, toastLayoutParams);
         }
 
         stopServiceRunnable = new StopServiceRunnable(startId) {
             @Override
             public void onRun() {
-                addUrlToTabQueue(intent);
+                addURLToTabQueue(intent, TabQueueHelper.FILE_NAME);
                 stopServiceRunnable = null;
             }
         };
 
         openNowButton.setOnClickListener(new View.OnClickListener() {
             @Override
-            public void onClick(View view) {
+            public void onClick(final View view) {
                 tabQueueHandler.removeCallbacks(stopServiceRunnable);
                 stopServiceRunnable = null;
 
 
                 Intent forwardIntent = new Intent(intent);
                 forwardIntent.setClass(getApplicationContext(), BrowserApp.class);
                 forwardIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                 startActivity(forwardIntent);
@@ -133,27 +142,33 @@ public class TabQueueService extends Ser
 
         return START_FLAG_REDELIVERY;
     }
 
     private void removeView() {
         windowManager.removeView(toastLayout);
     }
 
-    private void addUrlToTabQueue(Intent intentParam) {
-        if (intentParam == null) {
-            // This should never happen, but let's return silently instead of crash if it does.
+    private void addURLToTabQueue(final Intent intent, final String filename) {
+        if (intent == null) {
+            // This should never happen, but let's return silently instead of crashing if it does.
             Log.w(LOGTAG, "Error adding URL to tab queue - invalid intent passed in.");
             return;
         }
-        final ContextUtils.SafeIntent intent = new ContextUtils.SafeIntent(intentParam);
-        final String intentData = intent.getDataString();
+        final ContextUtils.SafeIntent safeIntent = new ContextUtils.SafeIntent(intent);
+        final String intentData = safeIntent.getDataString();
 
-        // TODO Add url to tab queue here - bug 1134235
-        Log.d(LOGTAG, "Adding URL to tab queue: " + intentData);
+        // As we're doing disk IO, let's run this stuff in a separate thread.
+        executorService.submit(new Runnable() {
+            @Override
+            public void run() {
+                final GeckoProfile profile = GeckoProfile.get(getApplicationContext());
+                TabQueueHelper.queueURL(profile, intentData, filename);
+            }
+        });
     }
 
     @Override
     public void onDestroy() {
         super.onDestroy();
         tabQueueHandler = null;
         handlerThread.quit();
     }
@@ -161,21 +176,21 @@ public class TabQueueService extends Ser
     /**
      * A modified Runnable which additionally removes the view from the window view hierarchy and stops the service
      * when run, unless explicitly instructed not to.
      */
     private abstract class StopServiceRunnable implements Runnable {
 
         private final int startId;
 
-        public StopServiceRunnable(int startId) {
+        public StopServiceRunnable(final int startId) {
             this.startId = startId;
         }
 
-        public void run(boolean shouldStopService) {
+        public void run(final boolean shouldStopService) {
             onRun();
 
             if (shouldStopService) {
                 removeView();
             }
 
             stopSelfResult(startId);
         }
--- a/mobile/android/chrome/content/Reader.js
+++ b/mobile/android/chrome/content/Reader.js
@@ -22,16 +22,23 @@ let Reader = {
 
   observe: function Reader_observe(aMessage, aTopic, aData) {
     switch (aTopic) {
       case "Reader:FetchContent": {
         let data = JSON.parse(aData);
         this._fetchContent(data.url, data.id);
         break;
       }
+
+      case "Reader:Added": {
+        let mm = window.getGroupMessageManager("browsers");
+        mm.broadcastAsyncMessage("Reader:Added", { url: aData });
+        break;
+      }
+
       case "Reader:Removed": {
         ReaderMode.removeArticleFromCache(aData).catch(e => Cu.reportError("Error removing article from cache: " + e));
 
         let mm = window.getGroupMessageManager("browsers");
         mm.broadcastAsyncMessage("Reader:Removed", { url: aData });
         break;
       }
     }
@@ -247,18 +254,16 @@ let Reader = {
     Messaging.sendRequestForResult({
       type: "Reader:AddToList",
       url: truncate(article.url, MAX_URI_LENGTH),
       title: truncate(article.title, MAX_TITLE_LENGTH),
       length: article.length,
       excerpt: article.excerpt,
       status: article.status,
     }).then((url) => {
-      let mm = window.getGroupMessageManager("browsers");
-      mm.broadcastAsyncMessage("Reader:Added", { url: url });
       ReaderMode.storeArticleInCache(article).catch(e => Cu.reportError("Error storing article in cache: " + e));
     }).catch(Cu.reportError);
   },
 
   /**
    * Gets an article for a given URL. This method will download and parse a document
    * if it does not find the article in the tab data or the cache.
    *
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -141,17 +141,17 @@ let lazilyLoadedObserverScripts = [
   ["MemoryObserver", ["memory-pressure", "Memory:Dump"], "chrome://browser/content/MemoryObserver.js"],
   ["ConsoleAPI", ["console-api-log-event"], "chrome://browser/content/ConsoleAPI.js"],
   ["FindHelper", ["FindInPage:Opened", "FindInPage:Closed", "Tab:Selected"], "chrome://browser/content/FindHelper.js"],
   ["PermissionsHelper", ["Permissions:Get", "Permissions:Clear"], "chrome://browser/content/PermissionsHelper.js"],
   ["FeedHandler", ["Feeds:Subscribe"], "chrome://browser/content/FeedHandler.js"],
   ["Feedback", ["Feedback:Show"], "chrome://browser/content/Feedback.js"],
   ["SelectionHandler", ["TextSelection:Get"], "chrome://browser/content/SelectionHandler.js"],
   ["EmbedRT", ["GeckoView:ImportScript"], "chrome://browser/content/EmbedRT.js"],
-  ["Reader", ["Reader:FetchContent", "Reader:Removed"], "chrome://browser/content/Reader.js"],
+  ["Reader", ["Reader:FetchContent", "Reader:Added", "Reader:Removed"], "chrome://browser/content/Reader.js"],
 ];
 if (AppConstants.MOZ_WEBRTC) {
   lazilyLoadedObserverScripts.push(
     ["WebrtcUI", ["getUserMedia:request", "recording-device-events"], "chrome://browser/content/WebrtcUI.js"])
 }
 
 lazilyLoadedObserverScripts.forEach(function (aScript) {
   let [name, notifications, script] = aScript;
@@ -3972,16 +3972,140 @@ Tab.prototype = {
     }
 
     if (!this.metatags[type] || this.metatags[type + "_quality"] < quality) {
       this.metatags[type] = value;
       this.metatags[type + "_quality"] = quality;
     }
   },
 
+  sanitizeRelString: function(linkRel) {
+    // Sanitize the rel string
+    let list = [];
+    if (linkRel) {
+      list = linkRel.toLowerCase().split(/\s+/);
+      let hash = {};
+      list.forEach(function(value) { hash[value] = true; });
+      list = [];
+      for (let rel in hash)
+      list.push("[" + rel + "]");
+    }
+    return list;
+  },
+
+  makeFaviconMessage: function(eventTarget) {
+    // We want to get the largest icon size possible for our UI.
+    let maxSize = 0;
+
+    // We use the sizes attribute if available
+    // see http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon
+    if (eventTarget.hasAttribute("sizes")) {
+      let sizes = eventTarget.getAttribute("sizes").toLowerCase();
+
+      if (sizes == "any") {
+        // Since Java expects an integer, use -1 to represent icons with sizes="any"
+        maxSize = -1;
+      } else {
+        let tokens = sizes.split(" ");
+        tokens.forEach(function(token) {
+          // TODO: check for invalid tokens
+          let [w, h] = token.split("x");
+          maxSize = Math.max(maxSize, Math.max(w, h));
+        });
+      }
+    }
+    return {
+      type: "Link:Favicon",
+      tabID: this.id,
+      href: resolveGeckoURI(eventTarget.href),
+      size: maxSize,
+      mime: eventTarget.getAttribute("type") || ""
+    };
+  },
+
+  makeFeedMessage: function(eventTarget, targetType) {
+    try {
+      // urlSecurityCeck will throw if things are not OK
+      ContentAreaUtils.urlSecurityCheck(eventTarget.href,
+            eventTarget.ownerDocument.nodePrincipal,
+            Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
+
+      if (!this.browser.feeds)
+        this.browser.feeds = [];
+
+      this.browser.feeds.push({
+        href: eventTarget.href,
+        title: eventTarget.title,
+        type: targetType
+      });
+
+      return {
+        type: "Link:Feed",
+        tabID: this.id
+      };
+    } catch (e) {
+        return null;
+    }
+  },
+
+  makeOpenSearchMessage: function(eventTarget) {
+    let type = eventTarget.type && eventTarget.type.toLowerCase();
+    // Replace all starting or trailing spaces or spaces before "*;" globally w/ "".
+    type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
+
+    // Check that type matches opensearch.
+    let isOpenSearch = (type == "application/opensearchdescription+xml");
+    if (isOpenSearch && eventTarget.title && /^(?:https?|ftp):/i.test(eventTarget.href)) {
+      Services.search.init(() => {
+        let visibleEngines = Services.search.getVisibleEngines();
+        // NOTE: Engines are currently identified by name, but this can be changed
+        // when Engines are identified by URL (see bug 335102).
+        if (visibleEngines.some(function(e) {
+          return e.name == eventTarget.title;
+        })) {
+          // This engine is already present, do nothing.
+          return null;
+        }
+
+        if (this.browser.engines) {
+          // This engine has already been handled, do nothing.
+          if (this.browser.engines.some(function(e) {
+            return e.url == eventTarget.href;
+          })) {
+            return null;
+          }
+        } else {
+            this.browser.engines = [];
+        }
+
+        // Get favicon.
+        let iconURL = eventTarget.ownerDocument.documentURIObject.prePath + "/favicon.ico";
+
+        let newEngine = {
+          title: eventTarget.title,
+          url: eventTarget.href,
+          iconURL: iconURL
+        };
+
+        this.browser.engines.push(newEngine);
+
+        // Don't send a message to display engines if we've already handled an engine.
+        if (this.browser.engines.length > 1)
+          return null;
+
+        // Broadcast message that this tab contains search engines that should be visible.
+        return {
+          type: "Link:OpenSearch",
+          tabID: this.id,
+          visible: true
+        };
+      });
+    }
+  },
+
   handleEvent: function(aEvent) {
     switch (aEvent.type) {
       case "DOMContentLoaded": {
         let target = aEvent.originalTarget;
 
         // ignore on frames and other documents
         if (target != this.browser.contentDocument)
           return;
@@ -4061,143 +4185,44 @@ Tab.prototype = {
             this.addMetadata("tileColor", target.content, this.METADATA_GOOD_MATCH);
             break;
         }
 
         break;
 
       case "DOMLinkAdded":
       case "DOMLinkChanged": {
+        let jsonMessage = null;
         let target = aEvent.originalTarget;
         if (!target.href || target.disabled)
           return;
 
         // Ignore on frames and other documents
         if (target.ownerDocument != this.browser.contentDocument)
           return;
 
-        // Sanitize the rel string
-        let list = [];
-        if (target.rel) {
-          list = target.rel.toLowerCase().split(/\s+/);
-          let hash = {};
-          list.forEach(function(value) { hash[value] = true; });
-          list = [];
-          for (let rel in hash)
-            list.push("[" + rel + "]");
-        }
-
+        // Sanitize rel link
+        let list = this.sanitizeRelString(target.rel);
         if (list.indexOf("[icon]") != -1) {
-          // We want to get the largest icon size possible for our UI.
-          let maxSize = 0;
-
-          // We use the sizes attribute if available
-          // see http://www.whatwg.org/specs/web-apps/current-work/multipage/links.html#rel-icon
-          if (target.hasAttribute("sizes")) {
-            let sizes = target.getAttribute("sizes").toLowerCase();
-
-            if (sizes == "any") {
-              // Since Java expects an integer, use -1 to represent icons with sizes="any"
-              maxSize = -1; 
-            } else {
-              let tokens = sizes.split(" ");
-              tokens.forEach(function(token) {
-                // TODO: check for invalid tokens
-                let [w, h] = token.split("x");
-                maxSize = Math.max(maxSize, Math.max(w, h));
-              });
-            }
-          }
-
-          let json = {
-            type: "Link:Favicon",
-            tabID: this.id,
-            href: resolveGeckoURI(target.href),
-            size: maxSize,
-            mime: target.getAttribute("type") || ""
-          };
-          Messaging.sendRequest(json);
+          jsonMessage = this.makeFaviconMessage(target);
         } else if (list.indexOf("[alternate]") != -1 && aEvent.type == "DOMLinkAdded") {
           let type = target.type.toLowerCase().replace(/^\s+|\s*(?:;.*)?$/g, "");
           let isFeed = (type == "application/rss+xml" || type == "application/atom+xml");
 
           if (!isFeed)
             return;
 
-          try {
-            // urlSecurityCeck will throw if things are not OK
-            ContentAreaUtils.urlSecurityCheck(target.href, target.ownerDocument.nodePrincipal, Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
-
-            if (!this.browser.feeds)
-              this.browser.feeds = [];
-            this.browser.feeds.push({ href: target.href, title: target.title, type: type });
-
-            let json = {
-              type: "Link:Feed",
-              tabID: this.id
-            };
-            Messaging.sendRequest(json);
-          } catch (e) {}
+          jsonMessage = this.makeFeedMessage(target, type);
         } else if (list.indexOf("[search]" != -1) && aEvent.type == "DOMLinkAdded") {
-          let type = target.type && target.type.toLowerCase();
-
-          // Replace all starting or trailing spaces or spaces before "*;" globally w/ "".
-          type = type.replace(/^\s+|\s*(?:;.*)?$/g, "");
-
-          // Check that type matches opensearch.
-          let isOpenSearch = (type == "application/opensearchdescription+xml");
-          if (isOpenSearch && target.title && /^(?:https?|ftp):/i.test(target.href)) {
-            Services.search.init(() => {
-              let visibleEngines = Services.search.getVisibleEngines();
-              // NOTE: Engines are currently identified by name, but this can be changed
-              // when Engines are identified by URL (see bug 335102).
-              if (visibleEngines.some(function(e) {
-                return e.name == target.title;
-              })) {
-                // This engine is already present, do nothing.
-                return;
-              }
-
-              if (this.browser.engines) {
-                // This engine has already been handled, do nothing.
-                if (this.browser.engines.some(function(e) {
-                  return e.url == target.href;
-                })) {
-                    return;
-                }
-              } else {
-                this.browser.engines = [];
-              }
-
-              // Get favicon.
-              let iconURL = target.ownerDocument.documentURIObject.prePath + "/favicon.ico";
-
-              let newEngine = {
-                title: target.title,
-                url: target.href,
-                iconURL: iconURL
-              };
-
-              this.browser.engines.push(newEngine);
-
-              // Don't send a message to display engines if we've already handled an engine.
-              if (this.browser.engines.length > 1)
-                return;
-
-              // Broadcast message that this tab contains search engines that should be visible.
-              let newEngineMessage = {
-                type: "Link:OpenSearch",
-                tabID: this.id,
-                visible: true
-              };
-
-              Messaging.sendRequest(newEngineMessage);
-            });
-          }
+          jsonMessage = this.makeOpenSearchMessage(target);
         }
+        if (!jsonMessage)
+         return;
+
+        Messaging.sendRequest(jsonMessage);
         break;
       }
 
       case "DOMTitleChanged": {
         if (!aEvent.isTrusted)
           return;
 
         // ignore on frames and other documents
--- a/mozglue/build/WindowsDllBlocklist.cpp
+++ b/mozglue/build/WindowsDllBlocklist.cpp
@@ -164,16 +164,19 @@ static DllBlockInfo sWindowsDllBlocklist
   { "activedetect64.dll", UNVERSIONED },
   { "windowsapihookdll32.dll", UNVERSIONED },
   { "windowsapihookdll64.dll", UNVERSIONED },
 
   // Flash crashes with RealNetworks RealDownloader, bug 1132663
   { "rndlnpshimswf.dll", ALL_VERSIONS },
   { "rndlmainbrowserrecordplugin.dll", ALL_VERSIONS },
 
+  // Crashes with CyberLink YouCam, bug 1136968
+  { "ycwebcamerasource.ax", MAKE_VERSION(2, 0, 0, 1611) },
+
   { nullptr, 0 }
 };
 
 #ifndef STATUS_DLL_NOT_FOUND
 #define STATUS_DLL_NOT_FOUND ((DWORD)0xC0000135L)
 #endif
 
 // define this for very verbose dll load debug spew
--- a/testing/docker/tester/Dockerfile
+++ b/testing/docker/tester/Dockerfile
@@ -1,27 +1,28 @@
 FROM          quay.io/mozilla/base-test:0.0.5
 MAINTAINER    Jonas Finnemann Jensen <jopsen@gmail.com>
 
 # Add utilities and configuration
 COPY           b2g-desktop-config.py         /home/worker/b2g-desktop-config.py
 COPY           dot-config                    /home/worker/.config
 COPY           dot-pulse                     /home/worker/.pulse
-COPY           hgrc                          /home/worker/.hgrc
 COPY           bin                           /home/worker/bin
 COPY           mozharness_configs            /home/worker/mozharness_configs
 COPY           buildprops.json               /home/worker/buildprops.json
+ADD            https://s3-us-west-2.amazonaws.com/test-caching/packages/linux64-stackwalk /usr/local/bin/linux64-minidump_stackwalk
 ADD            https://raw.githubusercontent.com/taskcluster/buildbot-step/master/buildbot_step /home/worker/bin/buildbot_step
 
 
 # Run test setup script
 RUN chmod u+x /home/worker/bin/buildbot_step
+RUN chmod u+x /usr/local/bin/linux64-minidump_stackwalk
 RUN apt-get install -y python-pip && pip install virtualenv;
 RUN mkdir Documents; mkdir Pictures; mkdir Music; mkdir Videos; mkdir artifacts
 RUN chown -R worker:worker /home/worker/* /home/worker/.*
-RUN npm install -g taskcluster-vcs@2.3.1
+RUN npm install -g taskcluster-vcs@2.3.4
 ENV PATH $PATH:/home/worker/bin
 
 # TODO Re-enable worker when bug 1093833 lands
 #USER          worker
 
 # Set a default command useful for debugging
 CMD ["/bin/bash", "--login"]
--- a/testing/docker/tester/VERSION
+++ b/testing/docker/tester/VERSION
@@ -1,1 +1,1 @@
-0.2.6
+0.2.8
deleted file mode 100644
--- a/testing/docker/tester/hgrc
+++ /dev/null
@@ -1,2 +0,0 @@
-[extensions]
-share =
--- a/testing/docker/tester/mozharness_configs/remove_executables.py
+++ b/testing/docker/tester/mozharness_configs/remove_executables.py
@@ -1,3 +1,6 @@
 config = {
+    # We bake this directly into the tester image now...
+    "download_minidump_stackwalk": False,
+    "minidump_stackwalk_path": "/usr/local/bin/linux64-minidump_stackwalk",
     "exes": {}
 }
--- a/testing/taskcluster/tasks/test.yml
+++ b/testing/taskcluster/tasks/test.yml
@@ -11,24 +11,22 @@ task:
   provisionerId: aws-provisioner
   schedulerId: task-graph-scheduler
 
   scopes:
     - 'docker-worker:image:{{#docker_image}}tester{{/docker_image}}'
     - 'queue:define-task:aws-provisioner/test-c4-2xlarge'
     - 'queue:create-task:aws-provisioner/test-c4-2xlarge'
     - 'docker-worker:cache:tc-vcs'
-    - 'docker-worker:cache:buildshare'
 
   payload:
     image: '{{#docker_image}}tester{{/docker_image}}'
     maxRunTime: 3600
     cache:
       tc-vcs: '/home/worker/.tc-vcs'
-      buildshare: '/builds/'
     env:
       GAIA_HEAD_REPOSITORY: '{{{gaia_head_repository}}}'
       GAIA_BASE_REPOSITORY: '{{{gaia_base_repository}}}'
       GAIA_REF: '{{{gaia_ref}}}'
       GAIA_REV: '{{{gaia_rev}}}'
       MOZHARNESS_REPOSITORY: '{{mozharness_repository}}'
       MOZHARNESS_REV: '{{mozharness_rev}}'
 
--- a/testing/taskcluster/tasks/tests/b2g_build_test.yml
+++ b/testing/taskcluster/tasks/tests/b2g_build_test.yml
@@ -10,16 +10,17 @@ task:
     command:
       - entrypoint # entrypoint ensures we are running in xvfb
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/gaia_build_integration.py
         --no-read-buildbot-config
         --config-file ./mozharness/configs/b2g/gaia_integration_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --download-symbols ondemand
         --gaia-repo https://hg.mozilla.org/integration/gaia-central
         --gaia-dir /home/worker
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre.linux-x86_64.zip
     artifacts:
       'public/build':
--- a/testing/taskcluster/tasks/tests/b2g_build_unit.yml
+++ b/testing/taskcluster/tasks/tests/b2g_build_unit.yml
@@ -10,16 +10,17 @@ task:
     command:
       - entrypoint
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/gaia_build_unit.py
         --no-read-buildbot-config
         --config-file ./mozharness/configs/b2g/gaia_integration_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --gaia-repo https://hg.mozilla.org/integration/gaia-central
         --gaia-dir /home/worker
     artifacts:
       'public/build':
         type: directory
--- a/testing/taskcluster/tasks/tests/b2g_emulator_cpp_unit.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_cpp_unit.yml
@@ -9,16 +9,17 @@ task:
   workerType: b2gtest-emulator
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --test-suite cppunittest
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre_linux_x86.zip
     artifacts:
       'public/build':
         type: directory
--- a/testing/taskcluster/tasks/tests/b2g_emulator_crashtest.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_crashtest.yml
@@ -10,16 +10,17 @@ task:
   payload:
     maxRunTime: 3600
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --test-suite crashtest
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre_linux_x86.zip
         --this-chunk {{chunk}}
         --total-chunk {{total_chunks}}
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_emulator_js_reftest.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_js_reftest.yml
@@ -9,16 +9,17 @@ task:
   workerType: b2gtest-emulator
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --test-suite jsreftest
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre_linux_x86.zip
         --this-chunk {{chunk}}
         --total-chunk {{total_chunks}}
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_emulator_mochitest.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_mochitest.yml
@@ -11,16 +11,17 @@ task:
     maxRunTime: 7200
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
         --config-file ./mozharness_configs/emulator_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --test-suite mochitest
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre_linux_x86.zip
         --this-chunk {{chunk}}
         --total-chunk {{total_chunks}}
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_emulator_mochitest_media.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_mochitest_media.yml
@@ -10,16 +10,17 @@ task:
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
         --config-file ./mozharness_configs/emulator_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --test-suite mochitest
         --test-path dom/media/tests/
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre_linux_x86.zip
     artifacts:
       'public/build':
--- a/testing/taskcluster/tasks/tests/b2g_emulator_reftest.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_reftest.yml
@@ -9,16 +9,17 @@ task:
   workerType: b2gtest-emulator
   payload:
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --test-suite reftest
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre_linux_x86.zip
         --this-chunk {{chunk}}
         --total-chunk {{total_chunks}}
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_emulator_xpcshell.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_xpcshell.yml
@@ -10,16 +10,17 @@ task:
   payload:
     maxRunTime: 6000
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --test-suite xpcshell
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre_linux_x86.zip
     artifacts:
       'public/build':
         type: directory
--- a/testing/taskcluster/tasks/tests/b2g_emulator_xpcshell_chunked.yml
+++ b/testing/taskcluster/tasks/tests/b2g_emulator_xpcshell_chunked.yml
@@ -10,16 +10,17 @@ task:
   payload:
     maxRunTime: 6000
     command:
       - entrypoint
       - >
         python ./mozharness/scripts/b2g_emulator_unittest.py
         --config-file ./mozharness/configs/b2g/emulator_automation_config.py
         --config-file ./mozharness_configs/emulator_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --test-suite xpcshell
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre_linux_x86.zip
         --this-chunk {{chunk}}
         --total-chunk {{total_chunks}}
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_gaia_js_integration_tests.yml
+++ b/testing/taskcluster/tasks/tests/b2g_gaia_js_integration_tests.yml
@@ -10,16 +10,17 @@ task:
     command:
       - entrypoint # entrypoint ensures we are running in xvfb
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/gaia_integration.py
         --no-read-buildbot-config
         --config-file b2g/gaia_integration_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --download-symbols ondemand
         --total-chunk {{total_chunks}}
         --this-chunk {{chunk}}
         --gaia-repo https://hg.mozilla.org/integration/gaia-central
         --gaia-dir /home/worker
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_gaia_ui_test_accessibility.yml
+++ b/testing/taskcluster/tasks/tests/b2g_gaia_ui_test_accessibility.yml
@@ -10,16 +10,17 @@ task:
     command:
       - entrypoint # entrypoint ensures we are running in xvfb
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/marionette.py
         --no-read-buildbot-config
         --config-file marionette/gaia_ui_test_prod_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --download-symbols ondemand
         --gip-suite accessibility
         --gaia-repo https://hg.mozilla.org/integration/gaia-central
         --gaia-dir /home/worker
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre.linux-x86_64.zip
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_gaia_ui_test_functional.yml
+++ b/testing/taskcluster/tasks/tests/b2g_gaia_ui_test_functional.yml
@@ -10,16 +10,17 @@ task:
     command:
       - entrypoint # entrypoint ensures we are running in xvfb
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/marionette.py
         --no-read-buildbot-config
         --config-file marionette/gaia_ui_test_prod_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --download-symbols ondemand
         --gip-suite functional
         --total-chunk {{total_chunks}}
         --this-chunk {{chunk}}
         --gaia-repo https://hg.mozilla.org/integration/gaia-central
         --gaia-dir /home/worker
--- a/testing/taskcluster/tasks/tests/b2g_gaia_ui_test_unit.yml
+++ b/testing/taskcluster/tasks/tests/b2g_gaia_ui_test_unit.yml
@@ -10,16 +10,17 @@ task:
     command:
       - entrypoint # entrypoint ensures we are running in xvfb
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/marionette.py
         --no-read-buildbot-config
         --config-file marionette/gaia_ui_test_prod_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --download-symbols ondemand
         --gip-suite unit
         --gaia-repo https://hg.mozilla.org/integration/gaia-central
         --gaia-dir /home/worker
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre.linux-x86_64.zip
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_gaia_unit.yml
+++ b/testing/taskcluster/tasks/tests/b2g_gaia_unit.yml
@@ -10,16 +10,17 @@ task:
     command:
       - entrypoint
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/gaia_unit.py
         --no-read-buildbot-config
         --config-file ./mozharness/configs/b2g/gaia_unit_production_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --download-symbols ondemand
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --gaia-repo https://hg.mozilla.org/integration/gaia-central
         --gaia-dir /home/worker
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre.linux-x86_64.zip
     artifacts:
       'public/build':
--- a/testing/taskcluster/tasks/tests/b2g_gaia_unit_oop.yml
+++ b/testing/taskcluster/tasks/tests/b2g_gaia_unit_oop.yml
@@ -10,16 +10,17 @@ task:
     command:
       - entrypoint
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/gaia_unit.py
         --no-read-buildbot-config
         --config-file ./mozharness/configs/b2g/gaia_unit_production_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --browser-arg -oop
         --download-symbols ondemand
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --gaia-repo https://hg.mozilla.org/integration/gaia-central
         --gaia-dir /home/worker
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre.linux-x86_64.zip
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_gip_oop.yml
+++ b/testing/taskcluster/tasks/tests/b2g_gip_oop.yml
@@ -10,16 +10,17 @@ task:
     command:
       - entrypoint # entrypoint ensures we are running in xvfb
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/marionette.py
         --no-read-buildbot-config
         --config-file marionette/gaia_ui_test_prod_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --app-arg -oop
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --download-symbols ondemand
         --gaia-repo https://hg.mozilla.org/integration/gaia-central
         --gaia-dir /home/worker
         --xre-url https://s3-us-west-2.amazonaws.com/test-caching/packages/xre.linux-x86_64.zip
     artifacts:
--- a/testing/taskcluster/tasks/tests/b2g_mochitest.yml
+++ b/testing/taskcluster/tasks/tests/b2g_mochitest.yml
@@ -8,16 +8,17 @@ task:
 
   payload:
     command:
       - entrypoint # entrypoint ensures we are running in xvfb
       - >
         python ./mozharness/scripts/b2g_desktop_unittest.py
         --no-read-buildbot-config
         --config-file /home/worker/b2g-desktop-config.py
+        --config-file ./mozharness_configs/remove_executables.py
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --download-symbols ondemand
         --test-suite mochitest
         --total-chunk={{total_chunks}}
         --this-chunk={{chunk}}
     artifacts:
       'public/build':
--- a/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml
+++ b/testing/taskcluster/tasks/tests/mulet_gaia_js_integration_tests.yml
@@ -11,16 +11,17 @@ task:
       - entrypoint # entrypoint ensures we are running in xvfb
       - ./bin/pull_gaia.sh &&
       - >
         python ./mozharness/scripts/gaia_integration.py
         --application firefox
         --no-read-buildbot-config
         --config-file b2g/gaia_integration_config.py
         --config-file ./mozharness_configs/gaia_integration_override.py
+        --config-file ./mozharness_configs/remove_executables.py
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --download-symbols ondemand
         --total-chunk {{total_chunks}}
         --this-chunk {{chunk}}
         --gaia-repo {{gaia_head_repository}}
         --gaia-dir /home/worker
     artifacts:
--- a/testing/taskcluster/tasks/tests/mulet_mochitests.yml
+++ b/testing/taskcluster/tasks/tests/mulet_mochitests.yml
@@ -9,16 +9,17 @@ task:
   payload:
     command:
       - entrypoint # entrypoint ensures we are running in xvfb
       - >
         python ./mozharness/scripts/desktop_unittest.py
         --no-read-buildbot-config
         --config-file ./mozharness/configs/unittests/linux_unittest.py
         --config-file ./mozharness_configs/linux_mulet_config.py
+        --config-file ./mozharness_configs/remove_executables.py
         --installer-url {{build_url}}
         --test-url {{tests_url}}
         --download-symbols ondemand
         --mochitest-suite plain-chunked
         --total-chunk={{total_chunks}}
         --this-chunk={{chunk}}
     artifacts:
       'public/build':
--- a/toolkit/components/alerts/resources/content/alert.js
+++ b/toolkit/components/alerts/resources/content/alert.js
@@ -40,16 +40,25 @@ function prefillAlertInfo() {
   // arguments[9] --> an optional callback listener (nsIObserver)
 
   switch (window.arguments.length) {
     default:
     case 10:
       gAlertListener = window.arguments[9];
     case 9:
       gReplacedWindow = window.arguments[8];
+    case 8:
+      if (window.arguments[7]) {
+        document.getElementById('alertTitleLabel').setAttribute('lang', window.arguments[7]);
+        document.getElementById('alertTextLabel').setAttribute('lang', window.arguments[7]);
+      }
+    case 7:
+      if (window.arguments[6]) {
+        document.getElementById('alertNotification').style.direction = window.arguments[6];
+      }
     case 6:
       gOrigin = window.arguments[5];
     case 5:
       gAlertCookie = window.arguments[4];
     case 4:
       gAlertTextClickable = window.arguments[3];
       if (gAlertTextClickable) {
         document.getElementById('alertNotification').setAttribute('clickable', true);
--- a/toolkit/components/feeds/test/xpcshell.ini
+++ b/toolkit/components/feeds/test/xpcshell.ini
@@ -1,12 +1,11 @@
 [DEFAULT]
 head = head.js
 tail =
-firefox-appdir = browser
 skip-if = toolkit == 'android' || toolkit == 'gonk'
 support-files =
     xml/rfc4287/author_namespaces.xml
     xml/rfc4287/entry_link_IANA.xml
     xml/rfc4287/feed_author_email_2.xml
     xml/rfc4287/feed_logo.xml
     xml/rfc4287/entry_author.xml
     xml/rfc4287/entry_link_otherURI_alt.xml
--- a/toolkit/components/passwordmgr/LoginRecipes.jsm
+++ b/toolkit/components/passwordmgr/LoginRecipes.jsm
@@ -225,13 +225,17 @@ let LoginRecipesContent = {
   },
 };
 
 const DEFAULT_RECIPES = {
   "siteRecipes": [
     {
       "description": "okta uses a hidden password field to disable filling",
       "hosts": ["mozilla.okta.com"],
-      "passwordSelector": "#pass-signin",
-      "pathRegex": "^(|\/login\/(login.htm|do\-login))$"
+      "passwordSelector": "#pass-signin"
     },
+    {
+      "description": "anthem uses a hidden password and username field to disable filling",
+      "hosts": ["www.anthem.com"],
+      "passwordSelector": "#LoginContent_txtLoginPass"
+    }
   ]
 };
--- a/toolkit/devtools/client/connection-manager.js
+++ b/toolkit/devtools/client/connection-manager.js
@@ -12,16 +12,18 @@ const EventEmitter = require("devtools/t
 const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
 DevToolsUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 
+const REMOTE_TIMEOUT = "devtools.debugger.remote-timeout";
+
 /**
  * Connection Manager.
  *
  * To use this module:
  * const {ConnectionManager} = require("devtools/client/connection-manager");
  *
  * # ConnectionManager
  *
@@ -47,16 +49,18 @@ DevToolsUtils.defineLazyModuleGetter(thi
  *  . disconnect()          Disconnect if connected. Expect a "disconnecting" event
  *
  * Properties:
  *  . host                  IP address or hostname
  *  . port                  Port
  *  . logs                  Current logs. "newlog" event notifies new available logs
  *  . store                 Reference to a local data store (see below)
  *  . keepConnecting        Should the connection keep trying to connect?
+ *  . timeoutDelay          When should we give up (in ms)?
+ *                          0 means wait forever.
  *  . encryption            Should the connection be encrypted?
  *  . authentication        What authentication scheme should be used?
  *  . authenticator         The |Authenticator| instance used.  Overriding
  *                          properties of this instance may be useful to
  *                          customize authentication UX for a specific use case.
  *  . advertisement         The server's advertisement if found by discovery
  *  . status                Connection status:
  *                            Connection.Status.CONNECTED
@@ -228,18 +232,21 @@ Connection.prototype = {
       host: this.host,
       port: this.port,
       encryption: this.encryption,
       authenticator: this.authenticator
     });
     return settings;
   },
 
+  timeoutDelay: Services.prefs.getIntPref(REMOTE_TIMEOUT),
+
   resetOptions() {
     this.keepConnecting = false;
+    this.timeoutDelay = Services.prefs.getIntPref(REMOTE_TIMEOUT);
     this.encryption = false;
     this.authentication = null;
     this.advertisement = null;
   },
 
   disconnect: function(force) {
     if (this.status == Connection.Status.DESTROYED) {
       return;
@@ -263,18 +270,19 @@ Connection.prototype = {
       this._customTransport = transport;
       if (this._customTransport) {
         this.log("connecting (custom transport)");
       } else {
         this.log("connecting to " + this.host + ":" + this.port);
       }
       this._setStatus(Connection.Status.CONNECTING);
 
-      let delay = Services.prefs.getIntPref("devtools.debugger.remote-timeout");
-      this._timeoutID = setTimeout(this._onTimeout, delay);
+      if (this.timeoutDelay > 0) {
+        this._timeoutID = setTimeout(this._onTimeout, this.timeoutDelay);
+      }
       this._clientConnect();
     } else {
       let msg = "Can't connect. Client is not fully disconnected";
       this.log(msg);
       throw new Error(msg);
     }
   },
 
--- a/toolkit/devtools/webconsole/test/test_page_errors.html
+++ b/toolkit/devtools/webconsole/test/test_page_errors.html
@@ -67,16 +67,21 @@ function onAttach(aState, aResponse)
   aState.dbgClient.addListener("pageError", onPageError);
   doPageErrors();
 }
 
 let pageErrors = [];
 
 function onPageError(aState, aType, aPacket)
 {
+  if (!aPacket.pageError.sourceName.contains("test_page_errors")) {
+    info("Ignoring error from unknown source: " + aPacket.pageError.sourceName);
+    return;
+  }
+
   is(aPacket.from, aState.actor, "page error actor");
 
   pageErrors.push(aPacket.pageError);
   if (pageErrors.length != expectedPageErrors.length) {
     return;
   }
 
   aState.dbgClient.removeListener("pageError", onPageError);
--- a/toolkit/modules/sessionstore/FormData.jsm
+++ b/toolkit/modules/sessionstore/FormData.jsm
@@ -81,20 +81,16 @@ function isValidCCNumber(value) {
  * The public API exported by this module that allows to collect
  * and restore form data for a document and its subframes.
  */
 this.FormData = Object.freeze({
   collect: function (frame) {
     return FormDataInternal.collect(frame);
   },
 
-  restore: function (frame, data) {
-    FormDataInternal.restore(frame, data);
-  },
-
   restoreTree: function (root, data) {
     FormDataInternal.restoreTree(root, data);
   }
 });
 
 /**
  * This module's internal API.
  */
--- a/toolkit/modules/sessionstore/ScrollPosition.jsm
+++ b/toolkit/modules/sessionstore/ScrollPosition.jsm
@@ -4,22 +4,35 @@
 
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["ScrollPosition"];
 
 const Ci = Components.interfaces;
 
 /**
- * It provides methods to collect and restore scroll positions for single
- * frames and frame trees.
+ * It provides methods to collect scroll positions from single frames and to
+ * restore scroll positions for frame trees.
  *
  * This is a child process module.
  */
 this.ScrollPosition = Object.freeze({
+  collect(frame) {
+    return ScrollPositionInternal.collect(frame);
+  },
+
+  restoreTree(root, data) {
+    ScrollPositionInternal.restoreTree(root, data);
+  }
+});
+
+/**
+ * This module's internal API.
+ */
+let ScrollPositionInternal = {
   /**
    * Collects scroll position data for any given |frame| in the frame hierarchy.
    *
    * @param frame (DOMWindow)
    *
    * @return {scroll: "x,y"} e.g. {scroll: "100,200"}
    *         Returns null when there is no scroll data we want to store for the
    *         given |frame|.
@@ -82,9 +95,9 @@ this.ScrollPosition = Object.freeze({
 
     let frames = root.frames;
     data.children.forEach((child, index) => {
       if (child && index < frames.length) {
         this.restoreTree(frames[index], child);
       }
     });
   }
-});
+};