Merge m-c to inbound a=merge
authorWes Kocher <wkocher@mozilla.com>
Tue, 21 Apr 2015 15:35:07 -0700
changeset 240416 d86eef3f31ee3a10372055b37f78e6a57a89cd45
parent 240415 9cfeed1582a3545bc29ad4f4455636363507a7a0 (current diff)
parent 240312 86725d1a2cf345520412ef89fab2225b383541e6 (diff)
child 240417 e01af01b70152d6de05b0d6d6b5cb0ad6f871f69
push id28636
push userkwierso@gmail.com
push dateThu, 23 Apr 2015 00:16:12 +0000
treeherdermozilla-central@a5af73b32ac8 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.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 m-c to inbound a=merge
b2g/installer/package-manifest.in
browser/installer/package-manifest.in
editor/libeditor/tests/file_bug915962.html
editor/libeditor/tests/test_bug915962.html
ipc/unixsocket/UnixSocket.cpp
ipc/unixsocket/UnixSocket.h
layout/reftests/layers/reftest.list
modules/libpref/init/all.js
toolkit/components/telemetry/tests/unit/test_ThirdPartyCookieProbe.js
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c7d4045742862a7cf3f0074902ebc7d1b339b0ee"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f4c86a6371082db0be8087b17a31bb79d35fb1d1"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="83760d213fb3bec7b4117d266fcfbf6fe2ba14ab"/>
   <project name="device/common" path="device/common" revision="6a2995683de147791e516aae2ccb31fdfbe2ad30"/>
@@ -129,16 +129,16 @@
   <project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
   <!-- dolphin specific things -->
   <project name="device/sprd" path="device/sprd" revision="a26ba0ab998133ad590102be1e5950818b86ce82"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="39a5b5bcadad745df3e6882316fce426f98b1669"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="8bb69db127112fc66da75f8ca7a1158614b919f6"/>
   <project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
   <project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
-  <project name="kernel/common" path="kernel" revision="df636d2c31ad4434a7de2565359ad982b3767118"/>
-  <project name="platform/system/core" path="system/core" revision="3747764c63d23de586c56e1cef6dca2cdd90b2ea"/>
+  <project name="kernel/common" path="kernel" revision="65d2a18bd4ab0f1ed36a76c1e3c4f9ae98f345b9"/>
+  <project name="platform/system/core" path="system/core" revision="7992618bd4ee33ce96897675a5c0a9b619122f13"/>
   <project name="u-boot" path="u-boot" revision="f1502910977ac88f43da7bf9277c3523ad4b0b2f"/>
   <project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="4c59900937dc2e978b7b14b7f1ea617e3d5d550e"/>
   <project name="vendor/sprd/open-source" path="vendor/sprd/open-source" revision="e503b1d14d7fdee532b8f391407299da193c1b2d"/>
   <project name="vendor/sprd/partner" path="vendor/sprd/partner" revision="8649c7145972251af11b0639997edfecabfc7c2e"/>
   <project name="vendor/sprd/proprietories" path="vendor/sprd/proprietories" revision="d2466593022f7078aaaf69026adf3367c2adb7bb"/>
 </manifest>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="46e1877c0d88b085f7ebc5f432d5bb8f1e2d1f3b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c7d4045742862a7cf3f0074902ebc7d1b339b0ee"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f4c86a6371082db0be8087b17a31bb79d35fb1d1"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
@@ -130,12 +130,12 @@
   <project name="android-development" path="development" remote="b2g" revision="dab55669da8f48b6e57df95d5af9f16b4a87b0b1"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="197cd9492b9fadaa915c5daf36ff557f8f4a8d1c"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="libnfcemu" path="external/libnfcemu" remote="b2g" revision="125ccf9bd5986c7728ea44508b3e1d1185ac028b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c5f8d282efe4a4e8b1e31a37300944e338e60e4f"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb8b8828c412bf8d164ad42a39766becd39f9fb5"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d82e00fb6380b4f6cea7a96213913ee9eb441239"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c7d4045742862a7cf3f0074902ebc7d1b339b0ee"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f4c86a6371082db0be8087b17a31bb79d35fb1d1"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="1950e4760fa14688b83cdbb5acaa1af9f82ef434"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="ac6eb97a37035c09fb5ede0852f0881e9aadf9ad"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="737f591c5f95477148d26602c7be56cbea0cdeb9"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="51da9b1981be481b92a59a826d4d78dc73d0989a"/>
   <project name="device/common" path="device/common" revision="798a3664597e6041985feab9aef42e98d458bc3d"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="6b0721ca0e92788df30daf8f7a5fb2863544f9c8">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c7d4045742862a7cf3f0074902ebc7d1b339b0ee"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f4c86a6371082db0be8087b17a31bb79d35fb1d1"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="46e1877c0d88b085f7ebc5f432d5bb8f1e2d1f3b"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="ef937d1aca7c4cf89ecb5cc43ae8c21c2000a9db">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c7d4045742862a7cf3f0074902ebc7d1b339b0ee"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f4c86a6371082db0be8087b17a31bb79d35fb1d1"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
@@ -141,13 +141,13 @@
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="2a1ded216a91bf62a72b1640cf01ab4998f37028"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="a74adcf8d88320d936daa8d20ce88ca0107fb916"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="9883ea57b0668d8f60dba025d4522dfa69a1fbfa"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="a558dc844bf5144fc38603fd8f4df8d9557052a5"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="57ee1320ed7b4a1a1274d8f3f6c177cd6b9becb2"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
   <project name="platform/system/core" path="system/core" revision="42839aedcf70bf6bc92a3b7ea4a5cc9bf9aef3f9"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb8b8828c412bf8d164ad42a39766becd39f9fb5"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d82e00fb6380b4f6cea7a96213913ee9eb441239"/>
   <project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4c187c1f3a0dffd8e51a961735474ea703535b99"/>
 </manifest>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c7d4045742862a7cf3f0074902ebc7d1b339b0ee"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f4c86a6371082db0be8087b17a31bb79d35fb1d1"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
@@ -140,13 +140,13 @@
   <project name="platform/hardware/qcom/camera" path="hardware/qcom/camera" revision="5e110615212302c5d798a3c223dcee458817651c"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="fa9ffd47948eb24466de227e48fe9c4a7c5e7711"/>
   <project name="platform/hardware/qcom/gps" path="hardware/qcom/gps" revision="cd76b19aafd4229ccf83853d02faef8c51ca8b34"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="8a0d0b0d9889ef99c4c6317c810db4c09295f15a"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="2208fa3537ace873b8f9ec2355055761c79dfd5f"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
   <project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
   <project name="platform/system/core" path="system/core" revision="adc485d8755af6a61641d197de7cfef667722580"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb8b8828c412bf8d164ad42a39766becd39f9fb5"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d82e00fb6380b4f6cea7a96213913ee9eb441239"/>
   <project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
   <project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
   <project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>
 </manifest>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "0645bbed4d6cbd8064652eebafe011edc3e417fd", 
+        "git_revision": "15134b080b5f406e5aa36f5136c17dafb4e31f64", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "b8a031b1d0d2dfe6dc33194ec18b25c6f0cd625a", 
+    "revision": "457b84a122653f399cb817513b5149c4c087afe9", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c7d4045742862a7cf3f0074902ebc7d1b339b0ee"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f4c86a6371082db0be8087b17a31bb79d35fb1d1"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
@@ -125,17 +125,17 @@
   <project name="platform/system/netd" path="system/netd" revision="56112dd7b811301b718d0643a82fd5cac9522073"/>
   <project name="platform/system/security" path="system/security" revision="f48ff68fedbcdc12b570b7699745abb6e7574907"/>
   <project name="platform/system/vold" path="system/vold" revision="8de05d4a52b5a91e7336e6baa4592f945a6ddbea"/>
   <default remote="caf" revision="refs/tags/android-4.3_r2.1" sync-j="4"/>
   <!-- Nexus 4 specific things -->
   <project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
   <project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb8b8828c412bf8d164ad42a39766becd39f9fb5"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d82e00fb6380b4f6cea7a96213913ee9eb441239"/>
   <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
   <project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>
   <project name="platform/hardware/qcom/audio" path="hardware/qcom/audio" revision="b0a528d839cfd9d170d092fe3743b5252b4243a6"/>
   <project name="platform/hardware/qcom/bt" path="hardware/qcom/bt" revision="380945eaa249a2dbdde0daa4c8adb8ca325edba6"/>
   <project name="platform/hardware/qcom/display" path="hardware/qcom/display" revision="6f3b0272cefaffeaed2a7d2bb8f633059f163ddc"/>
   <project name="platform/hardware/qcom/keymaster" path="hardware/qcom/keymaster" revision="16da8262c997a5a0d797885788a64a0771b26910"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="689b476ba3eb46c34b81343295fe144a0e81a18e"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="6b0721ca0e92788df30daf8f7a5fb2863544f9c8">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="0645bbed4d6cbd8064652eebafe011edc3e417fd"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="15134b080b5f406e5aa36f5136c17dafb4e31f64"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="f2f2f0cbee2f2517070dd194051d509c07cdacff"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="c7d4045742862a7cf3f0074902ebc7d1b339b0ee"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="f4c86a6371082db0be8087b17a31bb79d35fb1d1"/>
   <!-- Stock Android things -->
   <project groups="pdk,linux" name="platform/prebuilts/clang/linux-x86/host/3.5" path="prebuilts/clang/linux-x86/host/3.5" revision="ffc05a232799fe8fcb3e47b7440b52b1fb4244c0"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" path="prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.8" revision="337e0ef5e40f02a1ae59b90db0548976c70a7226"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.8" revision="8af5ff6f5dced9eb5a8127459df6c75d24342204"/>
   <project groups="pdk,linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.8" revision="30915518fa7ea07166efedc191a4f40aef516fe7"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.6" revision="96eee58e3389fb05a835310d6a06a6ba4486097a"/>
   <project groups="pdk,linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.11-4.8" revision="7c8a46698171aa2e0be09edb43d15a6acf832770"/>
   <project groups="pdk,linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" path="prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.8" revision="24b2038be8a636fd4a5d21f0abae1e466b07bcf7"/>
@@ -151,10 +151,10 @@
   <project name="hardware_qcom_display" path="hardware/qcom/display" remote="b2g" revision="d8a56d7215bd26a61e43dcde20e64826a5fec265"/>
   <project name="platform/hardware/qcom/keymaster" path="hardware/qcom/keymaster" revision="eaede9f8bc206736a889bc57817047c31e205589"/>
   <project name="platform/hardware/qcom/media" path="hardware/qcom/media" revision="12364db20d6710f0003a4f00962c9790ad3c13e3"/>
   <project name="platform/hardware/qcom/msm8x74" path="hardware/qcom/msm8x74" revision="f09920b2b488cf68bcbe9f7bbbde84a509a1d7a5"/>
   <project name="platform/hardware/qcom/power" path="hardware/qcom/power" revision="29e9aeaf278b16fea4cb1ab4d05b8b8c9083c15b"/>
   <project name="platform/hardware/qcom/sensors" path="hardware/qcom/sensors" revision="fde83fdf67e9b919f8a49008725bd595221bf33f"/>
   <project name="platform/hardware/qcom/wlan" path="hardware/qcom/wlan" revision="6417804bea95f6e46094a01a06025a86e28c5b0d"/>
   <project name="platform/hardware/ril" path="hardware/ril" revision="e00d716e7e3d31729f75399855b6921e90cb0b66"/>
-  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="eb8b8828c412bf8d164ad42a39766becd39f9fb5"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d82e00fb6380b4f6cea7a96213913ee9eb441239"/>
 </manifest>
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -322,17 +322,19 @@
 @RESPATH@/components/spellchecker.xpt
 @RESPATH@/components/storage.xpt
 @RESPATH@/components/telemetry.xpt
 @RESPATH@/components/toolkit_asyncshutdown.xpt
 @RESPATH@/components/toolkit_filewatcher.xpt
 @RESPATH@/components/toolkit_finalizationwitness.xpt
 @RESPATH@/components/toolkit_formautofill.xpt
 @RESPATH@/components/toolkit_osfile.xpt
+#ifdef NIGHTLY_BUILD
 @RESPATH@/components/toolkit_perfmonitoring.xpt
+#endif
 @RESPATH@/components/toolkit_xulstore.xpt
 @RESPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @RESPATH@/components/toolkitremote.xpt
 #endif
 @RESPATH@/components/txtsvc.xpt
 @RESPATH@/components/txmgr.xpt
 #ifdef MOZ_USE_NATIVE_UCONV
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -1175,18 +1175,18 @@ nsContextMenu.prototype = {
 
   // Save URL of clicked-on frame.
   saveFrame: function () {
     saveDocument(this.target.ownerDocument);
   },
 
   // Helper function to wait for appropriate MIME-type headers and
   // then prompt the user with a file picker
-  saveHelper: function(linkURL, linkText, dialogTitle, bypassCache, doc,
-                       linkDownload) {
+  saveHelper: function(linkURL, linkText, dialogTitle, bypassCache, doc, docURI,
+                       windowID, linkDownload) {
     // canonical def in nsURILoader.h
     const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020;
 
     // an object to proxy the data through to
     // nsIExternalHelperAppService.doContent, which will wait for the
     // appropriate MIME-type headers and then prompt the user with a
     // file picker
     function saveAsListener() {}
@@ -1211,17 +1211,20 @@ nsContextMenu.prototype = {
             const bundle = sbs.createBundle(
                     "chrome://mozapps/locale/downloads/downloads.properties");
 
             const title = bundle.GetStringFromName("downloadErrorAlertTitle");
             const msg = bundle.GetStringFromName("downloadErrorGeneric");
 
             const promptSvc = Cc["@mozilla.org/embedcomp/prompt-service;1"].
                               getService(Ci.nsIPromptService);
-            promptSvc.alert(doc.defaultView, title, msg);
+            const wm = Cc["@mozilla.org/appshell/window-mediator;1"].
+                       getService(Ci.nsIWindowMediator);
+            let window = wm.getOuterWindowWithId(windowID);
+            promptSvc.alert(window, title, msg);
           } catch (ex) {}
           return;
         }
 
         let extHelperAppSvc = 
           Cc["@mozilla.org/uriloader/external-helper-app-service;1"].
           getService(Ci.nsIExternalHelperAppService);
         let channel = aRequest.QueryInterface(Ci.nsIChannel);
@@ -1231,18 +1234,18 @@ nsContextMenu.prototype = {
         this.extListener.onStartRequest(aRequest, aContext);
       }, 
 
       onStopRequest: function saveLinkAs_onStopRequest(aRequest, aContext, 
                                                        aStatusCode) {
         if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
           // do it the old fashioned way, which will pick the best filename
           // it can without waiting.
-          saveURL(linkURL, linkText, dialogTitle, bypassCache, false,
-                  BrowserUtils.makeURIFromCPOW(doc.documentURIObject), doc);
+          saveURL(linkURL, linkText, dialogTitle, bypassCache, false, docURI,
+                  doc);
         }
         if (this.extListener)
           this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
       },
 
       onDataAvailable: function saveLinkAs_onDataAvailable(aRequest, aContext,
                                                            aInputStream,
                                                            aOffset, aCount) {
@@ -1301,17 +1304,17 @@ nsContextMenu.prototype = {
       flags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
 
     if (channel instanceof Ci.nsICachingChannel)
       flags |= Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
 
     channel.loadFlags |= flags;
 
     if (channel instanceof Ci.nsIHttpChannel) {
-      channel.referrer = BrowserUtils.makeURIFromCPOW(doc.documentURIObject);
+      channel.referrer = docURI;
       if (channel instanceof Ci.nsIHttpChannelInternal)
         channel.forceAllowThirdPartyCookie = true;
     }
 
     // fallback to the old way if we don't see the headers quickly 
     var timeToWait = 
       gPrefService.getIntPref("browser.download.saveLinkAsFilenameTimeout");
     var timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
@@ -1321,45 +1324,47 @@ nsContextMenu.prototype = {
     // kick off the channel with our proxy object as the listener
     channel.asyncOpen(new saveAsListener(), null);
   },
 
   // Save URL of clicked-on link.
   saveLink: function() {
     urlSecurityCheck(this.linkURL, this.principal);
     this.saveHelper(this.linkURL, this.linkText, null, true, this.ownerDoc,
+                    gContextMenuContentData.documentURIObject,
+                    gContextMenuContentData.frameOuterWindowID,
                     this.linkDownload);
   },
 
   // Backwards-compatibility wrapper
   saveImage : function() {
     if (this.onCanvas || this.onImage)
         this.saveMedia();
   },
 
   // Save URL of the clicked upon image, video, or audio.
   saveMedia: function() {
     var doc =  this.target.ownerDocument;
+    let referrerURI = gContextMenuContentData.documentURIObject;
     if (this.onCanvas) {
       // Bypass cache, since it's a data: URL.
       saveImageURL(this.target.toDataURL(), "canvas.png", "SaveImageTitle",
-                   true, false, gContextMenuContentData.documentURIObject,
-                   doc);
+                   true, false, referrerURI, doc);
     }
     else if (this.onImage) {
       urlSecurityCheck(this.mediaURL, this.principal);
-      let uri = gContextMenuContentData.documentURIObject;
       saveImageURL(this.mediaURL, null, "SaveImageTitle", false,
-                   false, uri, doc, gContextMenuContentData.contentType,
+                   false, referrerURI, doc, gContextMenuContentData.contentType,
                    gContextMenuContentData.contentDisposition);
     }
     else if (this.onVideo || this.onAudio) {
       urlSecurityCheck(this.mediaURL, this.principal);
       var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
-      this.saveHelper(this.mediaURL, null, dialogTitle, false, doc, "");
+      this.saveHelper(this.mediaURL, null, dialogTitle, false, doc, referrerURI,
+                      gContextMenuContentData.frameOuterWindowID, "");
     }
   },
 
   // Backwards-compatibility wrapper
   sendImage : function() {
     if (this.onCanvas || this.onImage)
         this.sendMedia();
   },
@@ -1657,17 +1662,17 @@ nsContextMenu.prototype = {
     SocialShare.sharePage(null, { url: this.browser.currentURI.spec, text: this.textSelected }, this.target);
   },
 
   savePageAs: function CM_savePageAs() {
     saveDocument(this.browser.contentDocumentAsCPOW);
   },
 
   printFrame: function CM_printFrame() {
-    PrintUtils.print(this.target.ownerDocument.defaultView);
+    PrintUtils.print(this.target.ownerDocument.defaultView, this.browser);
   },
 
   switchPageDirection: function CM_switchPageDirection() {
     this.browser.messageManager.sendAsyncMessage("SwitchDocumentDirection");
   },
 
   mediaCommand : function CM_mediaCommand(command, data) {
     let mm = this.browser.messageManager;
--- a/browser/base/content/test/social/browser_addons.js
+++ b/browser/base/content/test/social/browser_addons.js
@@ -1,13 +1,10 @@
-
-
 let AddonManager = Cu.import("resource://gre/modules/AddonManager.jsm", {}).AddonManager;
 let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
-let AddonWatcher = Cu.import("resource://gre/modules/AddonWatcher.jsm", {}).AddonWatcher;
 
 const ADDON_TYPE_SERVICE     = "service";
 const ID_SUFFIX              = "@services.mozilla.org";
 const STRING_TYPE_NAME       = "type.%ID%.name";
 const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
 
 let manifest = {
   name: "provider 1",
--- a/browser/components/loop/content/js/conversation.js
+++ b/browser/components/loop/content/js/conversation.js
@@ -44,24 +44,23 @@ loop.conversation = (function(mozL10n) {
 
     render: function() {
       switch(this.state.windowType) {
         // CallControllerView is used for both.
         case "incoming":
         case "outgoing": {
           return (React.createElement(CallControllerView, {
             dispatcher: this.props.dispatcher, 
-            mozLoop: this.props.mozLoop}
-          ));
+            mozLoop: this.props.mozLoop}));
         }
         case "room": {
           return (React.createElement(DesktopRoomConversationView, {
             dispatcher: this.props.dispatcher, 
-            roomStore: this.props.roomStore}
-          ));
+            mozLoop: this.props.mozLoop, 
+            roomStore: this.props.roomStore}));
         }
         case "failed": {
           return React.createElement(GenericFailureView, {cancelCall: this.closeWindow});
         }
         default: {
           // If we don't have a windowType, we don't know what we are yet,
           // so don't display anything.
           return null;
--- a/browser/components/loop/content/js/conversation.jsx
+++ b/browser/components/loop/content/js/conversation.jsx
@@ -44,24 +44,23 @@ loop.conversation = (function(mozL10n) {
 
     render: function() {
       switch(this.state.windowType) {
         // CallControllerView is used for both.
         case "incoming":
         case "outgoing": {
           return (<CallControllerView
             dispatcher={this.props.dispatcher}
-            mozLoop={this.props.mozLoop}
-          />);
+            mozLoop={this.props.mozLoop} />);
         }
         case "room": {
           return (<DesktopRoomConversationView
             dispatcher={this.props.dispatcher}
-            roomStore={this.props.roomStore}
-          />);
+            mozLoop={this.props.mozLoop}
+            roomStore={this.props.roomStore} />);
         }
         case "failed": {
           return <GenericFailureView cancelCall={this.closeWindow} />;
         }
         default: {
           // If we don't have a windowType, we don't know what we are yet,
           // so don't display anything.
           return null;
--- a/browser/components/loop/content/js/conversationViews.js
+++ b/browser/components/loop/content/js/conversationViews.js
@@ -223,17 +223,16 @@ loop.conversationViews = (function(mozL1
         props.primary = audioButton;
         props.secondary = videoButton;
       }
 
       return props;
     },
 
     render: function() {
-      /* jshint ignore:start */
       var dropdownMenuClassesDecline = React.addons.classSet({
         "native-dropdown-menu": true,
         "conversation-window-dropdown": true,
         "visually-hidden": !this.state.showMenu
       });
 
       return (
         React.createElement("div", {className: "call-window"}, 
@@ -271,51 +270,48 @@ loop.conversationViews = (function(mozL1
 
             React.createElement(AcceptCallButton, {mode: this._answerModeProps()}), 
 
             React.createElement("div", {className: "fx-embedded-call-button-spacer"})
 
           )
         )
       );
-      /* jshint ignore:end */
     }
   });
 
   /**
    * Incoming call view accept button, renders different primary actions
    * (answer with video / with audio only) based on the props received
    **/
   var AcceptCallButton = React.createClass({displayName: "AcceptCallButton",
 
     propTypes: {
       mode: React.PropTypes.object.isRequired,
     },
 
     render: function() {
       var mode = this.props.mode;
       return (
-        /* jshint ignore:start */
         React.createElement("div", {className: "btn-chevron-menu-group"}, 
           React.createElement("div", {className: "btn-group"}, 
             React.createElement("button", {className: "btn btn-accept", 
                     onClick: mode.primary.handler, 
                     title: mozL10n.get(mode.primary.tooltip)}, 
               React.createElement("span", {className: "fx-embedded-answer-btn-text"}, 
                 mozL10n.get("incoming_call_accept_button")
               ), 
               React.createElement("span", {className: mode.primary.className})
             ), 
             React.createElement("div", {className: mode.secondary.className, 
                  onClick: mode.secondary.handler, 
                  title: mozL10n.get(mode.secondary.tooltip)}
             )
           )
         )
-        /* jshint ignore:end */
       );
     }
   });
 
   /**
    * Something went wrong view. Displayed when there's a big problem.
    */
   var GenericFailureView = React.createClass({displayName: "GenericFailureView",
--- a/browser/components/loop/content/js/conversationViews.jsx
+++ b/browser/components/loop/content/js/conversationViews.jsx
@@ -223,17 +223,16 @@ loop.conversationViews = (function(mozL1
         props.primary = audioButton;
         props.secondary = videoButton;
       }
 
       return props;
     },
 
     render: function() {
-      /* jshint ignore:start */
       var dropdownMenuClassesDecline = React.addons.classSet({
         "native-dropdown-menu": true,
         "conversation-window-dropdown": true,
         "visually-hidden": !this.state.showMenu
       });
 
       return (
         <div className="call-window">
@@ -271,51 +270,48 @@ loop.conversationViews = (function(mozL1
 
             <AcceptCallButton mode={this._answerModeProps()} />
 
             <div className="fx-embedded-call-button-spacer"></div>
 
           </div>
         </div>
       );
-      /* jshint ignore:end */
     }
   });
 
   /**
    * Incoming call view accept button, renders different primary actions
    * (answer with video / with audio only) based on the props received
    **/
   var AcceptCallButton = React.createClass({
 
     propTypes: {
       mode: React.PropTypes.object.isRequired,
     },
 
     render: function() {
       var mode = this.props.mode;
       return (
-        /* jshint ignore:start */
         <div className="btn-chevron-menu-group">
           <div className="btn-group">
             <button className="btn btn-accept"
                     onClick={mode.primary.handler}
                     title={mozL10n.get(mode.primary.tooltip)}>
               <span className="fx-embedded-answer-btn-text">
                 {mozL10n.get("incoming_call_accept_button")}
               </span>
               <span className={mode.primary.className}></span>
             </button>
             <div className={mode.secondary.className}
                  onClick={mode.secondary.handler}
                  title={mozL10n.get(mode.secondary.tooltip)}>
             </div>
           </div>
         </div>
-        /* jshint ignore:end */
       );
     }
   });
 
   /**
    * Something went wrong view. Displayed when there's a big problem.
    */
   var GenericFailureView = React.createClass({
--- a/browser/components/loop/content/js/roomViews.js
+++ b/browser/components/loop/content/js/roomViews.js
@@ -26,46 +26,58 @@ loop.roomViews = (function(mozL10n) {
 
     propTypes: {
       roomStore: React.PropTypes.instanceOf(loop.store.RoomStore).isRequired
     },
 
     componentWillMount: function() {
       this.listenTo(this.props.roomStore, "change:activeRoom",
                     this._onActiveRoomStateChanged);
+      this.listenTo(this.props.roomStore, "change:error",
+                    this._onRoomError);
     },
 
     componentWillUnmount: function() {
       this.stopListening(this.props.roomStore);
     },
 
     _onActiveRoomStateChanged: function() {
       // Only update the state if we're mounted, to avoid the problem where
       // stopListening doesn't nuke the active listeners during a event
       // processing.
       if (this.isMounted()) {
         this.setState(this.props.roomStore.getStoreState("activeRoom"));
       }
     },
 
+    _onRoomError: function() {
+      // Only update the state if we're mounted, to avoid the problem where
+      // stopListening doesn't nuke the active listeners during a event
+      // processing.
+      if (this.isMounted()) {
+        this.setState({error: this.props.roomStore.getStoreState("error")});
+      }
+    },
+
     getInitialState: function() {
       var storeState = this.props.roomStore.getStoreState("activeRoom");
       return _.extend({
         // Used by the UI showcase.
         roomState: this.props.roomState || storeState.roomState
       }, storeState);
     }
   };
 
   var SocialShareDropdown = React.createClass({displayName: "SocialShareDropdown",
-    mixins: [ActiveRoomStoreMixin],
-
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      show: React.PropTypes.bool.isRequired
+      roomUrl: React.PropTypes.string,
+      show: React.PropTypes.bool.isRequired,
+      socialShareButtonAvailable: React.PropTypes.bool,
+      socialShareProviders: React.PropTypes.array
     },
 
     handleToolbarAddButtonClick: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(new sharedActions.AddSocialShareButton());
     },
 
@@ -74,44 +86,44 @@ loop.roomViews = (function(mozL10n) {
 
       this.props.dispatcher.dispatch(new sharedActions.AddSocialShareProvider());
     },
 
     handleProviderClick: function(event) {
       event.preventDefault();
 
       var origin = event.currentTarget.dataset.provider;
-      var provider = this.state.socialShareProviders.filter(function(provider) {
+      var provider = this.props.socialShareProviders.filter(function(provider) {
         return provider.origin == origin;
       })[0];
 
       this.props.dispatcher.dispatch(new sharedActions.ShareRoomUrl({
         provider: provider,
-        roomUrl: this.state.roomUrl,
+        roomUrl: this.props.roomUrl,
         previews: []
       }));
     },
 
     render: function() {
       // Don't render a thing when no data has been fetched yet.
-      if (!this.state.socialShareProviders) {
+      if (!this.props.socialShareProviders) {
         return null;
       }
 
       var cx = React.addons.classSet;
       var shareDropdown = cx({
         "share-service-dropdown": true,
         "dropdown-menu": true,
-        "share-button-unavailable": !this.state.socialShareButtonAvailable,
+        "share-button-unavailable": !this.props.socialShareButtonAvailable,
         "hide": !this.props.show
       });
 
       // When the button is not yet available, we offer to put it in the navbar
       // for the user.
-      if (!this.state.socialShareButtonAvailable) {
+      if (!this.props.socialShareButtonAvailable) {
         return (
           React.createElement("div", {className: shareDropdown}, 
             React.createElement("div", {className: "share-panel-header"}, 
               mozL10n.get("share_panel_header")
             ), 
             React.createElement("div", {className: "share-panel-body"}, 
               
                 mozL10n.get("share_panel_body", {
@@ -129,19 +141,19 @@ loop.roomViews = (function(mozL10n) {
       }
 
       return (
         React.createElement("ul", {className: shareDropdown}, 
           React.createElement("li", {className: "dropdown-menu-item", onClick: this.handleAddServiceClick}, 
             React.createElement("i", {className: "icon icon-add-share-service"}), 
             React.createElement("span", null, mozL10n.get("share_add_service_button"))
           ), 
-          this.state.socialShareProviders.length ? React.createElement("li", {className: "dropdown-menu-separator"}) : null, 
+          this.props.socialShareProviders.length ? React.createElement("li", {className: "dropdown-menu-separator"}) : null, 
           
-            this.state.socialShareProviders.map(function(provider, idx) {
+            this.props.socialShareProviders.map(function(provider, idx) {
               return (
                 React.createElement("li", {className: "dropdown-menu-item", 
                     key: "provider-" + idx, 
                     "data-provider": provider.origin, 
                     onClick: this.handleProviderClick}, 
                   React.createElement("img", {className: "icon", src: provider.iconURL}), 
                   React.createElement("span", null, provider.name)
                 )
@@ -152,126 +164,169 @@ loop.roomViews = (function(mozL10n) {
       );
     }
   });
 
   /**
    * Desktop room invitation view (overlay).
    */
   var DesktopRoomInvitationView = React.createClass({displayName: "DesktopRoomInvitationView",
-    mixins: [ActiveRoomStoreMixin, React.addons.LinkedStateMixin,
-             sharedMixins.DropdownMenuMixin],
+    mixins: [React.addons.LinkedStateMixin, sharedMixins.DropdownMenuMixin],
 
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      error: React.PropTypes.object,
+      // This data is supplied by the activeRoomStore.
+      roomData: React.PropTypes.object.isRequired,
+      show: React.PropTypes.bool.isRequired,
+      showContext: React.PropTypes.bool.isRequired
     },
 
     getInitialState: function() {
       return {
         copiedUrl: false,
-        newRoomName: "",
-        error: null,
+        newRoomName: ""
       };
     },
 
-    componentWillMount: function() {
-      this.listenTo(this.props.roomStore, "change:error",
-                    this.onRoomError);
-    },
-
-    componentWillUnmount: function() {
-      this.stopListening(this.props.roomStore);
-    },
-
     handleTextareaKeyDown: function(event) {
       // Submit the form as soon as the user press Enter in that field
       // Note: We're using a textarea instead of a simple text input to display
       // placeholder and entered text on two lines, to circumvent l10n
       // rendering/UX issues for some locales.
       if (event.which === 13) {
         this.handleFormSubmit(event);
       }
     },
 
     handleFormSubmit: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
-        roomToken: this.state.roomToken,
+        roomToken: this.props.roomData.roomToken,
         newRoomName: this.state.newRoomName
       }));
     },
 
     handleEmailButtonClick: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(
-        new sharedActions.EmailRoomUrl({roomUrl: this.state.roomUrl}));
+        new sharedActions.EmailRoomUrl({roomUrl: this.props.roomData.roomUrl}));
     },
 
     handleCopyButtonClick: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(
-        new sharedActions.CopyRoomUrl({roomUrl: this.state.roomUrl}));
+        new sharedActions.CopyRoomUrl({roomUrl: this.props.roomData.roomUrl}));
 
       this.setState({copiedUrl: true});
     },
 
     handleShareButtonClick: function(event) {
       event.preventDefault();
 
       this.toggleDropdownMenu();
     },
 
-    onRoomError: function() {
-      // Only update the state if we're mounted, to avoid the problem where
-      // stopListening doesn't nuke the active listeners during a event
-      // processing.
-      if (this.isMounted()) {
-        this.setState({error: this.props.roomStore.getStoreState("error")});
+    render: function() {
+      if (!this.props.show) {
+        return null;
       }
-    },
 
-    render: function() {
       var cx = React.addons.classSet;
       return (
         React.createElement("div", {className: "room-invitation-overlay"}, 
-          React.createElement("p", {className: cx({"error": !!this.state.error,
-                            "error-display-area": true})}, 
-            mozL10n.get("rooms_name_change_failed_label")
-          ), 
-          React.createElement("form", {onSubmit: this.handleFormSubmit}, 
-            React.createElement("textarea", {rows: "2", type: "text", className: "input-room-name", 
-              valueLink: this.linkState("newRoomName"), 
-              onBlur: this.handleFormSubmit, 
-              onKeyDown: this.handleTextareaKeyDown, 
-              placeholder: mozL10n.get("rooms_name_this_room_label")})
+          React.createElement("div", {className: "room-invitation-content"}, 
+            React.createElement("p", {className: cx({"error": !!this.props.error,
+                              "error-display-area": true})}, 
+              mozL10n.get("rooms_name_change_failed_label")
+            ), 
+            React.createElement("form", {onSubmit: this.handleFormSubmit}, 
+              React.createElement("textarea", {rows: "2", type: "text", className: "input-room-name", 
+                valueLink: this.linkState("newRoomName"), 
+                onBlur: this.handleFormSubmit, 
+                onKeyDown: this.handleTextareaKeyDown, 
+                placeholder: mozL10n.get("rooms_name_this_room_label")})
+            ), 
+            React.createElement("p", null, mozL10n.get("invite_header_text")), 
+            React.createElement("div", {className: "btn-group call-action-group"}, 
+              React.createElement("button", {className: "btn btn-info btn-email", 
+                      onClick: this.handleEmailButtonClick}, 
+                mozL10n.get("email_link_button")
+              ), 
+              React.createElement("button", {className: "btn btn-info btn-copy", 
+                      onClick: this.handleCopyButtonClick}, 
+                this.state.copiedUrl ? mozL10n.get("copied_url_button") :
+                                        mozL10n.get("copy_url_button2")
+              ), 
+              React.createElement("button", {className: "btn btn-info btn-share", 
+                      ref: "anchor", 
+                      onClick: this.handleShareButtonClick}, 
+                mozL10n.get("share_button3")
+              )
+            ), 
+            React.createElement(SocialShareDropdown, {dispatcher: this.props.dispatcher, 
+                                 roomUrl: this.props.roomData.roomUrl, 
+                                 show: this.state.showMenu, 
+                                 ref: "menu"})
           ), 
-          React.createElement("p", null, mozL10n.get("invite_header_text")), 
-          React.createElement("div", {className: "btn-group call-action-group"}, 
-            React.createElement("button", {className: "btn btn-info btn-email", 
-                    onClick: this.handleEmailButtonClick}, 
-              mozL10n.get("email_link_button")
-            ), 
-            React.createElement("button", {className: "btn btn-info btn-copy", 
-                    onClick: this.handleCopyButtonClick}, 
-              this.state.copiedUrl ? mozL10n.get("copied_url_button") :
-                                      mozL10n.get("copy_url_button2")
-            ), 
-            React.createElement("button", {className: "btn btn-info btn-share", 
-                    ref: "anchor", 
-                    onClick: this.handleShareButtonClick}, 
-              mozL10n.get("share_button3")
-            )
-          ), 
-          React.createElement(SocialShareDropdown, {dispatcher: this.props.dispatcher, 
-                               roomStore: this.props.roomStore, 
-                               show: this.state.showMenu, 
-                               ref: "menu"})
+          React.createElement(DesktopRoomContextView, {
+            roomData: this.props.roomData, 
+            show: this.props.showContext})
+        )
+      );
+    }
+  });
+
+  var DesktopRoomContextView = React.createClass({displayName: "DesktopRoomContextView",
+    propTypes: {
+      // This data is supplied by the activeRoomStore.
+      roomData: React.PropTypes.object.isRequired,
+      show: React.PropTypes.bool.isRequired
+    },
+
+    componentWillReceiveProps: function(nextProps) {
+      // When the 'show' prop is changed from outside this component, we do need
+      // to update the state.
+      if (("show" in nextProps) && nextProps.show !== this.props.show) {
+        this.setState({ show: nextProps.show });
+      }
+    },
+
+    getInitialState: function() {
+      return { show: this.props.show };
+    },
+
+    handleCloseClick: function() {
+      this.setState({ show: false });
+    },
+
+    render: function() {
+      if (!this.state.show)
+        return null;
+
+      var URL = this.props.roomData.roomContextUrls && this.props.roomData.roomContextUrls[0];
+      var thumbnail = URL && URL.thumbnail || "";
+      var URLDescription = URL && URL.description || "";
+      var location = URL && URL.location || "";
+      return (
+        React.createElement("div", {className: "room-context"}, 
+          React.createElement("img", {className: "room-context-thumbnail", src: thumbnail}), 
+          React.createElement("div", {className: "room-context-content"}, 
+            React.createElement("div", {className: "room-context-label"}, mozL10n.get("context_inroom_label")), 
+            React.createElement("div", {className: "room-context-description"}, URLDescription), 
+            React.createElement("a", {className: "room-context-url", href: location, target: "_blank"}, location), 
+            this.props.roomData.roomDescription ?
+              React.createElement("div", {className: "room-context-comment"}, this.props.roomData.roomDescription) :
+              null, 
+            React.createElement("button", {className: "room-context-btn-close", 
+                    onClick: this.handleCloseClick})
+          )
         )
       );
     }
   });
 
   /**
    * Desktop room conversation view.
    */
@@ -280,28 +335,18 @@ loop.roomViews = (function(mozL10n) {
       ActiveRoomStoreMixin,
       sharedMixins.DocumentTitleMixin,
       sharedMixins.MediaSetupMixin,
       sharedMixins.RoomsAudioMixin,
       sharedMixins.WindowCloseMixin
     ],
 
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
-    },
-
-    _renderInvitationOverlay: function() {
-      if (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS) {
-        return (
-          React.createElement(DesktopRoomInvitationView, {
-            roomStore: this.props.roomStore, 
-            dispatcher: this.props.dispatcher})
-        );
-      }
-      return null;
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      mozLoop: React.PropTypes.object.isRequired
     },
 
     componentWillUpdate: function(nextProps, nextState) {
       // The SDK needs to know about the configuration and the elements to use
       // for display. So the best way seems to pass the information here - ideally
       // the sdk wouldn't need to know this, but we can't change that.
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
@@ -336,16 +381,27 @@ loop.roomViews = (function(mozL10n) {
     publishStream: function(type, enabled) {
       this.props.dispatcher.dispatch(
         new sharedActions.SetMute({
           type: type,
           enabled: enabled
         }));
     },
 
+    _shouldRenderInvitationOverlay: function() {
+      return (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS);
+    },
+
+    _shouldRenderContextView: function() {
+      return !!(
+        this.props.mozLoop.getLoopPref("contextInConverations.enabled") &&
+        (this.state.roomContextUrls || this.state.roomDescription)
+      );
+    },
+
     render: function() {
       if (this.state.roomName) {
         this.setTitle(this.state.roomName);
       }
 
       var localStreamClasses = React.addons.classSet({
         local: true,
         "local-stream": true,
@@ -353,16 +409,20 @@ loop.roomViews = (function(mozL10n) {
         "room-preview": this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS
       });
 
       var screenShareData = {
         state: this.state.screenSharingState,
         visible: true
       };
 
+      var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
+      var shouldRenderContextView = this._shouldRenderContextView();
+      var roomData = this.props.roomStore.getStoreState("activeRoom");
+
       switch(this.state.roomState) {
         case ROOM_STATES.FAILED:
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
           //       FULL case should never happen on desktop.
           return (
             React.createElement(loop.conversationViews.GenericFailureView, {
               cancelCall: this.closeWindow, 
@@ -373,17 +433,22 @@ loop.roomViews = (function(mozL10n) {
           return (
             React.createElement(sharedViews.FeedbackView, {
               onAfterFeedbackReceived: this.closeWindow})
           );
         }
         default: {
           return (
             React.createElement("div", {className: "room-conversation-wrapper"}, 
-              this._renderInvitationOverlay(), 
+              React.createElement(DesktopRoomInvitationView, {
+                dispatcher: this.props.dispatcher, 
+                error: this.state.error, 
+                roomData: roomData, 
+                show: shouldRenderInvitationOverlay, 
+                showContext: shouldRenderContextView}), 
               React.createElement("div", {className: "video-layout-wrapper"}, 
                 React.createElement("div", {className: "conversation room-conversation"}, 
                   React.createElement("div", {className: "media nested"}, 
                     React.createElement("div", {className: "video_wrapper remote_wrapper"}, 
                       React.createElement("div", {className: "video_inner remote focus-stream"})
                     ), 
                     React.createElement("div", {className: localStreamClasses}), 
                     React.createElement("div", {className: "screen hide"})
@@ -391,24 +456,28 @@ loop.roomViews = (function(mozL10n) {
                   React.createElement(sharedViews.ConversationToolbar, {
                     dispatcher: this.props.dispatcher, 
                     video: {enabled: !this.state.videoMuted, visible: true}, 
                     audio: {enabled: !this.state.audioMuted, visible: true}, 
                     publishStream: this.publishStream, 
                     hangup: this.leaveRoom, 
                     screenShare: screenShareData})
                 )
-              )
+              ), 
+              React.createElement(DesktopRoomContextView, {
+                roomData: roomData, 
+                show: !shouldRenderInvitationOverlay && shouldRenderContextView})
             )
           );
         }
       }
     }
   });
 
   return {
     ActiveRoomStoreMixin: ActiveRoomStoreMixin,
     SocialShareDropdown: SocialShareDropdown,
+    DesktopRoomContextView: DesktopRoomContextView,
     DesktopRoomConversationView: DesktopRoomConversationView,
     DesktopRoomInvitationView: DesktopRoomInvitationView
   };
 
 })(document.mozL10n || navigator.mozL10n);
--- a/browser/components/loop/content/js/roomViews.jsx
+++ b/browser/components/loop/content/js/roomViews.jsx
@@ -26,46 +26,58 @@ loop.roomViews = (function(mozL10n) {
 
     propTypes: {
       roomStore: React.PropTypes.instanceOf(loop.store.RoomStore).isRequired
     },
 
     componentWillMount: function() {
       this.listenTo(this.props.roomStore, "change:activeRoom",
                     this._onActiveRoomStateChanged);
+      this.listenTo(this.props.roomStore, "change:error",
+                    this._onRoomError);
     },
 
     componentWillUnmount: function() {
       this.stopListening(this.props.roomStore);
     },
 
     _onActiveRoomStateChanged: function() {
       // Only update the state if we're mounted, to avoid the problem where
       // stopListening doesn't nuke the active listeners during a event
       // processing.
       if (this.isMounted()) {
         this.setState(this.props.roomStore.getStoreState("activeRoom"));
       }
     },
 
+    _onRoomError: function() {
+      // Only update the state if we're mounted, to avoid the problem where
+      // stopListening doesn't nuke the active listeners during a event
+      // processing.
+      if (this.isMounted()) {
+        this.setState({error: this.props.roomStore.getStoreState("error")});
+      }
+    },
+
     getInitialState: function() {
       var storeState = this.props.roomStore.getStoreState("activeRoom");
       return _.extend({
         // Used by the UI showcase.
         roomState: this.props.roomState || storeState.roomState
       }, storeState);
     }
   };
 
   var SocialShareDropdown = React.createClass({
-    mixins: [ActiveRoomStoreMixin],
-
     propTypes: {
       dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
-      show: React.PropTypes.bool.isRequired
+      roomUrl: React.PropTypes.string,
+      show: React.PropTypes.bool.isRequired,
+      socialShareButtonAvailable: React.PropTypes.bool,
+      socialShareProviders: React.PropTypes.array
     },
 
     handleToolbarAddButtonClick: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(new sharedActions.AddSocialShareButton());
     },
 
@@ -74,44 +86,44 @@ loop.roomViews = (function(mozL10n) {
 
       this.props.dispatcher.dispatch(new sharedActions.AddSocialShareProvider());
     },
 
     handleProviderClick: function(event) {
       event.preventDefault();
 
       var origin = event.currentTarget.dataset.provider;
-      var provider = this.state.socialShareProviders.filter(function(provider) {
+      var provider = this.props.socialShareProviders.filter(function(provider) {
         return provider.origin == origin;
       })[0];
 
       this.props.dispatcher.dispatch(new sharedActions.ShareRoomUrl({
         provider: provider,
-        roomUrl: this.state.roomUrl,
+        roomUrl: this.props.roomUrl,
         previews: []
       }));
     },
 
     render: function() {
       // Don't render a thing when no data has been fetched yet.
-      if (!this.state.socialShareProviders) {
+      if (!this.props.socialShareProviders) {
         return null;
       }
 
       var cx = React.addons.classSet;
       var shareDropdown = cx({
         "share-service-dropdown": true,
         "dropdown-menu": true,
-        "share-button-unavailable": !this.state.socialShareButtonAvailable,
+        "share-button-unavailable": !this.props.socialShareButtonAvailable,
         "hide": !this.props.show
       });
 
       // When the button is not yet available, we offer to put it in the navbar
       // for the user.
-      if (!this.state.socialShareButtonAvailable) {
+      if (!this.props.socialShareButtonAvailable) {
         return (
           <div className={shareDropdown}>
             <div className="share-panel-header">
               {mozL10n.get("share_panel_header")}
             </div>
             <div className="share-panel-body">
               {
                 mozL10n.get("share_panel_body", {
@@ -129,19 +141,19 @@ loop.roomViews = (function(mozL10n) {
       }
 
       return (
         <ul className={shareDropdown}>
           <li className="dropdown-menu-item" onClick={this.handleAddServiceClick}>
             <i className="icon icon-add-share-service"></i>
             <span>{mozL10n.get("share_add_service_button")}</span>
           </li>
-          {this.state.socialShareProviders.length ? <li className="dropdown-menu-separator"/> : null}
+          {this.props.socialShareProviders.length ? <li className="dropdown-menu-separator"/> : null}
           {
-            this.state.socialShareProviders.map(function(provider, idx) {
+            this.props.socialShareProviders.map(function(provider, idx) {
               return (
                 <li className="dropdown-menu-item"
                     key={"provider-" + idx}
                     data-provider={provider.origin}
                     onClick={this.handleProviderClick}>
                   <img className="icon" src={provider.iconURL}/>
                   <span>{provider.name}</span>
                 </li>
@@ -152,126 +164,169 @@ loop.roomViews = (function(mozL10n) {
       );
     }
   });
 
   /**
    * Desktop room invitation view (overlay).
    */
   var DesktopRoomInvitationView = React.createClass({
-    mixins: [ActiveRoomStoreMixin, React.addons.LinkedStateMixin,
-             sharedMixins.DropdownMenuMixin],
+    mixins: [React.addons.LinkedStateMixin, sharedMixins.DropdownMenuMixin],
 
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      error: React.PropTypes.object,
+      // This data is supplied by the activeRoomStore.
+      roomData: React.PropTypes.object.isRequired,
+      show: React.PropTypes.bool.isRequired,
+      showContext: React.PropTypes.bool.isRequired
     },
 
     getInitialState: function() {
       return {
         copiedUrl: false,
-        newRoomName: "",
-        error: null,
+        newRoomName: ""
       };
     },
 
-    componentWillMount: function() {
-      this.listenTo(this.props.roomStore, "change:error",
-                    this.onRoomError);
-    },
-
-    componentWillUnmount: function() {
-      this.stopListening(this.props.roomStore);
-    },
-
     handleTextareaKeyDown: function(event) {
       // Submit the form as soon as the user press Enter in that field
       // Note: We're using a textarea instead of a simple text input to display
       // placeholder and entered text on two lines, to circumvent l10n
       // rendering/UX issues for some locales.
       if (event.which === 13) {
         this.handleFormSubmit(event);
       }
     },
 
     handleFormSubmit: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(new sharedActions.RenameRoom({
-        roomToken: this.state.roomToken,
+        roomToken: this.props.roomData.roomToken,
         newRoomName: this.state.newRoomName
       }));
     },
 
     handleEmailButtonClick: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(
-        new sharedActions.EmailRoomUrl({roomUrl: this.state.roomUrl}));
+        new sharedActions.EmailRoomUrl({roomUrl: this.props.roomData.roomUrl}));
     },
 
     handleCopyButtonClick: function(event) {
       event.preventDefault();
 
       this.props.dispatcher.dispatch(
-        new sharedActions.CopyRoomUrl({roomUrl: this.state.roomUrl}));
+        new sharedActions.CopyRoomUrl({roomUrl: this.props.roomData.roomUrl}));
 
       this.setState({copiedUrl: true});
     },
 
     handleShareButtonClick: function(event) {
       event.preventDefault();
 
       this.toggleDropdownMenu();
     },
 
-    onRoomError: function() {
-      // Only update the state if we're mounted, to avoid the problem where
-      // stopListening doesn't nuke the active listeners during a event
-      // processing.
-      if (this.isMounted()) {
-        this.setState({error: this.props.roomStore.getStoreState("error")});
+    render: function() {
+      if (!this.props.show) {
+        return null;
       }
-    },
 
-    render: function() {
       var cx = React.addons.classSet;
       return (
         <div className="room-invitation-overlay">
-          <p className={cx({"error": !!this.state.error,
-                            "error-display-area": true})}>
-            {mozL10n.get("rooms_name_change_failed_label")}
-          </p>
-          <form onSubmit={this.handleFormSubmit}>
-            <textarea rows="2" type="text" className="input-room-name"
-              valueLink={this.linkState("newRoomName")}
-              onBlur={this.handleFormSubmit}
-              onKeyDown={this.handleTextareaKeyDown}
-              placeholder={mozL10n.get("rooms_name_this_room_label")} />
-          </form>
-          <p>{mozL10n.get("invite_header_text")}</p>
-          <div className="btn-group call-action-group">
-            <button className="btn btn-info btn-email"
-                    onClick={this.handleEmailButtonClick}>
-              {mozL10n.get("email_link_button")}
-            </button>
-            <button className="btn btn-info btn-copy"
-                    onClick={this.handleCopyButtonClick}>
-              {this.state.copiedUrl ? mozL10n.get("copied_url_button") :
-                                      mozL10n.get("copy_url_button2")}
-            </button>
-            <button className="btn btn-info btn-share"
-                    ref="anchor"
-                    onClick={this.handleShareButtonClick}>
-              {mozL10n.get("share_button3")}
-            </button>
+          <div className="room-invitation-content">
+            <p className={cx({"error": !!this.props.error,
+                              "error-display-area": true})}>
+              {mozL10n.get("rooms_name_change_failed_label")}
+            </p>
+            <form onSubmit={this.handleFormSubmit}>
+              <textarea rows="2" type="text" className="input-room-name"
+                valueLink={this.linkState("newRoomName")}
+                onBlur={this.handleFormSubmit}
+                onKeyDown={this.handleTextareaKeyDown}
+                placeholder={mozL10n.get("rooms_name_this_room_label")} />
+            </form>
+            <p>{mozL10n.get("invite_header_text")}</p>
+            <div className="btn-group call-action-group">
+              <button className="btn btn-info btn-email"
+                      onClick={this.handleEmailButtonClick}>
+                {mozL10n.get("email_link_button")}
+              </button>
+              <button className="btn btn-info btn-copy"
+                      onClick={this.handleCopyButtonClick}>
+                {this.state.copiedUrl ? mozL10n.get("copied_url_button") :
+                                        mozL10n.get("copy_url_button2")}
+              </button>
+              <button className="btn btn-info btn-share"
+                      ref="anchor"
+                      onClick={this.handleShareButtonClick}>
+                {mozL10n.get("share_button3")}
+              </button>
+            </div>
+            <SocialShareDropdown dispatcher={this.props.dispatcher}
+                                 roomUrl={this.props.roomData.roomUrl}
+                                 show={this.state.showMenu}
+                                 ref="menu"/>
           </div>
-          <SocialShareDropdown dispatcher={this.props.dispatcher}
-                               roomStore={this.props.roomStore}
-                               show={this.state.showMenu}
-                               ref="menu"/>
+          <DesktopRoomContextView
+            roomData={this.props.roomData}
+            show={this.props.showContext} />
+        </div>
+      );
+    }
+  });
+
+  var DesktopRoomContextView = React.createClass({
+    propTypes: {
+      // This data is supplied by the activeRoomStore.
+      roomData: React.PropTypes.object.isRequired,
+      show: React.PropTypes.bool.isRequired
+    },
+
+    componentWillReceiveProps: function(nextProps) {
+      // When the 'show' prop is changed from outside this component, we do need
+      // to update the state.
+      if (("show" in nextProps) && nextProps.show !== this.props.show) {
+        this.setState({ show: nextProps.show });
+      }
+    },
+
+    getInitialState: function() {
+      return { show: this.props.show };
+    },
+
+    handleCloseClick: function() {
+      this.setState({ show: false });
+    },
+
+    render: function() {
+      if (!this.state.show)
+        return null;
+
+      var URL = this.props.roomData.roomContextUrls && this.props.roomData.roomContextUrls[0];
+      var thumbnail = URL && URL.thumbnail || "";
+      var URLDescription = URL && URL.description || "";
+      var location = URL && URL.location || "";
+      return (
+        <div className="room-context">
+          <img className="room-context-thumbnail" src={thumbnail}/>
+          <div className="room-context-content">
+            <div className="room-context-label">{mozL10n.get("context_inroom_label")}</div>
+            <div className="room-context-description">{URLDescription}</div>
+            <a className="room-context-url" href={location} target="_blank">{location}</a>
+            {this.props.roomData.roomDescription ?
+              <div className="room-context-comment">{this.props.roomData.roomDescription}</div> :
+              null}
+            <button className="room-context-btn-close"
+                    onClick={this.handleCloseClick}/>
+          </div>
         </div>
       );
     }
   });
 
   /**
    * Desktop room conversation view.
    */
@@ -280,28 +335,18 @@ loop.roomViews = (function(mozL10n) {
       ActiveRoomStoreMixin,
       sharedMixins.DocumentTitleMixin,
       sharedMixins.MediaSetupMixin,
       sharedMixins.RoomsAudioMixin,
       sharedMixins.WindowCloseMixin
     ],
 
     propTypes: {
-      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired
-    },
-
-    _renderInvitationOverlay: function() {
-      if (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS) {
-        return (
-          <DesktopRoomInvitationView
-            roomStore={this.props.roomStore}
-            dispatcher={this.props.dispatcher} />
-        );
-      }
-      return null;
+      dispatcher: React.PropTypes.instanceOf(loop.Dispatcher).isRequired,
+      mozLoop: React.PropTypes.object.isRequired
     },
 
     componentWillUpdate: function(nextProps, nextState) {
       // The SDK needs to know about the configuration and the elements to use
       // for display. So the best way seems to pass the information here - ideally
       // the sdk wouldn't need to know this, but we can't change that.
       if (this.state.roomState !== ROOM_STATES.MEDIA_WAIT &&
           nextState.roomState === ROOM_STATES.MEDIA_WAIT) {
@@ -336,16 +381,27 @@ loop.roomViews = (function(mozL10n) {
     publishStream: function(type, enabled) {
       this.props.dispatcher.dispatch(
         new sharedActions.SetMute({
           type: type,
           enabled: enabled
         }));
     },
 
+    _shouldRenderInvitationOverlay: function() {
+      return (this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS);
+    },
+
+    _shouldRenderContextView: function() {
+      return !!(
+        this.props.mozLoop.getLoopPref("contextInConverations.enabled") &&
+        (this.state.roomContextUrls || this.state.roomDescription)
+      );
+    },
+
     render: function() {
       if (this.state.roomName) {
         this.setTitle(this.state.roomName);
       }
 
       var localStreamClasses = React.addons.classSet({
         local: true,
         "local-stream": true,
@@ -353,16 +409,20 @@ loop.roomViews = (function(mozL10n) {
         "room-preview": this.state.roomState !== ROOM_STATES.HAS_PARTICIPANTS
       });
 
       var screenShareData = {
         state: this.state.screenSharingState,
         visible: true
       };
 
+      var shouldRenderInvitationOverlay = this._shouldRenderInvitationOverlay();
+      var shouldRenderContextView = this._shouldRenderContextView();
+      var roomData = this.props.roomStore.getStoreState("activeRoom");
+
       switch(this.state.roomState) {
         case ROOM_STATES.FAILED:
         case ROOM_STATES.FULL: {
           // Note: While rooms are set to hold a maximum of 2 participants, the
           //       FULL case should never happen on desktop.
           return (
             <loop.conversationViews.GenericFailureView
               cancelCall={this.closeWindow}
@@ -373,17 +433,22 @@ loop.roomViews = (function(mozL10n) {
           return (
             <sharedViews.FeedbackView
               onAfterFeedbackReceived={this.closeWindow} />
           );
         }
         default: {
           return (
             <div className="room-conversation-wrapper">
-              {this._renderInvitationOverlay()}
+              <DesktopRoomInvitationView
+                dispatcher={this.props.dispatcher}
+                error={this.state.error}
+                roomData={roomData}
+                show={shouldRenderInvitationOverlay}
+                showContext={shouldRenderContextView} />
               <div className="video-layout-wrapper">
                 <div className="conversation room-conversation">
                   <div className="media nested">
                     <div className="video_wrapper remote_wrapper">
                       <div className="video_inner remote focus-stream"></div>
                     </div>
                     <div className={localStreamClasses}></div>
                     <div className="screen hide"></div>
@@ -392,23 +457,27 @@ loop.roomViews = (function(mozL10n) {
                     dispatcher={this.props.dispatcher}
                     video={{enabled: !this.state.videoMuted, visible: true}}
                     audio={{enabled: !this.state.audioMuted, visible: true}}
                     publishStream={this.publishStream}
                     hangup={this.leaveRoom}
                     screenShare={screenShareData} />
                 </div>
               </div>
+              <DesktopRoomContextView
+                roomData={roomData}
+                show={!shouldRenderInvitationOverlay && shouldRenderContextView} />
             </div>
           );
         }
       }
     }
   });
 
   return {
     ActiveRoomStoreMixin: ActiveRoomStoreMixin,
     SocialShareDropdown: SocialShareDropdown,
+    DesktopRoomContextView: DesktopRoomContextView,
     DesktopRoomConversationView: DesktopRoomConversationView,
     DesktopRoomInvitationView: DesktopRoomInvitationView
   };
 
 })(document.mozL10n || navigator.mozL10n);
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -848,22 +848,35 @@ html, .fx-embedded, #main,
   background-image: url("../img/icons-16x16.svg#leave");
 }
 
 .room-invitation-overlay {
   position: absolute;
   background: rgba(0,0,0,.6);
   /* This matches .fx-embedded .conversation toolbar height */
   top: 26px;
+  height: calc(100% - 26px);
   right: 0;
-  bottom: 0;
   left: 0;
   text-align: center;
   color: #fff;
   z-index: 1010;
+  display: flex;
+  flex-flow: column nowrap;
+  justify-content: flex-start;
+  align-items: stretch;
+}
+
+.room-invitation-content {
+  order: 1;
+  flex: 1 1 auto;
+  display: flex;
+  flex-flow: column nowrap;
+  justify-content: center;
+  align-items: center;
 }
 
 .room-invitation-overlay .error-display-area.error,
 .room-invitation-overlay input[type="text"] {
   display: block;
   background-color: rgba(0,0,0,.5);
   border-radius: 3px;
   padding: .5em;
@@ -878,38 +891,33 @@ html, .fx-embedded, #main,
   top: 2em;
   left: 1em;
   right: 1em;
   text-align: start;
   width: calc(258px - 2em);
   color: #d74345;
 }
 
-.room-invitation-overlay form {
-  padding: 6em 0 2em 0;
+.room-invitation-overlay .btn-group {
+  padding: 0;
 }
 
 .room-invitation-overlay textarea {
   display: block;
   background: rgba(0, 0, 0, .5);
   color: #fff;
   font-family: "Helvetica Neue", Arial, sans;
   font-size: 1.2em;
   border: none;
   width: 200px;
   margin: 0 auto;
   padding: .2em .4em;
   border-radius: .5em;
 }
 
-.room-invitation-overlay .btn-group {
-  position: absolute;
-  bottom: 10px;
-}
-
 .share-service-dropdown {
   color: #000;
   text-align: start;
   bottom: auto;
   top: 0;
 }
 
 .share-service-dropdown.share-button-unavailable {
@@ -957,16 +965,122 @@ body[dir=rtl] .share-service-dropdown .s
 .dropdown-menu-item:hover > .icon-add-share-service {
   background-image: url("../img/icons-16x16.svg#add-hover");
 }
 
 .dropdown-menu-item:hover:active > .icon-add-share-service {
   background-image: url("../img/icons-16x16.svg#add-active");
 }
 
+.room-context {
+  background: rgba(0,0,0,.6);
+  border-top: 2px solid #444;
+  border-bottom: 2px solid #444;
+  padding: .5rem;
+  max-height: 120px;
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  width: 100%;
+  font-size: .9em;
+  display: flex;
+  flex-flow: row nowrap;
+  align-content: flex-start;
+  align-items: flex-start;
+}
+
+.room-invitation-overlay .room-context {
+  position: relative;
+  left: auto;
+  bottom: auto;
+  order: 2;
+  flex: 0 1 auto;
+}
+
+.room-context-thumbnail {
+  width: 100px;
+  max-height: 200px;
+  -moz-margin-end: 1ch;
+  margin-bottom: 1em;
+  order: 1;
+  flex: 0 1 auto;
+}
+
+body[dir=rtl] .room-context-thumbnail {
+  order: 2;
+}
+
+.room-context-thumbnail[src=""] {
+  display: none;
+}
+
+.room-context-content {
+  order: 2;
+  flex: 1 1 auto;
+  text-align: start;
+}
+
+body[dir=rtl] .room-context-content {
+  order: 1;
+}
+
+.room-context-label {
+  margin-bottom: 1em;
+}
+
+.room-context-label,
+.room-context-description {
+  color: #fff;
+}
+
+.room-context-comment {
+  color: #707070;
+}
+
+.room-context-description,
+.room-context-comment {
+  word-wrap: break-word;
+}
+
+.room-context-url {
+  color: #59A1D7;
+  font-style: italic;
+  text-decoration: none;
+  margin-bottom: 1em;
+}
+
+.room-context-url:hover {
+  text-decoration: underline;
+}
+
+.room-context-btn-close {
+  position: absolute;
+  right: 5px;
+  top: 5px;
+  width: 8px;
+  height: 8px;
+  background-color: transparent;
+  background-image: url("../img/icons-10x10.svg#close");
+  background-size: 8px 8px;
+  background-repeat: no-repeat;
+  border: 0;
+  padding: 0;
+  cursor: pointer;
+}
+
+.room-context-btn-close:hover,
+.room-context-btn-close:hover:active {
+  background-image: url("../img/icons-10x10.svg#close-active");
+}
+
+body[dir=rtl] .room-context-btn-close {
+  right: auto;
+  left: 5px;
+}
+
 /* Standalone rooms */
 
 .standalone .room-conversation-wrapper {
   position: relative;
   height: 100%;
   background: #000;
 }
 
--- a/browser/components/loop/content/shared/js/actions.js
+++ b/browser/components/loop/content/shared/js/actions.js
@@ -396,32 +396,34 @@ loop.shared.actions = (function() {
 
     /**
      * Sets up the room information when it is received.
      * XXX: should move to some roomActions module - refs bug 1079284
      *
      * @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
      */
     SetupRoomInfo: Action.define("setupRoomInfo", {
+      // roomContextUrls: Array - Optional.
+      // roomDescription: String - Optional.
       // roomName: String - Optional.
       roomOwner: String,
       roomToken: String,
       roomUrl: String,
       socialShareButtonAvailable: Boolean,
       socialShareProviders: Array
     }),
 
     /**
      * Updates the room information when it is received.
      * XXX: should move to some roomActions module - refs bug 1079284
      *
      * @see https://wiki.mozilla.org/Loop/Architecture/Rooms#GET_.2Frooms.2F.7Btoken.7D
      */
     UpdateRoomInfo: Action.define("updateRoomInfo", {
-      // context: Object - Optional.
+      // description: String - Optional.
       // roomName: String - Optional.
       roomOwner: String,
       roomUrl: String
       // urls: Array - Optional.
       // See https://wiki.mozilla.org/Loop/Architecture/Context#Format_of_context.value
     }),
 
     /**
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -18,16 +18,23 @@ loop.store.ActiveRoomStore = (function()
   // Error numbers taken from
   // https://github.com/mozilla-services/loop-server/blob/master/loop/errno.json
   var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
 
   var ROOM_STATES = loop.store.ROOM_STATES;
 
   var ROOM_INFO_FAILURES = loop.shared.utils.ROOM_INFO_FAILURES;
 
+  var OPTIONAL_ROOMINFO_FIELDS = {
+    urls: "roomContextUrls",
+    description: "roomDescription",
+    roomInfoFailure: "roomInfoFailure",
+    roomName: "roomName"
+  };
+
   /**
    * Active room store.
    *
    * @param {loop.Dispatcher} dispatcher  The dispatcher for dispatching actions
    *                                      and registering to consume actions.
    * @param {Object} options Options object:
    * - {mozLoop}     mozLoop    The MozLoop API object.
    * - {OTSdkDriver} sdkDriver  The SDK driver instance.
@@ -79,16 +86,18 @@ loop.store.ActiveRoomStore = (function()
         localVideoDimensions: {},
         remoteVideoDimensions: {},
         screenSharingState: SCREEN_SHARE_STATES.INACTIVE,
         receivingScreenShare: false,
         // Any urls (aka context) associated with the room.
         roomContextUrls: null,
         // The roomCryptoKey to decode the context data if necessary.
         roomCryptoKey: null,
+        // The description for a room as stored in the context data.
+        roomDescription: null,
         // Room information failed to be obtained for a reason. See ROOM_INFO_FAILURES.
         roomInfoFailure: null,
         // The name of the room.
         roomName: null,
         // Social API state.
         socialShareButtonAvailable: false,
         socialShareProviders: null
       };
@@ -180,16 +189,18 @@ loop.store.ActiveRoomStore = (function()
               error: error,
               failedJoinRequest: false
             }));
             return;
           }
 
           this.dispatchAction(new sharedActions.SetupRoomInfo({
             roomToken: actionData.roomToken,
+            roomContextUrls: roomData.decryptedContext.urls,
+            roomDescription: roomData.decryptedContext.description,
             roomName: roomData.decryptedContext.roomName,
             roomOwner: roomData.roomOwner,
             roomUrl: roomData.roomUrl,
             socialShareButtonAvailable: this._mozLoop.isSocialShareButtonAvailable(),
             socialShareProviders: this._mozLoop.getSocialShareProviders()
           }));
 
           // For the conversation window, we need to automatically
@@ -270,16 +281,17 @@ loop.store.ActiveRoomStore = (function()
         }
 
         var dispatcher = this.dispatcher;
 
         crypto.decryptBytes(roomCryptoKey, result.context.value)
               .then(function(decryptedResult) {
           var realResult = JSON.parse(decryptedResult);
 
+          roomInfoData.description = realResult.description;
           roomInfoData.urls = realResult.urls;
           roomInfoData.roomName = realResult.roomName;
 
           dispatcher.dispatch(roomInfoData);
         }, function(err) {
           roomInfoData.roomInfoFailure = ROOM_INFO_FAILURES.DECRYPT_FAILED;
           dispatcher.dispatch(roomInfoData);
         });
@@ -294,16 +306,18 @@ loop.store.ActiveRoomStore = (function()
      */
     setupRoomInfo: function(actionData) {
       if (this._onUpdateListener) {
         console.error("Room info already set up!");
         return;
       }
 
       this.setStoreState({
+        roomContextUrls: actionData.roomContextUrls,
+        roomDescription: actionData.roomDescription,
         roomName: actionData.roomName,
         roomOwner: actionData.roomOwner,
         roomState: ROOM_STATES.READY,
         roomToken: actionData.roomToken,
         roomUrl: actionData.roomUrl,
         socialShareButtonAvailable: actionData.socialShareButtonAvailable,
         socialShareProviders: actionData.socialShareProviders
       });
@@ -319,23 +333,28 @@ loop.store.ActiveRoomStore = (function()
     },
 
     /**
      * Handles the updateRoomInfo action. Updates the room data.
      *
      * @param {sharedActions.UpdateRoomInfo} actionData
      */
     updateRoomInfo: function(actionData) {
-      this.setStoreState({
-        roomContextUrls: actionData.urls,
-        roomInfoFailure: actionData.roomInfoFailure,
-        roomName: actionData.roomName,
+      var newState = {
         roomOwner: actionData.roomOwner,
         roomUrl: actionData.roomUrl
+      };
+      // Iterate over the optional fields that _may_ be present on the actionData
+      // object.
+      Object.keys(OPTIONAL_ROOMINFO_FIELDS).forEach(function(field) {
+        if (actionData[field]) {
+          newState[OPTIONAL_ROOMINFO_FIELDS[field]] = actionData[field];
+        }
       });
+      this.setStoreState(newState);
     },
 
     /**
      * Handles the updateSocialShareInfo action. Updates the room data with new
      * Social API info.
      *
      * @param  {sharedActions.UpdateSocialShareInfo} actionData
      */
--- a/browser/components/loop/content/shared/js/views.js
+++ b/browser/components/loop/content/shared/js/views.js
@@ -61,21 +61,19 @@ loop.shared.views = (function(_, l10n) {
       var prefix = this.props.enabled ? "mute" : "unmute";
       var suffix = "button_title";
       var msgId = [prefix, this.props.scope, this.props.type, suffix].join("_");
       return l10n.get(msgId);
     },
 
     render: function() {
       return (
-        /* jshint ignore:start */
         React.createElement("button", {className: this._getClasses(), 
                 title: this._getTitle(), 
                 onClick: this.handleClick})
-        /* jshint ignore:end */
       );
     }
   });
 
   /**
    * Screen sharing control button.
    *
    * Required props:
@@ -446,34 +444,32 @@ loop.shared.views = (function(_, l10n) {
     },
 
     render: function() {
       var localStreamClasses = React.addons.classSet({
         local: true,
         "local-stream": true,
         "local-stream-audio": !this.state.video.enabled
       });
-      /* jshint ignore:start */
       return (
         React.createElement("div", {className: "video-layout-wrapper"}, 
           React.createElement("div", {className: "conversation in-call"}, 
             React.createElement("div", {className: "media nested"}, 
               React.createElement("div", {className: "video_wrapper remote_wrapper"}, 
                 React.createElement("div", {className: "video_inner remote focus-stream"})
               ), 
               React.createElement("div", {className: localStreamClasses})
             ), 
             React.createElement(ConversationToolbar, {video: this.state.video, 
                                  audio: this.state.audio, 
                                  publishStream: this.publishStream, 
                                  hangup: this.hangup})
           )
         )
       );
-      /* jshint ignore:end */
     }
   });
 
   /**
    * Notification view.
    */
   var NotificationView = React.createClass({displayName: "NotificationView",
     mixins: [Backbone.Events],
--- a/browser/components/loop/content/shared/js/views.jsx
+++ b/browser/components/loop/content/shared/js/views.jsx
@@ -61,21 +61,19 @@ loop.shared.views = (function(_, l10n) {
       var prefix = this.props.enabled ? "mute" : "unmute";
       var suffix = "button_title";
       var msgId = [prefix, this.props.scope, this.props.type, suffix].join("_");
       return l10n.get(msgId);
     },
 
     render: function() {
       return (
-        /* jshint ignore:start */
         <button className={this._getClasses()}
                 title={this._getTitle()}
                 onClick={this.handleClick}></button>
-        /* jshint ignore:end */
       );
     }
   });
 
   /**
    * Screen sharing control button.
    *
    * Required props:
@@ -446,34 +444,32 @@ loop.shared.views = (function(_, l10n) {
     },
 
     render: function() {
       var localStreamClasses = React.addons.classSet({
         local: true,
         "local-stream": true,
         "local-stream-audio": !this.state.video.enabled
       });
-      /* jshint ignore:start */
       return (
         <div className="video-layout-wrapper">
           <div className="conversation in-call">
             <div className="media nested">
               <div className="video_wrapper remote_wrapper">
                 <div className="video_inner remote focus-stream"></div>
               </div>
               <div className={localStreamClasses}></div>
             </div>
             <ConversationToolbar video={this.state.video}
                                  audio={this.state.audio}
                                  publishStream={this.publishStream}
                                  hangup={this.hangup} />
           </div>
         </div>
       );
-      /* jshint ignore:end */
     }
   });
 
   /**
    * Notification view.
    */
   var NotificationView = React.createClass({
     mixins: [Backbone.Events],
--- a/browser/components/loop/test/desktop-local/conversation_test.js
+++ b/browser/components/loop/test/desktop-local/conversation_test.js
@@ -132,17 +132,18 @@ describe("loop.conversation", function()
   describe("AppControllerView", function() {
     var conversationStore, client, ccView, dispatcher;
     var conversationAppStore, roomStore;
 
     function mountTestComponent() {
       return TestUtils.renderIntoDocument(
         React.createElement(loop.conversation.AppControllerView, {
           roomStore: roomStore,
-          dispatcher: dispatcher
+          dispatcher: dispatcher,
+          mozLoop: navigator.mozLoop
         }));
     }
 
     beforeEach(function() {
       client = new loop.Client();
       dispatcher = new loop.Dispatcher();
       conversationStore = new loop.store.ConversationStore(
         dispatcher, {
--- a/browser/components/loop/test/desktop-local/roomViews_test.js
+++ b/browser/components/loop/test/desktop-local/roomViews_test.js
@@ -3,18 +3,18 @@ var expect = chai.expect;
 /* jshint newcap:false */
 
 describe("loop.roomViews", function () {
   "use strict";
 
   var ROOM_STATES = loop.store.ROOM_STATES;
   var SCREEN_SHARE_STATES = loop.shared.utils.SCREEN_SHARE_STATES;
 
-  var sandbox, dispatcher, roomStore, activeRoomStore, fakeWindow;
-  var fakeMozLoop;
+  var sandbox, dispatcher, roomStore, activeRoomStore, fakeWindow,
+    fakeMozLoop, fakeContextURL;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
 
     dispatcher = new loop.Dispatcher();
 
     fakeMozLoop = {
       getAudioBlob: sinon.stub(),
@@ -43,16 +43,22 @@ describe("loop.roomViews", function () {
     activeRoomStore = new loop.store.ActiveRoomStore(dispatcher, {
       mozLoop: {},
       sdkDriver: {}
     });
     roomStore = new loop.store.RoomStore(dispatcher, {
       mozLoop: {},
       activeRoomStore: activeRoomStore
     });
+
+    fakeContextURL = {
+      description: "An invalid page",
+      location: "http://invalid.com",
+      thumbnail: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
+    };
   });
 
   afterEach(function() {
     sandbox.restore();
     loop.shared.mixins.setRootObject(window);
   });
 
   describe("ActiveRoomStoreMixin", function() {
@@ -98,51 +104,54 @@ describe("loop.roomViews", function () {
     beforeEach(function() {
       sandbox.stub(dispatcher, "dispatch");
     });
 
     afterEach(function() {
       view = null;
     });
 
-    function mountTestComponent() {
+    function mountTestComponent(props) {
+      props = _.extend({
+        dispatcher: dispatcher,
+        roomData: {},
+        show: true,
+        showContext: false
+      }, props);
       return TestUtils.renderIntoDocument(
-        React.createElement(
-          loop.roomViews.DesktopRoomInvitationView, {
-            dispatcher: dispatcher,
-            roomStore: roomStore
-          }));
+        React.createElement(loop.roomViews.DesktopRoomInvitationView, props));
     }
 
-    it("should dispatch an EmailRoomUrl action when the email button is " +
-      "pressed", function() {
-        view = mountTestComponent();
+    it("should dispatch an EmailRoomUrl action when the email button is pressed",
+      function() {
+        view = mountTestComponent({
+          roomData: { roomUrl: "http://invalid" }
+        });
 
-        view.setState({roomUrl: "http://invalid"});
-
-        var emailBtn = view.getDOMNode().querySelector('.btn-email');
+        var emailBtn = view.getDOMNode().querySelector(".btn-email");
 
         React.addons.TestUtils.Simulate.click(emailBtn);
 
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWith(dispatcher.dispatch,
           new sharedActions.EmailRoomUrl({roomUrl: "http://invalid"}));
       });
 
     describe("Rename Room", function() {
       var roomNameBox;
 
       beforeEach(function() {
-        view = mountTestComponent();
-        view.setState({
-          roomToken: "fakeToken",
-          roomName: "fakeName"
+        view = mountTestComponent({
+          roomData: {
+            roomToken: "fakeToken",
+            roomName: "fakeName"
+          }
         });
 
-        roomNameBox = view.getDOMNode().querySelector('.input-room-name');
+        roomNameBox = view.getDOMNode().querySelector(".input-room-name");
       });
 
       it("should dispatch a RenameRoom action when the focus is lost",
         function() {
           React.addons.TestUtils.Simulate.change(roomNameBox, { target: {
             value: "reallyFake"
           }});
 
@@ -170,34 +179,34 @@ describe("loop.roomViews", function () {
               roomToken: "fakeToken",
               newRoomName: "reallyFake"
             }));
         });
     });
 
     describe("Copy Button", function() {
       beforeEach(function() {
-        view = mountTestComponent();
-
-        view.setState({roomUrl: "http://invalid"});
+        view = mountTestComponent({
+          roomData: { roomUrl: "http://invalid" }
+        });
       });
 
       it("should dispatch a CopyRoomUrl action when the copy button is " +
         "pressed", function() {
-          var copyBtn = view.getDOMNode().querySelector('.btn-copy');
+          var copyBtn = view.getDOMNode().querySelector(".btn-copy");
 
           React.addons.TestUtils.Simulate.click(copyBtn);
 
           sinon.assert.calledOnce(dispatcher.dispatch);
           sinon.assert.calledWith(dispatcher.dispatch,
             new sharedActions.CopyRoomUrl({roomUrl: "http://invalid"}));
         });
 
       it("should change the text when the url has been copied", function() {
-          var copyBtn = view.getDOMNode().querySelector('.btn-copy');
+          var copyBtn = view.getDOMNode().querySelector(".btn-copy");
 
           React.addons.TestUtils.Simulate.click(copyBtn);
 
           // copied_url_button is the l10n string.
           expect(copyBtn.textContent).eql("copied_url_button");
       });
     });
 
@@ -210,16 +219,35 @@ describe("loop.roomViews", function () {
         var shareBtn = view.getDOMNode().querySelector(".btn-share");
 
         React.addons.TestUtils.Simulate.click(shareBtn);
 
         expect(view.state.showMenu).to.eql(true);
         expect(view.refs.menu.props.show).to.eql(true);
       });
     });
+
+    describe("Context", function() {
+      it("should not render the context data when told not to", function() {
+        view = mountTestComponent();
+
+        expect(view.getDOMNode().querySelector(".room-context")).to.eql(null);
+      });
+
+      it("should render context when data is available", function() {
+        view = mountTestComponent({
+          showContext: true,
+          roomData: {
+            roomContextUrls: [fakeContextURL]
+          }
+        });
+
+        expect(view.getDOMNode().querySelector(".room-context")).to.not.eql(null);
+      });
+    });
   });
 
   describe("DesktopRoomConversationView", function() {
     var view;
 
     beforeEach(function() {
       loop.store.StoreMixin.register({
         feedbackStore: new loop.store.FeedbackStore(dispatcher, {
@@ -463,127 +491,178 @@ describe("loop.roomViews", function () {
         iconURL: "http://example.com/foo.png"
       };
     });
 
     afterEach(function() {
       view = fakeProvider = null;
     });
 
-    function mountTestComponent() {
+    function mountTestComponent(props) {
+      props = _.extend({
+        dispatcher: dispatcher,
+        show: true
+      }, props);
       return TestUtils.renderIntoDocument(
-        React.createElement(loop.roomViews.SocialShareDropdown, {
-          dispatcher: dispatcher,
-          roomStore: roomStore,
-          show: true
-        })
-      );
+        React.createElement(loop.roomViews.SocialShareDropdown, props));
     }
 
     describe("#render", function() {
       it("should show no contents when the Social Providers have not been fetched yet", function() {
         view = mountTestComponent();
 
         expect(view.getDOMNode()).to.eql(null);
       });
 
       it("should show different contents when the Share XUL button is not available", function() {
-        activeRoomStore.setStoreState({
+        view = mountTestComponent({
           socialShareProviders: []
         });
-        view = mountTestComponent();
 
         var node = view.getDOMNode();
         expect(node.querySelector(".share-panel-header")).to.not.eql(null);
       });
 
       it("should show an empty list when no Social Providers are available", function() {
-        activeRoomStore.setStoreState({
+        view = mountTestComponent({
           socialShareButtonAvailable: true,
           socialShareProviders: []
         });
 
-        view = mountTestComponent();
-
         var node = view.getDOMNode();
         expect(node.querySelector(".icon-add-share-service")).to.not.eql(null);
         expect(node.querySelectorAll(".dropdown-menu-item").length).to.eql(1);
       });
 
       it("should show a list of available Social Providers", function() {
-        activeRoomStore.setStoreState({
+        view = mountTestComponent({
           socialShareButtonAvailable: true,
           socialShareProviders: [fakeProvider]
         });
 
-        view = mountTestComponent();
-
         var node = view.getDOMNode();
         expect(node.querySelector(".icon-add-share-service")).to.not.eql(null);
         expect(node.querySelector(".dropdown-menu-separator")).to.not.eql(null);
 
         var dropdownNodes = node.querySelectorAll(".dropdown-menu-item");
         expect(dropdownNodes.length).to.eql(2);
         expect(dropdownNodes[1].querySelector("img").src).to.eql(fakeProvider.iconURL);
         expect(dropdownNodes[1].querySelector("span").textContent)
           .to.eql(fakeProvider.name);
       });
     });
 
     describe("#handleToolbarAddButtonClick", function() {
       it("should dispatch an action when the 'add to toolbar' button is clicked", function() {
-        activeRoomStore.setStoreState({
+        view = mountTestComponent({
           socialShareProviders: []
         });
 
-        view = mountTestComponent();
-
         var addButton = view.getDOMNode().querySelector(".btn-toolbar-add");
         React.addons.TestUtils.Simulate.click(addButton);
 
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.AddSocialShareButton());
       });
     });
 
     describe("#handleAddServiceClick", function() {
       it("should dispatch an action when the 'add provider' item is clicked", function() {
-        activeRoomStore.setStoreState({
+        view = mountTestComponent({
           socialShareProviders: [],
           socialShareButtonAvailable: true
         });
 
-        view = mountTestComponent();
-
         var addItem = view.getDOMNode().querySelector(".dropdown-menu-item:first-child");
         React.addons.TestUtils.Simulate.click(addItem);
 
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.AddSocialShareProvider());
       });
     });
 
     describe("#handleProviderClick", function() {
       it("should dispatch an action when a provider item is clicked", function() {
-        activeRoomStore.setStoreState({
+        view = mountTestComponent({
           roomUrl: "http://example.com",
           socialShareButtonAvailable: true,
           socialShareProviders: [fakeProvider]
         });
 
-        view = mountTestComponent();
-
         var providerItem = view.getDOMNode().querySelector(".dropdown-menu-item:last-child");
         React.addons.TestUtils.Simulate.click(providerItem);
 
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.ShareRoomUrl({
             provider: fakeProvider,
-            roomUrl: activeRoomStore.getStoreState().roomUrl,
+            roomUrl: "http://example.com",
             previews: []
           }));
       });
     });
   });
+
+  describe("DesktopRoomContextView", function() {
+    var view;
+
+    afterEach(function() {
+      view = null;
+    });
+
+    function mountTestComponent(props) {
+      props = _.extend({
+        show: true
+      }, props);
+      return TestUtils.renderIntoDocument(
+        React.createElement(loop.roomViews.DesktopRoomContextView, props));
+    }
+
+    describe("#render", function() {
+      it("should show the context information properly when available", function() {
+        view = mountTestComponent({
+          roomData: {
+            roomDescription: "Hello, is it me you're looking for?",
+            roomContextUrls: [fakeContextURL]
+          }
+        });
+
+        var node = view.getDOMNode();
+        expect(node).to.not.eql(null);
+        expect(node.querySelector(".room-context-thumbnail").src).to.
+          eql(fakeContextURL.thumbnail);
+        expect(node.querySelector(".room-context-description").textContent).to.
+          eql(fakeContextURL.description);
+        expect(node.querySelector(".room-context-comment").textContent).to.
+          eql(view.props.roomData.roomDescription);
+      });
+
+      it("should not render optional data", function() {
+        view = mountTestComponent({
+          roomData: { roomContextUrls: [fakeContextURL] }
+        });
+
+        expect(view.getDOMNode().querySelector(".room-context-comment")).to.
+          eql(null);
+      });
+
+      it("should not render the component when 'show' is false", function() {
+        view = mountTestComponent({
+          show: false
+        });
+
+        expect(view.getDOMNode()).to.eql(null);
+      });
+
+      it("should close the view when the close button is clicked", function() {
+        view = mountTestComponent({
+          roomData: { roomContextUrls: [fakeContextURL] }
+        });
+
+        var closeBtn = view.getDOMNode().querySelector(".room-context-btn-close");
+        React.addons.TestUtils.Simulate.click(closeBtn);
+        expect(view.getDOMNode()).to.eql(null);
+      });
+    })
+  });
 });
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -277,16 +277,18 @@ describe("loop.store.ActiveRoomStore", f
           windowId: "42",
           type: "room",
           roomToken: fakeToken
         }));
 
         sinon.assert.calledTwice(dispatcher.dispatch);
         sinon.assert.calledWithExactly(dispatcher.dispatch,
           new sharedActions.SetupRoomInfo({
+            roomContextUrls: undefined,
+            roomDescription: undefined,
             roomToken: fakeToken,
             roomName: fakeRoomData.decryptedContext.roomName,
             roomOwner: fakeRoomData.roomOwner,
             roomUrl: fakeRoomData.roomUrl,
             socialShareButtonAvailable: false,
             socialShareProviders: []
           }));
       });
@@ -456,16 +458,17 @@ describe("loop.store.ActiveRoomStore", f
             roomInfoFailure: ROOM_INFO_FAILURES.DECRYPT_FAILED
           }, expectedDetails)));
       });
 
       it("should dispatch UpdateRoomInfo message with the context if decryption was successful", function() {
         fetchServerAction.cryptoKey = "fakeKey";
 
         var roomContext = {
+          description: "Never gonna let you down. Never gonna give you up...",
           roomName: "The wonderful Loopy room",
           urls: [{
             description: "An invalid page",
             location: "http://invalid.com",
             thumbnail: "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=="
           }]
         };
 
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -742,17 +742,19 @@ BrowserGlue.prototype = {
 
 #ifdef MOZ_CRASHREPORTER
     TabCrashReporter.init();
     PluginCrashReporter.init();
 #endif
 
     Services.obs.notifyObservers(null, "browser-ui-startup-complete", "");
 
+#ifdef NIGHTLY_BUILD
     AddonWatcher.init(this._notifySlowAddon);
+#endif
   },
 
   _checkForOldBuildUpdates: function () {
     // check for update if our build is old
     if (Services.prefs.getBoolPref("app.update.enabled") &&
         Services.prefs.getBoolPref("app.update.checkInstallTime")) {
 
       let buildID = Services.appinfo.appBuildID;
@@ -1001,17 +1003,19 @@ BrowserGlue.prototype = {
     WebappManager.uninit();
 #ifdef NIGHTLY_BUILD
     if (Services.prefs.getBoolPref("dom.identity.enabled")) {
       SignInToWebsiteUX.uninit();
     }
 #endif
     webrtcUI.uninit();
     FormValidationHandler.uninit();
+#ifdef NIGHTLY_BUILD
     AddonWatcher.uninit();
+#endif
   },
 
   _initServiceDiscovery: function () {
     if (!Services.prefs.getBoolPref("browser.casting.enabled")) {
       return;
     }
     var rokuDevice = {
       id: "roku:ecp",
--- a/browser/devtools/inspector/test/browser.ini
+++ b/browser/devtools/inspector/test/browser.ini
@@ -17,16 +17,17 @@ support-files =
   doc_inspector_highlighter.html
   doc_inspector_highlighter_rect.html
   doc_inspector_highlighter_rect_iframe.html
   doc_inspector_infobar_01.html
   doc_inspector_infobar_02.html
   doc_inspector_menu.html
   doc_inspector_remove-iframe-during-load.html
   doc_inspector_search.html
+  doc_inspector_search-reserved.html
   doc_inspector_search-suggestions.html
   doc_inspector_select-last-selected-01.html
   doc_inspector_select-last-selected-02.html
   head.js
 
 [browser_inspector_breadcrumbs.js]
 [browser_inspector_breadcrumbs_highlight_hover.js]
 [browser_inspector_breadcrumbs_mutations.js]
@@ -90,13 +91,14 @@ skip-if = e10s # GCLI isn't e10s compati
 [browser_inspector_remove-iframe-during-load.js]
 [browser_inspector_scrolling.js]
 skip-if = e10s # Test synthesize scrolling events in content. Also, see bug 1035661.
 [browser_inspector_search-01.js]
 [browser_inspector_search-02.js]
 [browser_inspector_search-03.js]
 [browser_inspector_search-04.js]
 [browser_inspector_search-05.js]
+[browser_inspector_search-reserved.js]
 [browser_inspector_select-docshell.js]
 [browser_inspector_select-last-selected.js]
 [browser_inspector_search-navigation.js]
 [browser_inspector_sidebarstate.js]
 [browser_inspector_switch-to-inspector-on-pick.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/browser_inspector_search-reserved.js
@@ -0,0 +1,134 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Testing searching for ids and classes that contain reserved characters.
+const TEST_URL = TEST_URL_ROOT + "doc_inspector_search-reserved.html";
+
+// An array of (key, suggestions) pairs where key is a key to press and
+// suggestions is an array of suggestions that should be shown in the popup.
+// Suggestion is an object with label of the entry and optional count
+// (defaults to 1)
+const TEST_DATA = [
+  {
+    key: "#",
+    suggestions: [{label: "#d1\\.d2", count: 1}]
+  },
+  {
+    key: "d",
+    suggestions: [{label: "#d1\\.d2", count: 1}]
+  },
+  {
+    key: "VK_BACK_SPACE",
+    suggestions: [{label: "#d1\\.d2", count: 1}]
+  },
+  {
+    key: "VK_BACK_SPACE",
+    suggestions: []
+  },
+  {
+    key: ".",
+    suggestions: [{label: ".c1\\.c2", count: 1}]
+  },
+  {
+    key: "c",
+    suggestions: [{label: ".c1\\.c2", count: 1}]
+  },
+  { 
+    key: "VK_BACK_SPACE", 
+    suggestions: [{label: ".c1\\.c2", count: 1}]
+  },
+  {
+    key: "VK_BACK_SPACE",
+    suggestions: []
+  },
+  {
+    key: "d", 
+    suggestions: [{label: "div", count: 2},
+                  {label: "#d1\\.d2", count: 1}]
+  },
+  {
+    key: "VK_BACK_SPACE",
+    suggestions: []
+  },
+  {
+    key:"c",
+    suggestions: [{label: ".c1\\.c2", count: 1}]
+  },
+  {
+    key: "VK_BACK_SPACE",
+    suggestions: []
+  },
+  {
+    key: "b",
+    suggestions: [{label: "body", count: 1}]
+  }, 
+  {
+    key: "o",
+    suggestions: [{label: "body", count: 1}]
+  },
+  {
+    key: "d",
+    suggestions: [{label: "body", count: 1}]
+  },
+  { 
+    key: "y",
+    suggestions: []
+  }, 
+  {
+    key: " ",
+    suggestions: [{label: "body div", count: 2}]
+  },
+  {
+    key: ".",
+    suggestions: [{label: "body .c1\\.c2", count: 1}]
+  },
+  {
+    key: "VK_BACK_SPACE",
+    suggestions: [{label: "body div", count: 2}]
+  },
+  {
+    key: "#",
+    suggestions: [{label: "body #", count: 1},
+                  {label: "body #d1\\.d2", count: 1}]
+  }
+];
+
+add_task(function* () {
+  let { inspector } = yield openInspectorForURL(TEST_URL);
+  let searchBox = inspector.searchBox;
+  let popup = inspector.searchSuggestions.searchPopup;
+
+  yield focusSearchBoxUsingShortcut(inspector.panelWin);
+
+  for (let { key, suggestions } of TEST_DATA) {
+    info("Pressing " + key + " to get " + formatSuggestions(suggestions));
+
+    let command = once(searchBox, "command");
+    EventUtils.synthesizeKey(key, {}, inspector.panelWin);
+    yield command;
+
+    info("Waiting for search query to complete");
+    yield inspector.searchSuggestions._lastQuery;
+
+    info("Query completed. Performing checks for input '" + searchBox.value + "'");
+    let actualSuggestions = popup.getItems().reverse();
+
+    is(popup.isOpen ? actualSuggestions.length: 0, suggestions.length,
+       "There are expected number of suggestions.");
+
+    for (let i = 0; i < suggestions.length; i++) {
+      is(suggestions[i].label, actualSuggestions[i].label,
+         "The suggestion at " + i + "th index is correct.");
+      is(suggestions[i].count || 1, actualSuggestions[i].count,
+         "The count for suggestion at " + i + "th index is correct.");
+    }
+  }
+});
+
+function formatSuggestions(suggestions) {
+  return "[" + suggestions
+                .map(s => "'" + s.label + "' (" + s.count || 1 + ")")
+                .join(", ") + "]";
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/inspector/test/doc_inspector_search-reserved.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <title>Inspector Search Box Reserved Character Test</title>
+</head>
+<body>
+  <div id="d1.d2">Hi, I'm an id that contains a CSS reserved character</div>
+  <div class="c1.c2">Hi, a class that contains a CSS reserved character</div>
+</body>
+</html>
--- a/browser/devtools/shared/test/browser_css_color.js
+++ b/browser/devtools/shared/test/browser_css_color.js
@@ -1,20 +1,18 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
-const COLOR_UNIT_PREF = "devtools.defaultColorUnit";
 const TEST_URI = "data:text/html;charset=utf-8,browser_css_color.js";
 let {colorUtils} = devtools.require("devtools/css-color");
 let origColorUnit;
 
 add_task(function*() {
   yield promiseTab("about:blank");
   let [host, win, doc] = yield createHost("bottom", TEST_URI);
-  origColorUnit = Services.prefs.getCharPref(COLOR_UNIT_PREF);
 
   info("Creating a test canvas element to test colors");
   let canvas = createTestCanvas(doc);
   info("Starting the test");
   testColorUtils(canvas);
 
   host.destroy();
   gBrowser.removeCurrentTab();
@@ -44,33 +42,29 @@ function testColorUtils(canvas) {
     testColorMatch(name, hex, hsl, rgb, color.rgba, canvas);
   }
 
   testProcessCSSString();
   testSetAlpha();
 }
 
 function testToString(color, name, hex, hsl, rgb) {
-  switchColorUnit(colorUtils.CssColor.COLORUNIT.name);
+  color.colorUnit = colorUtils.CssColor.COLORUNIT.name;
   is(color.toString(), name, "toString() with authored type");
 
-  switchColorUnit(colorUtils.CssColor.COLORUNIT.hex);
+  color.colorUnit = colorUtils.CssColor.COLORUNIT.hex;
   is(color.toString(), hex, "toString() with hex type");
 
-  switchColorUnit(colorUtils.CssColor.COLORUNIT.hsl);
+  color.colorUnit = colorUtils.CssColor.COLORUNIT.hsl;
   is(color.toString(), hsl, "toString() with hsl type");
 
-  switchColorUnit(colorUtils.CssColor.COLORUNIT.rgb);
+  color.colorUnit = colorUtils.CssColor.COLORUNIT.rgb;
   is(color.toString(), rgb, "toString() with rgb type");
 }
 
-function switchColorUnit(unit) {
-  Services.prefs.setCharPref(COLOR_UNIT_PREF, unit);
-}
-
 function testColorMatch(name, hex, hsl, rgb, rgba, canvas) {
   let target;
   let ctx = canvas.getContext("2d");
 
   let clearCanvas = function() {
     canvas.width = 1;
   };
   let setColor = function(aColor) {
@@ -105,17 +99,16 @@ function testColorMatch(name, hex, hsl, 
   };
 
   setTargetColor();
 
   test(name, "name");
   test(hex, "hex");
   test(hsl, "hsl");
   test(rgb, "rgb");
-  switchColorUnit(origColorUnit);
 }
 
 function testProcessCSSString() {
   let before = "border: 1px solid red; border-radius: 5px; " +
                "color rgb(0, 255, 0); font-weight: bold; " +
                "background-color: transparent; " +
                "border-top-color: rgba(0, 0, 255, 0.5);";
   let expected = "border: 1px solid #F00; border-radius: 5px; " +
--- a/browser/devtools/shared/test/browser_toolbar_webconsole_errors_count.js
+++ b/browser/devtools/shared/test/browser_toolbar_webconsole_errors_count.js
@@ -23,17 +23,17 @@ function test() {
 
   function openToolbar(browser, tab) {
     tab1 = tab;
     ignoreAllUncaughtExceptions(false);
 
     expectUncaughtException();
 
     if (!DeveloperToolbar.visible) {
-      DeveloperToolbar.show(true, onOpenToolbar);
+      DeveloperToolbar.show(true).then(onOpenToolbar);
     }
     else {
       onOpenToolbar();
     }
   }
 
   function onOpenToolbar() {
     ok(DeveloperToolbar.visible, "DeveloperToolbar is visible");
--- a/browser/devtools/shared/test/head.js
+++ b/browser/devtools/shared/test/head.js
@@ -39,16 +39,19 @@ function addTab(aURL, aCallback)
 }
 
 function promiseTab(aURL) {
   return new Promise(resolve =>
     addTab(aURL, resolve));
 }
 
 registerCleanupFunction(function tearDown() {
+  let target = TargetFactory.forTab(gBrowser.selectedTab);
+  yield gDevTools.closeToolbox(target);
+
   while (gBrowser.tabs.length > 1) {
     gBrowser.removeCurrentTab();
   }
 
   console = undefined;
 });
 
 function catchFail(func) {
--- a/browser/devtools/shared/widgets/Tooltip.js
+++ b/browser/devtools/shared/widgets/Tooltip.js
@@ -1015,16 +1015,21 @@ SwatchBasedEditorTooltip.prototype = {
       }
       swatchEl.removeEventListener("click", this._onSwatchClick, false);
       this.swatches.delete(swatchEl);
     }
   },
 
   _onSwatchClick: function(event) {
     let swatch = this.swatches.get(event.target);
+
+    if (event.shiftKey) {
+      event.stopPropagation();
+      return;
+    }
     if (swatch) {
       this.activeSwatch = event.target;
       this.show();
       event.stopPropagation();
     }
   },
 
   /**
--- a/browser/devtools/styleeditor/StyleSheetEditor.jsm
+++ b/browser/devtools/styleeditor/StyleSheetEditor.jsm
@@ -371,17 +371,17 @@ StyleSheetEditor.prototype = {
 
     this._inputElement = inputElement;
 
     let config = {
       value: this._state.text,
       lineNumbers: true,
       mode: Editor.modes.css,
       readOnly: false,
-      autoCloseBrackets: "{}()[]",
+      autoCloseBrackets: "{}()",
       extraKeys: this._getKeyBindings(),
       contextMenu: "sourceEditorContextMenu",
       autocomplete: Services.prefs.getBoolPref(AUTOCOMPLETION_PREF),
       autocompleteOpts: { walker: this.walker }
     };
     let sourceEditor = this._sourceEditor = new Editor(config);
 
     sourceEditor.on("dirty-change", this._onPropertyChange);
--- a/browser/devtools/styleeditor/test/browser.ini
+++ b/browser/devtools/styleeditor/test/browser.ini
@@ -43,16 +43,17 @@ support-files =
   sourcemaps-watching.html
   test_private.css
   test_private.html
   doc_uncached.css
   doc_uncached.html
   doc_xulpage.xul
 
 [browser_styleeditor_autocomplete.js]
+[browser_styleeditor_autocomplete-disabled.js]
 [browser_styleeditor_bug_740541_iframes.js]
 skip-if = os == "linux" || "mac" # bug 949355
 [browser_styleeditor_bug_851132_middle_click.js]
 [browser_styleeditor_bug_870339.js]
 [browser_styleeditor_cmd_edit.js]
 skip-if = e10s # Bug 1055333 - style editor tests disabled with e10s
 [browser_styleeditor_enabled.js]
 [browser_styleeditor_fetch-from-cache.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_autocomplete-disabled.js
@@ -0,0 +1,26 @@
+/* vim: set ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that autocomplete can be disabled.
+
+const TESTCASE_URI = TEST_BASE_HTTP + "autocomplete.html";
+
+// Pref which decides if CSS autocompletion is enabled in Style Editor or not.
+const AUTOCOMPLETION_PREF = "devtools.styleeditor.autocompletion-enabled";
+
+add_task(function* () {
+  Services.prefs.setBoolPref(AUTOCOMPLETION_PREF, false);
+  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
+  let editor = yield ui.editors[0].getSourceEditor();
+
+  is(editor.sourceEditor.getOption("autocomplete"), false,
+     "Autocompletion option does not exist");
+  ok(!editor.sourceEditor.getAutocompletionPopup(),
+     "Autocompletion popup does not exist");
+});
+
+registerCleanupFunction(() => {
+  Services.prefs.clearUserPref(AUTOCOMPLETION_PREF);
+});
--- a/browser/devtools/styleeditor/test/browser_styleeditor_autocomplete.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_autocomplete.js
@@ -1,25 +1,18 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
-///////////////////
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed. 
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
+// Test that autocompletion works as expected.
 
 const TESTCASE_URI = TEST_BASE_HTTP + "autocomplete.html";
 const MAX_SUGGESTIONS = 15;
 
-// Pref which decides if CSS autocompletion is enabled in Style Editor or not.
-const AUTOCOMPLETION_PREF = "devtools.styleeditor.autocompletion-enabled";
-
 const {CSSProperties, CSSValues} = getCSSKeywords();
 
 // Test cases to test that autocompletion works correctly when enabled.
 // Format:
 // [
 //   key,
 //   {
 //     total: Number of suggestions in the popup (-1 if popup is closed),
@@ -82,45 +75,31 @@ let TEST_CASES = [
   ['VK_RIGHT'],
   ['VK_RIGHT'],
   ['VK_RIGHT'],
   ['VK_RIGHT'],
   ['VK_RIGHT'],
   ['Ctrl+Space', {total: 1, current: 0}],
 ];
 
-let gEditor;
-let gPopup;
-let index = 0;
+add_task(function* () {
+  let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
+  let editor = yield ui.editors[0].getSourceEditor();
+  let sourceEditor = editor.sourceEditor;
+  let popup = sourceEditor.getAutocompletionPopup();
 
-function test()
-{
-  waitForExplicitFinish();
-
-  addTabAndOpenStyleEditors(1, testEditorAdded);
-
-  content.location = TESTCASE_URI;
-}
+  yield SimpleTest.promiseFocus(panel.panelWindow);
 
-function testEditorAdded(panel) {
-  info("Editor added, getting the source editor and starting tests");
-  panel.UI.editors[0].getSourceEditor().then(editor => {
-    info("source editor found, starting tests.");
-    gEditor = editor.sourceEditor;
-    gPopup = gEditor.getAutocompletionPopup();
-    waitForFocus(testState, gPanelWindow);
-  });
-}
+  for (let index in TEST_CASES) {
+    yield testState(index, sourceEditor, popup, panel.panelWindow);
+    yield checkState(index, sourceEditor, popup);
+  }
+});
 
-function testState() {
-  if (index == TEST_CASES.length) {
-    testAutocompletionDisabled();
-    return;
-  }
-
+function testState(index, sourceEditor, popup, panelWindow) {
   let [key, details] = TEST_CASES[index];
   let entered;
   if (details) {
     entered = details.entered;
   }
   let mods = {};
 
   info("pressing key " + key + " to get result: " +
@@ -131,90 +110,64 @@ function testState() {
   if (key == 'Ctrl+Space') {
     key = " ";
     mods.ctrlKey = true;
   }
   else if (key == "VK_RETURN" && entered) {
     evt = "popup-hidden";
   }
   else if (/(left|right|return|home|end)/ig.test(key) ||
-           (key == "VK_DOWN" && !gPopup.isOpen)) {
+           (key == "VK_DOWN" && !popup.isOpen)) {
     evt = "cursorActivity";
   }
   else if (key == "VK_TAB" || key == "VK_UP" || key == "VK_DOWN") {
     evt = "suggestion-entered";
   }
 
-  gEditor.once(evt, checkState);
-  EventUtils.synthesizeKey(key, mods, gPanelWindow);
+  let ready = sourceEditor.once(evt);
+  EventUtils.synthesizeKey(key, mods, panelWindow);
+
+  return ready;
 }
 
-function checkState() {
+function checkState(index, sourceEditor, popup) {
+  let deferred = promise.defer();
   executeSoon(() => {
     let [key, details] = TEST_CASES[index];
     details = details || {};
     let {total, current, inserted} = details;
 
     if (total != undefined) {
-      ok(gPopup.isOpen, "Popup is open for index " + index);
-      is(total, gPopup.itemCount,
+      ok(popup.isOpen, "Popup is open for index " + index);
+      is(total, popup.itemCount,
          "Correct total suggestions for index " + index);
-      is(current, gPopup.selectedIndex,
+      is(current, popup.selectedIndex,
          "Correct index is selected for index " + index);
       if (inserted) {
-        let { preLabel, label, text } = gPopup.getItemAtIndex(current);
-        let { line, ch } = gEditor.getCursor();
-        let lineText = gEditor.getText(line);
+        let { preLabel, label, text } = popup.getItemAtIndex(current);
+        let { line, ch } = sourceEditor.getCursor();
+        let lineText = sourceEditor.getText(line);
         is(lineText.substring(ch - text.length, ch), text,
            "Current suggestion from the popup is inserted into the editor.");
       }
     }
     else {
-      ok(!gPopup.isOpen, "Popup is closed for index " + index);
+      ok(!popup.isOpen, "Popup is closed for index " + index);
       if (inserted) {
-        let { preLabel, label, text } = gPopup.getItemAtIndex(current);
-        let { line, ch } = gEditor.getCursor();
-        let lineText = gEditor.getText(line);
+        let { preLabel, label, text } = popup.getItemAtIndex(current);
+        let { line, ch } = sourceEditor.getCursor();
+        let lineText = sourceEditor.getText(line);
         is(lineText.substring(ch - text.length, ch), text,
            "Current suggestion from the popup is inserted into the editor.");
       }
     }
-    index++;
-    testState();
+    deferred.resolve();
   });
-}
-
-function testAutocompletionDisabled() {
-  gBrowser.removeCurrentTab();
-
-  index = 0;
-  info("Starting test to check if autocompletion is disabled correctly.")
-  Services.prefs.setBoolPref(AUTOCOMPLETION_PREF, false);
-
-  addTabAndOpenStyleEditors(1, testEditorAddedDisabled);
-
-  content.location = TESTCASE_URI;
-}
 
-function testEditorAddedDisabled(panel) {
-  info("Editor added, getting the source editor and starting tests");
-  panel.UI.editors[0].getSourceEditor().then(editor => {
-    is(editor.sourceEditor.getOption("autocomplete"), false,
-       "Autocompletion option does not exist");
-    ok(!editor.sourceEditor.getAutocompletionPopup(),
-       "Autocompletion popup does not exist");
-    cleanup();
-  });
-}
-
-function cleanup() {
-  Services.prefs.clearUserPref(AUTOCOMPLETION_PREF);
-  gEditor = null;
-  gPopup = null;
-  finish();
+  return deferred.promise;
 }
 
 /**
  * Returns a list of all property names and a map of property name vs possible
  * CSS values provided by the Gecko engine.
  *
  * @return {Object} An object with following properties:
  *         - CSSProperties {Array} Array of string containing all the possible
--- a/browser/devtools/styleeditor/test/browser_styleeditor_bug_740541_iframes.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_bug_740541_iframes.js
@@ -1,12 +1,15 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
-function test()
+// Test that sheets inside iframes are shown in the editor.
+
+add_task(function* ()
 {
 
   function makeStylesheet(selector) {
     return ("data:text/css;charset=UTF-8," +
             encodeURIComponent(selector + " { }"));
   }
 
   function makeDocument(stylesheets, framedDocuments) {
@@ -64,15 +67,13 @@ function test()
                   [makeDocument([makeStylesheet(".c")],
                                 [])]),
      makeDocument([SIMPLE], []),
      SIMPLE_DOCUMENT
     ]);
 
   const EXPECTED_STYLE_SHEET_COUNT = 12;
 
-  waitForExplicitFinish();
+  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
 
-  // Wait for events until the right number of editors has been opened.
-  addTabAndOpenStyleEditors(EXPECTED_STYLE_SHEET_COUNT, () => finish());
-
-  content.location = TESTCASE_URI;
-}
+  is(ui.editors.length, EXPECTED_STYLE_SHEET_COUNT,
+    "Got the expected number of style sheets.");
+});
--- a/browser/devtools/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_bug_851132_middle_click.js
@@ -1,75 +1,56 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
-///////////////////
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed. 
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
+// Test that middle click on style sheet doesn't open styleeditor.xul in a new
+// tab (bug 851132).
 
 const TESTCASE_URI = TEST_BASE_HTTP + "four.html";
 
-let gUI;
-
-function test() {
-  waitForExplicitFinish();
-
-  addTabAndOpenStyleEditors(4, runTests);
-
-  content.location = TESTCASE_URI;
-}
-
-let timeoutID;
+add_task(function* () {
+  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
+  gBrowser.tabContainer.addEventListener("TabOpen", onTabAdded, false);
 
-function runTests(panel) {
-  gUI = panel.UI;
-
-  gBrowser.tabContainer.addEventListener("TabOpen", onTabAdded, false);
-  gUI.editors[0].getSourceEditor().then(onEditor0Attach);
-  gUI.editors[1].getSourceEditor().then(onEditor1Attach);
-}
-
-function getStylesheetNameLinkFor(aEditor) {
-  return aEditor.summary.querySelector(".stylesheet-name");
-}
-
-function onEditor0Attach(aEditor) {
+  yield ui.editors[0].getSourceEditor();
   info("first editor selected");
 
-  waitForFocus(function () {
-    // left mouse click should focus editor 1
-    EventUtils.synthesizeMouseAtCenter(
-      getStylesheetNameLinkFor(gUI.editors[1]),
-      {button: 0},
-      gPanelWindow);
-  }, gPanelWindow);
-}
+  info("Left-clicking on the second editor link.");
+  yield clickOnStyleSheetLink(ui.editors[1], 0);
+
+  info("Waiting for the second editor to be selected.");
+  let editor = yield ui.once("editor-selected");
 
-function onEditor1Attach(aEditor) {
-  info("second editor selected");
+  ok(editor.sourceEditor.hasFocus(),
+     "Left mouse click gave second editor focus.");
+
+  // middle mouse click should not open a new tab
+  info("Middle clicking on the third editor link.");
+  yield clickOnStyleSheetLink(ui.editors[2], 1);
+});
 
-  // Wait for the focus to be set.
-  executeSoon(function () {
-    ok(aEditor.sourceEditor.hasFocus(),
-       "left mouse click has given editor 1 focus");
+/**
+ * A helper that clicks on style sheet link in the sidebar.
+ *
+ * @param {StyleSheetEditor} editor
+ *        The editor of which link should be clicked.
+ * @param {MouseEvent.button} button
+ *        The button to click the link with.
+ */
+function* clickOnStyleSheetLink(editor, button) {
+  let window = editor._window;
+  let link = editor.summary.querySelector(".stylesheet-name");
 
-    // right mouse click should not open a new tab
-    EventUtils.synthesizeMouseAtCenter(
-      getStylesheetNameLinkFor(gUI.editors[2]),
-      {button: 1},
-      gPanelWindow);
+  info("Waiting for focus.");
+  yield SimpleTest.promiseFocus(window);
 
-    setTimeout(finish, 0);
-  });
+  info("Pressing button " + button + " on style sheet name link.");
+  EventUtils.synthesizeMouseAtCenter(link, { button }, window);
 }
 
 function onTabAdded() {
   ok(false, "middle mouse click has opened a new tab");
-  finish();
 }
 
 registerCleanupFunction(function () {
   gBrowser.tabContainer.removeEventListener("TabOpen", onTabAdded, false);
-  gUI = null;
 });
--- a/browser/devtools/styleeditor/test/browser_styleeditor_enabled.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_enabled.js
@@ -1,70 +1,55 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that style sheets can be disabled and enabled.
 
 // https rather than chrome to improve coverage
 const TESTCASE_URI = TEST_BASE_HTTPS + "simple.html";
 
-function test()
-{
-  waitForExplicitFinish();
+add_task(function* () {
+  let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
+  let editor = yield ui.editors[0].getSourceEditor();
 
-  let count = 0;
-  addTabAndOpenStyleEditors(2, function(panel) {
-    // we test against first stylesheet after all are ready
-    let UI = panel.UI;
-    let editor = UI.editors[0];
-    editor.getSourceEditor().then(runTests.bind(this, UI, editor));
-  });
-
-  content.location = TESTCASE_URI;
-}
-
-function runTests(UI, editor)
-{
-  testEnabledToggle(UI, editor);
-}
-
-function testEnabledToggle(UI, editor)
-{
   let summary = editor.summary;
   let enabledToggle = summary.querySelector(".stylesheet-enabled");
   ok(enabledToggle, "enabled toggle button exists");
 
   is(editor.styleSheet.disabled, false,
      "first stylesheet is initially enabled");
 
   is(summary.classList.contains("disabled"), false,
      "first stylesheet is initially enabled, UI does not have DISABLED class");
 
-  let disabledToggleCount = 0;
-  editor.on("property-change", function(event, property) {
-    if (property != "disabled") {
-      return;
-    }
-    disabledToggleCount++;
+  info("Disabling the first stylesheet.");
+  yield toggleEnabled(editor, enabledToggle, panel.panelWindow);
+
+  is(editor.styleSheet.disabled, true, "first stylesheet is now disabled");
+  is(summary.classList.contains("disabled"), true,
+     "first stylesheet is now disabled, UI has DISABLED class");
 
-    if (disabledToggleCount == 1) {
-      is(editor.styleSheet.disabled, true, "first stylesheet is now disabled");
-      is(summary.classList.contains("disabled"), true,
-         "first stylesheet is now disabled, UI has DISABLED class");
+  info("Enabling the first stylesheet again.");
+  yield toggleEnabled(editor, enabledToggle, panel.panelWindow);
+
+  is(editor.styleSheet.disabled, false, "first stylesheet is now enabled again");
+  is(summary.classList.contains("disabled"), false,
+     "first stylesheet is now enabled again, UI does not have DISABLED class");
+});
 
-      // now toggle it back to enabled
-      waitForFocus(function () {
-        EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gPanelWindow);
-      }, gPanelWindow);
-      return;
-    }
+function* toggleEnabled(editor, enabledToggle, panelWindow) {
+  let changed = editor.once("property-change");
+
+  info("Waiting for focus.");
+  yield SimpleTest.promiseFocus(panelWindow);
 
-    // disabledToggleCount == 2
-    is(editor.styleSheet.disabled, false, "first stylesheet is now enabled again");
-    is(summary.classList.contains("disabled"), false,
-       "first stylesheet is now enabled again, UI does not have DISABLED class");
+  info("Clicking on the toggle.");
+  EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, panelWindow);
 
-    finish();
-  });
-
-  waitForFocus(function () {
-    EventUtils.synthesizeMouseAtCenter(enabledToggle, {}, gPanelWindow);
-  }, gPanelWindow);
+  info("Waiting for stylesheet to be disabled.");
+  let property = yield changed;
+  while (property !== "disabled") {
+    info("Ignoring property-change for '" + property + "'.");
+    property = yield editor.once("property-change");
+  }
 }
--- a/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_filesave.js
@@ -1,68 +1,70 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that 'Save' function works.
 
 const TESTCASE_URI_HTML = TEST_BASE_HTTP + "simple.html";
 const TESTCASE_URI_CSS = TEST_BASE_HTTP + "simple.css";
 
 const Cc = Components.classes;
 const Ci = Components.interfaces;
 
 let tempScope = {};
 Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
 Components.utils.import("resource://gre/modules/NetUtil.jsm", tempScope);
 let FileUtils = tempScope.FileUtils;
 let NetUtil = tempScope.NetUtil;
 
-function test()
-{
-  waitForExplicitFinish();
-
-  copy(TESTCASE_URI_HTML, "simple.html", function(htmlFile) {
-    copy(TESTCASE_URI_CSS, "simple.css", function(cssFile) {
-      addTabAndOpenStyleEditors(1, function(panel) {
-        let UI = panel.UI;
-        let editor = UI.editors[0];
-        editor.getSourceEditor().then(runTests.bind(this, editor));
-      });
+add_task(function* () {
+  let htmlFile = yield copy(TESTCASE_URI_HTML, "simple.html");
+  let cssFile = yield copy(TESTCASE_URI_CSS, "simple.css");
+  let uri = Services.io.newFileURI(htmlFile);
+  let filePath = uri.resolve("");
 
-      let uri = Services.io.newFileURI(htmlFile);
-      let filePath = uri.resolve("");
-      content.location = filePath;
-    });
-  });
-}
+  let { ui } = yield openStyleEditorForURL(filePath);
 
-function runTests(editor)
-{
-  editor.sourceEditor.once("dirty-change", () => {
-    is(editor.sourceEditor.isClean(), false, "Editor is dirty.");
-    ok(editor.summary.classList.contains("unsaved"),
-       "Star icon is present in the corresponding summary.");
-  });
+  let editor = ui.editors[0];
+  yield editor.getSourceEditor();
+
+  info("Editing the style sheet.");
+  let dirty = editor.sourceEditor.once("dirty-change");
   let beginCursor = {line: 0, ch: 0};
   editor.sourceEditor.replaceText("DIRTY TEXT", beginCursor, beginCursor);
 
-  editor.sourceEditor.once("dirty-change", () => {
-    is(editor.sourceEditor.isClean(), true, "Editor is clean.");
-    ok(!editor.summary.classList.contains("unsaved"),
-       "Star icon is not present in the corresponding summary.");
-    finish();
-  });
+  yield dirty;
+
+  is(editor.sourceEditor.isClean(), false, "Editor is dirty.");
+  ok(editor.summary.classList.contains("unsaved"),
+     "Star icon is present in the corresponding summary.");
+
+  info("Saving the changes.");
+  dirty = editor.sourceEditor.once("dirty-change");
+
   editor.saveToFile(null, function (file) {
     ok(file, "file should get saved directly when using a file:// URI");
   });
-}
+
+  yield dirty;
+
+  is(editor.sourceEditor.isClean(), true, "Editor is clean.");
+  ok(!editor.summary.classList.contains("unsaved"),
+     "Star icon is not present in the corresponding summary.");
+});
 
-function copy(aSrcChromeURL, aDestFileName, aCallback)
+function copy(aSrcChromeURL, aDestFileName)
 {
+  let deferred = promise.defer();
   let destFile = FileUtils.getFile("ProfD", [aDestFileName]);
-  write(read(aSrcChromeURL), destFile, aCallback);
+  write(read(aSrcChromeURL), destFile, deferred.resolve);
+
+  return deferred.promise;
 }
 
 function read(aSrcChromeURL)
 {
   let scriptableStream = Cc["@mozilla.org/scriptableinputstream;1"]
     .getService(Ci.nsIScriptableInputStream);
 
   let channel = Services.io.newChannel2(aSrcChromeURL,
--- a/browser/devtools/styleeditor/test/browser_styleeditor_import.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_import.js
@@ -1,73 +1,56 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that the import button in the UI works.
 
 // http rather than chrome to improve coverage
 const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
 
 let tempScope = {};
 Components.utils.import("resource://gre/modules/FileUtils.jsm", tempScope);
 let FileUtils = tempScope.FileUtils;
 
 const FILENAME = "styleeditor-import-test.css";
 const SOURCE = "body{background:red;}";
 
-
-let gUI;
+add_task(function* () {
+  let { panel, ui } = yield openStyleEditorForURL(TESTCASE_URI);
 
-function test()
-{
-  waitForExplicitFinish();
+  let added = ui.once("editor-added");
+  importSheet(ui, panel.panelWindow);
 
-  addTabAndCheckOnStyleEditorAdded(panel => gUI = panel.UI, testEditorAdded);
+  info("Waiting for editor to be added for the imported sheet.");
+  let editor = yield added;
 
-  content.location = TESTCASE_URI;
-}
+  is(editor.savedFile.leafName, FILENAME,
+     "imported stylesheet will be saved directly into the same file");
+  is(editor.friendlyName, FILENAME,
+     "imported stylesheet has the same name as the filename");
+});
 
-function testImport()
+function importSheet(ui, panelWindow)
 {
   // create file to import first
   let file = FileUtils.getFile("ProfD", [FILENAME]);
   let ostream = FileUtils.openSafeFileOutputStream(file);
   let converter = Cc["@mozilla.org/intl/scriptableunicodeconverter"]
                     .createInstance(Ci.nsIScriptableUnicodeConverter);
   converter.charset = "UTF-8";
   let istream = converter.convertToInputStream(SOURCE);
   NetUtil.asyncCopy(istream, ostream, function (status) {
     FileUtils.closeSafeFileOutputStream(ostream);
 
     // click the import button now that the file to import is ready
-    gUI._mockImportFile = file;
+    ui._mockImportFile = file;
 
     waitForFocus(function () {
-      let document = gPanelWindow.document
+      let document = panelWindow.document
       let importButton = document.querySelector(".style-editor-importButton");
       ok(importButton, "import button exists");
 
-      EventUtils.synthesizeMouseAtCenter(importButton, {}, gPanelWindow);
-    }, gPanelWindow);
+      EventUtils.synthesizeMouseAtCenter(importButton, {}, panelWindow);
+    }, panelWindow);
   });
 }
-
-let gAddedCount = 0;
-function testEditorAdded(aEditor)
-{
-  if (++gAddedCount == 2) {
-    // test import after the 2 initial stylesheets have been loaded
-    gUI.editors[0].getSourceEditor().then(function() {
-      testImport();
-    });
-  }
-
-  if (!aEditor.savedFile) {
-    return;
-  }
-
-  is(aEditor.savedFile.leafName, FILENAME,
-     "imported stylesheet will be saved directly into the same file");
-  is(aEditor.friendlyName, FILENAME,
-     "imported stylesheet has the same name as the filename");
-
-  gUI = null;
-  finish();
-}
--- a/browser/devtools/styleeditor/test/browser_styleeditor_import_rule.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_import_rule.js
@@ -1,37 +1,25 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that style editor shows sheets loaded with @import rules.
 
 // http rather than chrome to improve coverage
 const TESTCASE_URI = TEST_BASE_HTTP + "import.html";
 
-let gUI;
-
-function test()
-{
-  waitForExplicitFinish();
-
-  addTabAndOpenStyleEditors(3, onEditorAdded);
+add_task(function* () {
+  let { ui } = yield openStyleEditorForURL(TESTCASE_URI);
 
-  content.location = TESTCASE_URI;
-}
-
-function onEditorAdded(panel)
-{
-  gUI = panel.UI;
-
-  is(gUI.editors.length, 3,
+  is(ui.editors.length, 3,
     "there are 3 stylesheets after loading @imports");
 
-  is(gUI.editors[0].styleSheet.href, TEST_BASE_HTTP + "simple.css",
+  is(ui.editors[0].styleSheet.href, TEST_BASE_HTTP + "simple.css",
     "stylesheet 1 is simple.css");
 
-  is(gUI.editors[1].styleSheet.href, TEST_BASE_HTTP + "import.css",
+  is(ui.editors[1].styleSheet.href, TEST_BASE_HTTP + "import.css",
     "stylesheet 2 is import.css");
 
-  is(gUI.editors[2].styleSheet.href, TEST_BASE_HTTP + "import2.css",
+  is(ui.editors[2].styleSheet.href, TEST_BASE_HTTP + "import2.css",
     "stylesheet 3 is import2.css");
-
-  gUI = null;
-  finish();
-}
+});
--- a/browser/devtools/styleeditor/test/browser_styleeditor_inline_friendly_names.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_inline_friendly_names.js
@@ -1,155 +1,99 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
 
-///////////////////
-//
-// Whitelisting this test.
-// As part of bug 1077403, the leaking uncaught rejection should be fixed. 
-//
-thisTestLeaksUncaughtRejectionsAndShouldBeFixed("Error: Unknown sheet source");
+// Test that inline style sheets get correct names if they are saved to disk and
+// that those names survice a reload but not navigation to another page.
 
-let gUI;
-
-const FIRST_TEST_PAGE = TEST_BASE_HTTP + "inline-1.html"
-const SECOND_TEST_PAGE = TEST_BASE_HTTP + "inline-2.html"
+const FIRST_TEST_PAGE = TEST_BASE_HTTP + "inline-1.html";
+const SECOND_TEST_PAGE = TEST_BASE_HTTP + "inline-2.html";
 const SAVE_PATH = "test.css";
 
-function test()
-{
-  waitForExplicitFinish();
+add_task(function* () {
+  let { ui } = yield openStyleEditorForURL(FIRST_TEST_PAGE);
 
-  addTabAndOpenStyleEditors(2, function(panel) {
-    gUI = panel.UI;
-
-    // First test that identifiers are correcly generated. If not other tests
-    // are likely to fail.
-    testIndentifierGeneration();
+  loadCommonFrameScript();
+  testIndentifierGeneration(ui);
 
-    saveFirstInlineStyleSheet()
-    .then(testFriendlyNamesAfterSave)
-    .then(reloadPage)
-    .then(testFriendlyNamesAfterSave)
-    .then(navigateToAnotherPage)
-    .then(testFriendlyNamesAfterNavigation)
-    .then(finishTests);
-  });
+  yield saveFirstInlineStyleSheet(ui);
+  yield testFriendlyNamesAfterSave(ui);
+  yield reloadPage(ui);
+  yield testFriendlyNamesAfterSave(ui);
+  yield navigateToAnotherPage(ui);
+  yield testFriendlyNamesAfterNavigation(ui);
+});
 
-  content.location = FIRST_TEST_PAGE;
-}
-
-function testIndentifierGeneration() {
+function testIndentifierGeneration(ui) {
   let fakeStyleSheetFile = {
     "href": "http://example.com/test.css",
     "nodeHref": "http://example.com/",
     "styleSheetIndex": 1
-  }
+  };
 
   let fakeInlineStyleSheet = {
     "href": null,
     "nodeHref": "http://example.com/",
     "styleSheetIndex": 2
-  }
+  };
 
-  is(gUI.getStyleSheetIdentifier(fakeStyleSheetFile), "http://example.com/test.css",
+  is(ui.getStyleSheetIdentifier(fakeStyleSheetFile), "http://example.com/test.css",
     "URI is the identifier of style sheet file.");
 
-  is(gUI.getStyleSheetIdentifier(fakeInlineStyleSheet), "inline-2-at-http://example.com/",
+  is(ui.getStyleSheetIdentifier(fakeInlineStyleSheet), "inline-2-at-http://example.com/",
     "Inline style sheets are identified by their page and position at that page.");
 }
 
-function saveFirstInlineStyleSheet() {
+function saveFirstInlineStyleSheet(ui) {
   let deferred = promise.defer();
-  let editor = gUI.editors[0];
+  let editor = ui.editors[0];
 
   let destFile = FileUtils.getFile("ProfD", [SAVE_PATH]);
 
   editor.saveToFile(destFile, function (file) {
     ok(file, "File was correctly saved.");
     deferred.resolve();
   });
 
   return deferred.promise;
 }
 
-function testFriendlyNamesAfterSave() {
-  let firstEditor = gUI.editors[0];
-  let secondEditor = gUI.editors[1];
+function testFriendlyNamesAfterSave(ui) {
+  let firstEditor = ui.editors[0];
+  let secondEditor = ui.editors[1];
 
   // The friendly name of first sheet should've been remembered, the second should
   // not be the same (bug 969900).
   is(firstEditor.friendlyName, SAVE_PATH,
     "Friendly name is correct for the saved inline style sheet.");
   isnot(secondEditor.friendlyName, SAVE_PATH,
     "Friendly name is for the second inline style sheet is not the same as first.");
 
   return promise.resolve(null);
 }
 
-function reloadPage() {
+function reloadPage(ui) {
   info("Reloading page.");
-  content.location.reload();
-  return waitForEditors(2);
+  executeInContent("devtools:test:reload", {}, {}, false /* no response */);
+  return ui.once("stylesheets-reset");
 }
 
-function navigateToAnotherPage() {
+function navigateToAnotherPage(ui) {
   info("Navigating to another page.");
-  let deferred = promise.defer();
-  gBrowser.removeCurrentTab();
-
-  gUI = null;
-
-  addTabAndOpenStyleEditors(2, function(panel) {
-    gUI = panel.UI;
-    deferred.resolve();
-  });
-
-  content.location = SECOND_TEST_PAGE;
-  return deferred.promise;
+  executeInContent("devtools:test:navigate", { location: SECOND_TEST_PAGE }, {}, false);
+  return ui.once("stylesheets-reset");
 }
 
-function testFriendlyNamesAfterNavigation() {
-  let firstEditor = gUI.editors[0];
-  let secondEditor = gUI.editors[1];
+function testFriendlyNamesAfterNavigation(ui) {
+  let firstEditor = ui.editors[0];
+  let secondEditor = ui.editors[1];
 
   // Inline style sheets shouldn't have the name of previously saved file as the
   // page is different.
   isnot(firstEditor.friendlyName, SAVE_PATH,
     "The first editor doesn't have the save path as a friendly name.");
   isnot(secondEditor.friendlyName, SAVE_PATH,
     "The second editor doesn't have the save path as a friendly name.");
 
   return promise.resolve(null);
 }
-
-function finishTests() {
-  gUI = null;
-  finish();
-}
-
-/**
- * Waits for all editors to be added.
- *
- * @param {int} aNumberOfEditors
- *        The number of editors to wait until proceeding.
- *
- * Returns a promise that's resolved once all editors are added.
- */
-function waitForEditors(aNumberOfEditors) {
-  let deferred = promise.defer();
-  let count = 0;
-
-  info("Waiting for " + aNumberOfEditors + " editors to be added");
-  gUI.on("editor-added", function editorAdded(event, editor) {
-    if (++count == aNumberOfEditors) {
-        info("All editors added. Resolving promise.");
-        gUI.off("editor-added", editorAdded);
-        gUI.editors[0].getSourceEditor().then(deferred.resolve);
-    }
-    else {
-        info ("Editor " + count + " of " + aNumberOfEditors + " added.");
-    }
-  });
-
-  return deferred.promise;
-}
--- a/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
+++ b/browser/devtools/styleeditor/test/browser_styleeditor_sv_resize.js
@@ -1,55 +1,44 @@
 /* vim: set ts=2 et sw=2 tw=80: */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+// Test that resizing the source editor container doesn't move the caret.
+
 const TESTCASE_URI = TEST_BASE_HTTP + "simple.html";
 
-let gOriginalWidth; // these are set by runTests()
-let gOriginalHeight;
+add_task(function* () {
+  let { toolbox, ui } = yield openStyleEditorForURL(TESTCASE_URI);
 
-function test()
-{
-  waitForExplicitFinish();
+  is(ui.editors.length, 2, "There are 2 style sheets initially");
+
+  info("Changing toolbox host to a window.");
+  yield toolbox.switchHost(devtools.Toolbox.HostType.WINDOW);
 
-  addTabAndOpenStyleEditors(2, panel => runTests(panel.UI));
-
-  content.location = TESTCASE_URI;
-}
+  let editor = yield ui.editors[0].getSourceEditor();
+  let originalSourceEditor = editor.sourceEditor;
 
-function runTests(aUI)
-{
-  is(aUI.editors.length, 2,
-     "there is 2 stylesheets initially");
+  let hostWindow = toolbox._host._window;
+  let originalWidth = hostWindow.outerWidth;
+  let originalHeight = hostWindow.outerHeight;
+
+  // to check the caret is preserved
+  originalSourceEditor.setCursor(originalSourceEditor.getPosition(4));
 
-  aUI.editors[0].getSourceEditor().then(aEditor => {
-    executeSoon(function () {
-      waitForFocus(function () {
-        // queue a resize to inverse aspect ratio
-        // this will trigger a detach and reattach (to workaround bug 254144)
-        let originalSourceEditor = aEditor.sourceEditor;
-        let editor = aEditor.sourceEditor;
-        editor.setCursor(editor.getPosition(4)); // to check the caret is preserved
+  info("Resizing window.");
+  hostWindow.resizeTo(120, 480);
 
-        gOriginalWidth = gPanelWindow.outerWidth;
-        gOriginalHeight = gPanelWindow.outerHeight;
-        gPanelWindow.resizeTo(120, 480);
+  let sourceEditor = ui.editors[0].sourceEditor;
+  is(sourceEditor, originalSourceEditor,
+     "the editor still references the same Editor instance");
 
-        executeSoon(function () {
-          is(aEditor.sourceEditor, originalSourceEditor,
-             "the editor still references the same Editor instance");
-          let editor = aEditor.sourceEditor;
-          is(editor.getOffset(editor.getCursor()), 4,
-             "the caret position has been preserved");
+  is(sourceEditor.getOffset(sourceEditor.getCursor()), 4,
+     "the caret position has been preserved");
 
-          // queue a resize to original aspect ratio
-          waitForFocus(function () {
-            gPanelWindow.resizeTo(gOriginalWidth, gOriginalHeight);
-            executeSoon(function () {
-              finish();
-            });
-          }, gPanelWindow);
-        });
-      }, gPanelWindow);
-    });
-  });
-}
+  info("Restoring window to original size.");
+  hostWindow.resizeTo(originalWidth, originalHeight);
+});
+
+registerCleanupFunction(() => {
+  // Restore the host type for other tests.
+  Services.prefs.clearUserPref("devtools.toolbox.host");
+});
--- a/browser/devtools/styleinspector/computed-view.js
+++ b/browser/devtools/styleinspector/computed-view.js
@@ -975,17 +975,16 @@ function createMenuItem(aMenu, aAttribut
 function PropertyView(aTree, aName)
 {
   this.tree = aTree;
   this.name = aName;
   this.getRTLAttr = aTree.getRTLAttr;
 
   this.link = "https://developer.mozilla.org/CSS/" + aName;
 
-  this.templateMatchedSelectors = aTree.styleDocument.getElementById("templateMatchedSelectors");
   this._propertyInfo = new PropertyInfo(aTree, aName);
 }
 
 PropertyView.prototype = {
   // The parent element which contains the open attribute
   element: null,
 
   // Property header node
@@ -1187,16 +1186,17 @@ PropertyView.prototype = {
 
     this.tree.numVisibleProperties++;
 
     let outputParser = this.tree._outputParser;
     let frag = outputParser.parseCssProperty(this.propertyInfo.name,
       this.propertyInfo.value,
       {
         colorSwatchClass: "computedview-colorswatch",
+        colorClass: "computedview-color",
         urlClass: "theme-link"
         // No need to use baseURI here as computed URIs are never relative.
       });
     this.valueNode.innerHTML = "";
     this.valueNode.appendChild(frag);
 
     this.refreshMatchedSelectors();
   },
@@ -1217,34 +1217,69 @@ PropertyView.prototype = {
 
     if (this.matchedExpanded && hasMatchedSelectors) {
       return this.tree.pageStyle.getMatchedSelectors(this.tree.viewedElement, this.name).then(matched => {
         if (!this.matchedExpanded) {
           return;
         }
 
         this._matchedSelectorResponse = matched;
-        CssHtmlTree.processTemplate(this.templateMatchedSelectors,
-          this.matchedSelectorsContainer, this);
+
+        this._buildMatchedSelectors();
         this.matchedExpander.setAttribute("open", "");
+
         this.tree.inspector.emit("computed-view-property-expanded");
       }).then(null, console.error);
     } else {
       this.matchedSelectorsContainer.innerHTML = "";
       this.matchedExpander.removeAttribute("open");
       this.tree.inspector.emit("computed-view-property-collapsed");
       return promise.resolve(undefined);
     }
   },
 
   get matchedSelectors()
   {
     return this._matchedSelectorResponse;
   },
 
+  _buildMatchedSelectors: function() {
+    let frag = this.element.ownerDocument.createDocumentFragment();
+
+    for (let selector of this.matchedSelectorViews) {
+      let p = createChild(frag, "p");
+      let span = createChild(p, "span", {
+        class: "rule-link"
+      });
+      let link = createChild(span, "a", {
+        target: "_blank",
+        class: "link theme-link",
+        title: selector.href,
+        sourcelocation: selector.source,
+        tabindex: "0",
+        textContent: selector.source
+      });
+      link.addEventListener("click", selector.openStyleEditor, false);
+      link.addEventListener("keydown", selector.maybeOpenStyleEditor, false);
+
+      let status = createChild(p, "span", {
+        dir: "ltr",
+        class: "rule-text theme-fg-color3 " + selector.statusClass,
+        title: selector.statusText,
+        textContent: selector.sourceText
+      });
+      let valueSpan = createChild(status, "span", {
+        class: "other-property-value theme-fg-color1"
+      });
+      valueSpan.appendChild(selector.outputFragment);
+    }
+
+    this.matchedSelectorsContainer.appendChild(frag);
+  },
+
   /**
    * Provide access to the matched SelectorViews that we are currently
    * displaying.
    */
   get matchedSelectorViews()
   {
     if (!this._matchedSelectorViews) {
       this._matchedSelectorViews = [];
@@ -1274,16 +1309,19 @@ PropertyView.prototype = {
   /**
    * The action when a user expands matched selectors.
    *
    * @param {Event} aEvent Used to determine the class name of the targets click
    * event.
    */
   onMatchedToggle: function PropertyView_onMatchedToggle(aEvent)
   {
+    if (aEvent.shiftKey) {
+      return;
+    }
     this.matchedExpanded = !this.matchedExpanded;
     this.refreshMatchedSelectors();
     aEvent.preventDefault();
   },
 
   /**
    * The action when a user clicks on the MDN help link for a property.
    */
@@ -1323,16 +1361,19 @@ PropertyView.prototype = {
  * @param aSelectorInfo
  */
 function SelectorView(aTree, aSelectorInfo)
 {
   this.tree = aTree;
   this.selectorInfo = aSelectorInfo;
   this._cacheStatusNames();
 
+  this.openStyleEditor = this.openStyleEditor.bind(this);
+  this.maybeOpenStyleEditor = this.maybeOpenStyleEditor.bind(this);
+
   this.updateSourceLink();
 }
 
 /**
  * Decode for cssInfo.rule.status
  * @see SelectorView.prototype._cacheStatusNames
  * @see CssLogic.STATUS
  */
@@ -1413,16 +1454,17 @@ SelectorView.prototype = {
     // we lose any events that are attached. This means that URLs will open in a
     // new window. At some point we should fix this by stopping using the
     // templater.
     let outputParser = this.tree._outputParser;
     let frag = outputParser.parseCssProperty(
       this.selectorInfo.name,
       this.selectorInfo.value, {
       colorSwatchClass: "computedview-colorswatch",
+      colorClass: "computedview-color",
       urlClass: "theme-link",
       baseURI: this.selectorInfo.rule.href
     });
     return frag;
   },
 
   /**
    * Update the text of the source link to reflect whether we're showing
@@ -1535,10 +1577,37 @@ SelectorView.prototype = {
           let sheet = source || href;
           toolbox.getCurrentPanel().selectStyleSheet(sheet, line, column);
         });
       }
     });
   }
 };
 
+/**
+ * Create a child element with a set of attributes.
+ *
+ * @param {Element} aParent
+ *        The parent node.
+ * @param {string} aTag
+ *        The tag name.
+ * @param {object} aAttributes
+ *        A set of attributes to set on the node.
+ */
+function createChild(aParent, aTag, aAttributes={}) {
+  let elt = aParent.ownerDocument.createElementNS(HTML_NS, aTag);
+  for (let attr in aAttributes) {
+    if (aAttributes.hasOwnProperty(attr)) {
+      if (attr === "textContent") {
+        elt.textContent = aAttributes[attr];
+      } else if(attr === "child") {
+        elt.appendChild(aAttributes[attr]);
+      } else {
+        elt.setAttribute(attr, aAttributes[attr]);
+      }
+    }
+  }
+  aParent.appendChild(elt);
+  return elt;
+}
+
 exports.CssHtmlTree = CssHtmlTree;
 exports.PropertyView = PropertyView;
--- a/browser/devtools/styleinspector/computedview.xhtml
+++ b/browser/devtools/styleinspector/computedview.xhtml
@@ -67,43 +67,10 @@
     <div id="propertyContainer" class="devtools-monospace">
     </div>
 
     <!-- When no properties are found the following block is displayed. -->
     <div id="noResults" hidden="">
       &noPropertiesFound;
     </div>
 
-    <!--
-    To visually debug the templates without running firefox, alter the display:none
-    -->
-    <div style="display:none;">
-      <!--
-      A templateMatchedSelectors sits inside each templateProperties showing the
-      list of selectors that affect that property. Each needs data like this:
-      {
-        matchedSelectorViews: ..., // from cssHtmlTree.propertyViews[name].matchedSelectorViews
-      }
-      This is a template so the parent does not need to be a table, except that
-      using a div as the parent causes the DOM to muck with the tr elements
-      -->
-      <div id="templateMatchedSelectors">
-        <loop foreach="selector in ${matchedSelectorViews}">
-          <p>
-            <span class="rule-link">
-              <a target="_blank" class="link theme-link"
-                  onclick="${selector.openStyleEditor}"
-                  onkeydown="${selector.maybeOpenStyleEditor}"
-                  title="${selector.href}"
-                  sourcelocation="${selector.source}"
-                  tabindex="0">${selector.source}</a>
-            </span>
-            <span dir="ltr" class="rule-text ${selector.statusClass} theme-fg-color3" title="${selector.statusText}">
-              ${selector.sourceText}
-              <span class="other-property-value theme-fg-color1">${selector.outputFragment}</span>
-            </span>
-          </p>
-        </loop>
-      </div>
-    </div>
-
   </body>
 </html>
--- a/browser/devtools/styleinspector/test/browser.ini
+++ b/browser/devtools/styleinspector/test/browser.ini
@@ -22,16 +22,17 @@ support-files =
   doc_sourcemaps.scss
   doc_style_editor_link.css
   doc_test_image.png
   doc_urls_clickable.css
   doc_urls_clickable.html
   head.js
 
 [browser_computedview_browser-styles.js]
+[browser_computedview_cycle_color.js]
 [browser_computedview_getNodeInfo.js]
 [browser_computedview_keybindings_01.js]
 [browser_computedview_keybindings_02.js]
 [browser_computedview_matched-selectors-toggle.js]
 [browser_computedview_matched-selectors_01.js]
 [browser_computedview_matched-selectors_02.js]
 [browser_computedview_media-queries.js]
 [browser_computedview_no-results-placeholder.js]
@@ -94,16 +95,17 @@ skip-if = (os == "win" && debug) || e10s
 [browser_ruleview_media-queries.js]
 [browser_ruleview_multiple-properties-duplicates.js]
 [browser_ruleview_multiple-properties-priority.js]
 [browser_ruleview_multiple-properties-unfinished_01.js]
 [browser_ruleview_multiple-properties-unfinished_02.js]
 [browser_ruleview_multiple_properties_01.js]
 [browser_ruleview_multiple_properties_02.js]
 [browser_ruleview_original-source-link.js]
+[browser_ruleview_cycle-color.js]
 [browser_ruleview_override.js]
 [browser_ruleview_pseudo-element_01.js]
 [browser_ruleview_pseudo-element_02.js]
 skip-if = e10s # Bug 1090340
 [browser_ruleview_refresh-on-attribute-change_01.js]
 [browser_ruleview_refresh-on-attribute-change_02.js]
 [browser_ruleview_refresh-on-style-change.js]
 [browser_ruleview_search-filter_01.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_computedview_cycle_color.js
@@ -0,0 +1,68 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Computed view color cycling test.
+
+const PAGE_CONTENT = [
+  "<style type=\"text/css\">",
+  ".matches {color: #F00;}</style>",
+  "<span id=\"matches\" class=\"matches\">Some styled text</span>",
+  "</div>"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8," +
+               "Computed view color cycling test.");
+  content.document.body.innerHTML = PAGE_CONTENT;
+
+  info("Opening the computed view");
+  let {toolbox, inspector, view} = yield openComputedView();
+
+  info("Selecting the test node");
+  yield selectNode("#matches", inspector);
+
+  info("Checking the property itself");
+  let container = getComputedViewPropertyView(view, "color").valueNode;
+  checkColorCycling(container, inspector);
+
+  info("Checking matched selectors");
+  container = yield getComputedViewMatchedRules(view, "color");
+  checkColorCycling(container, inspector);
+});
+
+function checkColorCycling(container, inspector) {
+  let swatch = container.querySelector(".computedview-colorswatch");
+  let valueNode = container.querySelector(".computedview-color");
+  let win = inspector.sidebar.getWindowForTab("computedview");
+
+  // Hex (default)
+  is(valueNode.textContent, "#F00", "Color displayed as a hex value.");
+
+  // HSL
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "hsl(0, 100%, 50%)",
+                            "Color displayed as an HSL value.");
+
+  // RGB
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "rgb(255, 0, 0)",
+                            "Color displayed as an RGB value.");
+
+  // Color name
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "red",
+                            "Color displayed as a color name.");
+
+  // "Authored" (currently the computed value)
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "rgb(255, 0, 0)",
+                            "Color displayed as an RGB value.");
+
+  // Back to hex
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "#F00",
+                            "Color displayed as hex again.");
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/styleinspector/test/browser_ruleview_cycle-color.js
@@ -0,0 +1,60 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test cycling color types in the rule view.
+
+const PAGE_CONTENT = [
+  "<style type=\"text/css\">",
+  "  body {",
+  "    color: #F00;",
+  "  }",
+  "</style>",
+  "Test cycling color types in the rule view!"
+].join("\n");
+
+add_task(function*() {
+  yield addTab("data:text/html;charset=utf-8,Test cycling color types in the " +
+               "rule view.");
+  content.document.body.innerHTML = PAGE_CONTENT;
+  let {toolbox, inspector, view} = yield openRuleView();
+
+  let container = getRuleViewProperty(view, "body", "color").valueSpan;
+  checkColorCycling(container, inspector);
+});
+
+function checkColorCycling(container, inspector) {
+  let swatch = container.querySelector(".ruleview-colorswatch");
+  let valueNode = container.querySelector(".ruleview-color");
+  let win = inspector.sidebar.getWindowForTab("ruleview");
+
+  // Hex (default)
+  is(valueNode.textContent, "#F00", "Color displayed as a hex value.");
+
+  // HSL
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "hsl(0, 100%, 50%)",
+                            "Color displayed as an HSL value.");
+
+  // RGB
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "rgb(255, 0, 0)",
+                            "Color displayed as an RGB value.");
+
+  // Color name
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "red",
+                            "Color displayed as a color name.");
+
+  // "Authored" (currently the computed value)
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "rgb(255, 0, 0)",
+                            "Color displayed as an RGB value.");
+
+  // Back to hex
+  EventUtils.synthesizeMouseAtCenter(swatch, {type: "mousedown", shiftKey: true}, win);
+  is(valueNode.textContent, "#F00",
+                            "Color displayed as hex again.");
+}
--- a/browser/installer/package-manifest.in
+++ b/browser/installer/package-manifest.in
@@ -323,17 +323,19 @@
 @RESPATH@/components/shistory.xpt
 @RESPATH@/components/spellchecker.xpt
 @RESPATH@/components/storage.xpt
 @RESPATH@/components/toolkit_asyncshutdown.xpt
 @RESPATH@/components/toolkit_filewatcher.xpt
 @RESPATH@/components/toolkit_finalizationwitness.xpt
 @RESPATH@/components/toolkit_formautofill.xpt
 @RESPATH@/components/toolkit_osfile.xpt
+#ifdef NIGHTLY_BUILD
 @RESPATH@/components/toolkit_perfmonitoring.xpt
+#endif
 @RESPATH@/components/toolkit_xulstore.xpt
 @RESPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @RESPATH@/components/toolkitremote.xpt
 #endif
 @RESPATH@/components/txtsvc.xpt
 @RESPATH@/components/txmgr.xpt
 @RESPATH@/components/uconv.xpt
--- a/dom/bluetooth/BluetoothCommon.h
+++ b/dom/bluetooth/BluetoothCommon.h
@@ -230,20 +230,26 @@ extern bool gBluetoothDebugFlag;
 /**
  * When the value of a characteristic of a remote BLE device changes, we'll
  * dispatch an event
  */
 #define GATT_CHARACTERISTIC_CHANGED_ID       "characteristicchanged"
 
 /**
  * When a remote BLE device gets connected / disconnected, we'll dispatch an
- * event
+ * event.
  */
 #define GATT_CONNECTION_STATE_CHANGED_ID     "connectionstatechanged"
 
+/**
+ * When attributes of BluetoothManager, BluetoothAdapter, or BluetoothDevice
+ * are changed, we'll dispatch an event.
+ */
+#define ATTRIBUTE_CHANGED_ID                 "attributechanged"
+
 // Bluetooth address format: xx:xx:xx:xx:xx:xx (or xx_xx_xx_xx_xx_xx)
 #define BLUETOOTH_ADDRESS_LENGTH 17
 #define BLUETOOTH_ADDRESS_NONE   "00:00:00:00:00:00"
 #define BLUETOOTH_ADDRESS_BYTES  6
 
 // Bluetooth stack internal error, such as I/O error
 #define ERR_INTERNAL_ERROR "InternalError"
 
--- a/dom/bluetooth/BluetoothSocketObserver.h
+++ b/dom/bluetooth/BluetoothSocketObserver.h
@@ -3,17 +3,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/. */
 
 #ifndef mozilla_dom_bluetooth_BluetoothSocketObserver_h
 #define mozilla_dom_bluetooth_BluetoothSocketObserver_h
 
 #include "BluetoothCommon.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include "mozilla/ipc/SocketBase.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocket;
 
 class BluetoothSocketObserver
 {
 public:
--- a/dom/bluetooth/bluedroid/BluetoothOppManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothOppManager.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
 #define mozilla_dom_bluetooth_bluetoothoppmanager_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
 #include "BluetoothSocketObserver.h"
 #include "DeviceStorage.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include "mozilla/ipc/SocketBase.h"
 #include "nsCOMArray.h"
 
 class nsIDOMBlob;
 class nsIOutputStream;
 class nsIInputStream;
 class nsIVolumeMountLock;
 
 namespace mozilla {
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -31,17 +31,17 @@
 // TODO: Support HID
 #endif
 #include "BluetoothOppManager.h"
 #include "BluetoothProfileController.h"
 #include "BluetoothReplyRunnable.h"
 #include "BluetoothUtils.h"
 #include "BluetoothUuid.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include "mozilla/ipc/SocketBase.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #ifdef MOZ_B2G_BT_API_V2
 #include "nsDataHashtable.h"
 #endif
 
 #ifdef MOZ_B2G_BT_API_V2
--- a/dom/bluetooth/bluedroid/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothSocket.cpp
@@ -8,16 +8,17 @@
 
 #include <fcntl.h>
 #include <sys/socket.h>
 
 #include "base/message_loop.h"
 #include "BluetoothSocketObserver.h"
 #include "BluetoothInterface.h"
 #include "BluetoothUtils.h"
+#include "mozilla/ipc/UnixSocketWatcher.h"
 #include "mozilla/FileUtils.h"
 #include "mozilla/RefPtr.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla::ipc;
 USING_BLUETOOTH_NAMESPACE
 
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
@@ -7,17 +7,17 @@
 #ifndef mozilla_dom_bluetooth_bluetoothhfpmanager_h__
 #define mozilla_dom_bluetooth_bluetoothhfpmanager_h__
 
 #include "BluetoothInterface.h"
 #include "BluetoothCommon.h"
 #include "BluetoothHfpManagerBase.h"
 #include "BluetoothRilListener.h"
 #include "BluetoothSocketObserver.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include "mozilla/ipc/SocketBase.h"
 #include "mozilla/Hal.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothReplyRunnable;
 class BluetoothSocket;
 class Call;
 
--- a/dom/bluetooth/bluetooth1/BluetoothService.cpp
+++ b/dom/bluetooth/bluetooth1/BluetoothService.cpp
@@ -23,17 +23,17 @@
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/dom/ipc/BlobChild.h"
 #include "mozilla/dom/ipc/BlobParent.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include "mozilla/ipc/SocketBase.h"
 #include "nsContentUtils.h"
 #include "nsIObserverService.h"
 #include "nsISettingsService.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITimer.h"
 #include "nsServiceManagerUtils.h"
 #include "nsXPCOM.h"
 #include "mozilla/dom/SettingChangeNotificationBinding.h"
--- a/dom/bluetooth/bluetooth1/BluetoothService.h
+++ b/dom/bluetooth/bluetooth1/BluetoothService.h
@@ -18,19 +18,16 @@
 
 class nsIDOMBlob;
 
 namespace mozilla {
 namespace dom {
 class BlobChild;
 class BlobParent;
 }
-namespace ipc {
-class UnixSocketConsumer;
-}
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothManager;
 class BluetoothNamedValue;
 class BluetoothReplyRunnable;
 class BluetoothSignal;
--- a/dom/bluetooth/bluetooth1/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth/bluetooth1/ipc/BluetoothServiceChildProcess.h
@@ -4,32 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_ipc_bluetoothservicechildprocess_h__
 #define mozilla_dom_bluetooth_ipc_bluetoothservicechildprocess_h__
 
 #include "BluetoothService.h"
 
-namespace mozilla {
-namespace ipc {
-class UnixSocketConsumer;
-}
-namespace dom {
-namespace bluetooth {
+BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothChild;
 
-} // namespace bluetooth
-} // namespace dom
-} // namespace mozilla
-
-
-BEGIN_BLUETOOTH_NAMESPACE
-
 class BluetoothServiceChildProcess : public BluetoothService
 {
   friend class mozilla::dom::bluetooth::BluetoothChild;
 
 public:
   static BluetoothServiceChildProcess*
   Create();
 
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.cpp
@@ -786,32 +786,32 @@ BluetoothAdapter::SetAdapterState(Blueto
 {
   if (mState == aState) {
     return;
   }
 
   mState = aState;
 
   // Fire BluetoothAttributeEvent for changed adapter state
-  nsTArray<nsString> types;
+  Sequence<nsString> types;
   BT_APPEND_ENUM_STRING(types,
                         BluetoothAdapterAttribute,
                         BluetoothAdapterAttribute::State);
   DispatchAttributeEvent(types);
 }
 
 void
 BluetoothAdapter::HandlePropertyChanged(const BluetoothValue& aValue)
 {
   MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
 
   const InfallibleTArray<BluetoothNamedValue>& arr =
     aValue.get_ArrayOfBluetoothNamedValue();
 
-  nsTArray<nsString> types;
+  Sequence<nsString> types;
   for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
     BluetoothAdapterAttribute type =
       ConvertStringToAdapterAttribute(arr[i].name());
 
     // Non-BluetoothAdapterAttribute properties
     if (type == BluetoothAdapterAttribute::Unknown) {
       SetPropertyByValue(arr[i]);
       continue;
@@ -907,36 +907,27 @@ BluetoothAdapter::HandleDeviceUnpaired(c
 
   // Notify application of unpaired device
   BluetoothDeviceEventInit init;
   init.mAddress = deviceAddress;
   DispatchDeviceEvent(NS_LITERAL_STRING(DEVICE_UNPAIRED_ID), init);
 }
 
 void
-BluetoothAdapter::DispatchAttributeEvent(const nsTArray<nsString>& aTypes)
+BluetoothAdapter::DispatchAttributeEvent(const Sequence<nsString>& aTypes)
 {
   NS_ENSURE_TRUE_VOID(aTypes.Length());
 
-  AutoJSAPI jsapi;
-  NS_ENSURE_TRUE_VOID(jsapi.Init(GetOwner()));
-  JSContext* cx = jsapi.cx();
-  JS::Rooted<JS::Value> value(cx);
+  BluetoothAttributeEventInit init;
+  init.mAttrs = aTypes;
 
-  if (!ToJSValue(cx, aTypes, &value)) {
-    JS_ClearPendingException(cx);
-    return;
-  }
+  nsRefPtr<BluetoothAttributeEvent> event =
+    BluetoothAttributeEvent::Constructor(
+      this, NS_LITERAL_STRING(ATTRIBUTE_CHANGED_ID), init);
 
-  RootedDictionary<BluetoothAttributeEventInit> init(cx);
-  init.mAttrs = value;
-  nsRefPtr<BluetoothAttributeEvent> event =
-    BluetoothAttributeEvent::Constructor(this,
-                                         NS_LITERAL_STRING("attributechanged"),
-                                         init);
   DispatchTrustedEvent(event);
 }
 
 void
 BluetoothAdapter::DispatchDeviceEvent(const nsAString& aType,
                                       const BluetoothDeviceEventInit& aInit)
 {
   BT_API2_LOGR("aType (%s)", NS_ConvertUTF16toUTF8(aType).get());
--- a/dom/bluetooth/bluetooth2/BluetoothAdapter.h
+++ b/dom/bluetooth/bluetooth2/BluetoothAdapter.h
@@ -248,17 +248,17 @@ private:
    *                    - nsString  'Address'
    *                    - bool      'Paired'
    */
   void HandleDeviceUnpaired(const BluetoothValue& aValue);
 
   /**
    * Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
    */
-  void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
+  void DispatchAttributeEvent(const Sequence<nsString>& aTypes);
 
   /**
    * Fire BluetoothDeviceEvent to trigger
    * ondeviceparied/ondeviceunpaired event handler.
    *
    * @param aType [in] Event type to fire
    * @param aInit [in] Event initialization value
    */
--- a/dom/bluetooth/bluetooth2/BluetoothDevice.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothDevice.cpp
@@ -256,17 +256,17 @@ BluetoothDevice::IsDeviceAttributeChange
 void
 BluetoothDevice::HandlePropertyChanged(const BluetoothValue& aValue)
 {
   MOZ_ASSERT(aValue.type() == BluetoothValue::TArrayOfBluetoothNamedValue);
 
   const InfallibleTArray<BluetoothNamedValue>& arr =
     aValue.get_ArrayOfBluetoothNamedValue();
 
-  nsTArray<nsString> types;
+  Sequence<nsString> types;
   for (uint32_t i = 0, propCount = arr.Length(); i < propCount; ++i) {
     BluetoothDeviceAttribute type =
       ConvertStringToDeviceAttribute(arr[i].name());
 
     // Non-BluetoothDeviceAttribute properties
     if (type == BluetoothDeviceAttribute::Unknown) {
       SetPropertyByValue(arr[i]);
       continue;
@@ -278,36 +278,26 @@ BluetoothDevice::HandlePropertyChanged(c
       BT_APPEND_ENUM_STRING(types, BluetoothDeviceAttribute, type);
     }
   }
 
   DispatchAttributeEvent(types);
 }
 
 void
-BluetoothDevice::DispatchAttributeEvent(const nsTArray<nsString>& aTypes)
+BluetoothDevice::DispatchAttributeEvent(const Sequence<nsString>& aTypes)
 {
   NS_ENSURE_TRUE_VOID(aTypes.Length());
 
-  AutoJSAPI jsapi;
-  NS_ENSURE_TRUE_VOID(jsapi.Init(GetOwner()));
-  JSContext* cx = jsapi.cx();
-  JS::Rooted<JS::Value> value(cx);
+  BluetoothAttributeEventInit init;
+  init.mAttrs = aTypes;
+  nsRefPtr<BluetoothAttributeEvent> event =
+    BluetoothAttributeEvent::Constructor(
+      this, NS_LITERAL_STRING(ATTRIBUTE_CHANGED_ID), init);
 
-  if (!ToJSValue(cx, aTypes, &value)) {
-    JS_ClearPendingException(cx);
-    return;
-  }
-
-  RootedDictionary<BluetoothAttributeEventInit> init(cx);
-  init.mAttrs = value;
-  nsRefPtr<BluetoothAttributeEvent> event =
-    BluetoothAttributeEvent::Constructor(this,
-                                         NS_LITERAL_STRING("attributechanged"),
-                                         init);
   DispatchTrustedEvent(event);
 }
 
 BluetoothGatt*
 BluetoothDevice::GetGatt()
 {
   NS_ENSURE_TRUE(mType == BluetoothDeviceType::Le ||
                  mType == BluetoothDeviceType::Dual,
--- a/dom/bluetooth/bluetooth2/BluetoothDevice.h
+++ b/dom/bluetooth/bluetooth2/BluetoothDevice.h
@@ -114,17 +114,17 @@ private:
    *
    * @param aValue [in] Array of changed properties
    */
   void HandlePropertyChanged(const BluetoothValue& aValue);
 
   /**
    * Fire BluetoothAttributeEvent to trigger onattributechanged event handler.
    */
-  void DispatchAttributeEvent(const nsTArray<nsString>& aTypes);
+  void DispatchAttributeEvent(const Sequence<nsString>& aTypes);
 
   /**
    * Convert uint32_t to BluetoothDeviceType.
    *
    * @param aValue [in] uint32_t to convert
    */
   BluetoothDeviceType ConvertUint32ToDeviceType(const uint32_t aValue);
 
--- a/dom/bluetooth/bluetooth2/BluetoothManager.cpp
+++ b/dom/bluetooth/bluetooth2/BluetoothManager.cpp
@@ -241,38 +241,28 @@ BluetoothManager::DispatchAdapterEvent(c
 }
 
 void
 BluetoothManager::DispatchAttributeEvent()
 {
   MOZ_ASSERT(NS_IsMainThread());
   BT_API2_LOGR();
 
-  AutoJSAPI jsapi;
-  NS_ENSURE_TRUE_VOID(jsapi.Init(GetOwner()));
-  JSContext* cx = jsapi.cx();
-  JS::Rooted<JS::Value> value(cx);
-
-  nsTArray<nsString> types;
+  Sequence<nsString> types;
   BT_APPEND_ENUM_STRING(types,
                         BluetoothManagerAttribute,
                         BluetoothManagerAttribute::DefaultAdapter);
 
-  if (!ToJSValue(cx, types, &value)) {
-    JS_ClearPendingException(cx);
-    return;
-  }
+  // Notify application of default adapter change
+  BluetoothAttributeEventInit init;
+  init.mAttrs = types;
+  nsRefPtr<BluetoothAttributeEvent> event =
+    BluetoothAttributeEvent::Constructor(
+      this, NS_LITERAL_STRING(ATTRIBUTE_CHANGED_ID), init);
 
-  // Notify application of default adapter change
-  RootedDictionary<BluetoothAttributeEventInit> init(cx);
-  init.mAttrs = value;
-  nsRefPtr<BluetoothAttributeEvent> event =
-    BluetoothAttributeEvent::Constructor(this,
-                                         NS_LITERAL_STRING("attributechanged"),
-                                         init);
   DispatchTrustedEvent(event);
 }
 
 void
 BluetoothManager::Notify(const BluetoothSignal& aData)
 {
   BT_LOGD("[M] %s", NS_ConvertUTF16toUTF8(aData.name()).get());
 
--- a/dom/bluetooth/bluetooth2/BluetoothService.h
+++ b/dom/bluetooth/bluetooth2/BluetoothService.h
@@ -19,19 +19,16 @@
 
 class nsIDOMBlob;
 
 namespace mozilla {
 namespace dom {
 class BlobChild;
 class BlobParent;
 }
-namespace ipc {
-class UnixSocketConsumer;
-}
 }
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothManager;
 class BluetoothNamedValue;
 class BluetoothReplyRunnable;
 class BluetoothSignal;
--- a/dom/bluetooth/bluetooth2/ipc/BluetoothServiceChildProcess.h
+++ b/dom/bluetooth/bluetooth2/ipc/BluetoothServiceChildProcess.h
@@ -4,32 +4,20 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_ipc_bluetoothservicechildprocess_h__
 #define mozilla_dom_bluetooth_ipc_bluetoothservicechildprocess_h__
 
 #include "BluetoothService.h"
 
-namespace mozilla {
-namespace ipc {
-class UnixSocketConsumer;
-}
-namespace dom {
-namespace bluetooth {
+BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothChild;
 
-} // namespace bluetooth
-} // namespace dom
-} // namespace mozilla
-
-
-BEGIN_BLUETOOTH_NAMESPACE
-
 class BluetoothServiceChildProcess : public BluetoothService
 {
   friend class mozilla::dom::bluetooth::BluetoothChild;
 
 public:
   static BluetoothServiceChildProcess*
   Create();
 
--- a/dom/bluetooth/bluez/BluetoothDBusService.cpp
+++ b/dom/bluetooth/bluez/BluetoothDBusService.cpp
@@ -35,17 +35,17 @@
 #include "nsThreadUtils.h"
 #include "nsDebug.h"
 #include "nsDataHashtable.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/Atomics.h"
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/dom/bluetooth/BluetoothTypes.h"
 #include "mozilla/Hal.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include "mozilla/ipc/SocketBase.h"
 #include "mozilla/ipc/DBusUtils.h"
 #include "mozilla/ipc/RawDBusConnection.h"
 #include "mozilla/LazyIdleThread.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/unused.h"
 
--- a/dom/bluetooth/bluez/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluez/BluetoothHfpManager.h
@@ -8,17 +8,17 @@
 #define mozilla_dom_bluetooth_bluetoothhfpmanager_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothHfpManagerBase.h"
 #ifdef MOZ_B2G_RIL
 #include "BluetoothRilListener.h"
 #endif
 #include "BluetoothSocketObserver.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include "mozilla/ipc/SocketBase.h"
 #include "mozilla/Hal.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothReplyRunnable;
 class BluetoothSocket;
 
 #ifdef MOZ_B2G_RIL
--- a/dom/bluetooth/bluez/BluetoothOppManager.h
+++ b/dom/bluetooth/bluez/BluetoothOppManager.h
@@ -6,17 +6,17 @@
 
 #ifndef mozilla_dom_bluetooth_bluetoothoppmanager_h__
 #define mozilla_dom_bluetooth_bluetoothoppmanager_h__
 
 #include "BluetoothCommon.h"
 #include "BluetoothProfileManagerBase.h"
 #include "BluetoothSocketObserver.h"
 #include "DeviceStorage.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include "mozilla/ipc/SocketBase.h"
 #include "nsCOMArray.h"
 
 class nsIDOMBlob;
 class nsIOutputStream;
 class nsIInputStream;
 class nsIVolumeMountLock;
 
 namespace mozilla {
--- a/dom/bluetooth/bluez/BluetoothSocket.cpp
+++ b/dom/bluetooth/bluez/BluetoothSocket.cpp
@@ -1,35 +1,572 @@
 /* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
 /* vim: set ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BluetoothSocket.h"
-
+#include <fcntl.h>
 #include "BluetoothSocketObserver.h"
 #include "BluetoothUnixSocketConnector.h"
+#include "mozilla/unused.h"
+#include "nsTArray.h"
 #include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
 
 using namespace mozilla::ipc;
-USING_BLUETOOTH_NAMESPACE
+
+BEGIN_BLUETOOTH_NAMESPACE
+
+static const size_t MAX_READ_SIZE = 1 << 16;
+
+//
+// BluetoothSocketIO
+//
+
+class BluetoothSocket::BluetoothSocketIO final
+  : public UnixSocketWatcher
+  , protected SocketIOBase
+{
+public:
+  BluetoothSocketIO(MessageLoop* mIOLoop,
+                    BluetoothSocket* aConsumer,
+                    UnixSocketConnector* aConnector,
+                    const nsACString& aAddress);
+  ~BluetoothSocketIO();
+
+  void                GetSocketAddr(nsAString& aAddrStr) const;
+  SocketConsumerBase* GetConsumer();
+  SocketBase*         GetSocketBase();
+
+  // Shutdown state
+  //
+
+  bool IsShutdownOnMainThread() const;
+  void ShutdownOnMainThread();
+
+  bool IsShutdownOnIOThread() const;
+  void ShutdownOnIOThread();
+
+  // Delayed-task handling
+  //
+
+  void SetDelayedConnectTask(CancelableTask* aTask);
+  void ClearDelayedConnectTask();
+  void CancelDelayedConnectTask();
+
+  // Task callback methods
+  //
+
+  /**
+   * Run bind/listen to prepare for further runs of accept()
+   */
+  void Listen();
+
+  /**
+   * Connect to a socket
+   */
+  void Connect();
+
+  void Send(UnixSocketRawData* aData);
+
+  // I/O callback methods
+  //
+
+  void OnAccepted(int aFd, const sockaddr_any* aAddr,
+                  socklen_t aAddrLen) override;
+  void OnConnected() override;
+  void OnError(const char* aFunction, int aErrno) override;
+  void OnListening() override;
+  void OnSocketCanReceiveWithoutBlocking() override;
+  void OnSocketCanSendWithoutBlocking() override;
+
+private:
+  void FireSocketError();
+
+  // Set up flags on file descriptor.
+  static bool SetSocketFlags(int aFd);
+
+  /**
+   * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
+   * directly from main thread. All non-main-thread accesses should happen with
+   * mIO as container.
+   */
+  RefPtr<BluetoothSocket> mConsumer;
+
+  /**
+   * Connector object used to create the connection we are currently using.
+   */
+  nsAutoPtr<UnixSocketConnector> mConnector;
+
+  /**
+   * If true, do not requeue whatever task we're running
+   */
+  bool mShuttingDownOnIOThread;
+
+  /**
+   * Address we are connecting to, assuming we are creating a client connection.
+   */
+  nsCString mAddress;
+
+  /**
+   * Size of the socket address struct
+   */
+  socklen_t mAddrSize;
+
+  /**
+   * Address struct of the socket currently in use
+   */
+  sockaddr_any mAddr;
+
+  /**
+   * Task member for delayed connect task. Should only be access on main thread.
+   */
+  CancelableTask* mDelayedConnectTask;
+};
+
+BluetoothSocket::BluetoothSocketIO::BluetoothSocketIO(
+  MessageLoop* mIOLoop,
+  BluetoothSocket* aConsumer,
+  UnixSocketConnector* aConnector,
+  const nsACString& aAddress)
+  : UnixSocketWatcher(mIOLoop)
+  , SocketIOBase(MAX_READ_SIZE)
+  , mConsumer(aConsumer)
+  , mConnector(aConnector)
+  , mShuttingDownOnIOThread(false)
+  , mAddress(aAddress)
+  , mDelayedConnectTask(nullptr)
+{
+  MOZ_ASSERT(mConsumer);
+  MOZ_ASSERT(mConnector);
+}
+
+BluetoothSocket::BluetoothSocketIO::~BluetoothSocketIO()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(IsShutdownOnMainThread());
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::GetSocketAddr(nsAString& aAddrStr) const
+{
+  if (!mConnector) {
+    NS_WARNING("No connector to get socket address from!");
+    aAddrStr.Truncate();
+    return;
+  }
+  mConnector->GetSocketAddr(mAddr, aAddrStr);
+}
+
+SocketConsumerBase*
+BluetoothSocket::BluetoothSocketIO::GetConsumer()
+{
+  return mConsumer.get();
+}
+
+SocketBase*
+BluetoothSocket::BluetoothSocketIO::GetSocketBase()
+{
+  return GetConsumer();
+}
+
+bool
+BluetoothSocket::BluetoothSocketIO::IsShutdownOnMainThread() const
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  return mConsumer == nullptr;
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::ShutdownOnMainThread()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(!IsShutdownOnMainThread());
+
+  mConsumer = nullptr;
+}
+
+bool
+BluetoothSocket::BluetoothSocketIO::IsShutdownOnIOThread() const
+{
+  return mShuttingDownOnIOThread;
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::ShutdownOnIOThread()
+{
+  MOZ_ASSERT(!NS_IsMainThread());
+  MOZ_ASSERT(!mShuttingDownOnIOThread);
+
+  Close(); // will also remove fd from I/O loop
+  mShuttingDownOnIOThread = true;
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::SetDelayedConnectTask(CancelableTask* aTask)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mDelayedConnectTask = aTask;
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::ClearDelayedConnectTask()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  mDelayedConnectTask = nullptr;
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::CancelDelayedConnectTask()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mDelayedConnectTask) {
+    return;
+  }
+
+  mDelayedConnectTask->Cancel();
+  ClearDelayedConnectTask();
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::Listen()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(mConnector);
+
+  // This will set things we don't particularly care about, but it will hand
+  // back the correct structure size which is what we do care about.
+  if (!mConnector->CreateAddr(true, mAddrSize, mAddr, nullptr)) {
+    NS_WARNING("Cannot create socket address!");
+    FireSocketError();
+    return;
+  }
+
+  if (!IsOpen()) {
+    int fd = mConnector->Create();
+    if (fd < 0) {
+      NS_WARNING("Cannot create socket fd!");
+      FireSocketError();
+      return;
+    }
+    if (!SetSocketFlags(fd)) {
+      NS_WARNING("Cannot set socket flags!");
+      FireSocketError();
+      return;
+    }
+    SetFd(fd);
+
+    // calls OnListening on success, or OnError otherwise
+    nsresult rv = UnixSocketWatcher::Listen(
+      reinterpret_cast<struct sockaddr*>(&mAddr), mAddrSize);
+    NS_WARN_IF(NS_FAILED(rv));
+  }
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::Connect()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(mConnector);
+
+  if (!IsOpen()) {
+    int fd = mConnector->Create();
+    if (fd < 0) {
+      NS_WARNING("Cannot create socket fd!");
+      FireSocketError();
+      return;
+    }
+    if (!SetSocketFlags(fd)) {
+      NS_WARNING("Cannot set socket flags!");
+      FireSocketError();
+      return;
+    }
+    SetFd(fd);
+  }
+
+  if (!mConnector->CreateAddr(false, mAddrSize, mAddr, mAddress.get())) {
+    NS_WARNING("Cannot create socket address!");
+    FireSocketError();
+    return;
+  }
+
+  // calls OnConnected() on success, or OnError() otherwise
+  nsresult rv = UnixSocketWatcher::Connect(
+    reinterpret_cast<struct sockaddr*>(&mAddr), mAddrSize);
+  NS_WARN_IF(NS_FAILED(rv));
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::Send(UnixSocketRawData* aData)
+{
+  EnqueueData(aData);
+  AddWatchers(WRITE_WATCHER, false);
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::OnAccepted(
+  int aFd, const sockaddr_any* aAddr, socklen_t aAddrLen)
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
+  MOZ_ASSERT(aAddr);
+  MOZ_ASSERT(aAddrLen > 0 && (size_t)aAddrLen <= sizeof(mAddr));
+
+  memcpy (&mAddr, aAddr, aAddrLen);
+  mAddrSize = aAddrLen;
+
+  if (!mConnector->SetUp(aFd)) {
+    NS_WARNING("Could not set up socket!");
+    return;
+  }
+
+  RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
+  Close();
+  if (!SetSocketFlags(aFd)) {
+    return;
+  }
+  SetSocket(aFd, SOCKET_IS_CONNECTED);
+
+  nsRefPtr<nsRunnable> r =
+    new SocketIOEventRunnable<BluetoothSocketIO>(
+      this, SocketIOEventRunnable<BluetoothSocketIO>::CONNECT_SUCCESS);
+  NS_DispatchToMainThread(r);
+
+  AddWatchers(READ_WATCHER, true);
+  if (HasPendingData()) {
+    AddWatchers(WRITE_WATCHER, false);
+  }
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::OnConnected()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
+
+  if (!SetSocketFlags(GetFd())) {
+    NS_WARNING("Cannot set socket flags!");
+    FireSocketError();
+    return;
+  }
+
+  if (!mConnector->SetUp(GetFd())) {
+    NS_WARNING("Could not set up socket!");
+    FireSocketError();
+    return;
+  }
+
+  nsRefPtr<nsRunnable> r =
+    new SocketIOEventRunnable<BluetoothSocketIO>(
+      this, SocketIOEventRunnable<BluetoothSocketIO>::CONNECT_SUCCESS);
+  NS_DispatchToMainThread(r);
+
+  AddWatchers(READ_WATCHER, true);
+  if (HasPendingData()) {
+    AddWatchers(WRITE_WATCHER, false);
+  }
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::OnListening()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
+
+  if (!mConnector->SetUpListenSocket(GetFd())) {
+    NS_WARNING("Could not set up listen socket!");
+    FireSocketError();
+    return;
+  }
+
+  AddWatchers(READ_WATCHER, true);
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::OnError(const char* aFunction, int aErrno)
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+
+  UnixFdWatcher::OnError(aFunction, aErrno);
+  FireSocketError();
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::OnSocketCanReceiveWithoutBlocking()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
+
+  ssize_t res = ReceiveData(GetFd(), this);
+  if (res < 0) {
+    /* I/O error */
+    RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
+  } else if (!res) {
+    /* EOF or peer shutdown */
+    RemoveWatchers(READ_WATCHER);
+  }
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::OnSocketCanSendWithoutBlocking()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
+
+  nsresult rv = SendPendingData(GetFd(), this);
+  if (NS_FAILED(rv)) {
+    return;
+  }
+
+  if (HasPendingData()) {
+    AddWatchers(WRITE_WATCHER, false);
+  }
+}
+
+void
+BluetoothSocket::BluetoothSocketIO::FireSocketError()
+{
+  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
+
+  // Clean up watchers, statuses, fds
+  Close();
+
+  // Tell the main thread we've errored
+  nsRefPtr<nsRunnable> r =
+    new SocketIOEventRunnable<BluetoothSocketIO>(
+      this, SocketIOEventRunnable<BluetoothSocketIO>::CONNECT_ERROR);
+
+  NS_DispatchToMainThread(r);
+}
+
+bool
+BluetoothSocket::BluetoothSocketIO::SetSocketFlags(int aFd)
+{
+  // Set socket addr to be reused even if kernel is still waiting to close
+  int n = 1;
+  if (setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) < 0) {
+    return false;
+  }
+
+  // Set close-on-exec bit.
+  int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFD));
+  if (-1 == flags) {
+    return false;
+  }
+  flags |= FD_CLOEXEC;
+  if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFD, flags))) {
+    return false;
+  }
+
+  // Set non-blocking status flag.
+  flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
+  if (-1 == flags) {
+    return false;
+  }
+  flags |= O_NONBLOCK;
+  if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags))) {
+    return false;
+  }
+
+  return true;
+}
+
+//
+// Socket tasks
+//
+
+class BluetoothSocket::ListenTask final
+  : public SocketIOTask<BluetoothSocketIO>
+{
+public:
+  ListenTask(BluetoothSocketIO* aIO)
+    : SocketIOTask<BluetoothSocketIO>(aIO)
+  { }
+
+  void Run() override
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+
+    if (!IsCanceled()) {
+      GetIO()->Listen();
+    }
+  }
+};
+
+class BluetoothSocket::ConnectTask final
+  : public SocketIOTask<BluetoothSocketIO>
+{
+public:
+  ConnectTask(BluetoothSocketIO* aIO)
+    : SocketIOTask<BluetoothSocketIO>(aIO)
+  { }
+
+  void Run() override
+  {
+    MOZ_ASSERT(!NS_IsMainThread());
+    MOZ_ASSERT(!IsCanceled());
+
+    GetIO()->Connect();
+  }
+};
+
+class BluetoothSocket::DelayedConnectTask final
+  : public SocketIOTask<BluetoothSocketIO>
+{
+public:
+  DelayedConnectTask(BluetoothSocketIO* aIO)
+    : SocketIOTask<BluetoothSocketIO>(aIO)
+  { }
+
+  void Run() override
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (IsCanceled()) {
+      return;
+    }
+
+    BluetoothSocketIO* io = GetIO();
+    if (io->IsShutdownOnMainThread()) {
+      return;
+    }
+
+    io->ClearDelayedConnectTask();
+    XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new ConnectTask(io));
+  }
+};
+
+//
+// BluetoothSocket
+//
 
 BluetoothSocket::BluetoothSocket(BluetoothSocketObserver* aObserver,
                                  BluetoothSocketType aType,
                                  bool aAuth,
                                  bool aEncrypt)
   : mObserver(aObserver)
   , mType(aType)
   , mAuth(aAuth)
   , mEncrypt(aEncrypt)
+  , mIO(nullptr)
 {
   MOZ_ASSERT(aObserver);
 }
 
+BluetoothSocket::~BluetoothSocket()
+{
+  MOZ_ASSERT(!mIO);
+}
+
 bool
 BluetoothSocket::Connect(const nsAString& aDeviceAddress,
                          const BluetoothUuid& aServiceUuid,
                          int aChannel)
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!aDeviceAddress.IsEmpty());
 
@@ -96,8 +633,127 @@ BluetoothSocket::OnConnectError()
 void
 BluetoothSocket::OnDisconnect()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mObserver);
   mObserver->OnSocketDisconnect(this);
 }
 
+bool
+BluetoothSocket::SendSocketData(UnixSocketRawData* aData)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mIO) {
+    return false;
+  }
+
+  MOZ_ASSERT(!mIO->IsShutdownOnMainThread());
+  XRE_GetIOMessageLoop()->PostTask(
+    FROM_HERE,
+    new SocketIOSendTask<BluetoothSocketIO, UnixSocketRawData>(mIO, aData));
+
+  return true;
+}
+
+bool
+BluetoothSocket::SendSocketData(const nsACString& aStr)
+{
+  if (aStr.Length() > MAX_READ_SIZE) {
+    return false;
+  }
+
+  nsAutoPtr<UnixSocketRawData> data(
+    new UnixSocketRawData(aStr.BeginReading(), aStr.Length()));
+
+  if (!SendSocketData(data)) {
+    return false;
+  }
+
+  unused << data.forget();
+
+  return true;
+}
+
+void
+BluetoothSocket::CloseSocket()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!mIO) {
+    return;
+  }
+
+  mIO->CancelDelayedConnectTask();
+
+  // From this point on, we consider mIO as being deleted.
+  // We sever the relationship here so any future calls to listen or connect
+  // will create a new implementation.
+  mIO->ShutdownOnMainThread();
+
+  XRE_GetIOMessageLoop()->PostTask(
+    FROM_HERE, new SocketIOShutdownTask<BluetoothSocketIO>(mIO));
+
+  mIO = nullptr;
+
+  NotifyDisconnect();
+}
+
+void
+BluetoothSocket::GetSocketAddr(nsAString& aAddrStr)
+{
+  aAddrStr.Truncate();
+  if (!mIO || GetConnectionStatus() != SOCKET_CONNECTED) {
+    NS_WARNING("No socket currently open!");
+    return;
+  }
+  mIO->GetSocketAddr(aAddrStr);
+}
+
+bool
+BluetoothSocket::ConnectSocket(BluetoothUnixSocketConnector* aConnector,
+                               const char* aAddress,
+                               int aDelayMs)
+{
+  MOZ_ASSERT(aConnector);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<UnixSocketConnector> connector(aConnector);
+
+  if (mIO) {
+    NS_WARNING("Socket already connecting/connected!");
+    return false;
+  }
+
+  nsCString addr(aAddress);
+  MessageLoop* ioLoop = XRE_GetIOMessageLoop();
+  mIO = new BluetoothSocketIO(ioLoop, this, connector.forget(), addr);
+  SetConnectionStatus(SOCKET_CONNECTING);
+  if (aDelayMs > 0) {
+    DelayedConnectTask* connectTask = new DelayedConnectTask(mIO);
+    mIO->SetDelayedConnectTask(connectTask);
+    MessageLoop::current()->PostDelayedTask(FROM_HERE, connectTask, aDelayMs);
+  } else {
+    ioLoop->PostTask(FROM_HERE, new ConnectTask(mIO));
+  }
+  return true;
+}
+
+bool
+BluetoothSocket::ListenSocket(BluetoothUnixSocketConnector* aConnector)
+{
+  MOZ_ASSERT(aConnector);
+  MOZ_ASSERT(NS_IsMainThread());
+
+  nsAutoPtr<UnixSocketConnector> connector(aConnector);
+
+  if (mIO) {
+    NS_WARNING("Socket already connecting/connected!");
+    return false;
+  }
+
+  mIO = new BluetoothSocketIO(
+    XRE_GetIOMessageLoop(), this, connector.forget(), EmptyCString());
+  SetConnectionStatus(SOCKET_LISTENING);
+  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new ListenTask(mIO));
+  return true;
+}
+
+END_BLUETOOTH_NAMESPACE
--- a/dom/bluetooth/bluez/BluetoothSocket.h
+++ b/dom/bluetooth/bluez/BluetoothSocket.h
@@ -3,29 +3,37 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_BluetoothSocket_h
 #define mozilla_dom_bluetooth_BluetoothSocket_h
 
 #include "BluetoothCommon.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include <stdlib.h>
+#include "mozilla/ipc/SocketBase.h"
+#include "mozilla/ipc/UnixSocketWatcher.h"
+#include "mozilla/RefPtr.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "nsThreadUtils.h"
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothSocketObserver;
+class BluetoothUnixSocketConnector;
 
-class BluetoothSocket : public mozilla::ipc::UnixSocketConsumer
+class BluetoothSocket final : public mozilla::ipc::SocketConsumerBase
 {
 public:
   BluetoothSocket(BluetoothSocketObserver* aObserver,
                   BluetoothSocketType aType,
                   bool aAuth,
                   bool aEncrypt);
+  ~BluetoothSocket();
 
   bool Connect(const nsAString& aDeviceAddress,
                const BluetoothUuid& aServiceUuid,
                int aChannel);
   bool Listen(const nsAString& aServiceName,
               const BluetoothUuid& aServiceUuid,
               int aChannel);
   inline void Disconnect()
@@ -39,18 +47,80 @@ public:
   virtual void ReceiveSocketData(
     nsAutoPtr<mozilla::ipc::UnixSocketRawData>& aMessage) override;
 
   inline void GetAddress(nsAString& aDeviceAddress)
   {
     GetSocketAddr(aDeviceAddress);
   }
 
+  /**
+   * Queue data to be sent to the socket on the IO thread. Can only be called on
+   * originating thread.
+   *
+   * @param aMessage Data to be sent to socket
+   *
+   * @return true if data is queued, false otherwise (i.e. not connected)
+   */
+  bool SendSocketData(mozilla::ipc::UnixSocketRawData* aMessage);
+
+  /**
+   * Convenience function for sending strings to the socket (common in bluetooth
+   * profile usage). Converts to a UnixSocketRawData struct. Can only be called
+   * on originating thread.
+   *
+   * @param aMessage String to be sent to socket
+   *
+   * @return true if data is queued, false otherwise (i.e. not connected)
+   */
+  bool SendSocketData(const nsACString& aMessage);
+
+  /**
+   * Starts a task on the socket that will try to connect to a socket in a
+   * non-blocking manner.
+   *
+   * @param aConnector Connector object for socket type specific functions
+   * @param aAddress Address to connect to.
+   * @param aDelayMs Time delay in milli-seconds.
+   *
+   * @return true on connect task started, false otherwise.
+   */
+  bool ConnectSocket(BluetoothUnixSocketConnector* aConnector,
+                     const char* aAddress,
+                     int aDelayMs = 0);
+
+  /**
+   * Starts a task on the socket that will try to accept a new connection in a
+   * non-blocking manner.
+   *
+   * @param aConnector Connector object for socket type specific functions
+   *
+   * @return true on listen started, false otherwise
+   */
+  bool ListenSocket(BluetoothUnixSocketConnector* aConnector);
+
+  /**
+   * Queues the internal representation of socket for deletion. Can be called
+   * from main thread.
+   */
+  void CloseSocket();
+
+  /**
+   * Get the current sockaddr for the socket
+   */
+  void GetSocketAddr(nsAString& aAddrStr);
+
 private:
+  class BluetoothSocketIO;
+  class ConnectTask;
+  class DelayedConnectTask;
+  class ListenTask;
+
   BluetoothSocketObserver* mObserver;
   BluetoothSocketType mType;
   bool mAuth;
   bool mEncrypt;
+  BluetoothSocketIO* mIO;
 };
 
 END_BLUETOOTH_NAMESPACE
 
 #endif
--- a/dom/bluetooth/bluez/BluetoothUnixSocketConnector.h
+++ b/dom/bluetooth/bluez/BluetoothUnixSocketConnector.h
@@ -4,17 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_bluetooth_BluetoothUnixSocketConnector_h
 #define mozilla_dom_bluetooth_BluetoothUnixSocketConnector_h
 
 #include "BluetoothCommon.h"
 #include <sys/socket.h>
-#include <mozilla/ipc/UnixSocket.h>
+#include <mozilla/ipc/UnixSocketConnector.h>
 
 BEGIN_BLUETOOTH_NAMESPACE
 
 class BluetoothUnixSocketConnector : public mozilla::ipc::UnixSocketConnector
 {
 public:
   BluetoothUnixSocketConnector(BluetoothSocketType aType, int aChannel,
                                bool aAuth, bool aEncrypt);
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -39,16 +39,17 @@ disabled = bug 1022281
 [test_browserElement_oop_ErrorSecurity.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_FirstPaint.html]
 [test_browserElement_oop_ForwardName.html]
 [test_browserElement_oop_FrameWrongURI.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_GetScreenshot.html]
 [test_browserElement_oop_GetScreenshotDppx.html]
+skip-if = (toolkit == 'windows')
 [test_browserElement_oop_Iconchange.html]
 [test_browserElement_oop_LoadEvents.html]
 [test_browserElement_oop_Manifestchange.html]
 [test_browserElement_oop_Metachange.html]
 [test_browserElement_oop_OpenMixedProcess.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_oop_OpenNamed.html]
 skip-if = (toolkit == 'gonk' && !debug)
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -157,16 +157,17 @@ disabled = bug 1022281
 [test_browserElement_inproc_ExposableURI.html]
 [test_browserElement_inproc_FirstPaint.html]
 [test_browserElement_inproc_ForwardName.html]
 [test_browserElement_inproc_FrameWrongURI.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_GetScreenshot.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only
 [test_browserElement_inproc_GetScreenshotDppx.html]
+skip-if = (toolkit == 'windows')
 [test_browserElement_inproc_Iconchange.html]
 [test_browserElement_inproc_LoadEvents.html]
 [test_browserElement_inproc_Manifestchange.html]
 [test_browserElement_inproc_Metachange.html]
 [test_browserElement_inproc_NextPaint.html]
 [test_browserElement_inproc_OpenNamed.html]
 skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_OpenTab.html]
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -479,18 +479,19 @@ EventStateManager::PreHandleEvent(nsPres
   NS_ENSURE_ARG(aPresContext);
   if (!aEvent) {
     NS_ERROR("aEvent is null.  This should never happen.");
     return NS_ERROR_NULL_POINTER;
   }
 
   NS_WARN_IF_FALSE(!aTargetFrame ||
                    !aTargetFrame->GetContent() ||
-                   aTargetFrame->GetContent() == aTargetContent,
-                   "aTargetContent should be related with aTargetFrame");
+                   aTargetFrame->GetContent() == aTargetContent ||
+                   aTargetFrame->GetContent()->GetParent() == aTargetContent,
+                   "aTargetFrame should be related with aTargetContent");
 
   mCurrentTarget = aTargetFrame;
   mCurrentTargetContent = nullptr;
 
   // Do not take account NS_MOUSE_ENTER/EXIT so that loading a page
   // when user is not active doesn't change the state to active.
   WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   if (aEvent->mFlags.mIsTrusted &&
--- a/dom/events/test/test_clickevent_on_input.html
+++ b/dom/events/test/test_clickevent_on_input.html
@@ -44,23 +44,36 @@ function isEnabledMiddleClickPaste()
 {
   try {
     return SpecialPowers.getBoolPref("middlemouse.paste");
   } catch (e) {
     return false;
   }
 }
 
+function isEnabledTouchCaret()
+{
+  try {
+    return SpecialPowers.getBoolPref("touchcaret.enabled");
+  } catch (e) {
+    return false;
+  }
+}
+
 function doTest(aButton)
 {
   // NOTE #1: Right click causes a context menu to popup, then, the click event
   //          isn't generated.
   // NOTE #2: If middle click causes text to be pasted, then, the click event
   //          isn't generated.
-  if (aButton != 2 && (aButton != 1 || !isEnabledMiddleClickPaste())) {
+  // NOTE #3: If touch caret is enabled, touch caret would ovelap input element,
+  //          then, the click event isn't generated.
+  if (aButton != 2 &&
+      (aButton != 1 || !isEnabledMiddleClickPaste()) &&
+      (aButton != 0 || !isEnabledTouchCaret())) {
     gClickCount = 0;
     // click on border of input
     synthesizeMouse(input, 5, 5, { button: aButton });
     is(gClickCount, 1,
        "click event doesn't fired on input element (button is " +
          aButton + ")");
 
     gClickCount = 0;
--- a/dom/ipc/PreallocatedProcessManager.cpp
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -87,16 +87,57 @@ private:
   void Enable();
   void Disable();
 
   void ObserveProcessShutdown(nsISupports* aSubject);
 
   bool mEnabled;
   bool mShutdown;
   nsRefPtr<ContentParent> mPreallocatedAppProcess;
+
+#if defined(MOZ_NUWA_PROCESS) && defined(ENABLE_TESTS)
+  // For testing NS_NewUnmonitoredThread().
+
+  void CreateUnmonitoredThread();
+  void DestroyUnmonitoredThread();
+
+  class UnmonitoredThreadRunnable : public nsRunnable
+  {
+  public:
+    UnmonitoredThreadRunnable()
+      : mMonitor("UnmonitoredThreadRunnable")
+      , mEnabled(true)
+    { }
+
+    NS_IMETHODIMP Run() override
+    {
+      MonitorAutoLock mon(mMonitor);
+      while (mEnabled) {
+        mMonitor.Wait();
+      }
+      return NS_OK;
+    }
+
+    void Disable()
+    {
+      MonitorAutoLock mon(mMonitor);
+      mEnabled = false;
+      mMonitor.NotifyAll();
+    }
+
+  private:
+    ~UnmonitoredThreadRunnable() { }
+
+    Monitor mMonitor;
+    bool mEnabled;
+  };
+
+  nsCOMPtr<nsIThread> mUnmonitoredThread;
+  nsRefPtr<UnmonitoredThreadRunnable> mUnmonitoredThreadRunnable;
+#endif
 };
 
 /* static */ StaticRefPtr<PreallocatedProcessManagerImpl>
 PreallocatedProcessManagerImpl::sSingleton;
 
 /* static */ PreallocatedProcessManagerImpl*
 PreallocatedProcessManagerImpl::Singleton()
 {
@@ -150,16 +191,50 @@ PreallocatedProcessManagerImpl::Observe(
     mShutdown = true;
   } else {
     MOZ_ASSERT(false);
   }
 
   return NS_OK;
 }
 
+#if defined(MOZ_NUWA_PROCESS) && defined(ENABLE_TESTS)
+void
+PreallocatedProcessManagerImpl::CreateUnmonitoredThread()
+{
+  if (Preferences::GetBool("dom.ipc.newUnmonitoredThread.testMode")) {
+    // Create an unmonitored thread and dispatch a blocking runnable in test
+    // case startup.
+    nsresult rv = NS_NewUnmonitoredThread(getter_AddRefs(mUnmonitoredThread),
+                                          nullptr);
+    NS_ENSURE_SUCCESS_VOID(rv);
+
+    mUnmonitoredThreadRunnable = new UnmonitoredThreadRunnable();
+    mUnmonitoredThread->Dispatch(mUnmonitoredThreadRunnable,
+                                 NS_DISPATCH_NORMAL);
+  }
+}
+
+void
+PreallocatedProcessManagerImpl::DestroyUnmonitoredThread()
+{
+  // Cleanup after the test case finishes.
+  if (mUnmonitoredThreadRunnable) {
+    mUnmonitoredThreadRunnable->Disable();
+  }
+
+  if (mUnmonitoredThread) {
+    mUnmonitoredThread->Shutdown();
+  }
+
+  mUnmonitoredThreadRunnable = nullptr;
+  mUnmonitoredThread = nullptr;
+}
+#endif
+
 void
 PreallocatedProcessManagerImpl::RereadPrefs()
 {
   if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
     Enable();
   } else {
     Disable();
   }
@@ -175,16 +250,21 @@ void
 PreallocatedProcessManagerImpl::Enable()
 {
   if (mEnabled) {
     return;
   }
 
   mEnabled = true;
 #ifdef MOZ_NUWA_PROCESS
+#ifdef ENABLE_TESTS
+  // For testing New_UnmonitoredThread().
+  CreateUnmonitoredThread();
+#endif
+
   ScheduleDelayedNuwaFork();
 #else
   AllocateAfterDelay();
 #endif
 }
 
 void
 PreallocatedProcessManagerImpl::AllocateAfterDelay()
@@ -367,16 +447,21 @@ PreallocatedProcessManagerImpl::Disable(
 {
   if (!mEnabled) {
     return;
   }
 
   mEnabled = false;
 
 #ifdef MOZ_NUWA_PROCESS
+#ifdef ENABLE_TESTS
+  // Shut down the test-only unmonitored thread.
+  DestroyUnmonitoredThread();
+#endif
+
   // Cancel pending fork.
   if (mPreallocateAppProcessTask) {
     mPreallocateAppProcessTask->Cancel();
     mPreallocateAppProcessTask = nullptr;
   }
 #endif
 
   if (mPreallocatedAppProcess) {
--- a/dom/ipc/tests/mochitest.ini
+++ b/dom/ipc/tests/mochitest.ini
@@ -12,12 +12,14 @@ skip-if = buildapp == 'b2g' || buildapp 
 # This test is only supposed to run in the main process
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || e10s || toolkit == 'android'
 [test_cpow_cookies.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet'
 [test_NuwaProcessCreation.html]
 skip-if = toolkit != 'gonk'
 [test_NuwaProcessDeadlock.html]
 skip-if = toolkit != 'gonk'
+[test_NewUnmonitoredThread.html]
+skip-if = toolkit != 'gonk'
 [test_child_docshell.html]
 skip-if = toolkit == 'cocoa' # disabled due to hangs, see changeset 6852e7c47edf
 [test_CrashService_crash.html]
 skip-if = !(crashreporter && !e10s && (toolkit == 'gtk2' || toolkit == 'gtk3' || toolkit == 'cocoa' || toolkit == 'windows') && (buildapp != 'b2g' || toolkit == 'gonk') && (buildapp != 'mulet')) # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
new file mode 100644
--- /dev/null
+++ b/dom/ipc/tests/test_NewUnmonitoredThread.html
@@ -0,0 +1,80 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test if Nuwa process created successfully.
+-->
+<head>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body onload="setup()">
+
+<script type="application/javascript;version=1.7">
+"use strict";
+
+function runTest()
+{
+  info("Launch the Nuwa process");
+  let cpmm = SpecialPowers.Cc["@mozilla.org/childprocessmessagemanager;1"]
+                          .getService(SpecialPowers.Ci.nsISyncMessageSender);
+  let seenNuwaReady = false;
+  let msgHandler = {
+    receiveMessage: function receiveMessage(msg) {
+      msg = SpecialPowers.wrap(msg);
+      if (msg.name == 'TEST-ONLY:nuwa-ready') {
+        ok(true, "Got nuwa-ready");
+        is(seenNuwaReady, false, "Already received nuwa ready");
+        seenNuwaReady = true;
+      } else if (msg.name == 'TEST-ONLY:nuwa-add-new-process') {
+        ok(true, "Got nuwa-add-new-process");
+        is(seenNuwaReady, true, "Receive nuwa-add-new-process before nuwa-ready");
+        shutdown();
+      }
+    }
+  };
+
+  function shutdown() {
+    info("Shut down the test case");
+    cpmm.removeMessageListener("TEST-ONLY:nuwa-ready", msgHandler);
+    cpmm.removeMessageListener("TEST-ONLY:nuwa-add-new-process", msgHandler);
+
+    SimpleTest.finish();
+  }
+
+  cpmm.addMessageListener("TEST-ONLY:nuwa-ready", msgHandler);
+  cpmm.addMessageListener("TEST-ONLY:nuwa-add-new-process", msgHandler);
+
+
+  // Setting this pref to true should cause us to prelaunch a process.
+  SpecialPowers.setBoolPref('dom.ipc.processPrelaunch.enabled', true);
+}
+
+function setup2()
+{
+  info("Enable the Nuwa process to test the unmonitored thread");
+
+  SpecialPowers.pushPrefEnv({
+    'set': [
+      ['dom.ipc.processPrelaunch.enabled', false],
+      ['dom.ipc.preallocatedProcessManager.testMode', true]
+    ]
+  }, runTest);
+}
+
+function setup()
+{
+  info("Create an unmonitored thread.");
+
+  SimpleTest.waitForExplicitFinish();
+
+  SpecialPowers.pushPrefEnv({
+    'set': [
+      // For testing NS_NewUnmonitoredThread()
+      ['dom.ipc.newUnmonitoredThread.testMode', true],
+    ]
+  }, setup2);
+}
+
+</script>
+</body>
+</html>
--- a/dom/mobilemessage/gonk/MmsService.js
+++ b/dom/mobilemessage/gonk/MmsService.js
@@ -162,16 +162,20 @@ XPCOMUtils.defineLazyServiceGetter(this,
 XPCOMUtils.defineLazyServiceGetter(this, "gNetworkManager",
                                    "@mozilla.org/network/manager;1",
                                    "nsINetworkManager");
 
 XPCOMUtils.defineLazyServiceGetter(this, "gMobileConnectionService",
                                    "@mozilla.org/mobileconnection/mobileconnectionservice;1",
                                    "nsIMobileConnectionService");
 
+XPCOMUtils.defineLazyServiceGetter(this, "gNetworkService",
+                                   "@mozilla.org/network/service;1",
+                                   "nsINetworkService");
+
 XPCOMUtils.defineLazyGetter(this, "MMS", function() {
   let MMS = {};
   Cu.import("resource://gre/modules/MmsPduHelper.jsm", MMS);
   return MMS;
 });
 
 // Internal Utilities
 
@@ -701,55 +705,61 @@ XPCOMUtils.defineLazyGetter(this, "gMmsT
           return;
         }
 
         // MMSC is available after an MMS connection is successfully acquired.
         if (!url) {
           url = mmsConnection.mmsc;
         }
 
-        let startTransaction = () => {
+        let startTransaction = netId => {
           if (DEBUG) debug("sendRequest: register proxy filter to " + url);
           let proxyFilter = new MmsProxyFilter(mmsConnection, url);
           gpps.registerFilter(proxyFilter, 0);
 
           cancellable.xhr =
             this.sendHttpRequest(mmsConnection, method,
-                                 url, istream, proxyFilter,
+                                 url, istream, proxyFilter, netId,
                                  (aHttpStatus, aData) =>
                                    cancellable.done(aHttpStatus, aData));
         };
 
+        let onRejected = aReason => {
+          debug(aReason);
+          mmsConnection.release();
+          cancellable.done(_HTTP_STATUS_FAILED_TO_ROUTE, null);
+        };
+
+        // TODO: |getNetId| will be implemented as a sync call in nsINetworkManager
+        //       once Bug 1141903 is landed.
         mmsConnection.ensureRouting(url)
-          .then(() => startTransaction(),
-                (aError) => {
-                  debug("Failed to ensureRouting: " + aError);
-
-                  mmsConnection.release();
-                  cancellable.done(_HTTP_STATUS_FAILED_TO_ROUTE, null);
-                });
+          .then(() => gNetworkService.getNetId(mmsConnection.networkInterface.name),
+                (aReason) => onRejected('Failed to ensureRouting: ' + aReason))
+          .then((netId) => startTransaction(netId),
+                (aReason) => onRejected('Failed to getNetId: ' + aReason));
       });
 
       return cancellable;
     },
 
     sendHttpRequest: function(mmsConnection, method, url, istream, proxyFilter,
-                              callback) {
+                              netId, callback) {
       let releaseMmsConnectionAndCallback = (httpStatus, data) => {
         gpps.unregisterFilter(proxyFilter);
         // Always release the MMS network connection before callback.
         mmsConnection.release();
         callback(httpStatus, data);
       };
 
       try {
         let xhr = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]
                   .createInstance(Ci.nsIXMLHttpRequest);
 
         // Basic setups
+        xhr.networkInterfaceId = netId;
         xhr.open(method, url, true);
         xhr.responseType = "arraybuffer";
         if (istream) {
           xhr.setRequestHeader("Content-Type",
                                "application/vnd.wap.mms-message");
           xhr.setRequestHeader("Content-Length", istream.available());
         }
 
--- a/dom/nfc/gonk/Nfc.js
+++ b/dom/nfc/gonk/Nfc.js
@@ -608,16 +608,20 @@ Nfc.prototype = {
       return;
     }
 
     let nfcMsgType = message.name + "Response";
     message.data.errorMsg = errorMsg;
     message.target.sendAsyncMessage(nfcMsgType, message.data);
   },
 
+  getErrorMessage: function getErrorMessage(errorCode) {
+    return NFC.NFC_ERROR_MSG[errorCode];
+  },
+
   /**
    * Process the incoming message from the NFC Service.
    */
   onEvent: function onEvent(event) {
     let message = Cu.cloneInto(event, this);
     DEBUG && debug("Received message from NFC Service: " + JSON.stringify(message));
 
     message.type = message.rspType || message.ntfType;
--- a/dom/nfc/gonk/NfcService.h
+++ b/dom/nfc/gonk/NfcService.h
@@ -1,17 +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/. */
 
 #ifndef NfcService_h
 #define NfcService_h
 
 #include "mozilla/ipc/Nfc.h"
-#include "mozilla/ipc/UnixSocket.h"
+#include "mozilla/ipc/SocketBase.h"
 #include "nsCOMPtr.h"
 #include "nsINfcService.h"
 #include "NfcMessageHandler.h"
 
 class nsIThread;
 
 namespace mozilla {
 namespace dom {
--- a/dom/system/gonk/AutoMounter.cpp
+++ b/dom/system/gonk/AutoMounter.cpp
@@ -33,16 +33,17 @@
 #include "nsCharSeparatedTokenizer.h"
 #include "nsMemory.h"
 #include "nsString.h"
 #include "nsThreadUtils.h"
 #include "nsXULAppAPI.h"
 #include "OpenFileFinder.h"
 #include "Volume.h"
 #include "VolumeManager.h"
+#include "nsIStatusReporter.h"
 
 using namespace mozilla::hal;
 USING_MTP_NAMESPACE
 
 /**************************************************************************
 *
 * The following "switch" files are available for monitoring usb
 * connections:
@@ -254,16 +255,20 @@ public:
         // Note: eventually CheckVolumeSettings will call
         //       AutoMounter::SetSharingMode, which will in turn call
         //       UpdateState if needed.
       }
     }
   }
 
   void UpdateState();
+  void GetStatus(bool& umsAvail, bool& umsConfigured, bool& umsEnabled, bool& mtpAvail,
+	         bool& mtpConfigured, bool& mtpEnabled, bool& rndisConfigured);
+
+  nsresult Dump(nsACString& desc);
 
   void ConfigureUsbFunction(const char* aUsbFunc);
 
   bool StartMtpServer();
   void StopMtpServer();
 
   void StartUmsSharing();
   void StopUmsSharing();
@@ -454,16 +459,31 @@ private:
   RefPtr<VolumeResponseCallback>  mResponseCallback;
   int32_t                         mMode;
   MozMtpStorage::Array            mMozMtpStorage;
 };
 
 static StaticRefPtr<AutoMounter> sAutoMounter;
 static StaticRefPtr<MozMtpServer> sMozMtpServer;
 
+// The following is for status reporter
+enum STATE_REPORTER_STATE
+{
+  REPORTER_UNREGISTERED,
+  REPORTER_REGISTERED
+};
+
+static int status_reporter_progress = REPORTER_UNREGISTERED;
+nsresult getState(nsACString &desc)
+{
+  sAutoMounter->Dump(desc);
+  return NS_OK;
+}
+NS_STATUS_REPORTER_IMPLEMENT(AutoMounter, "AutoMounter", getState);
+
 /***************************************************************************/
 
 void
 AutoVolumeManagerStateObserver::Notify(const VolumeManager::StateChangedEvent &)
 {
   LOG("VolumeManager state changed event: %s", VolumeManager::StateStr());
 
   if (!sAutoMounter) {
@@ -698,66 +718,22 @@ AutoMounter::UpdateState()
   }
 
   // Calling setprop sys.usb.config mtp,adb (or adding mass_storage) will
   // cause /sys/devices/virtual/android_usb/android0/state to go:
   // CONFIGURED -> DISCONNECTED -> CONNECTED -> CONFIGURED
   //
   // Since IsUsbCablePluggedIn returns state == CONFIGURED, it will look
   // like a cable pull and replugin.
-
-  bool umsAvail = false;
-  bool umsConfigured = false;
-  bool umsEnabled = false;
-  bool mtpAvail = false;
-  bool mtpConfigured = false;
-  bool mtpEnabled = false;
-  bool rndisConfigured = false;
+  bool umsAvail, umsConfigured, umsEnabled;
+  bool mtpAvail, mtpConfigured, mtpEnabled;
+  bool rndisConfigured;
   bool usbCablePluggedIn = IsUsbCablePluggedIn();
-
-  if (access(ICS_SYS_USB_FUNCTIONS, F_OK) == 0) {
-    char functionsStr[60];
-    if (!ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
-      ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
-      functionsStr[0] = '\0';
-    }
-    DBG("UpdateState: USB functions: '%s'", functionsStr);
-
-    bool  usbConfigured = IsUsbConfigured();
-
-    // On the Nexus 4/5, it advertises that the UMS usb function is available,
-    // but we have a further requirement that mass_storage be in the
-    // persist.sys.usb.config property.
-    char persistSysUsbConfig[PROPERTY_VALUE_MAX];
-    property_get(PERSIST_SYS_USB_CONFIG, persistSysUsbConfig, "");
-    if (IsUsbFunctionEnabled(persistSysUsbConfig, USB_FUNC_UMS)) {
-      umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0);
-    }
-    if (umsAvail) {
-      umsConfigured = usbConfigured && strstr(functionsStr, USB_FUNC_UMS) != nullptr;
-      umsEnabled = (mMode == AUTOMOUNTER_ENABLE_UMS) ||
-                   ((mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) && umsConfigured);
-    } else {
-      umsConfigured = false;
-      umsEnabled = false;
-    }
-
-    mtpAvail = (access(ICS_SYS_MTP_DIRECTORY, F_OK) == 0);
-    if (mtpAvail) {
-      mtpConfigured = usbConfigured && strstr(functionsStr, USB_FUNC_MTP) != nullptr;
-      mtpEnabled = (mMode == AUTOMOUNTER_ENABLE_MTP) ||
-                   ((mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) && mtpConfigured);
-    } else {
-      mtpConfigured = false;
-      mtpEnabled = false;
-    }
-
-    rndisConfigured = strstr(functionsStr, USB_FUNC_RNDIS) != nullptr;
-  }
-
+  GetStatus(umsAvail, umsConfigured, umsEnabled, mtpAvail,
+            mtpConfigured, mtpEnabled, rndisConfigured);
   bool enabled = mtpEnabled || umsEnabled;
 
   if (mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) {
     // DISABLE_WHEN_UNPLUGGED implies already enabled.
     enabled = usbCablePluggedIn;
     if (!usbCablePluggedIn) {
       mMode = AUTOMOUNTER_DISABLE;
       mtpEnabled = false;
@@ -1134,16 +1110,177 @@ AutoMounter::UpdateState()
     filesOpenDelayCount = 0;
     status = AUTOMOUNTER_STATUS_ENABLED;
   }
   SetAutoMounterStatus(status);
 }
 
 /***************************************************************************/
 
+void AutoMounter::GetStatus(bool& umsAvail, bool& umsConfigured, bool& umsEnabled,
+                            bool& mtpAvail, bool& mtpConfigured, bool& mtpEnabled,
+                            bool& rndisConfigured)
+{
+  umsConfigured = false;
+  umsEnabled = false;
+  mtpAvail = false;
+  mtpConfigured = false;
+  mtpEnabled = false;
+  rndisConfigured = false;
+
+  if (access(ICS_SYS_USB_FUNCTIONS, F_OK) != 0) {
+    return;
+  }
+
+  char functionsStr[60];
+  if (!ReadSysFile(ICS_SYS_USB_FUNCTIONS, functionsStr, sizeof(functionsStr))) {
+    ERR("Error reading file '%s': %s", ICS_SYS_USB_FUNCTIONS, strerror(errno));
+    functionsStr[0] = '\0';
+  }
+  DBG("GetStatus: USB functions: '%s'", functionsStr);
+
+  bool  usbConfigured = IsUsbConfigured();
+
+  // On the Nexus 4/5, it advertises that the UMS usb function is available,
+  // but we have a further requirement that mass_storage be in the
+  // persist.sys.usb.config property.
+  char persistSysUsbConfig[PROPERTY_VALUE_MAX];
+  property_get(PERSIST_SYS_USB_CONFIG, persistSysUsbConfig, "");
+  if (IsUsbFunctionEnabled(persistSysUsbConfig, USB_FUNC_UMS)) {
+    umsAvail = (access(ICS_SYS_UMS_DIRECTORY, F_OK) == 0);
+  }
+  if (umsAvail) {
+    umsConfigured = usbConfigured && strstr(functionsStr, USB_FUNC_UMS) != nullptr;
+    umsEnabled = (mMode == AUTOMOUNTER_ENABLE_UMS) ||
+                 ((mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) && umsConfigured);
+  } else {
+    umsConfigured = false;
+    umsEnabled = false;
+  }
+
+  mtpAvail = (access(ICS_SYS_MTP_DIRECTORY, F_OK) == 0);
+  if (mtpAvail) {
+    mtpConfigured = usbConfigured && strstr(functionsStr, USB_FUNC_MTP) != nullptr;
+    mtpEnabled = (mMode == AUTOMOUNTER_ENABLE_MTP) ||
+                 ((mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) && mtpConfigured);
+  } else {
+    mtpConfigured = false;
+    mtpEnabled = false;
+  }
+
+  rndisConfigured = strstr(functionsStr, USB_FUNC_RNDIS) != nullptr;
+}
+
+
+nsresult AutoMounter::Dump(nsACString& desc)
+{
+  DBG("GetState!");
+  bool umsAvail, umsConfigured, umsEnabled;
+  bool mtpAvail, mtpConfigured, mtpEnabled;
+  bool rndisConfigured;
+  GetStatus(umsAvail, umsConfigured, umsEnabled, mtpAvail,
+            mtpConfigured, mtpEnabled, rndisConfigured);
+  if (mMode == AUTOMOUNTER_DISABLE_WHEN_UNPLUGGED) {
+    // DISABLE_WHEN_UNPLUGGED implies already enabled.
+    if (!IsUsbCablePluggedIn()) {
+      mMode = AUTOMOUNTER_DISABLE;
+      mtpEnabled = false;
+      umsEnabled = false;
+    }
+  }
+
+  // Automounter information
+  desc += "Current Mode:";
+  desc += ModeStr(mMode);
+  desc += "|";
+
+
+  desc += "Current State:";
+  desc += StateStr(mState);
+  desc += "|";
+
+  desc += "UMS Status:";
+  if (umsAvail) {
+    desc += "Available";
+  } else {
+    desc += "UnAvailable";
+  }
+  desc += ",";
+  if (umsConfigured) {
+    desc += "Configured";
+  } else {
+    desc += "Un-Configured";
+  }
+  desc += ",";
+  if (umsEnabled) {
+    desc += "Enabled";
+  } else {
+    desc += "Disabled";
+  }
+  desc += "|";
+
+
+  desc += "MTP Status:";
+  if (mtpAvail) {
+    desc += "Available";
+  } else {
+    desc += "UnAvailable";
+  }
+  desc += ",";
+  if (mtpConfigured) {
+    desc += "Configured";
+  } else {
+    desc += "Un-Configured";
+  }
+  desc += ",";
+  if (mtpEnabled) {
+    desc += "Enabled";
+  } else {
+    desc += "Disabled";
+  }
+
+
+  // Volume information
+  VolumeArray::index_type volIndex;
+  VolumeArray::size_type  numVolumes = VolumeManager::NumVolumes();
+  for (volIndex = 0; volIndex < numVolumes; volIndex++) {
+    RefPtr<Volume>  vol = VolumeManager::GetVolume(volIndex);
+
+    desc += "|";
+    desc += vol->NameStr();
+    desc += ":";
+    desc += vol->StateStr();
+    desc += "@";
+    desc += vol->MountPoint().get();
+
+    if (!vol->MediaPresent()) {
+      continue;
+    }
+
+    if (vol->CanBeShared()) {
+      desc += ",CanBeShared";
+    }
+    if (vol->CanBeFormatted()) {
+      desc += ",CanBeFormatted";
+    }
+    if (vol->CanBeMounted()) {
+      desc += ",CanBeMounted";
+    }
+    if (vol->IsRemovable()) {
+      desc += ",Removable";
+    }
+    if (vol->IsHotSwappable()) {
+      desc += ",HotSwappable";
+    }
+  }
+
+  return NS_OK;
+}
+
+
 static void
 InitAutoMounterIOThread()
 {
   MOZ_ASSERT(MessageLoop::current() == XRE_GetIOMessageLoop());
   MOZ_ASSERT(!sAutoMounter);
 
   sAutoMounter = new AutoMounter();
 }
@@ -1265,16 +1402,22 @@ InitAutoMounter()
   XRE_GetIOMessageLoop()->PostTask(
       FROM_HERE,
       NewRunnableFunction(InitAutoMounterIOThread));
 
   // Switch Observers need to run on the main thread, so we need to
   // start it here and have it send events to the AutoMounter running
   // on the IO Thread.
   sUsbCableObserver = new UsbCableObserver();
+
+  // Register status reporter into reporter manager
+  if(status_reporter_progress == REPORTER_UNREGISTERED) {
+    NS_RegisterStatusReporter(new NS_STATUS_REPORTER_NAME(AutoMounter));
+  }
+  status_reporter_progress = REPORTER_REGISTERED;
 }
 
 int32_t
 GetAutoMounterStatus()
 {
   if (sAutoMounterSetting) {
     return sAutoMounterSetting->GetStatus();
   }
@@ -1335,16 +1478,21 @@ AutoMounterUnmountVolume(const nsCString
 }
 
 void
 ShutdownAutoMounter()
 {
   if (sAutoMounter) {
     DBG("ShutdownAutoMounter: About to StopMtpServer");
     sAutoMounter->StopMtpServer();
+    // Unregister status reporter into reporter manager
+    if(status_reporter_progress == REPORTER_REGISTERED) {
+      NS_UnregisterStatusReporter(new NS_STATUS_REPORTER_NAME(AutoMounter));
+    }
+    status_reporter_progress = REPORTER_UNREGISTERED;
   }
   sAutoMounterSetting = nullptr;
   sUsbCableObserver = nullptr;
 
   XRE_GetIOMessageLoop()->PostTask(
       FROM_HERE,
       NewRunnableFunction(ShutdownAutoMounterIOThread));
 }
--- a/dom/system/gonk/NetworkManager.js
+++ b/dom/system/gonk/NetworkManager.js
@@ -272,58 +272,63 @@ NetworkManager.prototype = {
 
     // Note that since Lollipop we need to allocate and initialize
     // something through netd, so we add createNetwork/destroyNetwork
     // to deal with that explicitly.
 
     switch (network.state) {
       case Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED:
         gNetworkService.createNetwork(network.name, () => {
-          // Add host route for data calls
-          if (this.isNetworkTypeMobile(network.type)) {
-            let currentInterfaceLinks = this.networkInterfaceLinks[networkId];
-            let newLinkRoutes = network.getDnses().concat(network.httpProxyHost);
-            // If gateways have changed, remove all old routes first.
-            this._handleGateways(networkId, network.getGateways())
-              .then(() => this._updateRoutes(currentInterfaceLinks.linkRoutes,
-                                             newLinkRoutes,
-                                             network.getGateways(), network.name))
-              .then(() => currentInterfaceLinks.setLinks(newLinkRoutes,
-                                                         network.getGateways(),
-                                                         network.name));
-          }
 
           // Remove pre-created default route and let setAndConfigureActive()
           // to set default route only on preferred network
           gNetworkService.removeDefaultRoute(network);
 
-          // Dun type is a special case where we add the default route to a
-          // secondary table.
-          if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
-            this.setSecondaryDefaultRoute(network);
-          }
-
-          this._addSubnetRoutes(network);
-          this.setAndConfigureActive();
+          // Set DNS server as early as possible to prevent from
+          // premature domain name lookup.
+          gNetworkService.setDNS(network, () => {
+            // Add host route for data calls
+            if (this.isNetworkTypeMobile(network.type)) {
+              let currentInterfaceLinks = this.networkInterfaceLinks[networkId];
+              let newLinkRoutes = network.getDnses().concat(network.httpProxyHost);
+              // If gateways have changed, remove all old routes first.
+              this._handleGateways(networkId, network.getGateways())
+                .then(() => this._updateRoutes(currentInterfaceLinks.linkRoutes,
+                                               newLinkRoutes,
+                                               network.getGateways(), network.name))
+                .then(() => currentInterfaceLinks.setLinks(newLinkRoutes,
+                                                           network.getGateways(),
+                                                           network.name));
+            }
 
-          // Update data connection when Wifi connected/disconnected
-          if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI && this.mRil) {
-            for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
-              this.mRil.getRadioInterface(i).updateRILNetworkInterface();
+            // Dun type is a special case where we add the default route to a
+            // secondary table.
+            if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_DUN) {
+              this.setSecondaryDefaultRoute(network);
             }
-          }
+
+            this._addSubnetRoutes(network);
+            this.setAndConfigureActive();
 
-          // Probing the public network accessibility after routing table is ready
-          CaptivePortalDetectionHelper
-            .notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active);
+            // Update data connection when Wifi connected/disconnected
+            if (network.type == Ci.nsINetworkInterface.NETWORK_TYPE_WIFI && this.mRil) {
+              for (let i = 0; i < this.mRil.numRadioInterfaces; i++) {
+                this.mRil.getRadioInterface(i).updateRILNetworkInterface();
+              }
+            }
 
-          // Notify outer modules like MmsService to start the transaction after
-          // the configuration of the network interface is done.
-          Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED,
-                                       this.convertConnectionType(network));
+            // Probing the public network accessibility after routing table is ready
+            CaptivePortalDetectionHelper
+              .notify(CaptivePortalDetectionHelper.EVENT_CONNECT, this.active);
+
+            // Notify outer modules like MmsService to start the transaction after
+            // the configuration of the network interface is done.
+            Services.obs.notifyObservers(network, TOPIC_CONNECTION_STATE_CHANGED,
+                                         this.convertConnectionType(network));
+          });
         });
 
         break;
       case Ci.nsINetworkInterface.NETWORK_STATE_DISCONNECTED:
         // Remove host route for data calls
         if (this.isNetworkTypeMobile(network.type)) {
           this._cleanupAllHostRoutes(networkId);
         }
@@ -636,28 +641,28 @@ NetworkManager.prototype = {
     let oldActive = this.active;
 
     if (this._overriddenActive) {
       debug("We have an override for the active network: " +
             this._overriddenActive.name);
       // The override was just set, so reconfigure the network.
       if (this.active != this._overriddenActive) {
         this.active = this._overriddenActive;
-        this._setDefaultRouteAndDNS(this.active, oldActive);
+        this._setDefaultRouteAndProxy(this.active, oldActive);
         Services.obs.notifyObservers(this.active, TOPIC_ACTIVE_CHANGED, null);
       }
       return;
     }
 
     // The active network is already our preferred type.
     if (this.active &&
         this.active.state == Ci.nsINetworkInterface.NETWORK_STATE_CONNECTED &&
         this.active.type == this._preferredNetworkType) {
       debug("Active network is already our preferred type.");
-      this._setDefaultRouteAndDNS(this.active, oldActive);
+      this._setDefaultRouteAndProxy(this.active, oldActive);
       return;
     }
 
     // Find a suitable network interface to activate.
     this.active = null;
 
     let defaultDataNetwork;
     for each (let network in this.networkInterfaces) {
@@ -681,20 +686,18 @@ NetworkManager.prototype = {
       // If default data APN is not connected, we still set default route
       // and DNS on secondary APN.
       if (defaultDataNetwork &&
           this.isNetworkTypeSecondaryMobile(this.active.type) &&
           this.active.type != this.preferredNetworkType) {
         this.active = defaultDataNetwork;
       }
       // Don't set default route on secondary APN
-      if (this.isNetworkTypeSecondaryMobile(this.active.type)) {
-        gNetworkService.setDNS(this.active, function() {});
-      } else {
-        this._setDefaultRouteAndDNS(this.active, oldActive);
+      if (!this.isNetworkTypeSecondaryMobile(this.active.type)) {
+        this._setDefaultRouteAndProxy(this.active, oldActive);
       }
     }
 
     if (this.active != oldActive) {
       Services.obs.notifyObservers(this.active, TOPIC_ACTIVE_CHANGED, null);
     }
 
     if (this._manageOfflineStatus) {
@@ -708,54 +711,58 @@ NetworkManager.prototype = {
       return Promise.reject(new Error("hostname is empty: " + hostname));
     }
 
     if (hostname.match(this.REGEXP_IPV4) ||
         hostname.match(this.REGEXP_IPV6)) {
       return Promise.resolve([hostname]);
     }
 
-    let deferred = Promise.defer();
-    let onLookupComplete = (aRequest, aRecord, aStatus) => {
-      if (!Components.isSuccessCode(aStatus)) {
-        deferred.reject(new Error(
-          "Failed to resolve '" + hostname + "', with status: " + aStatus));
-        return;
-      }
+    // Wrap gDNSService.asyncResolveExtended to a promise, which
+    // resolves with an array of ip addresses or rejects with
+    // the reason otherwise.
+    let hostResolveWrapper = aNetId => {
+      return new Promise((aResolve, aReject) => {
+        // Callback for gDNSService.asyncResolveExtended.
+        let onLookupComplete = (aRequest, aRecord, aStatus) => {
+          if (!Components.isSuccessCode(aStatus)) {
+            aReject(new Error("Failed to resolve '" + hostname +
+                              "', with status: " + aStatus));
+            return;
+          }
+
+          let retval = [];
+          while (aRecord.hasMore()) {
+            retval.push(aRecord.getNextAddrAsString());
+          }
 
-      let retval = [];
-      while (aRecord.hasMore()) {
-        retval.push(aRecord.getNextAddrAsString());
-      }
+          if (!retval.length) {
+            aReject(new Error("No valid address after DNS lookup!"));
+            return;
+          }
+
+          debug("hostname is resolved: " + hostname);
+          debug("Addresses: " + JSON.stringify(retval));
 
-      if (!retval.length) {
-        deferred.reject(new Error("No valid address after DNS lookup!"));
-        return;
-      }
+          aResolve(retval);
+        };
 
-      debug("hostname is resolved: " + hostname);
-      debug("Addresses: " + JSON.stringify(retval));
-
-      deferred.resolve(retval);
+        debug('Calling gDNSService.asyncResolveExtended: ' + aNetId + ', ' + hostname);
+        gDNSService.asyncResolveExtended(hostname,
+                                         0,
+                                         aNetId,
+                                         onLookupComplete,
+                                         Services.tm.mainThread);
+      });
     };
 
-    // Bug 1058282 - Explicitly request ipv4 to get around 8.8.8.8 probe at
-    // http://androidxref.com/4.3_r2.1/xref/bionic/libc/netbsd/net/getaddrinfo.c#1923
-    //
-    // Whenever MMS connection is the only network interface, there is no
-    // default route so that any ip probe will fail.
-    let flags = 0;
-    if (network.type === Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE_MMS) {
-      flags |= Ci.nsIDNSService.RESOLVE_DISABLE_IPV6;
-    }
-
-    // TODO: Bug 992772 - Resolve the hostname with specified networkInterface.
-    gDNSService.asyncResolve(hostname, flags, onLookupComplete, Services.tm.mainThread);
-
-    return deferred.promise;
+    // TODO: |getNetId| will be implemented as a sync call in nsINetworkManager
+    //       once Bug 1141903 is landed.
+    return gNetworkService.getNetId(network.name)
+      .then(aNetId => hostResolveWrapper(aNetId));
   },
 
   convertConnectionType: function(network) {
     // If there is internal interface change (e.g., MOBILE_MMS, MOBILE_SUPL),
     // the function will return null so that it won't trigger type change event
     // in NetworkInformation API.
     if (network.type != Ci.nsINetworkInterface.NETWORK_TYPE_WIFI &&
         network.type != Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE) {
@@ -769,25 +776,23 @@ NetworkManager.prototype = {
     switch (network.type) {
       case Ci.nsINetworkInterface.NETWORK_TYPE_WIFI:
         return CONNECTION_TYPE_WIFI;
       case Ci.nsINetworkInterface.NETWORK_TYPE_MOBILE:
         return CONNECTION_TYPE_CELLULAR;
     }
   },
 
-  _setDefaultRouteAndDNS: function(network, oldInterface) {
+  _setDefaultRouteAndProxy: function(network, oldInterface) {
     gNetworkService.setDefaultRoute(network, oldInterface, function(success) {
       if (!success) {
         gNetworkService.destroyNetwork(network, function() {});
         return;
       }
-      gNetworkService.setDNS(network, function(result) {
-        gNetworkService.setNetworkProxy(network);
-      });
+      gNetworkService.setNetworkProxy(network);
     });
   },
 };
 
 let CaptivePortalDetectionHelper = (function() {
 
   const EVENT_CONNECT = "Connect";
   const EVENT_DISCONNECT = "Disconnect";
--- a/dom/system/gonk/NetworkService.js
+++ b/dom/system/gonk/NetworkService.js
@@ -386,17 +386,18 @@ NetworkService.prototype = {
 
   setDNS: function(networkInterface, callback) {
     debug("Going DNS to " + networkInterface.name);
     let dnses = networkInterface.getDnses();
     let options = {
       cmd: "setDNS",
       ifname: networkInterface.name,
       domain: "mozilla." + networkInterface.name + ".doman",
-      dnses: dnses
+      dnses: dnses,
+      gateways: networkInterface.getGateways()
     };
     this.controlMessage(options, function(result) {
       callback.setDnsResult(result.success ? null : result.reason);
     });
   },
 
   setDefaultRoute: function(network, oldInterface, callback) {
     debug("Going to change default route to " + network.name);
@@ -757,11 +758,28 @@ NetworkService.prototype = {
       cmd: "destroyNetwork",
       ifname: interfaceName
     };
 
     this.controlMessage(params, function(result) {
       callback.nativeCommandResult(!result.error);
     });
   },
+
+  getNetId: function(interfaceName) {
+    let params = {
+      cmd: "getNetId",
+      ifname: interfaceName
+    };
+
+    return new Promise((aResolve, aReject) => {
+      this.controlMessage(params, result => {
+        if (result.error) {
+          aReject(result.reason);
+          return;
+        }
+        aResolve(result.netId);
+      });
+    });
+  },
 };
 
 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([NetworkService]);
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -435,17 +435,16 @@ CommandResult::CommandResult(int32_t aRe
   if (aResultCode != SUCCESS) {
     // The returned value is sometimes negative, make sure we pass a positive
     // error number to strerror.
     enum { STRERROR_R_BUF_SIZE = 1024, };
     char strerrorBuf[STRERROR_R_BUF_SIZE];
     strerror_r(abs(aResultCode), strerrorBuf, STRERROR_R_BUF_SIZE);
     mResult.mReason = NS_ConvertUTF8toUTF16(strerrorBuf);
   }
-  mResult.mRet = true;
 }
 
 CommandResult::CommandResult(const mozilla::dom::NetworkResultOptions& aResult)
   : mResult(aResult)
   , mIsPending(false)
 {
 }
 
@@ -1530,16 +1529,17 @@ void NetworkUtils::ExecuteCommand(Networ
     BUILD_ENTRY(updateUpStream),
     BUILD_ENTRY(configureInterface),
     BUILD_ENTRY(dhcpRequest),
     BUILD_ENTRY(enableInterface),
     BUILD_ENTRY(disableInterface),
     BUILD_ENTRY(resetConnections),
     BUILD_ENTRY(createNetwork),
     BUILD_ENTRY(destroyNetwork),
+    BUILD_ENTRY(getNetId),
 
     #undef BUILD_ENTRY
   };
 
   // Loop until we find the command name which matches aOptions.mCmd.
   CommandHandler handler = nullptr;
   for (size_t i = 0; i < mozilla::ArrayLength(COMMAND_HANDLER_TABLE); i++) {
     if (aOptions.mCmd.EqualsASCII(COMMAND_HANDLER_TABLE[i].mCommandName)) {
@@ -1699,30 +1699,36 @@ CommandResult NetworkUtils::setDNS(Netwo
   snprintf(num, PROPERTY_VALUE_MAX - 1, "%d", atoi(dnschange) + 1);
   property_set("net.dnschange", num);
 
   // DNS needs to be set through netd since JellyBean (4.3).
   if (SDK_VERSION >= 20) {
     // Lollipop.
     static CommandFunc COMMAND_CHAIN[] = {
       setInterfaceDns,
+      addDefaultRouteToNetwork,
       defaultAsyncSuccessHandler
     };
     NetIdManager::NetIdInfo netIdInfo;
     if (!mNetIdManager.lookup(aOptions.mIfname, &netIdInfo)) {
       return -1;
     }
     aOptions.mNetId = netIdInfo.mNetId;
     runChain(aOptions, COMMAND_CHAIN, setDnsFail);
     return CommandResult::Pending();
   }
   if (SDK_VERSION >= 18) {
     // JB, KK.
     static CommandFunc COMMAND_CHAIN[] = {
+    #if ANDROID_VERSION == 18
+      // Since we don't use per-interface DNS lookup feature on JB,
+      // we need to set the default DNS interface whenever setting the
+      // DNS name server.
       setDefaultInterface,
+    #endif
       setInterfaceDns,
       defaultAsyncSuccessHandler
     };
     runChain(aOptions, COMMAND_CHAIN, setDnsFail);
     return CommandResult::Pending();
   }
 
   return SUCCESS;
@@ -1893,16 +1899,27 @@ CommandResult NetworkUtils::setDefaultRo
 
     if (type == AF_INET6) {
       RETURN_IF_FAILED(mNetUtils->do_ifc_add_route(autoIfname.get(), "::", 0, gateway));
     } else { /* type == AF_INET */
       RETURN_IF_FAILED(mNetUtils->do_ifc_set_default_route(autoIfname.get(), inet_addr(gateway)));
     }
   }
 
+  // Set the default DNS interface.
+  if (SDK_VERSION >= 18) {
+    // For JB, KK only.
+    static CommandFunc COMMAND_CHAIN[] = {
+      setDefaultInterface,
+      defaultAsyncSuccessHandler
+    };
+    runChain(aOptions, COMMAND_CHAIN, setDnsFail);
+    return CommandResult::Pending();
+  }
+
   return SUCCESS;
 }
 
 /**
  * Remove default route for given network interface.
  */
 CommandResult NetworkUtils::removeDefaultRoute(NetworkParams& aOptions)
 {
@@ -2511,16 +2528,37 @@ CommandResult NetworkUtils::destroyNetwo
   NU_DBG("Interface %s (%d) is no longer used. Tell netd to destroy.",
          GET_CHAR(mIfname), netIdInfo.mNetId);
 
   aOptions.mNetId = netIdInfo.mNetId;
   runChain(aOptions, COMMAND_CHAIN, defaultAsyncFailureHandler);
   return CommandResult::Pending();
 }
 
+/**
+ * Query the netId associated with the given network interface name.
+ */
+CommandResult NetworkUtils::getNetId(NetworkParams& aOptions)
+{
+  NetworkResultOptions result;
+
+  if (SDK_VERSION < 20) {
+    // For pre-Lollipop, use the interface name as the fallback.
+    result.mNetId = GET_FIELD(mIfname);
+    return result;
+  }
+
+  NetIdManager::NetIdInfo netIdInfo;
+  if (-1 == mNetIdManager.lookup(GET_FIELD(mIfname), &netIdInfo)) {
+    return ESRCH;
+  }
+  result.mNetId.AppendInt(netIdInfo.mNetId, 10);
+  return result;
+}
+
 void NetworkUtils::sendBroadcastMessage(uint32_t code, char* reason)
 {
   NetworkResultOptions result;
   switch(code) {
     case NETD_COMMAND_INTERFACE_CHANGE:
       result.mTopic = NS_ConvertUTF8toUTF16("netd-interface-change");
       break;
     case NETD_COMMAND_BANDWIDTH_CONTROLLER:
--- a/dom/system/gonk/NetworkUtils.h
+++ b/dom/system/gonk/NetworkUtils.h
@@ -304,16 +304,17 @@ private:
   CommandResult setWifiOperationMode(NetworkParams& aOptions);
   CommandResult setDhcpServer(NetworkParams& aOptions);
   CommandResult setWifiTethering(NetworkParams& aOptions);
   CommandResult setUSBTethering(NetworkParams& aOptions);
   CommandResult enableUsbRndis(NetworkParams& aOptions);
   CommandResult updateUpStream(NetworkParams& aOptions);
   CommandResult createNetwork(NetworkParams& aOptions);
   CommandResult destroyNetwork(NetworkParams& aOptions);
+  CommandResult getNetId(NetworkParams& aOptions);
 
   CommandResult addHostRouteLegacy(NetworkParams& aOptions);
   CommandResult removeHostRouteLegacy(NetworkParams& aOptions);
   CommandResult setDefaultRouteLegacy(NetworkParams& aOptions);
   CommandResult removeDefaultRouteLegacy(NetworkParams& aOptions);
   CommandResult removeNetworkRouteLegacy(NetworkParams& aOptions);
 
 
--- a/dom/system/gonk/nsINetworkService.idl
+++ b/dom/system/gonk/nsINetworkService.idl
@@ -154,17 +154,17 @@ interface nsIDhcpRequestCallback : nsISu
    */
   void dhcpRequestResult(in boolean success, in jsval dhcpInfo);
 };
 
 
 /**
  * Provide network services.
  */
-[scriptable, uuid(e40dd966-cb04-4dc7-ac4b-6382769c00b9)]
+[scriptable, uuid(a0f29630-c25c-11e4-8830-0800200c9a66)]
 interface nsINetworkService : nsISupports
 {
   const long MODIFY_ROUTE_ADD    = 0;
   const long MODIFY_ROUTE_REMOVE = 1;
 
   /**
    * Enable or disable Wifi Tethering
    *
@@ -481,9 +481,22 @@ interface nsINetworkService : nsISupport
    * @param networkInterface
    *        The network interface name which we want to reset.
    *
    * @param callback
    *        Callback to notify the result of resetting connections.
    */
   void destroyNetwork(in DOMString interfaceName,
                       in nsINativeCommandCallback callback);
+
+  /**
+   * Query the netId associated with given network interface name.
+   *
+   * @param interfaceName
+   *        The network interface name which we want to query.
+   *
+   * @return A deferred promise that resolves with a string to indicate.
+   *         the queried netId on success and rejects if the interface name
+   *         is invalid.
+   *
+   */
+  jsval getNetId(in DOMString interfaceName);
 };
--- a/dom/webidl/BluetoothAttributeEvent.webidl
+++ b/dom/webidl/BluetoothAttributeEvent.webidl
@@ -4,18 +4,16 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 [CheckPermissions="bluetooth",
  Constructor(DOMString type,
              optional BluetoothAttributeEventInit eventInitDict)]
 interface BluetoothAttributeEvent : Event
 {
-  readonly attribute any attrs;
-  // We don't support sequence in event codegen yet (Bug 1023762)
-  // Bug 1015796:
-  // readonly attribute sequence<DOMString> attrs;
+  [Cached, Constant]
+  readonly attribute sequence<DOMString> attrs;
 };
 
 dictionary BluetoothAttributeEventInit : EventInit
 {
-  any attrs = null;
+  required sequence<DOMString> attrs;
 };
--- a/dom/webidl/NetworkOptions.webidl
+++ b/dom/webidl/NetworkOptions.webidl
@@ -91,9 +91,11 @@ dictionary NetworkResultOptions
   DOMString vendor_str = "";
   long      lease = 0;
   long      mask = 0;
   long      ipaddr = 0;
   long      gateway = 0;
   long      dns1 = 0;
   long      dns2 = 0;
   long      server = 0;
+
+  DOMString netId = "";                // for "getNetId".
 };
--- a/dom/xbl/nsXBLPrototypeHandler.cpp
+++ b/dom/xbl/nsXBLPrototypeHandler.cpp
@@ -19,17 +19,16 @@
 #include "nsIDocument.h"
 #include "nsIController.h"
 #include "nsIControllers.h"
 #include "nsIDOMXULElement.h"
 #include "nsIURI.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsFocusManager.h"
-#include "nsIFormControl.h"
 #include "nsIDOMEventListener.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsIDOMWindow.h"
 #include "nsIServiceManager.h"
 #include "nsIScriptError.h"
 #include "nsXPIDLString.h"
 #include "nsReadableUtils.h"
@@ -472,27 +471,44 @@ nsXBLPrototypeHandler::DispatchXBLComman
 
     nsCOMPtr<nsIContent> focusedContent;
     if (windowToCheck) {
       nsCOMPtr<nsPIDOMWindow> focusedWindow;
       focusedContent =
         nsFocusManager::GetFocusedDescendant(windowToCheck, true, getter_AddRefs(focusedWindow));
     }
 
-    // If the focus is in an editable region, don't scroll.
-    if (focusedContent->IsEditable()) {
-      return NS_OK;
-    }
+    bool isLink = false;
+    nsIContent *content = focusedContent;
+
+    // if the focused element is a link then we do want space to 
+    // scroll down. The focused element may be an element in a link,
+    // we need to check the parent node too. Only do this check if an
+    // element is focused and has a parent.
+    if (focusedContent && focusedContent->GetParent()) {
+      while (content) {
+        if (content->IsHTMLElement(nsGkAtoms::a)) {
+          isLink = true;
+          break;
+        }
 
-    // If the focus is in a form control, don't scroll.
-    for (nsIContent* c = focusedContent; c; c = c->GetParent()) {
-      nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(c);
-      if (formControl) {
+        if (content->HasAttr(kNameSpaceID_XLink, nsGkAtoms::type)) {
+          isLink = content->AttrValueIs(kNameSpaceID_XLink, nsGkAtoms::type,
+                                        nsGkAtoms::simple, eCaseMatters);
+
+          if (isLink) {
+            break;
+          }
+        }
+
+        content = content->GetParent();
+      }
+
+      if (!isLink)
         return NS_OK;
-      }
     }
   }
 
   // We are the default action for this command.
   // Stop any other default action from executing.
   aEvent->PreventDefault();
   
   if (controller)
deleted file mode 100644
--- a/editor/libeditor/tests/file_bug915962.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<html>
-  <body>
-    <button>Button</button>
-    <img src="green.png" usemap="#map">
-    <map name="map">
-      <!-- This URL ensures that the link doesn't get clicked, since
-           mochitests cannot access the outside network. -->
-      <area shape="rect" coords="0,0,10,10" href="https://youtube.com/">
-    </map>
-    <div style="height: 20000px;" tabindex="-1"><hr></div>
-  </body>
-</html>
--- a/editor/libeditor/tests/mochitest.ini
+++ b/editor/libeditor/tests/mochitest.ini
@@ -4,17 +4,16 @@ support-files =
   data/cfhtml-chromium.txt
   data/cfhtml-firefox.txt
   data/cfhtml-ie.txt
   data/cfhtml-ooo.txt
   data/cfhtml-nocontext.txt
   file_bug549262.html
   file_bug586662.html
   file_bug674770-1.html
-  file_bug915962.html
   file_select_all_without_body.html
   green.png
   spellcheck.js
 
 [test_bug200416.html]
 [test_bug289384.html]
 skip-if = os != "mac"
 [test_bug290026.html]
@@ -133,18 +132,16 @@ skip-if = toolkit == 'android' || e10s
 [test_bug780035.html]
 [test_bug787432.html]
 [test_bug790475.html]
 [test_bug795785.html]
 [test_bug796839.html]
 [test_bug832025.html]
 [test_bug857487.html]
 [test_bug858918.html]
-[test_bug915962.html]
-skip-if = toolkit == 'android' || e10s
 [test_bug966155.html]
 skip-if = os != "win"
 [test_bug966552.html]
 skip-if = os != "win"
 [test_bug998188.html]
 [test_bug1026397.html]
 [test_bug1067255.html]
 [test_bug1094000.html]
deleted file mode 100644
--- a/editor/libeditor/tests/test_bug915962.html
+++ /dev/null
@@ -1,85 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=915962
--->
-<head>
-  <title>Test for Bug 915962</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
-</head>
-<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=915962">Mozilla Bug 915962</a>
-<p id="display"></p>
-<div id="content">
-</div>
-<pre id="test">
-<script type="application/javascript">
-
-/** Test for Bug 915962 **/
-
-var smoothScrollPref = "general.smoothScroll";
-SimpleTest.waitForExplicitFinish();
-var win = window.open("file_bug915962.html", "_blank",
-                      "width=600,height=600,scrollbars=yes");
-
-// grab the timer right at the start
-var cwu = SpecialPowers.getDOMWindowUtils(win);
-function step() {
-  cwu.advanceTimeAndRefresh(100);
-}
-SimpleTest.waitForFocus(function() {
-  SpecialPowers.pushPrefEnv({"set":[[smoothScrollPref, false]]}, startTest);
-}, win);
-function startTest() {
-  // Make sure that pressing Space when a tabindex=-1 element is focused
-  // will scroll the page.
-  var button = win.document.querySelector("button");
-  var sc = win.document.querySelector("div");
-  sc.focus();
-  is(win.scrollY, 0, "Sanity check");
-  synthesizeKey(" ", {}, win);
-
-  step();
-
-  isnot(win.scrollY, 0, "Page is scrolled down");
-  var oldY = win.scrollY;
-  synthesizeKey(" ", {shiftKey: true}, win);
-
-  step();
-
-  ok(win.scrollY < oldY, "Page is scrolled up");
-
-  // Make sure that pressing Space when a tabindex=-1 element is focused
-  // will not scroll the page, and will activate the element.
-  button.focus();
-  var clicked = false;
-  button.onclick = () => clicked = true;
-  oldY = win.scrollY;
-  synthesizeKey(" ", {}, win);
-
-  step();
-
-  ok(win.scrollY <= oldY, "Page is not scrolled down");
-  ok(clicked, "The button should be clicked");
-  synthesizeKey("VK_TAB", {}, win);
-
-  step();
-
-  oldY = win.scrollY;
-  synthesizeKey(" ", {}, win);
-
-  step()
-
-  ok(win.scrollY >= oldY, "Page is scrolled down");
-
-  win.close();
-  cwu.restoreNormalRefresh();
-
-  SimpleTest.finish();
-}
-</script>
-</pre>
-</body>
-</html>
--- a/editor/reftests/reftest.list
+++ b/editor/reftests/reftest.list
@@ -72,17 +72,17 @@ skip-if(B2G||Mulet) fails-if(Android) ne
 == spellcheck-comma-valid.html spellcheck-comma-valid-ref.html
 == spellcheck-hyphen-multiple-valid.html spellcheck-hyphen-multiple-valid-ref.html
 skip-if(B2G||Mulet) fails-if(Android) needs-focus != spellcheck-hyphen-multiple-invalid.html spellcheck-hyphen-multiple-invalid-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == spellcheck-dotafterquote-valid.html spellcheck-dotafterquote-valid-ref.html
 == spellcheck-url-valid.html spellcheck-url-valid-ref.html
 == unneeded_scroll.html unneeded_scroll-ref.html
 skip-if(B2G||Mulet) == caret_on_presshell_reinit.html caret_on_presshell_reinit-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) == caret_on_presshell_reinit-2.html caret_on_presshell_reinit-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
-skip-if(B2G||Mulet) == 642800.html 642800-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(B2G||Mulet) skip-if(asyncPanZoom&&winWidget) == 642800.html 642800-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == selection_visibility_after_reframe.html selection_visibility_after_reframe-ref.html
 != selection_visibility_after_reframe-2.html selection_visibility_after_reframe-ref.html
 != selection_visibility_after_reframe-3.html selection_visibility_after_reframe-ref.html
 == 672709.html 672709-ref.html
 == 338427-1.html 338427-1-ref.html
 skip-if(Android||B2G||Mulet) needs-focus == 674212-spellcheck.html 674212-spellcheck-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(Android||B2G||Mulet) needs-focus == 338427-2.html 338427-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(Android||B2G||Mulet) needs-focus == 338427-3.html 338427-3-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
deleted file mode 100644
--- a/ipc/unixsocket/UnixSocket.cpp
+++ /dev/null
@@ -1,667 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "UnixSocket.h"
-#include "nsTArray.h"
-#include "nsXULAppAPI.h"
-#include <fcntl.h>
-#include "mozilla/unused.h"
-
-static const size_t MAX_READ_SIZE = 1 << 16;
-
-namespace mozilla {
-namespace ipc {
-
-//
-// UnixSocketConsumerIO
-//
-
-class UnixSocketConsumerIO final : public UnixSocketWatcher
-                                 , protected SocketIOBase
-{
-public:
-  UnixSocketConsumerIO(MessageLoop* mIOLoop,
-                       UnixSocketConsumer* aConsumer,
-                       UnixSocketConnector* aConnector,
-                       const nsACString& aAddress);
-  ~UnixSocketConsumerIO();
-
-  void                GetSocketAddr(nsAString& aAddrStr) const;
-  SocketConsumerBase* GetConsumer();
-  SocketBase*         GetSocketBase();
-
-  // Shutdown state
-  //
-
-  bool IsShutdownOnMainThread() const;
-  void ShutdownOnMainThread();
-
-  bool IsShutdownOnIOThread() const;
-  void ShutdownOnIOThread();
-
-  // Delayed-task handling
-  //
-
-  void SetDelayedConnectTask(CancelableTask* aTask);
-  void ClearDelayedConnectTask();
-  void CancelDelayedConnectTask();
-
-  // Task callback methods
-  //
-
-  /**
-   * Run bind/listen to prepare for further runs of accept()
-   */
-  void Listen();
-
-  /**
-   * Connect to a socket
-   */
-  void Connect();
-
-  void Send(UnixSocketRawData* aData);
-
-  // I/O callback methods
-  //
-
-  void OnAccepted(int aFd, const sockaddr_any* aAddr,
-                  socklen_t aAddrLen) override;
-  void OnConnected() override;
-  void OnError(const char* aFunction, int aErrno) override;
-  void OnListening() override;
-  void OnSocketCanReceiveWithoutBlocking() override;
-  void OnSocketCanSendWithoutBlocking() override;
-
-private:
-  void FireSocketError();
-
-  // Set up flags on file descriptor.
-  static bool SetSocketFlags(int aFd);
-
-  /**
-   * Consumer pointer. Non-thread safe RefPtr, so should only be manipulated
-   * directly from main thread. All non-main-thread accesses should happen with
-   * mIO as container.
-   */
-  RefPtr<UnixSocketConsumer> mConsumer;
-
-  /**
-   * Connector object used to create the connection we are currently using.
-   */
-  nsAutoPtr<UnixSocketConnector> mConnector;
-
-  /**
-   * If true, do not requeue whatever task we're running
-   */
-  bool mShuttingDownOnIOThread;
-
-  /**
-   * Address we are connecting to, assuming we are creating a client connection.
-   */
-  nsCString mAddress;
-
-  /**
-   * Size of the socket address struct
-   */
-  socklen_t mAddrSize;
-
-  /**
-   * Address struct of the socket currently in use
-   */
-  sockaddr_any mAddr;
-
-  /**
-   * Task member for delayed connect task. Should only be access on main thread.
-   */
-  CancelableTask* mDelayedConnectTask;
-};
-
-UnixSocketConsumerIO::UnixSocketConsumerIO(MessageLoop* mIOLoop,
-                                           UnixSocketConsumer* aConsumer,
-                                           UnixSocketConnector* aConnector,
-                                           const nsACString& aAddress)
-  : UnixSocketWatcher(mIOLoop)
-  , SocketIOBase(MAX_READ_SIZE)
-  , mConsumer(aConsumer)
-  , mConnector(aConnector)
-  , mShuttingDownOnIOThread(false)
-  , mAddress(aAddress)
-  , mDelayedConnectTask(nullptr)
-{
-  MOZ_ASSERT(mConsumer);
-  MOZ_ASSERT(mConnector);
-}
-
-UnixSocketConsumerIO::~UnixSocketConsumerIO()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(IsShutdownOnMainThread());
-}
-
-void
-UnixSocketConsumerIO::GetSocketAddr(nsAString& aAddrStr) const
-{
-  if (!mConnector) {
-    NS_WARNING("No connector to get socket address from!");
-    aAddrStr.Truncate();
-    return;
-  }
-  mConnector->GetSocketAddr(mAddr, aAddrStr);
-}
-
-SocketConsumerBase*
-UnixSocketConsumerIO::GetConsumer()
-{
-  return mConsumer.get();
-}
-
-SocketBase*
-UnixSocketConsumerIO::GetSocketBase()
-{
-  return GetConsumer();
-}
-
-bool
-UnixSocketConsumerIO::IsShutdownOnMainThread() const
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  return mConsumer == nullptr;
-}
-
-void
-UnixSocketConsumerIO::ShutdownOnMainThread()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(!IsShutdownOnMainThread());
-
-  mConsumer = nullptr;
-}
-
-bool
-UnixSocketConsumerIO::IsShutdownOnIOThread() const
-{
-  return mShuttingDownOnIOThread;
-}
-
-void
-UnixSocketConsumerIO::ShutdownOnIOThread()
-{
-  MOZ_ASSERT(!NS_IsMainThread());
-  MOZ_ASSERT(!mShuttingDownOnIOThread);
-
-  Close(); // will also remove fd from I/O loop
-  mShuttingDownOnIOThread = true;
-}
-
-void
-UnixSocketConsumerIO::SetDelayedConnectTask(CancelableTask* aTask)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  mDelayedConnectTask = aTask;
-}
-
-void
-UnixSocketConsumerIO::ClearDelayedConnectTask()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  mDelayedConnectTask = nullptr;
-}
-
-void
-UnixSocketConsumerIO::CancelDelayedConnectTask()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-
-  if (!mDelayedConnectTask) {
-    return;
-  }
-
-  mDelayedConnectTask->Cancel();
-  ClearDelayedConnectTask();
-}
-
-void
-UnixSocketConsumerIO::Listen()
-{
-  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
-  MOZ_ASSERT(mConnector);
-
-  // This will set things we don't particularly care about, but it will hand
-  // back the correct structure size which is what we do care about.
-  if (!mConnector->CreateAddr(true, mAddrSize, mAddr, nullptr)) {
-    NS_WARNING("Cannot create socket address!");
-    FireSocketError();
-    return;
-  }
-
-  if (!IsOpen()) {
-    int fd = mConnector->Create();
-    if (fd < 0) {
-      NS_WARNING("Cannot create socket fd!");
-      FireSocketError();
-      return;
-    }
-    if (!SetSocketFlags(fd)) {
-      NS_WARNING("Cannot set socket flags!");
-      FireSocketError();
-      return;
-    }
-    SetFd(fd);
-
-    // calls OnListening on success, or OnError otherwise
-    nsresult rv = UnixSocketWatcher::Listen(
-      reinterpret_cast<struct sockaddr*>(&mAddr), mAddrSize);
-    NS_WARN_IF(NS_FAILED(rv));
-  }
-}
-
-void
-UnixSocketConsumerIO::Connect()
-{
-  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
-  MOZ_ASSERT(mConnector);
-
-  if (!IsOpen()) {
-    int fd = mConnector->Create();
-    if (fd < 0) {
-      NS_WARNING("Cannot create socket fd!");
-      FireSocketError();
-      return;
-    }
-    if (!SetSocketFlags(fd)) {
-      NS_WARNING("Cannot set socket flags!");
-      FireSocketError();
-      return;
-    }
-    SetFd(fd);
-  }
-
-  if (!mConnector->CreateAddr(false, mAddrSize, mAddr, mAddress.get())) {
-    NS_WARNING("Cannot create socket address!");
-    FireSocketError();
-    return;
-  }
-
-  // calls OnConnected() on success, or OnError() otherwise
-  nsresult rv = UnixSocketWatcher::Connect(
-    reinterpret_cast<struct sockaddr*>(&mAddr), mAddrSize);
-  NS_WARN_IF(NS_FAILED(rv));
-}
-
-void
-UnixSocketConsumerIO::Send(UnixSocketRawData* aData)
-{
-  EnqueueData(aData);
-  AddWatchers(WRITE_WATCHER, false);
-}
-
-void
-UnixSocketConsumerIO::OnAccepted(int aFd,
-                                 const sockaddr_any* aAddr,
-                                 socklen_t aAddrLen)
-{
-  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
-  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
-  MOZ_ASSERT(aAddr);
-  MOZ_ASSERT(aAddrLen > 0 && (size_t)aAddrLen <= sizeof(mAddr));
-
-  memcpy (&mAddr, aAddr, aAddrLen);
-  mAddrSize = aAddrLen;
-
-  if (!mConnector->SetUp(aFd)) {
-    NS_WARNING("Could not set up socket!");
-    return;
-  }
-
-  RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
-  Close();
-  if (!SetSocketFlags(aFd)) {
-    return;
-  }
-  SetSocket(aFd, SOCKET_IS_CONNECTED);
-
-  nsRefPtr<nsRunnable> r =
-    new SocketIOEventRunnable<UnixSocketConsumerIO>(
-      this, SocketIOEventRunnable<UnixSocketConsumerIO>::CONNECT_SUCCESS);
-  NS_DispatchToMainThread(r);
-
-  AddWatchers(READ_WATCHER, true);
-  if (HasPendingData()) {
-    AddWatchers(WRITE_WATCHER, false);
-  }
-}
-
-void
-UnixSocketConsumerIO::OnConnected()
-{
-  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
-  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED);
-
-  if (!SetSocketFlags(GetFd())) {
-    NS_WARNING("Cannot set socket flags!");
-    FireSocketError();
-    return;
-  }
-
-  if (!mConnector->SetUp(GetFd())) {
-    NS_WARNING("Could not set up socket!");
-    FireSocketError();
-    return;
-  }
-
-  nsRefPtr<nsRunnable> r =
-    new SocketIOEventRunnable<UnixSocketConsumerIO>(
-      this, SocketIOEventRunnable<UnixSocketConsumerIO>::CONNECT_SUCCESS);
-  NS_DispatchToMainThread(r);
-
-  AddWatchers(READ_WATCHER, true);
-  if (HasPendingData()) {
-    AddWatchers(WRITE_WATCHER, false);
-  }
-}
-
-void
-UnixSocketConsumerIO::OnListening()
-{
-  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
-  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_LISTENING);
-
-  if (!mConnector->SetUpListenSocket(GetFd())) {
-    NS_WARNING("Could not set up listen socket!");
-    FireSocketError();
-    return;
-  }
-
-  AddWatchers(READ_WATCHER, true);
-}
-
-void
-UnixSocketConsumerIO::OnError(const char* aFunction, int aErrno)
-{
-  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
-
-  UnixFdWatcher::OnError(aFunction, aErrno);
-  FireSocketError();
-}
-
-void
-UnixSocketConsumerIO::OnSocketCanReceiveWithoutBlocking()
-{
-  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
-  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
-
-  ssize_t res = ReceiveData(GetFd(), this);
-  if (res < 0) {
-    /* I/O error */
-    RemoveWatchers(READ_WATCHER|WRITE_WATCHER);
-  } else if (!res) {
-    /* EOF or peer shutdown */
-    RemoveWatchers(READ_WATCHER);
-  }
-}
-
-void
-UnixSocketConsumerIO::OnSocketCanSendWithoutBlocking()
-{
-  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
-  MOZ_ASSERT(GetConnectionStatus() == SOCKET_IS_CONNECTED); // see bug 990984
-
-  nsresult rv = SendPendingData(GetFd(), this);
-  if (NS_FAILED(rv)) {
-    return;
-  }
-
-  if (HasPendingData()) {
-    AddWatchers(WRITE_WATCHER, false);
-  }
-}
-
-void
-UnixSocketConsumerIO::FireSocketError()
-{
-  MOZ_ASSERT(MessageLoopForIO::current() == GetIOLoop());
-
-  // Clean up watchers, statuses, fds
-  Close();
-
-  // Tell the main thread we've errored
-  nsRefPtr<nsRunnable> r =
-    new SocketIOEventRunnable<UnixSocketConsumerIO>(
-      this, SocketIOEventRunnable<UnixSocketConsumerIO>::CONNECT_ERROR);
-
-  NS_DispatchToMainThread(r);
-}
-
-bool
-UnixSocketConsumerIO::SetSocketFlags(int aFd)
-{
-  // Set socket addr to be reused even if kernel is still waiting to close
-  int n = 1;
-  if (setsockopt(aFd, SOL_SOCKET, SO_REUSEADDR, &n, sizeof(n)) < 0) {
-    return false;
-  }
-
-  // Set close-on-exec bit.
-  int flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFD));
-  if (-1 == flags) {
-    return false;
-  }
-  flags |= FD_CLOEXEC;
-  if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFD, flags))) {
-    return false;
-  }
-
-  // Set non-blocking status flag.
-  flags = TEMP_FAILURE_RETRY(fcntl(aFd, F_GETFL));
-  if (-1 == flags) {
-    return false;
-  }
-  flags |= O_NONBLOCK;
-  if (-1 == TEMP_FAILURE_RETRY(fcntl(aFd, F_SETFL, flags))) {
-    return false;
-  }
-
-  return true;
-}
-
-//
-// Socket tasks
-//
-
-class ListenTask final : public SocketIOTask<UnixSocketConsumerIO>
-{
-public:
-  ListenTask(UnixSocketConsumerIO* aIO)
-  : SocketIOTask<UnixSocketConsumerIO>(aIO)
-  { }
-
-  void Run() override
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-
-    if (!IsCanceled()) {
-      GetIO()->Listen();
-    }
-  }
-};
-
-class ConnectTask final : public SocketIOTask<UnixSocketConsumerIO>
-{
-public:
-  ConnectTask(UnixSocketConsumerIO* aIO)
-  : SocketIOTask<UnixSocketConsumerIO>(aIO)
-  { }
-
-  void Run() override
-  {
-    MOZ_ASSERT(!NS_IsMainThread());
-    MOZ_ASSERT(!IsCanceled());
-
-    GetIO()->Connect();
-  }
-};
-
-class DelayedConnectTask final : public SocketIOTask<UnixSocketConsumerIO>
-{
-public:
-  DelayedConnectTask(UnixSocketConsumerIO* aIO)
-  : SocketIOTask<UnixSocketConsumerIO>(aIO)
-  { }
-
-  void Run() override
-  {
-    MOZ_ASSERT(NS_IsMainThread());
-
-    if (IsCanceled()) {
-      return;
-    }
-
-    UnixSocketConsumerIO* io = GetIO();
-    if (io->IsShutdownOnMainThread()) {
-      return;
-    }
-
-    io->ClearDelayedConnectTask();
-    XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new ConnectTask(io));
-  }
-};
-
-//
-// UnixSocketConsumer
-//
-
-UnixSocketConsumer::UnixSocketConsumer()
-: mIO(nullptr)
-{ }
-
-UnixSocketConsumer::~UnixSocketConsumer()
-{
-  MOZ_ASSERT(!mIO);
-}
-
-bool
-UnixSocketConsumer::SendSocketData(UnixSocketRawData* aData)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mIO) {
-    return false;
-  }
-
-  MOZ_ASSERT(!mIO->IsShutdownOnMainThread());
-  XRE_GetIOMessageLoop()->PostTask(
-    FROM_HERE,
-    new SocketIOSendTask<UnixSocketConsumerIO, UnixSocketRawData>(mIO, aData));
-
-  return true;
-}
-
-bool
-UnixSocketConsumer::SendSocketData(const nsACString& aStr)
-{
-  if (aStr.Length() > MAX_READ_SIZE) {
-    return false;
-  }
-
-  nsAutoPtr<UnixSocketRawData> data(
-    new UnixSocketRawData(aStr.BeginReading(), aStr.Length()));
-
-  if (!SendSocketData(data)) {
-    return false;
-  }
-
-  unused << data.forget();
-
-  return true;
-}
-
-void
-UnixSocketConsumer::CloseSocket()
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!mIO) {
-    return;
-  }
-
-  mIO->CancelDelayedConnectTask();
-
-  // From this point on, we consider mIO as being deleted.
-  // We sever the relationship here so any future calls to listen or connect
-  // will create a new implementation.
-  mIO->ShutdownOnMainThread();
-
-  XRE_GetIOMessageLoop()->PostTask(
-    FROM_HERE, new SocketIOShutdownTask<UnixSocketConsumerIO>(mIO));
-
-  mIO = nullptr;
-
-  NotifyDisconnect();
-}
-
-void
-UnixSocketConsumer::GetSocketAddr(nsAString& aAddrStr)
-{
-  aAddrStr.Truncate();
-  if (!mIO || GetConnectionStatus() != SOCKET_CONNECTED) {
-    NS_WARNING("No socket currently open!");
-    return;
-  }
-  mIO->GetSocketAddr(aAddrStr);
-}
-
-bool
-UnixSocketConsumer::ConnectSocket(UnixSocketConnector* aConnector,
-                                  const char* aAddress,
-                                  int aDelayMs)
-{
-  MOZ_ASSERT(aConnector);
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsAutoPtr<UnixSocketConnector> connector(aConnector);
-
-  if (mIO) {
-    NS_WARNING("Socket already connecting/connected!");
-    return false;
-  }
-
-  nsCString addr(aAddress);
-  MessageLoop* ioLoop = XRE_GetIOMessageLoop();
-  mIO = new UnixSocketConsumerIO(ioLoop, this, connector.forget(), addr);
-  SetConnectionStatus(SOCKET_CONNECTING);
-  if (aDelayMs > 0) {
-    DelayedConnectTask* connectTask = new DelayedConnectTask(mIO);
-    mIO->SetDelayedConnectTask(connectTask);
-    MessageLoop::current()->PostDelayedTask(FROM_HERE, connectTask, aDelayMs);
-  } else {
-    ioLoop->PostTask(FROM_HERE, new ConnectTask(mIO));
-  }
-  return true;
-}
-
-bool
-UnixSocketConsumer::ListenSocket(UnixSocketConnector* aConnector)
-{
-  MOZ_ASSERT(aConnector);
-  MOZ_ASSERT(NS_IsMainThread());
-
-  nsAutoPtr<UnixSocketConnector> connector(aConnector);
-
-  if (mIO) {
-    NS_WARNING("Socket already connecting/connected!");
-    return false;
-  }
-
-  mIO = new UnixSocketConsumerIO(
-    XRE_GetIOMessageLoop(), this, connector.forget(), EmptyCString());
-  SetConnectionStatus(SOCKET_LISTENING);
-  XRE_GetIOMessageLoop()->PostTask(FROM_HERE, new ListenTask(mIO));
-  return true;
-}
-
-} // namespace ipc
-} // namespace mozilla
deleted file mode 100644
--- a/ipc/unixsocket/UnixSocket.h
+++ /dev/null
@@ -1,95 +0,0 @@
-/* -*- Mode: c++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 40 -*- */
-/* vim: set ts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef mozilla_ipc_unixsocket_h
-#define mozilla_ipc_unixsocket_h
-
-#include <stdlib.h>
-#include "nsAutoPtr.h"
-#include "nsString.h"
-#include "nsThreadUtils.h"
-#include "mozilla/ipc/SocketBase.h"
-#include "mozilla/ipc/UnixSocketWatcher.h"
-#include "mozilla/RefPtr.h"
-#include "UnixSocketConnector.h"
-
-namespace mozilla {
-namespace ipc {
-
-class UnixSocketConsumerIO;
-
-class UnixSocketConsumer : public SocketConsumerBase
-{
-protected:
-  virtual ~UnixSocketConsumer();
-
-public:
-  UnixSocketConsumer();
-
-  /**
-   * Queue data to be sent to the socket on the IO thread. Can only be called on
-   * originating thread.
-   *
-   * @param aMessage Data to be sent to socket
-   *
-   * @return true if data is queued, false otherwise (i.e. not connected)
-   */
-  bool SendSocketData(UnixSocketRawData* aMessage);
-
-  /**
-   * Convenience function for sending strings to the socket (common in bluetooth
-   * profile usage). Converts to a UnixSocketRawData struct. Can only be called
-   * on originating thread.
-   *
-   * @param aMessage String to be sent to socket
-   *
-   * @return true if data is queued, false otherwise (i.e. not connected)
-   */
-  bool SendSocketData(const nsACString& aMessage);
-
-  /**
-   * Starts a task on the socket that will try to connect to a socket in a
-   * non-blocking manner.
-   *
-   * @param aConnector Connector object for socket type specific functions
-   * @param aAddress Address to connect to.
-   * @param aDelayMs Time delay in milli-seconds.
-   *
-   * @return true on connect task started, false otherwise.
-   */
-  bool ConnectSocket(UnixSocketConnector* aConnector,
-                     const char* aAddress,
-                     int aDelayMs = 0);
-
-  /**
-   * Starts a task on the socket that will try to accept a new connection in a
-   * non-blocking manner.
-   *
-   * @param aConnector Connector object for socket type specific functions
-   *
-   * @return true on listen started, false otherwise
-   */
-  bool ListenSocket(UnixSocketConnector* aConnector);
-
-  /**
-   * Queues the internal representation of socket for deletion. Can be called
-   * from main thread.
-   */
-  void CloseSocket();
-
-  /**
-   * Get the current sockaddr for the socket
-   */
-  void GetSocketAddr(nsAString& aAddrStr);
-
-private:
-  UnixSocketConsumerIO* mIO;
-};
-
-} // namespace ipc
-} // namepsace mozilla
-
-#endif // mozilla_ipc_unixsocket_h
--- a/ipc/unixsocket/moz.build
+++ b/ipc/unixsocket/moz.build
@@ -4,26 +4,24 @@
 # 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/.
 
 EXPORTS.mozilla.ipc += [
     'ConnectionOrientedSocket.h',
     'ListenSocket.h',
     'SocketBase.h',
     'StreamSocket.h',
-    'UnixSocket.h',
     'UnixSocketConnector.h'
 ]
 
 SOURCES += [
     'ConnectionOrientedSocket.cpp',
     'ListenSocket.cpp',
     'SocketBase.cpp',
     'StreamSocket.cpp',
-    'UnixSocket.cpp',
     'UnixSocketConnector.cpp'
 ]
 
 FAIL_ON_WARNINGS = True
 
 include('/ipc/chromium/chromium-config.mozbuild')
 
 FINAL_LIBRARY = 'xul'
--- a/layout/base/SelectionCarets.cpp
+++ b/layout/base/SelectionCarets.cpp
@@ -533,18 +533,18 @@ SelectionCarets::UpdateSelectionCarets()
   nsRect firstRectInRootFrame = firstRectInStartFrame;
   nsRect lastRectInRootFrame = lastRectInEndFrame;
   nsLayoutUtils::TransformRect(startFrame, rootFrame, firstRectInRootFrame);
   nsLayoutUtils::TransformRect(endFrame, rootFrame, lastRectInRootFrame);
 
   SetStartFrameVisibility(startFrameVisible);
   SetEndFrameVisibility(endFrameVisible);
 
-  SetStartFramePos(firstRectInRootFrame.BottomLeft());
-  SetEndFramePos(lastRectInRootFrame.BottomRight());
+  SetStartFramePos(firstRectInRootFrame);
+  SetEndFramePos(lastRectInRootFrame);
   SetVisibility(true);
 
   // Use half of the first(last) rect as the dragup(dragdown) boundary
   mDragUpYBoundary =
     (firstRectInRootFrame.BottomLeft().y + firstRectInRootFrame.TopLeft().y) / 2;
   mDragDownYBoundary =
     (lastRectInRootFrame.BottomRight().y + lastRectInRootFrame.TopRight().y) / 2;
 
@@ -887,47 +887,51 @@ SelectionCarets::SetSelectionDirection(n
 {
   nsRefPtr<dom::Selection> selection = GetSelection();
   if (selection) {
     selection->AdjustAnchorFocusForMultiRange(aDir);
   }
 }
 
 static void
-SetFramePos(dom::Element* aElement, const nsPoint& aPosition)
+SetFramePos(dom::Element* aElement, const nsRect& aCaretRect)
 {
   if (!aElement) {
     return;
   }
 
   nsAutoString styleStr;
   styleStr.AppendLiteral("left: ");
-  styleStr.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(aPosition.x));
+  styleStr.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(aCaretRect.Center().x));
   styleStr.AppendLiteral("px; top: ");
-  styleStr.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(aPosition.y));
+  styleStr.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(aCaretRect.y));
+  styleStr.AppendLiteral("px; padding-top: ");
+  styleStr.AppendFloat(nsPresContext::AppUnitsToFloatCSSPixels(aCaretRect.height));
   styleStr.AppendLiteral("px;");
 
   SELECTIONCARETS_LOG_STATIC("Set style: %s",
                              NS_ConvertUTF16toUTF8(styleStr).get());
 
   aElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style, styleStr, true);
 }
 
 void
-SelectionCarets::SetStartFramePos(const nsPoint& aPosition)
+SelectionCarets::SetStartFramePos(const nsRect& aCaretRect)
 {
-  SELECTIONCARETS_LOG("x=%d, y=%d", aPosition.x, aPosition.y);
-  SetFramePos(mPresShell->GetSelectionCaretsStartElement(), aPosition);
+  SELECTIONCARETS_LOG("x=%d, y=%d, w=%d, h=%d",
+    aCaretRect.x, aCaretRect.y, aCaretRect.width, aCaretRect.height);
+  SetFramePos(mPresShell->GetSelectionCaretsStartElement(), aCaretRect);
 }
 
 void
-SelectionCarets::SetEndFramePos(const nsPoint& aPosition)
+SelectionCarets::SetEndFramePos(const nsRect& aCaretRect)
 {
-  SELECTIONCARETS_LOG("x=%d, y=%d", aPosition.y, aPosition.y);
-  SetFramePos(mPresShell->GetSelectionCaretsEndElement(), aPosition);
+  SELECTIONCARETS_LOG("x=%d, y=%d, w=%d, h=%d",
+    aCaretRect.x, aCaretRect.y, aCaretRect.width, aCaretRect.height);
+  SetFramePos(mPresShell->GetSelectionCaretsEndElement(), aCaretRect);
 }
 
 bool
 SelectionCarets::IsOnStartFrameInner(const nsPoint& aPosition)
 {
   return mVisible &&
     nsLayoutUtils::ContainsPoint(GetStartFrameRectInner(), aPosition,
                                  SelectionCaretsInflateSize());
--- a/layout/base/SelectionCarets.h
+++ b/layout/base/SelectionCarets.h
@@ -142,26 +142,26 @@ private:
    * Simulate drag state when we change the selection range.
    * Hence, the selection change event will fire normally.
    */
   void SetSelectionDragState(bool aState);
 
   void SetSelectionDirection(nsDirection aDir);
 
   /**
-   * Move start frame of selection caret to given position.
+   * Move start frame of selection caret based on current caret pos.
    * In app units.
    */
-  void SetStartFramePos(const nsPoint& aPosition);
+  void SetStartFramePos(const nsRect& aCaretRect);
 
   /**
-   * Move end frame of selection caret to given position.
+   * Move end frame of selection caret based on current caret pos.
    * In app units.
    */
-  void SetEndFramePos(const nsPoint& aPosition);
+  void SetEndFramePos(const nsRect& aCaretRect);
 
   /**
    * Check if aPosition is on the start or end frame of the
    * selection caret's inner div element.
    *
    * @param aPosition should be relative to document's root frame
    * in app units
    */
--- a/layout/base/TouchCaret.cpp
+++ b/layout/base/TouchCaret.cpp
@@ -244,38 +244,41 @@ TouchCaret::GetCaretYCenterPosition()
   nsIFrame* canvasFrame = GetCanvasFrame();
 
   nsLayoutUtils::TransformRect(focusFrame, canvasFrame, caretRect);
 
   return (caretRect.y + caretRect.height / 2);
 }
 
 void
-TouchCaret::SetTouchFramePos(const nsPoint& aOrigin)
+TouchCaret::SetTouchFramePos(const nsRect& aCaretRect)
 {
   nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShell);
   if (!presShell) {
     return;
   }
 
   mozilla::dom::Element* touchCaretElement = presShell->GetTouchCaretElement();
   if (!touchCaretElement) {
     return;
   }
 
   // Convert aOrigin to CSS pixels.
   nsRefPtr<nsPresContext> presContext = presShell->GetPresContext();
-  int32_t x = presContext->AppUnitsToIntCSSPixels(aOrigin.x);
-  int32_t y = presContext->AppUnitsToIntCSSPixels(aOrigin.y);
+  int32_t x = presContext->AppUnitsToIntCSSPixels(aCaretRect.Center().x);
+  int32_t y = presContext->AppUnitsToIntCSSPixels(aCaretRect.y);
+  int32_t padding = presContext->AppUnitsToIntCSSPixels(aCaretRect.height);
 
   nsAutoString styleStr;
   styleStr.AppendLiteral("left: ");
   styleStr.AppendInt(x);
   styleStr.AppendLiteral("px; top: ");
   styleStr.AppendInt(y);
+  styleStr.AppendLiteral("px; padding-top: ");
+  styleStr.AppendInt(padding);
   styleStr.AppendLiteral("px;");
 
   TOUCHCARET_LOG("Set style: %s", NS_ConvertUTF16toUTF8(styleStr).get());
 
   touchCaretElement->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
                              styleStr, true);
 }
 
@@ -478,64 +481,59 @@ TouchCaret::IsDisplayable()
   return true;
 }
 
 void
 TouchCaret::UpdatePosition()
 {
   MOZ_ASSERT(mVisible);
 
-  nsPoint pos = GetTouchCaretPosition();
-  pos = ClampPositionToScrollFrame(pos);
-  SetTouchFramePos(pos);
+  nsRect rect = GetTouchCaretRect();
+  rect = ClampRectToScrollFrame(rect);
+  SetTouchFramePos(rect);
 }
 
-nsPoint
-TouchCaret::GetTouchCaretPosition()
+nsRect
+TouchCaret::GetTouchCaretRect()
 {
   nsRect focusRect;
   nsIFrame* focusFrame = GetCaretFocusFrame(&focusRect);
   nsIFrame* rootFrame = GetRootFrame();
-
-  // Position of the touch caret relative to focusFrame.
-  nsPoint pos = nsPoint(focusRect.x + (focusRect.width / 2),
-                        focusRect.y + focusRect.height);
+  // Transform the position to make it relative to root frame.
+  nsLayoutUtils::TransformRect(focusFrame, rootFrame, focusRect);
 
-  // Transform the position to make it relative to root frame.
-  nsLayoutUtils::TransformPoint(focusFrame, rootFrame, pos);
-
-  return pos;
+  return focusRect;
 }
 
-nsPoint
-TouchCaret::ClampPositionToScrollFrame(const nsPoint& aPosition)
+nsRect
+TouchCaret::ClampRectToScrollFrame(const nsRect& aRect)
 {
-  nsPoint pos = aPosition;
+  nsRect rect = aRect;
   nsIFrame* focusFrame = GetCaretFocusFrame();
   nsIFrame* rootFrame = GetRootFrame();
 
   // Clamp the touch caret position to the scrollframe boundary.
   nsIFrame* closestScrollFrame =
     nsLayoutUtils::GetClosestFrameOfType(focusFrame, nsGkAtoms::scrollFrame);
 
   while (closestScrollFrame) {
     nsIScrollableFrame* sf = do_QueryFrame(closestScrollFrame);
     nsRect visualRect = sf->GetScrollPortRect();
 
     // Clamp the touch caret in the scroll port.
     nsLayoutUtils::TransformRect(closestScrollFrame, rootFrame, visualRect);
-    pos = visualRect.ClampPoint(pos);
+    rect = rect.Intersect(visualRect);
 
     // Get next ancestor scroll frame.
     closestScrollFrame =
       nsLayoutUtils::GetClosestFrameOfType(closestScrollFrame->GetParent(),
                                            nsGkAtoms::scrollFrame);
   }
 
-  return pos;
+  return rect;
 }
 
 /* static */void
 TouchCaret::DisableTouchCaretCallback(nsITimer* aTimer, void* aTouchCaret)
 {
   nsRefPtr<TouchCaret> self = static_cast<TouchCaret*>(aTouchCaret);
   NS_PRECONDITION(aTimer == self->mTouchCaretExpirationTimer,
                   "Unexpected timer");
--- a/layout/base/TouchCaret.h
+++ b/layout/base/TouchCaret.h
@@ -112,32 +112,32 @@ private:
 
   /**
    * Retrieve the center y position of the caret.
    * The returned point is relative to the canvas frame.
    */
   nscoord GetCaretYCenterPosition();
 
   /**
-   * Retrieve the position of the touch caret.
-   * The returned point is relative to the canvas frame.
+   * Retrieve the rect of the touch caret.
+   * The returned rect is relative to the canvas frame.
    */
-  nsPoint GetTouchCaretPosition();
+  nsRect GetTouchCaretRect();
 
   /**
    * Clamp the position of the touch caret to the scroll frame boundary.
-   * The returned point is relative to the canvas frame.
+   * The returned rect is relative to the canvas frame.
    */
-  nsPoint ClampPositionToScrollFrame(const nsPoint& aPosition);
+  nsRect ClampRectToScrollFrame(const nsRect& aRect);
 
   /**
    * Set the position of the touch caret.
    * Touch caret is an absolute positioned div.
    */
-  void SetTouchFramePos(const nsPoint& aOrigin);
+  void SetTouchFramePos(const nsRect& aRect);
 
   void LaunchExpirationTimer();
   void CancelExpirationTimer();
   static void DisableTouchCaretCallback(nsITimer* aTimer, void* aPresShell);
 
   /**
    * Move the caret to movePoint which is relative to the canvas frame.
    * Caret will be scrolled into view.
--- a/layout/base/nsPresShell.cpp
+++ b/layout/base/nsPresShell.cpp
@@ -7372,19 +7372,20 @@ PresShell::HandleEvent(nsIFrame* aFrame,
           EnumerateSubDocuments(FlushThrottledStyles, nullptr);
       }
 
       if (!weakFrame.IsAlive()) {
         frame = GetNearestFrameContainingPresShell(this);
       }
     }
 
-    NS_WARN_IF_FALSE(frame, "Nothing to handle this event!");
-    if (!frame)
+    if (!frame) {
+      NS_WARNING("Nothing to handle this event!");
       return NS_OK;
+    }
 
     nsPresContext* framePresContext = frame->PresContext();
     nsPresContext* rootPresContext = framePresContext->GetRootPresContext();
     NS_ASSERTION(rootPresContext == mPresContext->GetRootPresContext(),
                  "How did we end up outside the connected prescontext/viewmanager hierarchy?");
     // If we aren't starting our event dispatch from the root frame of the root prescontext,
     // then someone must be capturing the mouse. In that case we don't want to search the popup
     // list.
@@ -7575,21 +7576,28 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
       if (capturingFrame) {
         frame = capturingFrame;
       }
     }
 
     if (aEvent->mClass == ePointerEventClass) {
       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
+        // Try to keep frame for following check, because
+        // frame can be damaged during CheckPointerCaptureState.
+        nsWeakFrame frameKeeper(frame);
         // Before any pointer events, we should check state of pointer capture,
         // Thus got/lostpointercapture events emulate asynchronous behavior.
         // Handlers of got/lostpointercapture events can change capturing state,
         // That's why we should re-check pointer capture state until stable state.
         while(CheckPointerCaptureState(pointerEvent->pointerId));
+        // Prevent application crashes, in case damaged frame.
+        if (!frameKeeper.IsAlive()) {
+          frame = nullptr;
+        }
       }
     }
 
     if (aEvent->mClass == ePointerEventClass &&
         aEvent->message != NS_POINTER_DOWN) {
       if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
         uint32_t pointerId = pointerEvent->pointerId;
         nsIContent* pointerCapturingContent = GetPointerCapturingContent(pointerId);
@@ -7621,17 +7629,21 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
         mNoDelayedMouseEvents = true;
       } else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) {
         DelayedEvent* event = new DelayedMouseEvent(aEvent->AsMouseEvent());
         if (!mDelayedEvents.AppendElement(event)) {
           delete event;
         }
       }
-
+      return NS_OK;
+    }
+
+    if (!frame) {
+      NS_WARNING("Nothing to handle this event!");
       return NS_OK;
     }
 
     PresShell* shell =
         static_cast<PresShell*>(frame->PresContext()->PresShell());
     switch (aEvent->message) {
       case NS_TOUCH_MOVE:
       case NS_TOUCH_CANCEL:
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/bug1153130_inner.html
@@ -0,0 +1,72 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1153130
+-->
+<head>
+  <meta charset="utf-8">
+  <title>Test for Bug 1153130</title>
+  <meta name="author" content="Maksim Lebedev" />
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+  <style>
+    #target { background: yellow; padding: 10px; }
+  </style>
+  <script type="application/javascript">
+    var target = undefined;
+    var test_down = false;
+    var test_capture = false;
+    var test_move = false;
+    var test_success = false;
+
+    function TargetHandler(event) {
+      logger("Target receive event: " + event.type);
+      if(event.type == "pointerdown") {
+        test_down = true;
+        target.setPointerCapture(event.pointerId);
+      } else if(event.type == "gotpointercapture") {
+        test_capture = true;
+      } else if(event.type == "pointermove" && test_capture) {
+        test_move = true;
+      }
+    }
+    function logger(message) {
+      console.log(message);
+      var log = document.getElementById('target');
+      log.innerHTML = message + "<br>" + log.innerHTML;
+    }
+    function prepareTest() {
+      parent.turnOnPointerEvents(executeTest);
+    }
+    function executeTest() {
+      logger("executeTest");
+      target = document.getElementById("target");
+      target.addEventListener("pointerdown",        TargetHandler,  false);
+      target.addEventListener("gotpointercapture",  TargetHandler,  false);
+      target.addEventListener("pointermove",        TargetHandler,  false);
+      var rect = target.getBoundingClientRect();
+      synthesizePointer(target, rect.width/5, rect.height/5,  {type: "pointermove"});
+      synthesizePointer(target, rect.width/5, rect.height/5,  {type: "pointerdown"});
+      synthesizePointer(target, rect.width/4, rect.height/4,  {type: "pointermove"});
+      synthesizePointer(target, rect.width/3, rect.height/3,  {type: "pointermove"});
+      synthesizePointer(target, rect.width/3, rect.height/3,  {type: "pointerup"});
+      synthesizePointer(target, rect.width/2, rect.height/2,  {type: "pointermove"});
+      test_success = true;
+      finishTest();
+    }
+    function finishTest() {
+      parent.is(test_down,    true, "pointerdown event should be received by target");
+      parent.is(test_capture, true, "gotpointercapture event should be received by target");
+      parent.is(test_move,    true, "pointermove event should be received by target while pointer capture is active");
+      parent.is(test_success, true, "Firefox should be live!");
+      logger("finishTest");
+      parent.finishTest();
+    }
+  </script>
+</head>
+<body onload="prepareTest()">
+  <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1153130">Mozilla Bug 1153130</a>
+  <div id="target">div id=target</div>
+</body>
+</html>
--- a/layout/base/tests/chrome/chrome.ini
+++ b/layout/base/tests/chrome/chrome.ini
@@ -49,17 +49,17 @@ skip-if = buildapp == 'b2g'
 skip-if = buildapp == 'mulet' || buildapp == 'b2g'
 [test_default_background.xul]
 skip-if = buildapp == 'b2g'
 [test_dialog_with_positioning.html]
 skip-if = buildapp == 'b2g'
 [test_fixed_bg_scrolling_repaints.html]
 skip-if = buildapp == 'b2g'
 [test_leaf_layers_partition_browser_window.xul]
-skip-if = (!debug) || (toolkit == "cocoa") || (buildapp == 'b2g') # Disabled on Mac because of Bug 748219
+skip-if = (!debug) || (toolkit == "cocoa") || (buildapp == 'b2g') || (toolkit == 'windows') # Disabled on Mac because of Bug 748219
 [test_no_clip_iframe.xul]
 skip-if = buildapp == 'b2g'
 [test_passpointerevents.html]
 skip-if = buildapp == 'b2g'
 [test_passpointerevents_dynamic.html]
 skip-if = buildapp == 'b2g'
 [test_prerendered_transforms.html]
 skip-if = buildapp == 'b2g'
--- a/layout/base/tests/mochitest.ini
+++ b/layout/base/tests/mochitest.ini
@@ -256,8 +256,10 @@ support-files = bug1078327_inner.html
 [test_bug1080361.html]
 support-files = bug1080361_inner.html
 [test_frame_reconstruction_for_pseudo_elements.html]
 [test_touchcaret_visibility.html]
 [test_bug1093686.html]
 support-files = bug1093686_inner.html
 [test_bug1120705.html]
 skip-if = buildapp == 'android' || buildapp == 'b2g' || buildapp == 'b2g-debug' || os == 'mac' # android and b2g do not have clickable scrollbars, mac does not have scrollbar down and up buttons
+[test_bug1153130.html]
+support-files = bug1153130_inner.html
new file mode 100644
--- /dev/null
+++ b/layout/base/tests/test_bug1153130.html
@@ -0,0 +1,37 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1153130
+-->
+  <head>
+    <meta charset="utf-8">
+    <title>Test for Bug 1153130</title>
+    <meta name="author" content="Maksim Lebedev" />
+    <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+    <script type="text/javascript">
+      var iframe = undefined;
+      function prepareTest() {
+        SimpleTest.waitForExplicitFinish();
+        iframe = document.getElementById("testFrame");
+        turnOnPointerEvents(startTest);
+      }
+      function turnOnPointerEvents(callback) {
+        SpecialPowers.pushPrefEnv({
+          "set": [
+            ["dom.w3c_pointer_events.enabled", true]
+          ]
+        }, callback);
+      }
+      function startTest() {
+        iframe.src = "bug1153130_inner.html";
+      }
+      function finishTest() {
+        SimpleTest.finish();
+      }
+    </script>
+  </head>
+  <body onload="prepareTest()">
+    <iframe id="testFrame" height="700" width="700"></iframe>
+  </body>
+</html>
--- a/layout/forms/crashtests/crashtests.list
+++ b/layout/forms/crashtests/crashtests.list
@@ -42,17 +42,17 @@ load 407066.html
 load 451316.html
 load 455451-1.html
 load 457537-1.html
 load 457537-2.html
 load 478219-1.xhtml
 load 513113-1.html
 load 538062-1.xhtml
 load 570624-1.html
-skip-if(B2G) load 498698-1.html # bug 833371
+skip-if(B2G) skip-if(asyncPanZoom&&winWidget) load 498698-1.html # bug 833371
 asserts(1) load 578604-1.html # bug 584564
 asserts(4-7) load 590302-1.xhtml # bug 584564
 load 626014.xhtml
 load 639733.xhtml
 asserts(0-1) load 669767.html
 load 682684.xhtml
 load 865602.html
 load 944198.html
--- a/layout/generic/crashtests/crashtests.list
+++ b/layout/generic/crashtests/crashtests.list
@@ -358,17 +358,17 @@ load 494283-2.html
 load 494300-1.xul
 load 494332-1.html
 load 495875-1.html
 load 495875-2.html
 load 496742.html
 load 499138.html
 load 499862-1.html
 load 499857-1.html
-asserts-if(winWidget,0-3) load 499885-1.xhtml
+asserts-if(winWidget,0-3) skip-if(asyncPanZoom&&winWidget) load 499885-1.xhtml
 load 501535-1.html
 load 503961-1.xhtml
 load 503961-2.html
 load 508168-1.html
 load 508816-1.xul
 load 508908-1.html
 load 505912-1.html
 load 509749-1.html
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -505,19 +505,23 @@ nsCanvasFrame::BuildDisplayList(nsDispla
     if (needBlendContainer) {
       aLists.BorderBackground()->AppendNewToTop(
         new (aBuilder) nsDisplayBlendContainer(aBuilder, this, aLists.BorderBackground()));
     }
   }
 
   nsIFrame* kid;
   for (kid = GetFirstPrincipalChild(); kid; kid = kid->GetNextSibling()) {
-    // Skip touch caret frame if we do not build caret.
-    if (!aBuilder->IsBuildingCaret() && kid->GetContent() == mTouchCaretElement) {
-      continue;
+    // Skip touch/selection caret frame if we do not build caret.
+    if (!aBuilder->IsBuildingCaret()) {
+      if(kid->GetContent() == mTouchCaretElement ||
+         kid->GetContent() == mSelectionCaretsStartElement||
+         kid->GetContent() == mSelectionCaretsEndElement) {
+        continue;
+      }
     }
 
     // Put our child into its own pseudo-stack.
     BuildDisplayListForChild(aBuilder, kid, aDirtyRect, aLists);
   }
 
 #ifdef DEBUG_CANVAS_FOCUS
   nsCOMPtr<nsIContent> focusContent;
--- a/layout/reftests/abs-pos/reftest.list
+++ b/layout/reftests/abs-pos/reftest.list
@@ -44,19 +44,19 @@ skip-if((B2G&&browserIsRemote)||Mulet) !
 == table-footer-group-3.html table-internal-3-ref.html
 == table-footer-group-4.html table-internal-4-ref.html
 == table-footer-group-5.html table-internal-5-ref.html
 == table-footer-group-6.html table-internal-6-ref.html
 == table-footer-group-7.html table-internal-7-ref.html
 == continuation-positioned-inline-1.html continuation-positioned-inline-ref.html
 == continuation-positioned-inline-2.html continuation-positioned-inline-ref.html
 == scrollframe-1.html scrollframe-1-ref.html
-skip-if(B2G||Mulet) fuzzy-if(Android,9,185) == scrollframe-2.html scrollframe-2-ref.html #bug 756530 # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(B2G||Mulet) fuzzy-if(Android,9,185) skip-if(asyncPanZoom&&winWidget) == scrollframe-2.html scrollframe-2-ref.html #bug 756530 # Initial mulet triage: parity with B2G/B2G Desktop
 fuzzy-if(gtk2Widget,1,8) == select-1.html select-1-ref.html
 fuzzy-if(gtk2Widget,1,8) == select-1-dynamic.html select-1-ref.html
 == select-2.html select-2-ref.html
-fuzzy-if(gtk2Widget,1,19) fuzzy-if(Android||B2G,17,726) == select-3.html select-3-ref.html
+fuzzy-if(gtk2Widget,1,19) fuzzy-if(Android||B2G,17,726) skip-if(asyncPanZoom&&winWidget) == select-3.html select-3-ref.html
 == multi-column-1.html multi-column-1-ref.html
 == button-1.html button-1-ref.html
 == button-2.html button-2-ref.html
 == relative-row-animation-1.html relative-row-animation-1-ref.html
 fuzzy-if(Android&&AndroidVersion>=15,12,50) == fixed-pos-auto-offset-1a.html fixed-pos-auto-offset-1-ref.html
 fuzzy-if(Android&&AndroidVersion>=15,12,50) == fixed-pos-auto-offset-1b.html fixed-pos-auto-offset-1-ref.html
--- a/layout/reftests/border-image/reftest.list
+++ b/layout/reftests/border-image/reftest.list
@@ -31,17 +31,17 @@ fails-if(Android||B2G) == center-scaling
 == border-image-width-1b.html border-image-width-1-ref.html
 == border-image-width-1c.html border-image-width-1-ref.html
 == border-image-width-large.html border-image-width-large-ref.html
 == border-image-outset-1a.html border-image-outset-1-ref.html
 == border-image-outset-1b.html border-image-outset-1-ref.html
 == border-image-outset-1c.html border-image-outset-1-ref.html
 == border-image-nofill-1.html border-image-nofill-1-ref.html
 == border-image-outset-resize-1.html border-image-outset-resize-1-ref.html
-== border-image-outset-move-1.html border-image-outset-move-1-ref.html
+skip-if(asyncPanZoom&&winWidget) == border-image-outset-move-1.html border-image-outset-move-1-ref.html
 == border-image-style-none.html border-image-style-none-ref.html
 == border-image-style-none-length.html border-image-style-none-length-ref.html
 == border-image-style-none-auto.html border-image-style-none-auto-ref.html
 
 # border images with gradients
 == border-image-linear-gradient.html border-image-linear-gradient-ref.html
 fuzzy(1,98) == border-image-linear-gradient-slice-1.html border-image-linear-gradient-slice-1-ref.html
 fuzzy(1,149) fuzzy-if(OSX,1,10595) == border-image-linear-gradient-slice-2.html border-image-linear-gradient-slice-2-ref.html
--- a/layout/reftests/border-radius/reftest.list
+++ b/layout/reftests/border-radius/reftest.list
@@ -66,17 +66,17 @@ fuzzy-if(true,1,33) fuzzy-if(cocoaWidget
 # Table elements
 == table-collapse-1.html table-collapse-1-ref.html # border-radius is ignored on internal table elements
 # when border-collapse: collapse
 
 fuzzy-if(azureQuartz,1,3) skip-if(B2G||Mulet) == invalidate-1a.html invalidate-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fuzzy-if(azureQuartz,1,3) skip-if(B2G||Mulet) == invalidate-1b.html invalidate-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 
 # test that border-radius is reduced for scrollbars
-skip-if(B2G||Mulet) fails-if(Android) == scrollbar-clamping-1.html scrollbar-clamping-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(B2G||Mulet) fails-if(Android) skip-if(asyncPanZoom&&winWidget) == scrollbar-clamping-1.html scrollbar-clamping-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fails-if(Android) == scrollbar-clamping-2.html scrollbar-clamping-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 
 # Test for bad corner joins.
 fuzzy-if(true,1,1) == corner-joins-1.xhtml corner-joins-1-ref.xhtml
 skip-if(B2G||Mulet) random-if(winWidget) HTTP(..) == corner-joins-2.xhtml corner-joins-2-ref.xhtml # Initial mulet triage: parity with B2G/B2G Desktop
 
 skip-if(B2G||Mulet) fuzzy-if(/^Windows\x20NT\x206\.2/.test(http.oscpu),1,20) fuzzy-if(Android&&browserIsRemote,7,146) fuzzy-if(Android&&!browserIsRemote,166,400) fails-if(Android&&AndroidVersion==15) == scroll-1.html scroll-1-ref.html # see bug 732535 #Bug 959166 # Initial mulet triage: parity with B2G/B2G Desktop
 
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -83,17 +83,17 @@ fuzzy-if(gtk2Widget,6,26200) == 28811-2b
 == 76331-1.html 76331-1-ref.html
 == 81947-1.html 81947-ref.html
 == 82711-1.html 82711-1-ref.html
 == 82711-2.html 82711-2-ref.html
 == 82711-3.html 82711-3-ref.html
 != 82711-1-ref.html 82711-2-ref.html
 != 82711-1-ref.html 82711-3-ref.html
 != 82711-2-ref.html 82711-3-ref.html
-== 84400-1.html 84400-1-ref.html
+skip-if(asyncPanZoom&&winWidget) == 84400-1.html 84400-1-ref.html
 == 84400-2.html 84400-2-ref.html
 == 97777-1.html 97777-1-ref.html
 == 97777-2.html 97777-2-ref.html
 == 98223-1.html 98223-1-ref.html
 == 98223-2.html 98223-2-ref.html
 == 99850-1a.html 99850-1-ref.html
 random == 99850-1b.html 99850-1-ref.html # bug 471629
 == 99850-1c.html 99850-1-ref.html
@@ -1399,30 +1399,30 @@ skip-if(B2G||Mulet) == 502447-1.html 502
 == 502942-1.html 502942-1-ref.html
 == 503364-1a.html 503364-1-ref.html
 == 503364-1b.html 503364-1-ref.html
 # Reftest for bug 503531 marked as failing; should be re-enabled when
 # bug 607548 gets resolved.
 needs-focus fails == 503531-1.html 503531-1-ref.html
 == 504032-1.html 504032-1-ref.html
 == 505743-1.html about:blank
-skip-if(B2G||Mulet) fuzzy-if(Android,5,2800) == 506481-1.html 506481-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(B2G||Mulet) skip-if(asyncPanZoom&&winWidget) fuzzy-if(Android,5,2800) == 506481-1.html 506481-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == 507187-1.html 507187-1-ref.html
 == 507487-1.html 507487-1-ref.html
 == 507487-2.xhtml 507487-2-ref.xhtml
 == 507762-1.html 507762-1-ref.html
 == 507762-2.html 507762-2-ref.html
 == 507762-3.html 507762-1-ref.html
 == 507762-4.html 507762-2-ref.html
 skip-if(B2G||Mulet) random-if(cocoaWidget) == 508816-1.xul 508816-1-ref.xul # Bug 631982 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) == 508816-2.html 508816-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if((B2G&&browserIsRemote)||Mulet) == 508908-1.xul 508908-1-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
 == 508919-1.xhtml 508919-1-ref.xhtml
 == 509155-1.xhtml 509155-1-ref.xhtml
-skip-if(B2G||Mulet) fuzzy-if(Android,5,1656) == 512410.html 512410-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(B2G||Mulet) skip-if(asyncPanZoom&&winWidget) fuzzy-if(Android,5,1656) == 512410.html 512410-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == 512631-1.html 512631-1-ref.html
 == 513153-1a.html 513153-1-ref.html
 == 513153-1b.html 513153-1-ref.html
 == 513153-2a.html 513153-2-ref.html
 == 513153-2b.html 513153-2-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) == 513318-1.xul 513318-1-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fails-if(Android) != 513318-2.xul 513318-2-ref.xul # Initial mulet triage: parity with B2G/B2G Desktop
 == 514917-1.html 514917-1-ref.html
@@ -1486,17 +1486,17 @@ fuzzy-if(Android,17,2) skip-if((B2G&&bro
 == 545049-1.html 545049-1-ref.html
 == 546033-1.html 546033-1-ref.html
 random-if(!haveTestPlugin) == 546071-1.html 546071-1-ref.html
 == 549184-1.html 549184-1-ref.html
 == 550325-1.html 550325-1-ref.html
 == 550325-2.html 550325-1-ref.html
 == 550325-3.html 550325-1-ref.html
 == 550716-1.html 550716-1-ref.html
-skip-if(B2G||Mulet) fuzzy-if(Android&&AndroidVersion>=15,12,300) == 551463-1.html 551463-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(B2G||Mulet) skip-if(asyncPanZoom&&winWidget) fuzzy-if(Android&&AndroidVersion>=15,12,300) == 551463-1.html 551463-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == 551699-1.html 551699-1-ref.html
 == 552334-1.html 552334-1-ref.html
 # Bug 553571 was specific to MS Indic shaping behavior and Win7 font support;
 # the test is not currently relevant under harfbuzz shaping.
 # Keeping it here for the record, and because we may evolve HB's dotted-circle
 # behavior further in the future, which could make this become relevant again.
 # Marked "random" rather than "fails" because it may (spuriously) appear to pass
 # on B2G or Android devices that completely lack any Sinhala font support.
@@ -1570,17 +1570,17 @@ random-if(!winWidget) fails-if(winWidget
 == 577838-1.html 577838-1-ref.html
 == 577838-2.html 577838-2-ref.html
 == 579323-1.html 579323-1-ref.html
 == 579349-1.html 579349-1-ref.html
 == 579655-1.html 579655-1-ref.html
 skip-if(!haveTestPlugin) skip-if(B2G||Mulet) fails-if(Android) == 579808-1.html 579808-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fails-if(Android) random-if(layersGPUAccelerated) == 579985-1.html 579985-1-ref.html # bug 623452 for WinXP; this bug was only for a regression in BasicLayers anyway # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) skip-if(Android) == 580160-1.html 580160-1-ref.html # bug 920927 for Android; issues without the test-plugin # Initial mulet triage: parity with B2G/B2G Desktop
-HTTP(..) == 580863-1.html 580863-1-ref.html
+skip-if(asyncPanZoom&&winWidget) HTTP(..) == 580863-1.html 580863-1-ref.html
 skip-if(B2G||Mulet) fails-if(Android) random-if(layersGPUAccelerated) == 581317-1.html 581317-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == 581579-1.html 581579-1-ref.html
 == 582037-1a.html 582037-1-ref.html
 == 582037-1b.html 582037-1-ref.html
 skip-if(B2G||Mulet) fuzzy-if(Android,3,256) == 582037-2a.html 582037-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fuzzy-if(Android,3,256) == 582037-2b.html 582037-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 asserts(1-2) == 582146-1.html about:blank
 skip-if(B2G||Mulet) == 582476-1.svg 582476-1-ref.svg # Initial mulet triage: parity with B2G/B2G Desktop
@@ -1662,21 +1662,21 @@ fuzzy(1,68) skip-if(B2G||Mulet) fails-if
 skip-if(B2G||Mulet) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,1,61) == 635373-2.html 635373-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) random-if(d2d) fails-if(Android) fuzzy-if(winWidget&&!d2d,1,60) == 635373-3.html 635373-3-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 HTTP(..) == 635639-1.html 635639-1-ref.html
 HTTP(..) == 635639-2.html 635639-2-ref.html
 random == 637597-1.html 637597-1-ref.html # bug 637597 was never really fixed!
 fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-1.html 637852-1-ref.html
 fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-2.html 637852-2-ref.html
 fuzzy-if(Android&&AndroidVersion>=15,8,500) == 637852-3.html 637852-3-ref.html
-skip-if(B2G||Mulet) == 641770-1.html 641770-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(B2G||Mulet) skip-if(asyncPanZoom&&winWidget) == 641770-1.html 641770-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == 641856-1.html 641856-1-ref.html
 == 645491-1.html 645491-1-ref.html
 == 645768-1.html 645768-1-ref.html
-fails-if(layersGPUAccelerated&&cocoaWidget) fails-if(Android&&AndroidVersion<15&&AndroidVersion!=10) == 650228-1.html 650228-1-ref.html # Quartz alpha blending doesn't match GL alpha blending
+fails-if(layersGPUAccelerated&&cocoaWidget) fails-if(Android&&AndroidVersion<15&&AndroidVersion!=10) skip-if(asyncPanZoom&&winWidget) == 650228-1.html 650228-1-ref.html # Quartz alpha blending doesn't match GL alpha blending
 needs-focus == 652301-1a.html 652301-1-ref.html
 needs-focus == 652301-1b.html 652301-1-ref.html
 == 652775-1.html 652775-1-ref.html
 == 653930-1.html 653930-1-ref.html
 HTTP(..) == 654057-1.html 654057-1-ref.html
 fails-if(layersGPUAccelerated&&cocoaWidget) fails-if(Android&&AndroidVersion==15) == 654950-1.html 654950-1-ref.html # Quartz alpha blending doesn't match GL alpha blending
 == 655549-1.html 655549-1-ref.html
 == 655836-1.html 655836-1-ref.html
@@ -1751,17 +1751,17 @@ fuzzy-if(Android,8,608) == 811301-1.html
 == 814677.html 814677-ref.html
 skip-if(B2G||Mulet) == 814952-1.html 814952-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fuzzy-if(Android,4,400) == 815593-1.html 815593-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == 816359-1.html 816359-1-ref.html
 == 816458-1.html 816458-1-ref.html
 == 816948-1.html 816948-1-ref.html
 == 817019-1.html about:blank
 skip-if(B2G||Mulet) == 818276-1.html 818276-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
-fuzzy-if(asyncPanZoom,190,27) == 825999.html 825999-ref.html
+fuzzy-if(asyncPanZoom&&!winWidget,190,27) skip-if(asyncPanZoom&&winWidget) == 825999.html 825999-ref.html
 == 827577-1a.html 827577-1-ref.html
 == 827577-1b.html 827577-1-ref.html
 == 827799-1.html about:blank
 == 829958.html 829958-ref.html
 == 836844-1.html 836844-1-ref.html
 == 841192-1.html 841192-1-ref.html
 == 844178.html 844178-ref.html
 fuzzy-if(OSX,1,364) == 846144-1.html 846144-1-ref.html
@@ -1801,17 +1801,17 @@ fuzzy-if(B2G,1,7) == 942672-1.html 94267
 == 956513-1.svg 956513-1-ref.svg
 == 944291-1.html 944291-1-ref.html
 == 950436-1.html 950436-1-ref.html
 == 957770-1.svg 957770-1-ref.svg
 == 960277-1.html 960277-1-ref.html
 == 961887-1.html 961887-1-ref.html
 == 961887-2.html 961887-2-ref.html
 == 961887-3.html 961887-3-ref.html
-pref(layout.css.overflow-clip-box.enabled,true) fuzzy(50,145) == 966992-1.html 966992-1-ref.html
+pref(layout.css.overflow-clip-box.enabled,true) fuzzy(50,145) skip-if(asyncPanZoom&&winWidget) == 966992-1.html 966992-1-ref.html
 skip-if(Android) == 966510-1.html 966510-1-ref.html # scrollable elements other than the root probably won't work well on android until bug 776030 is fixed
 skip-if(Android) == 966510-2.html 966510-2-ref.html # same as above
 == 978911-1.svg 978911-1-ref.svg
 == 983084-1.html 983084-1-ref.html
 == 983084-2.html 983084-2-ref.html
 == 983084-3.html 983084-1-ref.html
 == 983691-1.html 983691-ref.html
 HTTP(..) == 983985-1.html 983985-1-ref.html
--- a/layout/reftests/css-ui-invalid/select/reftest.list
+++ b/layout/reftests/css-ui-invalid/select/reftest.list
@@ -5,14 +5,14 @@ needs-focus == select-disabled.html sele
 skip-if(B2G||Mulet) needs-focus == select-dyn-disabled.html select-disabled-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 needs-focus == select-dyn-not-disabled.html select-ref.html
 needs-focus == select-required-invalid-1.html select-required-ref.html
 needs-focus == select-required-invalid-2.html select-required-ref.html
 needs-focus == select-required-invalid-changed-1.html select-required-ref.html
 needs-focus == select-required-invalid-changed-2.html select-required-ref.html
 needs-focus == select-required-valid.html select-required-ref.html
 needs-focus == select-required-multiple-invalid.html select-required-multiple-ref.html
-needs-focus == select-required-multiple-invalid-changed.html select-required-multiple-ref.html
+skip-if(asyncPanZoom&&winWidget) needs-focus == select-required-multiple-invalid-changed.html select-required-multiple-ref.html
 needs-focus == select-required-multiple-valid.html select-required-multiple-ref.html
 skip-if(B2G||Mulet) fails-if(Android) needs-focus == select-disabled-fieldset-1.html select-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fails-if(Android) needs-focus == select-disabled-fieldset-2.html select-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 needs-focus == select-fieldset-legend.html select-fieldset-legend-ref.html
 needs-focus == select-novalidate.html select-required-ref.html
--- a/layout/reftests/css-ui-valid/select/reftest.list
+++ b/layout/reftests/css-ui-valid/select/reftest.list
@@ -6,13 +6,13 @@ needs-focus == select-dyn-disabled.html 
 needs-focus == select-dyn-not-disabled.html select-ref.html
 needs-focus == select-required-invalid.html select-required-ref.html
 needs-focus == select-required-valid-1.html select-required-ref.html
 needs-focus == select-required-valid-2.html select-required-ref.html
 needs-focus == select-required-valid-changed-1.html select-required-ref.html
 needs-focus == select-required-valid-changed-2.html select-required-ref.html
 needs-focus == select-required-multiple-invalid.html select-required-multiple-ref.html
 needs-focus == select-required-multiple-valid.html select-required-multiple-ref.html
-fuzzy(64,4) needs-focus == select-required-multiple-valid-changed.html select-required-multiple-ref.html
+fuzzy(64,4) skip-if(asyncPanZoom&&winWidget) needs-focus == select-required-multiple-valid-changed.html select-required-multiple-ref.html
 fails-if(Android||B2G||Mulet) needs-focus == select-disabled-fieldset-1.html select-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 fails-if(Android||B2G||Mulet) needs-focus == select-disabled-fieldset-2.html select-fieldset-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 needs-focus == select-fieldset-legend.html select-fieldset-legend-ref.html
 needs-focus == select-novalidate.html select-required-ref.html
--- a/layout/reftests/font-inflation/reftest.list
+++ b/layout/reftests/font-inflation/reftest.list
@@ -32,17 +32,17 @@ test-pref(font.size.inflation.emPerLine,
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == input-text-2-noheight.html input-text-2-noheight-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == input-text-3-height.html input-text-3-height-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == input-text-3-noheight.html input-text-3-noheight-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == textarea-1.html textarea-1-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == textarea-2.html textarea-2-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == textarea-3.html textarea-3-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == css-transform-1.html css-transform-1-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == css-transform-2.html css-transform-2-ref.html
-skip-if(B2G||Mulet) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == container-with-clamping.html container-with-clamping-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(B2G||Mulet) skip-if(asyncPanZoom&&winWidget) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) == container-with-clamping.html container-with-clamping-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) load video-1.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-min-1.html intrinsic-min-1-ref.html
 skip-if(B2G||Mulet) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-max-1.html intrinsic-max-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-fit-1a.html intrinsic-fit-1a-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-fit-1b.html intrinsic-fit-1b-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-fit-1c.html intrinsic-fit-1c-ref.html
 test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-fit-2a.html intrinsic-fit-1a-ref.html
 skip-if(B2G||Mulet) test-pref(font.size.inflation.emPerLine,15) test-pref(font.size.inflation.forceEnabled,true) test-pref(font.size.inflation.lineThreshold,0) HTTP(..) == intrinsic-fit-2b.html intrinsic-fit-1b-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
--- a/layout/reftests/forms/fieldset/reftest.list
+++ b/layout/reftests/forms/fieldset/reftest.list
@@ -1,14 +1,14 @@
 == dynamic-legend-scroll-1.html dynamic-legend-scroll-1-ref.html
 == fieldset-hidden-1.html fieldset-hidden-1-ref.html
 == fieldset-intrinsic-width-1.html fieldset-intrinsic-width-1-ref.html
 == fieldset-percentage-padding-1.html fieldset-percentage-padding-1-ref.html
 == fieldset-scroll-1.html fieldset-scroll-1-ref.html
 == fieldset-scrolled-1.html fieldset-scrolled-1-ref.html
 random-if(B2G||Mulet) == fieldset-overflow-auto-1.html fieldset-overflow-auto-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
-fuzzy-if(winWidget&&!layersGPUAccelerated,102,205) == positioned-container-1.html positioned-container-1-ref.html
+fuzzy-if(winWidget&&!layersGPUAccelerated,102,205) skip-if(asyncPanZoom&&winWidget) == positioned-container-1.html positioned-container-1-ref.html
 == relpos-legend-1.html relpos-legend-1-ref.html
 == relpos-legend-2.html relpos-legend-2-ref.html
 test-pref(layout.css.sticky.enabled,true) skip-if((B2G&&browserIsRemote)||Mulet) == sticky-legend-1.html sticky-legend-1-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == abs-pos-child-sizing.html abs-pos-child-sizing-ref.html
 == overflow-hidden.html overflow-hidden-ref.html
 == legend-rtl.html legend-rtl-ref.html
--- a/layout/reftests/forms/input/text/reftest.list
+++ b/layout/reftests/forms/input/text/reftest.list
@@ -1,10 +1,10 @@
 == bounds-1.html bounds-1-ref.html
-== size-1.html size-1-ref.html
+skip-if(asyncPanZoom&&winWidget) == size-1.html size-1-ref.html
 skip-if(B2G||Mulet) == size-2.html size-2-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 HTTP(..) == baseline-1.html baseline-1-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) HTTP(..) == centering-1.xul centering-1-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if((B2G&&browserIsRemote)||Mulet) == dynamic-height-1.xul dynamic-height-1-ref.xul # bug 974780 # Initial mulet triage: parity with B2G/B2G Desktop
 needs-focus == select.html select-ref.html
 == intrinsic-size.html intrinsic-size-ref.html
 == line-height-0.5.html line-height-1.0.html
 != line-height-1.5.html line-height-1.0.html
--- a/layout/reftests/forms/placeholder/reftest.list
+++ b/layout/reftests/forms/placeholder/reftest.list
@@ -11,18 +11,18 @@
 == placeholder-1-text.html placeholder-visible-ref.html
 == placeholder-1-password.html placeholder-visible-ref.html
 == placeholder-1-textarea.html placeholder-visible-textarea-ref.html
 == placeholder-2.html placeholder-visible-ref.html
 == placeholder-2-textarea.html placeholder-visible-textarea-ref.html
 == placeholder-3.html placeholder-overridden-ref.html
 == placeholder-4.html placeholder-overridden-ref.html
 == placeholder-5.html placeholder-visible-ref.html
-fuzzy-if(winWidget,160,7) == placeholder-6.html placeholder-overflow-ref.html
-skip-if(B2G||Mulet) == placeholder-6-textarea.html placeholder-overflow-textarea-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(asyncPanZoom&&winWidget) == placeholder-6.html placeholder-overflow-ref.html
+skip-if(B2G||Mulet) skip-if(asyncPanZoom&&winWidget) == placeholder-6-textarea.html placeholder-overflow-textarea-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 # needs-focus == placeholder-7.html placeholder-focus-ref.html
 # needs-focus == placeholder-8.html placeholder-focus-ref.html
 # needs-focus == placeholder-9.html placeholder-focus-ref.html
 needs-focus == placeholder-10.html placeholder-visible-ref.html
 == placeholder-11.html placeholder-visible-ref.html
 == placeholder-12.html placeholder-visible-ref.html
 == placeholder-13.html placeholder-visible-ref.html
 == placeholder-14.html placeholder-visible-ref.html
--- a/layout/reftests/forms/textarea/reftest.list
+++ b/layout/reftests/forms/textarea/reftest.list
@@ -5,10 +5,10 @@ skip-if(B2G||Mulet) fails-if(Android) !=
 skip-if(B2G||Mulet) fails-if(Android) != ltr-scrollbar.html rtl-scrollbar.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fails-if(Android) != in-ltr-doc-scrollbar.html in-rtl-doc-scrollbar.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fails-if(Android) != ltr.html no-resize.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) fails-if(Android) fails-if(gtk2Widget) != rtl.html no-resize.html # bug 834724 # Initial mulet triage: parity with B2G/B2G Desktop
 == rtl.html rtl-dynamic-attr.html
 == rtl.html rtl-dynamic-style.html
 == rtl.html in-dynamic-rtl-doc.html
 == setvalue-framereconstruction-1.html setvalue-framereconstruction-ref.html
-== padding-scrollbar-placement.html padding-scrollbar-placement-ref.html
+skip-if(asyncPanZoom&&winWidget) == padding-scrollbar-placement.html padding-scrollbar-placement-ref.html
 == various-cols.html various-cols-ref.html
--- a/layout/reftests/image/reftest.list
+++ b/layout/reftests/image/reftest.list
@@ -68,17 +68,17 @@ fuzzy(1,1) == image-orientation-generate
 fuzzy(1,1) == image-orientation-generated-content.html?90&flip    image-orientation-generated-content-ref.html?90&flip
 fuzzy(1,1) == image-orientation-generated-content.html?180&flip   image-orientation-generated-content-ref.html?180&flip
 fuzzy(1,1) == image-orientation-generated-content.html?270&flip   image-orientation-generated-content-ref.html?270&flip
 
 # Tests that image-orientation does not apply to decorative images:
 fuzzy(1,1) == image-orientation-background.html?from-image   image-orientation-ref.html?0
 fuzzy(1,1) == image-orientation-background.html?90&flip      image-orientation-ref.html?0
 == image-orientation-border-image.html?from-image            image-orientation-border-image.html?0
-== image-orientation-border-image.html?90&flip               image-orientation-border-image.html?0
+skip-if(asyncPanZoom&&winWidget) == image-orientation-border-image.html?90&flip               image-orientation-border-image.html?0
 == image-orientation-list-style-image.html?from-image        image-orientation-list-style-image.html?0
 == image-orientation-list-style-image.html?90&flip           image-orientation-list-style-image.html?0
 
 # Sanity checks for the image-orientation tests. Ensures that the various
 # combinations of rotations and flips actually look different from each other.
 != image-orientation-ref.html?0    image-orientation-ref.html?90
 != image-orientation-ref.html?0    image-orientation-ref.html?180
 != image-orientation-ref.html?0    image-orientation-ref.html?270
--- a/layout/reftests/invalidation/reftest.list
+++ b/layout/reftests/invalidation/reftest.list
@@ -56,11 +56,11 @@ pref(layout.animated-image-layers.enable
 != paintedlayer-recycling-6.html about:blank
 != paintedlayer-recycling-7.html about:blank
 != masklayer-1.html about:blank
 != masklayer-2.html about:blank
 != layer-splitting-1.html about:blank
 != layer-splitting-2.html about:blank
 != layer-splitting-3.html about:blank
 != layer-splitting-4.html about:blank
-!= layer-splitting-5.html about:blank
-!= layer-splitting-6.html about:blank
+skip-if(asyncPanZoom&&winWidget) != layer-splitting-5.html about:blank
+skip-if(asyncPanZoom&&winWidget) != layer-splitting-6.html about:blank
 != layer-splitting-7.html about:blank
--- a/layout/reftests/layers/reftest.list
+++ b/layout/reftests/layers/reftest.list
@@ -1,20 +1,21 @@
 == move-to-background-1.html move-to-background-1-ref.html
 fuzzy-if(cocoaWidget,2,6) random-if(Android&&!browserIsRemote) == component-alpha-exit-1.html component-alpha-exit-1-ref.html # bug 760275
-!= pull-background-1.html about:blank
-!= pull-background-2.html about:blank
-!= pull-background-3.html about:blank
-!= pull-background-4.html about:blank
-!= pull-background-5.html about:blank
-!= pull-background-6.html about:blank
+
+skip-if(asyncPanZoom) != pull-background-1.html about:blank
+skip-if(asyncPanZoom) != pull-background-2.html about:blank
+skip-if(asyncPanZoom) != pull-background-3.html about:blank # fails with non-overlay scrollbars and event regions due to bug 1148515
+skip-if(asyncPanZoom) != pull-background-4.html about:blank # fails with non-overlay scrollbars and event regions due to bug 1148515
+skip-if(asyncPanZoom) != pull-background-5.html about:blank # Fails with event regions, bug 1150941
+skip-if(asyncPanZoom) != pull-background-6.html about:blank
 # The animated-position tests are disabled for intermittent failures / passes, bug 1150941
 skip != pull-background-animated-position-1.html about:blank # Fails with event regions
 skip != pull-background-animated-position-2.html about:blank
 skip != pull-background-animated-position-3.html about:blank # Fails because PaintedLayer item assignment doesn't recognize overflow:hidden clips
 skip != pull-background-animated-position-4.html about:blank # Fails because PaintedLayer item assignment and background pulling don't recognize overflow:hidden clips
 skip != pull-background-animated-position-5.html about:blank # Fails because ownLayer bounds don't anticipate changes of animated contents, but doesn't fail with event regions
-skip-if(!asyncPanZoom) != pull-background-displayport-1.html about:blank
-skip-if(!asyncPanZoom) != pull-background-displayport-2.html about:blank
-skip-if(!asyncPanZoom) != pull-background-displayport-3.html about:blank # fails with non-overlay scrollbars and event regions due to bug 1148515
-skip-if(!asyncPanZoom) != pull-background-displayport-4.html about:blank # fails with non-overlay scrollbars and event regions due to bug 1148515
+skip-if(!asyncPanZoom) skip-if(asyncPanZoom) != pull-background-displayport-1.html about:blank
+skip-if(!asyncPanZoom) skip-if(asyncPanZoom) != pull-background-displayport-2.html about:blank
+skip-if(!asyncPanZoom) skip-if(asyncPanZoom) != pull-background-displayport-3.html about:blank # fails with non-overlay scrollbars and event regions due to bug 1148515
+skip-if(!asyncPanZoom) skip-if(asyncPanZoom) != pull-background-displayport-4.html about:blank # fails with non-overlay scrollbars and event regions due to bug 1148515
 fails skip-if(!asyncPanZoom) != pull-background-displayport-5.html about:blank # bug 1147673
-skip-if(!asyncPanZoom) != pull-background-displayport-6.html about:blank # fails with non-overlay scrollbars and event regions due to bug 1148515
+skip-if(!asyncPanZoom) skip-if(asyncPanZoom) != pull-background-displayport-6.html about:blank # fails with non-overlay scrollbars and event regions due to bug 1148515
--- a/layout/reftests/position-dynamic-changes/relative/reftest.list
+++ b/layout/reftests/position-dynamic-changes/relative/reftest.list
@@ -1,5 +1,5 @@
-fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) == move-right-bottom.html move-right-bottom-ref.html
-fuzzy-if(cocoaWidget,1,2) == move-top-left.html move-top-left-ref.html # Bug 688545
-fuzzy-if(cocoaWidget,1,3) == move-right-bottom-table.html move-right-bottom-table-ref.html
-fuzzy-if(cocoaWidget,1,3) == move-top-left-table.html move-top-left-table-ref.html # Bug 688545
+skip-if(asyncPanZoom&&winWidget) fuzzy-if(cocoaWidget,1,2) fuzzy-if(d2d,47,26) == move-right-bottom.html move-right-bottom-ref.html
+skip-if(asyncPanZoom&&winWidget) fuzzy-if(cocoaWidget,1,2) == move-top-left.html move-top-left-ref.html # Bug 688545
+skip-if(asyncPanZoom&&winWidget) fuzzy-if(cocoaWidget,1,3) == move-right-bottom-table.html move-right-bottom-table-ref.html
+skip-if(asyncPanZoom&&winWidget) fuzzy-if(cocoaWidget,1,3) == move-top-left-table.html move-top-left-table-ref.html # Bug 688545
 == percent.html percent-ref.html
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -14,17 +14,17 @@ include ../../image/test/reftest/reftest
 # CSSWG tests
 include w3c-css/submitted/reftest.list
 include w3c-css/received/reftest.list
 
 # relative and absolute positioning
 include abs-pos/reftest.list
 include position-relative/reftest.list
 
-include async-scrolling/reftest.list
+skip-if(asyncPanZoom&&winWidget) include async-scrolling/reftest.list
 
 # backgrounds/
 include backgrounds/reftest.list
 
 # bidi/
 include bidi/reftest.list
 
 # border-image
--- a/layout/reftests/scrolling/reftest.list
+++ b/layout/reftests/scrolling/reftest.list
@@ -10,28 +10,28 @@ random-if(Android&&!browserIsRemote) ski
 random-if(Android) HTTP == image-1.html image-1.html?ref
 random-if(Android&&!browserIsRemote) HTTP == opacity-mixed-scrolling-1.html opacity-mixed-scrolling-1.html?ref # bug 760269
 skip-if(B2G||Mulet) random-if(cocoaWidget) HTTP == opacity-mixed-scrolling-2.html opacity-mixed-scrolling-2.html?ref # see bug 625357 # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-1.html scroll-behavior-1.html?ref # see bug 1041833
 skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-2.html scroll-behavior-2.html?ref # see bug 1041833
 skip-if(Mulet) skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-3.html scroll-behavior-3.html?ref # see bug 1041833 # MULET: Bug 1144079: Re-enable Mulet mochitests and reftests taskcluster-specific disables
 skip-if(Mulet) skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-4.html scroll-behavior-4.html?ref # see bug 1041833 # MULET: Bug 1144079: Re-enable Mulet mochitests and reftests taskcluster-specific disables
 skip-if(Mulet) skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-5.html scroll-behavior-5.html?ref # see bug 1041833 # MULET: Bug 1144079: Re-enable Mulet mochitests and reftests taskcluster-specific disables
-skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-6.html scroll-behavior-6.html?ref # see bug 1041833
+skip-if(Android) skip-if(asyncPanZoom&&winWidget) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-6.html scroll-behavior-6.html?ref # see bug 1041833
 skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-7.html scroll-behavior-7.html?ref # see bug 1041833
 skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-8.html scroll-behavior-8.html?ref # see bug 1041833
 skip-if(Android) pref(layout.css.scroll-behavior.enabled,true) pref(layout.css.scroll-behavior.property-enabled,true) == scroll-behavior-9.html scroll-behavior-9.html?ref # see bug 1041833
 skip-if((B2G&&browserIsRemote)||Mulet) HTTP == simple-1.html simple-1.html?ref # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) HTTP == subpixel-1.html#d subpixel-1-ref.html#d # Initial mulet triage: parity with B2G/B2G Desktop
 fuzzy-if(Android,4,120) HTTP == text-1.html text-1.html?ref
 fuzzy-if(Android,4,120) HTTP == text-2.html?up text-2.html?ref
 skip-if(B2G||Mulet) fuzzy-if(Android&&AndroidVersion<15,251,722) fuzzy-if(d2d,1,4) HTTP == transformed-1.html transformed-1.html?ref # Initial mulet triage: parity with B2G/B2G Desktop
 HTTP == transformed-1.html?up transformed-1.html?ref
 fuzzy-if(Android,5,20000) == uncovering-1.html uncovering-1-ref.html
 fuzzy-if(Android,5,20000) == uncovering-2.html uncovering-2-ref.html
-skip-if(B2G||Mulet) == less-than-scrollbar-height.html less-than-scrollbar-height-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+skip-if(B2G||Mulet) skip-if(asyncPanZoom&&winWidget) == less-than-scrollbar-height.html less-than-scrollbar-height-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) == huge-horizontal-overflow.html huge-horizontal-overflow-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) == huge-vertical-overflow.html huge-vertical-overflow-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 == iframe-scrolling-attr-1.html iframe-scrolling-attr-ref.html
 skip-if((B2G&&browserIsRemote)||Mulet) == iframe-scrolling-attr-2.html iframe-scrolling-attr-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
-== frame-scrolling-attr-1.html frame-scrolling-attr-ref.html
-== frame-scrolling-attr-2.html frame-scrolling-attr-ref.html
+skip-if(asyncPanZoom&&winWidget) == frame-scrolling-attr-1.html frame-scrolling-attr-ref.html
+skip-if(asyncPanZoom&&winWidget) == frame-scrolling-attr-2.html frame-scrolling-attr-ref.html
 == move-item.html move-item-ref.html # bug 1125750
--- a/layout/reftests/text-overflow/reftest.list
+++ b/layout/reftests/text-overflow/reftest.list
@@ -17,10 +17,10 @@ skip-if(B2G||Mulet) random-if(/^Windows\
 HTTP(..) == marker-shadow.html marker-shadow-ref.html
 == aligned-baseline.html aligned-baseline-ref.html
 skip-if(Android||B2G) == clipped-elements.html clipped-elements-ref.html
 HTTP(..) == theme-overflow.html theme-overflow-ref.html
 skip-if(B2G||Mulet) HTTP(..) == table-cell.html table-cell-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(Mulet) HTTP(..) == two-value-syntax.html two-value-syntax-ref.html # MULET: Bug 1144079: Re-enable Mulet mochitests and reftests taskcluster-specific disables
 skip-if(B2G||Mulet) HTTP(..) == single-value.html single-value-ref.html  # Initial mulet triage: parity with B2G/B2G Desktop
 skip-if(B2G||Mulet) HTTP(..) == atomic-under-marker.html atomic-under-marker-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
-fuzzy(1,702) skip-if(Android||B2G||Mulet) HTTP(..) == xulscroll.html xulscroll-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
+fuzzy(1,702) skip-if(Android||B2G||Mulet) skip-if(asyncPanZoom&&winWidget) HTTP(..) == xulscroll.html xulscroll-ref.html # Initial mulet triage: parity with B2G/B2G Desktop
 HTTP(..) == combobox-zoom.html combobox-zoom-ref.html
--- a/layout/style/ua.css
+++ b/layout/style/ua.css
@@ -313,32 +313,36 @@ parsererror|sourcetext {
   font-weight: bold;
   font-size: 12pt;
 }
 
 div:-moz-native-anonymous.moz-touchcaret,
 div:-moz-native-anonymous.moz-selectioncaret-left,
 div:-moz-native-anonymous.moz-selectioncaret-right {
   position: fixed;
+  width: 44px;
+  height: 47px;
 }
 
 div:-moz-native-anonymous.moz-selectioncaret-left > div,
 div:-moz-native-anonymous.moz-selectioncaret-right > div {
   position: absolute;
+  width: 100%;
+  height: 100%;
+  bottom: 0;
 }
 
 div:-moz-native-anonymous.moz-touchcaret,
 div:-moz-native-anonymous.moz-selectioncaret-left,
 div:-moz-native-anonymous.moz-selectioncaret-right,
 div:-moz-native-anonymous.moz-selectioncaret-left > div,
 div:-moz-native-anonymous.moz-selectioncaret-right > div {
-  width: 44px;
-  height: 47px;
-  background-position: center center;
-  background-size: 100% 100%;
+  background-position: center bottom;
+  background-size: 100%;
+  background-repeat: no-repeat;
   z-index: 2147483647;
 }
 
 div:-moz-native-anonymous.moz-touchcaret,
 div:-moz-native-anonymous.moz-selectioncaret-left > div,
 div:-moz-native-anonymous.moz-selectioncaret-right > div {
   background-image: url("resource://gre/res/text_caret.png");
 }
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -109,17 +109,16 @@
 <!ENTITY overlay_share_send_other "Send to other devices">
 
 <!-- Localization note (overlay_share_send_tab_btn_label) : Used on the
      share overlay menu to represent the "Send Tab" action when the user
      either has not set up Sync, or has no other devices to send a tab
      to. -->
 <!ENTITY overlay_share_send_tab_btn_label "Send to another device">
 <!ENTITY overlay_share_no_url "No link found in this share">
-<!ENTITY overlay_share_retry "Try again">
 <!ENTITY overlay_share_select_device "Select device">
 <!-- Localization note (overlay_no_synced_devices) : Used when the menu option
      to send a tab to a synced device is pressed and no other synced devices
      are found. -->
 <!ENTITY overlay_no_synced_devices "No Firefox Account connected devices found">
 <!-- Localization note (overlay_share_tab_not_sent) : Used when the menu option
      to send a tab to a synced device is pressed and there is an error
      connecting to the server so the tab could not be sent. The text should be
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -112,17 +112,16 @@
   <string name="overlay_share_send_other">&overlay_share_send_other;</string>
   <string name="overlay_share_label">&overlay_share_label;</string>
   <string name="overlay_share_bookmark_btn_label">&overlay_share_bookmark_btn_label;</string>
   <string name="overlay_share_reading_list_btn_label">&overlay_share_reading_list_btn_label;</string>
   <string name="overlay_share_bookmark_btn_label_already">&overlay_share_bookmark_btn_label_already;</string>
   <string name="overlay_share_reading_list_btn_label_already">&overlay_share_reading_list_btn_label_already;</string>
   <string name="overlay_share_send_tab_btn_label">&overlay_share_send_tab_btn_label;</string>
   <string name="overlay_share_no_url">&overlay_share_no_url;</string>
-  <string name="overlay_share_retry">&overlay_share_retry;</string>
   <string name="overlay_share_select_device">&overlay_share_select_device;</string>
   <string name="overlay_no_synced_devices">&overlay_no_synced_devices;</string>
   <string name="overlay_share_tab_not_sent">&overlay_share_tab_not_sent;</string>
 
   <string name="settings">&settings;</string>
   <string name="settings_title">&settings_title;</string>
   <string name="pref_category_advanced">&pref_category_advanced;</string>
   <string name="pref_category_customize">&pref_category_customize;</string>
--- a/mobile/android/installer/package-manifest.in
+++ b/mobile/android/installer/package-manifest.in
@@ -248,17 +248,19 @@
 @BINPATH@/components/spellchecker.xpt
 @BINPATH@/components/storage.xpt
 @BINPATH@/components/telemetry.xpt
 @BINPATH@/components/toolkit_asyncshutdown.xpt
 @BINPATH@/components/toolkit_filewatcher.xpt
 @BINPATH@/components/toolkit_finalizationwitness.xpt
 @BINPATH@/components/toolkit_formautofill.xpt
 @BINPATH@/components/toolkit_osfile.xpt
+#ifdef NIGHTLY_BUILD
 @BINPATH@/components/toolkit_perfmonitoring.xpt
+#endif
 @BINPATH@/components/toolkit_xulstore.xpt
 @BINPATH@/components/toolkitprofile.xpt
 #ifdef MOZ_ENABLE_XREMOTE
 @BINPATH@/components/toolkitremote.xpt
 #endif
 @BINPATH@/components/txtsvc.xpt
 @BINPATH@/components/txmgr.xpt
 @BINPATH@/components/uconv.xpt
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -473,20 +473,27 @@ pref("media.video-queue.default-size", 1
 
 // Whether to disable the video stats to prevent fingerprinting
 pref("media.video_stats.enabled", true);
 
 // Whether to enable the audio writing APIs on the audio element
 pref("media.audio_data.enabled", false);
 
 // Whether to use async panning and zooming
+#ifdef XP_WIN
+pref("layers.async-pan-zoom.enabled", true);
+
+// Workaround a bug where inactive scrollframes test this pref directly.
+pref("layout.event-regions.enabled", true);
+#else
 pref("layers.async-pan-zoom.enabled", false);
 
 // Whether to enable event region building during painting
 pref("layout.event-regions.enabled", false);
+#endif
 
 // APZ preferences. For documentation/details on what these prefs do, check
 // gfx/layers/apz/src/AsyncPanZoomController.cpp.
 pref("apz.allow_checkerboarding", true);
 pref("apz.asyncscroll.throttle", 100);
 pref("apz.asyncscroll.timeout", 300);
 
 // Whether to lock touch scrolling to one axis at a time
--- a/testing/mochitest/browser-test.js
+++ b/testing/mochitest/browser-test.js
@@ -907,19 +907,19 @@ function testMessage(aName) {
 // cannot conflict with global variables used in tests.
 function testScope(aTester, aTest, expected) {
   this.__tester = aTester;
   this.__expected = expected;
   this.__num_failed = 0;
 
   var self = this;
   this.ok = function test_ok(condition, name, diag, stack) {
-    if (this.__expected == 'fail') {
+    if (self.__expected == 'fail') {
         if (!condition) {
-          this.__num_failed++;
+          self.__num_failed++;
           condition = true;
         }
     }
 
     aTest.addResult(new testResult(condition, name, diag, false,
                                    stack ? stack : Components.stack.caller));
   };
   this.is = function test_is(a, b, name) {
--- a/testing/mochitest/mach_commands.py
+++ b/testing/mochitest/mach_commands.py
@@ -839,17 +839,17 @@ class MachCommands(MachCommandBase):
         description='Run a plain mochitest (integration test, plain web page).',
         parser=setup_argument_parser)
     def run_mochitest_plain(self, test_paths, **kwargs):
         if is_platform_in('firefox', 'mulet')(self):
             return self.run_mochitest(test_paths, 'plain', **kwargs)
         elif conditions.is_emulator(self):
             return self.run_mochitest_remote(test_paths, **kwargs)
         elif conditions.is_b2g_desktop(self):
-            return self.run_b2g_desktop(test_paths, **kwargs)
+            return self.run_mochitest_b2g_desktop(test_paths, **kwargs)
         elif conditions.is_android(self):
             return self.run_mochitest_android(test_paths, **kwargs)
 
     @Command(
         'mochitest-chrome',
         category='testing',
         conditions=[is_platform_in('firefox', 'emulator', 'android')],
         description='Run a chrome mochitest (integration test with some XUL).',
--- a/toolkit/components/aboutperformance/content/aboutPerformance.js
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.js
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
 
 const { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
+const { AddonWatcher } = Cu.import("resource://gre/modules/AddonWatcher.jsm", {});
 const { PerformanceStats } = Cu.import("resource://gre/modules/PerformanceStats.jsm", {});
 
 /**
  * The various measures we display.
  */
 const MEASURES = [
   {key: "longestDuration", percentOfDeltaT: false, label: "Jank level"},
   {key: "totalUserTime", percentOfDeltaT: true, label: "User (%)"},
@@ -79,18 +80,103 @@ let State = {
     State._processData = snapshot.processData;
     State._date = now;
     return result;
   }
 };
 
 
 function update() {
+  updateLiveData();
+  updateSlowAddons();
+}
+
+/**
+ * Update the list of slow addons
+ */
+function updateSlowAddons() {
   try {
-    let dataElt = document.getElementById("data");
+    let data = AddonWatcher.alerts;
+    if (data.size == 0) {
+      // Nothing to display.
+      return;
+    }
+    let alerts = 0;
+    for (let [addonId, details] of data) {
+      for (let k of Object.keys(details.alerts)) {
+        alerts += details.alerts[k];
+      }
+    }
+
+    if (!alerts) {
+      // Still nothing to display.
+      return;
+    }
+
+
+    let elData = document.getElementById("slowAddonsList");
+    elData.innerHTML = "";
+    let elTable = document.createElement("table");
+    elData.appendChild(elTable);
+
+    // Generate header
+    let elHeader = document.createElement("tr");
+    elTable.appendChild(elHeader);
+    for (let name of [
+      "Alerts",
+      "Jank level alerts",
+      "(highest jank)",
+      "Cross-Process alerts",
+      "(highest CPOW)"
+    ]) {
+      let elName = document.createElement("td");
+      elName.textContent = name;
+      elHeader.appendChild(elName);
+      elName.classList.add("header");
+    }
+    for (let [addonId, details] of data) {
+      let elAddon = document.createElement("tr");
+
+      // Display the number of occurrences of each alerts
+      let elTotal = document.createElement("td");
+      let total = 0;
+      for (let k of Object.keys(details.alerts)) {
+        total += details.alerts[k];
+      }
+      elTotal.textContent = total;
+      elAddon.appendChild(elTotal);
+
+      for (let filter of ["longestDuration", "totalCPOWTime"]) {
+        for (let stat of ["alerts", "peaks"]) {
+          let el = document.createElement("td");
+          el.textContent = details[stat][filter] || 0;
+          elAddon.appendChild(el);
+        }
+      }
+
+      // Display the name of the add-on
+      let elName = document.createElement("td");
+      elAddon.appendChild(elName);
+      AddonManager.getAddonByID(addonId, a => {
+        elName.textContent = a ? a.name : addonId
+      });
+
+      elTable.appendChild(elAddon);
+    }
+  } catch (ex) {
+    console.error(ex);
+  }
+}
+
+/**
+ * Update the table of live data.
+ */
+function updateLiveData() {
+  try {
+    let dataElt = document.getElementById("liveData");
     dataElt.innerHTML = "";
 
     // Generate table headers
     let headerElt = document.createElement("tr");
     dataElt.appendChild(headerElt);
     headerElt.classList.add("header");
     for (let column of [...MEASURES, {key: "name", name: ""}]) {
       let el = document.createElement("td");
@@ -132,17 +218,17 @@ function update() {
       let id = item.id;
       el.classList.add("contents");
       el.classList.add("name");
       row.appendChild(el);
       if (item.addonId) {
         let _el = el;
         let _item = item;
         AddonManager.getAddonByID(item.addonId, a => {
-          _el.textContent = a?a.name:_item.name
+          _el.textContent = a ? a.name : _item.name
         });
       } else {
         el.textContent = item.name;
       }
     }
   } catch (ex) {
     console.error(ex);
   }
--- a/toolkit/components/aboutperformance/content/aboutPerformance.xhtml
+++ b/toolkit/components/aboutperformance/content/aboutPerformance.xhtml
@@ -80,12 +80,20 @@
       }
       td.jank10 {
         color: rgb(255, 0, 0);
         font-weight: bold;
       }
     </style>
   </head>
   <body onload="go()">
-    <table id="data">
+
+    <h1>Performance monitor</h1>
+    <table id="liveData">
     </table>
+
+    <h1>Slow add-ons alerts</h1>
+    <div id="slowAddonsList">
+      (none)
+    </div>
+
   </body>
 </html>
--- a/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
+++ b/toolkit/components/aboutperformance/tests/browser/browser_aboutperformance.js
@@ -11,17 +11,17 @@ const URL = "http://example.com/browser/
 function frameScript() {
   "use strict";
 
   addMessageListener("aboutperformance-test:hasItems", ({data: url}) => {
     let hasPlatform = false;
     let hasURL = false;
 
     try {
-      let eltData = content.document.getElementById("data");
+      let eltData = content.document.getElementById("liveData");
       if (!eltData) {
         return;
       }
 
       // Find if we have a row for "platform"
       hasPlatform = eltData.querySelector("tr.platform") != null;
 
       // Find if we have a row for our URL
--- a/toolkit/components/build/moz.build
+++ b/toolkit/components/build/moz.build
@@ -16,22 +16,26 @@ FINAL_LIBRARY = 'xul'
 
 LOCAL_INCLUDES += [
     '../../xre',
     '../alerts',
     '../downloads',
     '../feeds',
     '../find',
     '../jsdownloads/src',
-    '../perfmonitoring',
     '../protobuf',
     '../startup',
     '../statusfilter',
     '../typeaheadfind',
     '../url-classifier',
 ]
 
 if not CONFIG['MOZ_DISABLE_PARENTAL_CONTROLS']:
     LOCAL_INCLUDES += [
         '../parentalcontrols',
     ]
 
+if CONFIG['NIGHTLY_BUILD']:
+    LOCAL_INCLUDES += [
+        '../perfmonitoring',
+    ]
+
 FAIL_ON_WARNINGS = True
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -46,25 +46,33 @@
 #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID)
 #define MOZ_HAS_TERMINATOR
 #endif
 
 #if defined(MOZ_HAS_TERMINATOR)
 #include "nsTerminator.h"
 #endif
 
+#if defined(NIGHTLY_BUILD)
+#define MOZ_HAS_PERFSTATS
+#endif // defined(NIGHTLY_BUILD)
+
+#if defined(MOZ_HAS_PERFSTATS)
 #include "nsPerformanceStats.h"
+#endif // defined (MOZ_HAS_PERFSTATS)
 
 using namespace mozilla;
 
 /////////////////////////////////////////////////////////////////////////////
 
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsAppStartup, Init)
 
+#if defined(MOZ_HAS_PERFSTATS)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsPerformanceStatsService)
+#endif // defined (MOZ_HAS_PERFSTATS)
 
 #if defined(MOZ_HAS_TERMINATOR)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTerminator)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUserInfo)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsFindService)
 
@@ -114,17 +122,20 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsUpdateP
 #endif
 NS_GENERIC_FACTORY_CONSTRUCTOR(FinalizationWitnessService)
 NS_GENERIC_FACTORY_CONSTRUCTOR(NativeOSFileInternalsService)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(NativeFileWatcherService, Init)
 
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(AddonPathService, AddonPathService::GetInstance)
 
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_APPSTARTUP_CID);
+#if defined(MOZ_HAS_PERFSTATS)
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_PERFORMANCESTATSSERVICE_CID);
+#endif // defined (MOZ_HAS_PERFSTATS)
+
 #if defined(MOZ_HAS_TERMINATOR)
 NS_DEFINE_NAMED_CID(NS_TOOLKIT_TERMINATOR_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_USERINFO_CID);
 NS_DEFINE_NAMED_CID(NS_ALERTSSERVICE_CID);
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
 NS_DEFINE_NAMED_CID(NS_PARENTALCONTROLSSERVICE_CID);
 #endif
@@ -149,17 +160,19 @@ NS_DEFINE_NAMED_CID(NATIVE_OSFILE_INTERN
 NS_DEFINE_NAMED_CID(NS_ADDON_PATH_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NATIVE_FILEWATCHER_SERVICE_CID);
 
 static const Module::CIDEntry kToolkitCIDs[] = {
   { &kNS_TOOLKIT_APPSTARTUP_CID, false, nullptr, nsAppStartupConstructor },
 #if defined(MOZ_HAS_TERMINATOR)
   { &kNS_TOOLKIT_TERMINATOR_CID, false, nullptr, nsTerminatorConstructor },
 #endif
+#if defined(MOZ_HAS_PERFSTATS)
   { &kNS_TOOLKIT_PERFORMANCESTATSSERVICE_CID, false, nullptr, nsPerformanceStatsServiceConstructor },
+#endif // defined (MOZ_HAS_PERFSTATS)
   { &kNS_USERINFO_CID, false, nullptr, nsUserInfoConstructor },
   { &kNS_ALERTSSERVICE_CID, false, nullptr, nsAlertsServiceConstructor },
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
   { &kNS_PARENTALCONTROLSSERVICE_CID, false, nullptr, nsParentalControlsServiceConstructor },
 #endif
   { &kNS_DOWNLOADMANAGER_CID, false, nullptr, nsDownloadManagerConstructor },
   { &kNS_DOWNLOADPLATFORM_CID, false, nullptr, DownloadPlatformConstructor },
   { &kNS_DOWNLOAD_CID, false, nullptr, nsDownloadProxyConstructor },
@@ -183,17 +196,19 @@ static const Module::CIDEntry kToolkitCI
   { nullptr }
 };
 
 static const Module::ContractIDEntry kToolkitContracts[] = {
   { NS_APPSTARTUP_CONTRACTID, &kNS_TOOLKIT_APPSTARTUP_CID },
 #if defined(MOZ_HAS_TERMINATOR)
   { NS_TOOLKIT_TERMINATOR_CONTRACTID, &kNS_TOOLKIT_TERMINATOR_CID },
 #endif
+#if defined(MOZ_HAS_PERFSTATS)
   { NS_TOOLKIT_PERFORMANCESTATSSERVICE_CONTRACTID, &kNS_TOOLKIT_PERFORMANCESTATSSERVICE_CID },
+#endif // defined (MOZ_HAS_PERFSTATS)
   { NS_USERINFO_CONTRACTID, &kNS_USERINFO_CID },
   { NS_ALERTSERVICE_CONTRACTID, &kNS_ALERTSSERVICE_CID },
 #if !defined(MOZ_DISABLE_PARENTAL_CONTROLS)
   { NS_PARENTALCONTROLSSERVICE_CONTRACTID, &kNS_PARENTALCONTROLSSERVICE_CID },
 #endif
   { NS_DOWNLOADMANAGER_CONTRACTID, &kNS_DOWNLOADMANAGER_CID },
   { NS_DOWNLOADPLATFORM_CONTRACTID, &kNS_DOWNLOADPLATFORM_CID },
   { NS_FIND_SERVICE_CONTRACTID, &kNS_FIND_SERVICE_CID },
--- a/toolkit/components/moz.build
+++ b/toolkit/components/moz.build
@@ -6,17 +6,16 @@
 
 # These component dirs are built for all apps (including suite)
 if CONFIG['MOZ_ENABLE_XREMOTE']:
     DIRS += ['remote']
 
 DIRS += [
     'aboutcache',
     'aboutmemory',
-    'aboutperformance',
     'addoncompat',
     'alerts',
     'apppicker',
     'asyncshutdown',
     'commandlines',
     'console',
     'contentprefs',
     'cookie',
@@ -31,17 +30,16 @@ DIRS += [
     'find',
     'jsdownloads',
     'mediasniffer',
     'microformats',
     'osfile',
     'parentalcontrols',
     'passwordmgr',
     'perf',
-    'perfmonitoring',
     'places',
     'processsingleton',
     'promiseworker',
     'prompts',
     'protobuf',
     'reader',
     'reflect',
     'sqlite',
@@ -87,14 +85,20 @@ if CONFIG['MOZ_URL_CLASSIFIER']:
     DIRS += ['url-classifier']
 
 if CONFIG['MOZ_CAPTIVEDETECT']:
     DIRS += ['captivedetect']
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] != "gonk" and CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android':
     DIRS += ['terminator']
 
+if CONFIG['NIGHTLY_BUILD']: # Bug 1136927 - Performance Monitoring is not ready for prime-time yet
+    DIRS += [
+      'aboutperformance',
+      'perfmonitoring',
+    ]
+
 DIRS += ['build']
 
 EXTRA_COMPONENTS += [
     'nsDefaultCLH.js',
     'nsDefaultCLH.manifest',
 ]
--- a/toolkit/components/perfmonitoring/AddonWatcher.jsm
+++ b/toolkit/components/perfmonitoring/AddonWatcher.jsm
@@ -18,18 +18,26 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PerformanceStats",
                                   "resource://gre/modules/PerformanceStats.jsm");
 XPCOMUtils.defineLazyServiceGetter(this, "Telemetry",
                                   "@mozilla.org/base/telemetry;1",
                                   Ci.nsITelemetry);
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
                                   "resource://gre/modules/Services.jsm");
 
+const FILTERS = ["longestDuration", "totalCPOWTime"];
+
 let AddonWatcher = {
   _previousPerformanceIndicators: {},
+
+  /**
+   * Stats, designed to be consumed by clients of AddonWatcher.
+   *
+   */
+  _stats: new Map(),
   _timer: Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer),
   _callback: null,
   /**
    * The interval at which we poll the available performance information
    * to find out about possibly slow add-ons, in milliseconds.
    */
   _interval: 15000,
   _ignoreList: null,
@@ -156,34 +164,46 @@ let AddonWatcher = {
           Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_JANK_LEVEL").
             add(addonId, diff.longestDuration);
         }
         if (diff.totalCPOWTime > 0) {
           Telemetry.getKeyedHistogramById("MISBEHAVING_ADDONS_CPOW_TIME_MS").
             add(addonId, diff.totalCPOWTime / 1000);
         }
 
-        // Report mibehaviors to the user.
-        let reason = null;
+        // Store misbehaviors for about:performance and other clients
 
-        for (let k of ["longestDuration", "totalCPOWTime"]) {
-          if (limits[k] > 0 && diff[k] > limits[k]) {
-            reason = k;
-          }
+        let stats = this._stats.get(addonId);
+        if (!stats) {
+          stats = {
+            peaks: {},
+            alerts: {},
+          };
+          this._stats.set(addonId, stats);
         }
 
-        if (!reason) {
-          continue;
-        }
+        // Report misbehaviors to the user.
+
+        for (let filter of FILTERS) {
+          dump(`Checking addon ${addonId} with filter ${filter}\n`);
+          let peak = stats.peaks[filter] || 0;
+          stats.peaks[filter] = Math.max(diff[filter], peak);
 
-        try {
-          this._callback(addonId, reason);
-        } catch (ex) {
-          Cu.reportError("Error in AddonWatcher._checkAddons callback " + ex);
-          Cu.reportError(ex.stack);
+          if (limits[filter] <= 0 || diff[filter] <= limits[filter]) {
+            continue;
+          }
+
+          stats.alerts[filter] = (stats.alerts[filter] || 0) + 1;
+
+          try {
+            this._callback(addonId, filter);
+          } catch (ex) {
+            Cu.reportError("Error in AddonWatcher._checkAddons callback " + ex);
+            Cu.reportError(ex.stack);
+          }
         }
       }
     } catch (ex) {
       Cu.reportError("Error in AddonWatcher._checkAddons " + ex);
       Cu.reportError(ex.stack);
     }
   },
   ignoreAddonForSession: function(addonid) {
@@ -195,10 +215,29 @@ let AddonWatcher = {
       let ignoreList = JSON.parse(Preferences.get("browser.addon-watch.ignore", "[]"))
       if (!ignoreList.includes(addonid)) {
         ignoreList.push(addonid);
         Preferences.set("browser.addon-watch.ignore", JSON.stringify(ignoreList));
       }
     } catch (ex) {
       Preferences.set("browser.addon-watch.ignore", JSON.stringify([addonid]));
     }
-  }
+  },
+  /**
+   * The list of alerts for this session.
+   *
+   * @type {Map<String, Object>} A map associating addonId to
+   *  objects with fields
+   *  - {Object} peaks The h