Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Fri, 31 Oct 2014 14:23:10 +0100
changeset 239819 0c31008f237b1e89ee11db93b30c68f90017b175
parent 239818 677c24618e336c15b7d2f202d188cd59bf01da48 (current diff)
parent 239697 21fbf1e350900f5f7b9705535ac25e211fea5242 (diff)
child 239820 ef9ed51f78e08881ae3dc2bd06145e97d3d74db5
push id660
push userraliiev@mozilla.com
push dateWed, 18 Feb 2015 20:30:48 +0000
treeherdermozilla-release@49e493494178 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
browser/base/content/browser.js
browser/base/content/content.js
browser/base/content/test/general/browser.ini
browser/base/content/test/general/bug564387.html
browser/base/content/test/general/bug564387_video1.ogv
browser/base/content/test/general/bug564387_video1.ogv^headers^
configure.in
--- a/b2g/chrome/content/settings.js
+++ b/b2g/chrome/content/settings.js
@@ -149,30 +149,33 @@ Components.utils.import('resource://gre/
 
   let appInfo = Cc["@mozilla.org/xre/app-info;1"]
                   .getService(Ci.nsIXULAppInfo);
 
   // Get the hardware info and firmware revision from device properties.
   let hardware_info = null;
   let firmware_revision = null;
   let product_model = null;
+  let build_number = null;
 #ifdef MOZ_WIDGET_GONK
     hardware_info = libcutils.property_get('ro.hardware');
     firmware_revision = libcutils.property_get('ro.firmware_revision');
     product_model = libcutils.property_get('ro.product.model');
+    build_number = libcutils.property_get('ro.build.version.incremental');
 #endif
 
   // Populate deviceinfo settings,
   // copying any existing deviceinfo.os into deviceinfo.previous_os
   let lock = window.navigator.mozSettings.createLock();
   let req = lock.get('deviceinfo.os');
   req.onsuccess = req.onerror = () => {
     let previous_os = req.result && req.result['deviceinfo.os'] || '';
     let software = os_name + ' ' + os_version;
     let setting = {
+      'deviceinfo.build_number': build_number,
       'deviceinfo.os': os_version,
       'deviceinfo.previous_os': previous_os,
       'deviceinfo.software': software,
       'deviceinfo.platform_version': appInfo.platformVersion,
       'deviceinfo.platform_build_id': appInfo.platformBuildID,
       'deviceinfo.hardware': hardware_info,
       'deviceinfo.firmware_revision': firmware_revision,
       'deviceinfo.product_model': product_model
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--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="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <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="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
--- 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="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <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="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
   <!-- 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,17 +12,17 @@
   <!--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="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
   <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"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--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="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <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="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
--- 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="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <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="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
   <!-- 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,17 +10,17 @@
   <!--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="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <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="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--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="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
   <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"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
         "git_revision": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "5b7182e0e489747ff07ca952f5a99dc21a0226c9", 
+    "revision": "c349ef7fc9b4d343e5a3479c256471c3b2bd5284", 
     "repo_path": "/integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <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="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <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="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--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="8986df0f82e15ac2798df0b6c2ee3435400677ac">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
   <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"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="70eb0cb0977d6295e7da8896f9efb9f3ca1c13ea">
     <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="8ae6598f3ab7b0c34ac42a73083ddb74266affba"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a07994714f0552f89801d6097982308d8b0a1ee1"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="28be739bcdcbc9eb91c0bdbff1f7d3eab717969b"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -831,8 +831,21 @@ if (Services.appinfo.processType == Serv
 
 addEventListener("pageshow", function(event) {
   if (event.target == content.document) {
     sendAsyncMessage("PageVisibility:Show", {
       persisted: event.persisted,
     });
   }
 });
+
+addMessageListener("ContextMenu:SaveVideoFrameAsImage", (message) => {
+  let video = message.objects.target;
+  let canvas = content.document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
+  canvas.width = video.videoWidth;
+  canvas.height = video.videoHeight;
+
+  let ctxDraw = canvas.getContext("2d");
+  ctxDraw.drawImage(video, 0, 0);
+  sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage:Result", {
+    dataURL: canvas.toDataURL("image/jpeg", ""),
+  });
+});
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1032,34 +1032,40 @@ nsContextMenu.prototype = {
     }
 
     var doc = this.target.ownerDocument;
     openUILink(viewURL, e, { disallowInheritPrincipal: true,
                              referrerURI: doc.documentURIObject });
   },
 
   saveVideoFrameAsImage: function () {
+    let mm = this.browser.messageManager;
     let name = "";
     if (this.mediaURL) {
       try {
         let uri = makeURI(this.mediaURL);
         let url = uri.QueryInterface(Ci.nsIURL);
         if (url.fileBaseName)
           name = decodeURI(url.fileBaseName) + ".jpg";
       } catch (e) { }
     }
     if (!name)
       name = "snapshot.jpg";
-    var video = this.target;
-    var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
-    canvas.width = video.videoWidth;
-    canvas.height = video.videoHeight;
-    var ctxDraw = canvas.getContext("2d");
-    ctxDraw.drawImage(video, 0, 0);
-    saveImageURL(canvas.toDataURL("image/jpeg", ""), name, "SaveImageTitle", true, false, document.documentURIObject, this.target.ownerDocument);
+
+    mm.sendAsyncMessage("ContextMenu:SaveVideoFrameAsImage", {}, {
+      target: this.target,
+    });
+
+    let onMessage = (message) => {
+      mm.removeMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
+      let dataURL = message.data.dataURL;
+      saveImageURL(dataURL, name, "SaveImageTitle", true, false,
+                   document.documentURIObject, this.target.ownerDocument);
+    };
+    mm.addMessageListener("ContextMenu:SaveVideoFrameAsImage:Result", onMessage);
   },
 
   fullScreenVideo: function () {
     let video = this.target;
     if (document.mozFullScreenEnabled)
       video.mozRequestFullScreen();
   },
 
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -12,19 +12,16 @@ support-files =
   browser_bug678392-2.html
   browser_bug846489_content.js
   browser_bug970746.xhtml
   browser_fxa_oauth.html
   browser_registerProtocolHandler_notification.html
   browser_star_hsts.sjs
   browser_tab_dragdrop2_frame1.xul
   browser_web_channel.html
-  bug564387.html
-  bug564387_video1.ogv
-  bug564387_video1.ogv^headers^
   bug592338.html
   bug792517-2.html
   bug792517.html
   bug792517.sjs
   bug839103.css
   discovery.html
   domplate_test.js
   download_page.html
@@ -79,16 +76,19 @@ support-files =
   test_bug435035.html
   test_bug462673.html
   test_bug628179.html
   test_bug839103.html
   test_bug959531.html
   test_wyciwyg_copying.html
   title_test.svg
   video.ogg
+  web_video.html
+  web_video1.ogv
+  web_video1.ogv^headers^
   zoom_test.html
   test_no_mcb_on_http_site_img.html
   test_no_mcb_on_http_site_img.css
   test_no_mcb_on_http_site_font.html
   test_no_mcb_on_http_site_font.css
   test_no_mcb_on_http_site_font2.html
   test_no_mcb_on_http_site_font2.css
   test_mcb_redirect.html
@@ -409,16 +409,17 @@ skip-if = true  # disabled until the tre
                 # back to the clear recent history dialog (sanitize.xul), if
                 # it ever is (bug 480169)
 [browser_save_link-perwindowpb.js]
 skip-if = buildapp == 'mulet' || e10s # Bug ?????? - test directly manipulates content (event.target)
 [browser_save_private_link_perwindowpb.js]
 skip-if = buildapp == 'mulet' || e10s # e10s: Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly
 [browser_save_video.js]
 skip-if = buildapp == 'mulet' || e10s # Bug ?????? - test directly manipulates content (event.target)
+[browser_save_video_frame.js]
 [browser_scope.js]
 [browser_searchSuggestionUI.js]
 skip-if = e10s
 support-files =
   searchSuggestionUI.html
   searchSuggestionUI.js
 [browser_selectTabAtIndex.js]
 skip-if = e10s # Bug ?????? - no idea! "Accel+9 selects expected tab - Got 0, expected 9"
--- a/browser/base/content/test/general/browser_autocomplete_a11y_label.js
+++ b/browser/base/content/test/general/browser_autocomplete_a11y_label.js
@@ -19,11 +19,12 @@ add_task(function*() {
     return;
 
   let tab = gBrowser.addTab("about:about");
   yield promiseTabLoaded(tab);
 
   let actionURL = makeActionURI("switchtab", {url: "about:about"}).spec;
   yield check_a11y_label("% about", "about:about " + actionURL + " Tab");
 
+  gURLBar.popup.hidePopup();
   yield promisePopupHidden(gURLBar.popup);
   gBrowser.removeTab(tab);
 });
