Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 20 Oct 2014 15:14:35 +0200
changeset 211323 ae49e79844e01f48352da02c6e81f84194dfc12f
parent 211322 57ed7482de8033b9d161c4d69a5af5a1eb7e3064 (current diff)
parent 211241 f2d7d694aae5bda8957b0d20c6922f40f487d231 (diff)
child 211324 fff578fc740a66570c2cbed0247b31954d18483b
push id27673
push userkwierso@gmail.com
push dateTue, 21 Oct 2014 01:57:45 +0000
treeherdermozilla-central@29fbfc1b31aa [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
mobile/android/base/resources/drawable-large-v11/new_tablet_site_security_level.xml
mobile/android/base/resources/drawable-large-v11/new_tablet_site_security_unknown.xml
--- 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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
@@ -30,17 +30,17 @@
   <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"/>
   <project name="device/sample" path="device/sample" revision="1a3d8efa0ad32ec8f145367a3cf0f54b97385c3c"/>
   <project name="platform/abi/cpp" path="abi/cpp" revision="18f1b5e28734183ff8073fe86dc46bc4ebba8a59"/>
   <project name="platform/bionic" path="bionic" revision="86b1f589c313422a7da1812512b9ec8d1cf9ba3c"/>
-  <project name="platform/bootable/recovery" path="bootable/recovery" revision="4eece0d80928a2b5266b78421ebf0c8686d4ad2c"/>
+  <project name="platform/bootable/recovery" path="bootable/recovery" revision="67978bf5ecdd59441296436677b3729b4142e2ff"/>
   <project name="platform/external/aac" path="external/aac" revision="fa3eba16446cc8f2f5e2dfc20d86a49dbd37299e"/>
   <project name="platform/external/bison" path="external/bison" revision="c2418b886165add7f5a31fc5609f0ce2d004a90e"/>
   <project name="platform/external/bluetooth/bluedroid" path="external/bluetooth/bluedroid" revision="c8e99ca7e11c00f8124196fe1726a15e6e976587"/>
   <project name="platform/external/bsdiff" path="external/bsdiff" revision="23e322ab19fb7d74c2c37e40ce364d9f709bdcee"/>
   <project name="platform/external/bzip2" path="external/bzip2" revision="1cb636bd8e9e5cdfd5d5b2909a122f6e80db62de"/>
   <project name="platform/external/checkpolicy" path="external/checkpolicy" revision="0d73ef7049feee794f14cf1af88d05dae8139914"/>
   <project name="platform/external/dhcpcd" path="external/dhcpcd" revision="84b7252b0a9d0edc9a1db1e0c518771d26b23058"/>
   <project name="platform/external/dnsmasq" path="external/dnsmasq" revision="41d356427a632f5336384bfa45c8420ffc274f66"/>
--- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <!-- 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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
--- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <!-- 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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
--- 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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <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": "934b8c3014a3e20dd5d90ecf95f4b6b704dddb1e", 
+    "revision": "f486771c1a2a76bfea1a5c7eac8debe14f29927b", 
     "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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <!-- 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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <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="84923f1940625c47ff4c1fdf01b10fde3b7d909e">
     <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="1daf2dadcd0d554c733661a4c0be1b82001e9da0"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="dc496d04907dd314f9736ff78bab3bd27156f79a"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="cc1f362ce43dce92ac786187ff4abf39060094bd"/>
   <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="9f6b7471c881ee689183d681658cf2ba3dfc5610"/>
   <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/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1412711894000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1413590290000">
   <emItems>
       <emItem  blockID="i454" id="sqlmoz@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                                 <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
@@ -22,18 +22,18 @@
               </prefs>
     </emItem>
       <emItem  blockID="i71" id="youtube@2youtube.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i404" id="{a9bb9fa0-4122-4c75-bd9a-bc27db3f9155}">
-                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+      <emItem  blockID="i734" id="profsites@pr.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i708" id="{849ded12-59e9-4dae-8f86-918b70d213dc}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
@@ -79,42 +79,44 @@
               </prefs>
     </emItem>
       <emItem  blockID="i65" id="activity@facebook.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i527" id="/^({bfec236d-e122-4102-864f-f5f19d897f5e}|{3f842035-47f4-4f10-846b-6199b07f09b8}|{92ed4bbd-83f2-4c70-bb4e-f8d3716143fe})$/">
+      <emItem  blockID="i762" id="/^({2d7886a0-85bb-4bf2-b684-ba92b4b21d23}|{2fab2e94-d6f9-42de-8839-3510cef6424b}|{c02397f7-75b0-446e-a8fa-6ef70cfbf12b}|{8b337819-d1e8-48d3-8178-168ae8c99c36}|firefox@neurowise.info|firefox@allgenius.info)$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i535" id="/^ext@WebexpEnhancedV1alpha[0-9]+\.net$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i105" id="{95ff02bc-ffc6-45f0-a5c8-619b8226a9de}">
-                        <versionRange  minVersion="0" maxVersion="*">
+      <emItem  blockID="i758" id="toolbar10853@findwide.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
+                  <pref>browser.startup.homepage</pref>
+                  <pref>browser.search.defaultenginename</pref>
               </prefs>
     </emItem>
       <emItem  blockID="i626" id="{20AD702C-661E-4534-8CE9-BA4EC9AD6ECC}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
-                        <versionRange  minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
+      <emItem  blockID="i732" id=" {e935dd68-f90d-46a6-b89e-c4657534b353}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i506" id="/^ext@bettersurfplus/">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
@@ -290,16 +292,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i8" id="{B13721C7-F507-4982-B2E5-502A71474FED}">
                         <versionRange  minVersion=" " severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i461" id="{8E9E3331-D360-4f87-8803-52DE43566502}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i7" id="{2224e955-00e9-4613-a844-ce69fccaae91}">
                           <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i174" id="info@thebflix.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
@@ -349,17 +357,29 @@
               </prefs>
     </emItem>
       <emItem  blockID="i494" id="/^({e9df9360-97f8-4690-afe6-996c80790da4}|{687578b9-7132-4a7a-80e4-30ee31099e03}|{46a3135d-3683-48cf-b94c-82655cbc0e8a}|{49c795c2-604a-4d18-aeb1-b3eba27e5ea2}|{7473b6bd-4691-4744-a82b-7854eb3d70b6}|{96f454ea-9d38-474f-b504-56193e00c1a5})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i467" id="plugin@analytic-s.com">
+      <emItem  blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
+                        <versionRange  minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
+      <emItem  blockID="i364" id="{FE1DEEEA-DB6D-44b8-83F0-34FC0F9D1052}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
+      <emItem  blockID="i740" id="ascsurfingprotection@iobit.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i47" id="youtube@youtube2.com">
                           <prefs>
               </prefs>
@@ -405,16 +425,22 @@
                     </versionRange>
                                 <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                                 <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i722" id="{9802047e-5a84-4da3-b103-c55995d147d1}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i11" id="yslow@yahoo-inc.com">
                         <versionRange  minVersion="2.0.5" maxVersion="2.0.5">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                               <versionRange  minVersion="3.5.7" maxVersion="*" />
                           </targetApplication>
                     </versionRange>
                     <prefs>
               </prefs>
@@ -438,18 +464,18 @@
               </prefs>
     </emItem>
       <emItem  blockID="i99" id="pfzPXmnzQRXX6@2iABkVe.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i66" id="youtubeer@youtuber.com">
-                        <versionRange  minVersion="0" maxVersion="*">
+      <emItem  blockID="i527" id="/^({bfec236d-e122-4102-864f-f5f19d897f5e}|{3f842035-47f4-4f10-846b-6199b07f09b8}|{92ed4bbd-83f2-4c70-bb4e-f8d3716143fe})$/">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i438" id="{02edb56b-9b33-435b-b7df-b2843273a694}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
@@ -516,16 +542,28 @@
               </prefs>
     </emItem>
       <emItem  blockID="i447" id="{B18B1E5C-4D81-11E1-9C00-AFEB4824019B}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i754" id="{bb7b7a60-f574-47c2-8a0b-4c56f2da9802}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
+      <emItem  blockID="i105" id="{95ff02bc-ffc6-45f0-a5c8-619b8226a9de}">
+                        <versionRange  minVersion="0" maxVersion="*">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i668" id="/^(matchersite(pro(srcs?)?)?\@matchersite(pro(srcs?)?)?\.com)|((pro)?sitematcher(_srcs?|pro|site|sitesrc|-generic)?\@(pro)?sitematcher(_srcs?|pro|site|sitesrc|-generic)?\.com)$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i519" id="703db0db-5fe9-44b6-9f53-c6a91a0ad5bd@7314bc82-969e-4d2a-921b-e5edd0b02cf1.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -576,28 +614,42 @@
               </prefs>
     </emItem>
       <emItem  blockID="i168" id="flashX@adobe.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i433" id="{c95a4e8e-816d-4655-8c79-d736da1adb6d}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i39" id="{c2d64ff7-0ab8-4263-89c9-ea3b0f8f050c}">
                         <versionRange  minVersion="0.1" maxVersion="4.3.1.00" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i42" id="{D19CA586-DD6C-4a0a-96F8-14644F340D60}">
                         <versionRange  minVersion="0.1" maxVersion="14.4.0" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i760" id="toolbar11093@freshy.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+                  <pref>browser.startup.homepage</pref>
+                  <pref>browser.search.defaultenginename</pref>
+              </prefs>
+    </emItem>
       <emItem  blockID="i628" id="ffxtlbr@iminent.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
                   <pref>browser.startup.homepage</pref>
                   <pref>browser.search.defaultenginename</pref>
               </prefs>
     </emItem>
@@ -738,18 +790,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i338" id="{1FD91A9C-410C-4090-BBCC-55D3450EF433}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i364" id="{FE1DEEEA-DB6D-44b8-83F0-34FC0F9D1052}">
-                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+      <emItem  blockID="i10" id="{8CE11043-9A15-4207-A565-0C94C42D590D}">
+                          <prefs>
+              </prefs>
+    </emItem>
+      <emItem  blockID="i726" id="{d87d56b2-1379-49f4-b081-af2850c79d8e}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i59" id="ghostviewer@youtube2.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
@@ -762,18 +818,18 @@
               </prefs>
     </emItem>
       <emItem  blockID="i51" id="admin@youtubeplayer.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i433" id="{c95a4e8e-816d-4655-8c79-d736da1adb6d}">
-                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+      <emItem  blockID="i473" id="{81b13b5d-fba1-49fd-9a6b-189483ac548a}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i352" id="vpyekkifgv@vpyekkifgv.org">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
@@ -846,20 +902,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i584" id="{52b0f3db-f988-4788-b9dc-861d016f4487}">
                         <versionRange  minVersion="0" maxVersion="0.1.9999999" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i370" id="happylyrics@hpyproductions.net">
+      <emItem  name="/Astromenda/" blockID="i738">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
+                  <pref>browser.startup.homepage</pref>
+                  <pref>browser.search.defaultenginename</pref>
               </prefs>
     </emItem>
       <emItem  blockID="i440" id="{2d069a16-fca1-4e81-81ea-5d5086dcbd0c}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
@@ -914,18 +972,20 @@
               </prefs>
     </emItem>
       <emItem  blockID="i218" id="ffxtlbr@claro.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i10" id="{8CE11043-9A15-4207-A565-0C94C42D590D}">
-                          <prefs>
+      <emItem  blockID="i370" id="happylyrics@hpyproductions.net">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i548" id="/^firefox@(jumpflip|webconnect|browsesmart|mybuzzsearch|outobox|greygray|lemurleap|divapton|secretsauce|batbrowse|whilokii|linkswift|qualitink|browsefox|kozaka|diamondata|glindorus|saltarsmart|bizzybolt|websparkle)\.(com?|net|org|info|biz)$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
@@ -983,29 +1043,36 @@
               </prefs>
     </emItem>
       <emItem  blockID="i507" id="4zffxtbr-bs@VideoDownloadConverter_4z.com">
                         <versionRange  minVersion="0" maxVersion="5.75.3.25126" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i730" id="25p@9eAkaLq.net">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+                  <pref>browser.startup.homepage</pref>
+              </prefs>
+    </emItem>
       <emItem  blockID="i692" id="/^(j003-lqgrmgpcekslhg|SupraSavings|j003-dkqonnnthqjnkq|j003-kaggrpmirxjpzh)@jetpack$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i172" id="info@bflix.info">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i722" id="{9802047e-5a84-4da3-b103-c55995d147d1}">
+      <emItem  blockID="i510" id="{3c9a72a0-b849-40f3-8c84-219109c27554}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i525" id="/^({65f9f6b7-2dae-46fc-bfaf-f88e4af1beca}|{9ed31f84-c8b3-4926-b950-dff74047ff79}|{0134af61-7a0c-4649-aeca-90d776060cb3}|{02edb56b-9b33-435b-b7df-b2843273a694}|{da51d4f6-3e7e-4ef8-b400-9198e0874606}|{b24577db-155e-4077-bb37-3fdd3c302bb5})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
@@ -1189,28 +1256,34 @@
               </prefs>
     </emItem>
       <emItem  blockID="i560" id="adsremoval@adsremoval.net">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i461" id="{8E9E3331-D360-4f87-8803-52DE43566502}">
+      <emItem  blockID="i748" id="{32da2f20-827d-40aa-a3b4-2fc4a294352e}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i542" id="/^({bf67a47c-ea97-4caf-a5e3-feeba5331231}|{24a0cfe1-f479-4b19-b627-a96bf1ea3a56})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i750" id="{46eddf51-a4f6-4476-8d6c-31c5187b2a2f}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i493" id="12x3q@3244516.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i344" id="lrcsTube@hansanddeta.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -1231,16 +1304,23 @@
               </prefs>
     </emItem>
       <emItem  blockID="i448" id="{0134af61-7a0c-4649-aeca-90d776060cb3}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i728" id="l@AdLJ7uz.net">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+                  <pref>browser.startup.homepage</pref>
+              </prefs>
+    </emItem>
       <emItem  blockID="i694" id="59D317DB041748fdB89B47E6F96058F3@jetpack">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i104" id="yasd@youasdr3.com">
                         <versionRange  minVersion="0" maxVersion="*">
@@ -1273,18 +1353,18 @@
               </prefs>
     </emItem>
       <emItem  blockID="i432" id="lugcla21@gmail.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i91" id="crossriderapp4926@crossrider.com">
-                        <versionRange  minVersion="0" maxVersion="0.81.43" severity="1">
+      <emItem  blockID="i528" id="008abed2-b43a-46c9-9a5b-a771c87b82da@1ad61d53-2bdc-4484-a26b-b888ecae1906.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i480" id="pluggets@gmail.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                                 <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -1535,16 +1615,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i586" id="jid1-0xtMKhXFEs4jIg@jetpack">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i744" id="{84a93d51-b7a9-431e-8ff8-d60e5d7f5df1}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i318" id="ffxtlbr@incredibar.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i19" id="{46551EC9-40F0-4e47-8E18-8E5CF550CFB8}">
                         <versionRange  minVersion="1.1b1" maxVersion="1.1b1">
@@ -1553,16 +1639,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i531" id="/^(4cb61367-efbf-4aa1-8e3a-7f776c9d5763@cdece6e9-b2ef-40a9-b178-291da9870c59\.com|0efc9c38-1ec7-49ed-8915-53a48b6b7600@e7f17679-2a42-4659-83c5-7ba961fdf75a\.com|6be3335b-ef79-4b0b-a0ba-b87afbc6f4ad@6bbb4d2e-e33e-4fa5-9b37-934f4fb50182\.com)$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i404" id="{a9bb9fa0-4122-4c75-bd9a-bc27db3f9155}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i490" id="now.msn.com@services.mozilla.org">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i523" id="/^({7e8a1050-cf67-4575-92df-dcc60e7d952d}|{b3420a9c-a397-4409-b90d-bcf22da1a08a}|{eca6641f-2176-42ba-bdbe-f3e327f8e0af}|{707dca12-3f99-4d94-afea-06dcc0ae0108}|{aea20431-87fc-40be-bc5b-18066fe2819c}|{30ee6676-1ba6-455a-a7e8-298fa863a546})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -1571,16 +1663,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i312" id="extension21804@extension21804.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i756" id="{5eeb83d0-96ea-4249-942c-beead6847053}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i545" id="superlrcs@svenyor.net">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i324" id="/^((34qEOefiyYtRJT@IM5Munavn\.com)|(Mro5Fm1Qgrmq7B@ByrE69VQfZvZdeg\.com)|(KtoY3KGxrCe5ie@yITPUzbBtsHWeCdPmGe\.com)|(9NgIdLK5Dq4ZMwmRo6zk@FNt2GCCLGyUuOD\.com)|(NNux7bWWW@RBWyXdnl6VGls3WAwi\.com)|(E3wI2n@PEHTuuNVu\.com)|(2d3VuWrG6JHBXbQdbr@3BmSnQL\.com))$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
@@ -1619,18 +1717,18 @@
               </prefs>
     </emItem>
       <emItem  blockID="i320" id="torntv@torntv.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i528" id="008abed2-b43a-46c9-9a5b-a771c87b82da@1ad61d53-2bdc-4484-a26b-b888ecae1906.com">
-                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+      <emItem  blockID="i91" id="crossriderapp4926@crossrider.com">
+                        <versionRange  minVersion="0" maxVersion="0.81.43" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i469" id="OKitSpace@OKitSpace.es">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
@@ -1701,16 +1799,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i544" id="/^(93abedcf-8e3a-4d02-b761-d1441e437c09@243f129d-aee2-42c2-bcd1-48858e1c22fd\.com|9acfc440-ac2d-417a-a64c-f6f14653b712@09f9a966-9258-4b12-af32-da29bdcc28c5\.com|58ad0086-1cfb-48bb-8ad2-33a8905572bc@5715d2be-69b9-4930-8f7e-64bdeb961cfd\.com)$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i746" id="{58d2a791-6199-482f-a9aa-9b725ec61362}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i224" id="{336D0C35-8A85-403a-B9D2-65C292C39087}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i61" id="youtube@youtube3.com">
                         <versionRange  minVersion="0" maxVersion="*">
@@ -1763,28 +1867,34 @@
               </prefs>
     </emItem>
       <emItem  blockID="i70" id="psid-vhvxQHMZBOzUZA@jetpack">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i473" id="{81b13b5d-fba1-49fd-9a6b-189483ac548a}">
-                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+      <emItem  blockID="i66" id="youtubeer@youtuber.com">
+                        <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i690" id="{55dce8ba-9dec-4013-937e-adbf9317d990">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i742" id="{f894a29a-f065-40c3-bb19-da6057778493}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i437" id="{4933189D-C7F7-4C6E-834B-A29F087BFD23}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i394" id="{7D4F1959-3F72-49d5-8E59-F02F8AA6815D}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -1801,16 +1911,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i165" id="{EEF73632-A085-4fd3-A778-ECD82C8CB297}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i736" id="{c5e48979-bd7f-4cf7-9b73-2482a67a4f37}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i60" id="youtb3@youtb3.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i662" id="imbaty@taringamp3.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
@@ -1819,17 +1935,17 @@
               </prefs>
     </emItem>
       <emItem  blockID="i546" id="firefox@browsefox.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i510" id="{3c9a72a0-b849-40f3-8c84-219109c27554}">
+      <emItem  blockID="i724" id="{1cdbda58-45f8-4d91-b566-8edce18f8d0a}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i23" id="firefox@bandoo.com">
                         <versionRange  minVersion="5.0" maxVersion="5.0" severity="1">
                       <targetApplication  id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
@@ -1939,16 +2055,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i82" id="{8f42fb8b-b6f6-45de-81c0-d6d39f54f971}">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i467" id="plugin@analytic-s.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i380" id="{cc8f597b-0765-404e-a575-82aefbd81daf}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i322" id="jid0-Y6TVIzs0r7r4xkOogmJPNAGFGBw@jetpack">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
@@ -2009,16 +2131,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i374" id="update@firefox.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i752" id="savingsslider@mybrowserbar.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i314" id="crossriderapp8812@crossrider.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i21" id="support@update-firefox.com">
                           <prefs>
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -235,16 +235,21 @@
       <menuitem id="context-video-saveimage"
                 accesskey="&videoSaveImage.accesskey;"
                 label="&videoSaveImage.label;"
                 oncommand="gContextMenu.saveVideoFrameAsImage();"/>
       <menuitem id="context-sendvideo"
                 label="&emailVideoCmd.label;"
                 accesskey="&emailVideoCmd.accesskey;"
                 oncommand="gContextMenu.sendMedia();"/>
+      <menu id="context-castvideo"
+                label="&castVideoCmd.label;"
+                accesskey="&castVideoCmd.accesskey;">
+        <menupopup id="context-castvideo-popup" onpopupshowing="gContextMenu.populateCastVideoMenu(this)"/>
+      </menu>
       <menuitem id="context-sendaudio"
                 label="&emailAudioCmd.label;"
                 accesskey="&emailAudioCmd.accesskey;"
                 oncommand="gContextMenu.sendMedia();"/>
       <menuitem id="context-ctp-play"
                 label="&playPluginCmd.label;"
                 accesskey="&playPluginCmd.accesskey;"
                 oncommand="gContextMenu.playPlugin();"/>
--- a/browser/base/content/browser-tabPreviews.js
+++ b/browser/base/content/browser-tabPreviews.js
@@ -42,20 +42,16 @@ var tabPreviews = {
       img.src = PageThumbs.getThumbnailURL(uri);
       return img;
     }
 
     return this.capture(aTab, !aTab.hasAttribute("busy"));
   },
 
   capture: function tabPreviews_capture(aTab, aShouldCache) {
-    // Bug 863512 - Make page thumbnails work in electrolysis
-    if (gMultiProcessBrowser)
-      return new Image();
-
     let browser = aTab.linkedBrowser;
     let uri = browser.currentURI.spec;
 
     // FIXME: The gBrowserThumbnails._shouldCapture determines whether
     //        thumbnails should be written to disk. This should somehow be part
     //        of the PageThumbs API. (bug 1062414)
     if (aShouldCache &&
         gBrowserThumbnails._shouldCapture(browser)) {
@@ -72,17 +68,17 @@ var tabPreviews = {
 
     let canvas = PageThumbs.createCanvas(window);
 
     if (aShouldCache) {
       aTab.__thumbnail = canvas;
       aTab.__thumbnail_lastURI = uri;
     }
 
-    PageThumbs.captureToCanvas(aTab.linkedBrowser.contentWindow, canvas);
+    PageThumbs.captureToCanvas(browser, canvas);
     return canvas;
   },
 
   handleEvent: function tabPreviews_handleEvent(event) {
     switch (event.type) {
       case "TabSelect":
         if (this._selectedTab &&
             this._selectedTab.parentNode &&
--- a/browser/base/content/browser-thumbnails.js
+++ b/browser/base/content/browser-thumbnails.js
@@ -28,39 +28,31 @@ let gBrowserThumbnails = {
   _timeouts: null,
 
   /**
    * List of tab events we want to listen for.
    */
   _tabEvents: ["TabClose", "TabSelect"],
 
   init: function Thumbnails_init() {
-    // Bug 863512 - Make page thumbnails work in electrolysis
-    if (gMultiProcessBrowser)
-      return;
-
     PageThumbs.addExpirationFilter(this);
     gBrowser.addTabsProgressListener(this);
     Services.prefs.addObserver(this.PREF_DISK_CACHE_SSL, this, false);
 
     this._sslDiskCacheEnabled =
       Services.prefs.getBoolPref(this.PREF_DISK_CACHE_SSL);
 
     this._tabEvents.forEach(function (aEvent) {
       gBrowser.tabContainer.addEventListener(aEvent, this, false);
     }, this);
 
     this._timeouts = new WeakMap();
   },
 
   uninit: function Thumbnails_uninit() {
-    // Bug 863512 - Make page thumbnails work in electrolysis
-    if (gMultiProcessBrowser)
-      return;
-
     PageThumbs.removeExpirationFilter(this);
     gBrowser.removeTabsProgressListener(this);
     Services.prefs.removeObserver(this.PREF_DISK_CACHE_SSL, this);
 
     this._tabEvents.forEach(function (aEvent) {
       gBrowser.tabContainer.removeEventListener(aEvent, this, false);
     }, this);
   },
@@ -120,43 +112,43 @@ let gBrowserThumbnails = {
       this._capture(aBrowser);
     }.bind(this), this._captureDelayMS);
 
     this._timeouts.set(aBrowser, timeout);
   },
 
   // FIXME: This should be part of the PageThumbs API. (bug 1062414)
   _shouldCapture: function Thumbnails_shouldCapture(aBrowser) {
-    // Don't try to capture in e10s yet (because of bug 698371)
-    if (gMultiProcessBrowser)
-      return false;
-
     // Capture only if it's the currently selected tab.
     if (aBrowser != gBrowser.selectedBrowser)
       return false;
 
     // Don't capture in per-window private browsing mode.
     if (PrivateBrowsingUtils.isWindowPrivate(window))
       return false;
 
     let doc = aBrowser.contentDocument;
 
     // FIXME Bug 720575 - Don't capture thumbnails for SVG or XML documents as
     //       that currently regresses Talos SVG tests.
     if (doc instanceof SVGDocument || doc instanceof XMLDocument)
       return false;
 
+    // Don't take screenshots of about: pages.
+    if (aBrowser.currentURI.schemeIs("about"))
+      return false;
+
+    // FIXME e10s work around, we need channel information. bug 1073957
+    if (!aBrowser.docShell)
+      return true;
+
     // There's no point in taking screenshot of loading pages.
     if (aBrowser.docShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
       return false;
 
-    // Don't take screenshots of about: pages.
-    if (aBrowser.currentURI.schemeIs("about"))
-      return false;
-
     let channel = aBrowser.docShell.currentDocumentChannel;
 
     // No valid document channel. We shouldn't take a screenshot.
     if (!channel)
       return false;
 
     // Don't take screenshots of internally redirecting about: pages.
     // This includes error pages.
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -183,16 +183,22 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 #endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "FormValidationHandler",
   "resource:///modules/FormValidationHandler.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "UITour",
   "resource:///modules/UITour.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "CastingApps",
+  "resource:///modules/CastingApps.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "SimpleServiceDiscovery",
+  "resource://gre/modules/SimpleServiceDiscovery.jsm");
+
 let gInitialPages = [
   "about:blank",
   "about:newtab",
   "about:home",
   "about:privatebrowsing",
   "about:welcomeback",
   "about:sessionrestore"
 ];
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -203,19 +203,29 @@ nsContextMenu.prototype = {
     this.showItem("context-savevideo", this.onVideo);
     this.showItem("context-saveaudio", this.onAudio);
     this.showItem("context-video-saveimage", this.onVideo);
     this.setItemAttr("context-savevideo", "disabled", !this.mediaURL);
     this.setItemAttr("context-saveaudio", "disabled", !this.mediaURL);
     // Send media URL (but not for canvas, since it's a big data: URL)
     this.showItem("context-sendimage", this.onImage);
     this.showItem("context-sendvideo", this.onVideo);
+    this.showItem("context-castvideo", this.onVideo);
     this.showItem("context-sendaudio", this.onAudio);
     this.setItemAttr("context-sendvideo", "disabled", !this.mediaURL);
     this.setItemAttr("context-sendaudio", "disabled", !this.mediaURL);
+    // getServicesForVideo alone would be sufficient here (it depends on
+    // SimpleServiceDiscovery.services), but SimpleServiceDiscovery is garanteed
+    // to be already loaded, since we load it on startup, and CastingApps isn't,
+    // so check SimpleServiceDiscovery.services first to avoid needing to load
+    // CastingApps.jsm if we don't need to.
+    let shouldShowCast = this.mediaURL &&
+                         SimpleServiceDiscovery.services.length > 0 &&
+                         CastingApps.getServicesForVideo(this.target).length > 0;
+    this.setItemAttr("context-castvideo", "disabled", !shouldShowCast);
   },
 
   initViewItems: function CM_initViewItems() {
     // View source is always OK, unless in directory listing.
     this.showItem("context-viewpartialsource-selection",
                   this.isContentSelected);
     this.showItem("context-viewpartialsource-mathml",
                   this.onMathML && !this.isContentSelected);
@@ -1311,16 +1321,35 @@ nsContextMenu.prototype = {
     if (this.onCanvas || this.onImage)
         this.sendMedia();
   },
 
   sendMedia: function() {
     MailIntegration.sendMessage(this.mediaURL, "");
   },
 
+  castVideo: function() {
+    CastingApps.openExternal(this.target, window);
+  },
+
+  populateCastVideoMenu: function(popup) {
+    let videoEl = this.target;
+    popup.innerHTML = null;
+    let doc = popup.ownerDocument;
+    let services = CastingApps.getServicesForVideo(videoEl);
+    services.forEach(service => {
+      let item = doc.createElement("menuitem");
+      item.setAttribute("label", service.friendlyName);
+      item.addEventListener("command", event => {
+        CastingApps.sendVideoToService(videoEl, service);
+      });
+      popup.appendChild(item);
+    });
+  },
+
   playPlugin: function() {
     gPluginHandler.contextMenuCommand(this.browser, this.target, "play");
   },
 
   hidePlugin: function() {
     gPluginHandler.contextMenuCommand(this.browser, this.target, "hide");
   },
 
--- a/browser/base/content/socialchat.xml
+++ b/browser/base/content/socialchat.xml
@@ -702,17 +702,17 @@
         // canvas size (in CSS pixels) to the window's backing resolution in order
         // to get a full-resolution drag image for use on HiDPI displays.
         let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
         let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
         let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
         canvas.mozOpaque = true;
         canvas.width = 160 * scale;
         canvas.height = 90 * scale;
-        PageThumbs.captureToCanvas(chatbox.contentWindow, canvas);
+        PageThumbs.captureToCanvas(chatbox, canvas);
         dt.setDragImage(canvas, -16 * scale, -16 * scale);
 
         event.stopPropagation();
       ]]></handler>
 
       <handler event="dragend"><![CDATA[
         let dt = event.dataTransfer;
         let draggedChat = dt.mozGetDataAt("application/x-moz-chatbox", 0);
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -4525,17 +4525,17 @@
         let windowUtils = window.getInterface(Ci.nsIDOMWindowUtils);
         let scale = windowUtils.screenPixelsPerCSSPixel / windowUtils.fullZoom;
         let canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
         canvas.mozOpaque = true;
         canvas.width = 160 * scale;
         canvas.height = 90 * scale;
         if (!gMultiProcessBrowser) {
           // Bug 863512 - Make page thumbnails work in e10s
-          PageThumbs.captureToCanvas(browser.contentWindow, canvas);
+          PageThumbs.captureToCanvas(browser, canvas);
         }
         dt.setDragImage(canvas, -16 * scale, -16 * scale);
 
         // _dragData.offsetX/Y give the coordinates that the mouse should be
         // positioned relative to the corner of the new window created upon
         // dragend such that the mouse appears to have the same position
         // relative to the corner of the dragged tab.
         function clientX(ele) ele.getBoundingClientRect().left;
--- a/browser/base/content/test/general/test_contextmenu.html
+++ b/browser/base/content/test/general/test_contextmenu.html
@@ -169,17 +169,19 @@ function runTest(testNum) {
                           "context-video-showstats",    true,
                           "context-video-fullscreen",   true,
                           "---",                        null,
                           "context-viewvideo",          true,
                           "context-copyvideourl",       true,
                           "---",                        null,
                           "context-savevideo",          true,
                           "context-video-saveimage",    true,
-                          "context-sendvideo",          true
+                          "context-sendvideo",          true,
+                          "context-castvideo",          null,
+                            [], null
                          ].concat(inspectItems));
         closeContextMenu();
         openContextMenuFor(audio_in_video); // Invoke context menu for next test.
     },
 
     function () {
         // Context menu for a video (with an audio-only file)
           checkContextMenu(["context-media-play",         true,
@@ -213,17 +215,19 @@ function runTest(testNum) {
                           "context-video-showstats",    false,
                           "context-video-fullscreen",   false,
                           "---",                        null,
                           "context-viewvideo",          true,
                           "context-copyvideourl",       true,
                           "---",                        null,
                           "context-savevideo",          true,
                           "context-video-saveimage",    false,
-                          "context-sendvideo",          true
+                          "context-sendvideo",          true,
+                          "context-castvideo",          null,
+                            [], null
                          ].concat(inspectItems));
         closeContextMenu();
         openContextMenuFor(video_bad2); // Invoke context menu for next test.
     },
 
     function () {
         // Context menu for a video (with an INVALID media source)
         checkContextMenu(["context-media-play",         false,
@@ -237,17 +241,19 @@ function runTest(testNum) {
                           "context-video-showstats",    false,
                           "context-video-fullscreen",   false,
                           "---",                        null,
                           "context-viewvideo",          false,
                           "context-copyvideourl",       false,
                           "---",                        null,
                           "context-savevideo",          false,
                           "context-video-saveimage",    false,
-                          "context-sendvideo",          false
+                          "context-sendvideo",          false,
+                          "context-castvideo",          null,
+                            [], null
                          ].concat(inspectItems));
         closeContextMenu();
         openContextMenuFor(iframe); // Invoke context menu for next test.
     },
 
     function () {
         // Context menu for an iframe
         checkContextMenu(["context-navigation", null,
@@ -296,16 +302,18 @@ function runTest(testNum) {
                           "context-video-fullscreen",   true,
                           "---",                        null,
                           "context-viewvideo",          true,
                           "context-copyvideourl",       true,
                           "---",                        null,
                           "context-savevideo",          true,
                           "context-video-saveimage",    true,
                           "context-sendvideo",          true,
+                          "context-castvideo",          null,
+                            [], null,
                           "frame",                null,
                               ["context-showonlythisframe", true,
                                "context-openframeintab",    true,
                                "context-openframe",         true,
                                "---",                       null,
                                "context-reloadframe",       true,
                                "---",                       null,
                                "context-bookmarkframe",     true,
--- a/browser/components/customizableui/CustomizableWidgets.jsm
+++ b/browser/components/customizableui/CustomizableWidgets.jsm
@@ -919,17 +919,17 @@ const CustomizableWidgets = [
     onCommand: function(aEvent) {
       let win = aEvent.view;
       win.MailIntegration.sendLinkForWindow(win.content);
     }
   }, {
     id: "loop-call-button",
     type: "custom",
     label: "loop-call-button3.label",
-    tooltiptext: "loop-call-button2.tooltiptext",
+    tooltiptext: "loop-call-button3.tooltiptext",
     defaultArea: CustomizableUI.AREA_NAVBAR,
     introducedInVersion: 1,
     onBuild: function(aDocument) {
       let node = aDocument.createElementNS(kNSXUL, "toolbarbutton");
       node.setAttribute("id", this.id);
       node.classList.add("toolbarbutton-1");
       node.classList.add("chromeclass-toolbar-additional");
       node.classList.add("badged-button");
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -94,16 +94,19 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource:///modules/BrowserUITelemetry.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
                                   "resource://gre/modules/AsyncShutdown.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "LoginManagerParent",
                                   "resource://gre/modules/LoginManagerParent.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "SimpleServiceDiscovery",
+                                  "resource://gre/modules/SimpleServiceDiscovery.jsm");
+
 #ifdef NIGHTLY_BUILD
 XPCOMUtils.defineLazyModuleGetter(this, "SignInToWebsiteUX",
                                   "resource:///modules/SignInToWebsite.jsm");
 #endif
 
 XPCOMUtils.defineLazyModuleGetter(this, "ContentSearch",
                                   "resource:///modules/ContentSearch.jsm");
 
@@ -742,18 +745,40 @@ BrowserGlue.prototype = {
     if (Services.prefs.getBoolPref("dom.identity.enabled")) {
       SignInToWebsiteUX.uninit();
     }
 #endif
     webrtcUI.uninit();
     FormValidationHandler.uninit();
   },
 
+  _initServiceDiscovery: function () {
+    var rokuDevice = {
+      id: "roku:ecp",
+      target: "roku:ecp",
+      factory: function(aService) {
+        Cu.import("resource://gre/modules/RokuApp.jsm");
+        return new RokuApp(aService);
+      },
+      mirror: false,
+      types: ["video/mp4"],
+      extensions: ["mp4"]
+    };
+
+    // Register targets
+    SimpleServiceDiscovery.registerDevice(rokuDevice);
+
+    // Search for devices continuously every 120 seconds
+    SimpleServiceDiscovery.search(120 * 1000);
+  },
+
   // All initial windows have opened.
   _onWindowsRestored: function BG__onWindowsRestored() {
+    this._initServiceDiscovery();
+
     // Show update notification, if needed.
     if (Services.prefs.prefHasUserValue("app.update.postupdate"))
       this._showUpdateNotification();
 
     // Load the "more info" page for a locked places.sqlite
     // This property is set earlier by places-database-locked topic.
     if (this._isPlacesDatabaseLocked) {
       this._showPlacesLockedNotificationBox();
@@ -1339,17 +1364,17 @@ BrowserGlue.prototype = {
     var notifyBox = win.gBrowser.getNotificationBox();
     var notification = notifyBox.appendNotification(text, title, null,
                                                     notifyBox.PRIORITY_CRITICAL_MEDIUM,
                                                     buttons);
     notification.persistence = -1; // Until user closes it
   },
 
   _migrateUI: function BG__migrateUI() {
-    const UI_VERSION = 23;
+    const UI_VERSION = 24;
     const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
     let currentUIVersion = 0;
     try {
       currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
     } catch(ex) {}
     if (currentUIVersion >= UI_VERSION)
       return;
 
@@ -1417,25 +1442,16 @@ BrowserGlue.prototype = {
         };
 
         if (toolbarIsCustomized || getToolbarFolderCount() > 3) {
           xulStore.setValue(BROWSER_DOCURL, "PersonalToolbar", "collapsed", "false");
         }
       }
     }
 
-    if (currentUIVersion < 8) {
-      // Reset homepage pref for users who have it set to google.com/firefox
-      let uri = Services.prefs.getComplexValue("browser.startup.homepage",
-                                               Ci.nsIPrefLocalizedString).data;
-      if (uri && /^https?:\/\/(www\.)?google(\.\w{2,3}){1,2}\/firefox\/?$/.test(uri)) {
-        Services.prefs.clearUserPref("browser.startup.homepage");
-      }
-    }
-
     if (currentUIVersion < 9) {
       // This code adds the customizable downloads buttons.
       let currentset = xulStore.getValue(BROWSER_DOCURL, "nav-bar", "currentset");
 
       // Since the Downloads button is located in the navigation bar by default,
       // migration needs to happen only if the toolbar was customized using a
       // previous UI version, and the button was not already placed on the
       // toolbar manually.
@@ -1490,20 +1506,16 @@ BrowserGlue.prototype = {
         if (currentset.contains("bookmarks-menu-button-container")) {
           currentset = currentset.replace(/(^|,)bookmarks-menu-button-container($|,)/,
                                           "$1bookmarks-menu-button$2");
           xulStore.setValue(BROWSER_DOCURL, "nav-bar", "currentset", currentset);
         }
       }
     }
 
-    if (currentUIVersion < 13) {
-      /* Obsolete */
-    }
-
     if (currentUIVersion < 14) {
       // DOM Storage doesn't specially handle about: pages anymore.
       let path = OS.Path.join(OS.Constants.Path.profileDir,
                               "chromeappsstore.sqlite");
       OS.File.remove(path);
     }
 
     if (currentUIVersion < 16) {
@@ -1588,16 +1600,28 @@ BrowserGlue.prototype = {
         try {
           let name = Services.prefs.getComplexValue(kSelectedEnginePref,
                                                     Ci.nsIPrefLocalizedString).data;
           Services.search.currentEngine = Services.search.getEngineByName(name);
         } catch (ex) {}
       }
     }
 
+    if (currentUIVersion < 24) {
+      // Reset homepage pref for users who have it set to start.mozilla.org
+      // or google.com/firefox.
+      const HOMEPAGE_PREF = "browser.startup.homepage";
+      let uri = Services.prefs.getComplexValue(HOMEPAGE_PREF,
+                                               Ci.nsIPrefLocalizedString).data;
+      if (uri && (uri.startsWith("http://start.mozilla.org") ||
+                  /^https?:\/\/(www\.)?google\.[a-z.]+\/firefox/i.test(uri))) {
+        Services.prefs.clearUserPref(HOMEPAGE_PREF);
+      }
+    }
+
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
   },
 
   // ------------------------------
   // public nsIBrowserGlue members
   // ------------------------------
 
--- a/browser/components/shell/nsGNOMEShellService.cpp
+++ b/browser/components/shell/nsGNOMEShellService.cpp
@@ -308,52 +308,53 @@ nsGNOMEShellService::SetDefaultBrowser(b
       // Add mime types for html, xhtml extension and set app to just created appinfo.
       for (unsigned int i = 0; i < ArrayLength(appTypes); ++i) {
         appInfo->SetAsDefaultForMimeType(nsDependentCString(appTypes[i].mimeType));
         appInfo->SetAsDefaultForFileExtensions(nsDependentCString(appTypes[i].extensions));
       }
     }
   }
 
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+  if (prefs) {
+    (void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsGNOMEShellService::GetShouldCheckDefaultBrowser(bool* aResult)
 {
   // If we've already checked, the browser has been started and this is a 
   // new window open, and we don't want to check again.
   if (mCheckedThisSession) {
     *aResult = false;
     return NS_OK;
   }
 
-  nsCOMPtr<nsIPrefBranch> prefs;
-  nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
-  if (pserve)
-    pserve->GetBranch("", getter_AddRefs(prefs));
+  nsresult rv;
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-  if (prefs)
-    prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
-
-  return NS_OK;
+  return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
 }
 
 NS_IMETHODIMP
 nsGNOMEShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
 {
-  nsCOMPtr<nsIPrefBranch> prefs;
-  nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
-  if (pserve)
-    pserve->GetBranch("", getter_AddRefs(prefs));
+  nsresult rv;
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-  if (prefs)
-    prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
-
-  return NS_OK;
+  return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
 }
 
 NS_IMETHODIMP
 nsGNOMEShellService::GetCanSetDesktopBackground(bool* aResult)
 {
   // setting desktop background is currently only supported
   // for Gnome or desktops using the same GSettings and GConf keys
   const char* gnomeSession = getenv("GNOME_DESKTOP_SESSION_ID");
--- a/browser/components/shell/nsMacShellService.cpp
+++ b/browser/components/shell/nsMacShellService.cpp
@@ -85,51 +85,54 @@ nsMacShellService::SetDefaultBrowser(boo
   if (aClaimAllTypes) {
     if (::LSSetDefaultHandlerForURLScheme(CFSTR("ftp"), firefoxID) != noErr) {
       return NS_ERROR_FAILURE;
     }
     if (::LSSetDefaultRoleHandlerForContentType(kUTTypeHTML, kLSRolesAll, firefoxID) != noErr) {
       return NS_ERROR_FAILURE;
     }
   }
-  
+
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+  if (prefs) {
+    (void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
+  }
+
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsMacShellService::GetShouldCheckDefaultBrowser(bool* aResult)
 {
   // If we've already checked, the browser has been started and this is a 
   // new window open, and we don't want to check again.
   if (mCheckedThisSession) {
     *aResult = false;
     return NS_OK;
   }
 
-  nsCOMPtr<nsIPrefBranch> prefs;
-  nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
-  if (pserve)
-    pserve->GetBranch("", getter_AddRefs(prefs));
+  nsresult rv;
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-  prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
-
-  return NS_OK;
+  return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
 }
 
 NS_IMETHODIMP
 nsMacShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
 {
-  nsCOMPtr<nsIPrefBranch> prefs;
-  nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID));
-  if (pserve)
-    pserve->GetBranch("", getter_AddRefs(prefs));
+  nsresult rv;
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
-  prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
-
-  return NS_OK;
+  return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
 }
 
 NS_IMETHODIMP
 nsMacShellService::GetCanSetDesktopBackground(bool* aResult)
 {
   *aResult = true;
   return NS_OK;
 }
--- a/browser/components/shell/nsWindowsShellService.cpp
+++ b/browser/components/shell/nsWindowsShellService.cpp
@@ -270,48 +270,43 @@ nsWindowsShellService::ShortcutMaintenan
   if (!isSupported)
     return NS_OK;
 
   nsAutoString appId;
   if (NS_FAILED(taskbarInfo->GetDefaultGroupId(appId)))
     return NS_ERROR_UNEXPECTED;
 
   NS_NAMED_LITERAL_CSTRING(prefName, "browser.taskbar.lastgroupid");
-  nsCOMPtr<nsIPrefService> prefs =
+  nsCOMPtr<nsIPrefBranch> prefs =
     do_GetService(NS_PREFSERVICE_CONTRACTID);
   if (!prefs)
     return NS_ERROR_UNEXPECTED;
 
-  nsCOMPtr<nsIPrefBranch> prefBranch;
-  prefs->GetBranch(nullptr, getter_AddRefs(prefBranch));
-  if (!prefBranch)
-    return NS_ERROR_UNEXPECTED;
-
   nsCOMPtr<nsISupportsString> prefString;
-  rv = prefBranch->GetComplexValue(prefName.get(),
-                                   NS_GET_IID(nsISupportsString),
-                                   getter_AddRefs(prefString));
+  rv = prefs->GetComplexValue(prefName.get(),
+                              NS_GET_IID(nsISupportsString),
+                              getter_AddRefs(prefString));
   if (NS_SUCCEEDED(rv)) {
     nsAutoString version;
     prefString->GetData(version);
     if (!version.IsEmpty() && version.Equals(appId)) {
       // We're all good, get out of here.
       return NS_OK;
     }
   }
   // Update the version in prefs
   prefString =
     do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv);
   if (NS_FAILED(rv))
     return rv;
 
   prefString->SetData(appId);
-  rv = prefBranch->SetComplexValue(prefName.get(),
-                                   NS_GET_IID(nsISupportsString),
-                                   prefString);
+  rv = prefs->SetComplexValue(prefName.get(),
+                              NS_GET_IID(nsISupportsString),
+                              prefString);
   if (NS_FAILED(rv)) {
     NS_WARNING("Couldn't set last user model id!");
     return NS_ERROR_UNEXPECTED;
   }
 
   nsAutoString appHelperPath;
   if (NS_FAILED(GetHelperPath(appHelperPath)))
     return NS_ERROR_UNEXPECTED;
@@ -702,53 +697,53 @@ nsWindowsShellService::SetDefaultBrowser
       // The above calls hould never really fail, but just in case
       // fallb ack to showing control panel for all defaults
       if (NS_FAILED(rv)) {
         rv = LaunchControlPanelDefaultPrograms();
       }
     }
   }
 
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID));
+  if (prefs) {
+    (void) prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, true);
+  }
+
   return rv;
 }
 
 NS_IMETHODIMP
 nsWindowsShellService::GetShouldCheckDefaultBrowser(bool* aResult)
 {
   NS_ENSURE_ARG_POINTER(aResult);
 
   // If we've already checked, the browser has been started and this is a 
   // new window open, and we don't want to check again.
   if (mCheckedThisSession) {
     *aResult = false;
     return NS_OK;
   }
 
-  nsCOMPtr<nsIPrefBranch> prefs;
   nsresult rv;
-  nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  rv = pserve->GetBranch("", getter_AddRefs(prefs));
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   return prefs->GetBoolPref(PREF_CHECKDEFAULTBROWSER, aResult);
 }
 
 NS_IMETHODIMP
 nsWindowsShellService::SetShouldCheckDefaultBrowser(bool aShouldCheck)
 {
-  nsCOMPtr<nsIPrefBranch> prefs;
   nsresult rv;
-
-  nsCOMPtr<nsIPrefService> pserve(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
-  NS_ENSURE_SUCCESS(rv, rv);
-  
-  rv = pserve->GetBranch("", getter_AddRefs(prefs));
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<nsIPrefBranch> prefs(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+  if (NS_FAILED(rv)) {
+    return rv;
+  }
 
   return prefs->SetBoolPref(PREF_CHECKDEFAULTBROWSER, aShouldCheck);
 }
 
 static nsresult
 WriteBitmap(nsIFile* aFile, imgIContainer* aImage)
 {
   nsresult rv;
--- a/browser/components/tabview/tabitems.js
+++ b/browser/components/tabview/tabitems.js
@@ -1378,25 +1378,19 @@ TabCanvas.prototype = Utils.extend(new S
   // ----------
   // Function: paint
   paint: function TabCanvas_paint(evt) {
     var w = this.canvas.width;
     var h = this.canvas.height;
     if (!w || !h)
       return;
 
-    if (!this.tab.linkedBrowser.contentWindow) {
-      Utils.log('no tab.linkedBrowser.contentWindow in TabCanvas.paint()');
-      return;
-    }
-
-    let win = this.tab.linkedBrowser.contentWindow;
-    gPageThumbnails.captureToCanvas(win, this.canvas);
-
-    this._sendToSubscribers("painted");
+    gPageThumbnails.captureToCanvas(this.tab.linkedBrowser, this.canvas, () => {
+      this._sendToSubscribers("painted");
+    });
   },
 
   // ----------
   // Function: toImageData
   toImageData: function TabCanvas_toImageData() {
     return this.canvas.toDataURL("image/png");
   }
 });
--- a/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js
+++ b/browser/devtools/timeline/test/browser_timeline_overview-initial-selection-01.js
@@ -24,17 +24,17 @@ let test = Task.async(function*() {
 
   yield TimelineController.toggleRecording();
   ok(true, "Recording has ended.");
 
   let markers = TimelineController.getMarkers();
   let selection = TimelineView.overview.getSelection();
 
   is((selection.start) | 0,
-     (markers[0].start * TimelineView.overview.dataScaleX) | 0,
+     ((markers[0].start - markers.startTime) * TimelineView.overview.dataScaleX) | 0,
     "The initial selection start is correct.");
 
   is((selection.end - selection.start) | 0,
      (selectionRatio * TimelineView.overview.width) | 0,
     "The initial selection end is correct.");
 
   yield teardown(panel);
   finish();
--- a/browser/devtools/timeline/timeline.js
+++ b/browser/devtools/timeline/timeline.js
@@ -102,23 +102,29 @@ let TimelineController = {
       yield this._stopRecording();
     }
   }),
 
   /**
    * Starts the recording, updating the UI as needed.
    */
   _startRecording: function*() {
+    TimelineView.handleRecordingStarted();
+    let startTime = yield gFront.start();
+    // Times must come from the actor in order to be self-consistent.
+    // However, we also want to update the view with the elapsed time
+    // even when the actor is not generating data.  To do this we get
+    // the local time and use it to compute a reasonable elapsed time.
+    // See _onRecordingTick.
+    this._localStartTime = performance.now();
+
     this._markers = [];
-    this._markers.startTime = performance.now();
-    this._markers.endTime = performance.now();
+    this._markers.startTime = startTime;
+    this._markers.endTime = startTime;
     this._updateId = setInterval(this._onRecordingTick, OVERVIEW_UPDATE_INTERVAL);
-
-    TimelineView.handleRecordingStarted();
-    yield gFront.start();
   },
 
   /**
    * Stops the recording, updating the UI as needed.
    */
   _stopRecording: function*() {
     clearInterval(this._updateId);
 
@@ -137,27 +143,36 @@ let TimelineController = {
   },
 
   /**
    * Callback handling the "markers" event on the timeline front.
    *
    * @param array markers
    *        A list of new markers collected since the last time this
    *        function was invoked.
+   * @param number endTime
+   *        A time after the last marker in markers was collected.
    */
-  _onMarkers: function(markers) {
+  _onMarkers: function(markers, endTime) {
     Array.prototype.push.apply(this._markers, markers);
+    this._markers.endTime = endTime;
   },
 
   /**
    * Callback invoked at a fixed interval while recording.
    * Updates the markers store with the current time and the timeline overview.
    */
   _onRecordingTick: function() {
-    this._markers.endTime = performance.now();
+    // Compute an approximate ending time for the view.  This is
+    // needed to ensure that the view updates even when new data is
+    // not being generated.
+    let fakeTime = this._markers.startTime + (performance.now() - this._localStartTime);
+    if (fakeTime > this._markers.endTime) {
+      this._markers.endTime = fakeTime;
+    }
     TimelineView.handleMarkersUpdate(this._markers);
   }
 };
 
 /**
  * Functions handling the timeline frontend view.
  */
 let TimelineView = {
@@ -209,22 +224,22 @@ let TimelineView = {
   handleRecordingEnded: function() {
     $("#record-button").removeAttribute("checked");
     $("#timeline-pane").selectedPanel = $("#timeline-waterfall");
 
     this.overview.selectionEnabled = true;
 
     let markers = TimelineController.getMarkers();
     if (markers.length) {
-      let start = markers[0].start * this.overview.dataScaleX;
+      let start = (markers[0].start - markers.startTime) * this.overview.dataScaleX;
       let end = start + this.overview.width * OVERVIEW_INITIAL_SELECTION_RATIO;
       this.overview.setSelection({ start, end });
     } else {
       let duration = markers.endTime - markers.startTime;
-      this.waterfall.setData(markers, 0, duration);
+      this.waterfall.setData(markers, markers.startTime, markers.endTime);
     }
 
     window.emit(EVENTS.RECORDING_ENDED);
   },
 
   /**
    * Signals that a new set of markers was made available by the controller,
    * or that the overview graph needs to be updated.
@@ -246,18 +261,18 @@ let TimelineView = {
       this.waterfall.clearView();
       return;
     }
     let selection = this.overview.getSelection();
     let start = selection.start / this.overview.dataScaleX;
     let end = selection.end / this.overview.dataScaleX;
 
     let markers = TimelineController.getMarkers();
-    let timeStart = Math.min(start, end);
-    let timeEnd = Math.max(start, end);
+    let timeStart = markers.startTime + Math.min(start, end);
+    let timeEnd = markers.startTime + Math.max(start, end);
     this.waterfall.setData(markers, timeStart, timeEnd);
   },
 
   /**
    * Callback handling the "refresh" event on the timeline overview.
    */
   _onRefresh: function() {
     this.waterfall.recalculateBounds();
--- a/browser/devtools/timeline/widgets/overview.js
+++ b/browser/devtools/timeline/widgets/overview.js
@@ -165,16 +165,19 @@ Overview.prototype = Heritage.extend(Abs
       gradient.addColorStop(OVERVIEW_MARKERS_COLOR_STOPS[0], style.stroke);
       gradient.addColorStop(OVERVIEW_MARKERS_COLOR_STOPS[1], style.fill);
       gradient.addColorStop(OVERVIEW_MARKERS_COLOR_STOPS[2], style.fill);
       gradient.addColorStop(OVERVIEW_MARKERS_COLOR_STOPS[3], style.stroke);
       ctx.fillStyle = gradient;
       ctx.beginPath();
 
       for (let { start, end } of batch) {
+        start -= this._data.startTime;
+        end -= this._data.startTime;
+
         let left = start * dataScale;
         let duration = Math.max(end - start, OVERVIEW_MARKER_DURATION_MIN);
         let width = Math.max(duration * dataScale, this._pixelRatio);
         ctx.rect(left, top, width, height);
       }
 
       ctx.fill();
 
--- a/browser/devtools/timeline/widgets/waterfall.js
+++ b/browser/devtools/timeline/widgets/waterfall.js
@@ -71,26 +71,27 @@ function Waterfall(parent) {
 
 Waterfall.prototype = {
   /**
    * Populates this view with the provided data source.
    *
    * @param array markers
    *        A list of markers received from the controller.
    * @param number timeStart
-   *        The delta time (in milliseconds) to start drawing from.
+   *        The time (in milliseconds) to start drawing from.
    * @param number timeEnd
-   *        The delta time (in milliseconds) to end drawing at.
+   *        The time (in milliseconds) to end drawing at.
    */
   setData: function(markers, timeStart, timeEnd) {
     this.clearView();
 
     let dataScale = this._waterfallWidth / (timeEnd - timeStart);
     this._drawWaterfallBackground(dataScale);
-    this._buildHeader(this._headerContents, timeStart, dataScale);
+    // Label the header as if the first possible marker was at T=0.
+    this._buildHeader(this._headerContents, timeStart - markers.startTime, dataScale);
     this._buildMarkers(this._listContents, markers, timeStart, timeEnd, dataScale);
   },
 
   /**
    * Depopulates this view.
    */
   clearView: function() {
     while (this._headerContents.hasChildNodes()) {
--- a/browser/devtools/webaudioeditor/test/browser.ini
+++ b/browser/devtools/webaudioeditor/test/browser.ini
@@ -18,16 +18,17 @@ support-files =
 [browser_audionode-actor-get-param-flags.js]
 [browser_audionode-actor-get-params-01.js]
 [browser_audionode-actor-get-params-02.js]
 [browser_audionode-actor-get-set-param.js]
 [browser_audionode-actor-get-type.js]
 [browser_audionode-actor-is-source.js]
 [browser_audionode-actor-bypass.js]
 [browser_audionode-actor-connectnode-disconnect.js]
+[browser_audionode-actor-connectparam.js]
 [browser_webaudio-actor-simple.js]
 [browser_webaudio-actor-destroy-node.js]
 [browser_webaudio-actor-connect-param.js]
 
 [browser_wa_destroy-node-01.js]
 
 [browser_wa_first-run.js]
 [browser_wa_reset-01.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectparam.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that AudioNodeActor#connectParam() work.
+ * Uses the editor front as the actors do not retain connect state.
+ */
+
+function spawnTest() {
+  let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+  let { panelWin } = panel;
+  let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+
+  reload(target);
+
+  let [actors] = yield Promise.all([
+    get3(gFront, "create-node"),
+    waitForGraphRendered(panelWin, 3, 2)
+  ]);
+
+  let [dest, osc, gain] = actors;
+
+  yield osc.disconnect();
+
+  osc.connectParam(gain, "gain");
+  yield Promise.all([
+    waitForGraphRendered(panelWin, 3, 1, 1),
+    once(gAudioNodes, "connect")
+  ]);
+  ok(true, "Oscillator connect to Gain's Gain AudioParam, event emitted.");
+
+  yield teardown(panel);
+  finish();
+}
+
--- a/browser/devtools/webide/content/addons.js
+++ b/browser/devtools/webide/content/addons.js
@@ -25,23 +25,24 @@ window.addEventListener("unload", functi
   ForgetAddonsList();
 }, true);
 
 function CloseUI() {
   window.parent.UI.openProject();
 }
 
 function BuildUI(addons) {
-  BuildItem(addons.adb, true /* is adb */);
+  BuildItem(addons.adb, "adb");
+  BuildItem(addons.adapters, "adapters");
   for (let addon of addons.simulators) {
-    BuildItem(addon, false /* is adb */);
+    BuildItem(addon, "simulator");
   }
 }
 
-function BuildItem(addon, isADB) {
+function BuildItem(addon, type) {
 
   function onAddonUpdate(event, arg) {
     switch (event) {
       case "update":
         progress.removeAttribute("value");
         li.setAttribute("status", addon.status);
         status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
         break;
@@ -68,30 +69,39 @@ function BuildItem(addon, isADB) {
     for (let e of events) {
       addon.off(e, onAddonUpdate);
     }
   });
 
   let li = document.createElement("li");
   li.setAttribute("status", addon.status);
 
-  // Used in tests
-  if (isADB) {
-    li.setAttribute("addon", "adb");
-  } else {
-    li.setAttribute("addon", "simulator-" + addon.version);
-  }
-
   let name = document.createElement("span");
   name.className = "name";
-  if (isADB) {
-    name.textContent = Strings.GetStringFromName("addons_adb_label");
-  } else {
-    let stability = Strings.GetStringFromName("addons_" + addon.stability);
-    name.textContent = Strings.formatStringFromName("addons_simulator_label", [addon.version, stability], 2);
+
+  switch (type) {
+    case "adb":
+      li.setAttribute("addon", type);
+      name.textContent = Strings.GetStringFromName("addons_adb_label");
+      break;
+    case "adapters":
+      li.setAttribute("addon", type);
+      try {
+        name.textContent = Strings.GetStringFromName("addons_adapters_label");
+      } catch(e) {
+        // This code (bug 1081093) will be backported to Aurora, which doesn't
+        // contain this string.
+        name.textContent = "Tools Adapters Add-on";
+      }
+      break;
+    case "simulator":
+      li.setAttribute("addon", "simulator-" + addon.version);
+      let stability = Strings.GetStringFromName("addons_" + addon.stability);
+      name.textContent = Strings.formatStringFromName("addons_simulator_label", [addon.version, stability], 2);
+      break;
   }
 
   li.appendChild(name);
 
   let status = document.createElement("span");
   status.className = "status";
   status.textContent = Strings.GetStringFromName("addons_status_" + addon.status);
   li.appendChild(status);
@@ -106,17 +116,17 @@ function BuildItem(addon, isADB) {
   uninstallButton.className = "uninstall-button";
   uninstallButton.onclick = () => addon.uninstall();
   uninstallButton.textContent = Strings.GetStringFromName("addons_uninstall_button");
   li.appendChild(uninstallButton);
 
   let progress = document.createElement("progress");
   li.appendChild(progress);
 
-  if (isADB) {
+  if (type == "adb") {
     let warning = document.createElement("p");
     warning.textContent = Strings.GetStringFromName("addons_adb_warning");
     warning.className = "warning";
     li.appendChild(warning);
   }
 
   document.querySelector("ul").appendChild(li);
 }
--- a/browser/devtools/webide/content/webide.js
+++ b/browser/devtools/webide/content/webide.js
@@ -64,25 +64,32 @@ let UI = {
 
     this.onfocus = this.onfocus.bind(this);
     window.addEventListener("focus", this.onfocus, true);
 
     AppProjects.load().then(() => {
       this.autoSelectProject();
     });
 
-    // Auto install the ADB Addon Helper. Only once.
-    // If the user decides to uninstall the addon, we won't install it again.
-    let autoInstallADBHelper = Services.prefs.getBoolPref("devtools.webide.autoinstallADBHelper");
-    if (autoInstallADBHelper && !Devices.helperAddonInstalled) {
+    // Auto install the ADB Addon Helper and Tools Adapters. Only once.
+    // If the user decides to uninstall any of this addon, we won't install it again.
+    let autoinstallADBHelper = Services.prefs.getBoolPref("devtools.webide.autoinstallADBHelper");
+    let autoinstallFxdtAdapters = Services.prefs.getBoolPref("devtools.webide.autoinstallFxdtAdapters");
+    if (autoinstallADBHelper) {
       GetAvailableAddons().then(addons => {
         addons.adb.install();
       }, console.error);
     }
+    if (autoinstallFxdtAdapters) {
+      GetAvailableAddons().then(addons => {
+        addons.adapters.install();
+      }, console.error);
+    }
     Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
+    Services.prefs.setBoolPref("devtools.webide.autoinstallFxdtAdapters", false);
 
     this.lastConnectedRuntime = Services.prefs.getCharPref("devtools.webide.lastConnectedRuntime");
 
     this.setupDeck();
   },
 
   uninit: function() {
     window.removeEventListener("focus", this.onfocus, true);
--- a/browser/devtools/webide/modules/addons.js
+++ b/browser/devtools/webide/modules/addons.js
@@ -8,59 +8,49 @@ const {AddonManager} = Cu.import("resour
 const {EventEmitter} = Cu.import("resource://gre/modules/devtools/event-emitter.js");
 const {Simulator} = Cu.import("resource://gre/modules/devtools/Simulator.jsm");
 const {Devices} = Cu.import("resource://gre/modules/devtools/Devices.jsm");
 const {Services} = Cu.import("resource://gre/modules/Services.jsm");
 const {GetAddonsJSON} = require("devtools/webide/remote-resources");
 
 let SIMULATOR_LINK = Services.prefs.getCharPref("devtools.webide.simulatorAddonsURL");
 let ADB_LINK = Services.prefs.getCharPref("devtools.webide.adbAddonURL");
+let ADAPTERS_LINK = Services.prefs.getCharPref("devtools.webide.adaptersAddonURL");
 let SIMULATOR_ADDON_ID = Services.prefs.getCharPref("devtools.webide.simulatorAddonID");
 let ADB_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adbAddonID");
+let ADAPTERS_ADDON_ID = Services.prefs.getCharPref("devtools.webide.adaptersAddonID");
 
 let platform = Services.appShell.hiddenDOMWindow.navigator.platform;
 let OS = "";
 if (platform.indexOf("Win") != -1) {
   OS = "win32";
 } else if (platform.indexOf("Mac") != -1) {
   OS = "mac64";
 } else if (platform.indexOf("Linux") != -1) {
   if (platform.indexOf("x86_64") != -1) {
     OS = "linux64";
   } else {
-    OS = "linux";
+    OS = "linux32";
   }
 }
 
-Simulator.on("unregister", updateSimulatorAddons);
-Simulator.on("register", updateSimulatorAddons);
-Devices.on("addon-status-updated", updateAdbAddon);
-
-function updateSimulatorAddons(event, version) {
+let addonsListener = {};
+addonsListener.onEnabled =
+addonsListener.onDisabled =
+addonsListener.onInstalled =
+addonsListener.onUninstalled = (updatedAddon) => {
   GetAvailableAddons().then(addons => {
-    let foundAddon = null;
-    for (let addon of addons.simulators) {
-      if (addon.version == version) {
-        foundAddon = addon;
-        break;
+    for (let a of [...addons.simulators, addons.adb, addons.adapters]) {
+      if (a.addonID == updatedAddon.id) {
+        a.updateInstallStatus();
       }
     }
-    if (!foundAddon) {
-      console.warn("An unknown simulator (un)registered", version);
-      return;
-    }
-    foundAddon.updateInstallStatus();
   });
 }
-
-function updateAdbAddon() {
-  GetAvailableAddons().then(addons => {
-    addons.adb.updateInstallStatus();
-  });
-}
+AddonManager.addAddonListener(addonsListener);
 
 let GetAvailableAddons_promise = null;
 let GetAvailableAddons = exports.GetAvailableAddons = function() {
   if (!GetAvailableAddons_promise) {
     let deferred = promise.defer();
     GetAvailableAddons_promise = deferred.promise;
     let addons = {
       simulators: [],
@@ -68,16 +58,17 @@ let GetAvailableAddons = exports.GetAvai
     }
     GetAddonsJSON(true).then(json => {
       for (let stability in json) {
         for (let version of json[stability]) {
           addons.simulators.push(new SimulatorAddon(stability, version));
         }
       }
       addons.adb = new ADBAddon();
+      addons.adapters = new AdaptersAddon();
       deferred.resolve(addons);
     }, e => {
       GetAvailableAddons_promise = null;
       deferred.reject(e);
     });
   }
   return GetAvailableAddons_promise;
 }
@@ -94,33 +85,42 @@ Addon.prototype = {
       this._status = value;
       this.emit("update");
     }
   },
   get status() {
     return this._status;
   },
 
+  updateInstallStatus: function() {
+    AddonManager.getAddonByID(this.addonID, (addon) => {
+      if (addon && !addon.userDisabled) {
+        this.status = "installed";
+      } else {
+        this.status = "uninstalled";
+      }
+    });
+  },
+
   install: function() {
-    if (this.status != "uninstalled") {
-      throw new Error("Not uninstalled");
-    }
-    this.status = "preparing";
-
     AddonManager.getAddonByID(this.addonID, (addon) => {
+      if (addon && !addon.userDisabled) {
+        this.status = "installed";
+        return;
+      }
+      this.status = "preparing";
       if (addon && addon.userDisabled) {
         addon.userDisabled = false;
       } else {
         AddonManager.getInstallForURL(this.xpiLink, (install) => {
           install.addListener(this);
           install.install();
         }, "application/x-xpinstall");
       }
     });
-
   },
 
   uninstall: function() {
     AddonManager.getAddonByID(this.addonID, (addon) => {
       addon.uninstall();
     });
   },
 
@@ -162,49 +162,35 @@ Addon.prototype = {
     this.installFailureHandler(install, "Install failed");
   },
 }
 
 function SimulatorAddon(stability, version) {
   EventEmitter.decorate(this);
   this.stability = stability;
   this.version = version;
-  this.xpiLink = SIMULATOR_LINK.replace(/#OS#/g, OS)
+  // This addon uses the string "linux" for "linux32"
+  let fixedOS = OS == "linux32" ? "linux" : OS;
+  this.xpiLink = SIMULATOR_LINK.replace(/#OS#/g, fixedOS)
                                .replace(/#VERSION#/g, version)
                                .replace(/#SLASHED_VERSION#/g, version.replace(/\./g, "_"));
   this.addonID = SIMULATOR_ADDON_ID.replace(/#SLASHED_VERSION#/g, version.replace(/\./g, "_"));
   this.updateInstallStatus();
 }
-
-SimulatorAddon.prototype = Object.create(Addon.prototype, {
-  updateInstallStatus: {
-    enumerable: true,
-    value: function() {
-      let sim = Simulator.getByVersion(this.version);
-      if (sim) {
-        this.status = "installed";
-      } else {
-        this.status = "uninstalled";
-      }
-    }
-  },
-});
+SimulatorAddon.prototype = Object.create(Addon.prototype);
 
 function ADBAddon() {
   EventEmitter.decorate(this);
-  this.xpiLink = ADB_LINK.replace(/#OS#/g, OS);
+  // This addon uses the string "linux" for "linux32"
+  let fixedOS = OS == "linux32" ? "linux" : OS;
+  this.xpiLink = ADB_LINK.replace(/#OS#/g, fixedOS);
   this.addonID = ADB_ADDON_ID;
   this.updateInstallStatus();
 }
+ADBAddon.prototype = Object.create(Addon.prototype);
 
-ADBAddon.prototype = Object.create(Addon.prototype, {
-  updateInstallStatus: {
-    enumerable: true,
-    value: function() {
-      if (Devices.helperAddonInstalled) {
-        this.status = "installed";
-      } else {
-        this.status = "uninstalled";
-      }
-    }
-  },
-});
-
+function AdaptersAddon() {
+  EventEmitter.decorate(this);
+  this.xpiLink = ADAPTERS_LINK.replace(/#OS#/g, OS);
+  this.addonID = ADAPTERS_ADDON_ID;
+  this.updateInstallStatus();
+}
+AdaptersAddon.prototype = Object.create(Addon.prototype);
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a512ae3d199746350c650e527b0178411b0440e
GIT binary patch
literal 1156
zc$^FHW@Zs#U|`^2SQYE%`bJKz+?|nuA%=;8frmkcAt^t<q`0Igu|O}YI5dQlfqBCR
zn~+UFTw1}+z{v6qs1&TbcY?1sv!OuS``_AZ{<)gcFE?JAti-{^y0_v?TKe6IIcX<-
zqPG6HKetY!Dqv5_r;mHyTML^v^YVH-vQE9ql(IXXFS<suHtVXnLGjet2c7+yzQ+_f
z?06#faMovrGWDgE#}e3hZYgU#b8Ogs_+j6D4LAPRmt43+&pepMq;}9UCoAI0bJIH|
z4-`NA__Z|h{-JB!Dq$I(Cc(iX(KjZ2W1H|M*Q)E;#}lkg{s(%iHcb-P<FwvNZ?1IT
zP1EP<>GRHAirFi_jBAd|!$}FLlX(`OoaE(uGU|Pe!_FTnr~DUbxK^)ORD1m$m$_Nj
zp7#@jCObx|r%OKl;5zO9l#5jv-Y$i@c~dOAtXIBDmpr#j+<kfJvqqNbk2n51|53$C
z;%wsgytL5ppnyj#DR+NMsILvTZ|{k-@a6s0CNW`aPrXv@#cA7L>MvUozuAr<03I3B
zfsygsZq}Tiz!1L9%)r16jEu~@;*!Li9KE8HG;mz>0^^|<GcHntV*6(s2-G|e&)Ioh
zilb9c-fKsguX5<F$$q(^EBm>1I-@sC+}jef`rr3o3J;q;wb)v3-fUg*w_uf*#iWfv
zt9$J^mE=8At)^$~|0u0@<WRlGp0C!54}LpF{a7YFS+M4k<w3vMTrz31zlf*utZC$D
z@maQMLHD~)Oa7nO<WiA$DJV^jfumxIq^9@#hY}7lE{rvr42qY(o?cK@*CM8?adv|D
zX}4v(!F+3<G%I`DHrGmdx$Dmr-jo#%%X==>iZB|<%y=Ct5w^E}!CAYKSm)>BYzi)q
zDqpX^ExY6E)Vn{@%WiLTw0?VH^3x(ow?c{Ihn}=<(C_-FGG*`Q>a$&|Cce3)(YGkl
z<Lc@U=lt)id98c%%!ygCry5q~-Du6e@=7PlGgb0*P{@iD$7$9RCkJkwR2ej<$ZFHI
zrJ^&E-+tOEzwXVoM1N_O=l5rxd9<!%CtqpZO3jzqcMCO^L~Z7le%<2hEc=jGDx!Cl
z;qSR35x3?Qv+bGp@mEOMrGSP0vFDn#XW4!{zN_p#L#0H9%sE!+vj5&Um1emejY%nN
zNh-6~K4R4C;j_%*sH*Ps%SYeb*J$eAHR;vy%CA4{ItngrTzA1x+Ml^v&c*gS@9r~F
z_jqnUuh@MjRLV`e?YvgIdeR-G#~Ko?qN>Z+`IgIY{hS&afByKhduJV9U$A|3<TrnS
zHzSiAGp@`m!2kk4F2j;W5DPVHvqG{qT2ewb5i><1o7fAaP|_zWBz<BS$jS!N!~}$k
Kf%Iu+5Dx$(df(Lm
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a512ae3d199746350c650e527b0178411b0440e
GIT binary patch
literal 1156
zc$^FHW@Zs#U|`^2SQYE%`bJKz+?|nuA%=;8frmkcAt^t<q`0Igu|O}YI5dQlfqBCR
zn~+UFTw1}+z{v6qs1&TbcY?1sv!OuS``_AZ{<)gcFE?JAti-{^y0_v?TKe6IIcX<-
zqPG6HKetY!Dqv5_r;mHyTML^v^YVH-vQE9ql(IXXFS<suHtVXnLGjet2c7+yzQ+_f
z?06#faMovrGWDgE#}e3hZYgU#b8Ogs_+j6D4LAPRmt43+&pepMq;}9UCoAI0bJIH|
z4-`NA__Z|h{-JB!Dq$I(Cc(iX(KjZ2W1H|M*Q)E;#}lkg{s(%iHcb-P<FwvNZ?1IT
zP1EP<>GRHAirFi_jBAd|!$}FLlX(`OoaE(uGU|Pe!_FTnr~DUbxK^)ORD1m$m$_Nj
zp7#@jCObx|r%OKl;5zO9l#5jv-Y$i@c~dOAtXIBDmpr#j+<kfJvqqNbk2n51|53$C
z;%wsgytL5ppnyj#DR+NMsILvTZ|{k-@a6s0CNW`aPrXv@#cA7L>MvUozuAr<03I3B
zfsygsZq}Tiz!1L9%)r16jEu~@;*!Li9KE8HG;mz>0^^|<GcHntV*6(s2-G|e&)Ioh
zilb9c-fKsguX5<F$$q(^EBm>1I-@sC+}jef`rr3o3J;q;wb)v3-fUg*w_uf*#iWfv
zt9$J^mE=8At)^$~|0u0@<WRlGp0C!54}LpF{a7YFS+M4k<w3vMTrz31zlf*utZC$D
z@maQMLHD~)Oa7nO<WiA$DJV^jfumxIq^9@#hY}7lE{rvr42qY(o?cK@*CM8?adv|D
zX}4v(!F+3<G%I`DHrGmdx$Dmr-jo#%%X==>iZB|<%y=Ct5w^E}!CAYKSm)>BYzi)q
zDqpX^ExY6E)Vn{@%WiLTw0?VH^3x(ow?c{Ihn}=<(C_-FGG*`Q>a$&|Cce3)(YGkl
z<Lc@U=lt)id98c%%!ygCry5q~-Du6e@=7PlGgb0*P{@iD$7$9RCkJkwR2ej<$ZFHI
zrJ^&E-+tOEzwXVoM1N_O=l5rxd9<!%CtqpZO3jzqcMCO^L~Z7le%<2hEc=jGDx!Cl
z;qSR35x3?Qv+bGp@mEOMrGSP0vFDn#XW4!{zN_p#L#0H9%sE!+vj5&Um1emejY%nN
zNh-6~K4R4C;j_%*sH*Ps%SYeb*J$eAHR;vy%CA4{ItngrTzA1x+Ml^v&c*gS@9r~F
z_jqnUuh@MjRLV`e?YvgIdeR-G#~Ko?qN>Z+`IgIY{hS&afByKhduJV9U$A|3<TrnS
zHzSiAGp@`m!2kk4F2j;W5DPVHvqG{qT2ewb5i><1o7fAaP|_zWBz<BS$jS!N!~}$k
Kf%Iu+5Dx$(df(Lm
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a512ae3d199746350c650e527b0178411b0440e
GIT binary patch
literal 1156
zc$^FHW@Zs#U|`^2SQYE%`bJKz+?|nuA%=;8frmkcAt^t<q`0Igu|O}YI5dQlfqBCR
zn~+UFTw1}+z{v6qs1&TbcY?1sv!OuS``_AZ{<)gcFE?JAti-{^y0_v?TKe6IIcX<-
zqPG6HKetY!Dqv5_r;mHyTML^v^YVH-vQE9ql(IXXFS<suHtVXnLGjet2c7+yzQ+_f
z?06#faMovrGWDgE#}e3hZYgU#b8Ogs_+j6D4LAPRmt43+&pepMq;}9UCoAI0bJIH|
z4-`NA__Z|h{-JB!Dq$I(Cc(iX(KjZ2W1H|M*Q)E;#}lkg{s(%iHcb-P<FwvNZ?1IT
zP1EP<>GRHAirFi_jBAd|!$}FLlX(`OoaE(uGU|Pe!_FTnr~DUbxK^)ORD1m$m$_Nj
zp7#@jCObx|r%OKl;5zO9l#5jv-Y$i@c~dOAtXIBDmpr#j+<kfJvqqNbk2n51|53$C
z;%wsgytL5ppnyj#DR+NMsILvTZ|{k-@a6s0CNW`aPrXv@#cA7L>MvUozuAr<03I3B
zfsygsZq}Tiz!1L9%)r16jEu~@;*!Li9KE8HG;mz>0^^|<GcHntV*6(s2-G|e&)Ioh
zilb9c-fKsguX5<F$$q(^EBm>1I-@sC+}jef`rr3o3J;q;wb)v3-fUg*w_uf*#iWfv
zt9$J^mE=8At)^$~|0u0@<WRlGp0C!54}LpF{a7YFS+M4k<w3vMTrz31zlf*utZC$D
z@maQMLHD~)Oa7nO<WiA$DJV^jfumxIq^9@#hY}7lE{rvr42qY(o?cK@*CM8?adv|D
zX}4v(!F+3<G%I`DHrGmdx$Dmr-jo#%%X==>iZB|<%y=Ct5w^E}!CAYKSm)>BYzi)q
zDqpX^ExY6E)Vn{@%WiLTw0?VH^3x(ow?c{Ihn}=<(C_-FGG*`Q>a$&|Cce3)(YGkl
z<Lc@U=lt)id98c%%!ygCry5q~-Du6e@=7PlGgb0*P{@iD$7$9RCkJkwR2ej<$ZFHI
zrJ^&E-+tOEzwXVoM1N_O=l5rxd9<!%CtqpZO3jzqcMCO^L~Z7le%<2hEc=jGDx!Cl
z;qSR35x3?Qv+bGp@mEOMrGSP0vFDn#XW4!{zN_p#L#0H9%sE!+vj5&Um1emejY%nN
zNh-6~K4R4C;j_%*sH*Ps%SYeb*J$eAHR;vy%CA4{ItngrTzA1x+Ml^v&c*gS@9r~F
z_jqnUuh@MjRLV`e?YvgIdeR-G#~Ko?qN>Z+`IgIY{hS&afByKhduJV9U$A|3<TrnS
zHzSiAGp@`m!2kk4F2j;W5DPVHvqG{qT2ewb5i><1o7fAaP|_zWBz<BS$jS!N!~}$k
Kf%Iu+5Dx$(df(Lm
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5a512ae3d199746350c650e527b0178411b0440e
GIT binary patch
literal 1156
zc$^FHW@Zs#U|`^2SQYE%`bJKz+?|nuA%=;8frmkcAt^t<q`0Igu|O}YI5dQlfqBCR
zn~+UFTw1}+z{v6qs1&TbcY?1sv!OuS``_AZ{<)gcFE?JAti-{^y0_v?TKe6IIcX<-
zqPG6HKetY!Dqv5_r;mHyTML^v^YVH-vQE9ql(IXXFS<suHtVXnLGjet2c7+yzQ+_f
z?06#faMovrGWDgE#}e3hZYgU#b8Ogs_+j6D4LAPRmt43+&pepMq;}9UCoAI0bJIH|
z4-`NA__Z|h{-JB!Dq$I(Cc(iX(KjZ2W1H|M*Q)E;#}lkg{s(%iHcb-P<FwvNZ?1IT
zP1EP<>GRHAirFi_jBAd|!$}FLlX(`OoaE(uGU|Pe!_FTnr~DUbxK^)ORD1m$m$_Nj
zp7#@jCObx|r%OKl;5zO9l#5jv-Y$i@c~dOAtXIBDmpr#j+<kfJvqqNbk2n51|53$C
z;%wsgytL5ppnyj#DR+NMsILvTZ|{k-@a6s0CNW`aPrXv@#cA7L>MvUozuAr<03I3B
zfsygsZq}Tiz!1L9%)r16jEu~@;*!Li9KE8HG;mz>0^^|<GcHntV*6(s2-G|e&)Ioh
zilb9c-fKsguX5<F$$q(^EBm>1I-@sC+}jef`rr3o3J;q;wb)v3-fUg*w_uf*#iWfv
zt9$J^mE=8At)^$~|0u0@<WRlGp0C!54}LpF{a7YFS+M4k<w3vMTrz31zlf*utZC$D
z@maQMLHD~)Oa7nO<WiA$DJV^jfumxIq^9@#hY}7lE{rvr42qY(o?cK@*CM8?adv|D
zX}4v(!F+3<G%I`DHrGmdx$Dmr-jo#%%X==>iZB|<%y=Ct5w^E}!CAYKSm)>BYzi)q
zDqpX^ExY6E)Vn{@%WiLTw0?VH^3x(ow?c{Ihn}=<(C_-FGG*`Q>a$&|Cce3)(YGkl
z<Lc@U=lt)id98c%%!ygCry5q~-Du6e@=7PlGgb0*P{@iD$7$9RCkJkwR2ej<$ZFHI
zrJ^&E-+tOEzwXVoM1N_O=l5rxd9<!%CtqpZO3jzqcMCO^L~Z7le%<2hEc=jGDx!Cl
z;qSR35x3?Qv+bGp@mEOMrGSP0vFDn#XW4!{zN_p#L#0H9%sE!+vj5&Um1emejY%nN
zNh-6~K4R4C;j_%*sH*Ps%SYeb*J$eAHR;vy%CA4{ItngrTzA1x+Ml^v&c*gS@9r~F
z_jqnUuh@MjRLV`e?YvgIdeR-G#~Ko?qN>Z+`IgIY{hS&afByKhduJV9U$A|3<TrnS
zHzSiAGp@`m!2kk4F2j;W5DPVHvqG{qT2ewb5i><1o7fAaP|_zWBz<BS$jS!N!~}$k
Kf%Iu+5Dx$(df(Lm
--- a/browser/devtools/webide/test/chrome.ini
+++ b/browser/devtools/webide/test/chrome.ini
@@ -15,16 +15,20 @@ support-files =
   addons/fxos_3_0_simulator-linux.xpi
   addons/fxos_3_0_simulator-linux64.xpi
   addons/fxos_3_0_simulator-win32.xpi
   addons/fxos_3_0_simulator-mac64.xpi
   addons/adbhelper-linux.xpi
   addons/adbhelper-linux64.xpi
   addons/adbhelper-win32.xpi
   addons/adbhelper-mac64.xpi
+  addons/fxdt-adapters-linux32.xpi
+  addons/fxdt-adapters-linux64.xpi
+  addons/fxdt-adapters-win32.xpi
+  addons/fxdt-adapters-mac64.xpi
   head.js
   hosted_app.manifest
   templates.json
 
 [test_basic.html]
 [test_newapp.html]
 [test_import.html]
 [test_duplicate_import.html]
--- a/browser/devtools/webide/test/head.js
+++ b/browser/devtools/webide/test/head.js
@@ -22,35 +22,32 @@ if (window.location === "chrome://browse
 }
 
 Services.prefs.setBoolPref("devtools.webide.enabled", true);
 Services.prefs.setBoolPref("devtools.webide.enableLocalRuntime", true);
 
 Services.prefs.setCharPref("devtools.webide.addonsURL", TEST_BASE + "addons/simulators.json");
 Services.prefs.setCharPref("devtools.webide.simulatorAddonsURL", TEST_BASE + "addons/fxos_#SLASHED_VERSION#_simulator-#OS#.xpi");
 Services.prefs.setCharPref("devtools.webide.adbAddonURL", TEST_BASE + "addons/adbhelper-#OS#.xpi");
+Services.prefs.setCharPref("devtools.webide.adaptersAddonURL", TEST_BASE + "addons/fxdt-adapters-#OS#.xpi");
 Services.prefs.setCharPref("devtools.webide.templatesURL", TEST_BASE + "templates.json");
 
 
 SimpleTest.registerCleanupFunction(() => {
-  Services.prefs.clearUserPref("devtools.webide.templatesURL");
   Services.prefs.clearUserPref("devtools.webide.enabled");
   Services.prefs.clearUserPref("devtools.webide.enableLocalRuntime");
-  Services.prefs.clearUserPref("devtools.webide.addonsURL");
-  Services.prefs.clearUserPref("devtools.webide.simulatorAddonsURL");
-  Services.prefs.clearUserPref("devtools.webide.adbAddonURL");
-  Services.prefs.clearUserPref("devtools.webide.autoInstallADBHelper", false);
+  Services.prefs.clearUserPref("devtools.webide.autoinstallADBHelper");
+  Services.prefs.clearUserPref("devtools.webide.autoinstallFxdtAdapters");
 });
 
-function openWebIDE(autoInstallADBHelper) {
+function openWebIDE(autoInstallAddons) {
   info("opening WebIDE");
 
-  if (!autoInstallADBHelper) {
-    Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", false);
-  }
+  Services.prefs.setBoolPref("devtools.webide.autoinstallADBHelper", !!autoInstallAddons);
+  Services.prefs.setBoolPref("devtools.webide.autoinstallFxdtAdapters", !!autoInstallAddons);
 
   let deferred = promise.defer();
 
   let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher);
   let win = ww.openWindow(null, "chrome://webide/content/", "webide", "chrome,centerscreen,resizable", null);
 
   win.addEventListener("load", function onLoad() {
     win.removeEventListener("load", onLoad);
--- a/browser/devtools/webide/test/test_addons.html
+++ b/browser/devtools/webide/test/test_addons.html
@@ -104,20 +104,20 @@
 
           win.Cmds.showAddons();
 
           let frame = win.document.querySelector("#deck-panel-addons");
           let addonDoc = frame.contentWindow.document;
           let lis;
 
           lis = addonDoc.querySelectorAll("li");
-          is(lis.length, 4, "4 addons listed");
+          is(lis.length, 5, "5 addons listed");
 
           lis = addonDoc.querySelectorAll('li[status="installed"]');
-          is(lis.length, 2, "2 addons installed");
+          is(lis.length, 3, "3 addons installed");
 
           lis = addonDoc.querySelectorAll('li[status="uninstalled"]');
           is(lis.length, 2, "2 addons uninstalled");
 
           info("Uninstalling Simulator 2.0");
 
           yield installSimulatorFromUI(addonDoc, "2.0");
 
--- a/browser/devtools/webide/webide-prefs.js
+++ b/browser/devtools/webide/webide-prefs.js
@@ -1,18 +1,21 @@
 # -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 pref("devtools.webide.showProjectEditor", true);
 pref("devtools.webide.templatesURL", "https://code.cdn.mozilla.net/templates/list.json");
 pref("devtools.webide.autoinstallADBHelper", true);
+pref("devtools.webide.autoinstallFxdtAdapters", false);
 pref("devtools.webide.restoreLastProject", true);
 pref("devtools.webide.enableLocalRuntime", true);
 pref("devtools.webide.addonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/index.json");
 pref("devtools.webide.simulatorAddonsURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/#VERSION#/#OS#/fxos_#SLASHED_VERSION#_simulator-#OS#-latest.xpi");
 pref("devtools.webide.simulatorAddonID", "fxos_#SLASHED_VERSION#_simulator@mozilla.org");
 pref("devtools.webide.adbAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxos-simulator/adb-helper/#OS#/adbhelper-#OS#-latest.xpi");
 pref("devtools.webide.adbAddonID", "adbhelper@mozilla.org");
+pref("devtools.webide.adaptersAddonURL", "https://ftp.mozilla.org/pub/mozilla.org/labs/fxdt-adapters/#OS#/fxdt-adapters-#OS#-latest.xpi");
+pref("devtools.webide.adaptersAddonID", "fxdevtools-adapters@mozilla.org");
 pref("devtools.webide.monitorWebSocketURL", "ws://localhost:9000");
 pref("devtools.webide.lastConnectedRuntime", "");
 pref("devtools.webide.lastSelectedProject", "");
--- a/browser/fuel/test/browser_ApplicationPrefs.js
+++ b/browser/fuel/test/browser_ApplicationPrefs.js
@@ -122,17 +122,17 @@ function test() {
   var allPrefs = Application.prefs.all;
   ok(allPrefs.length >= 800, "Check 'Application.prefs.all' for the right number of preferences");
   ok(allPrefs[0].name.length > 0, "Check 'Application.prefs.all' for a valid name in the starting preference");
 
   // test the value of the preference root
   is(Application.prefs.root, "", "Check the Application preference root");
 
   // test for user changed preferences
-  ok(Application.prefs.get("browser.shell.checkDefaultBrowser").modified, "A single preference is marked as modified.");
+  ok(Application.prefs.get("browser.dom.window.dump.enabled").modified, "A single preference is marked as modified.");
   ok(!Application.prefs.get(testdata.string).modified, "A single preference is marked as not modified.");
 
   // test for a locked preference
   var pref = Application.prefs.get(testdata.string);
   ok(!pref.locked, "A single preference should not be locked.");
 
   pref.locked = true;
   ok(pref.locked, "A single preference should be locked.");
--- a/browser/locales/en-US/chrome/browser/browser.dtd
+++ b/browser/locales/en-US/chrome/browser/browser.dtd
@@ -479,16 +479,18 @@ These should match what Safari and other
 <!ENTITY saveVideoCmd.label           "Save Video As…">
 <!ENTITY saveVideoCmd.accesskey       "v">
 <!ENTITY saveAudioCmd.label           "Save Audio As…">
 <!ENTITY saveAudioCmd.accesskey       "v">
 <!ENTITY emailImageCmd.label          "Email Image…">
 <!ENTITY emailImageCmd.accesskey      "g">
 <!ENTITY emailVideoCmd.label          "Email Video…">
 <!ENTITY emailVideoCmd.accesskey      "a">
+<!ENTITY castVideoCmd.label           "Send Video To Device">
+<!ENTITY castVideoCmd.accesskey       "e">
 <!ENTITY emailAudioCmd.label          "Email Audio…">
 <!ENTITY emailAudioCmd.accesskey      "a">
 <!ENTITY playPluginCmd.label          "Activate this plugin">
 <!ENTITY playPluginCmd.accesskey      "c">
 <!ENTITY hidePluginCmd.label          "Hide this plugin">
 <!ENTITY hidePluginCmd.accesskey      "H">
 <!ENTITY copyLinkCmd.label            "Copy Link Location">
 <!ENTITY copyLinkCmd.accesskey        "a">
--- a/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
+++ b/browser/locales/en-US/chrome/browser/customizableui/customizableWidgets.properties
@@ -95,17 +95,17 @@ email-link-button.tooltiptext3 = Email a
 quit-button.tooltiptext.linux2 = Quit %1$S (%2$S)
 # LOCALIZATION NOTE(quit-button.tooltiptext.mac): %1$S is the brand name (e.g. Firefox),
 # %2$S is the keyboard shortcut
 quit-button.tooltiptext.mac = Quit %1$S (%2$S)
 
 # LOCALIZATION NOTE(loop-call-button3.label): This is a brand name, request
 # approval before you change it.
 loop-call-button3.label = Hello
-loop-call-button2.tooltiptext = Start a conversation
+loop-call-button3.tooltiptext = Start a conversation
 
 social-share-button.label = Share This Page
 social-share-button.tooltiptext = Share This Page
 
 panic-button.label = Forget
 panic-button.tooltiptext = Forget about some browsing history
 
 web-apps-button.label = Apps
--- a/browser/locales/en-US/chrome/browser/devtools/webide.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/webide.properties
@@ -47,16 +47,17 @@ addons_unstable=unstable
 # LOCALIZATION NOTE (addons_simulator_label): This label is shown as the name of
 # a given simulator version in the "Manage Simulators" pane.  %1$S: Firefox OS
 # version in the simulator, ex. 1.3.  %2$S: Simulator stability label, ex.
 # "stable" or "unstable".
 addons_simulator_label=Firefox OS %1$S Simulator (%2$S)
 addons_install_button=install
 addons_uninstall_button=uninstall
 addons_adb_label=ADB Helper Add-on
+addons_adapters_label=Tools Adapters Add-on
 addons_adb_warning=USB devices won't be detected without this add-on
 addons_status_unknown=?
 addons_status_installed=Installed
 addons_status_uninstalled=Not Installed
 addons_status_preparing=preparing
 addons_status_downloading=downloading
 addons_status_installing=installing
 
new file mode 100644
--- /dev/null
+++ b/browser/modules/CastingApps.jsm
@@ -0,0 +1,160 @@
+// -*- Mode: js; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+this.EXPORTED_SYMBOLS = ["CastingApps"];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/SimpleServiceDiscovery.jsm");
+
+
+var CastingApps = {
+  _sendEventToVideo: function (element, data) {
+    let event = element.ownerDocument.createEvent("CustomEvent");
+    event.initCustomEvent("media-videoCasting", false, true, JSON.stringify(data));
+    element.dispatchEvent(event);
+  },
+
+  makeURI: function (url, charset, baseURI) {
+    return Services.io.newURI(url, charset, baseURI);
+  },
+
+  getVideo: function (element) {
+    if (!element) {
+      return null;
+    }
+
+    let extensions = SimpleServiceDiscovery.getSupportedExtensions();
+    let types = SimpleServiceDiscovery.getSupportedMimeTypes();
+
+    // Grab the poster attribute from the <video>
+    let posterURL = element.poster;
+
+    // First, look to see if the <video> has a src attribute
+    let sourceURL = element.src;
+
+    // If empty, try the currentSrc
+    if (!sourceURL) {
+      sourceURL = element.currentSrc;
+    }
+
+    if (sourceURL) {
+      // Use the file extension to guess the mime type
+      let sourceURI = this.makeURI(sourceURL, null, this.makeURI(element.baseURI));
+      if (this.allowableExtension(sourceURI, extensions)) {
+        return { element: element, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI};
+      }
+    }
+
+    // Next, look to see if there is a <source> child element that meets
+    // our needs
+    let sourceNodes = element.getElementsByTagName("source");
+    for (let sourceNode of sourceNodes) {
+      let sourceURI = this.makeURI(sourceNode.src, null, this.makeURI(sourceNode.baseURI));
+
+      // Using the type attribute is our ideal way to guess the mime type. Otherwise,
+      // fallback to using the file extension to guess the mime type
+      if (this.allowableMimeType(sourceNode.type, types) || this.allowableExtension(sourceURI, extensions)) {
+        return { element: element, source: sourceURI.spec, poster: posterURL, sourceURI: sourceURI, type: sourceNode.type };
+      }
+    }
+
+    return null;
+  },
+
+  sendVideoToService: function (videoElement, service) {
+    if (!service)
+      return;
+
+    let video = this.getVideo(videoElement);
+    if (!video) {
+      return;
+    }
+
+    // Make sure we have a player app for the given service
+    let app = SimpleServiceDiscovery.findAppForService(service);
+    if (!app)
+      return;
+
+    video.title = videoElement.ownerDocument.defaultView.top.document.title;
+    if (video.element) {
+      // If the video is currently playing on the device, pause it
+      if (!video.element.paused) {
+        video.element.pause();
+      }
+    }
+
+    app.stop(() => {
+      app.start(started => {
+        if (!started) {
+          Cu.reportError("CastingApps: Unable to start app");
+          return;
+        }
+
+        app.remoteMedia(remoteMedia => {
+          if (!remoteMedia) {
+            Cu.reportError("CastingApps: Failed to create remotemedia");
+            return;
+          }
+
+          this.session = {
+            service: service,
+            app: app,
+            remoteMedia: remoteMedia,
+            data: {
+              title: video.title,
+              source: video.source,
+              poster: video.poster
+            },
+            videoRef: Cu.getWeakReference(video.element)
+          };
+        }, this);
+      });
+    });
+  },
+
+  getServicesForVideo: function (videoElement) {
+    let video = this.getVideo(videoElement);
+    if (!video) {
+      return {};
+    }
+
+    let filteredServices = SimpleServiceDiscovery.services.filter(service => {
+      return this.allowableExtension(video.sourceURI, service.extensions) ||
+             this.allowableMimeType(video.type, service.types);
+    });
+
+    return filteredServices;
+  },
+
+  // RemoteMedia callback API methods
+  onRemoteMediaStart: function (remoteMedia) {
+    if (!this.session) {
+      return;
+    }
+
+    remoteMedia.load(this.session.data);
+
+    let video = this.session.videoRef.get();
+    if (video) {
+      this._sendEventToVideo(video, { active: true });
+    }
+  },
+
+  onRemoteMediaStop: function (remoteMedia) {
+  },
+
+  onRemoteMediaStatus: function (remoteMedia) {
+  },
+
+  allowableExtension: function (uri, extensions) {
+    return (uri instanceof Ci.nsIURL) && extensions.indexOf(uri.fileExtension) != -1;
+  },
+
+  allowableMimeType: function (type, types) {
+    return types.indexOf(type) != -1;
+  }
+};
--- a/browser/modules/moz.build
+++ b/browser/modules/moz.build
@@ -9,16 +9,17 @@ MOCHITEST_CHROME_MANIFESTS += ['test/chr
 XPCSHELL_TESTS_MANIFESTS += [
     'test/unit/social/xpcshell.ini',
     'test/xpcshell/xpcshell.ini',
 ]
 
 EXTRA_JS_MODULES += [
     'BrowserNewTabPreloader.jsm',
     'BrowserUITelemetry.jsm',
+    'CastingApps.jsm',
     'Chat.jsm',
     'ContentClick.jsm',
     'ContentLinkHandler.jsm',
     'ContentSearch.jsm',
     'ContentWebRTC.jsm',
     'CustomizationTabPreloader.jsm',
     'DirectoryLinksProvider.jsm',
     'E10SUtils.jsm',
--- a/browser/themes/windows/browser-aero.css
+++ b/browser/themes/windows/browser-aero.css
@@ -160,19 +160,21 @@
     color: white;
   }
 
   #toolbar-menubar:not(:-moz-lwtheme) {
     text-shadow: 0 0 .5em white, 0 0 .5em white, 0 1px 0 rgba(255,255,255,.4);
   }
 
   /* Vertical toolbar border */
-  #main-window[sizemode=normal] #navigator-toolbox:not(:-moz-lwtheme)::after,
-  #main-window[sizemode=normal] #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(:-moz-lwtheme),
-  #main-window[sizemode=normal] #navigator-toolbox:-moz-lwtheme {
+  #main-window:not([customizing])[sizemode=normal] #navigator-toolbox:not(:-moz-lwtheme)::after,
+  #main-window:not([customizing])[sizemode=normal] #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar):not(:-moz-lwtheme),
+  #main-window:not([customizing])[sizemode=normal] #navigator-toolbox:-moz-lwtheme,
+  #main-window[customizing] #navigator-toolbox::after,
+  #main-window[customizing] #navigator-toolbox > toolbar:not(#toolbar-menubar):not(#TabsToolbar) {
     border-left: 1px solid @toolbarShadowColor@;
     border-right: 1px solid @toolbarShadowColor@;
     background-clip: padding-box;
   }
   #main-window[sizemode=normal] #browser-border-start,
   #main-window[sizemode=normal] #browser-border-end {
     display: -moz-box;
     background-color: @toolbarShadowColor@;
--- a/content/media/MediaRecorder.cpp
+++ b/content/media/MediaRecorder.cpp
@@ -381,16 +381,29 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
 
     NS_ENSURE_TRUE(mTrackUnionStream, NS_ERROR_FAILURE);
     mTrackUnionStream->ChangeExplicitBlockerCount(-1);
 
     return NS_OK;
   }
 
+  nsresult RequestData()
+  {
+    LOG(PR_LOG_DEBUG, ("Session.RequestData"));
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (NS_FAILED(NS_DispatchToMainThread(new PushBlobRunnable(this)))) {
+      MOZ_ASSERT(false, "RequestData NS_DispatchToMainThread failed");
+      return NS_ERROR_FAILURE;
+    }
+
+    return NS_OK;
+  }
+
   already_AddRefed<nsIDOMBlob> GetEncodedData()
   {
     MOZ_ASSERT(NS_IsMainThread());
     return mEncodedBufferCache->ExtractBlob(mRecorder->GetParentObject(),
                                             mMimeType);
   }
 
   bool IsEncoderError()
@@ -811,47 +824,27 @@ MediaRecorder::Resume(ErrorResult& aResu
   nsresult rv = mSessions.LastElement()->Resume();
   if (NS_FAILED(rv)) {
     NotifyError(rv);
     return;
   }
   mState = RecordingState::Recording;
 }
 
-class CreateAndDispatchBlobEventRunnable : public nsRunnable {
-  nsCOMPtr<nsIDOMBlob> mBlob;
-  nsRefPtr<MediaRecorder> mRecorder;
-public:
-  CreateAndDispatchBlobEventRunnable(already_AddRefed<nsIDOMBlob>&& aBlob,
-                                     MediaRecorder* aRecorder)
-    : mBlob(aBlob), mRecorder(aRecorder)
-  { }
-
-  NS_IMETHOD
-  Run();
-};
-
-NS_IMETHODIMP
-CreateAndDispatchBlobEventRunnable::Run()
-{
-  return mRecorder->CreateAndDispatchBlobEvent(mBlob.forget());
-}
-
 void
 MediaRecorder::RequestData(ErrorResult& aResult)
 {
   if (mState != RecordingState::Recording) {
     aResult.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return;
   }
   MOZ_ASSERT(mSessions.Length() > 0);
-  if (NS_FAILED(NS_DispatchToMainThread(
-                  new CreateAndDispatchBlobEventRunnable(
-                    mSessions.LastElement()->GetEncodedData(), this)))) {
-    MOZ_ASSERT(false, "NS_DispatchToMainThread CreateAndDispatchBlobEventRunnable failed");
+  nsresult rv = mSessions.LastElement()->RequestData();
+  if (NS_FAILED(rv)) {
+    NotifyError(rv);
   }
 }
 
 JSObject*
 MediaRecorder::WrapObject(JSContext* aCx)
 {
   return MediaRecorderBinding::Wrap(aCx, this);
 }
--- a/content/media/MediaRecorder.h
+++ b/content/media/MediaRecorder.h
@@ -37,17 +37,16 @@ class AudioNode;
  * When the recorder starts, it creates a "Media Encoder" thread to read data from MediaEncoder object and store buffer in EncodedBufferCache object.
  * Also extract the encoded data and create blobs on every timeslice passed from start function or RequestData function called by UA.
  */
 
 class MediaRecorder : public DOMEventTargetHelper,
                       public nsIDocumentActivity
 {
   class Session;
-  friend class CreateAndDispatchBlobEventRunnable;
 
 public:
   MediaRecorder(DOMMediaStream& aSourceMediaStream, nsPIDOMWindow* aOwnerWindow);
   MediaRecorder(AudioNode& aSrcAudioNode, uint32_t aSrcOutput, nsPIDOMWindow* aOwnerWindow);
 
   // nsWrapperCache
   virtual JSObject* WrapObject(JSContext* aCx) MOZ_OVERRIDE;
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -2829,34 +2829,34 @@ NS_IMETHODIMP
 nsDocShell::SetRecordProfileTimelineMarkers(bool aValue)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
   bool currentValue;
   GetRecordProfileTimelineMarkers(&currentValue);
   if (currentValue != aValue) {
     if (aValue) {
       ++gProfileTimelineRecordingsCount;
-      mProfileTimelineStartTime = TimeStamp::Now();
+      mProfileTimelineRecording = true;
     } else {
       --gProfileTimelineRecordingsCount;
-      mProfileTimelineStartTime = TimeStamp();
+      mProfileTimelineRecording = false;
       ClearProfileTimelineMarkers();
     }
   }
 
   return NS_OK;
 #else
   return NS_ERROR_FAILURE;
 #endif
 }
 
 NS_IMETHODIMP
 nsDocShell::GetRecordProfileTimelineMarkers(bool* aValue)
 {
-  *aValue = !mProfileTimelineStartTime.IsNull();
+  *aValue = mProfileTimelineRecording;
   return NS_OK;
 }
 
 nsresult
 nsDocShell::PopProfileTimelineMarkers(JSContext* aCx,
                           JS::MutableHandle<JS::Value> aProfileTimelineMarkers)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
@@ -2934,45 +2934,49 @@ nsDocShell::PopProfileTimelineMarkers(JS
   mProfileTimelineMarkers.SwapElements(keptMarkers);
 
   return NS_OK;
 #else
   return NS_ERROR_FAILURE;
 #endif
 }
 
-float
-nsDocShell::GetProfileTimelineDelta()
-{
-  return (TimeStamp::Now() - mProfileTimelineStartTime).ToMilliseconds();
+nsresult
+nsDocShell::Now(DOMHighResTimeStamp* aWhen)
+{
+  bool ignore;
+  *aWhen = (TimeStamp::Now() - TimeStamp::ProcessCreation(ignore)).ToMilliseconds();
+  return NS_OK;
 }
 
 void
 nsDocShell::AddProfileTimelineMarker(const char* aName,
                                      TracingMetadata aMetaData)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
-  if (!mProfileTimelineStartTime.IsNull()) {
-    float delta = GetProfileTimelineDelta();
+  if (mProfileTimelineRecording) {
+    DOMHighResTimeStamp delta;
+    Now(&delta);
     ProfilerMarkerTracing* payload = new ProfilerMarkerTracing("Timeline",
                                                                aMetaData);
     mProfileTimelineMarkers.AppendElement(
       new InternalProfileTimelineMarker(aName, payload, delta));
   }
 #endif
 }
 
 void
 nsDocShell::AddProfileTimelineMarker(const char* aName,
                                      ProfilerBacktrace* aCause,
                                      TracingMetadata aMetaData)
 {
 #ifdef MOZ_ENABLE_PROFILER_SPS
-  if (!mProfileTimelineStartTime.IsNull()) {
-    float delta = GetProfileTimelineDelta();
+  if (mProfileTimelineRecording) {
+    DOMHighResTimeStamp delta;
+    Now(&delta);
     ProfilerMarkerTracing* payload = new ProfilerMarkerTracing("Timeline",
                                                                aMetaData,
                                                                aCause);
     mProfileTimelineMarkers.AppendElement(
       new InternalProfileTimelineMarker(aName, payload, delta));
   }
 #endif
 }
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -946,46 +946,42 @@ private:
     nsCOMPtr<nsIPrincipal> mParentCharsetPrincipal;
     nsTObserverArray<nsWeakPtr> mPrivacyObservers;
     nsTObserverArray<nsWeakPtr> mReflowObservers;
     nsTObserverArray<nsWeakPtr> mScrollObservers;
     nsCString         mOriginalUriString;
     nsWeakPtr mOpener;
     nsWeakPtr mOpenedRemote;
 
-    // Storing profile timeline markers and if/when recording started
-    mozilla::TimeStamp mProfileTimelineStartTime;
+    // True if recording profiles.
+    bool mProfileTimelineRecording;
 
 #ifdef MOZ_ENABLE_PROFILER_SPS
     struct InternalProfileTimelineMarker
     {
       InternalProfileTimelineMarker(const char* aName,
                                     ProfilerMarkerTracing* aPayload,
-                                    float aTime)
+                                    DOMHighResTimeStamp aTime)
         : mName(aName)
         , mPayload(aPayload)
         , mTime(aTime)
       {}
 
       ~InternalProfileTimelineMarker()
       {
         delete mPayload;
       }
 
       const char* mName;
       ProfilerMarkerTracing* mPayload;
-      float mTime;
+      DOMHighResTimeStamp mTime;
     };
     nsTArray<InternalProfileTimelineMarker*> mProfileTimelineMarkers;
 #endif
 
-    // Get the elapsed time (in millis) since the profile timeline recording
-    // started
-    float GetProfileTimelineDelta();
-
     // Get rid of all the timeline markers accumulated so far
     void ClearProfileTimelineMarkers();
 
     // Separate function to do the actual name (i.e. not _top, _self etc.)
     // searching for FindItemWithName.
     nsresult DoFindItemWithName(const char16_t* aName,
                                 nsISupports* aRequestor,
                                 nsIDocShellTreeItem* aOriginalRequestor,
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -49,17 +49,17 @@ interface nsIWebBrowserPrint;
 interface nsIVariant;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
  
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(23157a63-26fd-44a0-a0f9-fdc64dcc004c)]
+[scriptable, builtinclass, uuid(da8f78f1-8f20-4d6d-be56-fe53e177b630)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -668,16 +668,24 @@ interface nsIDocShell : nsIDocShellTreeI
     out nsIPrincipal parentCharsetPrincipal);
 
   /**
    * Whether the docShell records profile timeline markers at the moment
    */
   [infallible] attribute boolean recordProfileTimelineMarkers;
 
   /**
+   * Return a DOMHighResTimeStamp representing the number of
+   * milliseconds from an arbitrary point in time.  The reference
+   * point is shared by all DocShells and is also used by timestamps
+   * on markers.
+   */
+  DOMHighResTimeStamp now();
+
+  /**
    * Returns and flushes the profile timeline markers gathered by the docShell
    */
   [implicit_jscontext]
   jsval popProfileTimelineMarkers();
 
   /**
    * Add an observer to the list of parties to be notified when this docshell's
    * private browsing status is changed. |obs| must support weak references.
--- a/dom/base/DOMRequestHelper.jsm
+++ b/dom/base/DOMRequestHelper.jsm
@@ -271,25 +271,35 @@ DOMRequestIpcHelper.prototype = {
   },
 
   _getRandomId: function() {
     return Cc["@mozilla.org/uuid-generator;1"]
              .getService(Ci.nsIUUIDGenerator).generateUUID().toString();
   },
 
   createRequest: function() {
+    // If we don't have a valid window object, throw.
+    if (!this._window) {
+      Cu.reportError("DOMRequestHelper trying to create a DOMRequest without a valid window, failing.");
+      throw Cr.NS_ERROR_FAILURE;
+    }
     return Services.DOMRequest.createRequest(this._window);
   },
 
   /**
    * createPromise() creates a new Promise, with `aPromiseInit` as the
    * PromiseInit callback. The promise constructor is obtained from the
    * reference to window owned by this DOMRequestIPCHelper.
    */
   createPromise: function(aPromiseInit) {
+    // If we don't have a valid window object, throw.
+    if (!this._window) {
+      Cu.reportError("DOMRequestHelper trying to create a Promise without a valid window, failing.");
+      throw Cr.NS_ERROR_FAILURE;
+    }
     return new this._window.Promise(aPromiseInit);
   },
 
   forEachRequest: function(aCallback) {
     if (!this._requests) {
       return;
     }
 
--- a/dom/mobileconnection/MobileConnectionArray.cpp
+++ b/dom/mobileconnection/MobileConnectionArray.cpp
@@ -100,14 +100,14 @@ already_AddRefed<nsIMobileConnectionServ
 NS_CreateMobileConnectionService()
 {
   nsCOMPtr<nsIMobileConnectionService> service;
 
   if (XRE_GetProcessType() == GeckoProcessType_Content) {
     service = new mozilla::dom::mobileconnection::MobileConnectionIPCService();
   } else {
 #if defined(MOZ_WIDGET_GONK) && defined(MOZ_B2G_RIL)
-    service = do_CreateInstance(GONK_MOBILECONNECTION_SERVICE_CONTRACTID);
+    service = do_GetService(GONK_MOBILECONNECTION_SERVICE_CONTRACTID);
 #endif
   }
 
   return service.forget();
 }
--- a/dom/settings/SettingsManager.js
+++ b/dom/settings/SettingsManager.js
@@ -81,24 +81,16 @@ SettingsLock.prototype = {
   },
 
   get closed() {
     return !this._open;
   },
 
   _closeHelper: function() {
     if (DEBUG) debug("closing lock " + this._id);
-    // sendMessage can get queued to run later in a thread via
-    // _closeHelper, but the SettingsManager may have died in between
-    // the time it was scheduled and the time it runs. Make sure our
-    // window is valid before sending, otherwise just ignore.
-    if (!this._settingsManager._window) {
-      if (DEBUG) debug("SettingsManager died, cannot send " + aMessageName + " message window principal.");
-      return;
-    }
     this._open = false;
     this._closeCalled = false;
     if (!this._requests || Object.keys(this._requests).length == 0) {
       if (DEBUG) debug("Requests exhausted, finalizing " + this._id);
       this._settingsManager.unregisterLock(this._id);
       this.sendMessage("Settings:Finalize", {lockID: this._id});
     } else {
       if (DEBUG) debug("Requests left: " + Object.keys(this._requests).length);
@@ -107,16 +99,28 @@ SettingsLock.prototype = {
   },
 
 
   _wrap: function _wrap(obj) {
     return Cu.cloneInto(obj, this._settingsManager._window);
   },
 
   sendMessage: function(aMessageName, aData) {
+    // sendMessage can be called after our window has died, or get
+    // queued to run later in a thread via _closeHelper, but the
+    // SettingsManager may have died in between the time it was
+    // scheduled and the time it runs. Make sure our window is valid
+    // before sending, otherwise just ignore.
+    if (!this._settingsManager._window) {
+      Cu.reportError(
+        "SettingsManager window died, cannot run settings transaction." +
+        " SettingsMessage: " + aMessageName +
+        " SettingsData: " + JSON.stringify(aData));
+      return;
+    }
     cpmm.sendAsyncMessage(aMessageName,
                           aData,
                           undefined,
                           this._settingsManager._window.document.nodePrincipal);
   },
 
   receiveMessage: function(aMessage) {
     let msg = aMessage.data;
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -1967,16 +1967,19 @@ RadioInterface.prototype = {
         break;
     }
     return null;
   },
 
   handleUnsolicitedWorkerMessage: function(message) {
     let connHandler = gDataConnectionManager.getConnectionHandler(this.clientId);
     switch (message.rilMessageType) {
+      case "audioStateChanged":
+        gTelephonyService.notifyAudioStateChanged(this.clientId, message.state);
+        break;
       case "callRing":
         gTelephonyService.notifyCallRing();
         break;
       case "callStateChange":
         gTelephonyService.notifyCallStateChanged(this.clientId, message.call);
         break;
       case "callDisconnected":
         gTelephonyService.notifyCallDisconnected(this.clientId, message.call);
--- a/dom/system/gonk/ril_consts.js
+++ b/dom/system/gonk/ril_consts.js
@@ -465,16 +465,21 @@ this.NETWORK_CREG_TECH_LTE = 14;
 this.NETWORK_CREG_TECH_HSPAP = 15;
 this.NETWORK_CREG_TECH_GSM = 16;
 
 this.CELL_INFO_TYPE_GSM = 1;
 this.CELL_INFO_TYPE_CDMA = 2;
 this.CELL_INFO_TYPE_LTE = 3;
 this.CELL_INFO_TYPE_WCDMA = 4;
 
+// Order matters.
+this.AUDIO_STATE_NO_CALL  = 0;
+this.AUDIO_STATE_INCOMING = 1;
+this.AUDIO_STATE_IN_CALL  = 2;
+
 this.CALL_STATE_UNKNOWN = -1;
 this.CALL_STATE_ACTIVE = 0;
 this.CALL_STATE_HOLDING = 1;
 this.CALL_STATE_DIALING = 2;
 this.CALL_STATE_ALERTING = 3;
 this.CALL_STATE_INCOMING = 4;
 this.CALL_STATE_WAITING = 5;
 
--- a/dom/system/gonk/ril_worker.js
+++ b/dom/system/gonk/ril_worker.js
@@ -3988,16 +3988,36 @@ RilObject.prototype = {
     }
 
     if (clearConferenceRequest) {
       this._hasConferenceRequest = false;
     }
     if (conferenceChanged) {
       this._ensureConference();
     }
+
+    // Update audio state.
+    let message = {rilMessageType: "audioStateChanged",
+                   state: this._detectAudioState()};
+    this.sendChromeMessage(message);
+  },
+
+  _detectAudioState: function() {
+    let callNum = Object.keys(this.currentCalls).length;
+    if (!callNum) {
+      return AUDIO_STATE_NO_CALL;
+    }
+
+    let firstIndex = Object.keys(this.currentCalls)[0];
+    if (callNum == 1 &&
+        this.currentCalls[firstIndex].state == CALL_STATE_INCOMING) {
+      return AUDIO_STATE_INCOMING;
+    }
+
+    return AUDIO_STATE_IN_CALL;
   },
 
   _addNewVoiceCall: function(newCall) {
     // Format international numbers appropriately.
     if (newCall.number && newCall.toa == TOA_INTERNATIONAL &&
         newCall.number[0] != "+") {
       newCall.number = "+" + newCall.number;
     }
--- a/dom/telephony/gonk/TelephonyService.js
+++ b/dom/telephony/gonk/TelephonyService.js
@@ -38,19 +38,16 @@ const CALL_WAKELOCK_TIMEOUT = 5000;
 
 // Index of the CDMA second call which isn't held in RIL but only in TelephoyService.
 const CDMA_SECOND_CALL_INDEX = 2;
 
 const DIAL_ERROR_INVALID_STATE_ERROR = "InvalidStateError";
 const DIAL_ERROR_OTHER_CONNECTION_IN_USE = "OtherConnectionInUse";
 const DIAL_ERROR_BAD_NUMBER = RIL.GECKO_CALL_ERROR_BAD_NUMBER;
 
-const AUDIO_STATE_NO_CALL  = 0;
-const AUDIO_STATE_INCOMING = 1;
-const AUDIO_STATE_IN_CALL  = 2;
 const AUDIO_STATE_NAME = [
   "PHONE_STATE_NORMAL",
   "PHONE_STATE_RINGTONE",
   "PHONE_STATE_IN_CALL"
 ];
 
 const DEFAULT_EMERGENCY_NUMBERS = ["112", "911"];
 
@@ -151,16 +148,17 @@ function TelephonyService() {
   this._numClients = gRadioInterfaceLayer.numRadioInterfaces;
   this._listeners = [];
 
   this._mmiRegExp = null;
 
   this._isDialing = false;
   this._cachedDialRequest = null;
   this._currentCalls = {};
+  this._audioStates = {};
 
   this._cdmaCallWaitingNumber = null;
 
   // _isActiveCall[clientId][callIndex] shows the active status of the call.
   this._isActiveCall = {};
   this._numActiveCall = 0;
 
   this._updateDebugFlag();
@@ -169,16 +167,17 @@ function TelephonyService() {
   Services.prefs.addObserver(kPrefRilDebuggingEnabled, this, false);
   Services.prefs.addObserver(kPrefDefaultServiceId, this, false);
 
   Services.obs.addObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
 
   for (let i = 0; i < this._numClients; ++i) {
     this._enumerateCallsForClient(i);
     this._isActiveCall[i] = {};
+    this._audioStates[i] = RIL.AUDIO_STATE_NO_CALL;
   }
 }
 TelephonyService.prototype = {
   classID: GONK_TELEPHONYSERVICE_CID,
   classInfo: XPCOMUtils.generateCI({classID: GONK_TELEPHONYSERVICE_CID,
                                     contractID: GONK_TELEPHONYSERVICE_CONTRACTID,
                                     classDescription: "TelephonyService",
                                     interfaces: [Ci.nsITelephonyService,
@@ -272,49 +271,40 @@ TelephonyService.prototype = {
     // Update active count and info.
     let oldActive = this._isActiveCall[aCall.clientId][aCall.callIndex];
     if (!oldActive && active) {
       this._numActiveCall++;
     } else if (oldActive && !active) {
       this._numActiveCall--;
     }
     this._isActiveCall[aCall.clientId][aCall.callIndex] = active;
-
-    if (incoming && !this._numActiveCall) {
-      // Change the phone state into RINGTONE only when there's no active call.
-      this._updateCallAudioState(AUDIO_STATE_INCOMING);
-    } else if (this._numActiveCall) {
-      this._updateCallAudioState(AUDIO_STATE_IN_CALL);
-    } else {
-      this._updateCallAudioState(AUDIO_STATE_NO_CALL);
-    }
   },
 
-  _updateCallAudioState: function(aAudioState) {
+  _updateAudioState: function(aAudioState) {
     switch (aAudioState) {
-      case AUDIO_STATE_NO_CALL:
+      case RIL.AUDIO_STATE_NO_CALL:
         gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_NORMAL;
         break;
 
-      case AUDIO_STATE_INCOMING:
+      case RIL.AUDIO_STATE_INCOMING:
         gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_RINGTONE;
         break;
 
-      case AUDIO_STATE_IN_CALL:
+      case RIL.AUDIO_STATE_IN_CALL:
         gAudioManager.phoneState = nsIAudioManager.PHONE_STATE_IN_CALL;
         if (this.speakerEnabled) {
           gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION,
                                        nsIAudioManager.FORCE_SPEAKER);
         }
         break;
     }
 
     if (DEBUG) {
       debug("Put audio system into " + AUDIO_STATE_NAME[aAudioState] + ": " +
-            gAudioManager.phoneState);
+            aAudioState + ", result is: " + gAudioManager.phoneState);
     }
   },
 
   _convertRILCallState: function(aState) {
     switch (aState) {
       case RIL.CALL_STATE_UNKNOWN:
         return nsITelephonyService.CALL_STATE_UNKNOWN;
       case RIL.CALL_STATE_ACTIVE:
@@ -1092,16 +1082,27 @@ TelephonyService.prototype = {
                            nsIAudioManager.FORCE_NONE;
     gAudioManager.setForceForUse(nsIAudioManager.USE_COMMUNICATION, force);
   },
 
   /**
    * nsIGonkTelephonyService interface.
    */
 
+  notifyAudioStateChanged: function(aClientId, aState) {
+    this._audioStates[aClientId] = aState;
+
+    let audioState = aState;
+    for (let i = 0; i < this._numClients; ++i) {
+      audioState = Math.max(audioState, this._audioStates[i]);
+    }
+
+    this._updateAudioState(audioState);
+  },
+
   /**
    * Handle call disconnects by updating our current state and the audio system.
    */
   notifyCallDisconnected: function(aClientId, aCall) {
     if (DEBUG) debug("handleCallDisconnected: " + JSON.stringify(aCall));
 
     aCall.clientId = aClientId;
     aCall.state = nsITelephonyService.CALL_STATE_DISCONNECTED;
--- a/dom/telephony/nsIGonkTelephonyService.idl
+++ b/dom/telephony/nsIGonkTelephonyService.idl
@@ -5,19 +5,21 @@
 
 #include "nsITelephonyService.idl"
 
 %{C++
 #define GONK_TELEPHONY_SERVICE_CONTRACTID \
         "@mozilla.org/telephony/gonktelephonyservice;1"
 %}
 
-[scriptable, uuid(79eec3c3-2dfc-4bbf-b106-af5457651ae0)]
+[scriptable, uuid(068d7bf2-1773-48ef-95f8-bd835115fed7)]
 interface nsIGonkTelephonyService : nsITelephonyService
 {
+  void notifyAudioStateChanged(in unsigned long clientId, in short state);
+
   void notifyCallDisconnected(in unsigned long clientId, in jsval call);
 
   void notifyCallRing();
 
   void notifyCallStateChanged(in unsigned long clientId, in jsval call,
                               [optional] in boolean skipStateConversion);
 
   void notifyCdmaCallWaiting(in unsigned long clientId, in jsval waitingCall);
--- a/dom/telephony/test/marionette/head.js
+++ b/dom/telephony/test/marionette/head.js
@@ -1,13 +1,13 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Emulate Promise.jsm semantics.
-Promise.defer = function() { return new Deferred(); }
+Promise.defer = function() { return new Deferred(); };
 function Deferred()  {
   this.promise = new Promise(function(resolve, reject) {
     this.resolve = resolve;
     this.reject = reject;
   }.bind(this));
   Object.freeze(this);
 }
 
@@ -615,16 +615,48 @@ let emulator = (function() {
       deferred.resolve(call);
     };
     call.hold();
 
     return deferred.promise;
   }
 
   /**
+   * Resume a call.
+   *
+   * @param call
+   *        A TelephonyCall object.
+   * @return A deferred promise.
+   */
+  function resume(call) {
+    log("Resuming the held call.");
+
+    let deferred = Promise.defer();
+
+    let gotResuming = false;
+    call.onresuming = function onresuming(event) {
+      log("Received 'resuming' call event");
+      call.onresuming = null;
+      checkEventCallState(event, call, "resuming");
+      gotResuming = true;
+    };
+
+    call.onconnected = function onconnected(event) {
+      log("Received 'connected' call event");
+      call.onconnected = null;
+      checkEventCallState(event, call, "connected");
+      ok(gotResuming);
+      deferred.resolve(call);
+    };
+    call.resume();
+
+    return deferred.promise;
+  }
+
+  /**
    * Locally hang up a call.
    *
    * @param call
    *        A TelephonyCall object.
    * @return A deferred promise.
    */
   function hangUp(call) {
     let deferred = Promise.defer();
@@ -1198,16 +1230,17 @@ let emulator = (function() {
   this.gInCallStrPool = inCallStrPool;
   this.gCheckState = checkState;
   this.gCheckAll = checkAll;
   this.gDial = dial;
   this.gDialEmergency = dialEmergency;
   this.gAnswer = answer;
   this.gHangUp = hangUp;
   this.gHold = hold;
+  this.gResume = resume;
   this.gRemoteDial = remoteDial;
   this.gRemoteAnswer = remoteAnswer;
   this.gRemoteHangUp = remoteHangUp;
   this.gRemoteHangUpCalls = remoteHangUpCalls;
   this.gAddCallsToConference = addCallsToConference;
   this.gHoldConference = holdConference;
   this.gResumeConference = resumeConference;
   this.gRemoveCallInConference = removeCallInConference;
--- a/dom/telephony/test/marionette/test_audiomanager_phonestate.js
+++ b/dom/telephony/test/marionette/test_audiomanager_phonestate.js
@@ -78,16 +78,20 @@ startTest(function() {
     .then(() => check(PHONE_STATE_NORMAL, PHONE_STATE_NORMAL))
 
     // Dial out
     .then(() => gDial(outNumber))
     .then(call => { outCall = call; })
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
     .then(() => gRemoteAnswer(outCall))
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
+    .then(() => gHold(outCall))
+    .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
+    .then(() => gResume(outCall))
+    .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
     // Dial out and dial in
     .then(() => gRemoteDial(inNumber))
     .then(call => { inCall = call; })
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
     .then(() => gAnswer(inCall))
     .then(() => check(PHONE_STATE_IN_CALL, PHONE_STATE_IN_CALL))
     // Conference
     .then(() => gAddCallsToConference([outCall, inCall]))
--- a/dom/webidl/ProfileTimelineMarker.webidl
+++ b/dom/webidl/ProfileTimelineMarker.webidl
@@ -1,11 +1,11 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 dictionary ProfileTimelineMarker {
   DOMString name = "";
-  DOMTimeStamp start = 0;
-  DOMTimeStamp end = 0;
+  DOMHighResTimeStamp start = 0;
+  DOMHighResTimeStamp end = 0;
 };
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -432,17 +432,18 @@ nsPermissionManager::Init()
       rv = GetPrincipal(perm.host, perm.appId, perm.isInBrowserElement, getter_AddRefs(principal));
       NS_ENSURE_SUCCESS(rv, rv);
 
       // The child process doesn't care about modification times - it neither
       // reads nor writes, nor removes them based on the date - so 0 (which
       // will end up as now()) is fine.
       uint64_t modificationTime = 0;
       AddInternal(principal, perm.type, perm.capability, 0, perm.expireType,
-                  perm.expireTime, modificationTime, eNotify, eNoDBOperation);
+                  perm.expireTime, modificationTime, eNotify, eNoDBOperation,
+                  true /* ignoreSessionPermissions */);
     }
 
     // Stop here; we don't need the DB in the child process
     return NS_OK;
   }
 
   // ignore failure here, since it's non-fatal (we can run fine without
   // persistent storage - e.g. if there's no profile).
@@ -712,17 +713,18 @@ nsresult
 nsPermissionManager::AddInternal(nsIPrincipal* aPrincipal,
                                  const nsAFlatCString &aType,
                                  uint32_t              aPermission,
                                  int64_t               aID,
                                  uint32_t              aExpireType,
                                  int64_t               aExpireTime,
                                  int64_t               aModificationTime,
                                  NotifyOperationType   aNotifyOperation,
-                                 DBOperationType       aDBOperation)
+                                 DBOperationType       aDBOperation,
+                                 const bool            aIgnoreSessionPermissions)
 {
   nsAutoCString host;
   nsresult rv = GetHostForPrincipal(aPrincipal, host);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (!IsChildProcess()) {
     uint32_t appId;
     rv = aPrincipal->GetAppId(&appId);
@@ -734,16 +736,22 @@ nsPermissionManager::AddInternal(nsIPrin
 
     IPC::Permission permission(host, appId, isInBrowserElement, aType,
                                aPermission, aExpireType, aExpireTime);
 
     nsTArray<ContentParent*> cplist;
     ContentParent::GetAll(cplist);
     for (uint32_t i = 0; i < cplist.Length(); ++i) {
       ContentParent* cp = cplist[i];
+      // On platforms where we use a preallocated template process we don't
+      // want to notify this process about session specific permissions so
+      // new tabs or apps created on it won't inherit the session permissions.
+      if (cp->IsPreallocated() &&
+          aExpireType == nsIPermissionManager::EXPIRE_SESSION)
+        continue;
       if (cp->NeedsPermissionsUpdate())
         unused << cp->SendAddPermission(permission);
     }
   }
 
   // look up the type index
   int32_t typeIndex = GetTypeIndex(aType.get(), true);
   NS_ENSURE_TRUE(typeIndex != -1, NS_ERROR_OUT_OF_MEMORY);
@@ -819,16 +827,26 @@ nsPermissionManager::AddInternal(nsIPrin
       if (aDBOperation == eWriteToDB) {
         // we'll be writing to the database - generate a known unique id
         id = ++mLargestID;
       } else {
         // we're reading from the database - use the id already assigned
         id = aID;
       }
 
+      // When we do the initial addition of the permissions we don't want to
+      // inherit session specific permissions from other tabs or apps
+      // so we ignore them and set the permission to PROMPT_ACTION if it was
+      // previously allowed or denied by the user.
+      if (aIgnoreSessionPermissions &&
+          aExpireType == nsIPermissionManager::EXPIRE_SESSION) {
+        aPermission = nsIPermissionManager::PROMPT_ACTION;
+        aExpireType = nsIPermissionManager::EXPIRE_NEVER;
+      }
+
       entry->GetPermissions().AppendElement(PermissionEntry(id, typeIndex, aPermission,
                                                             aExpireType, aExpireTime,
                                                             aModificationTime));
 
       if (aDBOperation == eWriteToDB && aExpireType != nsIPermissionManager::EXPIRE_SESSION) {
         uint32_t appId;
         rv = aPrincipal->GetAppId(&appId);
         NS_ENSURE_SUCCESS(rv, rv);
--- a/extensions/cookie/nsPermissionManager.h
+++ b/extensions/cookie/nsPermissionManager.h
@@ -150,17 +150,17 @@ public:
     }
 
     inline PermissionEntry GetPermission(uint32_t aType) const
     {
       for (uint32_t i = 0; i < mPermissions.Length(); ++i)
         if (mPermissions[i].mType == aType)
           return mPermissions[i];
 
-      // unknown permission... return relevant data 
+      // unknown permission... return relevant data
       return PermissionEntry(-1, aType, nsIPermissionManager::UNKNOWN_ACTION,
                              nsIPermissionManager::EXPIRE_NEVER, 0, 0);
     }
 
   private:
     nsAutoTArray<PermissionEntry, 1> mPermissions;
   };
 
@@ -200,17 +200,18 @@ public:
   nsresult AddInternal(nsIPrincipal* aPrincipal,
                        const nsAFlatCString &aType,
                        uint32_t aPermission,
                        int64_t aID,
                        uint32_t aExpireType,
                        int64_t  aExpireTime,
                        int64_t aModificationTime,
                        NotifyOperationType aNotifyOperation,
-                       DBOperationType aDBOperation);
+                       DBOperationType aDBOperation,
+                       const bool aIgnoreSessionPermissions = false);
 
   /**
    * Initialize the "webapp-uninstall" observing.
    * Will create a nsPermissionManager instance if needed.
    * That way, we can prevent have nsPermissionManager created at startup just
    * to be able to clear data when an application is uninstalled.
    */
   static void AppClearDataObserverInit();
--- a/extensions/cookie/test/unit_ipc/test_child.js
+++ b/extensions/cookie/test/unit_ipc/test_child.js
@@ -26,33 +26,34 @@ function run_test() {
                          getService(Ci.nsISyncMessageSender);
 
     var messageListener = {
       receiveMessage: function(aMessage) {
         switch(aMessage.name) {
           case "TESTING:Stage2A":
             // Permissions created after the child is present
             do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.org"), "cookie1"), pm.ALLOW_ACTION);
-            do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.DENY_ACTION);
+            do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.PROMPT_ACTION);
             do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.net"), "cookie3"), pm.ALLOW_ACTION);
             do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://firefox.org"), "cookie1"), pm.ALLOW_ACTION);
             do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://firefox.com"), "cookie2"), pm.DENY_ACTION);
             do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://firefox.net"), "cookie3"), pm.ALLOW_ACTION);
 
             mM.sendAsyncMessage("TESTING:Stage3");
             break;
 
         }
         return true;
       },
     };
 
     mM.addMessageListener("TESTING:Stage2A", messageListener);
 
+    // Permissions created before the child is present
     var pm = Cc["@mozilla.org/permissionmanager;1"].getService(Ci.nsIPermissionManager);
     do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.org"), "cookie1"), pm.ALLOW_ACTION);
-    do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.DENY_ACTION);
+    do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.com"), "cookie2"), pm.PROMPT_ACTION);
     do_check_eq(pm.testPermissionFromPrincipal(getPrincipalForURI("http://mozilla.net"), "cookie3"), pm.ALLOW_ACTION);
 
     mM.sendAsyncMessage("TESTING:Stage2");
   }
 }
 
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -1253,17 +1253,16 @@ static const mozilla::Module::CategoryEn
 #endif
   CONTENTDLF_CATEGORIES
 #ifdef MOZ_WIDGET_GONK
   { "profile-after-change", "Gonk System Worker Manager", SYSTEMWORKERMANAGER_CONTRACTID },
 #endif
 #ifdef MOZ_B2G_BT
   { "profile-after-change", "Bluetooth Service", BLUETOOTHSERVICE_CONTRACTID },
 #endif
-  { "profile-after-change", "MobileConnection Service", NS_MOBILE_CONNECTION_SERVICE_CONTRACTID },
   { nullptr }
 };
 
 static void
 LayoutModuleDtor()
 {
   Shutdown();
   nsContentUtils::XPCOMShutdown();
--- a/mobile/android/base/BrowserApp.java
+++ b/mobile/android/base/BrowserApp.java
@@ -16,20 +16,20 @@ import java.util.Locale;
 import java.util.Vector;
 
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.DynamicToolbar.PinReason;
 import org.mozilla.gecko.DynamicToolbar.VisibilityTransition;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
+import org.mozilla.gecko.ReadingListHelper;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 import org.mozilla.gecko.db.BrowserContract.Combined;
-import org.mozilla.gecko.db.BrowserContract.ReadingListItems;
 import org.mozilla.gecko.db.BrowserContract.SearchHistory;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.db.SuggestedSites;
 import org.mozilla.gecko.distribution.Distribution;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.favicons.LoadFaviconTask;
 import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
 import org.mozilla.gecko.favicons.decoders.IconDirectoryEntry;
@@ -141,20 +141,16 @@ public class BrowserApp extends GeckoApp
                                    OnUrlOpenListener,
                                    OnUrlOpenInBackgroundListener,
                                    ActionModeCompat.Presenter,
                                    LayoutInflater.Factory {
     private static final String LOGTAG = "GeckoBrowserApp";
 
     private static final int TABS_ANIMATION_DURATION = 450;
 
-    private static final int READER_ADD_SUCCESS = 0;
-    private static final int READER_ADD_FAILED = 1;
-    private static final int READER_ADD_DUPLICATE = 2;
-
     private static final String ADD_SHORTCUT_TOAST = "add_shortcut_toast";
     public static final String GUEST_BROWSING_ARG = "--guest";
 
     private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding";
 
     private static final String BROWSER_SEARCH_TAG = "browser_search";
 
     // Request ID for startActivityForResult.
@@ -224,16 +220,18 @@ public class BrowserApp extends GeckoApp
     private SharedPreferencesHelper mSharedPreferencesHelper;
 
     private OrderedBroadcastHelper mOrderedBroadcastHelper;
 
     private BroadcastReceiver mOnboardingReceiver;
 
     private BrowserHealthReporter mBrowserHealthReporter;
 
+    private ReadingListHelper mReadingListHelper;
+
     private SystemBarTintManager mTintManager;
 
     // The tab to be selected on editing mode exit.
     private Integer mTargetTabForEditingMode;
 
     // The animator used to toggle HomePager visibility has a race where if the HomePager is shown
     // (starting the animation), the HomePager is hidden, and the HomePager animation completes,
     // both the web content and the HomePager will be hidden. This flag is used to prevent the
@@ -428,100 +426,16 @@ public class BrowserApp extends GeckoApp
     @Override
     public boolean onKeyUp(int keyCode, KeyEvent event) {
         if (AndroidGamepadManager.handleKeyEvent(event)) {
             return true;
         }
         return super.onKeyUp(keyCode, event);
     }
 
-    void handleReaderListStatusRequest(final String url) {
-        ThreadUtils.postToBackgroundThread(new Runnable() {
-            @Override
-            public void run() {
-                final int inReadingList = BrowserDB.isReadingListItem(getContentResolver(), url) ? 1 : 0;
-
-                final JSONObject json = new JSONObject();
-                try {
-                    json.put("url", url);
-                    json.put("inReadingList", inReadingList);
-                } catch (JSONException e) {
-                    Log.e(LOGTAG, "JSON error - failed to return inReadingList status", e);
-                    return;
-                }
-
-                GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:ListStatusReturn", json.toString()));
-            }
-        });
-    }
-
-    private void handleReaderAdded(int result, final ContentValues values) {
-        if (result != READER_ADD_SUCCESS) {
-            if (result == READER_ADD_FAILED) {
-                showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT);
-            } else if (result == READER_ADD_DUPLICATE) {
-                showToast(R.string.reading_list_duplicate, Toast.LENGTH_SHORT);
-            }
-
-            return;
-        }
-
-        ThreadUtils.postToBackgroundThread(new Runnable() {
-            @Override
-            public void run() {
-                BrowserDB.addReadingListItem(getContentResolver(), values);
-                showToast(R.string.reading_list_added, Toast.LENGTH_SHORT);
-            }
-        });
-    }
-
-    private ContentValues messageToReadingListContentValues(JSONObject message) {
-        final ContentValues values = new ContentValues();
-        values.put(ReadingListItems.URL, message.optString("url"));
-        values.put(ReadingListItems.TITLE, message.optString("title"));
-        values.put(ReadingListItems.LENGTH, message.optInt("length"));
-        values.put(ReadingListItems.EXCERPT, message.optString("excerpt"));
-        return values;
-    }
-
-    void handleReaderRemoved(final String url) {
-        ThreadUtils.postToBackgroundThread(new Runnable() {
-            @Override
-            public void run() {
-                BrowserDB.removeReadingListItemWithURL(getContentResolver(), url);
-                showToast(R.string.page_removed, Toast.LENGTH_SHORT);
-            }
-        });
-    }
-
-    private void handleReaderFaviconRequest(final String url) {
-        (new UIAsyncTask.WithoutParams<String>(ThreadUtils.getBackgroundHandler()) {
-            @Override
-            public String doInBackground() {
-                return Favicons.getFaviconURLForPageURL(getContext(), url);
-            }
-
-            @Override
-            public void onPostExecute(String faviconUrl) {
-                JSONObject args = new JSONObject();
-
-                if (faviconUrl != null) {
-                    try {
-                        args.put("url", url);
-                        args.put("faviconUrl", faviconUrl);
-                    } catch (JSONException e) {
-                        Log.w(LOGTAG, "Error building JSON favicon arguments.", e);
-                    }
-                }
-
-                GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Reader:FaviconReturn", args.toString()));
-            }
-        }).execute();
-    }
-
     @Override
     public void onCreate(Bundle savedInstanceState) {
         mAboutHomeStartupTimer = new Telemetry.UptimeTimer("FENNEC_STARTUP_TIME_ABOUTHOME");
 
         final Intent intent = getIntent();
         final GeckoProfile p = GeckoProfile.get(this);
         if (p != null && !p.inGuestMode()) {
             // This is *only* valid because we never want to use the guest mode
@@ -595,49 +509,46 @@ public class BrowserApp extends GeckoApp
 
         setBrowserToolbarListeners();
 
         mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page);
         mMediaCastingBar = (MediaCastingBar) findViewById(R.id.media_casting);
 
         EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this,
             "Menu:Update",
-            "Reader:Added",
-            "Reader:FaviconRequest",
             "Search:Keyword",
             "Prompt:ShowTop",
             "Accounts:Exist");
 
         EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this,
             "Accounts:Create",
             "CharEncoding:Data",
             "CharEncoding:State",
             "Feedback:LastUrl",
             "Feedback:MaybeLater",
             "Feedback:OpenPlayStore",
             "Menu:Add",
             "Menu:Remove",
-            "Reader:ListStatusRequest",
-            "Reader:Removed",
             "Reader:Share",
             "Settings:Show",
             "Telemetry:Gather",
             "Updater:Launch",
             "BrowserToolbar:Visibility");
 
         Distribution distribution = Distribution.init(this);
 
         // Init suggested sites engine in BrowserDB.
         final SuggestedSites suggestedSites = new SuggestedSites(appContext, distribution);
         BrowserDB.setSuggestedSites(suggestedSites);
 
         JavaAddonManager.getInstance().init(appContext);
         mSharedPreferencesHelper = new SharedPreferencesHelper(appContext);
         mOrderedBroadcastHelper = new OrderedBroadcastHelper(appContext);
         mBrowserHealthReporter = new BrowserHealthReporter();
+        mReadingListHelper = new ReadingListHelper(appContext);
 
         if (AppConstants.MOZ_ANDROID_BEAM) {
             NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
             if (nfc != null) {
                 nfc.setNdefPushMessageCallback(new NfcAdapter.CreateNdefMessageCallback() {
                     @Override
                     public NdefMessage createNdefMessage(NfcEvent event) {
                         Tab tab = Tabs.getInstance().getSelectedTab();
@@ -1137,35 +1048,36 @@ public class BrowserApp extends GeckoApp
             mOrderedBroadcastHelper = null;
         }
 
         if (mBrowserHealthReporter != null) {
             mBrowserHealthReporter.uninit();
             mBrowserHealthReporter = null;
         }
 
+        if (mReadingListHelper != null) {
+            mReadingListHelper.uninit();
+            mReadingListHelper = null;
+        }
+
         EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
             "Menu:Update",
-            "Reader:Added",
-            "Reader:FaviconRequest",
             "Search:Keyword",
             "Prompt:ShowTop",
             "Accounts:Exist");
 
         EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener)this,
             "Accounts:Create",
             "CharEncoding:Data",
             "CharEncoding:State",
             "Feedback:LastUrl",
             "Feedback:MaybeLater",
             "Feedback:OpenPlayStore",
             "Menu:Add",
             "Menu:Remove",
-            "Reader:ListStatusRequest",
-            "Reader:Removed",
             "Reader:Share",
             "Settings:Show",
             "Telemetry:Gather",
             "Updater:Launch",
             "BrowserToolbar:Visibility");
 
         if (AppConstants.MOZ_ANDROID_BEAM) {
             NfcAdapter nfc = NfcAdapter.getDefaultAdapter(this);
@@ -1514,23 +1426,16 @@ public class BrowserApp extends GeckoApp
             final int id = message.getInt("id") + ADDON_MENU_OFFSET;
             ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
                     removeAddonMenuItem(id);
                 }
             });
 
-        } else if ("Reader:ListStatusRequest".equals(event)) {
-            handleReaderListStatusRequest(message.getString("url"));
-
-        } else if ("Reader:Removed".equals(event)) {
-            final String url = message.getString("url");
-            handleReaderRemoved(url);
-
         } else if ("Reader:Share".equals(event)) {
             final String title = message.getString("title");
             final String url = message.getString("url");
             GeckoAppShell.openUriExternal(url, "text/plain", "", "", Intent.ACTION_SEND, title);
 
         } else if ("Settings:Show".equals(event)) {
             final String resource =
                     message.optString(GeckoPreferences.INTENT_EXTRA_RESOURCES, null);
@@ -1635,22 +1540,16 @@ public class BrowserApp extends GeckoApp
                     }
                 });
 
                 // Display notification for Mozilla data reporting, if data should be collected.
                 if (AppConstants.MOZ_DATA_REPORTING) {
                     DataReportingNotification.checkAndNotifyPolicy(GeckoAppShell.getContext());
                 }
 
-            } else if (event.equals("Reader:Added")) {
-                final int result = message.getInt("result");
-                handleReaderAdded(result, messageToReadingListContentValues(message));
-            } else if (event.equals("Reader:FaviconRequest")) {
-                final String url = message.getString("url");
-                handleReaderFaviconRequest(url);
             } else if (event.equals("Search:Keyword")) {
                 storeSearchQuery(message.getString("query"));
             } else if (event.equals("Prompt:ShowTop")) {
                 // Bring this activity to front so the prompt is visible..
                 Intent bringToFrontIntent = new Intent();
                 bringToFrontIntent.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.BROWSER_INTENT_CLASS_NAME);
                 bringToFrontIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
                 startActivity(bringToFrontIntent);
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -1830,17 +1830,17 @@ public class GeckoAppShell
         if (AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
             final File f = new File(aFile);
             final DownloadManager dm = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
             dm.addCompletedDownload(f.getName(),
                                     f.getName(),
                                     true, // Media scanner should scan this
                                     mimeType,
                                     f.getAbsolutePath(),
-                                    Math.max(0, f.length()),
+                                    Math.max(1, f.length()), // Some versions of Android require downloads to be at least length 1
                                     false); // Don't show a notification.
         } else {
             Context context = getContext();
             GeckoMediaScannerClient.startScan(context, aFile, mimeType);
         }
     }
 
     @WrapElementForJNI(stubName = "GetIconForExtensionWrapper")
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/ReadingListHelper.java
@@ -0,0 +1,195 @@
+/* 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 org.mozilla.gecko.db.BrowserContract.ReadingListItems;
+import org.mozilla.gecko.db.BrowserDB;
+import org.mozilla.gecko.favicons.Favicons;
+import org.mozilla.gecko.util.EventCallback;
+import org.mozilla.gecko.util.GeckoEventListener;
+import org.mozilla.gecko.util.NativeEventListener;
+import org.mozilla.gecko.util.NativeJSObject;
+import org.mozilla.gecko.util.ThreadUtils;
+import org.mozilla.gecko.util.UIAsyncTask;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+import android.widget.Toast;
+
+public final class ReadingListHelper implements GeckoEventListener, NativeEventListener {
+    private static final String LOGTAG = "ReadingListHelper";
+
+    private static final int READER_ADD_SUCCESS = 0;
+    private static final int READER_ADD_FAILED = 1;
+    private static final int READER_ADD_DUPLICATE = 2;
+
+    protected final Context context;
+
+
+    public ReadingListHelper(Context context) {
+        this.context = context;
+
+        EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this,
+            "Reader:Added", "Reader:FaviconRequest");
+        EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this,
+            "Reader:ListStatusRequest", "Reader:Removed");
+    }
+
+    public void uninit() {
+        EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) this,
+            "Reader:Added", "Reader:FaviconRequest");
+        EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this,
+            "Reader:ListStatusRequest", "Reader:Removed");
+    }
+
+    @Override
+    public void handleMessage(String event, JSONObject message) {
+        switch(event) {
+            case "Reader:Added": {
+                handleReadingListAdded(message);
+                break;
+            }
+
+            case "Reader:FaviconRequest": {
+                handleReaderModeFaviconRequest(message.optString("url"));
+                break;
+            }
+        }
+    }
+
+    @Override
+    public void handleMessage(final String event, final NativeJSObject message,
+                              final EventCallback callback) {
+        switch(event) {
+            case "Reader:Removed": {
+                handleReadingListRemoved(message.getString("url"));
+                break;
+            }
+
+            case "Reader:ListStatusRequest": {
+                handleReadingListStatusRequest(message.getString("url"));
+                break;
+            }
+        }
+    }
+
+    /**
+     * A page can be added to the ReadingList by long-tap of the page-action
+     * icon, or by tapping the readinglist-add icon in the ReaderMode banner.
+     */
+    private void handleReadingListAdded(JSONObject message) {
+        final int result = message.optInt("result", READER_ADD_FAILED);
+        if (result != READER_ADD_SUCCESS) {
+            if (result == READER_ADD_FAILED) {
+                showToast(R.string.reading_list_failed, Toast.LENGTH_SHORT);
+            } else if (result == READER_ADD_DUPLICATE) {
+                showToast(R.string.reading_list_duplicate, Toast.LENGTH_SHORT);
+            }
+            return;
+        }
+
+        final ContentValues values = new ContentValues();
+        values.put(ReadingListItems.URL, message.optString("url"));
+        values.put(ReadingListItems.TITLE, message.optString("title"));
+        values.put(ReadingListItems.LENGTH, message.optInt("length"));
+        values.put(ReadingListItems.EXCERPT, message.optString("excerpt"));
+
+        ThreadUtils.postToBackgroundThread(new Runnable() {
+            @Override
+            public void run() {
+                BrowserDB.addReadingListItem(context.getContentResolver(), values);
+                showToast(R.string.reading_list_added, Toast.LENGTH_SHORT);
+            }
+        });
+    }
+
+    /**
+     * Gecko (ReaderMode) requests the page favicon to append to the
+     * document head for display.
+     */
+    private void handleReaderModeFaviconRequest(final String url) {
+        (new UIAsyncTask.WithoutParams<String>(ThreadUtils.getBackgroundHandler()) {
+            @Override
+            public String doInBackground() {
+                return Favicons.getFaviconURLForPageURL(context, url);
+            }
+
+            @Override
+            public void onPostExecute(String faviconUrl) {
+                JSONObject args = new JSONObject();
+
+                if (faviconUrl != null) {
+                    try {
+                        args.put("url", url);
+                        args.put("faviconUrl", faviconUrl);
+                    } catch (JSONException e) {
+                        Log.w(LOGTAG, "Error building JSON favicon arguments.", e);
+                    }
+                }
+
+                GeckoAppShell.sendEventToGecko(
+                    GeckoEvent.createBroadcastEvent("Reader:FaviconReturn", args.toString()));
+            }
+        }).execute();
+    }
+
+    /**
+     * 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 handleReadingListRemoved(final String url) {
+        ThreadUtils.postToBackgroundThread(new Runnable() {
+            @Override
+            public void run() {
+                BrowserDB.removeReadingListItemWithURL(context.getContentResolver(), 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).
+     */
+    private void handleReadingListStatusRequest(final String url) {
+        ThreadUtils.postToBackgroundThread(new Runnable() {
+            @Override
+            public void run() {
+                final int inReadingList =
+                    BrowserDB.isReadingListItem(context.getContentResolver(), url) ? 1 : 0;
+
+                final JSONObject json = new JSONObject();
+                try {
+                    json.put("url", url);
+                    json.put("inReadingList", inReadingList);
+                } catch (JSONException e) {
+                    Log.e(LOGTAG, "JSON error - failed to return inReadingList status", e);
+                    return;
+                }
+
+                GeckoAppShell.sendEventToGecko(
+                    GeckoEvent.createBroadcastEvent("Reader:ListStatusReturn", json.toString()));
+            }
+        });
+    }
+
+    /**
+     * Show various status toasts.
+     */
+    private void showToast(final int resId, final int duration) {
+        ThreadUtils.postToUiThread(new Runnable() {
+            @Override
+            public void run() {
+                Toast.makeText(context, resId, duration).show();
+            }
+        });
+    }
+}
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -77,17 +77,17 @@ public class LocalBrowserDB {
     }
 
     private final String mProfile;
 
     // Map of folder GUIDs to IDs. Used for caching.
     private final HashMap<String, Long> mFolderIdMap;
 
     // Use wrapped Boolean so that we can have a null state
-    private Boolean mDesktopBookmarksExist;
+    private volatile Boolean mDesktopBookmarksExist;
 
     private final Uri mBookmarksUriWithProfile;
     private final Uri mParentsUriWithProfile;
     private final Uri mHistoryUriWithProfile;
     private final Uri mHistoryExpireUriWithProfile;
     private final Uri mCombinedUriWithProfile;
     private final Uri mUpdateHistoryUriWithProfile;
     private final Uri mFaviconsUriWithProfile;
@@ -754,22 +754,23 @@ public class LocalBrowserDB {
                                   Bookmarks.PARENT + " = ? OR " +
                                   Bookmarks.PARENT + " = ?",
                                   new String[] { String.valueOf(getFolderIdFromGuid(cr, Bookmarks.TOOLBAR_FOLDER_GUID)),
                                                  String.valueOf(getFolderIdFromGuid(cr, Bookmarks.MENU_FOLDER_GUID)),
                                                  String.valueOf(getFolderIdFromGuid(cr, Bookmarks.UNFILED_FOLDER_GUID)) },
                                   null);
 
         try {
-            mDesktopBookmarksExist = c.getCount() > 0;
+            // Don't read back out of the cache to avoid races with invalidation.
+            final boolean e = c.getCount() > 0;
+            mDesktopBookmarksExist = e;
+            return e;
         } finally {
             c.close();
         }
-
-        return mDesktopBookmarksExist;
     }
 
     @RobocopTarget
     public boolean isBookmark(ContentResolver cr, String uri) {
         final Cursor c = cr.query(bookmarksUriWithLimit(1),
                                   new String[] { Bookmarks._ID },
                                   Bookmarks.URL + " = ? AND " + Bookmarks.PARENT + " != ?",
                                   new String[] { uri, String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID) },
--- a/mobile/android/base/menu/MenuItemActionBar.java
+++ b/mobile/android/base/menu/MenuItemActionBar.java
@@ -1,23 +1,24 @@
 /* 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.menu;
 
 import org.mozilla.gecko.NewTabletUI;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.widget.ThemedImageButton;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.ImageButton;
 
-public class MenuItemActionBar extends ImageButton
+public class MenuItemActionBar extends ThemedImageButton
                                implements GeckoMenuItem.Layout {
     private static final String LOGTAG = "GeckoMenuItemActionBar";
 
     public MenuItemActionBar(Context context) {
         this(context, null);
     }
 
     public MenuItemActionBar(Context context, AttributeSet attrs) {
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -363,16 +363,17 @@ gbjar.sources += [
     'prompts/IntentHandler.java',
     'prompts/Prompt.java',
     'prompts/PromptInput.java',
     'prompts/PromptListAdapter.java',
     'prompts/PromptListItem.java',
     'prompts/PromptService.java',
     'prompts/TabInput.java',
     'ReaderModeUtils.java',
+    'ReadingListHelper.java',
     'RemoteClientsDialogFragment.java',
     'RemoteTabsExpandableListAdapter.java',
     'Restarter.java',
     'RestrictedProfiles.java',
     'ScrollAnimator.java',
     'ServiceNotificationClient.java',
     'SessionParser.java',
     'SharedPreferencesHelper.java',
--- a/mobile/android/base/newtablet/res/drawable-large-v11/new_tablet_action_bar_button.xml
+++ b/mobile/android/base/newtablet/res/drawable-large-v11/new_tablet_action_bar_button.xml
@@ -1,14 +1,47 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <!-- 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/. -->
 
-<selector xmlns:android="http://schemas.android.com/apk/res/android">
+<selector xmlns:android="http://schemas.android.com/apk/res/android"
+          xmlns:gecko="http://schemas.android.com/apk/res-auto">
+
+   <item gecko:state_private="true"
+         android:state_pressed="true"
+         android:state_enabled="true">
+
+        <inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
+               android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
+            <shape android:shape="rectangle">
+                <solid android:color="@color/new_tablet_highlight_pb"/>
+                <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
+            </shape>
+        </inset>
+
+    </item>
+
+    <item gecko:state_private="true"
+          android:state_focused="true"
+          android:state_pressed="false">
+
+        <inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
+               android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
+               android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
+            <shape android:shape="rectangle">
+                <solid android:color="@color/new_tablet_highlight_focused_pb"/>
+                <corners android:radius="@dimen/new_tablet_browser_toolbar_menu_item_corner_radius"/>
+            </shape>
+        </inset>
+
+    </item>
 
     <item android:state_pressed="true"
           android:state_enabled="true">
 
         <inset android:insetTop="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
                android:insetBottom="@dimen/new_tablet_browser_toolbar_menu_item_inset_vertical"
                android:insetLeft="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal"
                android:insetRight="@dimen/new_tablet_browser_toolbar_menu_item_inset_horizontal">
rename from mobile/android/base/resources/drawable-large-v11/new_tablet_site_security_level.xml
rename to mobile/android/base/newtablet/res/drawable-large-v11/new_tablet_site_security_level.xml
rename from mobile/android/base/resources/drawable-large-v11/new_tablet_site_security_unknown.xml
rename to mobile/android/base/newtablet/res/drawable-large-v11/new_tablet_site_security_unknown.xml
--- a/mobile/android/base/newtablet/res/layout-large-v11/new_tablet_browser_toolbar.xml
+++ b/mobile/android/base/newtablet/res/layout-large-v11/new_tablet_browser_toolbar.xml
@@ -6,50 +6,49 @@
 <merge xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:gecko="http://schemas.android.com/apk/res-auto">
 
     <ImageView android:id="@+id/url_bar_entry"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_alignLeft="@+id/back"
                android:layout_toLeftOf="@id/menu_items"
-               android:layout_marginLeft="@dimen/back_button_width_half"
+               android:layout_marginLeft="@dimen/new_tablet_nav_button_width_half"
                android:layout_marginTop="10dp"
                android:layout_marginBottom="10dp"
                android:duplicateParentState="true"
                android:clickable="false"
                android:focusable="false"
                android:background="@drawable/url_bar_entry"/>
 
     <org.mozilla.gecko.toolbar.ForwardButton style="@style/UrlBar.ImageButton.Forward.NewTablet"
                                              android:id="@+id/forward"
                                              android:layout_alignLeft="@id/back"/>
 
     <org.mozilla.gecko.toolbar.BackButton android:id="@id/back"
                                           style="@style/UrlBar.ImageButton.NewTablet"
-                                          android:layout_width="@dimen/back_button_width"
-                                          android:layout_height="@dimen/back_button_width"
+                                          android:layout_width="@dimen/new_tablet_nav_button_width"
+                                          android:layout_height="@dimen/new_tablet_nav_button_width"
                                           android:layout_centerVertical="true"
                                           android:layout_marginLeft="12dp"
                                           android:layout_alignParentLeft="true"
                                           android:src="@drawable/new_tablet_ic_menu_back"
                                           android:contentDescription="@string/back"
                                           android:background="@drawable/new_tablet_url_bar_nav_button"/>
 
     <org.mozilla.gecko.toolbar.ToolbarEditLayout android:id="@+id/edit_layout"
                   style="@style/UrlBar.Button"
                   android:paddingLeft="12dp"
                   android:paddingRight="12dp"
                   android:visibility="gone"
                   android:orientation="horizontal"
                   android:layout_toRightOf="@id/back"
                   android:layout_toLeftOf="@id/menu_items"/>
 
-    <!-- Note: * Values of marginLeft are used to animate the forward button so don't change its value.
-               * We set the padding on the site security icon to increase its tappable area. -->
+    <!-- Note: we set the padding on the site security icon to increase its tappable area. -->
     <org.mozilla.gecko.toolbar.ToolbarDisplayLayout android:id="@+id/display_layout"
                   style="@style/UrlBar.Button.Container"
                   android:layout_toRightOf="@id/back"
                   android:layout_toLeftOf="@id/menu_items"
                   android:paddingRight="4dip"/>
 
     <LinearLayout android:id="@+id/menu_items"
                   android:layout_width="wrap_content"
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/drawable/new_tablet_site_security_level.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+
+<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
+        android:src="@null"/>
--- a/mobile/android/base/resources/values-large-v11/dimens.xml
+++ b/mobile/android/base/resources/values-large-v11/dimens.xml
@@ -3,16 +3,12 @@
    - 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/. -->
 
 <resources>
 
     <dimen name="browser_toolbar_height">56dp</dimen>
     <dimen name="browser_toolbar_button_padding">16dp</dimen>
 
-    <!-- If you update one of these values, update the other. -->
-    <dimen name="back_button_width">42dp</dimen>
-    <dimen name="back_button_width_half">21dp</dimen>
-
     <dimen name="tabs_counter_size">26sp</dimen>
     <dimen name="panel_grid_view_column_width">200dp</dimen>
 
 </resources>
--- a/mobile/android/base/resources/values-large-v11/styles.xml
+++ b/mobile/android/base/resources/values-large-v11/styles.xml
@@ -20,25 +20,40 @@
         <item name="android:layout_centerVertical">true</item>
         <item name="android:src">@drawable/ic_menu_forward</item>
         <item name="android:background">@drawable/url_bar_nav_button</item>
         <!-- Start with the button hidden -->
         <item name="android:alpha">0</item>
         <item name="android:layout_marginLeft">@dimen/forward_default_offset</item>
     </style>
 
+    <!-- Note: this style is for the visible and expanded forward button.  We translate/hide
+         the forward button in code - see BrowserToolbarNewTablet.animateForwardButton. -->
     <style name="UrlBar.ImageButton.Forward.NewTablet">
-        <item name="android:layout_marginLeft">@dimen/new_tablet_forward_default_offset</item>
         <item name="android:layout_height">match_parent</item>
         <item name="android:paddingTop">0dp</item>
         <item name="android:paddingBottom">0dp</item>
         <item name="android:layout_marginTop">11.5dp</item>
         <item name="android:layout_marginBottom">11.5dp</item>
         <item name="android:src">@drawable/new_tablet_ic_menu_forward</item>
         <item name="android:background">@drawable/new_tablet_url_bar_nav_button</item>
+
+        <!-- The visible area of the forward button is a nav_button_width and the
+             non-visible area slides halfway under the back button. This non-visible
+             area is used to ensure the forward button background fully
+             covers the space to the right of the back button. -->
+        <item name="android:layout_width">@dimen/new_tablet_nav_button_width_plus_half</item>
+
+        <!-- (See note above) We left align with back,
+             but only need to hide halfway underneath. -->
+        <item name="android:layout_marginLeft">@dimen/new_tablet_nav_button_width_half</item>
+
+        <!-- We use left padding to center the arrow in the
+             visible area as opposed to the true width. -->
+        <item name="android:paddingLeft">18dp</item>
     </style>
 
     <style name="UrlBar.ImageButton.TabCount.NewTablet">
         <item name="android:background">@drawable/new_tablet_tabs_count</item>
 
         <!-- From UrlBar.ImageButton.NewTablet because we can't inherit directly. -->
         <item name="android:layout_width">@dimen/new_tablet_browser_toolbar_menu_item_width</item>
     </style>
--- a/mobile/android/base/resources/values/dimens.xml
+++ b/mobile/android/base/resources/values/dimens.xml
@@ -12,21 +12,25 @@
     <dimen name="browser_toolbar_button_padding">12dp</dimen>
     <dimen name="browser_toolbar_icon_width">48dp</dimen>
     <dimen name="browser_toolbar_site_security_width">12dp</dimen>
     <!-- favicon_size includes 4dp of right padding. We can't use margin (which would allow us to
          specify the actual size) because that would decrease the size of our hit target. -->
     <dimen name="browser_toolbar_favicon_size">21.33dip</dimen>
     <dimen name="browser_toolbar_shadow_size">2dp</dimen>
 
+    <!-- If you update one of these values, update the others. -->
+    <dimen name="new_tablet_nav_button_width">42dp</dimen>
+    <dimen name="new_tablet_nav_button_width_half">21dp</dimen>
+    <dimen name="new_tablet_nav_button_width_plus_half">63dp</dimen>
+
     <dimen name="new_tablet_tab_strip_height">48dp</dimen>
     <dimen name="new_tablet_tab_strip_item_width">250dp</dimen>
     <dimen name="new_tablet_tab_strip_item_margin">-30dp</dimen>
     <dimen name="new_tablet_tab_strip_favicon_size">16dp</dimen>
-    <dimen name="new_tablet_forward_default_offset">-6dp</dimen>
     <dimen name="new_tablet_site_security_height">60dp</dimen>
     <dimen name="new_tablet_site_security_width">34dp</dimen>
     <!-- We primarily use padding (instead of margins) to increase the hit area. -->
     <dimen name="new_tablet_site_security_padding_vertical">21dp</dimen>
     <dimen name="new_tablet_site_security_padding_horizontal">8dp</dimen>
     <dimen name="new_tablet_site_security_right_margin">1dp</dimen>
     <dimen name="new_tablet_browser_toolbar_height">60dp</dimen>
     <dimen name="new_tablet_browser_toolbar_menu_item_width">56dp</dimen>
--- a/mobile/android/base/toolbar/BrowserToolbarNewTablet.java
+++ b/mobile/android/base/toolbar/BrowserToolbarNewTablet.java
@@ -5,36 +5,38 @@
 
 package org.mozilla.gecko.toolbar;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.animation.PropertyAnimator;
 import org.mozilla.gecko.animation.ViewHelper;
 
 import android.content.Context;
-import android.content.res.Resources;
 import android.util.AttributeSet;
 
 /**
  * A toolbar implementation for the tablet redesign (bug 1014156).
  * Expected to replace BrowserToolbarTablet once complete.
  */
 class BrowserToolbarNewTablet extends BrowserToolbarTabletBase {
 
     private static final int FORWARD_ANIMATION_DURATION = 450;
 
-    private final int urlBarViewOffset;
-    private final int defaultForwardMargin;
+    private final int forwardButtonTranslationWidth;
 
     public BrowserToolbarNewTablet(final Context context, final AttributeSet attrs) {
         super(context, attrs);
 
-        final Resources res = getResources();
-        urlBarViewOffset = res.getDimensionPixelSize(R.dimen.url_bar_offset_left);
-        defaultForwardMargin = res.getDimensionPixelSize(R.dimen.new_tablet_forward_default_offset);
+        forwardButtonTranslationWidth =
+                getResources().getDimensionPixelOffset(R.dimen.new_tablet_nav_button_width);
+
+        // The forward button is initially expanded (in the layout file)
+        // so translate it for start of the expansion animation; future
+        // iterations translate it to this position when hiding and will already be set up.
+        ViewHelper.setTranslationX(forwardButton, -forwardButtonTranslationWidth);
     }
 
     @Override
     public boolean isAnimating() {
         return false;
     }
 
     @Override
@@ -44,35 +46,34 @@ class BrowserToolbarNewTablet extends Br
 
     @Override
     protected void triggerStopEditingTransition() {
         hideUrlEditLayout();
     }
 
     @Override
     protected void animateForwardButton(final ForwardButtonAnimation animation) {
-        final boolean showing = (animation == ForwardButtonAnimation.SHOW);
+        final boolean willShowForward = (animation == ForwardButtonAnimation.SHOW);
 
-        // if the forward button's margin is non-zero, this means it has already
-        // been animated to be visible¸ and vice-versa.
-        MarginLayoutParams fwdParams = (MarginLayoutParams) forwardButton.getLayoutParams();
-        if ((fwdParams.leftMargin > defaultForwardMargin && showing) ||
-            (fwdParams.leftMargin == defaultForwardMargin && !showing)) {
+        // If we're not in the appropriate state to start a particular animation,
+        // then we must be in the opposite state and do not need to animate.
+        final float forwardOffset = ViewHelper.getTranslationX(forwardButton);
+        if ((forwardOffset >= 0 && willShowForward) ||
+                forwardOffset < 0 && !willShowForward) {
             return;
         }
 
         // We want the forward button to show immediately when switching tabs
         final PropertyAnimator forwardAnim =
                 new PropertyAnimator(isSwitchingTabs ? 10 : FORWARD_ANIMATION_DURATION);
-        final int width = Math.round(forwardButton.getWidth() * .75f);
 
         forwardAnim.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
             @Override
             public void onPropertyAnimationStart() {
-                if (!showing) {
+                if (!willShowForward) {
                     // Set the margin before the transition when hiding the forward button. We
                     // have to do this so that the favicon isn't clipped during the transition
                     MarginLayoutParams layoutParams =
                         (MarginLayoutParams) urlDisplayLayout.getLayoutParams();
                     layoutParams.leftMargin = 0;
 
                     // Do the same on the URL edit container
                     layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams();
@@ -82,52 +83,49 @@ class BrowserToolbarNewTablet extends Br
                     // Note, we already translated the favicon, site security, and text field
                     // in prepareForwardAnimation, so they should appear to have not moved at
                     // all at this point.
                 }
             }
 
             @Override
             public void onPropertyAnimationEnd() {
-                if (showing) {
+                if (willShowForward) {
+                    // Increase the margins to ensure the text does not run outside the View.
                     MarginLayoutParams layoutParams =
                         (MarginLayoutParams) urlDisplayLayout.getLayoutParams();
-                    layoutParams.leftMargin = urlBarViewOffset;
+                    layoutParams.leftMargin = forwardButtonTranslationWidth;
 
                     layoutParams = (MarginLayoutParams) urlEditLayout.getLayoutParams();
-                    layoutParams.leftMargin = urlBarViewOffset;
+                    layoutParams.leftMargin = forwardButtonTranslationWidth;
                 }
 
                 urlDisplayLayout.finishForwardAnimation();
 
-                MarginLayoutParams layoutParams = (MarginLayoutParams) forwardButton.getLayoutParams();
-                layoutParams.leftMargin = defaultForwardMargin + (showing ? width : 0);
-                ViewHelper.setTranslationX(forwardButton, 0);
-
                 requestLayout();
             }
         });
 
-        prepareForwardAnimation(forwardAnim, animation, width);
+        prepareForwardAnimation(forwardAnim, animation, forwardButtonTranslationWidth);
         forwardAnim.start();
     }
 
     private void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) {
         if (animation == ForwardButtonAnimation.HIDE) {
             anim.attach(forwardButton,
                       PropertyAnimator.Property.TRANSLATION_X,
                       -width);
             anim.attach(forwardButton,
                       PropertyAnimator.Property.ALPHA,
                       0);
 
         } else {
             anim.attach(forwardButton,
                       PropertyAnimator.Property.TRANSLATION_X,
-                      width);
+                      0);
             anim.attach(forwardButton,
                       PropertyAnimator.Property.ALPHA,
                       1);
         }
 
         urlDisplayLayout.prepareForwardAnimation(anim, animation, width);
     }
 
--- a/mobile/android/base/toolbar/BrowserToolbarTabletBase.java
+++ b/mobile/android/base/toolbar/BrowserToolbarTabletBase.java
@@ -5,16 +5,17 @@
 
 package org.mozilla.gecko.toolbar;
 
 import java.util.Arrays;
 
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.Tab;
 import org.mozilla.gecko.Tabs;
+import org.mozilla.gecko.menu.MenuItemActionBar;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.view.View;
 import android.widget.Button;
 import android.widget.ImageButton;
 import android.widget.LinearLayout;
@@ -116,18 +117,23 @@ abstract class BrowserToolbarTabletBase 
         super.setNextFocusDownId(nextId);
         backButton.setNextFocusDownId(nextId);
         forwardButton.setNextFocusDownId(nextId);
     }
 
     @Override
     public void setPrivateMode(final boolean isPrivate) {
         super.setPrivateMode(isPrivate);
+
         backButton.setPrivateMode(isPrivate);
         forwardButton.setPrivateMode(isPrivate);
+        for (int i = 0; i < actionItemBar.getChildCount(); ++i) {
+            final MenuItemActionBar child = (MenuItemActionBar) actionItemBar.getChildAt(i);
+            child.setPrivateMode(isPrivate);
+        }
     }
 
     protected boolean canDoBack(final Tab tab) {
         return (tab.canDoBack() && !isEditing());
     }
 
     protected boolean canDoForward(final Tab tab) {
         return (tab.canDoForward() && !isEditing());
--- a/mobile/android/base/toolbar/ToolbarDisplayLayout.java
+++ b/mobile/android/base/toolbar/ToolbarDisplayLayout.java
@@ -530,16 +530,18 @@ public class ToolbarDisplayLayout extend
             return mSiteSecurity;
         }
     }
 
     void prepareForwardAnimation(PropertyAnimator anim, ForwardButtonAnimation animation, int width) {
         mForwardAnim = anim;
 
         if (animation == ForwardButtonAnimation.HIDE) {
+            // We animate these items individually, rather than this entire view,
+            // so that we don't animate certain views, e.g. the stop button.
             anim.attach(mTitle,
                         PropertyAnimator.Property.TRANSLATION_X,
                         0);
             anim.attach(mFavicon,
                         PropertyAnimator.Property.TRANSLATION_X,
                         0);
             anim.attach(mSiteSecurity,
                         PropertyAnimator.Property.TRANSLATION_X,
--- a/mobile/android/base/widget/ThemedEditText.java.in
+++ b/mobile/android/base/widget/ThemedEditText.java.in
@@ -1,4 +1,5 @@
 //#filter substitution
 //#define VIEW_NAME_SUFFIX EditText
 //#define BASE_TYPE android.widget.EditText
+//#define STYLE_CONSTRUCTOR 1
 //#include ThemedView.java.frag
--- a/mobile/android/base/widget/ThemedImageButton.java.in
+++ b/mobile/android/base/widget/ThemedImageButton.java.in
@@ -1,4 +1,5 @@
 //#filter substitution
 //#define VIEW_NAME_SUFFIX ImageButton
 //#define BASE_TYPE android.widget.ImageButton
+//#define STYLE_CONSTRUCTOR 1
 //#include ThemedView.java.frag
--- a/mobile/android/base/widget/ThemedImageView.java.in
+++ b/mobile/android/base/widget/ThemedImageView.java.in
@@ -1,4 +1,5 @@
 //#filter substitution
 //#define VIEW_NAME_SUFFIX ImageView
 //#define BASE_TYPE android.widget.ImageView
+//#define STYLE_CONSTRUCTOR 1
 //#include ThemedView.java.frag
--- a/mobile/android/base/widget/ThemedRelativeLayout.java.in
+++ b/mobile/android/base/widget/ThemedRelativeLayout.java.in
@@ -1,4 +1,5 @@
 //#filter substitution
 //#define VIEW_NAME_SUFFIX RelativeLayout
 //#define BASE_TYPE android.widget.RelativeLayout
+//#define STYLE_CONSTRUCTOR 1
 //#include ThemedView.java.frag
--- a/mobile/android/base/widget/ThemedTextView.java.in
+++ b/mobile/android/base/widget/ThemedTextView.java.in
@@ -1,4 +1,5 @@
 //#filter substitution
 //#define VIEW_NAME_SUFFIX TextView
 //#define BASE_TYPE android.widget.TextView
+//#define STYLE_CONSTRUCTOR 1
 //#include ThemedView.java.frag
--- a/mobile/android/base/widget/ThemedView.java.frag
+++ b/mobile/android/base/widget/ThemedView.java.frag
@@ -10,36 +10,47 @@ import org.mozilla.gecko.R;
 
 import android.content.Context;
 import android.content.res.TypedArray;
 import android.graphics.drawable.ColorDrawable;
 import android.util.AttributeSet;
 
 public class Themed@VIEW_NAME_SUFFIX@ extends @BASE_TYPE@
                                      implements LightweightTheme.OnChangeListener {
-    private final LightweightTheme mTheme;
+    private LightweightTheme mTheme;
 
     private static final int[] STATE_PRIVATE_MODE = { R.attr.state_private };
     private static final int[] STATE_LIGHT = { R.attr.state_light };
     private static final int[] STATE_DARK = { R.attr.state_dark };
 
     protected static final int[] PRIVATE_PRESSED_STATE_SET = { R.attr.state_private, android.R.attr.state_pressed };
     protected static final int[] PRIVATE_FOCUSED_STATE_SET = { R.attr.state_private, android.R.attr.state_focused };
     protected static final int[] PRIVATE_STATE_SET = { R.attr.state_private };
 
     private boolean mIsPrivate;
     private boolean mIsLight;
     private boolean mIsDark;
     private boolean mAutoUpdateTheme = true;
 
     public Themed@VIEW_NAME_SUFFIX@(Context context, AttributeSet attrs) {
         super(context, attrs);
+        initialize(context, attrs);
+    }
+
+//#ifdef STYLE_CONSTRUCTOR
+    public Themed@VIEW_NAME_SUFFIX@(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initialize(context, attrs);
+    }
+
+//#endif
+    private void initialize(final Context context, final AttributeSet attrs) {
         mTheme = ((GeckoApplication) context.getApplicationContext()).getLightweightTheme();
 
-        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme);
+        final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.LightweightTheme);
         mAutoUpdateTheme = a.getBoolean(R.styleable.LightweightTheme_autoUpdateTheme, true);
         a.recycle();
     }
 
     @Override
     public void onAttachedToWindow() {
         super.onAttachedToWindow();
 
--- a/mobile/android/base/widget/ThemedView.java.in
+++ b/mobile/android/base/widget/ThemedView.java.in
@@ -1,4 +1,5 @@
 //#filter substitution
 //#define VIEW_NAME_SUFFIX View
 //#define BASE_TYPE android.view.View
+//#define STYLE_CONSTRUCTOR 1
 //#include ThemedView.java.frag
--- a/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/Reporter.java
+++ b/mobile/android/stumbler/java/org/mozilla/mozstumbler/service/stumblerthread/Reporter.java
@@ -137,30 +137,37 @@ public final class Reporter extends Broa
 
     private void putWifiResults(List<ScanResult> results) {
         if (mBundle == null) {
             return;
         }
 
         Map<String, ScanResult> currentWifiData = mBundle.getWifiData();
         for (ScanResult result : results) {
+            if (currentWifiData.size() > MAX_WIFIS_PER_LOCATION) {
+                return;
+            }
+
             String key = result.BSSID;
             if (!currentWifiData.containsKey(key)) {
                 currentWifiData.put(key, result);
             }
         }
     }
 
     private void putCellResults(List<CellInfo> cells) {
         if (mBundle == null) {
             return;
         }
 
         Map<String, CellInfo> currentCellData = mBundle.getCellData();
         for (CellInfo result : cells) {
+            if (currentCellData.size() > MAX_CELLS_PER_LOCATION) {
+                return;
+            }
             String key = result.getCellIdentity();
             if (!currentCellData.containsKey(key)) {
                 currentCellData.put(key, result);
             }
         }
     }
 
     private void reportCollectedLocation() {
@@ -186,17 +193,19 @@ public final class Reporter extends Broa
             Log.w(LOG_TAG, "Failed to convert bundle to JSON: " + e);
             return;
         }
 
         if (AppGlobals.isDebug) {
             Log.d(LOG_TAG, "Received bundle: " + mlsObj.toString());
         }
 
-        AppGlobals.guiLogInfo(mlsObj.toString());
+        if (wifiCount + cellCount < 1) {
+            return;
+        }
 
         try {
             DataStorageManager.getInstance().insert(mlsObj.toString(), wifiCount, cellCount);
         } catch (IOException e) {
             Log.w(LOG_TAG, e.toString());
         }
     }
 }
--- a/security/manager/boot/src/StaticHPKPins.h
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -1087,9 +1087,9 @@ static const TransportSecurityPreload kP
   { "youtube.com", true, false, false, -1, &kPinset_google_root_pems },
   { "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
 };
 
 // Pinning Preload List Length = 331;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1421489353719000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1422094197058000);
--- a/security/manager/boot/src/nsSTSPreloadList.errors
+++ b/security/manager/boot/src/nsSTSPreloadList.errors
@@ -3,23 +3,24 @@ adsfund.org: could not connect to host
 airbnb.com: did not receive HSTS header
 api.lookout.com: could not connect to host
 api.mega.co.nz: could not connect to host
 api.recurly.com: did not receive HSTS header
 apis.google.com: did not receive HSTS header (error ignored - included regardless)
 app.manilla.com: could not connect to host
 appengine.google.com: did not receive HSTS header (error ignored - included regardless)
 appseccalifornia.org: did not receive HSTS header
-auf-feindgebiet.de: could not connect to host
 azprep.us: did not receive HSTS header
 bassh.net: did not receive HSTS header
 bccx.com: could not connect to host
+bedeta.de: could not connect to host
 betnet.fr: could not connect to host
 bigshinylock.minazo.net: could not connect to host
 blog.lookout.com: did not receive HSTS header
+boxcryptor.com: could not connect to host
 braintreegateway.com: did not receive HSTS header
 braintreepayments.com: did not receive HSTS header
 browserid.org: did not receive HSTS header
 business.medbank.com.mt: did not receive HSTS header
 calyxinstitute.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 126"  data: no]
 carlolly.co.uk: did not receive HSTS header
 cartucce24.it: could not connect to host
 celltek-server.de: did not receive HSTS header
@@ -85,17 +86,17 @@ logentries.com: [Exception... "Component
 login.corp.google.com: max-age too low: 7776000 (error ignored - included regardless)
 logotype.se: did not receive HSTS header
 ludwig.im: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 126"  data: no]
 lumi.do: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 126"  data: no]
 m.gparent.org: could not connect to host
 mail.google.com: did not receive HSTS header (error ignored - included regardless)
 market.android.com: did not receive HSTS header (error ignored - included regardless)
 markusueberallassetmanagement.de: could not connect to host
-matteomarescotti.name: could not connect to host
+matteomarescotti.name: max-age too low: 0
 mobilethreat.net: could not connect to host
 mobilethreatnetwork.net: could not connect to host
 mudcrab.us: could not connect to host
 my.alfresco.com: did not receive HSTS header
 mydigipass.com: did not receive HSTS header
 mykolab.com: did not receive HSTS header
 neonisi.com: could not connect to host
 netzpolitik.org: did not receive HSTS header
@@ -112,22 +113,22 @@ piratenlogin.de: did not receive HSTS he
 platform.lookout.com: could not connect to host
 play.google.com: did not receive HSTS header (error ignored - included regardless)
 pressfreedomfoundation.org: did not receive HSTS header
 prodpad.com: did not receive HSTS header
 profiles.google.com: did not receive HSTS header (error ignored - included regardless)
 promecon-gmbh.de: did not receive HSTS header
 rapidresearch.me: could not connect to host
 riseup.net: did not receive HSTS header
+rme.li: did not receive HSTS header
 robteix.com: did not receive HSTS header
 sah3.net: could not connect to host
 saturngames.co.uk: did not receive HSTS header
 script.google.com: did not receive HSTS header (error ignored - included regardless)
 security.google.com: did not receive HSTS header (error ignored - included regardless)
-secuvera.de: could not connect to host
 semenkovich.com: did not receive HSTS header
 serverdensity.io: did not receive HSTS header
 shops.neonisi.com: could not connect to host
 siammedia.co: did not receive HSTS header
 silentcircle.org: could not connect to host
 simon.butcher.name: max-age too low: 2629743
 sites.google.com: did not receive HSTS header (error ignored - included regardless)
 sol.io: could not connect to host
@@ -143,18 +144,16 @@ sunshinepress.org: could not connect to 
 surfeasy.com: did not receive HSTS header
 talk.google.com: did not receive HSTS header (error ignored - included regardless)
 talkgadget.google.com: did not receive HSTS header (error ignored - included regardless)
 tektoria.de: did not receive HSTS header
 translate.googleapis.com: did not receive HSTS header (error ignored - included regardless)
 uprotect.it: could not connect to host
 wallet.google.com: did not receive HSTS header (error ignored - included regardless)
 webmail.mayfirst.org: did not receive HSTS header
-wf-training-master.appspot.com: could not connect to host
-wf-training-master.appspot.com: could not connect to host (error ignored - included regardless)
 whonix.org: did not receive HSTS header
 wiz.biz: did not receive HSTS header
 www.calyxinstitute.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 126"  data: no]
 www.cueup.com: could not connect to host
 www.developer.mydigipass.com: could not connect to host
 www.dropbox.com: max-age too low: 2592000
 www.elanex.biz: did not receive HSTS header
 www.gmail.com: did not receive HSTS header (error ignored - included regardless)
--- a/security/manager/boot/src/nsSTSPreloadList.inc
+++ b/security/manager/boot/src/nsSTSPreloadList.inc
@@ -3,17 +3,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.     */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1423908549615000);
+const PRTime gPreloadListExpirationTime = INT64_C(1424513391772000);
 
 class nsSTSPreload
 {
   public:
     const char *mHost;
     const bool mIncludeSubdomains;
 };
 
@@ -265,17 +265,16 @@ static const nsSTSPreload kSTSPreloadLis
   { "malnex.de", true },
   { "manage.zenpayroll.com", false },
   { "manageprojects.com", true },
   { "manager.linode.com", false },
   { "mandala-ausmalbilder.de", true },
   { "market.android.com", true },
   { "markusueberallassetmanagement.de", true },
   { "mathiasbynens.be", true },
-  { "matteomarescotti.name", true },
   { "mattmccutchen.net", true },
   { "mbp.banking.co.at", false },
   { "mediacru.sh", true },
   { "medium.com", true },
   { "mega.co.nz", false },
   { "members.mayfirst.org", false },
   { "members.nearlyfreespeech.net", false },
   { "mig5.net", true },
@@ -347,17 +346,16 @@ static const nsSTSPreload kSTSPreloadLis
   { "raiseyourflag.com", true },
   { "redports.org", true },
   { "reedloden.com", true },
   { "reserve-online.net", true },
   { "reviews.anime.my", true },
   { "riccy.org", true },
   { "riesenmagnete.de", true },
   { "rippleunion.com", true },
-  { "rme.li", false },
   { "roddis.net", false },
   { "romab.com", true },
   { "rosenkeller.org", true },
   { "roundcube.mayfirst.org", false },
   { "rws-vertriebsportal.de", true },
   { "s-c.se", true },
   { "sakaki.anime.my", true },
   { "salaervergleich.com", true },
--- a/toolkit/components/search/SearchSuggestionController.jsm
+++ b/toolkit/components/search/SearchSuggestionController.jsm
@@ -42,17 +42,18 @@ Services.prefs.addObserver(BROWSER_SUGGE
  * @constructor
  */
 this.SearchSuggestionController = function SearchSuggestionController(callback = null) {
   this._callback = callback;
 };
 
 this.SearchSuggestionController.prototype = {
   /**
-   * The maximum number of local form history results to return.
+   * The maximum number of local form history results to return. This limit is
+   * only enforced if remote results are also returned.
    */
   maxLocalResults: 7,
 
   /**
    * The maximum number of remote search engine results to return.
    */
   maxRemoteResults: 10,
 
@@ -195,18 +196,17 @@ this.SearchSuggestionController.prototyp
         switch (result.searchResult) {
           case Ci.nsIAutoCompleteResult.RESULT_SUCCESS:
           case Ci.nsIAutoCompleteResult.RESULT_NOMATCH:
             if (result.searchString !== this._searchString) {
               deferredFormHistory.resolve("Unexpected response, this._searchString does not match form history response");
               return;
             }
             let fhEntries = [];
-            let maxHistoryItems = Math.min(result.matchCount, this.maxLocalResults);
-            for (let i = 0; i < maxHistoryItems; ++i) {
+            for (let i = 0; i < result.matchCount; ++i) {
               fhEntries.push(result.getValueAt(i));
             }
             deferredFormHistory.resolve({
               result: fhEntries,
               formHistoryResult: result,
             });
             break;
           case Ci.nsIAutoCompleteResult.RESULT_FAILURE:
@@ -330,18 +330,23 @@ this.SearchSuggestionController.prototyp
       } else if (result.formHistoryResult) { // Local results have a formHistoryResult property.
         results.formHistoryResult = result.formHistoryResult;
         results.local = result.result || [];
       } else { // Remote result
         results.remote = result.result || [];
       }
     }
 
+    // If we have remote results, cap the number of local results
+    if (results.remote.length) {
+      results.local = results.local.slice(0, this.maxLocalResults);
+    }
+
     // We don't want things to appear in both history and suggestions so remove entries from
-    // remote results that are alrady in local.
+    // remote results that are already in local.
     if (results.remote.length && results.local.length) {
       for (let i = 0; i < results.local.length; ++i) {
         let term = results.local[i];
         let dupIndex = results.remote.indexOf(term);
         if (dupIndex != -1) {
           results.remote.splice(dupIndex, 1);
         }
       }
--- a/toolkit/components/search/nsSearchSuggestions.js
+++ b/toolkit/components/search/nsSearchSuggestions.js
@@ -20,16 +20,17 @@ XPCOMUtils.defineLazyModuleGetter(this, 
  */
 function SuggestAutoComplete() {
   this._init();
 }
 SuggestAutoComplete.prototype = {
 
   _init: function() {
     this._suggestionController = new SearchSuggestionController(obj => this.onResultsReturned(obj));
+    this._suggestionController.maxLocalResults = this._historyLimit;
   },
 
   get _suggestionLabel() {
     delete this._suggestionLabel;
     let bundle = Services.strings.createBundle("chrome://global/locale/search/search.properties");
     return this._suggestionLabel = bundle.GetStringFromName("suggestion_label");
   },
 
@@ -52,18 +53,17 @@ SuggestAutoComplete.prototype = {
    * Callback for handling results from SearchSuggestionController.jsm
    * @private
    */
   onResultsReturned: function(results) {
     let finalResults = [];
     let finalComments = [];
 
     // If form history has results, add them to the list.
-    let maxHistoryItems = Math.min(results.local.length, this._historyLimit);
-    for (let i = 0; i < maxHistoryItems; ++i) {
+    for (let i = 0; i < results.local.length; ++i) {
       finalResults.push(results.local[i]);
       finalComments.push("");
     }
 
     // If there are remote matches, add them.
     if (results.remote.length) {
       // "comments" column values for suggestions starts as empty strings
       let comments = new Array(results.remote.length).fill("", 1);
--- a/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
+++ b/toolkit/components/search/tests/xpcshell/test_searchSuggest.js
@@ -144,17 +144,16 @@ add_task(function* simple_non_ascii() {
   let result = yield controller.fetch("I ❤️", false, getEngine);
   do_check_eq(result.term, "I ❤️");
   do_check_eq(result.local.length, 1);
   do_check_eq(result.local[0], "I ❤️ XUL");
   do_check_eq(result.remote.length, 1);
   do_check_eq(result.remote[0], "I ❤️ Mozilla");
 });
 
-
 add_task(function* both_local_remote_result_dedupe() {
   yield updateSearchHistory("bump", "Mozilla");
 
   let controller = new SearchSuggestionController();
   let result = yield controller.fetch("mo", false, getEngine);
   do_check_eq(result.term, "mo");
   do_check_eq(result.local.length, 1);
   do_check_eq(result.local[0], "Mozilla");
@@ -264,16 +263,46 @@ add_task(function* both_identical_with_m
   }
   do_check_eq(result.remote.length, 10);
   for (let i = 0; i < controller.maxRemoteResults; i++) {
     do_check_eq(result.remote[i],
                 "letter " + String.fromCharCode("A".charCodeAt() + controller.maxLocalResults + i));
   }
 });
 
+add_task(function* noremote_maxLocal() {
+  let controller = new SearchSuggestionController();
+  controller.maxLocalResults = 2; // (should be ignored because no remote results)
+  controller.maxRemoteResults = 0;
+  let result = yield controller.fetch("letter ", false, getEngine);
+  do_check_eq(result.term, "letter ");
+  do_check_eq(result.local.length, 26);
+  for (let i = 0; i < result.local.length; i++) {
+    do_check_eq(result.local[i], "letter " + String.fromCharCode("A".charCodeAt() + i));
+  }
+  do_check_eq(result.remote.length, 0);
+});
+
+add_task(function* someremote_maxLocal() {
+  let controller = new SearchSuggestionController();
+  controller.maxLocalResults = 2;
+  controller.maxRemoteResults = 2;
+  let result = yield controller.fetch("letter ", false, getEngine);
+  do_check_eq(result.term, "letter ");
+  do_check_eq(result.local.length, 2);
+  for (let i = 0; i < result.local.length; i++) {
+    do_check_eq(result.local[i], "letter " + String.fromCharCode("A".charCodeAt() + i));
+  }
+  do_check_eq(result.remote.length, 2);
+  // "A" and "B" will have been de-duped, start at C for remote results
+  for (let i = 0; i < result.remote.length; i++) {
+    do_check_eq(result.remote[i], "letter " + String.fromCharCode("C".charCodeAt() + i));
+  }
+});
+
 add_task(function* one_of_each() {
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 1;
   controller.maxRemoteResults = 1;
   let result = yield controller.fetch("letter ", false, getEngine);
   do_check_eq(result.term, "letter ");
   do_check_eq(result.local.length, 1);
   do_check_eq(result.local[0], "letter A");
@@ -283,31 +312,35 @@ add_task(function* one_of_each() {
 
 add_task(function* local_result_returned_remote_result_disabled() {
   Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 1;
   controller.maxRemoteResults = 1;
   let result = yield controller.fetch("letter ", false, getEngine);
   do_check_eq(result.term, "letter ");
-  do_check_eq(result.local.length, 1);
-  do_check_eq(result.local[0], "letter A");
+  do_check_eq(result.local.length, 26);
+  for (let i = 0; i < 26; i++) {
+    do_check_eq(result.local[i], "letter " + String.fromCharCode("A".charCodeAt() + i));
+  }
   do_check_eq(result.remote.length, 0);
   Services.prefs.setBoolPref("browser.search.suggest.enabled", true);
 });
 
 add_task(function* local_result_returned_remote_result_disabled_after_creation_of_controller() {
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 1;
   controller.maxRemoteResults = 1;
   Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
   let result = yield controller.fetch("letter ", false, getEngine);
   do_check_eq(result.term, "letter ");
-  do_check_eq(result.local.length, 1);
-  do_check_eq(result.local[0], "letter A");
+  do_check_eq(result.local.length, 26);
+  for (let i = 0; i < 26; i++) {
+    do_check_eq(result.local[i], "letter " + String.fromCharCode("A".charCodeAt() + i));
+  }
   do_check_eq(result.remote.length, 0);
   Services.prefs.setBoolPref("browser.search.suggest.enabled", true);
 });
 
 add_task(function* one_of_each_disabled_before_creation_enabled_after_creation_of_controller() {
   Services.prefs.setBoolPref("browser.search.suggest.enabled", false);
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 1;
@@ -326,18 +359,20 @@ add_task(function* reset_suggestions_pre
 });
 
 add_task(function* one_local_zero_remote() {
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 1;
   controller.maxRemoteResults = 0;
   let result = yield controller.fetch("letter ", false, getEngine);
   do_check_eq(result.term, "letter ");
-  do_check_eq(result.local.length, 1);
-  do_check_eq(result.local[0], "letter A");
+  do_check_eq(result.local.length, 26);
+  for (let i = 0; i < 26; i++) {
+    do_check_eq(result.local[i], "letter " + String.fromCharCode("A".charCodeAt() + i));
+  }
   do_check_eq(result.remote.length, 0);
 });
 
 add_task(function* zero_local_one_remote() {
   let controller = new SearchSuggestionController();
   controller.maxLocalResults = 0;
   controller.maxRemoteResults = 1;
   let result = yield controller.fetch("letter ", false, getEngine);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/thumbnails/PageThumbUtils.jsm
@@ -0,0 +1,105 @@
+/* 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/. */
+
+/*
+ * Common thumbnailing routines used by various consumers, including
+ * PageThumbs and backgroundPageThumbsContent.
+ */
+
+this.EXPORTED_SYMBOLS = ["PageThumbUtils"];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Promise.jsm", this);
+
+this.PageThumbUtils = {
+  // The default background color for page thumbnails.
+  THUMBNAIL_BG_COLOR: "#fff",
+  // The namespace for thumbnail canvas elements.
+  HTML_NAMESPACE: "http://www.w3.org/1999/xhtml",
+
+  /**
+   * Creates a new canvas element in the context of aWindow, or if aWindow
+   * is undefined, in the context of hiddenDOMWindow.
+   *
+   * @param aWindow (optional) The document of this window will be used to
+   *  create the canvas.  If not given, the hidden window will be used.
+   * @return The newly created canvas.
+   */
+  createCanvas: function (aWindow) {
+    let doc = (aWindow || Services.appShell.hiddenDOMWindow).document;
+    let canvas = doc.createElementNS(this.HTML_NAMESPACE, "canvas");
+    canvas.mozOpaque = true;
+    canvas.mozImageSmoothingEnabled = true;
+    let [thumbnailWidth, thumbnailHeight] = this.getThumbnailSize();
+    canvas.width = thumbnailWidth;
+    canvas.height = thumbnailHeight;
+    return canvas;
+  },
+
+  /**
+   * Calculates a preferred initial thumbnail size based on current desktop
+   * dimensions. The resulting dims will generally be about 1/3 the
+   * size of the desktop. (jimm: why??)
+   *
+   * @return The calculated thumbnail size or a default if unable to calculate.
+   */
+  getThumbnailSize: function () {
+    if (!this._thumbnailWidth || !this._thumbnailHeight) {
+      let screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
+                            .getService(Ci.nsIScreenManager);
+      let left = {}, top = {}, width = {}, height = {};
+      screenManager.primaryScreen.GetRectDisplayPix(left, top, width, height);
+      this._thumbnailWidth = Math.round(width.value / 3);
+      this._thumbnailHeight = Math.round(height.value / 3);
+    }
+    return [this._thumbnailWidth, this._thumbnailHeight];
+  },
+
+  /**
+   * Determine a good thumbnail crop size and scale for a given content
+   * window.
+   *
+   * @param aWindow The content window.
+   * @param aCanvas The target canvas.
+   * @return An array containing width, height and scale.
+   */
+  determineCropSize: function (aWindow, aCanvas) {
+    if (Cu.isCrossProcessWrapper(aWindow)) {
+      throw new Error('Do not pass cpows here.');
+    }
+    let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindowUtils);
+    // aWindow may be a cpow, add exposed props security values.
+    let sbWidth = {}, sbHeight = {};
+
+    try {
+      utils.getScrollbarSize(false, sbWidth, sbHeight);
+    } catch (e) {
+      // This might fail if the window does not have a presShell.
+      Cu.reportError("Unable to get scrollbar size in determineCropSize.");
+      sbWidth.value = sbHeight.value = 0;
+    }
+
+    // Even in RTL mode, scrollbars are always on the right.
+    // So there's no need to determine a left offset.
+    let width = aWindow.innerWidth - sbWidth.value;
+    let height = aWindow.innerHeight - sbHeight.value;
+
+    let {width: thumbnailWidth, height: thumbnailHeight} = aCanvas;
+    let scale = Math.min(Math.max(thumbnailWidth / width, thumbnailHeight / height), 1);
+    let scaledWidth = width * scale;
+    let scaledHeight = height * scale;
+
+    if (scaledHeight > thumbnailHeight)
+      height -= Math.floor(Math.abs(scaledHeight - thumbnailHeight) * scale);
+
+    if (scaledWidth > thumbnailWidth)
+      width -= Math.floor(Math.abs(scaledWidth - thumbnailWidth) * scale);
+
+    return [width, height, scale];
+  }
+};
--- a/toolkit/components/thumbnails/PageThumbs.jsm
+++ b/toolkit/components/thumbnails/PageThumbs.jsm
@@ -5,38 +5,34 @@
 "use strict";
 
 this.EXPORTED_SYMBOLS = ["PageThumbs", "PageThumbsStorage"];
 
 const Cu = Components.utils;
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
-const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
 const PREF_STORAGE_VERSION = "browser.pagethumbnails.storage_version";
 const LATEST_STORAGE_VERSION = 3;
 
 const EXPIRATION_MIN_CHUNK_SIZE = 50;
 const EXPIRATION_INTERVAL_SECS = 3600;
 
+var gRemoteThumbId = 0;
+
 // If a request for a thumbnail comes in and we find one that is "stale"
 // (or don't find one at all) we automatically queue a request to generate a
 // new one.
 const MAX_THUMBNAIL_AGE_SECS = 172800; // 2 days == 60*60*24*2 == 172800 secs.
 
 /**
  * Name of the directory in the profile that contains the thumbnails.
  */
 const THUMBNAIL_DIRECTORY = "thumbnails";
 
-/**
- * The default background color for page thumbnails.
- */
-const THUMBNAIL_BG_COLOR = "#fff";
-
 Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
 Cu.import("resource://gre/modules/PromiseWorker.jsm", this);
 Cu.import("resource://gre/modules/Promise.jsm", this);
 Cu.import("resource://gre/modules/osfile.jsm", this);
 
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
   "resource://gre/modules/NetUtil.jsm");
 
@@ -64,16 +60,18 @@ XPCOMUtils.defineLazyGetter(this, "gUnic
 });
 
 XPCOMUtils.defineLazyModuleGetter(this, "Task",
   "resource://gre/modules/Task.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
   "resource://gre/modules/Deprecated.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AsyncShutdown",
   "resource://gre/modules/AsyncShutdown.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PageThumbUtils",
+  "resource://gre/modules/PageThumbUtils.jsm");
 
 /**
  * Utilities for dealing with promises and Task.jsm
  */
 const TaskUtils = {
   /**
    * Read the bytes from a blob, asynchronously.
    *
@@ -163,123 +161,195 @@ this.PageThumbs = {
     * @param aUrl The web page's url.
     * @return The path of the thumbnail file.
     */
    getThumbnailPath: function PageThumbs_getThumbnailPath(aUrl) {
      return PageThumbsStorage.getFilePathForURL(aUrl);
    },
 
   /**
-   * Captures a thumbnail for the given window.
-   * @param aWindow The DOM window to capture a thumbnail from.
-   * @param aCallback The function to be called when the thumbnail has been
-   *                  captured. The first argument will be the data stream
-   *                  containing the image data.
-   */
-  capture: function PageThumbs_capture(aWindow, aCallback) {
-    if (!this._prefEnabled()) {
-      return;
-    }
-
-    let canvas = this.createCanvas();
-    this.captureToCanvas(aWindow, canvas);
-
-    // Fetch the canvas data on the next event loop tick so that we allow
-    // some event processing in between drawing to the canvas and encoding
-    // its data. We want to block the UI as short as possible. See bug 744100.
-    Services.tm.currentThread.dispatch(function () {
-      canvas.mozFetchAsStream(aCallback, this.contentType);
-    }.bind(this), Ci.nsIThread.DISPATCH_NORMAL);
-  },
-
-
-  /**
-   * Captures a thumbnail for the given window.
+   * Asynchronously returns a thumbnail as a blob for the given
+   * window.
    *
-   * @param aWindow The DOM window to capture a thumbnail from.
+   * @param aBrowser The <browser> to capture a thumbnail from.
    * @return {Promise}
    * @resolve {Blob} The thumbnail, as a Blob.
    */
-  captureToBlob: function PageThumbs_captureToBlob(aWindow) {
+  captureToBlob: function PageThumbs_captureToBlob(aBrowser) {
     if (!this._prefEnabled()) {
       return null;
     }
 
-    let canvas = this.createCanvas();
-    this.captureToCanvas(aWindow, canvas);
+    let deferred = Promise.defer();
 
-    let deferred = Promise.defer();
-    let type = this.contentType;
-    // Fetch the canvas data on the next event loop tick so that we allow
-    // some event processing in between drawing to the canvas and encoding
-    // its data. We want to block the UI as short as possible. See bug 744100.
-    canvas.toBlob(function asBlob(blob) {
-      deferred.resolve(blob, type);
+    let canvas = this.createCanvas();
+    this.captureToCanvas(aBrowser, canvas, () => {
+      canvas.toBlob(blob => {
+        deferred.resolve(blob, this.contentType);
+      });
     });
+
     return deferred.promise;
   },
 
   /**
    * Captures a thumbnail from a given window and draws it to the given canvas.
-   * @param aWindow The DOM window to capture a thumbnail from.
+   * Note, when dealing with remote content, this api draws into the passed
+   * canvas asynchronously. Pass aCallback to receive an async callback after
+   * canvas painting has completed.
+   * @param aBrowser The browser to capture a thumbnail from.
    * @param aCanvas The canvas to draw to.
+   * @param aCallback (optional) A callback invoked once the thumbnail has been
+   * rendered to aCanvas.
    */
-  captureToCanvas: function PageThumbs_captureToCanvas(aWindow, aCanvas) {
+  captureToCanvas: function PageThumbs_captureToCanvas(aBrowser, aCanvas, aCallback) {
     let telemetryCaptureTime = new Date();
-    this._captureToCanvas(aWindow, aCanvas);
-    let telemetry = Services.telemetry;
-    telemetry.getHistogramById("FX_THUMBNAILS_CAPTURE_TIME_MS")
-      .add(new Date() - telemetryCaptureTime);
+    this._captureToCanvas(aBrowser, aCanvas, function () {
+      Services.telemetry
+              .getHistogramById("FX_THUMBNAILS_CAPTURE_TIME_MS")
+              .add(new Date() - telemetryCaptureTime);
+      if (aCallback) {
+        aCallback(aCanvas);
+      }
+    });
   },
 
   // The background thumbnail service captures to canvas but doesn't want to
   // participate in this service's telemetry, which is why this method exists.
-  _captureToCanvas: function PageThumbs__captureToCanvas(aWindow, aCanvas) {
-    let [sw, sh, scale] = this._determineCropSize(aWindow, aCanvas);
+  _captureToCanvas: function (aBrowser, aCanvas, aCallback) {
+    if (aBrowser.isRemoteBrowser) {
+      Task.spawn(function () {
+        let data =
+          yield this._captureRemoteThumbnail(aBrowser, aCanvas);
+        let canvas = data.thumbnail;
+        let ctx = canvas.getContext("2d");
+        let imgData = ctx.getImageData(0, 0, canvas.width, canvas.height);
+        aCanvas.getContext("2d").putImageData(imgData, 0, 0);
+        if (aCallback) {
+          aCallback(aCanvas);
+        }
+      }.bind(this));
+      return;
+    }
+
+    // Generate in-process content thumbnail
+    let [width, height, scale] =
+      PageThumbUtils.determineCropSize(aBrowser.contentWindow, aCanvas);
     let ctx = aCanvas.getContext("2d");
 
     // Scale the canvas accordingly.
     ctx.save();
     ctx.scale(scale, scale);
 
     try {
       // Draw the window contents to the canvas.
-      ctx.drawWindow(aWindow, 0, 0, sw, sh, THUMBNAIL_BG_COLOR,
+      ctx.drawWindow(aBrowser.contentWindow, 0, 0, width, height,
+                     PageThumbUtils.THUMBNAIL_BG_COLOR,
                      ctx.DRAWWINDOW_DO_NOT_FLUSH);
     } catch (e) {
       // We couldn't draw to the canvas for some reason.
     }
+    ctx.restore();
 
-    ctx.restore();
+    if (aCallback) {
+      aCallback(aCanvas);
+    }
   },
 
   /**
+   * Asynchrnously render an appropriately scaled thumbnail to canvas.
+   *
+   * @param aBrowser The browser to capture a thumbnail from.
+   * @param aCanvas The canvas to draw to.
+   * @return a promise
+   */
+  _captureRemoteThumbnail: function (aBrowser, aCanvas) {
+    let deferred = Promise.defer();
+
+    // The index we send with the request so we can identify the
+    // correct response.
+    let index = gRemoteThumbId++;
+
+    // Thumbnail request response handler
+    let mm = aBrowser.messageManager;
+
+    // Browser:Thumbnail:Response handler
+    let thumbFunc = function (aMsg) {
+      // Ignore events unrelated to our request
+      if (aMsg.data.id != index) {
+        return;
+      }
+
+      mm.removeMessageListener("Browser:Thumbnail:Response", thumbFunc);
+      let imageBlob = aMsg.data.thumbnail;
+      let doc = aBrowser.parentElement.ownerDocument;
+      let reader = Cc["@mozilla.org/files/filereader;1"].
+                   createInstance(Ci.nsIDOMFileReader);
+      reader.addEventListener("loadend", function() {
+        let image = doc.createElementNS(PageThumbUtils.HTML_NAMESPACE, "img");
+        image.onload = function () {
+          let thumbnail = doc.createElementNS(PageThumbUtils.HTML_NAMESPACE, "canvas");
+          thumbnail.width = image.naturalWidth;
+          thumbnail.height = image.naturalHeight;
+          let ctx = thumbnail.getContext("2d");
+          ctx.drawImage(image, 0, 0);
+          deferred.resolve({
+            thumbnail: thumbnail
+          });
+        }
+        image.src = reader.result;
+      });
+      // xxx wish there was a way to skip this encoding step
+      reader.readAsDataURL(imageBlob);
+    }
+
+    // Send a thumbnail request
+    mm.addMessageListener("Browser:Thumbnail:Response", thumbFunc);
+    mm.sendAsyncMessage("Browser:Thumbnail:Request", {
+      canvasWidth: aCanvas.width,
+      canvasHeight: aCanvas.height,
+      background: PageThumbUtils.THUMBNAIL_BG_COLOR,
+      id: index
+    });
+
+    return deferred.promise;
+  },
+
+  /**
    * Captures a thumbnail for the given browser and stores it to the cache.
    * @param aBrowser The browser to capture a thumbnail for.
    * @param aCallback The function to be called when finished (optional).
    */
   captureAndStore: function PageThumbs_captureAndStore(aBrowser, aCallback) {
     if (!this._prefEnabled()) {
       return;
     }
 
     let url = aBrowser.currentURI.spec;
-    let channel = aBrowser.docShell.currentDocumentChannel;
-    let originalURL = channel.originalURI.spec;
+    let originalURL;
+    let channelError = false;
 
-    // see if this was an error response.
-    let wasError = this._isChannelErrorResponse(channel);
+    if (!aBrowser.isRemoteBrowser) {
+      let channel = aBrowser.docShell.currentDocumentChannel;
+      originalURL = channel.originalURI.spec;
+      // see if this was an error response.
+      channelError = this._isChannelErrorResponse(channel);
+    } else {
+      // We need channel info (bug 1073957)
+      originalURL = url;
+    }
 
     Task.spawn((function task() {
       let isSuccess = true;
       try {
-        let blob = yield this.captureToBlob(aBrowser.contentWindow);
+        let blob = yield this.captureToBlob(aBrowser);
         let buffer = yield TaskUtils.readBlob(blob);
-        yield this._store(originalURL, url, buffer, wasError);
-      } catch (_) {
+        yield this._store(originalURL, url, buffer, channelError);
+      } catch (ex) {
+        Components.utils.reportError("Exception thrown during thumbnail capture: '" + ex + "'");
         isSuccess = false;
       }
       if (aCallback) {
         aCallback(isSuccess);
       }
     }).bind(this));
   },
 
@@ -370,84 +440,23 @@ this.PageThumbs = {
    * Unregister an expiration filter.
    * @param aFilter A filter that was previously passed to addExpirationFilter.
    */
   removeExpirationFilter: function PageThumbs_removeExpirationFilter(aFilter) {
     PageThumbsExpiration.removeFilter(aFilter);
   },
 
   /**
-   * Determines the crop size for a given content window.
-   * @param aWindow The content window.
-   * @param aCanvas The target canvas.
-   * @return An array containing width, height and scale.
-   */
-  _determineCropSize: function PageThumbs_determineCropSize(aWindow, aCanvas) {
-    let utils = aWindow.QueryInterface(Ci.nsIInterfaceRequestor)
-                       .getInterface(Ci.nsIDOMWindowUtils);
-    let sbWidth = {}, sbHeight = {};
-
-    try {
-      utils.getScrollbarSize(false, sbWidth, sbHeight);
-    } catch (e) {
-      // This might fail if the window does not have a presShell.
-      Cu.reportError("Unable to get scrollbar size in _determineCropSize.");
-      sbWidth.value = sbHeight.value = 0;
-    }
-
-    // Even in RTL mode, scrollbars are always on the right.
-    // So there's no need to determine a left offset.
-    let sw = aWindow.innerWidth - sbWidth.value;
-    let sh = aWindow.innerHeight - sbHeight.value;
-
-    let {width: thumbnailWidth, height: thumbnailHeight} = aCanvas;
-    let scale = Math.min(Math.max(thumbnailWidth / sw, thumbnailHeight / sh), 1);
-    let scaledWidth = sw * scale;
-    let scaledHeight = sh * scale;
-
-    if (scaledHeight > thumbnailHeight)
-      sh -= Math.floor(Math.abs(scaledHeight - thumbnailHeight) * scale);
-
-    if (scaledWidth > thumbnailWidth)
-      sw -= Math.floor(Math.abs(scaledWidth - thumbnailWidth) * scale);
-
-    return [sw, sh, scale];
-  },
-
-  /**
    * Creates a new hidden canvas element.
    * @param aWindow The document of this window will be used to create the
    *                canvas.  If not given, the hidden window will be used.
    * @return The newly created canvas.
    */
   createCanvas: function PageThumbs_createCanvas(aWindow) {
-    let doc = (aWindow || Services.appShell.hiddenDOMWindow).document;
-    let canvas = doc.createElementNS(HTML_NAMESPACE, "canvas");
-    canvas.mozOpaque = true;
-    canvas.mozImageSmoothingEnabled = true;
-    let [thumbnailWidth, thumbnailHeight] = this._getThumbnailSize();
-    canvas.width = thumbnailWidth;
-    canvas.height = thumbnailHeight;
-    return canvas;
-  },
-
-  /**
-   * Calculates the thumbnail size based on current desktop's dimensions.
-   * @return The calculated thumbnail size or a default if unable to calculate.
-   */
-  _getThumbnailSize: function PageThumbs_getThumbnailSize() {
-    if (!this._thumbnailWidth || !this._thumbnailHeight) {
-      let screenManager = Cc["@mozilla.org/gfx/screenmanager;1"]
-                            .getService(Ci.nsIScreenManager);
-      let left = {}, top = {}, width = {}, height = {};
-      screenManager.primaryScreen.GetRectDisplayPix(left, top, width, height);
-      this._thumbnailWidth = Math.round(width.value / 3);
-      this._thumbnailHeight = Math.round(height.value / 3);
-    }
-    return [this._thumbnailWidth, this._thumbnailHeight];
+    return PageThumbUtils.createCanvas(aWindow);
   },
 
   /**
    * Given a channel, returns true if it should be considered an "error
    * response", false otherwise.
    */
   _isChannelErrorResponse: function(channel) {
     // No valid document channel sounds like an error to me!
--- a/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
+++ b/toolkit/components/thumbnails/content/backgroundPageThumbsContent.js
@@ -1,19 +1,17 @@
 /* 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 () { // bug 673569 workaround :(
-
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 Cu.importGlobalProperties(['Blob']);
 
-Cu.import("resource://gre/modules/PageThumbs.jsm");
+Cu.import("resource://gre/modules/PageThumbUtils.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 const STATE_LOADING = 1;
 const STATE_CAPTURING = 2;
 const STATE_CANCELED = 3;
 
 const backgroundPageThumbsContent = {
@@ -44,17 +42,17 @@ const backgroundPageThumbsContent = {
       addProgressListener(this, Ci.nsIWebProgress.NOTIFY_STATE_WINDOW);
   },
 
   observe: function (subj, topic, data) {
     // Arrange to prevent (most) popup dialogs for this window - popups done
     // in the parent (eg, auth) aren't prevented, but alert() etc are.
     // disableDialogs only works on the current inner window, so it has
     // to be called every page load, but before scripts run.
-    if (subj == content.document) {
+    if (content && subj == content.document) {
       content.
         QueryInterface(Ci.nsIInterfaceRequestor).
         getInterface(Ci.nsIDOMWindowUtils).
         disableDialogs();
     }
   },
 
   get _webNav() {
@@ -124,19 +122,29 @@ const backgroundPageThumbsContent = {
     }
   },
 
   _captureCurrentPage: function () {
     let capture = this._currentCapture;
     capture.finalURL = this._webNav.currentURI.spec;
     capture.pageLoadTime = new Date() - capture.pageLoadStartDate;
 
-    let canvas = PageThumbs.createCanvas(content);
     let canvasDrawDate = new Date();
-    PageThumbs._captureToCanvas(content, canvas);
+
+    let canvas = PageThumbUtils.createCanvas(content);
+    let [sw, sh, scale] = PageThumbUtils.determineCropSize(content, canvas);
+
+    let ctx = canvas.getContext("2d");
+    ctx.save();
+    ctx.scale(scale, scale);
+    ctx.drawWindow(content, 0, 0, sw, sh,
+                   PageThumbUtils.THUMBNAIL_BG_COLOR,
+                   ctx.DRAWWINDOW_DO_NOT_FLUSH);
+    ctx.restore();
+
     capture.canvasDrawTime = new Date() - canvasDrawDate;
 
     canvas.toBlob(blob => {
       capture.imageBlob = new Blob([blob]);
       // Load about:blank to finish the capture and wait for onStateChange.
       this._loadAboutBlank();
     });
   },
@@ -179,10 +187,8 @@ const backgroundPageThumbsContent = {
   QueryInterface: XPCOMUtils.generateQI([
     Ci.nsIWebProgressListener,
     Ci.nsISupportsWeakReference,
     Ci.nsIObserver,
   ]),
 };
 
 backgroundPageThumbsContent.init();
-
-})();
--- a/toolkit/components/thumbnails/moz.build
+++ b/toolkit/components/thumbnails/moz.build
@@ -10,11 +10,12 @@ EXTRA_COMPONENTS += [
     'BrowserPageThumbs.manifest',
     'PageThumbsProtocol.js',
 ]
 
 EXTRA_JS_MODULES += [
     'BackgroundPageThumbs.jsm',
     'PageThumbs.jsm',
     'PageThumbsWorker.js',
+    'PageThumbUtils.jsm',
 ]
 
 JAR_MANIFESTS += ['jar.mn']
--- a/toolkit/components/thumbnails/test/browser.ini
+++ b/toolkit/components/thumbnails/test/browser.ini
@@ -1,40 +1,46 @@
 [DEFAULT]
-skip-if = e10s # Bug 863512 - thumbnails are disabled with e10s enabled.
 support-files =
   background_red.html
   background_red_redirect.sjs
   background_red_scroll.html
   head.js
   privacy_cache_control.sjs
   thumbnails_background.sjs
   thumbnails_crash_content_helper.js
   thumbnails_update.sjs
 
 [browser_thumbnails_bg_bad_url.js]
 [browser_thumbnails_bg_crash_during_capture.js]
-skip-if = buildapp == 'mulet' || !crashreporter
+skip-if = buildapp == 'mulet' || !crashreporter || e10s # crashing the remote thumbnailer crashes the remote test tab
 [browser_thumbnails_bg_crash_while_idle.js]
-skip-if = buildapp == 'mulet' || !crashreporter
+skip-if = buildapp == 'mulet' || !crashreporter || e10s
 [browser_thumbnails_bg_basic.js]
 [browser_thumbnails_bg_queueing.js]
 [browser_thumbnails_bg_timeout.js]
 [browser_thumbnails_bg_redirect.js]
 [browser_thumbnails_bg_destroy_browser.js]
 [browser_thumbnails_bg_no_cookies_sent.js]
+skip-if = e10s # e10s cookie problems
 [browser_thumbnails_bg_no_cookies_stored.js]
 [browser_thumbnails_bg_no_auth_prompt.js]
 [browser_thumbnails_bg_no_alert.js]
 [browser_thumbnails_bg_no_duplicates.js]
 [browser_thumbnails_bg_captureIfMissing.js]
 [browser_thumbnails_bug726727.js]
 skip-if = buildapp == 'mulet'
 [browser_thumbnails_bug727765.js]
+skip-if = e10s # tries to open crypto/local file from the child
 [browser_thumbnails_bug818225.js]
+skip-if = e10s # load event issues, bug 1084637.
 [browser_thumbnails_capture.js]
+skip-if = e10s # tries to call drawWindow with a remote browser.
 [browser_thumbnails_expiration.js]
 [browser_thumbnails_privacy.js]
+skip-if = e10s # nsSSLStatus has null mServerCert, bug 820466
 [browser_thumbnails_redirect.js]
+skip-if = e10s # bug 1050869
 [browser_thumbnails_storage.js]
 [browser_thumbnails_storage_migrate3.js]
 skip-if = buildapp == 'mulet'
 [browser_thumbnails_update.js]
+skip-if = e10s # tries to open crypto/local file from the child
--- a/toolkit/components/thumbnails/test/head.js
+++ b/toolkit/components/thumbnails/test/head.js
@@ -132,16 +132,17 @@ function captureAndCheckColor(aRed, aGre
       next();
     });
   });
 }
 
 /**
  * For a given URL, loads the corresponding thumbnail
  * to a canvas and passes its image data to the callback.
+ * Note, not compat with e10s!
  * @param aURL The url associated with the thumbnail.
  * @param aCallback The function to pass the image data to.
  */
 function retrieveImageDataForURL(aURL, aCallback) {
   let width = 100, height = 100;
   let thumb = PageThumbs.getThumbnailURL(aURL, width, height);
   // create a tab with a chrome:// URL so it can host the thumbnail image.
   // Note that we tried creating the element directly in the top-level chrome
--- a/toolkit/content/browser-child.js
+++ b/toolkit/content/browser-child.js
@@ -6,16 +6,19 @@ let Cc = Components.classes;
 let Ci = Components.interfaces;
 let Cu = Components.utils;
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import('resource://gre/modules/XPCOMUtils.jsm');
 Cu.import("resource://gre/modules/RemoteAddonsChild.jsm");
 Cu.import("resource://gre/modules/Timer.jsm");
 
+XPCOMUtils.defineLazyModuleGetter(this, "PageThumbUtils",
+  "resource://gre/modules/PageThumbUtils.jsm");
+
 #ifdef MOZ_CRASHREPORTER
 XPCOMUtils.defineLazyServiceGetter(this, "CrashReporter",
                                    "@mozilla.org/xre/app-info;1",
                                    "nsICrashReporter");
 #endif
 
 let FocusSyncHandler = {
   init: function() {
@@ -363,16 +366,47 @@ addEventListener("ZoomChangeUsingMouseWh
   sendAsyncMessage("ZoomChangeUsingMouseWheel", {});
 }, false);
 
 addMessageListener("UpdateCharacterSet", function (aMessage) {
   docShell.charset = aMessage.data.value;
   docShell.gatherCharsetMenuTelemetry();
 });
 
+/**
+ * Remote thumbnail request handler for PageThumbs thumbnails.
+ */
+addMessageListener("Browser:Thumbnail:Request", function (aMessage) {
+  let thumbnail = content.document.createElementNS(PageThumbUtils.HTML_NAMESPACE,
+                                                   "canvas");
+  thumbnail.mozOpaque = true;
+  thumbnail.mozImageSmoothingEnabled = true;
+
+  thumbnail.width = aMessage.data.canvasWidth;
+  thumbnail.height = aMessage.data.canvasHeight;
+
+  let [width, height, scale] =
+    PageThumbUtils.determineCropSize(content, thumbnail);
+
+  let ctx = thumbnail.getContext("2d");
+  ctx.save();
+  ctx.scale(scale, scale);
+  ctx.drawWindow(content, 0, 0, width, height,
+                 aMessage.data.background,
+                 ctx.DRAWWINDOW_DO_NOT_FLUSH);
+  ctx.restore();
+
+  thumbnail.toBlob(function (aBlob) {
+    sendAsyncMessage("Browser:Thumbnail:Response", {
+      thumbnail: aBlob,
+      id: aMessage.data.id
+    });
+  });
+});
+
 // The AddonsChild needs to be rooted so that it stays alive as long as
 // the tab.
 let AddonsChild;
 if (Services.appinfo.browserTabsRemoteAutostart) {
   // Currently, the addon shims are only supported when autostarting
   // with remote tabs.
   AddonsChild = RemoteAddonsChild.init(this);
 
--- a/toolkit/devtools/server/actors/timeline.js
+++ b/toolkit/devtools/server/actors/timeline.js
@@ -46,17 +46,18 @@ let TimelineActor = exports.TimelineActo
      * at most, when profile markers are found. A marker has the following
      * properties:
      * - start {Number} ms
      * - end {Number} ms
      * - name {String}
      */
     "markers" : {
       type: "markers",
-      markers: Arg(0, "array:json")
+      markers: Arg(0, "array:json"),
+      endTime: Arg(1, "number")
     },
 
     /**
      * "memory" events emitted in tandem with "markers", if this was enabled
      * when the recording started.
      */
     "memory" : {
       type: "memory",
@@ -75,16 +76,17 @@ let TimelineActor = exports.TimelineActo
     }
   },
 
   initialize: function(conn, tabActor) {
     protocol.Actor.prototype.initialize.call(this, conn);
     this.tabActor = tabActor;
 
     this._isRecording = false;
+    this._startTime = 0;
 
     // Make sure to get markers from new windows as they become available
     this._onWindowReady = this._onWindowReady.bind(this);
     events.on(this.tabActor, "window-ready", this._onWindowReady);
   },
 
   /**
    * The timeline actor is the first (and last) in its hierarchy to use protocol.js
@@ -137,17 +139,18 @@ let TimelineActor = exports.TimelineActo
       return;
     }
 
     let markers = [];
     for (let docShell of this.docShells) {
       markers = [...markers, ...docShell.popProfileTimelineMarkers()];
     }
     if (markers.length > 0) {
-      events.emit(this, "markers", markers);
+      let endTime = this.docShells[0].now();
+      events.emit(this, "markers", markers, endTime);
     }
     if (this._memoryActor) {
       events.emit(this, "memory", Date.now(), this._memoryActor.measure());
     }
     if (this._framerateActor) {
       events.emit(this, "ticks", Date.now(), this._framerateActor.getPendingTicks());
     }
 
@@ -171,35 +174,40 @@ let TimelineActor = exports.TimelineActo
   /**
    * Start recording profile markers.
    */
   start: method(function({ withMemory, withTicks }) {
     if (this._isRecording) {
       return;
     }
     this._isRecording = true;
+    this._startTime = this.docShells[0].now();
 
     for (let docShell of this.docShells) {
       docShell.recordProfileTimelineMarkers = true;
     }
 
     if (withMemory) {
       this._memoryActor = new MemoryActor(this.conn, this.tabActor);
       events.emit(this, "memory", Date.now(), this._memoryActor.measure());
     }
     if (withTicks) {
       this._framerateActor = new FramerateActor(this.conn, this.tabActor);
       this._framerateActor.startRecording();
     }
 
     this._pullTimelineData();
+    return this._startTime;
   }, {
     request: {
       withMemory: Option(0, "boolean"),
       withTicks: Option(0, "boolean")
+    },
+    response: {
+      value: RetVal("number")
     }
   }),
 
   /**
    * Stop recording profile markers.
    */
   stop: method(function() {
     if (!this._isRecording) {
@@ -223,18 +231,16 @@ let TimelineActor = exports.TimelineActo
   }, {}),
 
   /**
    * When a new window becomes available in the tabActor, start recording its
    * markers if we were recording.
    */
   _onWindowReady: function({window}) {
     if (this._isRecording) {
-      // XXX As long as bug 1070089 isn't fixed, each docShell has its own start
-      // recording time, so markers aren't going to be properly ordered.
       let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
                            .getInterface(Ci.nsIWebNavigation)
                            .QueryInterface(Ci.nsIDocShell);
       docShell.recordProfileTimelineMarkers = true;
     }
   }
 });
 
--- a/toolkit/devtools/server/actors/webaudio.js
+++ b/toolkit/devtools/server/actors/webaudio.js
@@ -288,16 +288,46 @@ let AudioNodeActor = exports.AudioNodeAc
     let props = Object.keys(NODE_PROPERTIES[this.type]);
     return props.map(prop =>
       ({ param: prop, value: this.getParam(prop), flags: this.getParamFlags(prop) }));
   }, {
     response: { params: RetVal("json") }
   }),
 
   /**
+   * Connects this audionode to an AudioParam via `node.connect(param)`.
+   */
+  connectParam: method(function (destActor, paramName, output) {
+    let srcNode = this.node.get();
+    let destNode = destActor.node.get();
+
+    if (srcNode === null || destNode === null) {
+      return CollectedAudioNodeError();
+    }
+
+    try {
+      // Connect via the unwrapped node, so we can call the
+      // patched method that fires the webaudio actor's `connect-param` event.
+      // Connect directly to the wrapped `destNode`, otherwise
+      // the patched method thinks this is a new node and won't be
+      // able to find it in `_nativeToActorID`.
+      XPCNativeWrapper.unwrap(srcNode).connect(destNode[paramName], output);
+    } catch (e) {
+      return constructError(e);
+    }
+  }, {
+    request: {
+      destActor: Arg(0, "audionode"),
+      paramName: Arg(1, "string"),
+      output: Arg(2, "nullable:number")
+    },
+    response: { error: RetVal("nullable:json") }
+  }),
+
+  /**
    * Connects this audionode to another via `node.connect(dest)`.
    */
   connectNode: method(function (destActor, output, input) {
     let srcNode = this.node.get();
     let destNode = destActor.node.get();
 
     if (srcNode === null || destNode === null) {
       return CollectedAudioNodeError();
--- a/toolkit/modules/secondscreen/SimpleServiceDiscovery.jsm
+++ b/toolkit/modules/secondscreen/SimpleServiceDiscovery.jsm
@@ -130,17 +130,19 @@ var SimpleServiceDiscovery = {
 
   // Stop the current continuous search
   stopSearch: function stopSearch() {
     this._searchRepeat.cancel();
   },
 
   _usingLAN: function() {
     let network = Cc["@mozilla.org/network/network-link-service;1"].getService(Ci.nsINetworkLinkService);
-    return (network.linkType == Ci.nsINetworkLinkService.LINK_TYPE_WIFI || network.linkType == Ci.nsINetworkLinkService.LINK_TYPE_ETHERNET);
+    return (network.linkType == Ci.nsINetworkLinkService.LINK_TYPE_WIFI ||
+            network.linkType == Ci.nsINetworkLinkService.LINK_TYPE_ETHERNET ||
+            network.linkType == Ci.nsINetworkLinkService.LINK_TYPE_UNKNOWN);
   },
 
   _search: function _search() {
     // If a search is already active, shut it down.
     this._searchShutdown();
 
     // We only search if on local network
     if (!this._usingLAN()) {
--- a/toolkit/themes/linux/global/toolbar.css
+++ b/toolkit/themes/linux/global/toolbar.css
@@ -29,16 +29,17 @@ menubar, toolbar[type="menubar"] {
   min-width: 1px;
   min-height: 20px;
   padding: 1px 0px;
 }
 
 menubar:-moz-lwtheme,
 toolbar:-moz-lwtheme {
   -moz-appearance: none;
+  color: inherit;
 }
 
 /* in browser.xul, the menubar is inside a toolbar... */
 toolbaritem > menubar {
   -moz-appearance: none;
 }
 
 /* ::::: toolbar decorations ::::: */
--- a/widget/gonk/nsWindow.cpp
+++ b/widget/gonk/nsWindow.cpp
@@ -39,31 +39,33 @@
 #include "nsWindow.h"
 #include "nsIWidgetListener.h"
 #include "cutils/properties.h"
 #include "ClientLayerManager.h"
 #include "BasicLayers.h"
 #include "libdisplay/GonkDisplay.h"
 #include "pixelflinger/format.h"
 #include "mozilla/BasicEvents.h"
+#include "mozilla/gfx/2D.h"
 #include "mozilla/layers/APZCTreeManager.h"
 #include "mozilla/layers/CompositorParent.h"
 #include "ParentProcessController.h"
 #include "nsThreadUtils.h"
 #include "HwcComposer2D.h"
 
 #define LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gonk" , ## args)
 #define LOGW(args...) __android_log_print(ANDROID_LOG_WARN, "Gonk", ## args)
 #define LOGE(args...) __android_log_print(ANDROID_LOG_ERROR, "Gonk", ## args)
 
 #define IS_TOPLEVEL() (mWindowType == eWindowType_toplevel || mWindowType == eWindowType_dialog)
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::hal;
+using namespace mozilla::gfx;
 using namespace mozilla::gl;
 using namespace mozilla::layers;
 using namespace mozilla::widget;
 
 nsIntRect gScreenBounds;
 static uint32_t sScreenRotation;
 static uint32_t sPhysicalScreenRotation;
 static nsIntRect sVirtualBounds;
@@ -121,16 +123,18 @@ displayEnabledCallback(bool enabled)
     HwcComposer2D::GetInstance()->EnableVsync(enabled);
     NS_DispatchToMainThread(enabled ? sScreenOnEvent : sScreenOffEvent);
 }
 
 } // anonymous namespace
 
 nsWindow::nsWindow()
 {
+    mFramebuffer = nullptr;
+
     if (sScreenInitialized)
         return;
 
     sScreenOnEvent = new ScreenOnOffEvent(true);
     ClearOnShutdown(&sScreenOnEvent);
     sScreenOffEvent = new ScreenOnOffEvent(false);
     ClearOnShutdown(&sScreenOffEvent);
     GetGonkDisplay()->OnEnabled(displayEnabledCallback);
@@ -467,16 +471,84 @@ nsWindow::MakeFullScreen(bool aFullScree
         // unpainted.
         Resize(sVirtualBounds.x, sVirtualBounds.y,
                sVirtualBounds.width, sVirtualBounds.height,
                /*repaint*/true);
     }
     return NS_OK;
 }
 
+static gralloc_module_t const*
+gralloc_module()
+{
+    hw_module_t const *module;
+    if (hw_get_module(GRALLOC_HARDWARE_MODULE_ID, &module)) {
+        return nullptr;
+    }
+    return reinterpret_cast<gralloc_module_t const*>(module);
+}
+
+static SurfaceFormat
+HalFormatToSurfaceFormat(int aHalFormat, int* bytepp)
+{
+    switch (aHalFormat) {
+    case HAL_PIXEL_FORMAT_RGBA_8888:
+        *bytepp = 4;
+        return SurfaceFormat::R8G8B8A8;
+    case HAL_PIXEL_FORMAT_RGBX_8888:
+        *bytepp = 4;
+        return SurfaceFormat::R8G8B8X8;
+    case HAL_PIXEL_FORMAT_BGRA_8888:
+        *bytepp = 4;
+        return SurfaceFormat::B8G8R8A8;
+    case HAL_PIXEL_FORMAT_RGB_565:
+        *bytepp = 2;
+        return SurfaceFormat::R5G6B5;
+    default:
+        MOZ_CRASH("Unhandled HAL pixel format");
+        return SurfaceFormat::UNKNOWN; // not reached
+    }
+}
+
+TemporaryRef<DrawTarget>
+nsWindow::StartRemoteDrawing()
+{
+    GonkDisplay* display = GetGonkDisplay();
+    mFramebuffer = display->DequeueBuffer();
+    int width = mFramebuffer->width, height = mFramebuffer->height;
+    void *vaddr;
+    if (gralloc_module()->lock(gralloc_module(), mFramebuffer->handle,
+                               GRALLOC_USAGE_SW_READ_NEVER |
+                               GRALLOC_USAGE_SW_WRITE_OFTEN |
+                               GRALLOC_USAGE_HW_FB,
+                               0, 0, width, height, &vaddr)) {
+        EndRemoteDrawing();
+        return nullptr;
+    }
+    int bytepp;
+    SurfaceFormat format = HalFormatToSurfaceFormat(display->surfaceformat,
+                                                    &bytepp);
+    return mFramebufferTarget = Factory::CreateDrawTargetForData(
+        BackendType::CAIRO, (uint8_t*)vaddr,
+        IntSize(width, height), mFramebuffer->stride * bytepp, format);
+}
+
+void
+nsWindow::EndRemoteDrawing()
+{
+    if (mFramebufferTarget) {
+        gralloc_module()->unlock(gralloc_module(), mFramebuffer->handle);
+    }
+    if (mFramebuffer) {
+        GetGonkDisplay()->QueueBuffer(mFramebuffer);
+    }
+    mFramebuffer = nullptr;
+    mFramebufferTarget = nullptr;
+}
+
 float
 nsWindow::GetDPI()
 {
     return GetGonkDisplay()->xdpi;
 }
 
 double
 nsWindow::GetDefaultScaleInternal()
--- a/widget/gonk/nsWindow.h
+++ b/widget/gonk/nsWindow.h
@@ -14,28 +14,31 @@
  */
 
 #ifndef nsWindow_h
 #define nsWindow_h
 
 #include "nsBaseWidget.h"
 #include "nsRegion.h"
 #include "nsIIdleServiceInternal.h"
+#include "Units.h"
 
 extern nsIntRect gScreenBounds;
 
 namespace mozilla {
 namespace gl {
 class GLContext;
 }
 namespace layers {
 class LayersManager;
 }
 }
 
+class ANativeWindowBuffer;
+
 namespace android {
 class FramebufferNativeWindow;
 }
 
 namespace widget {
 struct InputContext;
 struct InputContextAction;
 }
@@ -89,16 +92,20 @@ public:
                                    bool aDoCapture)
     {
         return NS_ERROR_NOT_IMPLEMENTED;
     }
     NS_IMETHOD ReparentNativeWidget(nsIWidget* aNewParent);
 
     NS_IMETHOD MakeFullScreen(bool aFullScreen) /*MOZ_OVERRIDE*/;
 
+    virtual mozilla::TemporaryRef<mozilla::gfx::DrawTarget>
+        StartRemoteDrawing() MOZ_OVERRIDE;
+    virtual void EndRemoteDrawing() MOZ_OVERRIDE;
+
     virtual float GetDPI();
     virtual double GetDefaultScaleInternal();
     virtual mozilla::layers::LayerManager*
         GetLayerManager(PLayerTransactionChild* aShadowManager = nullptr,
                         LayersBackend aBackendHint = mozilla::layers::LayersBackend::LAYERS_NONE,
                         LayerManagerPersistence aPersistence = LAYER_MANAGER_CURRENT,
                         bool* aAllowRetaining = nullptr);
 
@@ -113,16 +120,21 @@ public:
 
     virtual Composer2D* GetComposer2D() MOZ_OVERRIDE;
 
 protected:
     nsWindow* mParent;
     bool mVisible;
     InputContext mInputContext;
     nsCOMPtr<nsIIdleServiceInternal> mIdleService;
+    // If we're using a BasicCompositor, these fields are temporarily
+    // set during frame composition.  They wrap the hardware
+    // framebuffer.
+    mozilla::RefPtr<mozilla::gfx::DrawTarget> mFramebufferTarget;
+    ANativeWindowBuffer* mFramebuffer;
 
     void BringToTop();
 
     // Call this function when the users activity is the direct cause of an
     // event (like a keypress or mouse click).
     void UserActivity();
 };
 
--- a/widget/windows/winrt/APZController.cpp
+++ b/widget/windows/winrt/APZController.cpp
@@ -142,28 +142,28 @@ APZController::RequestContentRepaint(con
   WinUtils::Log("APZController: mScrollOffset: %f %f", aFrameMetrics.mScrollOffset.x,
     aFrameMetrics.mScrollOffset.y);
 #endif
 
   nsCOMPtr<nsIDocument> subDocument;
   nsCOMPtr<nsIContent> targetContent;
   if (!GetDOMTargets(aFrameMetrics.GetScrollId(),
                      subDocument, targetContent)) {
-    return NS_OK;
+    return;
   }
 
   // If we're dealing with a sub frame or content editable element,
   // call UpdateSubFrame.
   if (targetContent) {
 #ifdef DEBUG_CONTROLLER
     WinUtils::Log("APZController: detected subframe or content editable");
 #endif
     FrameMetrics metrics = aFrameMetrics;
     mozilla::layers::APZCCallbackHelper::UpdateSubFrame(targetContent, metrics);
-    return NS_OK;
+    return;
   }
 
 #ifdef DEBUG_CONTROLLER
   WinUtils::Log("APZController: detected tab");
 #endif
 
   // We're dealing with a tab, call UpdateRootFrame.
   nsCOMPtr<nsIDOMWindowUtils> utils;