--- a/browser/base/content/test/general/browser_save_video.js
+++ b/browser/base/content/test/general/browser_save_video.js
@@ -7,17 +7,17 @@ MockFilePicker.init(window);
 /**
  * TestCase for bug 564387
  * <https://bugzilla.mozilla.org/show_bug.cgi?id=564387>
  */
 function test() {
   waitForExplicitFinish();
   var fileName;
 
-  gBrowser.loadURI("http://mochi.test:8888/browser/browser/base/content/test/general/bug564387.html");
+  gBrowser.loadURI("http://mochi.test:8888/browser/browser/base/content/test/general/web_video.html");
 
   gBrowser.addEventListener("pageshow", function pageShown(event) {
     if (event.target.location == "about:blank")
       return;
     gBrowser.removeEventListener("pageshow", pageShown);
 
     executeSoon(function () {
       document.addEventListener("popupshown", contextMenuOpened);
@@ -61,17 +61,17 @@ function test() {
     info("context-savevideo command executed");
 
     event.target.hidePopup();
   }
 
   function onTransferComplete(downloadSuccess) {
     ok(downloadSuccess, "Video file should have been downloaded successfully");
 
-    is(fileName, "Bug564387-expectedName.ogv",
+    is(fileName, "web-video1-expectedName.ogv",
        "Video file name is correctly retrieved from Content-Disposition http header");
 
     finish();
   }
 }
 
 Cc["@mozilla.org/moz/jssubscript-loader;1"]
   .getService(Ci.mozIJSSubScriptLoader)
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_save_video_frame.js
@@ -0,0 +1,126 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const VIDEO_URL = "http://mochi.test:8888/browser/browser/base/content/test/general/web_video.html";
+
+/**
+ * mockTransfer.js provides a utility that lets us mock out
+ * the "Save File" dialog.
+ */
+Cc["@mozilla.org/moz/jssubscript-loader;1"]
+  .getService(Ci.mozIJSSubScriptLoader)
+  .loadSubScript("chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
+                 this);
+
+/**
+ * Creates and returns an nsIFile for a new temporary save
+ * directory.
+ *
+ * @return nsIFile
+ */
+function createTemporarySaveDirectory() {
+  let saveDir = Cc["@mozilla.org/file/directory_service;1"]
+                  .getService(Ci.nsIProperties)
+                  .get("TmpD", Ci.nsIFile);
+  saveDir.append("testsavedir");
+  if (!saveDir.exists())
+    saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
+  return saveDir;
+}
+/**
+ * MockTransfer exposes a "mockTransferCallback" global which
+ * allows us to define a callback to be called once the mock file
+ * selector has selected where to save the file.
+ */
+function waitForTransferComplete() {
+  return new Promise((resolve) => {
+    mockTransferCallback = () => {
+      ok(true, "Transfer completed");
+      resolve();
+    }
+  });
+}
+
+/**
+ * Given some browser, loads a framescript that right-clicks
+ * on the video1 element to spawn a contextmenu.
+ */
+function rightClickVideo(browser) {
+  let frame_script = () => {
+    const Ci = Components.interfaces;
+    let utils = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils);
+
+    let document = content.document;
+    let video = document.getElementById("video1");
+    let rect = video.getBoundingClientRect();
+
+    /* Synthesize a click in the center of the video. */
+    let left = rect.left + (rect.width / 2);
+    let top = rect.top + (rect.height / 2);
+
+    utils.sendMouseEvent("contextmenu", left, top,
+                         2, /* aButton */
+                         1, /* aClickCount */
+                         0  /* aModifiers */);
+  };
+  let mm = browser.messageManager;
+  mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", true);
+}
+
+/**
+ * Loads a page with a <video> element, right-clicks it and chooses
+ * to save a frame screenshot to the disk. Completes once we've
+ * verified that the frame has been saved to disk.
+ */
+add_task(function*() {
+  let MockFilePicker = SpecialPowers.MockFilePicker;
+  MockFilePicker.init(window);
+
+  // Create the folder the video will be saved into.
+  let destDir = createTemporarySaveDirectory();
+  let destFile = destDir.clone();
+
+  MockFilePicker.displayDirectory = destDir;
+  MockFilePicker.showCallback = function(fp) {
+    destFile.append(fp.defaultString);
+    MockFilePicker.returnFiles = [destFile];
+    MockFilePicker.filterIndex = 1; // kSaveAsType_URL
+  };
+
+  mockTransferRegisterer.register();
+
+  // Make sure that we clean these things up when we're done.
+  registerCleanupFunction(function () {
+    mockTransferRegisterer.unregister();
+    MockFilePicker.cleanup();
+    destDir.remove(true);
+  });
+
+  let tab = gBrowser.addTab();
+  gBrowser.selectedTab = tab;
+  let browser = tab.linkedBrowser;
+  info("Loading video tab");
+  yield promiseTabLoadEvent(tab, VIDEO_URL);
+  info("Video tab loaded.");
+
+  let video = browser.contentDocument.getElementById("video1");
+  let context = document.getElementById("contentAreaContextMenu");
+  let popupPromise = promisePopupShown(context);
+
+  info("Synthesizing right-click on video element");
+  rightClickVideo(browser);
+  info("Waiting for popup to fire popupshown.");
+  yield popupPromise;
+  info("Popup fired popupshown");
+
+  let saveSnapshotCommand = document.getElementById("context-video-saveimage");
+  let promiseTransfer = waitForTransferComplete()
+  info("Firing save snapshot command");
+  saveSnapshotCommand.doCommand();
+  context.hidePopup();
+  info("Waiting for transfer completion");
+  yield promiseTransfer;
+  info("Transfer complete");
+  gBrowser.removeTab(tab);
+});
--- a/browser/base/content/test/general/head.js
+++ b/browser/base/content/test/general/head.js
@@ -739,17 +739,17 @@ function is_element_visible(element, msg
 function is_element_hidden(element, msg) {
   isnot(element, null, "Element should not be null, when checking visibility");
   ok(is_hidden(element), msg);
 }
 
 function promisePopupEvent(popup, eventSuffix) {
   let endState = {shown: "open", hidden: "closed"}[eventSuffix];
 
-  if (popup.state = endState)
+  if (popup.state == endState)
     return Promise.resolve();
 
   let eventType = "popup" + eventSuffix;
   let deferred = Promise.defer();
   popup.addEventListener(eventType, function onPopupShown(event) {
     popup.removeEventListener(eventType, onPopupShown);
     deferred.resolve();
   });
rename from browser/base/content/test/general/bug564387.html
rename to browser/base/content/test/general/web_video.html
--- a/browser/base/content/test/general/bug564387.html
+++ b/browser/base/content/test/general/web_video.html
@@ -1,11 +1,10 @@
 <html>
-  <!-- https://bugzilla.mozilla.org/show_bug.cgi?id=564387 -->
   <head>
-    <title> Bug 564387 test</title>
+    <title>Document with Web Video</title>
   </head>
   <body>
-    Testing for Mozilla Bug: 564387
+    This document has some web video in it.
     <br>
-    <video src="bug564387_video1.ogv" id="video1"> </video> 
+    <video src="web_video1.ogv" id="video1"> </video>
   </body>
 </html>
rename from browser/base/content/test/general/bug564387_video1.ogv
rename to browser/base/content/test/general/web_video1.ogv
rename from browser/base/content/test/general/bug564387_video1.ogv^headers^
rename to browser/base/content/test/general/web_video1.ogv^headers^
--- a/browser/base/content/test/general/bug564387_video1.ogv^headers^
+++ b/browser/base/content/test/general/web_video1.ogv^headers^
@@ -1,3 +1,3 @@
-Content-Disposition: filename="Bug564387-expectedName.ogv" 
+Content-Disposition: filename="web-video1-expectedName.ogv" 
 Content-Type: video/ogg
 
--- a/browser/components/preferences/in-content/sync.js
+++ b/browser/components/preferences/in-content/sync.js
@@ -121,19 +121,19 @@ let gSyncPane = {
       gSyncPane.openSetup(null);
     });
     setEventListener("noAccountPair", "click", function (aEvent) {
       aEvent.stopPropagation();
       gSyncPane.openSetup('pair');
     });
     setEventListener("syncViewQuota", "command", gSyncPane.openQuotaDialog);
     setEventListener("syncChangePassword", "command",
-      gSyncUtils.changePassword);
+      () => gSyncUtils.changePassword());
     setEventListener("syncResetPassphrase", "command",
-      gSyncUtils.resetPassphrase);
+      () => gSyncUtils.resetPassphrase());
     setEventListener("syncReset", "command", gSyncPane.resetSync);
     setEventListener("syncAddDeviceLabel", "click", function () {
       gSyncPane.openAddDevice();
       return false;
     });
     setEventListener("syncEnginesList", "select", function () {
       if (this.selectedCount)
         this.clearSelection();
--- a/browser/components/sessionstore/test/browser.ini
+++ b/browser/components/sessionstore/test/browser.ini
@@ -60,16 +60,18 @@ support-files =
 
 [browser_aboutPrivateBrowsing.js]
 [browser_aboutSessionRestore.js]
 [browser_attributes.js]
 [browser_backup_recovery.js]
 [browser_broadcast.js]
 [browser_capabilities.js]
 [browser_cleaner.js]
+[browser_crashedTabs.js]
+skip-if = !e10s || os == "linux" # Waiting on OMTC enabled by default on Linux (Bug 994541)
 [browser_dying_cache.js]
 [browser_dynamic_frames.js]
 [browser_form_restore_events.js]
 [browser_formdata.js]
 skip-if = buildapp == 'mulet'
 [browser_formdata_format.js]
 [browser_formdata_xpath.js]
 [browser_frametree.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/sessionstore/test/browser_crashedTabs.js
@@ -0,0 +1,328 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PAGE_1 = "data:text/html,<html><body>A%20regular,%20everyday,%20normal%20page.";
+const PAGE_2 = "data:text/html,<html><body>Another%20regular,%20everyday,%20normal%20page.";
+
+/**
+ * Returns a Promise that resolves once a remote <xul:browser> has experienced
+ * a crash. Also does the job of cleaning up the minidump of the crash.
+ *
+ * @param browser
+ *        The <xul:browser> that will crash
+ * @return Promise
+ */
+function crashBrowser(browser) {
+  // This frame script is injected into the remote browser, and used to
+  // intentionally crash the tab. We crash by using js-ctypes and dereferencing
+  // a bad pointer. The crash should happen immediately upon loading this
+  // frame script.
+  let frame_script = () => {
+    const Cu = Components.utils;
+    Cu.import("resource://gre/modules/ctypes.jsm");
+
+    let dies = function() {
+      let zero = new ctypes.intptr_t(8);
+      let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+      badptr.contents
+    };
+
+    dump("Et tu, Brute?");
+    dies();
+  }
+
+  let crashCleanupPromise = new Promise((resolve, reject) => {
+    let observer = (subject, topic, data) => {
+      is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
+      ok(subject instanceof Ci.nsIPropertyBag2,
+         'Subject implements nsIPropertyBag2.');
+      // we might see this called as the process terminates due to previous tests.
+      // We are only looking for "abnormal" exits...
+      if (!subject.hasKey("abnormal")) {
+        info("This is a normal termination and isn't the one we are looking for...");
+        return;
+      }
+
+      let dumpID;
+      if ('nsICrashReporter' in Ci) {
+        dumpID = subject.getPropertyAsAString('dumpID');
+        ok(dumpID, "dumpID is present and not an empty string");
+      }
+
+      if (dumpID) {
+        let minidumpDirectory = getMinidumpDirectory();
+        removeFile(minidumpDirectory, dumpID + '.dmp');
+        removeFile(minidumpDirectory, dumpID + '.extra');
+      }
+
+      Services.obs.removeObserver(observer, 'ipc:content-shutdown');
+      resolve();
+    };
+
+    Services.obs.addObserver(observer, 'ipc:content-shutdown');
+  });
+
+  let aboutTabCrashedLoadPromise = new Promise((resolve, reject) => {
+    browser.addEventListener("AboutTabCrashedLoad", function onCrash() {
+      browser.removeEventListener("AboutTabCrashedLoad", onCrash, false);
+      resolve();
+    }, false, true);
+  });
+
+  // This frame script will crash the remote browser as soon as it is
+  // evaluated.
+  let mm = browser.messageManager;
+  mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
+  return Promise.all([crashCleanupPromise, aboutTabCrashedLoadPromise]);
+}
+
+/**
+ * Returns the directory where crash dumps are stored.
+ *
+ * @return nsIFile
+ */
+function getMinidumpDirectory() {
+  let dir = Services.dirsvc.get('ProfD', Ci.nsIFile);
+  dir.append("minidumps");
+  return dir;
+}
+
+/**
+ * Removes a file from a directory. This is a no-op if the file does not
+ * exist.
+ *
+ * @param directory
+ *        The nsIFile representing the directory to remove from.
+ * @param filename
+ *        A string for the file to remove from the directory.
+ */
+function removeFile(directory, filename) {
+  let file = directory.clone();
+  file.append(filename);
+  if (file.exists()) {
+    file.remove(false);
+  }
+}
+
+/**
+ * Checks the documentURI of the root document of a remote browser
+ * to see if it equals URI. Returns a Promise that resolves if
+ * there is a match, and rejects with an error message if they
+ * do not match.
+ *
+ * @param browser
+ *        The remote <xul:browser> to check the root document URI in.
+ * @param URI
+ *        A string to match the root document URI against.
+ * @return Promise
+ */
+function promiseContentDocumentURIEquals(browser, URI) {
+  return new Promise((resolve, reject) => {
+    let frame_script = () => {
+      sendAsyncMessage("test:documenturi", {
+        uri: content.document.documentURI,
+      });
+    };
+
+    let mm = browser.messageManager;
+    mm.addMessageListener("test:documenturi", function onMessage(message) {
+      mm.removeMessageListener("test:documenturi", onMessage);
+      let contentURI = message.data.uri;
+      if (contentURI == URI) {
+        resolve();
+      } else {
+        reject(`Content has URI ${contentURI} which does not match ${URI}`);
+      }
+    });
+
+    mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
+  });
+}
+
+/**
+ * Checks the window.history.length of the root window of a remote
+ * browser to see if it equals length. Returns a Promise that resolves
+ * if there is a match, and rejects with an error message if they
+ * do not match.
+ *
+ * @param browser
+ *        The remote <xul:browser> to check the root window.history.length
+ * @param length
+ *        The expected history length
+ * @return Promise
+ */
+function promiseHistoryLength(browser, length) {
+  return new Promise((resolve, reject) => {
+    let frame_script = () => {
+      sendAsyncMessage("test:historylength", {
+        length: content.history.length,
+      });
+    };
+
+    let mm = browser.messageManager;
+    mm.addMessageListener("test:historylength", function onMessage(message) {
+      mm.removeMessageListener("test:historylength", onMessage);
+      let contentLength = message.data.length;
+      if (contentLength == length) {
+        resolve();
+      } else {
+        reject(`Content has window.history.length ${contentLength} which does ` +
+               `not equal expected ${length}`);
+      }
+    });
+
+    mm.loadFrameScript("data:,(" + frame_script.toString() + ")();", false);
+  });
+}
+
+/**
+ * Checks that if a tab crashes, that information about the tab crashed
+ * page does not get added to the tab history.
+ */
+add_task(function test_crash_page_not_in_history() {
+  let newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  let browser = newTab.linkedBrowser;
+  ok(browser.isRemoteBrowser, "Should be a remote browser");
+  yield promiseBrowserLoaded(browser);
+
+  browser.loadURI(PAGE_1);
+  yield promiseBrowserLoaded(browser);
+  TabState.flush(browser);
+
+  // Crash the tab
+  yield crashBrowser(browser);
+  // Flush out any notifications from the crashed browser.
+  TabState.flush(browser);
+
+  // Check the tab state and make sure the tab crashed page isn't
+  // mentioned.
+  let {entries} = JSON.parse(ss.getTabState(newTab));
+  is(entries.length, 1, "Should have a single history entry");
+  is(entries[0].url, PAGE_1,
+    "Single entry should be the page we visited before crashing");
+
+  gBrowser.removeTab(newTab);
+});
+
+/**
+ * Checks that if a tab crashes, that when we browse away from that page
+ * to a non-blacklisted site (so the browser becomes remote again), that
+ * we record history for that new visit.
+ */
+add_task(function test_revived_history_from_remote() {
+  let newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  let browser = newTab.linkedBrowser;
+  ok(browser.isRemoteBrowser, "Should be a remote browser");
+  yield promiseBrowserLoaded(browser);
+
+  browser.loadURI(PAGE_1);
+  yield promiseBrowserLoaded(browser);
+  TabState.flush(browser);
+
+  // Crash the tab
+  yield crashBrowser(browser);
+  // Flush out any notifications from the crashed browser.
+  TabState.flush(browser);
+
+  // Browse to a new site that will cause the browser to
+  // become remote again.
+  browser.loadURI(PAGE_2);
+  yield promiseBrowserLoaded(browser);
+  ok(browser.isRemoteBrowser, "Should be a remote browser");
+  TabState.flush(browser);
+
+  // Check the tab state and make sure the tab crashed page isn't
+  // mentioned.
+  let {entries} = JSON.parse(ss.getTabState(newTab));
+  is(entries.length, 2, "Should have two history entries");
+  is(entries[0].url, PAGE_1,
+    "First entry should be the page we visited before crashing");
+  is(entries[1].url, PAGE_2,
+    "Second entry should be the page we visited after crashing");
+
+  gBrowser.removeTab(newTab);
+});
+
+/**
+ * Checks that if a tab crashes, that when we browse away from that page
+ * to a blacklisted site (so the browser stays non-remote), that
+ * we record history for that new visit.
+ */
+add_task(function test_revived_history_from_non_remote() {
+  let newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  let browser = newTab.linkedBrowser;
+  ok(browser.isRemoteBrowser, "Should be a remote browser");
+  yield promiseBrowserLoaded(browser);
+
+  browser.loadURI(PAGE_1);
+  yield promiseBrowserLoaded(browser);
+  TabState.flush(browser);
+
+  // Crash the tab
+  yield crashBrowser(browser);
+  // Flush out any notifications from the crashed browser.
+  TabState.flush(browser);
+
+  // Browse to a new site that will not cause the browser to
+  // become remote again.
+  browser.loadURI("about:mozilla");
+  yield promiseBrowserLoaded(browser);
+  ok(!browser.isRemoteBrowser, "Should not be a remote browser");
+  TabState.flush(browser);
+
+  // Check the tab state and make sure the tab crashed page isn't
+  // mentioned.
+  let {entries} = JSON.parse(ss.getTabState(newTab));
+  is(entries.length, 2, "Should have two history entries");
+  is(entries[0].url, PAGE_1,
+    "First entry should be the page we visited before crashing");
+  is(entries[1].url, "about:mozilla",
+    "Second entry should be the page we visited after crashing");
+
+  gBrowser.removeTab(newTab);
+});
+
+/**
+ * Checks that we can revive a crashed tab back to the page that
+ * it was on when it crashed.
+ */
+add_task(function test_revive_tab_from_session_store() {
+  let newTab = gBrowser.addTab();
+  gBrowser.selectedTab = newTab;
+  let browser = newTab.linkedBrowser;
+  ok(browser.isRemoteBrowser, "Should be a remote browser");
+  yield promiseBrowserLoaded(browser);
+
+  browser.loadURI(PAGE_1);
+  yield promiseBrowserLoaded(browser);
+
+  browser.loadURI(PAGE_2);
+  yield promiseBrowserLoaded(browser);
+
+  TabState.flush(browser);
+
+  // Crash the tab
+  yield crashBrowser(browser);
+  // Flush out any notifications from the crashed browser.
+  TabState.flush(browser);
+
+  // Use SessionStore to revive the tab
+  SessionStore.reviveCrashedTab(newTab);
+  yield promiseBrowserLoaded(browser);
+
+  // We can't just check browser.currentURI.spec, because from
+  // the outside, a crashed tab has the same URI as the page
+  // it crashed on (much like an about:neterror page). Instead,
+  // we have to use the documentURI on the content.
+  yield promiseContentDocumentURIEquals(browser, PAGE_2);
+
+  // We should also have two entries in the browser history.
+  yield promiseHistoryLength(browser, 2);
+
+  gBrowser.removeTab(newTab);
+});
\ No newline at end of file
--- a/browser/components/sessionstore/test/head.js
+++ b/browser/components/sessionstore/test/head.js
@@ -315,16 +315,39 @@ function promiseBrowserLoaded(aBrowser, 
   });
 }
 function whenBrowserUnloaded(aBrowser, aContainer, aCallback = next) {
   aBrowser.addEventListener("unload", function onUnload() {
     aBrowser.removeEventListener("unload", onUnload, true);
     executeSoon(aCallback);
   }, true);
 }
+
+/**
+ * Loads a page in a browser, and returns a Promise that
+ * resolves once a "load" event has been fired within that
+ * browser.
+ *
+ * @param browser
+ *        The browser to load the page in.
+ * @param uri
+ *        The URI to load.
+ *
+ * @return Promise
+ */
+function loadPage(browser, uri) {
+  return new Promise((resolve, reject) => {
+    browser.addEventListener("load", function onLoad(event) {
+      browser.removeEventListener("load", onLoad, true);
+      resolve();
+    }, true);
+    browser.loadURI(uri);
+  });
+}
+
 function promiseBrowserUnloaded(aBrowser, aContainer) {
   return new Promise(resolve => {
     whenBrowserUnloaded(aBrowser, aContainer, resolve);
   });
 }
 
 function whenWindowLoaded(aWindow, aCallback = next) {
   aWindow.addEventListener("load", function windowLoadListener() {
--- a/configure.in
+++ b/configure.in
@@ -8920,16 +8920,19 @@ AC_SUBST(LIBJPEG_TURBO_X64_ASM)
 AC_SUBST(LIBJPEG_TURBO_ARM_ASM)
 
 AC_SUBST(MOZ_PACKAGE_JSSHELL)
 AC_SUBST(MOZ_FOLD_LIBS)
 
 AC_SUBST(MOZ_ENABLE_SZIP)
 AC_SUBST(MOZ_SZIP_FLAGS)
 
+dnl Host JavaScript runtime, if any, to use during cross compiles.
+AC_SUBST(JS_BINARY)
+
 if test "$MOZ_DEBUG"; then
     MOZ_EM_DEBUG=1
 fi
 AC_SUBST(MOZ_EM_DEBUG)
 
 AC_SUBST(NSS_EXTRA_SYMBOLS_FILE)
 
 if test -n "$COMPILE_ENVIRONMENT"; then
--- a/dom/permission/PermissionSettings.jsm
+++ b/dom/permission/PermissionSettings.jsm
@@ -30,32 +30,32 @@ XPCOMUtils.defineLazyServiceGetter(this,
 this.PermissionSettingsModule = {
   init: function init() {
     debug("Init");
     ppmm.addMessageListener("PermissionSettings:AddPermission", this);
     Services.obs.addObserver(this, "profile-before-change", false);
   },
 
 
-  _isChangeAllowed: function(aPrincipal, aPermName, aAction) {
+  _isChangeAllowed: function(aPrincipal, aPermName, aAction, aAppKind) {
     // Bug 812289:
     // Change is allowed from a child process when all of the following
     // conditions stand true:
     //   * the action isn't "unknown" (so the change isn't a delete) if the app
     //     is installed
     //   * the permission already exists on the database
     //   * the permission is marked as explicit on the permissions table
     // Note that we *have* to check the first two conditions here because
     // permissionManager doesn't know if it's being called as a result of
     // a parent process or child process request. We could check
     // if the permission is actually explicit (and thus modifiable) or not
     // on permissionManager also but we currently don't.
     let perm =
       Services.perms.testExactPermissionFromPrincipal(aPrincipal,aPermName);
-    let isExplicit = isExplicitInPermissionsTable(aPermName, aPrincipal.appStatus);
+    let isExplicit = isExplicitInPermissionsTable(aPermName, aPrincipal.appStatus, aAppKind);
 
     return (aAction === "unknown" &&
             aPrincipal.appStatus === Ci.nsIPrincipal.APP_STATUS_NOT_INSTALLED) ||
            (aAction !== "unknown" &&
             (perm !== Ci.nsIPermissionManager.UNKNOWN_ACTION) &&
             isExplicit);
   },
 
@@ -63,18 +63,18 @@ this.PermissionSettingsModule = {
 
     this._internalAddPermission(aData, true, aCallbacks);
 
   },
 
 
   _internalAddPermission: function _internalAddPermission(aData, aAllowAllChanges, aCallbacks) {
     let uri = Services.io.newURI(aData.origin, null, null);
-    let appID = appsService.getAppLocalIdByManifestURL(aData.manifestURL);
-    let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, appID, aData.browserFlag);
+    let app = appsService.getAppByManifestURL(aData.manifestURL);
+    let principal = Services.scriptSecurityManager.getAppCodebasePrincipal(uri, app.localId, aData.browserFlag);
 
     let action;
     switch (aData.value)
     {
       case "unknown":
         action = Ci.nsIPermissionManager.UNKNOWN_ACTION;
         break;
       case "allow":
@@ -87,22 +87,22 @@ this.PermissionSettingsModule = {
         action = Ci.nsIPermissionManager.PROMPT_ACTION;
         break;
       default:
         dump("Unsupported PermisionSettings Action: " + aData.value +"\n");
         action = Ci.nsIPermissionManager.UNKNOWN_ACTION;
     }
 
     if (aAllowAllChanges ||
-        this._isChangeAllowed(principal, aData.type, aData.value)) {
-      debug("add: " + aData.origin + " " + appID + " " + action);
+        this._isChangeAllowed(principal, aData.type, aData.value, app.kind)) {
+      debug("add: " + aData.origin + " " + app.localId + " " + action);
       Services.perms.addFromPrincipal(principal, aData.type, action);
       return true;
     } else {
-      debug("add Failure: " + aData.origin + " " + appID + " " + action);
+      debug("add Failure: " + aData.origin + " " + app.localId + " " + action);
       return false; // This isn't currently used, see comment on setPermission
     }
   },
 
   getPermission: function getPermission(aPermName, aManifestURL, aOrigin, aBrowserFlag) {
     debug("getPermission: " + aPermName + ", " + aManifestURL + ", " + aOrigin);
     let uri = Services.io.newURI(aOrigin, null, null);
     let appID = appsService.getAppLocalIdByManifestURL(aManifestURL);
--- a/mobile/android/base/FindInPageBar.java
+++ b/mobile/android/base/FindInPageBar.java
@@ -11,24 +11,26 @@ import org.mozilla.gecko.util.ThreadUtil
 
 import org.json.JSONObject;
 
 import android.content.Context;
 import android.text.Editable;
 import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.util.AttributeSet;
+import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.LinearLayout;
 import android.widget.TextView;
 
 public class FindInPageBar extends LinearLayout implements TextWatcher, View.OnClickListener, GeckoEventListener  {
+    private static final String LOGTAG = "FindInPageBar";
     private static final String REQUEST_ID = "FindInPageBar";
 
     private final Context mContext;
     private CustomEditText mFindText;
     private TextView mStatusText;
     private boolean mInflated;
 
     public FindInPageBar(Context context, AttributeSet attrs) {
@@ -176,23 +178,33 @@ public class FindInPageBar extends Linea
                }
             });
         }
     }
 
     /**
      * Request find operation, and update matchCount results (current count and total).
      */
-    private void sendRequestToFinderHelper(String request, String searchString) {
+    private void sendRequestToFinderHelper(final String request, final String searchString) {
         GeckoAppShell.sendRequestToGecko(new GeckoRequest(request, searchString) {
             @Override
             public void onResponse(NativeJSObject nativeJSObject) {
                 final int total = nativeJSObject.optInt("total", 0);
                 final int current = nativeJSObject.optInt("current", 0);
+                updateResult(total, current);
+            }
 
+            public void onError() {
+                // Gecko didn't respond due to state change, javascript error, etc.
+                updateResult(0, 0);
+                Log.d(LOGTAG, "No response from Gecko on request to match string: [" +
+                    searchString + "]");
+            }
+
+            private void updateResult(int total, int current) {
                 final Boolean statusVisibility = (total > 0);
                 final String statusText = current + "/" + total;
 
                 ThreadUtils.postToUiThread(new Runnable() {
                     @Override
                     public void run() {
                         mStatusText.setVisibility(statusVisibility ? View.VISIBLE : View.GONE);
                         mStatusText.setText(statusText);
--- a/mobile/android/base/fxa/activities/FxAccountConfirmAccountActivity.java
+++ b/mobile/android/base/fxa/activities/FxAccountConfirmAccountActivity.java
@@ -2,42 +2,45 @@
  * 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.fxa.activities;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
+import org.mozilla.gecko.fxa.activities.FxAccountGetStartedActivity;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.fxa.login.Engaged;
 import org.mozilla.gecko.fxa.login.State;
 import org.mozilla.gecko.fxa.login.State.Action;
 import org.mozilla.gecko.fxa.sync.FxAccountSyncStatusHelper;
 import org.mozilla.gecko.fxa.tasks.FxAccountCodeResender;
 import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
 
 import android.accounts.Account;
 import android.app.Activity;
 import android.content.Context;
+import android.content.Intent;
 import android.os.Bundle;
 import android.view.View;
 import android.view.View.OnClickListener;
 import android.widget.TextView;
 
 /**
  * Activity which displays account created successfully screen to the user, and
  * starts them on the email verification path.
  */
 public class FxAccountConfirmAccountActivity extends FxAccountAbstractActivity implements OnClickListener {
   private static final String LOG_TAG = FxAccountConfirmAccountActivity.class.getSimpleName();
 
   // Set in onCreate.
   protected TextView verificationLinkTextView;
   protected View resendLink;
+  protected View changeEmail;
 
   // Set in onResume.
   protected AndroidFxAccount fxAccount;
 
   protected final InnerSyncStatusDelegate syncStatusDelegate = new InnerSyncStatusDelegate();
 
   public FxAccountConfirmAccountActivity() {
     super(CANNOT_RESUME_WHEN_NO_ACCOUNTS_EXIST);
@@ -51,16 +54,18 @@ public class FxAccountConfirmAccountActi
     Logger.debug(LOG_TAG, "onCreate(" + icicle + ")");
 
     super.onCreate(icicle);
     setContentView(R.layout.fxaccount_confirm_account);
 
     verificationLinkTextView = (TextView) ensureFindViewById(null, R.id.verification_link_text, "verification link text");
     resendLink = ensureFindViewById(null, R.id.resend_confirmation_email_link, "resend confirmation email link");
     resendLink.setOnClickListener(this);
+    changeEmail = ensureFindViewById(null, R.id.change_confirmation_email_link, "change confirmation email address");
+    changeEmail.setOnClickListener(this);
 
     View backToBrowsingButton = ensureFindViewById(null, R.id.button, "back to browsing button");
     backToBrowsingButton.setOnClickListener(new OnClickListener() {
       @Override
       public void onClick(View v) {
         ActivityUtils.openURLInFennec(v.getContext(), null);
         setResult(Activity.RESULT_OK);
         finish();
@@ -154,11 +159,17 @@ public class FxAccountConfirmAccountActi
 
     boolean resendLinkShouldBeEnabled = ((Engaged) state).getSessionToken() != null;
     resendLink.setEnabled(resendLinkShouldBeEnabled);
     resendLink.setClickable(resendLinkShouldBeEnabled);
   }
 
   @Override
   public void onClick(View v) {
-    FxAccountCodeResender.resendCode(this, fxAccount);
+    if (v.equals(resendLink)) {
+        FxAccountCodeResender.resendCode(this, fxAccount);
+    } else if (v.equals(changeEmail)) {
+      final Account account = fxAccount.getAndroidAccount();
+      Intent intent = new Intent(this, FxAccountGetStartedActivity.class);
+      FxAccountStatusActivity.maybeDeleteAndroidAccount(this, account, intent);
+    }
   }
 }
--- a/mobile/android/base/fxa/activities/FxAccountStatusActivity.java
+++ b/mobile/android/base/fxa/activities/FxAccountStatusActivity.java
@@ -5,16 +5,17 @@
 package org.mozilla.gecko.fxa.activities;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
 import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
 import org.mozilla.gecko.sync.Utils;
+import org.mozilla.gecko.LocaleAware.LocaleAwareActivity;
 import org.mozilla.gecko.LocaleAware.LocaleAwareFragmentActivity;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.AccountManagerCallback;
 import android.accounts.AccountManagerFuture;
 import android.annotation.SuppressLint;
 import android.annotation.TargetApi;
@@ -108,55 +109,57 @@ public class FxAccountStatusActivity ext
     return new AndroidFxAccount(this, account);
   }
 
 
   /**
    * Helper function to maybe remove the given Android account.
    */
   @SuppressLint("InlinedApi")
-  public void maybeDeleteAndroidAccount(final Account account) {
+  public static void maybeDeleteAndroidAccount(final Activity activity, final Account account, final Intent intent) {
     if (account == null) {
       Logger.warn(LOG_TAG, "Trying to delete null account; ignoring request.");
       return;
     }
 
     final AccountManagerCallback<Boolean> callback = new AccountManagerCallback<Boolean>() {
       @Override
       public void run(AccountManagerFuture<Boolean> future) {
         Logger.info(LOG_TAG, "Account " + Utils.obfuscateEmail(account.name) + " removed.");
-        final Activity activity = FxAccountStatusActivity.this;
         final String text = activity.getResources().getString(R.string.fxaccount_remove_account_toast, account.name);
         Toast.makeText(activity, text, Toast.LENGTH_LONG).show();
-
-        finish();
+        if (intent != null) {
+            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+            activity.startActivity(intent);
+        }
+        activity.finish();
       }
     };
 
     /*
      * Get the best dialog icon from the theme on v11+.
      * See http://stackoverflow.com/questions/14910536/android-dialog-theme-makes-icon-too-light/14910945#14910945.
      */
     final int icon;
     if (AppConstants.Versions.feature11Plus) {
       final TypedValue typedValue = new TypedValue();
-      getTheme().resolveAttribute(android.R.attr.alertDialogIcon, typedValue, true);
+      activity.getTheme().resolveAttribute(android.R.attr.alertDialogIcon, typedValue, true);
       icon = typedValue.resourceId;
     } else {
       icon = android.R.drawable.ic_dialog_alert;
     }
 
-    final AlertDialog dialog = new AlertDialog.Builder(this)
+    final AlertDialog dialog = new AlertDialog.Builder(activity)
       .setTitle(R.string.fxaccount_remove_account_dialog_title)
       .setIcon(icon)
       .setMessage(R.string.fxaccount_remove_account_dialog_message)
       .setPositiveButton(android.R.string.ok, new Dialog.OnClickListener() {
         @Override
         public void onClick(DialogInterface dialog, int which) {
-          AccountManager.get(FxAccountStatusActivity.this).removeAccount(account, callback, null);
+          AccountManager.get(activity).removeAccount(account, callback, null);
         }
       })
       .setNegativeButton(android.R.string.cancel, new Dialog.OnClickListener() {
         @Override
         public void onClick(DialogInterface dialog, int which) {
           dialog.cancel();
         }
       })
@@ -169,17 +172,17 @@ public class FxAccountStatusActivity ext
   public boolean onOptionsItemSelected(MenuItem item) {
     int itemId = item.getItemId();
     if (itemId == android.R.id.home) {
       finish();
       return true;
     }
 
     if (itemId == R.id.remove_account) {
-      maybeDeleteAndroidAccount(FirefoxAccounts.getFirefoxAccount(this));
+      maybeDeleteAndroidAccount(this, FirefoxAccounts.getFirefoxAccount(this), null);
       return true;
     }
 
     return super.onOptionsItemSelected(item);
   }
 
   @Override
   public boolean onCreateOptionsMenu(Menu menu) {
--- a/mobile/android/base/home/HistoryPanel.java
+++ b/mobile/android/base/home/HistoryPanel.java
@@ -225,17 +225,16 @@ public class HistoryPanel extends HomeFr
             emptyText.setText(R.string.home_most_recent_empty);
 
             final TextView emptyHint = (TextView) mEmptyView.findViewById(R.id.home_empty_hint);
             final String hintText = getResources().getString(R.string.home_most_recent_emptyhint);
 
             final SpannableStringBuilder hintBuilder = formatHintText(hintText);
             if (hintBuilder != null) {
                 emptyHint.setText(hintBuilder);
-                emptyHint.setText(hintBuilder);
                 emptyHint.setMovementMethod(LinkMovementMethod.getInstance());
                 emptyHint.setVisibility(View.VISIBLE);
             }
 
             mList.setEmptyView(mEmptyView);
         }
     }
 
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -395,19 +395,19 @@ size. -->
 <!ENTITY home_clear_history_button "Clear browsing history">
 <!ENTITY home_clear_history_confirm "Are you sure you want to clear your history?">
 <!ENTITY home_bookmarks_empty "Bookmarks you save show up here.">
 <!ENTITY home_closed_tabs_title "Recently closed tabs">
 <!ENTITY home_last_tabs_title "Tabs from last time">
 <!ENTITY home_last_tabs_empty "Your recent tabs show up here.">
 <!ENTITY home_open_all "Open all">
 <!ENTITY home_most_recent_empty "Websites you visited most recently show up here.">
-<!-- Localization note (home_most_recent_emptyhint): "Psst" is a sound that might be used to attract someone's attention unobtrusively, and intended to hint at "Private browsing" to the user.
+<!-- Localization note (home_most_recent_emptyhint2): "Psst" is a sound that might be used to attract someone's attention unobtrusively, and intended to hint at Private Browsing to the user.
      The placeholders &formatS1; and &formatS2; are used to mark the location of text underlining. -->
-<!ENTITY home_most_recent_emptyhint "Psst: &formatS1;Private browsing&formatS2; mode won\'t save your history.">
+<!ENTITY home_most_recent_emptyhint2 "Psst: using a &formatS1;New Private Tab&formatS2; won\'t save your history.">
 <!ENTITY home_reading_list_empty "Articles you save for later show up here.">
 <!-- Localization note (home_reading_list_hint): The "TIP" string is synonymous to "hint", "clue", etc. This string is displayed
      as an advisory message on how to add content to the reading list when the reading list empty.
      The placeholder &formatI; will be replaced by a small image of the icon described, and can be moved to wherever
      it is applicable. -->
 <!ENTITY home_reading_list_hint2 "TIP: Save articles to your reading list by long pressing the &formatI; icon when it appears in the title bar.">
 <!-- Localization note (home_reading_list_hint_accessible): This string is used
      as alternate text for accessibility. It is not visible in the UI. -->
--- a/mobile/android/base/locales/en-US/sync_strings.dtd
+++ b/mobile/android/base/locales/en-US/sync_strings.dtd
@@ -162,16 +162,17 @@
 <!ENTITY fxaccount_account_create_not_allowed 'Cannot create account'>
 <!ENTITY fxaccount_account_create_not_allowed_you_must_meet_certain_age_requirements 'You must meet certain age requirements to create an account.'>
 <!ENTITY fxaccount_account_create_not_allowed_learn_more 'Learn more'>
 
 <!ENTITY fxaccount_confirm_account_header 'Confirm your account'>
 <!-- Localization note: &formatS; is the Firefox Account's email address. -->
 <!ENTITY fxaccount_confirm_account_verification_link 'A verification link has been sent to &formatS;'>
 <!ENTITY fxaccount_confirm_account_resend_email 'Resend email'>
+<!ENTITY fxaccount_confirm_account_change_email 'Forget this email address?'>
 <!ENTITY fxaccount_confirm_account_verification_link_sent2 'Verification email sent'>
 <!ENTITY fxaccount_confirm_account_verification_link_not_sent2 'Couldn\&apos;t send verification email'>
 
 <!ENTITY fxaccount_sign_in_sub_header 'Sign in'>
 <!ENTITY fxaccount_sign_in_button_label 'Sign in'>
 <!ENTITY fxaccount_sign_in_forgot_password 'Forgot password?'>
 <!ENTITY fxaccount_sign_in_create_account_instead 'Create an account'>
 <!ENTITY fxaccount_sign_in_unknown_error 'Could not sign in'>
--- a/mobile/android/base/resources/layout/fxaccount_confirm_account.xml
+++ b/mobile/android/base/resources/layout/fxaccount_confirm_account.xml
@@ -40,16 +40,21 @@
             style="@style/FxAccountButton"
             android:text="@string/fxaccount_back_to_browsing" />
 
         <TextView
             android:id="@+id/resend_confirmation_email_link"
             style="@style/FxAccountLinkItem"
             android:text="@string/fxaccount_confirm_account_resend_email" />
 
+       	<TextView
+            android:id="@+id/change_confirmation_email_link"
+            style="@style/FxAccountLinkItem"
+            android:text="@string/fxaccount_confirm_account_change_email" />
+
         <LinearLayout style="@style/FxAccountSpacer" />
 
         <ImageView
             style="@style/FxAccountIcon"
             android:contentDescription="@string/fxaccount_empty_contentDescription" />
     </LinearLayout>
 
 </ScrollView>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -350,17 +350,17 @@
   <string name="home_clear_history_button">&home_clear_history_button;</string>
   <string name="home_clear_history_confirm">&home_clear_history_confirm;</string>
   <string name="home_bookmarks_empty">&home_bookmarks_empty;</string>
   <string name="home_closed_tabs_title">&home_closed_tabs_title;</string>
   <string name="home_last_tabs_title">&home_last_tabs_title;</string>
   <string name="home_last_tabs_empty">&home_last_tabs_empty;</string>
   <string name="home_open_all">&home_open_all;</string>
   <string name="home_most_recent_empty">&home_most_recent_empty;</string>
-  <string name="home_most_recent_emptyhint">&home_most_recent_emptyhint;</string>
+  <string name="home_most_recent_emptyhint">&home_most_recent_emptyhint2;</string>
   <string name="home_reading_list_empty">&home_reading_list_empty;</string>
   <string name="home_reading_list_hint">&home_reading_list_hint2;</string>
   <string name="home_reading_list_hint_accessible">&home_reading_list_hint_accessible;</string>
   <string name="home_default_empty">&home_default_empty;</string>
   <string name="home_move_up_to_filter">&home_move_up_to_filter;</string>
   <string name="home_remote_tabs_title">&home_remote_tabs_title;</string>
   <string name="home_remote_tabs_empty">&home_remote_tabs_empty;</string>
   <string name="home_remote_tabs_unable_to_connect">&home_remote_tabs_unable_to_connect;</string>
new file mode 100755
--- /dev/null
+++ b/mobile/android/config/js_wrapper.sh
@@ -0,0 +1,20 @@
+#! /bin/sh
+# 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/.
+
+# Wrapper for running SpiderMonkey js shell in automation with correct
+# LD_LIBRARY_PATH.
+
+# We don't have a reference to topsrcdir at this point, but we are invoked as
+# "$topsrcdir/mobile/android/config/js_wrapper.sh" so we can extract topsrcdir
+# from $0.
+topsrcdir=`cd \`dirname $0\`/../../..; pwd`
+
+JS_BINARY="$topsrcdir/jsshell/js"
+
+LD_LIBRARY_PATH="$topsrcdir/jsshell${LD_LIBRARY_PATH+:$LD_LIBRARY_PATH}"
+export LD_LIBRARY_PATH
+
+# Pass through all arguments and exit with status from js shell.
+exec "$JS_BINARY" "$@"
--- a/mobile/android/config/mozconfigs/common
+++ b/mobile/android/config/mozconfigs/common
@@ -48,8 +48,10 @@ HOST_CC="/tools/gcc-4.7.2-0moz1/bin/gcc"
 HOST_CXX="/tools/gcc-4.7.2-0moz1/bin/g++"
 
 # Avoid dependency on libstdc++ 4.7
 ac_add_options --enable-stdcxx-compat
 
 mk_add_options "export ANT_HOME=$topsrcdir/apache-ant"
 
 mk_add_options "export PATH=$topsrcdir/apache-ant/bin:$PATH"
+
+JS_BINARY="$topsrcdir/mobile/android/config/js_wrapper.sh"
--- a/mobile/android/config/tooltool-manifests/android-armv6/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-armv6/releng.manifest
@@ -23,10 +23,17 @@
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 },
 {
 "size": 7920445,
 "digest": "e28b7a12fbbef02ad742958df8dd356ea2adb8ef79e95cd8eb8dbc953eb4cc11888969dac7d636187fd3ace9c63d9a6bc3d7795021c1d811a843e413fe5e52c9",
 "algorithm": "sha512",
 "filename": "apache-ant-bin.tar.bz2"
+},
+{
+"size": 4906080,
+"digest": "d735544e039da89382c53b2302b7408d4610247b4f8b5cdc5a4d5a8ec5470947b19e8ea7f7a37e78222e661347e394e0030d81f41534138b527b14e9c4e55634",
+"algorithm": "sha512",
+"filename": "jsshell.tar.xz",
+"unpack": "True"
 }
 ]
--- a/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android-x86/releng.manifest
@@ -23,10 +23,17 @@
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 },
 {
 "size": 7920445,
 "digest": "e28b7a12fbbef02ad742958df8dd356ea2adb8ef79e95cd8eb8dbc953eb4cc11888969dac7d636187fd3ace9c63d9a6bc3d7795021c1d811a843e413fe5e52c9",
 "algorithm": "sha512",
 "filename": "apache-ant-bin.tar.bz2"
+},
+{
+"size": 4906080,
+"digest": "d735544e039da89382c53b2302b7408d4610247b4f8b5cdc5a4d5a8ec5470947b19e8ea7f7a37e78222e661347e394e0030d81f41534138b527b14e9c4e55634",
+"algorithm": "sha512",
+"filename": "jsshell.tar.xz",
+"unpack": "True"
 }
 ]
--- a/mobile/android/config/tooltool-manifests/android/releng.manifest
+++ b/mobile/android/config/tooltool-manifests/android/releng.manifest
@@ -23,10 +23,17 @@
 "algorithm": "sha512",
 "filename": "sccache.tar.bz2"
 },
 {
 "size": 7920445,
 "digest": "e28b7a12fbbef02ad742958df8dd356ea2adb8ef79e95cd8eb8dbc953eb4cc11888969dac7d636187fd3ace9c63d9a6bc3d7795021c1d811a843e413fe5e52c9",
 "algorithm": "sha512",
 "filename": "apache-ant-bin.tar.bz2"
+},
+{
+"size": 4906080,
+"digest": "d735544e039da89382c53b2302b7408d4610247b4f8b5cdc5a4d5a8ec5470947b19e8ea7f7a37e78222e661347e394e0030d81f41534138b527b14e9c4e55634",
+"algorithm": "sha512",
+"filename": "jsshell.tar.xz",
+"unpack": "True"
 }
 ]
--- a/mobile/android/installer/Makefile.in
+++ b/mobile/android/installer/Makefile.in
@@ -30,16 +30,23 @@ endif
 ifdef MOZ_PKG_MANIFEST_P
 MOZ_PKG_MANIFEST = package-manifest
 endif
 
 MOZ_PACKAGER_MINIFY=1
 
 include $(topsrcdir)/toolkit/mozapps/installer/packager.mk
 
+# Note that JS_BINARY can be defined in packager.mk, so this test must come
+# after including that file. MOZ_PACKAGER_MINIFY_JS is used in packager.mk, but
+# since recipe evaluation is deferred, we can set it here after the inclusion.
+ifneq (,$(JS_BINARY))
+MOZ_PACKAGER_MINIFY_JS=1
+endif
+
 ifeq (bundle, $(MOZ_FS_LAYOUT))
 BINPATH = $(_BINPATH)
 DEFINES += -DAPPNAME=$(_APPNAME)
 else
 # Every other platform just winds up in dist/bin
 BINPATH = bin
 endif
 DEFINES += -DBINPATH=$(BINPATH)
--- a/mobile/android/services/strings.xml.in
+++ b/mobile/android/services/strings.xml.in
@@ -149,16 +149,17 @@
 
 <string name="fxaccount_account_create_not_allowed">&fxaccount_account_create_not_allowed;</string>
 <string name="fxaccount_account_create_not_allowed_you_must_meet_certain_age_requirements">&fxaccount_account_create_not_allowed_you_must_meet_certain_age_requirements;</string>
 <string name="fxaccount_account_create_not_allowed_learn_more">&fxaccount_account_create_not_allowed_learn_more;</string>
 
 <string name="fxaccount_confirm_account_header">&fxaccount_confirm_account_header;</string>
 <string name="fxaccount_confirm_account_verification_link">&fxaccount_confirm_account_verification_link;</string>
 <string name="fxaccount_confirm_account_resend_email">&fxaccount_confirm_account_resend_email;</string>
+<string name="fxaccount_confirm_account_change_email">&fxaccount_confirm_account_change_email;</string>
 <string name="fxaccount_confirm_account_verification_link_sent">&fxaccount_confirm_account_verification_link_sent2;</string>
 <string name="fxaccount_confirm_account_verification_link_not_sent">&fxaccount_confirm_account_verification_link_not_sent2;</string>
 
 <string name="fxaccount_sign_in_sub_header">&fxaccount_sign_in_sub_header;</string>
 <string name="fxaccount_sign_in_button_label">&fxaccount_sign_in_button_label;</string>
 <string name="fxaccount_sign_in_forgot_password">&fxaccount_sign_in_forgot_password;</string>
 <string name="fxaccount_sign_in_create_account_instead">&fxaccount_sign_in_create_account_instead;</string>
 <string name="fxaccount_sign_in_unknown_error">&fxaccount_sign_in_unknown_error;</string>
--- a/python/jsmin/jsmin/__init__.py
+++ b/python/jsmin/jsmin/__init__.py
@@ -32,17 +32,17 @@ else:
     import StringIO
     try:
         import cStringIO
     except ImportError:
         cStringIO = None
 
 
 __all__ = ['jsmin', 'JavascriptMinify']
-__version__ = '2.0.3'
+__version__ = '2.0.11'
 
 
 def jsmin(js):
     """
     returns a minified version of the javascript string
     """
     if not is_3:        
         if cStringIO and not isinstance(js, unicode):
@@ -67,65 +67,88 @@ class JavascriptMinify(object):
 
     def __init__(self, instream=None, outstream=None):
         self.ins = instream
         self.outs = outstream
 
     def minify(self, instream=None, outstream=None):
         if instream and outstream:
             self.ins, self.outs = instream, outstream
-        write = self.outs.write
+
+        self.is_return = False
+        self.return_buf = ''
+
+        def write(char):
+            # all of this is to support literal regular expressions.
+            # sigh
+            if char in 'return':
+                self.return_buf += char
+                self.is_return = self.return_buf == 'return'
+            self.outs.write(char)
+            if self.is_return:
+                self.return_buf = ''
+
         read = self.ins.read
 
         space_strings = "abcdefghijklmnopqrstuvwxyz"\
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_$\\"
         starters, enders = '{[(+-', '}])+-"\''
         newlinestart_strings = starters + space_strings
         newlineend_strings = enders + space_strings
         do_newline = False
         do_space = False
+        escape_slash_count = 0
         doing_single_comment = False
         previous_before_comment = ''
         doing_multi_comment = False
         in_re = False
         in_quote = ''
         quote_buf = []
 
         previous = read(1)
+        if previous == '\\':
+            escape_slash_count += 1
         next1 = read(1)
         if previous == '/':
             if next1 == '/':
                 doing_single_comment = True
             elif next1 == '*':
                 doing_multi_comment = True
+                previous = next1
+                next1 = read(1)
             else:
+                in_re = True  # literal regex at start of script
                 write(previous)
         elif not previous:
             return
         elif previous >= '!':
             if previous in "'\"":
                 in_quote = previous
             write(previous)
             previous_non_space = previous
         else:
             previous_non_space = ' '
         if not next1:
             return
 
         while 1:
-            next2 = read(1)  
+            next2 = read(1)
             if not next2:
                 last = next1.strip()
                 if not (doing_single_comment or doing_multi_comment)\
                     and last not in ('', '/'):
+                    if in_quote:
+                        write(''.join(quote_buf))
                     write(last)
                 break
             if doing_multi_comment:
                 if next1 == '*' and next2 == '/':
                     doing_multi_comment = False
+                    if previous_before_comment and previous_before_comment in space_strings:
+                        do_space = True
                     next2 = read(1)
             elif doing_single_comment:
                 if next1 in '\r\n':
                     doing_single_comment = False
                     while next2 in '\r\n':
                         next2 = read(1)
                         if not next2:
                             break
@@ -159,37 +182,56 @@ class JavascriptMinify(object):
                                 or next2 > '~' or next2 == '/':
                                 do_newline = True
                             break
             elif next1 < '!' and not in_re:
                 if (previous_non_space in space_strings \
                     or previous_non_space > '~') \
                     and (next2 in space_strings or next2 > '~'):
                     do_space = True
+                elif previous_non_space in '-+' and next2 == previous_non_space:
+                    # protect against + ++ or - -- sequences
+                    do_space = True
+                elif self.is_return and next2 == '/':
+                    # returning a regex...
+                    write(' ')
             elif next1 == '/':
+                if do_space:
+                    write(' ')
                 if in_re:
-                    if previous != '\\':
+                    if previous != '\\' or (not escape_slash_count % 2) or next2 in 'gimy':
                         in_re = False
                     write('/')
                 elif next2 == '/':
                     doing_single_comment = True
                     previous_before_comment = previous_non_space
                 elif next2 == '*':
                     doing_multi_comment = True
+                    previous_before_comment = previous_non_space
+                    previous = next1
+                    next1 = next2
+                    next2 = read(1)
                 else:
-                    in_re = previous_non_space in '(,=:[?!&|'
+                    in_re = previous_non_space in '(,=:[?!&|;' or self.is_return  # literal regular expression
                     write('/')
             else:
                 if do_space:
                     do_space = False
                     write(' ')
                 if do_newline:
                     write('\n')
                     do_newline = False
+
                 write(next1)
                 if not in_re and next1 in "'\"":
                     in_quote = next1
                     quote_buf = []
+
             previous = next1
             next1 = next2
 
             if previous >= '!':
                 previous_non_space = previous
+
+            if previous == '\\':
+                escape_slash_count += 1
+            else:
+                escape_slash_count = 0
--- a/python/jsmin/jsmin/test.py
+++ b/python/jsmin/jsmin/test.py
@@ -89,20 +89,47 @@ another thing;"""
         it ends here */
         function foo() {
             alert('crud');
         }
         
         """
         expected = r"""function foo(){alert('crud');}"""
         self.assertMinified(js, expected)
-        
+
+    def testBlockCommentStartingWithSlash(self):
+        self.assertMinified('A; /*/ comment */ B', 'A;B')
+
+    def testBlockCommentEndingWithSlash(self):
+        self.assertMinified('A; /* comment /*/ B', 'A;B')
+
+    def testLeadingBlockCommentStartingWithSlash(self):
+        self.assertMinified('/*/ comment */ A', 'A')
+
+    def testLeadingBlockCommentEndingWithSlash(self):
+        self.assertMinified('/* comment /*/ A', 'A')
+
+    def testEmptyBlockComment(self):
+        self.assertMinified('/**/ A', 'A')
+
+    def testBlockCommentMultipleOpen(self):
+        self.assertMinified('/* A /* B */ C', 'C')
+
     def testJustAComment(self):
         self.assertMinified('     // a comment', '')
-        
+
+    def test_issue_10(self):
+        js = '''
+        files = [{name: value.replace(/^.*\\\\/, '')}];
+        // comment
+        A
+        '''
+        expected = '''files=[{name:value.replace(/^.*\\\\/,'')}]; A'''
+        self.assertMinified(js, expected)
+
     def testRe(self):
         js = r'''  
         var str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '');
         return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);
         });'''
         expected = r"""var str=this.replace(/\\./g,'@').replace(/"[^"\\\n\r]*"/g,'');return(/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str);});"""
         self.assertMinified(js, expected)
 
@@ -148,16 +175,30 @@ Element.cleanWhitespace(element);"""
 
         unfilterJSON: function(filter) {
           return this.sub(filter || Prototype.JSONFilter, '#{1}');
         },
         """
         expected = r"""inspect:function(useDoubleQuotes){var escapedString=this.gsub(/[\x00-\x1f\\]/,function(match){var character=String.specialChar[match[0]];return character?character:'\\u00'+match[0].charCodeAt().toPaddedString(2,16);});if(useDoubleQuotes)return'"'+escapedString.replace(/"/g,'\\"')+'"';return"'"+escapedString.replace(/'/g,'\\\'')+"'";},toJSON:function(){return this.inspect(true);},unfilterJSON:function(filter){return this.sub(filter||Prototype.JSONFilter,'#{1}');},"""
         self.assertMinified(js, expected)
 
+    def testLiteralRe(self):
+        js = r"""
+        myString.replace(/\\/g, '/');
+        console.log("hi");
+        """
+        expected = r"""myString.replace(/\\/g,'/');console.log("hi");"""
+        self.assertMinified(js, expected)
+
+        js = r''' return /^data:image\//i.test(url) ||
+        /^(https?|ftp|file|about|chrome|resource):/.test(url);
+        '''
+        expected = r'''return /^data:image\//i.test(url)||/^(https?|ftp|file|about|chrome|resource):/.test(url);'''
+        self.assertMinified(js, expected)
+
     def testNoBracesWithComment(self):
         js = r"""
         onSuccess: function(transport) {
             var js = transport.responseText.strip();
             if (!/^\[.*\]$/.test(js)) // TODO: improve sanity check
               throw 'Server returned an invalid collection representation.';
             this._collection = eval(js);
             this.checkForExternalText();
@@ -242,11 +283,73 @@ var  foo    =  "hey";
 
     def testImplicitSemicolon2(self):
         self.assertMinified("return//comment...\r\na", "return\na")
     
     def testSingleComment2(self):
         self.assertMinified('x.replace(/\//, "_")// slash to underscore',
                 'x.replace(/\//,"_")')
 
+    def testSlashesNearComments(self):
+        original = '''
+        { a: n / 2, }
+        // comment
+        '''
+        expected = '''{a:n/2,}'''
+        self.assertMinified(original, expected)
+
+    def testReturn(self):
+        original = '''
+        return foo;//comment
+        return bar;'''
+        expected = 'return foo; return bar;'
+        self.assertMinified(original, expected)
+
+    def test_space_plus(self):
+        original = '"s" + ++e + "s"'
+        expected = '"s"+ ++e+"s"'
+        self.assertMinified(original, expected)
+
+    def test_no_final_newline(self):
+        original = '"s"'
+        expected = '"s"'
+        self.assertMinified(original, expected)
+
+    def test_space_with_regex_repeats(self):
+        original = '/(NaN| {2}|^$)/.test(a)&&(a="M 0 0");'
+        self.assertMinified(original, original)  # there should be nothing jsmin can do here
+
+    def test_space_with_regex_repeats_not_at_start(self):
+        original = 'aaa;/(NaN| {2}|^$)/.test(a)&&(a="M 0 0");'
+        self.assertMinified(original, original)  # there should be nothing jsmin can do here
+
+    def test_space_in_regex(self):
+        original = '/a (a)/.test("a")'
+        self.assertMinified(original, original)
+
+    def test_angular_1(self):
+        original = '''var /** holds major version number for IE or NaN for real browsers */
+                      msie,
+                      jqLite,           // delay binding since jQuery could be loaded after us.'''
+        minified = jsmin.jsmin(original)
+        self.assertTrue('var msie' in minified)
+
+    def test_angular_2(self):
+        original = 'var/* comment */msie;'
+        expected = 'var msie;'
+        self.assertMinified(original, expected)
+
+    def test_angular_3(self):
+        original = 'var /* comment */msie;'
+        expected = 'var msie;'
+        self.assertMinified(original, expected)
+
+    def test_angular_4(self):
+        original = 'var /* comment */ msie;'
+        expected = 'var msie;'
+        self.assertMinified(original, expected)
+
+    def test_angular_4(self):
+        original = 'a/b'
+        self.assertMinified(original, original)
 
 if __name__ == '__main__':
     unittest.main()
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -1059,23 +1059,26 @@ CycleCollectedJSRuntime::DeferredFinaliz
 
 void
 CycleCollectedJSRuntime::DeferredFinalize(nsISupports* aSupports)
 {
 #if defined(XP_MACOSX) && defined(__LP64__)
   // We'll crash here if aSupports is poisoned (== 0x5a5a5a5a5a5a5a5a).  This
   // is better (more informative) than crashing in ReleaseSliceNow().  See
   // bug 997908.  This patch should get backed out when bug 997908 gets fixed,
-  // or if it doesn't actually help diagnose that bug.
+  // or if it doesn't actually help diagnose that bug.  Specifying a constraint
+  // of "r" for aSupports ensures %0 is a register.  Without this, clang
+  // sometimes mishandles this inline assembly code, causing crashes.  See
+  // bug 1091801.
   __asm__ __volatile__("push %%rax;"
                        "push %%rdx;"
                        "movq %0, %%rax;"
                        "movq (%%rax), %%rdx;"
                        "pop %%rdx;"
-                       "pop %%rax;" : : "g" (aSupports));
+                       "pop %%rax;" : : "r" (aSupports));
 #endif
   mDeferredSupports.AppendElement(aSupports);
 }
 
 void
 CycleCollectedJSRuntime::DumpJSHeap(FILE* aFile)
 {
   js::DumpHeapComplete(Runtime(), aFile, js::CollectNurseryBeforeDump);