Merge m-c to inbound. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Tue, 20 Jan 2015 22:15:04 -0500
changeset 251952 49c547ee2a1119f4f2780727b8a69c1b6add667d
parent 251951 ffa9eece82875fb681722503aa78d66086391cfb (current diff)
parent 251887 540077a308669a42c0d8fe7dbd43d4cc36c9a5ff (diff)
child 251953 9c6ac12d931a1a881e7d9e43c9e153ec37d00278
push id4610
push userjlund@mozilla.com
push dateMon, 30 Mar 2015 18:32:55 +0000
treeherdermozilla-beta@4df54044d9ef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.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
--- 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="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- 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"/>
@@ -124,17 +124,17 @@
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="2e43efe1b30d0b98574d293059556aebd2f46454"/>
   <!--original fetch url was http://sprdsource.spreadtrum.com:8085/b2g/android-->
   <remote fetch="https://git.mozilla.org/external/sprd-aosp" name="sprd-aosp"/>
   <default remote="sprd-aosp" revision="sprdb2g_gonk4.4" sync-j="4"/>
   <!-- Stock Android things -->
   <project name="platform/external/icu4c" path="external/icu4c" revision="2bb01561780583cc37bc667f0ea79f48a122d8a2"/>
   <!-- dolphin specific things -->
-  <project name="device/sprd" path="device/sprd" revision="56f0085d1105fd6519d5d3773f91d48b2ad66d5c"/>
+  <project name="device/sprd" path="device/sprd" revision="8491e6338aa1e12699a2064895b02fb015f91b89"/>
   <project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="4e58336019b5cbcfd134caf55b142236cf986618"/>
   <project name="platform/frameworks/av" path="frameworks/av" revision="4387fe988e5a1001f29ce05fcfda03ed2d32137b"/>
   <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="6c6f012cea17fb8b3263605737816cf6663432f1"/>
   <project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
   <project name="u-boot" path="u-boot" revision="5167e5eec5cb6b3147839da158637e6d953a4e4f"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,23 +14,23 @@
   <!--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="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- 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="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,25 +10,25 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- 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/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,23 +14,23 @@
   <!--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="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="6fa7a4936414ceb4055fd27f7a30e76790f834fb"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="425f8b5fadf5889834c5acd27d23c9e0b2129c28"/>
   <project name="device/common" path="device/common" revision="42b808b7e93d0619286ae8e59110b176b7732389"/>
   <project name="device/sample" path="device/sample" revision="237bd668d0f114d801a8d6455ef5e02cc3577587"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
   <project name="platform/external/bluetooth/bluez" path="external/bluetooth/bluez" revision="52a1a862a8bac319652b8f82d9541ba40bfa45ce"/>
--- 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="e0c735ec89df011ea7dd435087a9045ecff9ff9e">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.7" revision="a1e239a0bb5cd1d69680bf1075883aa9a7bf2429"/>
   <project groups="linux,x86" name="platform/prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" path="prebuilts/gcc/linux-x86/x86/i686-linux-android-4.7" revision="c7931763d41be602407ed9d71e2c0292c6597e00"/>
   <project groups="linux,x86" name="platform/prebuilts/python/linux-x86/2.7.5" path="prebuilts/python/linux-x86/2.7.5" revision="a32003194f707f66a2d8cdb913ed1869f1926c5d"/>
   <project name="device/common" path="device/common" revision="96d4d2006c4fcb2f19a3fa47ab10cb409faa017b"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,20 +12,20 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="0e94c080bee081a50aa2097527b0b40852f9143f">
     <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="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="8b880805d454664b3eed11d0f053cdeafa1ff06e"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "579e01ad4d6e4177a8f636305ac877835d99f134", 
+        "git_revision": "5e98dc164b17fd6decb48a9eaddef0e55b82e249", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "91945bbce2f909915fe3edf033215baa4c80e3cd", 
+    "revision": "d60b9f55a77aa72606acb397b3770adc1bcf5110", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -12,21 +12,21 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="746bc48f34f5060f90801925dcdd964030c1ab6d"/>
   <project name="platform/development" path="development" revision="2460485184bc8535440bb63876d4e63ec1b4770c"/>
   <project name="device/common" path="device/common" revision="0dcc1e03659db33b77392529466f9eb685cdd3c7"/>
   <project name="device/sample" path="device/sample" revision="68b1cb978a20806176123b959cb05d4fa8adaea4"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="b2g/ics_strawberry" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="1a2a32eda22ef2cd18f57f423a5e7b22a105a6f8"/>
--- 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="0e94c080bee081a50aa2097527b0b40852f9143f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="9025e50b9d29b3cabbbb21e1dd94d0d13121a17e"/>
   <project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="b89fda71fcd0fa0cf969310e75be3ea33e048b44"/>
   <project groups="linux,arm" name="platform/prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" path="prebuilts/gcc/linux-x86/arm/arm-eabi-4.7" revision="2e7d5348f35575870b3c7e567a9a9f6d66f8d6c5"/>
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -12,22 +12,22 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="ics_chocolate_rb4.2" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="df362ace56338da8173d30d3e09e08c42c1accfa">
     <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="579e01ad4d6e4177a8f636305ac877835d99f134"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="5e98dc164b17fd6decb48a9eaddef0e55b82e249"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="320dad6b787c296ba701bca8377f71ab5fd72ee8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="e7c3ee43c96a0079d7e4f3fe471149293225917b"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="fe91ec3af5396edab45b15e546e21613785724b5"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform/bootable/recovery" path="bootable/recovery" revision="e0a9ac010df3afaa47ba107192c05ac8b5516435"/>
   <project name="platform/development" path="development" revision="a384622f5fcb1d2bebb9102591ff7ae91fe8ed2d"/>
   <project name="device/common" path="device/common" revision="7c65ea240157763b8ded6154a17d3c033167afb7"/>
   <project name="device/sample" path="device/sample" revision="c328f3d4409db801628861baa8d279fb8855892f"/>
--- a/browser/app/blocklist.xml
+++ b/browser/app/blocklist.xml
@@ -1,10 +1,10 @@
 <?xml version="1.0"?>
-<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1419896968000">
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist" lastupdate="1421170069000">
   <emItems>
       <emItem  blockID="i58" id="webmaster@buzzzzvideos.info">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i71" id="youtube@2youtube.com">
@@ -861,16 +861,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i286" id="{58bd07eb-0ee0-4df0-8121-dc9b693373df}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i816" id="noOpus@outlook.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i528" id="008abed2-b43a-46c9-9a5b-a771c87b82da@1ad61d53-2bdc-4484-a26b-b888ecae1906.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i540" id="/^(ffxtlbr@mixidj\.com|{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}|{67097627-fd8e-4f6b-af4b-ecb65e50112e}|{f6f0f973-a4a3-48cf-9a7a-b7a69c30d71a}|{a3d0e35f-f1da-4ccb-ae77-e9d27777e68d}|{1122b43d-30ee-403f-9bfa-3cc99b0caddd})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
@@ -1007,22 +1013,16 @@
               </prefs>
     </emItem>
       <emItem  blockID="i55" id="youtube@youtube7.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i497" id="{872b5b88-9db5-4310-bdd0-ac189557e5f5}">
-                        <versionRange  minVersion="0" maxVersion="*" severity="1">
-                    </versionRange>
-                    <prefs>
-              </prefs>
-    </emItem>
       <emItem  blockID="i76" id="crossriderapp3924@crossrider.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i82" id="{8f42fb8b-b6f6-45de-81c0-d6d39f54f971}">
                         <versionRange  minVersion="0" maxVersion="*">
@@ -1107,16 +1107,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i674" id="crossriderapp12555@crossrider.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i812" id="{1e4ea5fc-09e5-4f45-a43b-c048304899fc}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i710" id="{e0352044-1439-48ba-99b6-b05ed1a4d2de}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i20" id="{AB2CE124-6272-4b12-94A9-7303C7397BD1}">
                         <versionRange  minVersion="0.1" maxVersion="5.2.0.7164" severity="1">
@@ -1155,17 +1161,17 @@
               </prefs>
     </emItem>
       <emItem  blockID="i754" id="{bb7b7a60-f574-47c2-8a0b-4c56f2da9802}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i354" id="{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}">
+      <emItem  blockID="i497" id="{872b5b88-9db5-4310-bdd0-ac189557e5f5}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i499" id="{babb9931-ad56-444c-b935-38bffe18ad26}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
@@ -1344,16 +1350,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i115" id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i820" id="{aab02ab1-33cf-4dfa-8a9f-f4e60e976d27}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i692" id="/^(j003-lqgrmgpcekslhg|SupraSavings|j003-dkqonnnthqjnkq|j003-kaggrpmirxjpzh)@jetpack$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i525" id="/^({65f9f6b7-2dae-46fc-bfaf-f88e4af1beca}|{9ed31f84-c8b3-4926-b950-dff74047ff79}|{0134af61-7a0c-4649-aeca-90d776060cb3}|{02edb56b-9b33-435b-b7df-b2843273a694}|{da51d4f6-3e7e-4ef8-b400-9198e0874606}|{b24577db-155e-4077-bb37-3fdd3c302bb5})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -1482,16 +1494,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i518" id="/^({d6e79525-4524-4707-9b97-1d70df8e7e59}|{ddb4644d-1a37-4e6d-8b6e-8e35e2a8ea6c}|{e55007f4-80c5-418e-ac33-10c4d60db01e}|{e77d8ca6-3a60-4ae9-8461-53b22fa3125b}|{e89a62b7-248e-492f-9715-43bf8c507a2f}|{5ce3e0cb-aa83-45cb-a7da-a2684f05b8f3})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i354" id="{c0c2693d-2ee8-47b4-9df7-b67a0ee31988}">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i726" id="{d87d56b2-1379-49f4-b081-af2850c79d8e}">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i505" id="extacylife@a.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
@@ -1818,22 +1836,20 @@
               </prefs>
     </emItem>
       <emItem  blockID="i42" id="{D19CA586-DD6C-4a0a-96F8-14644F340D60}">
                         <versionRange  minVersion="0.1" maxVersion="14.4.0" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
-      <emItem  blockID="i760" id="toolbar11093@freshy.com">
-                        <versionRange  minVersion="0" maxVersion="*" severity="1">
-                    </versionRange>
-                    <prefs>
-                  <pref>browser.startup.homepage</pref>
-                  <pref>browser.search.defaultenginename</pref>
+      <emItem  blockID="i818" id="contentarget@maildrop.cc">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i142" id="{a3a5c777-f583-4fef-9380-ab4add1bc2a8}">
                         <versionRange  minVersion="2.0.3" maxVersion="2.0.3">
                     </versionRange>
                                 <versionRange  minVersion="4.2" maxVersion="4.2" severity="3">
                     </versionRange>
                     <prefs>
@@ -2058,16 +2074,22 @@
               </prefs>
     </emItem>
       <emItem  blockID="i91" id="crossriderapp4926@crossrider.com">
                         <versionRange  minVersion="0" maxVersion="0.81.43" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i814" id="liiros@facebook.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="3">
+                    </versionRange>
+                    <prefs>
+              </prefs>
+    </emItem>
       <emItem  blockID="i67" id="youtube2@youtube2.com">
                         <versionRange  minVersion="0" maxVersion="*">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i520" id="/^({7316e43a-3ebd-4bb4-95c1-9caf6756c97f}|{0cc09160-108c-4759-bab1-5c12c216e005}|{ef03e721-f564-4333-a331-d4062cee6f2b}|{465fcfbb-47a4-4866-a5d5-d12f9a77da00}|{7557724b-30a9-42a4-98eb-77fcb0fd1be3}|{b7c7d4b0-7a84-4b73-a7ef-48ef59a52c3b})$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
@@ -2163,16 +2185,24 @@
               </prefs>
     </emItem>
       <emItem  blockID="i312" id="extension21804@extension21804.com">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
+      <emItem  blockID="i760" id="toolbar11093@freshy.com">
+                        <versionRange  minVersion="0" maxVersion="*" severity="1">
+                    </versionRange>
+                    <prefs>
+                  <pref>browser.startup.homepage</pref>
+                  <pref>browser.search.defaultenginename</pref>
+              </prefs>
+    </emItem>
       <emItem  blockID="i324" id="/^((34qEOefiyYtRJT@IM5Munavn\.com)|(Mro5Fm1Qgrmq7B@ByrE69VQfZvZdeg\.com)|(KtoY3KGxrCe5ie@yITPUzbBtsHWeCdPmGe\.com)|(9NgIdLK5Dq4ZMwmRo6zk@FNt2GCCLGyUuOD\.com)|(NNux7bWWW@RBWyXdnl6VGls3WAwi\.com)|(E3wI2n@PEHTuuNVu\.com)|(2d3VuWrG6JHBXbQdbr@3BmSnQL\.com))$/">
                         <versionRange  minVersion="0" maxVersion="*" severity="3">
                     </versionRange>
                     <prefs>
               </prefs>
     </emItem>
       <emItem  blockID="i756" id="{5eeb83d0-96ea-4249-942c-beead6847053}">
                         <versionRange  minVersion="0" maxVersion="*" severity="1">
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1417,16 +1417,19 @@ pref("devtools.timeline.hiddenMarkers", 
 
 pref("devtools.performance.ui.show-timeline-memory", false);
 
 // The default Profiler UI settings
 pref("devtools.profiler.ui.flatten-tree-recursion", true);
 pref("devtools.profiler.ui.show-platform-data", false);
 pref("devtools.profiler.ui.show-idle-blocks", true);
 
+// The default Performance UI settings
+pref("devtools.performance.ui.invert-call-tree", true);
+
 // The default cache UI setting
 pref("devtools.cache.disabled", false);
 
 // Enable the Network Monitor
 pref("devtools.netmonitor.enabled", true);
 
 // The default Network Monitor UI settings
 pref("devtools.netmonitor.panes-network-details-width", 550);
--- a/browser/base/content/browser-ctrlTab.js
+++ b/browser/base/content/browser-ctrlTab.js
@@ -179,22 +179,30 @@ var ctrlTab = {
       keys[key] = document.getElementById("key_" + key)
                           .getAttribute("key")
                           .toLocaleLowerCase().charCodeAt(0);
     });
     delete this.keys;
     return this.keys = keys;
   },
   _selectedIndex: 0,
-  get selected () this._selectedIndex < 0 ?
-                    document.activeElement :
-                    this.previews.item(this._selectedIndex),
-  get isOpen   () this.panel.state == "open" || this.panel.state == "showing" || this._timer,
-  get tabCount () this.tabList.length,
-  get tabPreviewCount () Math.min(this.maxTabPreviews, this.tabCount),
+  get selected () {
+    return this._selectedIndex < 0 ?
+             document.activeElement :
+             this.previews.item(this._selectedIndex);
+  },
+  get isOpen () {
+    return this.panel.state == "open" || this.panel.state == "showing" || this._timer;
+  },
+  get tabCount () {
+    return this.tabList.length;
+  },
+  get tabPreviewCount () {
+    return Math.min(this.maxTabPreviews, this.tabCount);
+  },
 
   get tabList () {
     return this._recentlyUsedTabs;
   },
 
   init: function ctrlTab_init() {
     if (!this._recentlyUsedTabs) {
       tabPreviews.init();
--- a/browser/components/loop/content/js/conversationViews.js
+++ b/browser/components/loop/content/js/conversationViews.js
@@ -6,16 +6,18 @@
 
 /* global loop:true, React */
 
 var loop = loop || {};
 loop.conversationViews = (function(mozL10n) {
 
   var CALL_STATES = loop.store.CALL_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
+  var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
+  var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var sharedViews = loop.shared.views;
   var sharedMixins = loop.shared.mixins;
   var sharedModels = loop.shared.models;
 
   // This duplicates a similar function in contacts.jsx that isn't used in the
   // conversation window. If we get too many of these, we might want to consider
@@ -765,18 +767,18 @@ loop.conversationViews = (function(mozL1
       }
       return React.createElement("p", {className: "error"}, mozL10n.get("unable_retrieve_url"));
     },
 
     _getTitleMessage: function() {
       var callStateReason =
         this.props.store.getStoreState("callStateReason");
 
-      if (callStateReason === "reject" || callStateReason === "busy" ||
-          callStateReason === "user-unknown") {
+      if (callStateReason === WEBSOCKET_REASONS.REJECT || callStateReason === WEBSOCKET_REASONS.BUSY ||
+          callStateReason === REST_ERRNOS.USER_UNAVAILABLE) {
         var contactDisplayName = _getContactDisplayName(this.props.contact);
         if (contactDisplayName.length) {
           return mozL10n.get(
             "contact_unavailable_title",
             {"contactName": contactDisplayName});
         }
 
         return mozL10n.get("generic_contact_unavailable_title");
--- a/browser/components/loop/content/js/conversationViews.jsx
+++ b/browser/components/loop/content/js/conversationViews.jsx
@@ -6,16 +6,18 @@
 
 /* global loop:true, React */
 
 var loop = loop || {};
 loop.conversationViews = (function(mozL10n) {
 
   var CALL_STATES = loop.store.CALL_STATES;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
+  var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
+  var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var sharedViews = loop.shared.views;
   var sharedMixins = loop.shared.mixins;
   var sharedModels = loop.shared.models;
 
   // This duplicates a similar function in contacts.jsx that isn't used in the
   // conversation window. If we get too many of these, we might want to consider
@@ -765,18 +767,18 @@ loop.conversationViews = (function(mozL1
       }
       return <p className="error">{mozL10n.get("unable_retrieve_url")}</p>;
     },
 
     _getTitleMessage: function() {
       var callStateReason =
         this.props.store.getStoreState("callStateReason");
 
-      if (callStateReason === "reject" || callStateReason === "busy" ||
-          callStateReason === "user-unknown") {
+      if (callStateReason === WEBSOCKET_REASONS.REJECT || callStateReason === WEBSOCKET_REASONS.BUSY ||
+          callStateReason === REST_ERRNOS.USER_UNAVAILABLE) {
         var contactDisplayName = _getContactDisplayName(this.props.contact);
         if (contactDisplayName.length) {
           return mozL10n.get(
             "contact_unavailable_title",
             {"contactName": contactDisplayName});
         }
 
         return mozL10n.get("generic_contact_unavailable_title");
--- a/browser/components/loop/content/shared/js/activeRoomStore.js
+++ b/browser/components/loop/content/shared/js/activeRoomStore.js
@@ -6,25 +6,21 @@
 
 var loop = loop || {};
 loop.store = loop.store || {};
 
 loop.store.ActiveRoomStore = (function() {
   "use strict";
 
   var sharedActions = loop.shared.actions;
-  var FAILURE_REASONS = loop.shared.utils.FAILURE_REASONS;
+  var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
 
   // Error numbers taken from
   // https://github.com/mozilla-services/loop-server/blob/master/loop/errno.json
-  var SERVER_CODES = loop.store.SERVER_CODES = {
-    INVALID_TOKEN: 105,
-    EXPIRED: 111,
-    ROOM_FULL: 202
-  };
+  var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
 
   var ROOM_STATES = loop.store.ROOM_STATES;
   /**
    * Active room store.
    *
    * @param {loop.Dispatcher} dispatcher  The dispatcher for dispatching actions
    *                                      and registering to consume actions.
    * @param {Object} options Options object:
@@ -79,33 +75,33 @@ loop.store.ActiveRoomStore = (function()
     /**
      * Handles a room failure.
      *
      * @param {sharedActions.RoomFailure} actionData
      */
     roomFailure: function(actionData) {
       function getReason(serverCode) {
         switch (serverCode) {
-          case SERVER_CODES.INVALID_TOKEN:
-          case SERVER_CODES.EXPIRED:
-            return FAILURE_REASONS.EXPIRED_OR_INVALID;
+          case REST_ERRNOS.INVALID_TOKEN:
+          case REST_ERRNOS.EXPIRED:
+            return FAILURE_DETAILS.EXPIRED_OR_INVALID;
           default:
-            return FAILURE_REASONS.UNKNOWN;
+            return FAILURE_DETAILS.UNKNOWN;
         }
       }
 
       console.error("Error in state `" + this._storeState.roomState + "`:",
         actionData.error);
 
       this.setStoreState({
         error: actionData.error,
         failureReason: getReason(actionData.error.errno)
       });
 
-      this._leaveRoom(actionData.error.errno === SERVER_CODES.ROOM_FULL ?
+      this._leaveRoom(actionData.error.errno === REST_ERRNOS.ROOM_FULL ?
           ROOM_STATES.FULL : ROOM_STATES.FAILED);
     },
 
     /**
      * Registers the actions with the dispatcher that this store is interested
      * in after the initial setup has been performed.
      */
     _registerPostSetupActions: function() {
--- a/browser/components/loop/content/shared/js/conversationStore.js
+++ b/browser/components/loop/content/shared/js/conversationStore.js
@@ -6,16 +6,17 @@
 
 var loop = loop || {};
 loop.store = loop.store || {};
 
 (function() {
   var sharedActions = loop.shared.actions;
   var CALL_TYPES = loop.shared.utils.CALL_TYPES;
 
+  var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   /**
    * Websocket states taken from:
    * https://docs.services.mozilla.com/loop/apis.html#call-progress-state-change-progress
    */
   var WS_STATES = loop.store.WS_STATES = {
     // The call is starting, and the remote party is not yet being alerted.
     INIT: "init",
     // The called party is being alerted.
@@ -370,18 +371,18 @@ loop.store = loop.store || {};
       appendContactValues("tel", true);
 
       this.client.setupOutgoingCall(contactAddresses,
         this.getStoreState("callType"),
         function(err, result) {
           if (err) {
             console.error("Failed to get outgoing call data", err);
             var failureReason = "setup";
-            if (err.errno == 122) {
-              failureReason = "user-unknown";
+            if (err.errno == REST_ERRNOS.USER_UNAVAILABLE) {
+              failureReason = REST_ERRNOS.USER_UNAVAILABLE;
             }
             this.dispatcher.dispatch(
               new sharedActions.ConnectionFailure({reason: failureReason}));
             return;
           }
 
           // Success, dispatch a new action.
           this.dispatcher.dispatch(
--- a/browser/components/loop/content/shared/js/otSdkDriver.js
+++ b/browser/components/loop/content/shared/js/otSdkDriver.js
@@ -3,17 +3,17 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* global loop:true */
 
 var loop = loop || {};
 loop.OTSdkDriver = (function() {
 
   var sharedActions = loop.shared.actions;
-  var FAILURE_REASONS = loop.shared.utils.FAILURE_REASONS;
+  var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
 
   /**
    * This is a wrapper for the OT sdk. It is used to translate the SDK events into
    * actions, and instruct the SDK what to do as a result of actions.
    */
   var OTSdkDriver = function(options) {
       if (!options.dispatcher) {
         throw new Error("Missing option dispatcher");
@@ -155,17 +155,17 @@ loop.OTSdkDriver = (function() {
      * Called once the session has finished connecting.
      *
      * @param {Error} error An OT error object, null if there was no error.
      */
     _onConnectionComplete: function(error) {
       if (error) {
         console.error("Failed to complete connection", error);
         this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
-          reason: FAILURE_REASONS.COULD_NOT_CONNECT
+          reason: FAILURE_DETAILS.COULD_NOT_CONNECT
         }));
         return;
       }
 
       this.dispatcher.dispatch(new sharedActions.ConnectedToSdkServers());
       this._sessionConnected = true;
       this._maybePublishLocalStream();
     },
@@ -192,20 +192,20 @@ loop.OTSdkDriver = (function() {
      *
      * @param {SessionDisconnectEvent} event The event details:
      * https://tokbox.com/opentok/libraries/client/js/reference/SessionDisconnectEvent.html
      */
     _onSessionDisconnected: function(event) {
       var reason;
       switch (event.reason) {
         case "networkDisconnected":
-          reason = FAILURE_REASONS.NETWORK_DISCONNECTED;
+          reason = FAILURE_DETAILS.NETWORK_DISCONNECTED;
           break;
         case "forceDisconnected":
-          reason = FAILURE_REASONS.EXPIRED_OR_INVALID;
+          reason = FAILURE_DETAILS.EXPIRED_OR_INVALID;
           break;
         default:
           // Other cases don't need to be handled.
           return;
       }
 
       this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
         reason: reason
@@ -273,17 +273,17 @@ loop.OTSdkDriver = (function() {
      *
      * @param {OT.Event} event
      */
     _onPublishDenied: function(event) {
       // This prevents the SDK's "access denied" dialog showing.
       event.preventDefault();
 
       this.dispatcher.dispatch(new sharedActions.ConnectionFailure({
-        reason: FAILURE_REASONS.MEDIA_DENIED
+        reason: FAILURE_DETAILS.MEDIA_DENIED
       }));
     },
 
     /**
      * Publishes the local stream if the session is connected
      * and the publisher is ready.
      */
     _maybePublishLocalStream: function() {
--- a/browser/components/loop/content/shared/js/utils.js
+++ b/browser/components/loop/content/shared/js/utils.js
@@ -12,17 +12,34 @@ loop.shared.utils = (function(mozL10n) {
   /**
    * Call types used for determining if a call is audio/video or audio-only.
    */
   var CALL_TYPES = {
     AUDIO_VIDEO: "audio-video",
     AUDIO_ONLY: "audio"
   };
 
-  var FAILURE_REASONS = {
+  var REST_ERRNOS = {
+    INVALID_TOKEN: 105,
+    EXPIRED: 111,
+    USER_UNAVAILABLE: 122,
+    ROOM_FULL: 202
+  };
+
+  var WEBSOCKET_REASONS = {
+    ANSWERED_ELSEWHERE: "answered-elsewhere",
+    BUSY: "busy",
+    CANCEL: "cancel",
+    CLOSED: "closed",
+    MEDIA_FAIL: "media-fail",
+    REJECT: "reject",
+    TIMEOUT: "timeout"
+  };
+
+  var FAILURE_DETAILS = {
     MEDIA_DENIED: "reason-media-denied",
     COULD_NOT_CONNECT: "reason-could-not-connect",
     NETWORK_DISCONNECTED: "reason-network-disconnected",
     EXPIRED_OR_INVALID: "reason-expired-or-invalid",
     UNKNOWN: "reason-unknown"
   };
 
   /**
@@ -113,15 +130,17 @@ loop.shared.utils = (function(mozL10n) {
         learnMoreUrl: navigator.mozLoop.getLoopPref("learnMoreUrl")
       }),
       recipient
     );
   }
 
   return {
     CALL_TYPES: CALL_TYPES,
-    FAILURE_REASONS: FAILURE_REASONS,
+    FAILURE_DETAILS: FAILURE_DETAILS,
+    REST_ERRNOS: REST_ERRNOS,
+    WEBSOCKET_REASONS: WEBSOCKET_REASONS,
     Helper: Helper,
     composeCallUrlEmail: composeCallUrlEmail,
     formatDate: formatDate,
     getBoolPreference: getBoolPreference
   };
 })(document.mozL10n || navigator.mozL10n);
--- a/browser/components/loop/content/shared/js/websocket.js
+++ b/browser/components/loop/content/shared/js/websocket.js
@@ -3,16 +3,18 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /* global loop:true */
 
 var loop = loop || {};
 loop.CallConnectionWebSocket = (function() {
   "use strict";
 
+  var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
+
   // Response timeout is 5 seconds as per API.
   var kResponseTimeout = 5000;
 
   /**
    * Handles a websocket specifically for a call connection.
    *
    * There should be one of these created for each call connection.
    *
@@ -61,17 +63,17 @@ loop.CallConnectionWebSocket = (function
           this.socket = new WebSocket(this.options.url);
           this.socket.onopen = this._onopen.bind(this);
           this.socket.onmessage = this._onmessage.bind(this);
           this.socket.onerror = this._onerror.bind(this);
           this.socket.onclose = this._onclose.bind(this);
 
           var timeout = setTimeout(function() {
             if (this.connectDetails && this.connectDetails.reject) {
-              this.connectDetails.reject("timeout");
+              this.connectDetails.reject(WEBSOCKET_REASONS.TIMEOUT);
               this._clearConnectionFlags();
             }
           }.bind(this), kResponseTimeout);
           this.connectDetails = {
             resolve: resolve,
             reject: reject,
             timeout: timeout
           };
@@ -134,17 +136,17 @@ loop.CallConnectionWebSocket = (function
 
     /**
      * Notifies the server that the user has declined the call.
      */
     decline: function() {
       this._send({
         messageType: "action",
         event: "terminate",
-        reason: "reject"
+        reason: WEBSOCKET_REASONS.REJECT
       });
     },
 
     /**
      * Notifies the server that the user has accepted the call.
      */
     accept: function() {
       this._send({
@@ -167,28 +169,28 @@ loop.CallConnectionWebSocket = (function
     /**
      * Notifies the server that the outgoing call is cancelled by the
      * user.
      */
     cancel: function() {
       this._send({
         messageType: "action",
         event: "terminate",
-        reason: "cancel"
+        reason: WEBSOCKET_REASONS.CANCEL
       });
     },
 
     /**
      * Notifies the server that something failed during setup.
      */
     mediaFail: function() {
       this._send({
         messageType: "action",
         event: "terminate",
-        reason: "media-fail"
+        reason: WEBSOCKET_REASONS.MEDIA_FAIL
       });
     },
 
     /**
      * Sends data on the websocket.
      *
      * @param {Object} data The data to send.
      */
@@ -223,36 +225,36 @@ loop.CallConnectionWebSocket = (function
     },
 
     /**
      * Called when a message is received from the server.
      *
      * @param {Object} event The websocket onmessage event.
      */
     _onmessage: function(event) {
-      var msg;
+      var msgData;
       try {
-        msg = JSON.parse(event.data);
+        msgData = JSON.parse(event.data);
       } catch (x) {
         console.error("Error parsing received message:", x);
         return;
       }
 
       this._log("WS Receiving", event.data);
 
       var previousState = this._lastServerState;
-      this._lastServerState = msg.state;
+      this._lastServerState = msgData.state;
 
-      switch(msg.messageType) {
+      switch(msgData.messageType) {
         case "hello":
-          this._completeConnection(msg.state);
+          this._completeConnection(msgData.state);
           break;
         case "progress":
-          this.trigger("progress:" + msg.state);
-          this.trigger("progress", msg, previousState);
+          this.trigger("progress:" + msgData.state);
+          this.trigger("progress", msgData, previousState);
           break;
       }
     },
 
     /**
      * Called when there is an error on the websocket.
      *
      * @param {Object} event A simple error event.
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -6,17 +6,17 @@
 
 /* global loop:true, React */
 /* jshint newcap:false, maxlen:false */
 
 var loop = loop || {};
 loop.standaloneRoomViews = (function(mozL10n) {
   "use strict";
 
-  var FAILURE_REASONS = loop.shared.utils.FAILURE_REASONS;
+  var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
   var sharedViews = loop.shared.views;
 
   var StandaloneRoomInfoArea = React.createClass({displayName: "StandaloneRoomInfoArea",
     propTypes: {
       helper: React.PropTypes.instanceOf(loop.shared.utils.Helper).isRequired,
@@ -54,19 +54,19 @@ loop.standaloneRoomViews = (function(moz
       );
     },
 
     /**
      * @return String An appropriate string according to the failureReason.
      */
     _getFailureString: function() {
       switch(this.props.failureReason) {
-        case FAILURE_REASONS.MEDIA_DENIED:
+        case FAILURE_DETAILS.MEDIA_DENIED:
           return mozL10n.get("rooms_media_denied_message");
-        case FAILURE_REASONS.EXPIRED_OR_INVALID:
+        case FAILURE_DETAILS.EXPIRED_OR_INVALID:
           return mozL10n.get("rooms_unavailable_notification_message");
         default:
           return mozL10n.get("status_error");
       }
     },
 
     render: function() {
       switch(this.props.roomState) {
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -6,17 +6,17 @@
 
 /* global loop:true, React */
 /* jshint newcap:false, maxlen:false */
 
 var loop = loop || {};
 loop.standaloneRoomViews = (function(mozL10n) {
   "use strict";
 
-  var FAILURE_REASONS = loop.shared.utils.FAILURE_REASONS;
+  var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var ROOM_STATES = loop.store.ROOM_STATES;
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
   var sharedViews = loop.shared.views;
 
   var StandaloneRoomInfoArea = React.createClass({
     propTypes: {
       helper: React.PropTypes.instanceOf(loop.shared.utils.Helper).isRequired,
@@ -54,19 +54,19 @@ loop.standaloneRoomViews = (function(moz
       );
     },
 
     /**
      * @return String An appropriate string according to the failureReason.
      */
     _getFailureString: function() {
       switch(this.props.failureReason) {
-        case FAILURE_REASONS.MEDIA_DENIED:
+        case FAILURE_DETAILS.MEDIA_DENIED:
           return mozL10n.get("rooms_media_denied_message");
-        case FAILURE_REASONS.EXPIRED_OR_INVALID:
+        case FAILURE_DETAILS.EXPIRED_OR_INVALID:
           return mozL10n.get("rooms_unavailable_notification_message");
         default:
           return mozL10n.get("status_error");
       }
     },
 
     render: function() {
       switch(this.props.roomState) {
--- a/browser/components/loop/standalone/content/js/webapp.jsx
+++ b/browser/components/loop/standalone/content/js/webapp.jsx
@@ -14,16 +14,17 @@ loop.webapp = (function($, _, OT, mozL10
   loop.config = loop.config || {};
   loop.config.serverUrl = loop.config.serverUrl || "http://localhost:5000";
 
   var sharedActions = loop.shared.actions;
   var sharedMixins = loop.shared.mixins;
   var sharedModels = loop.shared.models;
   var sharedViews = loop.shared.views;
   var sharedUtils = loop.shared.utils;
+  var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
 
   var multiplexGum = loop.standaloneMedia.multiplexGum;
 
   /**
    * Homepage view.
    */
   var HomeView = React.createClass({
     render: function() {
@@ -887,17 +888,17 @@ loop.webapp = (function($, _, OT, mozL10
      * Handles call rejection.
      *
      * @param {String} reason The reason the call was terminated (reject, busy,
      *                        timeout, cancel, media-fail, user-unknown, closed)
      */
     _handleCallTerminated: function(reason) {
       multiplexGum.reset();
 
-      if (reason === "cancel") {
+      if (reason === WEBSOCKET_REASONS.CANCEL) {
         this.setState({callStatus: "start"});
         return;
       }
       // XXX later, we'll want to display more meaningfull messages (needs UX)
       this.props.notifications.errorL10n("call_timeout_notification_text");
       this.setState({callStatus: "failure"});
     },
 
--- a/browser/components/loop/test/desktop-local/conversationViews_test.js
+++ b/browser/components/loop/test/desktop-local/conversationViews_test.js
@@ -7,16 +7,18 @@ describe("loop.conversationViews", funct
   "use strict";
 
   var sharedUtils = loop.shared.utils;
   var sharedView = loop.shared.views;
   var sandbox, oldTitle, view, dispatcher, contact, fakeAudioXHR;
   var fakeMozLoop, fakeWindow;
 
   var CALL_STATES = loop.store.CALL_STATES;
+  var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
+  var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
 
   // XXX refactor to Just Work with "sandbox.stubComponent" or else
   // just pass in the sandbox and put somewhere generally usable
 
   function stubComponent(obj, component, mockTagName){
     var reactClass = React.createClass({
       render: function() {
         var mockTagName = mockTagName || "div";
@@ -410,39 +412,39 @@ describe("loop.conversationViews", funct
 
       sinon.assert.calledOnce(navigator.mozLoop.getAudioBlob);
       sinon.assert.calledWithExactly(navigator.mozLoop.getAudioBlob,
                                      "failure", sinon.match.func);
       sinon.assert.calledOnce(fakeAudio.play);
       expect(fakeAudio.loop).to.equal(false);
     });
 
-    it("should show 'something went wrong' when the reason is 'media-fail'",
+    it("should show 'something went wrong' when the reason is WEBSOCKET_REASONS.MEDIA_FAIL",
       function () {
-        store.setStoreState({callStateReason: "media-fail"});
+        store.setStoreState({callStateReason: WEBSOCKET_REASONS.MEDIA_FAIL});
 
         view = mountTestComponent({contact: contact});
 
         sinon.assert.calledWith(document.mozL10n.get, "generic_failure_title");
       });
 
-    it("should show 'contact unavailable' when the reason is 'reject'",
+    it("should show 'contact unavailable' when the reason is WEBSOCKET_REASONS.REJECT",
       function () {
-        store.setStoreState({callStateReason: "reject"});
+        store.setStoreState({callStateReason: WEBSOCKET_REASONS.REJECT});
 
         view = mountTestComponent({contact: contact});
 
         sinon.assert.calledWithExactly(document.mozL10n.get,
           "contact_unavailable_title",
           {contactName: loop.conversationViews._getContactDisplayName(contact)});
       });
 
-    it("should show 'contact unavailable' when the reason is 'busy'",
+    it("should show 'contact unavailable' when the reason is WEBSOCKET_REASONS.BUSY",
       function () {
-        store.setStoreState({callStateReason: "busy"});
+        store.setStoreState({callStateReason: WEBSOCKET_REASONS.BUSY});
 
         view = mountTestComponent({contact: contact});
 
         sinon.assert.calledWithExactly(document.mozL10n.get,
           "contact_unavailable_title",
           {contactName: loop.conversationViews._getContactDisplayName(contact)});
       });
 
@@ -451,30 +453,30 @@ describe("loop.conversationViews", funct
         store.setStoreState({callStateReason: "setup"});
 
         view = mountTestComponent({contact: contact});
 
         sinon.assert.calledWithExactly(document.mozL10n.get,
           "generic_failure_title");
       });
 
-    it("should show 'contact unavailable' when the reason is 'user-unknown'",
+    it("should show 'contact unavailable' when the reason is REST_ERRNOS.USER_UNAVAILABLE",
       function () {
-        store.setStoreState({callStateReason: "user-unknown"});
+        store.setStoreState({callStateReason: REST_ERRNOS.USER_UNAVAILABLE});
 
         view = mountTestComponent({contact: contact});
 
         sinon.assert.calledWithExactly(document.mozL10n.get,
           "contact_unavailable_title",
           {contactName: loop.conversationViews._getContactDisplayName(contact)});
       });
 
     it("should display a generic contact unavailable msg when the reason is" +
-       " 'busy' and no display name is available", function() {
-        store.setStoreState({callStateReason: "busy"});
+       " WEBSOCKET_REASONS.BUSY and no display name is available", function() {
+        store.setStoreState({callStateReason: WEBSOCKET_REASONS.BUSY});
         var phoneOnlyContact = {
           tel: [{"pref": true, type: "work", value: ""}]
         };
 
         view = mountTestComponent({contact: phoneOnlyContact});
 
         sinon.assert.calledWith(document.mozL10n.get,
           "generic_contact_unavailable_title");
@@ -853,41 +855,41 @@ describe("loop.conversationViews", funct
             sandbox.stub(loop.CallConnectionWebSocket.prototype, "close");
           });
 
           describe("progress - terminated (previousState = alerting)", function() {
             it("should stop alerting", function(done) {
               promise.then(function() {
                 icView._websocket.trigger("progress", {
                   state: "terminated",
-                  reason: "timeout"
+                  reason: WEBSOCKET_REASONS.TIMEOUT
                 }, "alerting");
 
                 sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
                 done();
               });
             });
 
             it("should close the websocket", function(done) {
               promise.then(function() {
                 icView._websocket.trigger("progress", {
                   state: "terminated",
-                  reason: "closed"
+                  reason: WEBSOCKET_REASONS.CLOSED
                 }, "alerting");
 
                 sinon.assert.calledOnce(icView._websocket.close);
                 done();
               });
             });
 
             it("should close the window", function(done) {
               promise.then(function() {
                 icView._websocket.trigger("progress", {
                   state: "terminated",
-                  reason: "answered-elsewhere"
+                  reason: WEBSOCKET_REASONS.ANSWERED_ELSEWHERE
                 }, "alerting");
 
                 sandbox.clock.tick(1);
 
                 sinon.assert.calledOnce(fakeWindow.close);
                 done();
               });
             });
@@ -896,29 +898,29 @@ describe("loop.conversationViews", funct
 
           describe("progress - terminated (previousState not init" +
                    " nor alerting)",
             function() {
               it("should set the state to end", function(done) {
                 promise.then(function() {
                   icView._websocket.trigger("progress", {
                     state: "terminated",
-                    reason: "media-fail"
+                    reason: WEBSOCKET_REASONS.MEDIA_FAIL
                   }, "connecting");
 
                   expect(icView.state.callStatus).eql("end");
                   done();
                 });
               });
 
               it("should stop alerting", function(done) {
                 promise.then(function() {
                   icView._websocket.trigger("progress", {
                     state: "terminated",
-                    reason: "media-fail"
+                    reason: WEBSOCKET_REASONS.MEDIA_FAIL
                   }, "connecting");
 
                   sinon.assert.calledOnce(navigator.mozLoop.stopAlerting);
                   done();
                 });
               });
             });
         });
--- a/browser/components/loop/test/shared/activeRoomStore_test.js
+++ b/browser/components/loop/test/shared/activeRoomStore_test.js
@@ -1,19 +1,19 @@
 /* global chai, loop */
 
 var expect = chai.expect;
 var sharedActions = loop.shared.actions;
 
 describe("loop.store.ActiveRoomStore", function () {
   "use strict";
 
-  var SERVER_CODES = loop.store.SERVER_CODES;
+  var REST_ERRNOS = loop.shared.utils.REST_ERRNOS;
   var ROOM_STATES = loop.store.ROOM_STATES;
-  var FAILURE_REASONS = loop.shared.utils.FAILURE_REASONS;
+  var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var sandbox, dispatcher, store, fakeMozLoop, fakeSdkDriver;
   var fakeMultiplexGum;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     sandbox.useFakeTimers();
 
     dispatcher = new loop.Dispatcher();
@@ -89,48 +89,48 @@ describe("loop.store.ActiveRoomStore", f
       store.roomFailure({error: fakeError});
 
       sinon.assert.calledOnce(console.error);
       sinon.assert.calledWith(console.error,
         sinon.match(ROOM_STATES.JOINED), fakeError);
     });
 
     it("should set the state to `FULL` on server error room full", function() {
-      fakeError.errno = SERVER_CODES.ROOM_FULL;
+      fakeError.errno = REST_ERRNOS.ROOM_FULL;
 
       store.roomFailure({error: fakeError});
 
       expect(store._storeState.roomState).eql(ROOM_STATES.FULL);
     });
 
     it("should set the state to `FAILED` on generic error", function() {
       store.roomFailure({error: fakeError});
 
       expect(store._storeState.roomState).eql(ROOM_STATES.FAILED);
-      expect(store._storeState.failureReason).eql(FAILURE_REASONS.UNKNOWN);
+      expect(store._storeState.failureReason).eql(FAILURE_DETAILS.UNKNOWN);
     });
 
     it("should set the failureReason to EXPIRED_OR_INVALID on server error: " +
       "invalid token", function() {
-        fakeError.errno = SERVER_CODES.INVALID_TOKEN;
+        fakeError.errno = REST_ERRNOS.INVALID_TOKEN;
 
         store.roomFailure({error: fakeError});
 
         expect(store._storeState.roomState).eql(ROOM_STATES.FAILED);
-        expect(store._storeState.failureReason).eql(FAILURE_REASONS.EXPIRED_OR_INVALID);
+        expect(store._storeState.failureReason).eql(FAILURE_DETAILS.EXPIRED_OR_INVALID);
       });
 
     it("should set the failureReason to EXPIRED_OR_INVALID on server error: " +
       "expired", function() {
-        fakeError.errno = SERVER_CODES.EXPIRED;
+        fakeError.errno = REST_ERRNOS.EXPIRED;
 
         store.roomFailure({error: fakeError});
 
         expect(store._storeState.roomState).eql(ROOM_STATES.FAILED);
-        expect(store._storeState.failureReason).eql(FAILURE_REASONS.EXPIRED_OR_INVALID);
+        expect(store._storeState.failureReason).eql(FAILURE_DETAILS.EXPIRED_OR_INVALID);
       });
 
     it("should reset the multiplexGum", function() {
       store.roomFailure({error: fakeError});
 
       sinon.assert.calledOnce(fakeMultiplexGum.reset);
     });
 
--- a/browser/components/loop/test/shared/conversationStore_test.js
+++ b/browser/components/loop/test/shared/conversationStore_test.js
@@ -3,16 +3,17 @@
 
 var expect = chai.expect;
 
 describe("loop.store.ConversationStore", function () {
   "use strict";
 
   var CALL_STATES = loop.store.CALL_STATES;
   var WS_STATES = loop.store.WS_STATES;
+  var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
   var sharedActions = loop.shared.actions;
   var sharedUtils = loop.shared.utils;
   var sandbox, dispatcher, client, store, fakeSessionData, sdkDriver;
   var contact, fakeMozLoop;
   var connectPromise, resolveConnectPromise, rejectConnectPromise;
   var wsCancelSpy, wsCloseSpy, wsMediaUpSpy, fakeWebsocket;
 
   function checkFailures(done, f) {
@@ -754,25 +755,25 @@ describe("loop.store.ConversationStore",
           new sharedActions.ConnectCall({sessionData: fakeSessionData}));
 
         sandbox.stub(dispatcher, "dispatch");
       });
 
       it("should dispatch a connection failure action on 'terminate'", function() {
         store._websocket.trigger("progress", {
           state: WS_STATES.TERMINATED,
-          reason: "reject"
+          reason: WEBSOCKET_REASONS.REJECT
         });
 
         sinon.assert.calledOnce(dispatcher.dispatch);
         // Can't use instanceof here, as that matches any action
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("name", "connectionFailure"));
         sinon.assert.calledWithMatch(dispatcher.dispatch,
-          sinon.match.hasOwn("reason", "reject"));
+          sinon.match.hasOwn("reason", WEBSOCKET_REASONS.REJECT));
       });
 
       it("should dispatch a connection progress action on 'alerting'", function() {
         store._websocket.trigger("progress", {state: WS_STATES.ALERTING});
 
         sinon.assert.calledOnce(dispatcher.dispatch);
         // Can't use instanceof here, as that matches any action
         sinon.assert.calledWithMatch(dispatcher.dispatch,
--- a/browser/components/loop/test/shared/otSdkDriver_test.js
+++ b/browser/components/loop/test/shared/otSdkDriver_test.js
@@ -2,17 +2,17 @@
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var expect = chai.expect;
 
 describe("loop.OTSdkDriver", function () {
   "use strict";
 
   var sharedActions = loop.shared.actions;
-  var FAILURE_REASONS = loop.shared.utils.FAILURE_REASONS;
+  var FAILURE_DETAILS = loop.shared.utils.FAILURE_DETAILS;
   var sandbox;
   var dispatcher, driver, publisher, sdk, session, sessionData;
   var fakeLocalElement, fakeRemoteElement, publisherConfig, fakeEvent;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
 
     fakeLocalElement = {fake: 1};
@@ -148,17 +148,17 @@ describe("loop.OTSdkDriver", function ()
         sandbox.stub(dispatcher, "dispatch");
 
         driver.connectSession(sessionData);
 
         sinon.assert.calledOnce(dispatcher.dispatch);
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("name", "connectionFailure"));
         sinon.assert.calledWithMatch(dispatcher.dispatch,
-          sinon.match.hasOwn("reason", FAILURE_REASONS.COULD_NOT_CONNECT));
+          sinon.match.hasOwn("reason", FAILURE_DETAILS.COULD_NOT_CONNECT));
       });
     });
   });
 
   describe("#disconnectionSession", function() {
     it("should disconnect the session", function() {
       driver.session = session;
 
@@ -260,30 +260,30 @@ describe("loop.OTSdkDriver", function ()
           session.trigger("sessionDisconnected", {
             reason: "networkDisconnected"
           });
 
           sinon.assert.calledOnce(dispatcher.dispatch);
           sinon.assert.calledWithMatch(dispatcher.dispatch,
             sinon.match.hasOwn("name", "connectionFailure"));
           sinon.assert.calledWithMatch(dispatcher.dispatch,
-            sinon.match.hasOwn("reason", FAILURE_REASONS.NETWORK_DISCONNECTED));
+            sinon.match.hasOwn("reason", FAILURE_DETAILS.NETWORK_DISCONNECTED));
         });
 
       it("should dispatch a connectionFailure action if the session was " +
          "forcibly disconnected", function() {
           session.trigger("sessionDisconnected", {
             reason: "forceDisconnected"
           });
 
           sinon.assert.calledOnce(dispatcher.dispatch);
           sinon.assert.calledWithMatch(dispatcher.dispatch,
             sinon.match.hasOwn("name", "connectionFailure"));
           sinon.assert.calledWithMatch(dispatcher.dispatch,
-            sinon.match.hasOwn("reason", FAILURE_REASONS.EXPIRED_OR_INVALID));
+            sinon.match.hasOwn("reason", FAILURE_DETAILS.EXPIRED_OR_INVALID));
         });
     });
 
     describe("streamCreated", function() {
       var fakeStream;
 
       beforeEach(function() {
         fakeStream = {
@@ -371,17 +371,17 @@ describe("loop.OTSdkDriver", function ()
 
       it("should dispatch connectionFailure", function() {
         publisher.trigger("accessDenied", fakeEvent);
 
         sinon.assert.called(dispatcher.dispatch);
         sinon.assert.calledWithMatch(dispatcher.dispatch,
           sinon.match.hasOwn("name", "connectionFailure"));
         sinon.assert.calledWithMatch(dispatcher.dispatch,
-          sinon.match.hasOwn("reason", FAILURE_REASONS.MEDIA_DENIED));
+          sinon.match.hasOwn("reason", FAILURE_DETAILS.MEDIA_DENIED));
       });
     });
 
     describe("accessDialogOpened", function() {
       it("should prevent the default event behavior", function() {
         publisher.trigger("accessDialogOpened", fakeEvent);
 
         sinon.assert.calledOnce(fakeEvent.preventDefault);
--- a/browser/components/loop/test/shared/websocket_test.js
+++ b/browser/components/loop/test/shared/websocket_test.js
@@ -1,19 +1,22 @@
 /* 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/. */
 
 /*global loop, sinon, it, beforeEach, afterEach, describe */
 
 var expect = chai.expect;
 
+
 describe("loop.CallConnectionWebSocket", function() {
   "use strict";
 
+  var WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
+
   var sandbox,
       dummySocket;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     sandbox.useFakeTimers();
 
     dummySocket = {
@@ -75,17 +78,17 @@ describe("loop.CallConnectionWebSocket",
 
       it("should reject the promise if connection is not completed in " +
          "5 seconds", function(done) {
         var promise = callWebSocket.promiseConnect();
 
         sandbox.clock.tick(5101);
 
         promise.then(function() {}, function(error) {
-          expect(error).to.be.equal("timeout");
+          expect(error).to.be.equal(WEBSOCKET_REASONS.TIMEOUT);
           done();
         });
       });
 
       it("should reject the promise if the connection errors", function(done) {
         var promise = callWebSocket.promiseConnect();
 
         dummySocket.onerror("error");
@@ -152,17 +155,17 @@ describe("loop.CallConnectionWebSocket",
         callWebSocket.promiseConnect();
 
         callWebSocket.decline();
 
         sinon.assert.calledOnce(dummySocket.send);
         sinon.assert.calledWithExactly(dummySocket.send, JSON.stringify({
           messageType: "action",
           event: "terminate",
-          reason: "reject"
+          reason: WEBSOCKET_REASONS.REJECT
         }));
       });
     });
 
     describe("#accept", function() {
       it("should send an accept message to the server", function() {
         callWebSocket.promiseConnect();
 
@@ -186,60 +189,60 @@ describe("loop.CallConnectionWebSocket",
         sinon.assert.calledWithExactly(dummySocket.send, JSON.stringify({
           messageType: "action",
           event: "media-up"
         }));
       });
     });
 
     describe("#cancel", function() {
-      it("should send a terminate message to the server with a reason of cancel",
+      it("should send a terminate message to the server with a reason of WEBSOCKET_REASONS.CANCEL",
         function() {
           callWebSocket.promiseConnect();
 
           callWebSocket.cancel();
 
           sinon.assert.calledOnce(dummySocket.send);
           sinon.assert.calledWithExactly(dummySocket.send, JSON.stringify({
             messageType: "action",
             event: "terminate",
-            reason: "cancel"
+            reason: WEBSOCKET_REASONS.CANCEL
           }));
         });
     });
 
     describe("#mediaFail", function() {
-      it("should send a terminate message to the server with a reason of media-fail",
+      it("should send a terminate message to the server with a reason of WEBSOCKET_REASONS.MEDIA_FAIL",
         function() {
           callWebSocket.promiseConnect();
 
           callWebSocket.mediaFail();
 
           sinon.assert.calledOnce(dummySocket.send);
           sinon.assert.calledWithExactly(dummySocket.send, JSON.stringify({
             messageType: "action",
             event: "terminate",
-            reason: "media-fail"
+            reason: WEBSOCKET_REASONS.MEDIA_FAIL
           }));
         });
     });
 
     describe("Events", function() {
       beforeEach(function() {
         sandbox.stub(callWebSocket, "trigger");
 
         callWebSocket.promiseConnect();
       });
 
       describe("Progress", function() {
         it("should trigger a progress event on the callWebSocket", function() {
           var eventData = {
             messageType: "progress",
             state: "terminate",
-            reason: "reject"
+            reason: WEBSOCKET_REASONS.REJECT
           };
 
           dummySocket.onmessage({
             data: JSON.stringify(eventData)
           });
 
           sinon.assert.called(callWebSocket.trigger);
           sinon.assert.calledWithExactly(callWebSocket.trigger, "progress",
@@ -256,33 +259,33 @@ describe("loop.CallConnectionWebSocket",
           // ready for the main test below.
           dummySocket.onmessage({
             data: JSON.stringify(previousEventData)
           });
 
           var currentEventData = {
             messageType: "progress",
             state: "terminate",
-            reason: "reject"
+            reason: WEBSOCKET_REASONS.REJECT
           };
 
           dummySocket.onmessage({
             data: JSON.stringify(currentEventData)
           });
 
           sinon.assert.called(callWebSocket.trigger);
           sinon.assert.calledWithExactly(callWebSocket.trigger, "progress",
                                          currentEventData, "alerting");
         });
 
         it("should trigger a progress:<state> event on the callWebSocket", function() {
           var eventData = {
             messageType: "progress",
             state: "terminate",
-            reason: "reject"
+            reason: WEBSOCKET_REASONS.REJECT
           };
 
           dummySocket.onmessage({
             data: JSON.stringify(eventData)
           });
 
           sinon.assert.called(callWebSocket.trigger);
           sinon.assert.calledWithExactly(callWebSocket.trigger, "progress:terminate");
--- a/browser/components/loop/test/standalone/webapp_test.js
+++ b/browser/components/loop/test/standalone/webapp_test.js
@@ -15,17 +15,18 @@ describe("loop.webapp", function() {
       sharedViews = loop.shared.views,
       sharedUtils = loop.shared.utils,
       standaloneMedia = loop.standaloneMedia,
       sandbox,
       notifications,
       stubGetPermsAndCacheMedia,
       fakeAudioXHR,
       dispatcher,
-      feedbackStore;
+      feedbackStore,
+      WEBSOCKET_REASONS = loop.shared.utils.WEBSOCKET_REASONS;
 
   beforeEach(function() {
     sandbox = sinon.sandbox.create();
     dispatcher = new loop.Dispatcher();
     notifications = new sharedModels.NotificationCollection();
     feedbackStore = new loop.store.FeedbackStore(dispatcher, {
       feedbackClient: {}
     });
@@ -237,54 +238,54 @@ describe("loop.webapp", function() {
             beforeEach(function() {
               sandbox.stub(notifications, "errorL10n");
               sandbox.stub(window, "XMLHttpRequest").returns(fakeAudioXHR);
             });
 
             it("should display the FailedConversationView", function() {
               ocView._websocket.trigger("progress", {
                 state: "terminated",
-                reason: "reject"
+                reason: WEBSOCKET_REASONS.REJECT
               });
 
               TestUtils.findRenderedComponentWithType(ocView,
                 loop.webapp.FailedConversationView);
             });
 
             it("should reset multiplexGum when a call is rejected",
               function() {
                 var multiplexGum = new standaloneMedia._MultiplexGum();
                 standaloneMedia.setSingleton(multiplexGum);
                 sandbox.stub(standaloneMedia._MultiplexGum.prototype, "reset");
 
                 ocView._websocket.trigger("progress", {
                   state: "terminated",
-                  reason: "reject"
+                  reason: WEBSOCKET_REASONS.REJECT
                 });
 
                 sinon.assert.calledOnce(multiplexGum.reset);
               });
 
-            it("should display an error message if the reason is not 'cancel'",
+            it("should display an error message if the reason is not WEBSOCKET_REASONS.CANCEL",
               function() {
                 ocView._websocket.trigger("progress", {
                   state: "terminated",
-                  reason: "reject"
+                  reason: WEBSOCKET_REASONS.REJECT
                 });
 
                 sinon.assert.calledOnce(notifications.errorL10n);
                 sinon.assert.calledWithExactly(notifications.errorL10n,
                   "call_timeout_notification_text");
               });
 
-            it("should not display an error message if the reason is 'cancel'",
+            it("should not display an error message if the reason is WEBSOCKET_REASONS.CANCEL",
               function() {
                 ocView._websocket.trigger("progress", {
                   state: "terminated",
-                  reason: "cancel"
+                  reason: WEBSOCKET_REASONS.CANCEL
                 });
 
                 sinon.assert.notCalled(notifications.errorL10n);
               });
           });
 
           describe("state: connecting", function() {
             it("should set display the ConversationView", function() {
--- a/browser/devtools/framework/test/browser.ini
+++ b/browser/devtools/framework/test/browser.ini
@@ -5,19 +5,17 @@ support-files =
   browser_toolbox_options_disable_js_iframe.html
   browser_toolbox_options_disable_cache.sjs
   browser_toolbox_sidebar_tool.xul
   head.js
   helper_disable_cache.js
   doc_theme.css
 
 [browser_devtools_api.js]
-skip-if = e10s # Bug 1090340
 [browser_devtools_api_destroy.js]
-skip-if = e10s # Bug 1070837 - devtools/framework/toolbox.js |doc| getter not e10s friendly
 [browser_dynamic_tool_enabling.js]
 [browser_keybindings.js]
 [browser_new_activation_workflow.js]
 [browser_target_events.js]
 [browser_target_remote.js]
 [browser_target_support.js]
 [browser_two_tabs.js]
 [browser_toolbox_dynamic_registration.js]
@@ -27,17 +25,17 @@ skip-if = e10s # Bug 1070837 - devtools/
 [browser_toolbox_options.js]
 [browser_toolbox_options_devedition.js]
 [browser_toolbox_options_disable_buttons.js]
 [browser_toolbox_options_disable_cache-01.js]
 skip-if = e10s # Bug 1030318
 [browser_toolbox_options_disable_cache-02.js]
 skip-if = e10s # Bug 1030318
 [browser_toolbox_options_disable_js.js]
-skip-if = e10s # Bug 1070837 - devtools/framework/toolbox.js |doc| getter not e10s friendly
+skip-if = e10s # Bug 1030318
 # [browser_toolbox_raise.js] # Bug 962258
 # skip-if = os == "win"
 [browser_toolbox_ready.js]
 [browser_toolbox_select_event.js]
 skip-if = e10s # Bug 1069044 - destroyInspector may hang during shutdown
 [browser_toolbox_sidebar.js]
 [browser_toolbox_sidebar_events.js]
 [browser_toolbox_sidebar_existing_tabs.js]
--- a/browser/devtools/jar.mn
+++ b/browser/devtools/jar.mn
@@ -91,16 +91,17 @@ browser.jar:
     content/browser/devtools/profiler.js                               (profiler/profiler.js)
     content/browser/devtools/ui-recordings.js                          (profiler/ui-recordings.js)
     content/browser/devtools/ui-profile.js                             (profiler/ui-profile.js)
 #ifdef MOZ_DEVTOOLS_PERFTOOLS
     content/browser/devtools/performance.xul                           (performance/performance.xul)
     content/browser/devtools/performance/performance-controller.js     (performance/performance-controller.js)
     content/browser/devtools/performance/performance-view.js           (performance/performance-view.js)
     content/browser/devtools/performance/views/overview.js             (performance/views/overview.js)
+    content/browser/devtools/performance/views/toolbar.js              (performance/views/toolbar.js)
     content/browser/devtools/performance/views/details.js              (performance/views/details.js)
     content/browser/devtools/performance/views/details-call-tree.js    (performance/views/details-call-tree.js)
     content/browser/devtools/performance/views/details-waterfall.js    (performance/views/details-waterfall.js)
     content/browser/devtools/performance/views/details-flamegraph.js   (performance/views/details-flamegraph.js)
     content/browser/devtools/performance/views/recordings.js           (performance/views/recordings.js)
 #endif
     content/browser/devtools/responsivedesign/resize-commands.js       (responsivedesign/resize-commands.js)
     content/browser/devtools/commandline.css                           (commandline/commandline.css)
--- a/browser/devtools/performance/performance-controller.js
+++ b/browser/devtools/performance/performance-controller.js
@@ -39,30 +39,37 @@ devtools.lazyRequireGetter(this, "Waterf
 devtools.lazyRequireGetter(this, "MarkerDetails",
   "devtools/timeline/marker-details", true);
 devtools.lazyRequireGetter(this, "CallView",
   "devtools/profiler/tree-view", true);
 devtools.lazyRequireGetter(this, "ThreadNode",
   "devtools/profiler/tree-model", true);
 devtools.lazyRequireGetter(this, "FrameNode",
   "devtools/profiler/tree-model", true);
+devtools.lazyRequireGetter(this, "OptionsView",
+  "devtools/shared/options-view", true);
 
 devtools.lazyImporter(this, "CanvasGraphUtils",
   "resource:///modules/devtools/Graphs.jsm");
 devtools.lazyImporter(this, "LineGraphWidget",
   "resource:///modules/devtools/Graphs.jsm");
 devtools.lazyImporter(this, "FlameGraphUtils",
   "resource:///modules/devtools/FlameGraph.jsm");
 devtools.lazyImporter(this, "FlameGraph",
   "resource:///modules/devtools/FlameGraph.jsm");
 devtools.lazyImporter(this, "SideMenuWidget",
   "resource:///modules/devtools/SideMenuWidget.jsm");
 
+const BRANCH_NAME = "devtools.performance.ui.";
+
 // Events emitted by various objects in the panel.
 const EVENTS = {
+  // Fired by the OptionsView when a preference changes.
+  PREF_CHANGED: "Preformance:PrefChanged",
+
   // Emitted by the PerformanceController or RecordingView
   // when a recording model is selected
   RECORDING_SELECTED: "Performance:RecordingSelected",
 
   // Emitted by the PerformanceView on record button click
   UI_START_RECORDING: "Performance:UI:StartRecording",
   UI_STOP_RECORDING: "Performance:UI:StopRecording",
 
@@ -161,53 +168,64 @@ let PrefObserver = {
 let PerformanceController = {
   _recordings: [],
   _currentRecording: null,
 
   /**
    * Listen for events emitted by the current tab target and
    * main UI events.
    */
-  initialize: function() {
+  initialize: Task.async(function* () {
     this.startRecording = this.startRecording.bind(this);
     this.stopRecording = this.stopRecording.bind(this);
     this.importRecording = this.importRecording.bind(this);
     this.exportRecording = this.exportRecording.bind(this);
     this._onTimelineData = this._onTimelineData.bind(this);
     this._onRecordingSelectFromView = this._onRecordingSelectFromView.bind(this);
+    this._onPrefChanged = this._onPrefChanged.bind(this);
 
+    ToolbarView.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
     PerformanceView.on(EVENTS.UI_START_RECORDING, this.startRecording);
     PerformanceView.on(EVENTS.UI_STOP_RECORDING, this.stopRecording);
     PerformanceView.on(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
     RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
     RecordingsView.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
 
     gFront.on("ticks", this._onTimelineData); // framerate
     gFront.on("markers", this._onTimelineData); // timeline markers
     gFront.on("frames", this._onTimelineData); // stack frames
     gFront.on("memory", this._onTimelineData); // timeline memory
-  },
+  }),
 
   /**
    * Remove events handled by the PerformanceController
    */
   destroy: function() {
+    ToolbarView.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
     PerformanceView.off(EVENTS.UI_START_RECORDING, this.startRecording);
     PerformanceView.off(EVENTS.UI_STOP_RECORDING, this.stopRecording);
     PerformanceView.off(EVENTS.UI_IMPORT_RECORDING, this.importRecording);
     RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
     RecordingsView.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
 
     gFront.off("ticks", this._onTimelineData);
     gFront.off("markers", this._onTimelineData);
     gFront.off("frames", this._onTimelineData);
     gFront.off("memory", this._onTimelineData);
   },
 
   /**
+   * Get a preference setting from `prefName` via the underlying
+   * OptionsView in the ToolbarView.
+   */
+  getPref: function (prefName) {
+    return ToolbarView.optionsView.getPref(prefName);
+  },
+
+  /**
    * Starts recording with the PerformanceFront. Emits `EVENTS.RECORDING_STARTED`
    * when the front has started to record.
    */
   startRecording: Task.async(function *() {
     let recording = this.createNewRecording();
     this.setCurrentRecording(recording);
     yield recording.startRecording();
 
@@ -310,16 +328,24 @@ let PerformanceController = {
   },
 
   /**
    * Fired from RecordingsView, we listen on the PerformanceController so we can
    * set it here and re-emit on the controller, where all views can listen.
    */
   _onRecordingSelectFromView: function (_, recording) {
     this.setCurrentRecording(recording);
+  },
+
+  /**
+   * Fired when the ToolbarView fires a PREF_CHANGED event.
+   * with the value.
+   */
+  _onPrefChanged: function (_, prefName, value) {
+    this.emit(EVENTS.PREF_CHANGED, prefName, value);
   }
 };
 
 /**
  * Convenient way of emitting events from the controller.
  */
 EventEmitter.decorate(PerformanceController);
 
--- a/browser/devtools/performance/performance-view.js
+++ b/browser/devtools/performance/performance-view.js
@@ -24,16 +24,17 @@ let PerformanceView = {
 
     // Bind to controller events to unlock the record button
     PerformanceController.on(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
     PerformanceController.on(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
 
     return promise.all([
       RecordingsView.initialize(),
       OverviewView.initialize(),
+      ToolbarView.initialize(),
       DetailsView.initialize()
     ]);
   },
 
   /**
    * Unbinds events and destroys subviews.
    */
   destroy: function () {
@@ -41,16 +42,17 @@ let PerformanceView = {
     this._importButton.removeEventListener("click", this._onImportButtonClick);
 
     PerformanceController.off(EVENTS.RECORDING_STARTED, this._unlockRecordButton);
     PerformanceController.off(EVENTS.RECORDING_STOPPED, this._unlockRecordButton);
 
     return promise.all([
       RecordingsView.destroy(),
       OverviewView.destroy(),
+      ToolbarView.destroy(),
       DetailsView.destroy()
     ]);
   },
 
   /**
    * Adds the `locked` attribute on the record button. This prevents it
    * from being clicked while recording is started or stopped.
    */
--- a/browser/devtools/performance/performance.xul
+++ b/browser/devtools/performance/performance.xul
@@ -13,22 +13,32 @@
 ]>
 
 <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
   <script src="chrome://browser/content/devtools/theme-switching.js"/>
   <script type="application/javascript" src="performance/performance-controller.js"/>
   <script type="application/javascript" src="performance/performance-view.js"/>
   <script type="application/javascript" src="performance/recording-model.js"/>
   <script type="application/javascript" src="performance/views/overview.js"/>
+  <script type="application/javascript" src="performance/views/toolbar.js"/>
   <script type="application/javascript" src="performance/views/details.js"/>
   <script type="application/javascript" src="performance/views/details-call-tree.js"/>
   <script type="application/javascript" src="performance/views/details-waterfall.js"/>
   <script type="application/javascript" src="performance/views/details-flamegraph.js"/>
   <script type="application/javascript" src="performance/views/recordings.js"/>
 
+  <popupset id="performance-options-popupset">
+    <menupopup id="performance-options-menupopup">
+      <menuitem id="option-invert-call-tree"
+                type="checkbox"
+                data-pref="invert-call-tree"
+                label="&profilerUI.invertTree;"
+                tooltiptext="&profilerUI.invertTree.tooltiptext;"/>
+    </menupopup>
+  </popupset>
   <hbox class="theme-body" flex="1">
     <vbox id="recordings-pane">
       <toolbar id="recordings-toolbar"
                class="devtools-toolbar">
         <hbox id="recordings-controls"
               class="devtools-toolbarbutton-group">
           <toolbarbutton id="record-button"
                          class="devtools-toolbarbutton"
@@ -52,16 +62,22 @@
           <toolbarbutton id="select-calltree-view"
                          class="devtools-toolbarbutton"
                          data-view="calltree" />
           <toolbarbutton id="select-flamegraph-view"
                          class="devtools-toolbarbutton"
                          data-view="flamegraph" />
         </hbox>
         <spacer flex="1"></spacer>
+        <hbox id="performance-toolbar-control-options" class="devtools-toolbarbutton-group">
+          <toolbarbutton id="performance-options-button"
+                         class="devtools-toolbarbutton devtools-option-toolbarbutton"
+                         popup="performance-options-menupopup"
+                         tooltiptext="&profilerUI.options.tooltiptext;"/>
+        </hbox>
       </toolbar>
 
       <vbox id="overview-pane">
         <hbox id="markers-overview"/>
         <hbox id="memory-overview"/>
         <hbox id="time-framerate"/>
       </vbox>
       <deck id="details-pane" flex="1">
--- a/browser/devtools/performance/test/browser.ini
+++ b/browser/devtools/performance/test/browser.ini
@@ -22,16 +22,17 @@ support-files =
 [browser_perf-front-profiler-02.js]
 [browser_perf-front-profiler-03.js]
 [browser_perf-front-profiler-04.js]
 #[browser_perf-front-profiler-05.js] bug 1077464
 #[browser_perf-front-profiler-06.js]
 [browser_perf-front.js]
 [browser_perf-jump-to-debugger-01.js]
 [browser_perf-jump-to-debugger-02.js]
+[browser_perf-options-invert-call-tree.js]
 [browser_perf-overview-render-01.js]
 [browser_perf-overview-render-02.js]
 [browser_perf-overview-selection-01.js]
 [browser_perf-overview-selection-02.js]
 [browser_perf-overview-selection-03.js]
 [browser_perf-shared-connection-02.js]
 [browser_perf-shared-connection-03.js]
 # [browser_perf-shared-connection-04.js] bug 1077464
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/test/browser_perf-options-invert-call-tree.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+const INVERT_PREF = "devtools.performance.ui.invert-call-tree";
+
+/**
+ * Tests that the call tree view renders after recording.
+ */
+function spawnTest () {
+  let { panel } = yield initPerformance(SIMPLE_URL);
+  let { EVENTS, CallTreeView } = panel.panelWin;
+
+  Services.prefs.setBoolPref(INVERT_PREF, true);
+
+  yield startRecording(panel);
+  yield busyWait(100);
+
+  let rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
+  yield stopRecording(panel);
+  yield rendered;
+
+  rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
+  Services.prefs.setBoolPref(INVERT_PREF, false);
+  yield rendered;
+
+  ok(true, "CallTreeView rerendered when toggling invert-call-tree.");
+
+  rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
+  Services.prefs.setBoolPref(INVERT_PREF, true);
+  yield rendered;
+
+  ok(true, "CallTreeView rerendered when toggling back invert-call-tree.");
+
+  yield teardown(panel);
+  finish();
+}
--- a/browser/devtools/performance/test/head.js
+++ b/browser/devtools/performance/test/head.js
@@ -1,25 +1,16 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 "use strict";
 
 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
 
 let { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
 
-// Enable logging for all the tests. Both the debugger server and frontend will
-// be affected by this pref.
-let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
-Services.prefs.setBoolPref("devtools.debugger.log", false);
-
-// Enable the new performance panel for all tests. Remove this after
-// bug 1075567 is resolved.
-let gToolEnabled = Services.prefs.getBoolPref("devtools.performance_dev.enabled");
-
 let { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
 let { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
 let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {});
 let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {});
 let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
 let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {});
 let { getPerformanceActorsConnection, PerformanceFront } = devtools.require("devtools/performance/front");
 let nsIProfilerModule = Cc["@mozilla.org/tools/profiler;1"].getService(Ci.nsIProfiler);
@@ -30,33 +21,52 @@ const FRAME_SCRIPT_UTILS_URL = "chrome:/
 const EXAMPLE_URL = "http://example.com/browser/browser/devtools/performance/test/";
 const SIMPLE_URL = EXAMPLE_URL + "doc_simple-test.html";
 
 // All tests are asynchronous.
 waitForExplicitFinish();
 
 gDevTools.testing = true;
 
+let DEFAULT_PREFS = [
+  "devtools.debugger.log",
+  "devtools.performance.ui.invert-call-tree",
+  // remove after bug 1075567 is resolved.
+  "devtools.performance_dev.enabled"
+].reduce((prefs, pref) => {
+  prefs[pref] = Services.prefs.getBoolPref(pref);
+  return prefs;
+}, {});
+
+// Enable the new performance panel for all tests. Remove this after
+// bug 1075567 is resolved.
+Services.prefs.setBoolPref("devtools.performance_dev.enabled", true);
+// Enable logging for all the tests. Both the debugger server and frontend will
+// be affected by this pref.
+Services.prefs.setBoolPref("devtools.debugger.log", false);
+
 /**
  * Call manually in tests that use frame script utils after initializing
  * the tool. Must be called after initializing so we can detect
  * whether or not `content` is a CPOW or not. Call after init but before navigating
  * to different pages.
  */
 function loadFrameScripts () {
   mm = gBrowser.selectedBrowser.messageManager;
   mm.loadFrameScript(FRAME_SCRIPT_UTILS_URL, false);
 }
 
 registerCleanupFunction(() => {
   gDevTools.testing = false;
   info("finish() was called, cleaning up...");
 
-  Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
-  Services.prefs.setBoolPref("devtools.performance_dev.enabled", gToolEnabled);
+  // Rollback any pref changes
+  Object.keys(DEFAULT_PREFS).forEach(pref => {
+    Services.prefs.setBoolPref(pref, DEFAULT_PREFS[pref]);
+  });
 
   // Make sure the profiler module is stopped when the test finishes.
   nsIProfilerModule.StopProfiler();
 
   Cu.forceGC();
 });
 
 function addTab(aUrl, aWindow) {
@@ -163,17 +173,16 @@ function initPerformance(aUrl, selectedT
   info("Initializing a performance pane.");
 
   return Task.spawn(function*() {
     let tab = yield addTab(aUrl);
     let target = TargetFactory.forTab(tab);
 
     yield target.makeRemote();
 
-    Services.prefs.setBoolPref("devtools.performance_dev.enabled", true);
     let toolbox = yield gDevTools.showToolbox(target, selectedTool);
     let panel = toolbox.getCurrentPanel();
     return { target, panel, toolbox };
   });
 }
 
 function* teardown(panel) {
   info("Destroying the performance tool.");
--- a/browser/devtools/performance/views/details-call-tree.js
+++ b/browser/devtools/performance/views/details-call-tree.js
@@ -9,30 +9,33 @@
 let CallTreeView = {
   /**
    * Sets up the view with event binding.
    */
   initialize: function () {
     this._callTree = $(".call-tree-cells-container");
     this._onRecordingStoppedOrSelected = this._onRecordingStoppedOrSelected.bind(this);
     this._onRangeChange = this._onRangeChange.bind(this);
+    this._onPrefChanged = this._onPrefChanged.bind(this);
     this._onLink = this._onLink.bind(this);
 
     PerformanceController.on(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
     PerformanceController.on(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
+    PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
     OverviewView.on(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
     OverviewView.on(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
   },
 
   /**
    * Unbinds events.
    */
   destroy: function () {
     PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStoppedOrSelected);
     PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingStoppedOrSelected);
+    PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
     OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, this._onRangeChange);
     OverviewView.off(EVENTS.OVERVIEW_RANGE_CLEARED, this._onRangeChange);
   },
 
   /**
    * Method for handling all the set up for rendering a new call tree.
    */
   render: function (profilerData, beginAt, endAt, options={}) {
@@ -77,23 +80,27 @@ let CallTreeView = {
       () => this.emit(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER),
       () => this.emit(EVENTS.SOURCE_NOT_FOUND_IN_JS_DEBUGGER));
   },
 
   /**
    * Called when the recording is stopped and prepares data to
    * populate the call tree.
    */
-  _prepareCallTree: function (profilerData, beginAt, endAt, options) {
+  _prepareCallTree: function (profilerData, startTime, endTime, options) {
     let threadSamples = profilerData.profile.threads[0].samples;
     let contentOnly = !Prefs.showPlatformData;
-    // TODO handle inverted tree bug 1102347
-    let invertTree = false;
+    let invertTree = PerformanceController.getPref("invert-call-tree");
 
-    let threadNode = new ThreadNode(threadSamples, contentOnly, beginAt, endAt, invertTree);
+    let threadNode = new ThreadNode(threadSamples,
+      { startTime, endTime, contentOnly, invertTree });
+
+    // If we have an empty profile (no samples), then don't invert the tree, as
+    // it would hide the root node and a completely blank call tree space can be
+    // mis-interpreted as an error.
     options.inverted = invertTree && threadNode.samples > 0;
 
     return threadNode;
   },
 
   /**
    * Renders the call tree.
    */
@@ -109,16 +116,27 @@ let CallTreeView = {
     root.on("link", this._onLink);
 
     // Clear out other call trees.
     this._callTree.innerHTML = "";
     root.attachTo(this._callTree);
 
     let contentOnly = !Prefs.showPlatformData;
     root.toggleCategories(!contentOnly);
+  },
+
+  /**
+   * Called when a preference under "devtools.performance.ui." is changed.
+   */
+  _onPrefChanged: function (_, prefName, value) {
+    if (prefName === "invert-call-tree") {
+      let { beginAt, endAt } = OverviewView.getRange();
+      let profilerData = PerformanceController.getCurrentRecording().getProfilerData();
+      this.render(profilerData, beginAt || void 0, endAt || void 0);
+    }
   }
 };
 
 /**
  * Convenient way of emitting events from the view.
  */
 EventEmitter.decorate(CallTreeView);
 
--- a/browser/devtools/performance/views/overview.js
+++ b/browser/devtools/performance/views/overview.js
@@ -19,16 +19,19 @@ const MEMORY_GRAPH_HEIGHT = 30; // px
 
 const GRAPH_SCROLL_EVENTS_DRAIN = 50; // ms
 
 /**
  * View handler for the overview panel's time view, displaying
  * framerate, markers and memory over time.
  */
 let OverviewView = {
+  _beginAt: null,
+  _endAt: null,
+
   /**
    * Sets up the view with event binding.
    */
   initialize: Task.async(function *() {
     this._onRecordingStarted = this._onRecordingStarted.bind(this);
     this._onRecordingStopped = this._onRecordingStopped.bind(this);
     this._onRecordingSelected = this._onRecordingSelected.bind(this);
     this._onRecordingTick = this._onRecordingTick.bind(this);
@@ -64,16 +67,23 @@ let OverviewView = {
 
     clearNamedTimeout("graph-scroll");
     PerformanceController.off(EVENTS.RECORDING_STARTED, this._onRecordingStarted);
     PerformanceController.off(EVENTS.RECORDING_STOPPED, this._onRecordingStopped);
     PerformanceController.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelected);
   },
 
   /**
+   * Gets currently selected range's beginAt and endAt values.
+   */
+  getRange: function () {
+    return { beginAt: this._beginAt, endAt: this._endAt };
+  },
+
+  /**
    * Sets up the framerate graph.
    */
   _showFramerateGraph: Task.async(function *() {
     this.framerateGraph = new LineGraphWidget($("#time-framerate"), {
       metric: L10N.getStr("graphs.fps")
     });
     this.framerateGraph.fixedHeight = FRAMERATE_GRAPH_HEIGHT;
     yield this.framerateGraph.ready();
@@ -143,18 +153,22 @@ let OverviewView = {
 
   /**
    * Fired when the graph selection has changed. Called by
    * mouseup and scroll events.
    */
   _onSelectionChange: function () {
     if (this.framerateGraph.hasSelection()) {
       let { min: beginAt, max: endAt } = this.framerateGraph.getMappedSelection();
+      this._beginAt = beginAt;
+      this._endAt = endAt;
       this.emit(EVENTS.OVERVIEW_RANGE_SELECTED, { beginAt, endAt });
     } else {
+      this._beginAt = null;
+      this._endAt = null;
       this.emit(EVENTS.OVERVIEW_RANGE_CLEARED);
     }
   },
 
   /**
    * Listener handling the "mouseup" event for the framerate graph.
    * Fires an event to be handled elsewhere.
    */
--- a/browser/devtools/performance/views/recordings.js
+++ b/browser/devtools/performance/views/recordings.js
@@ -93,52 +93,49 @@ let RecordingsView = Heritage.extend(Wid
    */
   _onRecordingStarted: function (_, recording) {
     // Insert a "dummy" recording item, to hint that recording has now started.
     let recordingItem;
 
     // If a label is specified (e.g due to a call to `console.profile`),
     // then try reusing a pre-existing recording item, if there is one.
     // This is symmetrical to how `this.handleRecordingEnded` works.
-    if (recording.getLabel()) {
-      recordingItem = this.getItemForAttachment(e =>
-        e.getLabel() === recording.getLabel());
+    let profileLabel = recording.getLabel();
+    if (profileLabel) {
+      recordingItem = this.getItemForAttachment(e => e.getLabel() == profileLabel);
     }
     // Otherwise, create a new empty recording item.
     if (!recordingItem) {
       recordingItem = this.addEmptyRecording(recording);
     }
 
     // Mark the corresponding item as being a "record in progress".
     recordingItem.isRecording = true;
 
     // If this is a manual recording, immediately select it.
     if (!recording.getLabel()) {
       this.selectedItem = recordingItem;
     }
-
-    this.emit(EVENTS.RECORDING_SELECTED, recording);
   },
 
   /**
    * Signals that a recording session has ended.
    *
    * @param RecordingModel recording
    *        The model of the recording that just stopped.
    */
   _onRecordingStopped: function (_, recording) {
-    let profileLabel = recording.getLabel();
     let recordingItem;
 
     // If a label is specified (e.g due to a call to `console.profileEnd`),
     // then try reusing a pre-existing recording item, if there is one.
     // This is symmetrical to how `this.handleRecordingStarted` works.
+    let profileLabel = recording.getLabel();
     if (profileLabel) {
-      recordingItem = this.getItemForAttachment(e =>
-        e.profilerData.profileLabel == profileLabel);
+      recordingItem = this.getItemForAttachment(e => e.getLabel() == profileLabel);
     }
     // Otherwise, just use the first available recording item.
     if (!recordingItem) {
       recordingItem = this.getItemForPredicate(e => e.isRecording);
     }
 
     // Mark the corresponding item as being a "finished recording".
     recordingItem.isRecording = false;
@@ -158,19 +155,16 @@ let RecordingsView = Heritage.extend(Wid
     let recordingItem = this.addEmptyRecording(model);
     recordingItem.isRecording = false;
 
     // Immediately select the imported recording
     this.selectedItem = recordingItem;
 
     // Render the recording item with finalized information (timing, etc)
     this.finalizeRecording(recordingItem);
-
-    // Fire the selection and allow to propogate.
-    this.emit(EVENTS.RECORDING_SELECTED, model);
   },
 
   /**
    * Adds recording data to a recording item in this container.
    *
    * @param Item recordingItem
    *        An item inserted via `RecordingsView.addEmptyRecording`.
    */
new file mode 100644
--- /dev/null
+++ b/browser/devtools/performance/views/toolbar.js
@@ -0,0 +1,43 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+"use strict";
+
+/**
+ * View handler for toolbar events (mostly option toggling and triggering)
+ */
+let ToolbarView = {
+  /**
+   * Sets up the view with event binding.
+   */
+  initialize: Task.async(function *() {
+    this._onPrefChanged = this._onPrefChanged.bind(this);
+
+    this.optionsView = new OptionsView({
+      branchName: BRANCH_NAME,
+      menupopup: $("#performance-options-menupopup")
+    });
+
+    yield this.optionsView.initialize();
+    this.optionsView.on("pref-changed", this._onPrefChanged);
+  }),
+
+  /**
+   * Unbinds events and cleans up view.
+   */
+  destroy: function () {
+    this.optionsView.off("pref-changed", this._onPrefChanged);
+    this.optionsView.destroy();
+  },
+
+  /**
+   * Fired when a preference changes in the underlying OptionsView.
+   * Propogated by the PerformanceController.
+   */
+  _onPrefChanged: function (_, prefName) {
+    let value = Services.prefs.getBoolPref(BRANCH_NAME + prefName);
+    this.emit(EVENTS.PREF_CHANGED, prefName, value);
+  }
+};
+
+EventEmitter.decorate(ToolbarView);
--- a/browser/devtools/profiler/test/browser_profiler_tree-model-03.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-model-03.js
@@ -6,17 +6,17 @@
  * while at the same time filtering by duration.
  */
 
 function test() {
   let { ThreadNode } = devtools.require("devtools/profiler/tree-model");
 
   // Create a root node from a given samples array, filtering by time.
 
-  let root = new ThreadNode(gSamples, false, 11, 18);
+  let root = new ThreadNode(gSamples, { startTime: 11, endTime: 18 });
 
   // Test the root node.
 
   is(root.duration, 18,
     "The correct duration was calculated for the root node.");
 
   is(Object.keys(root.calls).length, 1,
     "The correct number of child calls were calculated for the root node.");
--- a/browser/devtools/profiler/test/browser_profiler_tree-model-04.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-model-04.js
@@ -6,17 +6,17 @@
  * while at the same time filtering by duration and content-only frames.
  */
 
 function test() {
   let { ThreadNode } = devtools.require("devtools/profiler/tree-model");
 
   // Create a root node from a given samples array, filtering by time.
 
-  let root = new ThreadNode(gSamples, true, 11, 18);
+  let root = new ThreadNode(gSamples, { startTime: 11, endTime: 18, contentOnly: true });
 
   // Test the root node.
 
   is(root.duration, 18,
     "The correct duration was calculated for the root node.");
 
   is(Object.keys(root.calls).length, 2,
     "The correct number of child calls were calculated for the root node.");
--- a/browser/devtools/profiler/test/browser_profiler_tree-model-05.js
+++ b/browser/devtools/profiler/test/browser_profiler_tree-model-05.js
@@ -40,17 +40,17 @@ let samples = [{
     { location: "B" },
     { location: "F" }
   ]
 }];
 
 function test() {
   let { ThreadNode } = devtools.require("devtools/profiler/tree-model");
 
-  let root = new ThreadNode(samples, undefined, undefined, undefined, true);
+  let root = new ThreadNode(samples, { invertTree: true });
 
   is(Object.keys(root.calls).length, 2,
      "Should get the 2 youngest frames, not the 1 oldest frame");
 
   let C = root.calls.C;
   ok(C, "Should have C as a child of the root.");
 
   is(Object.keys(C.calls).length, 3,
--- a/browser/devtools/profiler/ui-profile.js
+++ b/browser/devtools/profiler/ui-profile.js
@@ -447,30 +447,31 @@ let ProfileView = {
   /**
    * Populates the call tree view in the specified tab's panel with the
    * provided data. The already existing tree will be removed.
    *
    * @param nsIDOMNode panel
    *        The <panel> element in this <tabbox>.
    * @param object profilerData
    *        The data source for this tree.
-   * @param number beginAt
+   * @param number startTime
    *        The earliest time in the data source to start at (in milliseconds).
-   * @param number endAt
+   * @param number endTime
    *        The latest time in the data source to end at (in milliseconds).
    * @param object options
    *        Additional options supported by this operation.
    *        @see ProfileView._populatePanelWidgets
    */
-  _populateCallTree: function(panel, profilerData, beginAt, endAt, options = {}) {
+  _populateCallTree: function(panel, profilerData, startTime, endTime, options = {}) {
     let threadSamples = profilerData.profile.threads[0].samples;
     let contentOnly = !Prefs.showPlatformData;
     let invertChecked = this._invertTree.hasAttribute("checked");
-    let threadNode = new ThreadNode(threadSamples, contentOnly, beginAt, endAt,
-                                    invertChecked);
+    let threadNode = new ThreadNode(threadSamples,
+      { startTime, endTime, contentOnly, invertChecked });
+
     // If we have an empty profile (no samples), then don't invert the tree, as
     // it would hide the root node and a completely blank call tree space can be
     // mis-interpreted as an error.
     options.inverted = invertChecked && threadNode.samples > 0;
     this._populateCallTreeFromFrameNode(panel, threadNode, options);
   },
 
   /**
--- a/browser/devtools/profiler/utils/tree-model.js
+++ b/browser/devtools/profiler/utils/tree-model.js
@@ -39,75 +39,75 @@ exports.FrameNode.isContent = isContent;
  *       }
  *     }, // FrameNode
  *     ...
  *   }
  * } // ThreadNode
  *
  * @param object threadSamples
  *        The raw samples array received from the backend.
- * @param boolean contentOnly [optional]
- *        @see ThreadNode.prototype.insert
- * @param number beginAt [optional]
- *        @see ThreadNode.prototype.insert
- * @param number endAt [optional]
- *        @see ThreadNode.prototype.insert
- * @param boolean invert [optional]
- *        @see ThreadNode.prototype.insert
+ * @param object options
+ *        Additional supported options, @see ThreadNode.prototype.insert
+ *          - number startTime [optional]
+ *          - number endTime [optional]
+ *          - boolean contentOnly [optional]
+ *          - boolean invertTree [optional]
  */
-function ThreadNode(threadSamples, contentOnly, beginAt, endAt, invert) {
+function ThreadNode(threadSamples, options = {}) {
   this.samples = 0;
   this.duration = 0;
   this.calls = {};
   this._previousSampleTime = 0;
 
   for (let sample of threadSamples) {
-    this.insert(sample, contentOnly, beginAt, endAt, invert);
+    this.insert(sample, options);
   }
 }
 
 ThreadNode.prototype = {
   /**
    * Adds function calls in the tree from a sample's frames.
    *
    * @param object sample
    *        The { frames, time } sample, containing an array of frames and
    *        the time the sample was taken. This sample is assumed to be older
    *        than the most recently inserted one.
-   * @param boolean contentOnly [optional]
-   *        Specifies if platform frames shouldn't be taken into consideration.
-   * @param number beginAt [optional]
-   *        The earliest sample to start at (in milliseconds).
-   * @param number endAt [optional]
-   *        The latest sample to end at (in milliseconds).
-   * @param boolean inverted [optional]
-   *        Specifies if the call tree should be inverted (youngest -> oldest
-   *        frames).
+   * @param object options [optional]
+   *        Additional supported options:
+   *          - number startTime: the earliest sample to start at (in milliseconds)
+   *          - number endTime: the latest sample to end at (in milliseconds)
+   *          - boolean contentOnly: if platform frames shouldn't be used
+   *          - boolean invertTree: if the call tree should be inverted
    */
-  insert: function(sample, contentOnly = false, beginAt = 0, endAt = Infinity, inverted = false) {
+  insert: function(sample, options = {}) {
+    let startTime = options.startTime || 0;
+    let endTime = options.endTime || Infinity;
     let sampleTime = sample.time;
-    if (!sampleTime || sampleTime < beginAt || sampleTime > endAt) {
+    if (!sampleTime || sampleTime < startTime || sampleTime > endTime) {
       return;
     }
 
     let sampleFrames = sample.frames;
 
     // Filter out platform frames if only content-related function calls
     // should be taken into consideration.
-    if (contentOnly) {
+    if (options.contentOnly) {
       // The (root) node is not considered a content function, it'll be removed.
       sampleFrames = sampleFrames.filter(isContent);
     } else {
       // Remove the (root) node manually.
       sampleFrames = sampleFrames.slice(1);
     }
+    // If no frames remain after filtering, then this is a leaf node, no need
+    // to continue.
     if (!sampleFrames.length) {
       return;
     }
-    if (inverted) {
+    // Invert the tree after filtering, if preferred.
+    if (options.invertTree) {
       sampleFrames.reverse();
     }
 
     let sampleDuration = sampleTime - this._previousSampleTime;
     this._previousSampleTime = sampleTime;
     this.samples++;
     this.duration += sampleDuration;
 
--- a/browser/devtools/shared/options-view.js
+++ b/browser/devtools/shared/options-view.js
@@ -3,27 +3,26 @@ const { Services } = require("resource:/
 
 const OPTIONS_SHOWN_EVENT = "options-shown";
 const OPTIONS_HIDDEN_EVENT = "options-hidden";
 const PREF_CHANGE_EVENT = "pref-changed";
 
 /**
  * OptionsView constructor. Takes several options, all required:
  * - branchName: The name of the prefs branch, like "devtools.debugger."
- * - window: The window the XUL elements live in.
  * - menupopup: The XUL `menupopup` item that contains the pref buttons.
  *
  * Fires an event, PREF_CHANGE_EVENT, with the preference name that changed as the second
  * argument. Fires events on opening/closing the XUL panel (OPTIONS_SHOW_EVENT, OPTIONS_HIDDEN_EVENT)
  * as the second argument in the listener, used for tests mostly.
  */
 const OptionsView = function (options={}) {
   this.branchName = options.branchName;
-  this.window = options.window;
   this.menupopup = options.menupopup;
+  this.window = this.menupopup.ownerDocument.defaultView;
   let { document } = this.window;
   this.$ = document.querySelector.bind(document);
   this.$$ = document.querySelectorAll.bind(document);
 
   this.prefObserver = new PrefObserver(this.branchName);
 
   EventEmitter.decorate(this);
 };
@@ -74,16 +73,23 @@ OptionsView.prototype = {
   destroy: function () {
     this.mutationObserver.disconnect();
     this.prefObserver.off(PREF_CHANGE_EVENT, this._onPrefChange);
     this.menupopup.removeEventListener("popupshown", this._onPopupShown);
     this.menupopup.removeEventListener("popuphidden", this._onPopupHidden);
   },
 
   /**
+   * Returns the value for the specified `prefName`
+   */
+  getPref: function (prefName) {
+    return this.prefObserver.get(prefName);
+  },
+
+  /**
    * Called when a preference is changed (either via clicking an option
    * button or by changing it in about:config). Updates the checked status
    * of the corresponding button.
    */
   _onPrefChange: function (_, prefName) {
     let $el = this.$(`menuitem[data-pref="${prefName}"]`, this.menupopup);
     let value = this.prefObserver.get(prefName);
 
--- a/browser/devtools/shared/test/browser.ini
+++ b/browser/devtools/shared/test/browser.ini
@@ -20,16 +20,17 @@ support-files =
 [browser_flame-graph-02.js]
 [browser_flame-graph-03a.js]
 [browser_flame-graph-03b.js]
 [browser_flame-graph-04.js]
 [browser_flame-graph-utils-01.js]
 [browser_flame-graph-utils-02.js]
 [browser_flame-graph-utils-03.js]
 [browser_flame-graph-utils-04.js]
+[browser_flame-graph-utils-hash.js]
 [browser_graphs-01.js]
 [browser_graphs-02.js]
 [browser_graphs-03.js]
 [browser_graphs-04.js]
 [browser_graphs-05.js]
 [browser_graphs-06.js]
 [browser_graphs-07a.js]
 [browser_graphs-07b.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/test/browser_flame-graph-utils-hash.js
@@ -0,0 +1,24 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+// Tests if (idle) nodes are added when necessary in the flame graph data.
+
+let {FlameGraphUtils} = Cu.import("resource:///modules/devtools/FlameGraph.jsm", {});
+
+let test = Task.async(function*() {
+  let hash1 = FlameGraphUtils._getStringHash("abc");
+  let hash2 = FlameGraphUtils._getStringHash("acb");
+  let hash3 = FlameGraphUtils._getStringHash(Array.from(Array(100000)).join("a"));
+  let hash4 = FlameGraphUtils._getStringHash(Array.from(Array(100000)).join("b"));
+
+  isnot(hash1, hash2, "The hashes should not be equal (1).");
+  isnot(hash2, hash3, "The hashes should not be equal (2).");
+  isnot(hash3, hash4, "The hashes should not be equal (3).");
+
+  ok(Number.isInteger(hash1), "The hashes should be integers, not Infinity or NaN (1).");
+  ok(Number.isInteger(hash2), "The hashes should be integers, not Infinity or NaN (2).");
+  ok(Number.isInteger(hash3), "The hashes should be integers, not Infinity or NaN (3).");
+  ok(Number.isInteger(hash4), "The hashes should be integers, not Infinity or NaN (4).");
+
+  finish();
+});
--- a/browser/devtools/shared/test/browser_options-view-01.js
+++ b/browser/devtools/shared/test/browser_options-view-01.js
@@ -39,16 +39,20 @@ function* testOptionsView(tab) {
 
   // Test default config
   is(ppEl.getAttribute("checked"), "true", "`true` prefs are checked on start");
   is(bbEl.getAttribute("checked"), "", "`false` prefs are unchecked on start");
 
   // Test buttons update when preferences update outside of the menu
   Services.prefs.setBoolPref(BRANCH + PRETTY_PRINT_PREF, false);
   Services.prefs.setBoolPref(BRANCH + BLACK_BOX_PREF, true);
+
+  is(options.getPref(PRETTY_PRINT_PREF), false, "getPref returns correct value");
+  is(options.getPref(BLACK_BOX_PREF), true, "getPref returns correct value");
+
   is(ppEl.getAttribute("checked"), "", "menuitems update when preferences change");
   is(bbEl.getAttribute("checked"), "true", "menuitems update when preferences change");
 
   // Tests events are fired when preferences update outside of the menu
   is(events.length, 2, "two 'pref-changed' events fired");
   is(events[0], "auto-pretty-print", "correct pref passed in 'pref-changed' event (auto-pretty-print)");
   is(events[1], "auto-black-box", "correct pref passed in 'pref-changed' event (auto-black-box)");
 
@@ -60,16 +64,18 @@ function* testOptionsView(tab) {
   yield click(options, window, bbEl);
   is(bbEl.getAttribute("checked"), "", "menuitems update when clicked");
   is(Services.prefs.getBoolPref(BRANCH + BLACK_BOX_PREF), false, "preference updated via click");
 
   // Tests events are fired when preferences updated via click
   is(events.length, 4, "two 'pref-changed' events fired");
   is(events[2], "auto-pretty-print", "correct pref passed in 'pref-changed' event (auto-pretty-print)");
   is(events[3], "auto-black-box", "correct pref passed in 'pref-changed' event (auto-black-box)");
+
+  yield options.destroy();
 }
 
 function wait(window) {
   return new Promise(function (resolve, reject) {
   window.setTimeout(() => resolve, 60000);
   });
 }
 function createOptionsView (tab) {
--- a/browser/devtools/shared/widgets/FlameGraph.jsm
+++ b/browser/devtools/shared/widgets/FlameGraph.jsm
@@ -946,13 +946,17 @@ let FlameGraphUtils = {
     const STRING_HASH_PRIME1 = 7;
     const STRING_HASH_PRIME2 = 31;
 
     let hash = STRING_HASH_PRIME1;
 
     for (let i = 0, len = input.length; i < len; i++) {
       hash *= STRING_HASH_PRIME2;
       hash += input.charCodeAt(i);
+
+      if (hash > Number.MAX_SAFE_INTEGER / STRING_HASH_PRIME2) {
+        return hash;
+      }
     }
 
     return hash;
   }
 };
--- a/browser/locales/en-US/chrome/browser/devtools/profiler.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/profiler.dtd
@@ -36,28 +36,33 @@
 <!-- LOCALIZATION NOTE (profilerUI.exportButton): This string is displayed
   -  on a button that opens a dialog to export a saved profile data file. -->
 <!ENTITY profilerUI.exportButton "Save">
 
 <!-- LOCALIZATION NOTE (profilerUI.clearButton): This string is displayed
   -  on a button that remvoes all the recordings. -->
 <!ENTITY profilerUI.clearButton "Clear">
 
-<!-- LOCALIZATION NOTE (profilerUI.invertTree): This is the label shown next to
-  -  a checkbox that inverts and un-inverts the profiler's call tree. -->
-<!ENTITY profilerUI.invertTree "Invert Call Tree">
-
-<!-- LOCALIZATION NOTE (profilerUI.invertTree.tooltiptext): This is the tooltip
-  -  for the tree-inverting checkbox's label.  -->
-<!ENTITY profilerUI.invertTree.tooltiptext "Inverting the call tree displays the profiled call paths starting from the youngest frames and expanding out to the older frames.">
-
 <!-- LOCALIZATION NOTE (profilerUI.table.*): These strings are displayed
   -  in the call tree headers for a recording. -->
 <!ENTITY profilerUI.table.totalDuration   "Total Time (ms)">
 <!ENTITY profilerUI.table.selfDuration    "Self Time (ms)">
 <!ENTITY profilerUI.table.totalPercentage "Total Cost">
 <!ENTITY profilerUI.table.selfPercentage  "Self Cost">
 <!ENTITY profilerUI.table.samples         "Samples">
 <!ENTITY profilerUI.table.function        "Function">
 
 <!-- LOCALIZATION NOTE (profilerUI.newtab.tooltiptext): The tooltiptext shown
   -  on the "+" (new tab) button for a profile when a selection is available. -->
 <!ENTITY profilerUI.newtab.tooltiptext "Add new tab from selection">
+
+<!-- LOCALIZATION NOTE (profilerUI.options.tooltiptext): This is the tooltip
+  -  for the options button. -->
+<!ENTITY profilerUI.options.tooltiptext "Configure performance preferences.">
+
+<!-- LOCALIZATION NOTE (profilerUI.invertTree): This is the label shown next to
+  -  a checkbox that inverts and un-inverts the profiler's call tree. -->
+<!ENTITY profilerUI.invertTree "Invert Call Tree">
+
+<!-- LOCALIZATION NOTE (profilerUI.invertTree.tooltiptext): This is the tooltip
+  -  for the tree-inverting checkbox's label.  -->
+<!ENTITY profilerUI.invertTree.tooltiptext "Inverting the call tree displays the profiled call paths starting from the youngest frames and expanding out to the older frames.">
+
--- a/dom/bluetooth/BluetoothInterface.cpp
+++ b/dom/bluetooth/BluetoothInterface.cpp
@@ -1,15 +1,18 @@
 /* -*- 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 "BluetoothInterface.h"
+#if ANDROID_VERSION >= 17
+#include <cutils/properties.h>
+#endif
 #ifdef MOZ_B2G_BT_BLUEDROID
 #include "BluetoothHALInterface.h"
 #endif
 #ifdef MOZ_B2G_BT_DAEMON
 #include "BluetoothDaemonInterface.h"
 #endif
 
 BEGIN_BLUETOOTH_NAMESPACE
@@ -86,30 +89,70 @@ BluetoothNotificationHandler::~Bluetooth
 { }
 
 // Interface
 //
 
 BluetoothInterface*
 BluetoothInterface::GetInstance()
 {
+#if ANDROID_VERSION >= 17
+  /* We pick a default backend from the available ones. The branches
+   * are ordered by preference.
+   */
+#ifdef MOZ_B2G_BT_DAEMON
+  static const char sDefaultBackend[] = "bluetoothd";
+#else
+#ifdef MOZ_B2G_BT_BLUEDROID
+  static const char sDefaultBackend[] = "bluedroid";
+#else
+  static const char const * sDefaultBackend = nullptr;
+#endif
+#endif
+
   /* Here's where we decide which implementation to use. Currently
    * there is only Bluedroid and the Bluetooth daemon, but others are
    * possible. Having multiple interfaces built-in and selecting the
-   * correct one at runtime could also be an option.
+   * correct one at runtime is also an option.
    */
+
+  char value[PROPERTY_VALUE_MAX];
+  int len;
+
+  len = property_get("ro.moz.bluetooth.backend", value, sDefaultBackend);
+  if (len < 0) {
+    BT_WARNING("No Bluetooth backend available.");
+    return nullptr;
+  }
+
+  const nsDependentCString backend(value, len);
+
 #ifdef MOZ_B2G_BT_BLUEDROID
-  return BluetoothHALInterface::GetInstance();
+  if (backend.LowerCaseEqualsLiteral("bluedroid")) {
+    return BluetoothHALInterface::GetInstance();
+  } else
+#endif
+#ifdef MOZ_B2G_BT_DAEMON
+  if (backend.LowerCaseEqualsLiteral("bluetoothd")) {
+    return BluetoothDaemonInterface::GetInstance();
+  } else
+#endif
+  {
+    BT_WARNING("Bluetooth backend '%s' is unknown or not available.",
+               backend.get());
+  }
+  return nullptr;
+
 #else
-#ifdef MOZ_B2G_BT_DAEMON
-  return BluetoothDaemonInterface::GetInstance();
-#else
+  /* Anything that's not Android 4.2 or later uses BlueZ instead. The
+   * code should actually never reach this point.
+   */
+  BT_WARNING("No Bluetooth backend available for your system.");
   return nullptr;
 #endif
-#endif
 }
 
 BluetoothInterface::BluetoothInterface()
 { }
 
 BluetoothInterface::~BluetoothInterface()
 { }
 
--- a/dom/system/gonk/NetworkUtils.cpp
+++ b/dom/system/gonk/NetworkUtils.cpp
@@ -127,30 +127,33 @@ static nsTArray<CommandChain*> gCommandC
 const CommandFunc NetworkUtils::sWifiEnableChain[] = {
   NetworkUtils::clearWifiTetherParms,
   NetworkUtils::wifiFirmwareReload,
   NetworkUtils::startAccessPointDriver,
   NetworkUtils::setAccessPoint,
   NetworkUtils::startSoftAP,
   NetworkUtils::setInterfaceUp,
   NetworkUtils::tetherInterface,
+  NetworkUtils::addInterfaceToLocalNetwork,
+  NetworkUtils::addRouteToLocalNetwork,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::tetheringStatus,
   NetworkUtils::startTethering,
   NetworkUtils::setDnsForwarders,
   NetworkUtils::enableNat,
   NetworkUtils::wifiTetheringSuccess
 };
 
 const CommandFunc NetworkUtils::sWifiDisableChain[] = {
   NetworkUtils::clearWifiTetherParms,
   NetworkUtils::stopSoftAP,
   NetworkUtils::stopAccessPointDriver,
   NetworkUtils::wifiFirmwareReload,
   NetworkUtils::untetherInterface,
+  NetworkUtils::removeInterfaceFromLocalNetwork,
   NetworkUtils::preTetherInterfaceList,
   NetworkUtils::postTetherInterfaceList,
   NetworkUtils::disableNat,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::stopTethering,
   NetworkUtils::wifiTetheringSuccess
 };
 
@@ -168,16 +171,18 @@ const CommandFunc NetworkUtils::sWifiRet
 
   // sWifiEnableChain:
   NetworkUtils::wifiFirmwareReload,
   NetworkUtils::startAccessPointDriver,
   NetworkUtils::setAccessPoint,
   NetworkUtils::startSoftAP,
   NetworkUtils::setInterfaceUp,
   NetworkUtils::tetherInterface,
+  NetworkUtils::addInterfaceToLocalNetwork,
+  NetworkUtils::addRouteToLocalNetwork,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::tetheringStatus,
   NetworkUtils::startTethering,
   NetworkUtils::setDnsForwarders,
   NetworkUtils::enableNat,
   NetworkUtils::wifiTetheringSuccess
 };
 
@@ -186,24 +191,27 @@ const CommandFunc NetworkUtils::sWifiOpe
   NetworkUtils::wifiOperationModeSuccess
 };
 
 const CommandFunc NetworkUtils::sUSBEnableChain[] = {
   NetworkUtils::setInterfaceUp,
   NetworkUtils::enableNat,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::tetherInterface,
+  NetworkUtils::addInterfaceToLocalNetwork,
+  NetworkUtils::addRouteToLocalNetwork,
   NetworkUtils::tetheringStatus,
   NetworkUtils::startTethering,
   NetworkUtils::setDnsForwarders,
   NetworkUtils::usbTetheringSuccess
 };
 
 const CommandFunc NetworkUtils::sUSBDisableChain[] = {
   NetworkUtils::untetherInterface,
+  NetworkUtils::removeInterfaceFromLocalNetwork,
   NetworkUtils::preTetherInterfaceList,
   NetworkUtils::postTetherInterfaceList,
   NetworkUtils::disableNat,
   NetworkUtils::setIpForwardingEnabled,
   NetworkUtils::stopTethering,
   NetworkUtils::usbTetheringSuccess
 };
 
@@ -729,16 +737,58 @@ void NetworkUtils::tetherInterface(Comma
                                    NetworkResultOptions& aResult)
 {
   char command[MAX_COMMAND_SIZE];
   snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface add %s", GET_CHAR(mIfname));
 
   doCommand(command, aChain, aCallback);
 }
 
+void NetworkUtils::addInterfaceToLocalNetwork(CommandChain* aChain,
+                                              CommandCallback aCallback,
+                                              NetworkResultOptions& aResult)
+{
+  // Skip the command for sdk version < 20.
+  if (SDK_VERSION < 20) {
+    aResult.mResultCode = 0;
+    aResult.mResultReason = NS_ConvertUTF8toUTF16("");
+    aCallback(aChain, false, aResult);
+    return;
+  }
+
+  char command[MAX_COMMAND_SIZE];
+  snprintf(command, MAX_COMMAND_SIZE - 1, "network interface add local %s",
+           GET_CHAR(mInternalIfname));
+
+  doCommand(command, aChain, aCallback);
+}
+
+void NetworkUtils::addRouteToLocalNetwork(CommandChain* aChain,
+                                          CommandCallback aCallback,
+                                          NetworkResultOptions& aResult)
+{
+  // Skip the command for sdk version < 20.
+  if (SDK_VERSION < 20) {
+    aResult.mResultCode = 0;
+    aResult.mResultReason = NS_ConvertUTF8toUTF16("");
+    aCallback(aChain, false, aResult);
+    return;
+  }
+
+  char command[MAX_COMMAND_SIZE];
+  uint32_t prefix = atoi(GET_CHAR(mPrefix));
+  uint32_t ip = inet_addr(GET_CHAR(mIp));
+  char* networkAddr = getNetworkAddr(ip, prefix);
+
+  snprintf(command, MAX_COMMAND_SIZE - 1, "network route add local %s %s/%s",
+           GET_CHAR(mInternalIfname), networkAddr, GET_CHAR(mPrefix));
+
+  doCommand(command, aChain, aCallback);
+}
+
 void NetworkUtils::preTetherInterfaceList(CommandChain* aChain,
                                           CommandCallback aCallback,
                                           NetworkResultOptions& aResult)
 {
   char command[MAX_COMMAND_SIZE];
   if (SDK_VERSION >= 16) {
     snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface list");
   } else {
@@ -842,22 +892,48 @@ void NetworkUtils::untetherInterface(Com
                                      NetworkResultOptions& aResult)
 {
   char command[MAX_COMMAND_SIZE];
   snprintf(command, MAX_COMMAND_SIZE - 1, "tether interface remove %s", GET_CHAR(mIfname));
 
   doCommand(command, aChain, aCallback);
 }
 
+void NetworkUtils::removeInterfaceFromLocalNetwork(CommandChain* aChain,
+                                                   CommandCallback aCallback,
+                                                   NetworkResultOptions& aResult)
+{
+  // Skip the command for sdk version < 20.
+  if (SDK_VERSION < 20) {
+    aResult.mResultCode = 0;
+    aResult.mResultReason = NS_ConvertUTF8toUTF16("");
+    aCallback(aChain, false, aResult);
+    return;
+  }
+
+  char command[MAX_COMMAND_SIZE];
+  snprintf(command, MAX_COMMAND_SIZE - 1, "network interface remove local %s",
+           GET_CHAR(mIfname));
+
+  doCommand(command, aChain, aCallback);
+}
+
 void NetworkUtils::setDnsForwarders(CommandChain* aChain,
                                     CommandCallback aCallback,
                                     NetworkResultOptions& aResult)
 {
   char command[MAX_COMMAND_SIZE];
-  snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %s %s", GET_CHAR(mDns1), GET_CHAR(mDns2));
+
+  if (SDK_VERSION >= 20) {
+    snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %d %s %s",
+             GET_FIELD(mNetId), GET_CHAR(mDns1), GET_CHAR(mDns2));
+  } else {
+    snprintf(command, MAX_COMMAND_SIZE - 1, "tether dns set %s %s",
+             GET_CHAR(mDns1), GET_CHAR(mDns2));
+  }
 
   doCommand(command, aChain, aCallback);
 }
 
 void NetworkUtils::enableNat(CommandChain* aChain,
                              CommandCallback aCallback,
                              NetworkResultOptions& aResult)
 {
@@ -2118,16 +2194,25 @@ CommandResult NetworkUtils::setWifiTethe
   if (strcmp(interfaceProperties.dns2, "")) {
     int type = getIpType(interfaceProperties.dns2);
     if (type != AF_INET6) {
       aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2);
     }
   }
   dumpParams(aOptions, "WIFI");
 
+  if (SDK_VERSION >= 20) {
+    NetIdManager::NetIdInfo netIdInfo;
+    if (!mNetIdManager.lookup(aOptions.mExternalIfname, &netIdInfo)) {
+      ERROR("No such interface: %s", GET_CHAR(mExternalIfname));
+      return -1;
+    }
+    aOptions.mNetId = netIdInfo.mNetId;
+  }
+
   if (enable) {
     NU_DBG("Starting Wifi Tethering on %s <-> %s",
            GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
     runChain(aOptions, sWifiEnableChain, wifiTetheringFail);
   } else {
     NU_DBG("Stopping Wifi Tethering on %s <-> %s",
            GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
     runChain(aOptions, sWifiDisableChain, wifiTetheringFail);
@@ -2150,16 +2235,25 @@ CommandResult NetworkUtils::setUSBTether
   if (strcmp(interfaceProperties.dns2, "")) {
     int type = getIpType(interfaceProperties.dns2);
     if (type != AF_INET6) {
       aOptions.mDns2 = NS_ConvertUTF8toUTF16(interfaceProperties.dns2);
     }
   }
   dumpParams(aOptions, "USB");
 
+  if (SDK_VERSION >= 20) {
+    NetIdManager::NetIdInfo netIdInfo;
+    if (!mNetIdManager.lookup(aOptions.mExternalIfname, &netIdInfo)) {
+      ERROR("No such interface: %s", GET_CHAR(mExternalIfname));
+      return -1;
+    }
+    aOptions.mNetId = netIdInfo.mNetId;
+  }
+
   if (enable) {
     NU_DBG("Starting USB Tethering on %s <-> %s",
            GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
     runChain(aOptions, sUSBEnableChain, usbTetheringFail);
   } else {
     NU_DBG("Stopping USB Tethering on %s <-> %s",
            GET_CHAR(mInternalIfname), GET_CHAR(mExternalIfname));
     runChain(aOptions, sUSBDisableChain, usbTetheringFail);
--- a/dom/system/gonk/NetworkUtils.h
+++ b/dom/system/gonk/NetworkUtils.h
@@ -351,23 +351,26 @@ private:
   static void clearWifiTetherParms(PARAMS);
   static void enableAlarm(PARAMS);
   static void disableAlarm(PARAMS);
   static void setQuota(PARAMS);
   static void removeQuota(PARAMS);
   static void setAlarm(PARAMS);
   static void setInterfaceUp(PARAMS);
   static void tetherInterface(PARAMS);
+  static void addInterfaceToLocalNetwork(PARAMS);
+  static void addRouteToLocalNetwork(PARAMS);
   static void preTetherInterfaceList(PARAMS);
   static void postTetherInterfaceList(PARAMS);
   static void setIpForwardingEnabled(PARAMS);
   static void tetheringStatus(PARAMS);
   static void stopTethering(PARAMS);
   static void startTethering(PARAMS);
   static void untetherInterface(PARAMS);
+  static void removeInterfaceFromLocalNetwork(PARAMS);
   static void setDnsForwarders(PARAMS);
   static void enableNat(PARAMS);
   static void disableNat(PARAMS);
   static void setDefaultInterface(PARAMS);
   static void setInterfaceDns(PARAMS);
   static void wifiTetheringSuccess(PARAMS);
   static void usbTetheringSuccess(PARAMS);
   static void networkInterfaceAlarmSuccess(PARAMS);
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -1736,22 +1736,16 @@ GCMarker::processMarkStackTop(SliceBudge
     }
 
     processMarkStackOther(tag, addr);
     return;
 
   scan_value_array:
     MOZ_ASSERT(vp <= end);
     while (vp != end) {
-        budget.step();
-        if (budget.isOverBudget()) {
-            pushValueArray(obj, vp, end);
-            return;
-        }
-
         const Value &v = *vp++;
         if (v.isString()) {
             markAndScanString(obj, v.toString());
         } else if (v.isObject()) {
             JSObject *obj2 = &v.toObject();
             if (markObject(obj, obj2)) {
                 pushValueArray(obj, vp, end);
                 obj = obj2;
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -3160,17 +3160,17 @@ GCRuntime::maybeAllocTriggerZoneGC(Zone 
     size_t usedBytes = zone->usage.gcBytes();
     size_t thresholdBytes = zone->threshold.gcTriggerBytes();
     size_t igcThresholdBytes = thresholdBytes * tunables.zoneAllocThresholdFactor();
 
     if (usedBytes >= thresholdBytes) {
         // The threshold has been surpassed, immediately trigger a GC,
         // which will be done non-incrementally.
         triggerZoneGC(zone, JS::gcreason::ALLOC_TRIGGER);
-    } else if (usedBytes >= igcThresholdBytes && interFrameGC) {
+    } else if (usedBytes >= igcThresholdBytes) {
         // Reduce the delay to the start of the next incremental slice.
         if (zone->gcDelayBytes < ArenaSize)
             zone->gcDelayBytes = 0;
         else
             zone->gcDelayBytes -= ArenaSize;
 
         if (!zone->gcDelayBytes) {
             // Start or continue an in progress incremental GC. We do this
index 9ee9cebcc1d260c22a5ed82f7892e3aa9738616b..cd2268577b9940c23e6af79d8395ead1b23def66
GIT binary patch
literal 832
zc%17D@N?(olHy`uVBq!ia0vp^1|ZDA3?vioaBgK_VAK!r32|kBfzZ%U2L}gZ<1`?{
z)6)}3#>U11$>`|l(9pD~sHo7y#F&`0h@_;rxQwXe<cNffsHCKrnB<tGq}bS$=*0Ax
zr1bdsl<0(%_~i81#FUtX^ti-~_@vaB<g~cN^mw3bVtQP1YHSivJS{dUBNikAWTeG`
z)Mvycr2#d@B>@@f5cP3DjUX~39!h2aRmLZS6aZC%1wraT3?K>D3r6uEkqn43B$NTw
z1rpBy>xGz^4lyJy8EQ^E#1N25h;>kMUb^aGU|^+`1o;IsFfuW-u(Gjpa`W)<3yMlg
zNy{rJsi<pe=^I<vIl21y1%$^XCMBn&W@KjPm(@15cXanpo;q#Lg5@h#t=qI^>$W|6
z_a8WT?8K=v=PzBoe(Uc2$IqU>eD(V6`;VW$e*6CO*Y7`n|E0Y@0E{=r7*7|+kcwMZ
zr$z=JG7xEX7G1sQO09FC;MUN>`=S#U>6W?*OwdxA_5Z*0oY{pv{?DHH?r%Sn@@$UD
zN!y*N|0kWe6lwY8Eyt?uTrm%#9{j5O*V<is_}Tp%6BM<1?H0XX>+QT^n)GSWb;%xa
zllksls^}AbvG8$quF%~xq6@Q4jd?GH%;hr+VBwmsHF3#K(Z!zAAKU(w>uovS`8biy
zu;d7*e&fmH*}jve2d(P3Sr@U}XSUzeC%ru@yEdF=)i>Gl{qZS|-7!UW3q>FPR$=~7
zzvjGvQ|5{nQmcH5y;<*;-@f%So#{{1>+C=`ht}QMorUuRPi5Hb>OS^DQ(bSF#F-L@
zzW&3Gd#38yOn6$kn0;Hx`tCzhF0<uq<hysW$kciAi>_ClLX#`~QjX6MZMa+gZCXnH
zZpJ%%t;4e;lqY}5-Ji5J$f$_PEZK8K_J>AY#{D`9zdl86d~y0v;zjX`KUUW;RTOVI
UQ{g;Q3zQ2yUHx3vIVCg!0Qmi5&j0`b
index f18e4cf1ebe026c8219a5018996d0bb88fc1bc59..2d90420bf31a68e333b66333807618ca6d945fc3
GIT binary patch
literal 606
zc%17D@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dy4+DHcTp3^>G&Iz~!NJ%#4ao5H
z^mIr_h>eYnj*bouO^%C;i;PT(NKA~4O^HfM0+KOF>9I-aF$pQLiK(#(>2Zna@ktr6
zNg44V@wC{)jJV|VxTLiBL=Xw$0;Q7E;*!&V3gVK1vOtwUK_ELFB$AXK4`cuZAtaa^
z2VsC@GoZ@AA|S;{5c4yjBFPyL6Tr4WjD<3Q<i%@o9zgGTl?3?(GcYnSv#_$Ub8zzs
z35&}rDrxGQ*t+}rhb5#Hl$BRhR#i83PM$e?>GIWE_w3t$=*XF~=PzEpe&gn?+xMS5
zefIL*r_W!$e*5wB_uqeV7FW1|ratm?aSW+o+&Vea{IG+FL%gf@)+>vq8ExGn<|^5(
zB&Ni@^Z$Q#%X2kT%!_LZ_V1fB=gGd`Pxg28_^$2z@^gm#u~))u?Sib`hqXTYbt+G_
zy45UmURr9exW?D~7f~8*#p<Wxb$S~vE;@1LpYyldhu>^qxV`pz+1>u_aSwN157?SI
zf&Z3W?$yHM>)-l`1j$r-{0SC`klmhr@2R@z9KF>)V`jd*>ixrJhB*Ih&ON#2t}6Ft
z*IZE*f4}GO-(6>7R-Dw8sXcgeq5jUP#Rul!Ii{Rd)OSrqVC|c>P5Etn?oofGpMLpw
Z&fN5bi}A;($s0jw#M9N!Wt~$(697Qi0Sy2E
index 0895a217b4cb8c4548e2b258ac27322f9de34add..c6140a983e78a84ff6a3a7b285ca2e5c167ef307
GIT binary patch
literal 898
zc%17D@N?(olHy`uVBq!ia0vp^4j|0I3?%1nZ+yeRz-ScU6XMDM1EHaz4h{~+#%Vx?
zr>CbwLPBh8ERY=?9UU5)7Lbq-8JQNEm>7|i6c?8km7E+Kn-Z0f9+Q+5nUDr#M<=Go
z$EQXoq{Sqq$HisDC#T0GWW*$-$0cUOB&WqCWyB<<#wDhMQ5sM%E;&68s1ih`#U^IN
zCZ@-N*cm_t@jx;O$W4n+N{4en?Bom}8Jh%B1{913aY5z)?ShgJDG(bZ3t`8>G{Q)b
zfglFdc9<ba5OctGLAg*iOb|-C$P2#$hF5AykY6wZBNH<VD;qlpHxD1bfQYD+oV=o{
zx`w8fwywUJor8x@Kwxl4OiFrwQF&E$b6b0NPj6rUgo%@;Oq;oI(ehPm)^6Imedpf&
zhmM~+f8pZQYu9hyz5nFd%U7@8fB5|6>-Qf&fBpXR_urodiXFh{n&s)@7*cWT?es{0
zCP#r5b{3Y9Cjz1_ADlig2nIOXIEqYl<`WdNOqu!rzj)8LV-7WiS)84m_b;bSe)Z;z
zU-<P6nv;)->U#t!zGHgY)^cY5L}50M`A_XrCUSAwxiCfSKhr+p=y%0)?j6Q~Ts19B
z!mf{$J(@n<tX;S2K-$b=QSXAwoDSv}C%u`>_PcjiLbJ+No{|YQszN1)((cav5m>g&
zvheYbthN&lYi5ci*$UZIOKy~@&HA%*?$$=Dldl^k*&dvoyhnVt`;)$Gza?{<+P;1h
z*lbw5!}-%Be#13+tK9f|7w{SDESkTiq0Ker?t(<Yr@B+yN<ZD_d0pN(?Ut&@)m{Ho
z?aD>+rhho;UZPz5<Be)u`PLg>^5y5&UAX*sSH-^y{dqSfqE8k)I(5#$EN;qq&Y2Qz
zb?ZG<SJm+zj<5+gQ+=MOko`R*u^@4&`0C?7mqmEpUZ=6QFeBxR1Z&W&j|*HEo$9k|
zUTTt^wZJFbVrga3j|z2@e_C<|9KkEjhR&K-Ymjz%vf9(z6<3ZPHk14Qk(<5l=|TP2
b;A5xMS!Av9xYZ`~g7T54tDnm{r-UW|XPkLR
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..7ad7e2e0189456dc23dba61a529e043b7c64e45e
GIT binary patch
literal 1274
zc%17D@N?(olHy`uVBq!ia0vp^2_VeD3?#3*wF4y>X9xI%xH7;%XlSUfZ(3|@ERf-s
zloTBu9gvU^6%`elm<SY%iAjq{N{Wlih>lK)NKA~2ON&ZQj!4LejZKM4N{Wd|j!sAg
z%El(9MJJ@hC8ftCrbfqS#3ZD|C#T0Irp6>>#3iN0CZ@zErNt$s#wVr5B&WqDr^Y9y
z$0el2BxS@UrpG6x$0er4CS`yGlT%}1+_=PyIG`aQjX<M-A|M1dHa!l^h)V+6n;xH>
z24aA@K$ReFVtOo)OwItAoSXqNFd1wMP&Nr<CRkYpG8d>9tQcZQ23!GHBa|BtQI?zz
zF*zO~n*n7cBdmyrs014eXT(7)MTo<=$p{;P<kPh}=YSE@Q4-`A%)rRR$;HjX%O@Zx
zDkdo<Eh8%@ub`-;Vrph?VQFP;YiIA^=;Y?<7Z4E{6&({7pOBc8oRXTJm7SYcP+U`2
z-`LdL(%RPE(b?TMd;Wrji<c~0zGCI7)oa$S+qh-xwxh?6pE!By^qI5g&R@KA{pQ^V
z4<9{#^7PsB7cbv``1tAbm#^Qx|M>aq_n*K2yfU0w85o$_JY5_^Dj471tPH-KAi(y)
z*CNX1mIxQusfP<g0|iBTS3FqQ5%y|r$Vv?jk+r87b!W+~y|qF9wxHzZtbd<xO3r-w
zo;fGo>BR3n=P&(VwCvcX;F&dclQ-#J66gxA3^tWh+A-sBld$pz#p$YL?HpRqKFQya
z`0~^GkGQ&vMd3F=b&1ud>{f{_yz%C=+7yxBUV>h2wW%Dc=l6YT5!`chmVE->vl*iD
z7aJ?5U8sMujdR+C^_dUST2(*T&$675%K3NYi<!ck?P}cW-z@4sy5dm5-G%uV&X+#^
zYV?28JPzTDH%sp{CVyGm;TimP%fkY((v-5jV$EXJytlSI_TwykVQw$-xLf(FipgC6
zW!L}dZZnC|?ELv%_mGjFPUpwV)tou!w@B=-xul@;dR~Q~cepgi>bxZ<=DDApAQ4?*
zU#aLMT_t|>hk5J)iQlC*PFL(ykFB3$w860Kt&fw<pHIDR6+KhuxjZxsoAtW!c!Xy4
zEtlgFFHc7>$R#gLnVr|Ocdcyl{Osd5`fnL-Tk)5<@0)Vf^1ASg`*P<7@6+G3wC3^C
z?d+lN-UJm*4|=VzugtOcp35oTSyE^Fr`%(Izi!UP7fYF{=WS!Uz5UXmCr8zkZdvO2
z{XLb@@S$vf!uhQIo)yQ%ZW!P8o#a)Pqqt{-&;Hbzb%|DSKcAEzl|JF4{_yJmy7n%C
zxF?L&UellI7R>&e^G|re?ssz%&tEERuDdmF#e!Ygi}PpN>RZ2PSGG(kzgys2##w&Y
z<uA{>b<ru(K{9qxN6W9@-K?Woa{bwXRc?x^tZv#;Q)gYVS{TIT_4{(v)@3<qQ+K~!
z(7k@z$EOzi?%n5Fct0#&D0BVJE8?ZMR=d4TUHNTScy;35r(y3*P46?hU%V<P*_j><
PD!)8k{an^LB{Ts50Q5Z7
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -145,10 +145,14 @@ skip-if = android_version == "10"
 [testJavascriptBridge]
 [testNativeCrypto]
 [testSessionHistory]
 
 # testSelectionHandler disabled on Android 2.3 by trailing skip-if, due to bug 980074
 [testSelectionHandler]
 skip-if = android_version == "10"
 
+# testTextareaSelections disabled on Android 2.3 by trailing skip-if, due to bug 980074
+[testTextareaSelections]
+skip-if = android_version == "10"
+
 [testStumblerSetting]
 skip-if = android_version == "10"
--- a/mobile/android/base/tests/roboextender/testSelectionHandler.html
+++ b/mobile/android/base/tests/roboextender/testSelectionHandler.html
@@ -22,16 +22,21 @@ Cu.import("resource://gre/modules/Servic
  */
 function startTests() {
   testSelectAllDivs().
     then(testSelectDivAtPoint).
     then(testSelectInput).
     then(testSelectTextarea).
     then(testReadonlyInput).
     then(testCloseSelection).
+    then(testStartSelectionFail).
+
+    then(testAttachCaret).
+    then(testAttachCaretFail).
+
     then(finishTests, function(err) {
       ok(false, "Error in selection test " + err);
       finishTests();
     });
 }
 
 /* =================================================================================
  *
@@ -46,20 +51,22 @@ function testSelectAllDivs() {
   // Check the initial state of the selection handler, and selectable/non-selectable <div>s.
   return Promise.all([
     ok(!sh.isSelectionActive(), "Selection should not be active at start of testSelectAllDivs"),
     ok(sh.canSelect(selDiv), "Can select selectable <div>"),
     ok(!sh.canSelect(nonSelDiv), "Can't select non-selectable <div>"),
 
   ]).then(function() {
     // Select all on a non-editable text node selects all the text in the page.
-    sh.startSelection(selDiv);
+    var startSelectionResult = sh.startSelection(selDiv);
     var selection = sh._getSelection();
 
     return Promise.all([
+      is(startSelectionResult, sh.ERROR_NONE,
+        "startSelection() should have completed successfully"),
       ok(sh.isSelectionActive(), "Selection should be active now"),
       is(selection.anchorNode, document.documentElement, "Anchor Node should be start of document"),
       is(selection.anchorOffset, 0, "Anchor offset should be 0"),
       is(selection.focusNode, document.body.lastChild, "Focus node should be lastChild of document"),
       is(selection.focusOffset, document.body.lastChild.textContent.length, "Focus offset should be it's length"),
     ]);
   });
 }
@@ -71,34 +78,36 @@ function testSelectAllDivs() {
  *
  */
 function testSelectDivAtPoint() {
   var sh = getSelectionHandler();
   var selDiv = document.getElementById("selDiv");
 
   // Select word at point in <div>
   var rect = selDiv.getBoundingClientRect();
-  sh.startSelection(selDiv, {
+  var startSelectionResult = sh.startSelection(selDiv, {
     mode: sh.SELECT_AT_POINT,
     x: rect.left + 1,
     y: rect.top + 1
   });
   var selection = sh._getSelection();
 
   // Check the state of the selection handler after selecting at a point.
   return Promise.all([
     ok(sh.isSelectionActive(), "Selection should be active at start of testSelectDivAtPoint"),
+    is(startSelectionResult, sh.ERROR_NONE,
+      "startSelection() should have completed successfully"),
     is(selection.toString(), DIV_POINT_TEXT, "The first word in the <div> was selected"),
 
   ]).then(function() {
     // Check the state of the selection handler after collapsing a selection.
     selection.collapseToStart();
 
     return Promise.all([
-      ok(selection.getRangeAt(0).collapsed, "Selection should be collapsed"),
+      ok(selection.collapsed, "Selection should be collapsed"),
       ok(!sh.isSelectionActive(), "Selection should not be active"),
     ]);
   });
 }
 
 /* =================================================================================
  *
  * "Select all" text selection test, for <input> (editable) field.
@@ -111,20 +120,22 @@ function testSelectInput() {
 
   // Test that calling startSelection with an input selects all the text in the input.
   return Promise.all([
     ok(!sh.isSelectionActive(), "Selection should not be active at start of testSelectInput"),
     ok(sh.canSelect(inputNode), "Can select selectable <input>"),
 
   ]).then(function() {
     // Check the state of the selection handler after calling startSelection on it.
-    sh.startSelection(inputNode);
+    var startSelectionResult = sh.startSelection(inputNode);
     var selection = sh._getSelection();
 
     return Promise.all([
+      is(startSelectionResult, sh.ERROR_NONE,
+        "startSelection() should have completed successfully"),
       ok(sh.isSelectionActive(), "Selection should be active"),
       ok((sh._targetElement instanceof Ci.nsIDOMNSEditableElement), "Selected element is editable"),
       is(selection.toString(), INPUT_TEXT, "All text in the <input> was selected"),
     ]);
   });
 }
 
 /* =================================================================================
@@ -134,21 +145,23 @@ function testSelectInput() {
  */
 
 function testSelectTextarea() {
   var sh = getSelectionHandler();
   var textareaNode = document.getElementById("textareaNode");
   textareaNode.value = TEXTAREA_TEXT;
 
   // Change (still-active) selection from previous <input> field to <textarea>
-  sh.startSelection(textareaNode);
+  var startSelectionResult = sh.startSelection(textareaNode);
   var selection = sh._getSelection();
 
   return Promise.all([
     ok(sh.isSelectionActive(), "Selection should be active at start of testSelectTextarea"),
+    is(startSelectionResult, sh.ERROR_NONE,
+      "startSelection() should have completed successfully"),
     ok((sh._targetElement instanceof Ci.nsIDOMHTMLTextAreaElement), "Selected element is editable, and a <textarea>"),
     is(selection.toString(), TEXTAREA_TEXT, "All text in the <textarea> was selected"),
 
   ]).then(function() {
     // Collpase the selection to close it again.
     selection.collapseToStart();
 
     return Promise.all([
@@ -217,16 +230,131 @@ function testCloseSelection() {
     sh.startSelection(inputNode);
     sh.handleEvent({ type: "blur" });
     return ok(!sh.isSelectionActive(), "blur should close active selection");
   });
 }
 
 /* =================================================================================
  *
+ * Various text selection tests to ensure we fail certain startSelection() requests.
+ *
+ */
+function testStartSelectionFail() {
+  var sh = getSelectionHandler();
+
+  return Promise.all([
+    ok(!sh.isSelectionActive(),
+      "Selection should not be active at start of testStartSelectionFail"),
+
+  ]).then(function() {
+    // We cannot perform an invalid selection request.
+    var element = document.getElementById("inputNode");
+    var rect = element.getBoundingClientRect();
+    var startSelectionResult = sh.startSelection(element, {
+      mode: "fooMode",
+      x: rect.left + 1,
+      y: rect.top + 1
+    });
+
+    return Promise.all([
+      is(startSelectionResult, sh.START_ERROR_INVALID_MODE,
+        "startSelection() should have failed predictably."),
+      ok(!sh.isSelectionActive(), "We cannot select text with a bad mode request."),
+    ]);
+
+  }).then(function() {
+    // Select all on a Button should fail.
+    var element = document.getElementById("inputButton");
+    var startSelectionResult = sh.startSelection(element);
+
+    return Promise.all([
+      is(startSelectionResult, sh.START_ERROR_NONTEXT_INPUT,
+        "startSelection() should have failed predictably."),
+      ok(!sh.isSelectionActive(), "We cannot select text in an input Button."),
+    ]);
+
+  }).then(function() {
+    // We cannot Select Word where no point exists.
+    var element = document.getElementById("inputNode");
+    var rect = element.getBoundingClientRect();
+    var startSelectionResult = sh.startSelection(element, {
+      mode: sh.SELECT_AT_POINT,
+      x: -1000,
+      y: -1000
+    });
+
+    return Promise.all([
+      is(startSelectionResult, sh.START_ERROR_SELECT_WORD_FAILED,
+        "startSelection() should have failed predictably."),
+      ok(!sh.isSelectionActive(), "We cannot select text at a bad location request."),
+    ]);
+  });
+}
+
+/* =================================================================================
+ *
+ * Test to ensure we can attach a Caret to an input field.
+ *
+ */
+function testAttachCaret() {
+  var sh = getSelectionHandler();
+
+  return Promise.all([
+    ok(!sh.isSelectionActive(), "Selection should not be active at start of testAttachCaret"),
+
+  ]).then(function() {
+    var element = document.getElementById("inputNode");
+    element.value = INPUT_TEXT;
+    var attachCaretResult = sh.attachCaret(element);
+
+    return Promise.all([
+      is(attachCaretResult, sh.ERROR_NONE,
+        "attachCaret() should have completed successfully"),
+    ]);
+
+  }).then(function() {
+    sh.observe(null, "TextSelection:End", {});
+
+    return Promise.all([
+      ok(!sh.isSelectionActive(), "Selection should not be active at end of testAttachCaret"),
+    ]);
+  });
+}
+
+/* =================================================================================
+ *
+ * Test to ensure we fail certain attachCaret() requests.
+ *
+ */
+function testAttachCaretFail() {
+  var sh = getSelectionHandler();
+
+  return Promise.all([
+    is(sh._activeType, sh.TYPE_NONE,
+      "Selection should not be active at start of testAttachCaretFail."),
+
+  ]).then(function() {
+    // We cannot attach Caret into disabled input.
+    var element = document.getElementById("inputDisabled");
+    element.value = INPUT_TEXT;
+    var attachCaretResult = sh.attachCaret(element);
+
+    return Promise.all([
+      is(attachCaretResult, sh.ATTACH_ERROR_INCOMPATIBLE,
+        "attachCaret() should have failed predictably."),
+      is(sh._activeType, sh.TYPE_NONE,
+        "Selection should not be active at end of testAttachCaretFail."),
+    ]);
+  });
+}
+
+
+/* =================================================================================
+ *
  * After finish of all selection tests, wrap up and go home.
  *
  */
 function finishTests() {
   Messaging.sendRequest({
     type: "Robocop:testSelectionHandler",
     result: true,
     msg: "Done!",
@@ -297,10 +425,13 @@ function is(one, two, msg) {
       ligula interdum enim, vel varius libero sem ut ligula.</div><br>
 
     <input id="inputNode" type="text"><br>
 
     <textarea id="textareaNode"></textarea><br>
 
     <input id="readOnlyTextInput" type="text" readonly><br>
 
+    <input id="inputButton" type="button" value="Click me"><br>
+
+    <input id="inputDisabled" type="text" disabled><br>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/roboextender/testTextareaSelections.html
@@ -0,0 +1,854 @@
+<html>
+  <head>
+    <title>Automated RTL/LTR Text Selection tests for Textareas</title>
+    <meta name="viewport" content="initial-scale=1.0"/>
+    <script type="application/javascript"
+      src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+    <script type="application/javascript">
+
+// Used to create handle movement events for SelectionHandler.
+const ANCHOR = "ANCHOR";
+const FOCUS = "FOCUS";
+
+// Used to specifiy midpoint selection text left/right of center.
+const EST_SEL_TEXT_BOUND_CHARS = 5;
+
+// Used to ensure calculated coords for handle movement events get us
+// "into" the next/prev line vertically.
+const EST_SEL_LINE_CHG_PTS = 10;
+
+
+const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
+Cu.import("resource://gre/modules/Messaging.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import('resource://gre/modules/Geometry.jsm');
+
+// Distance between text selection lines. Reality tested, and also
+// Used to perform multi-line selection selections.
+var selectionLineHeight = 0;
+
+/* =================================================================================
+ *
+ * Start of all text selection tests, check initialization state.
+ */
+function startTests() {
+  testLTR_selectionPoints().
+    then(testRTL_selectionPoints).
+
+    then(test_selectionLineHeight).
+
+    then(testLTR_moveFocusHandleDown).
+    then(testLTR_moveFocusHandleUp).
+    then(testLTR_moveAnchorHandleUp).
+    then(testLTR_moveAnchorHandleDown).
+
+    then(testRTL_moveFocusHandleDown).
+    then(testRTL_moveFocusHandleUp).
+    then(testRTL_moveAnchorHandleUp).
+    then(testRTL_moveAnchorHandleDown).
+
+    then(finishTests, function(err) {
+      ok(false, "Error in selection test " + err);
+      finishTests();
+    });
+}
+
+/* =================================================================================
+ *
+ * LTR Textarea test will create a single line selection in the middle of the element
+ * and ensure that the anchor point is to the left of the focus point.
+ */
+function testLTR_selectionPoints() {
+  // Select entire LTRTextArea.
+  var sh = getSelectionHandler();
+  var element = document.getElementById("LTRTextarea");
+  sh.startSelection(element);
+
+  return Promise.all([
+    ok(sh.isSelectionActive(),
+      "testLTR_selectionPoints starts, selection should be active."),
+
+  ]).then(function() {
+    // setSelectionRange() (in editable elements), gets us a single-line selection of
+    // midpoint character +- EST_SEL_TEXT_BOUND_CHARS chars on either side.
+    var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+    element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                              midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+    // Grab values that are cleared by closing selection.
+    var selection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                      focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+    var midpointSelText = sh._getSelectedText();
+
+    // Close selection and complete test.
+    sh.observe(null, "TextSelection:End", {});
+
+    return Promise.all([
+      selectionExists(selection, "LTR Selection existed at points"),
+
+      is(midpointSelText, " plasma of", "LTR Selection should match expected text"),
+      is(selection.anchorPt.y, selection.focusPt.y,
+        "LTR Selection anchorPt should match focusPt vertically"),
+      lessThan(selection.anchorPt.x, selection.focusPt.x,
+        "LTR Selection anchorPt should be the left of focusPt"),
+      ok(!sh.isSelectionActive(),
+        "testLTR_selectionPoints finishes, selection should not be active."),
+    ]);
+  });
+}
+
+/* =================================================================================
+ *
+ * RTL Textarea test will create a single line selection in the middle of the element
+ * and ensure that the anchor point is to the right of the focus point.
+ */
+function testRTL_selectionPoints() {
+  // Select entire RTLTextArea.
+  var sh = getSelectionHandler();
+  var element = document.getElementById("RTLTextarea");
+  sh.startSelection(element);
+
+  return Promise.all([
+    ok(sh.isSelectionActive(),
+      "testRTL_selectionPoints starts, selection should be active."),
+
+  ]).then(function() {
+    // setSelectionRange() (in editable elements), gets us a single-line selection of
+    // midpoint character +- EST_SEL_TEXT_BOUND_CHARS chars on either side.
+    var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+    element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                              midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+    // Grab values that are cleared by closing selection.
+    var selection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                      focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+    var midpointSelText = sh._getSelectedText();
+
+    // Close selection and complete test.
+    sh.observe(null, "TextSelection:End", {});
+
+    return Promise.all([
+      selectionExists(selection, "RTL Selection existed at points"),
+
+      is(midpointSelText, "ל גם את הב", "RTL Selection should match expected text"),
+      is(selection.anchorPt.y, selection.focusPt.y,
+        "RTL Selection anchorPt should match focusPt vertically"),
+      greaterThan(selection.anchorPt.x, selection.focusPt.x,
+        "RTL Selection anchorPt should be to the right of focusPt"),
+      ok(!sh.isSelectionActive(),
+        "testRTL_selectionPoints finishes, selection should not be active."),
+    ]);
+  });
+}
+
+/* =================================================================================
+ *
+ * Textarea test will create (a) a single-line selection in the middle of the element,
+ * move the focus handle down a line creating (b) a two-line selection, and then
+ * ensure that the vertical distance between the bottom of (a) and (b) is > 0.
+ *
+ * The result is used later to ensure more-precise handle up/down movements.
+ */
+function test_selectionLineHeight() {
+  var sh = getSelectionHandler();
+  var element = document.getElementById("LTRTextarea");
+  var initialSelection = null;
+  var changedSelection = null;
+
+  // Select entire textarea, refine selection to midpoint string.
+  sh.startSelection(element);
+  var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+  element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                            midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+  // Note initial selection points.
+  initialSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Force selection focus to next lower line (estimate distance required).
+  sh.observe(null, "TextSelection:Move",
+    JSON.stringify({ handleType : FOCUS,
+      x : initialSelection.focusPt.x,
+      y : initialSelection.focusPt.y + EST_SEL_LINE_CHG_PTS
+    })
+  );
+  sh.observe(null, "TextSelection:Position",
+    JSON.stringify({ handleType : FOCUS })
+  );
+
+  // Note changed selection points after handle movement.
+  changedSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Note selection line height for reality test,
+  // and later handle movement calculations.
+  selectionLineHeight = changedSelection.focusPt.y - initialSelection.focusPt.y;
+
+  return Promise.all([
+    ok(sh.isSelectionActive(),
+      "test_selectionLineHeight starts, selection should be active."),
+
+  ]).then(function() {
+    // Complete test, and report.
+    sh.observe(null, "TextSelection:End", {});
+
+    return Promise.all([
+      greaterThan(selectionLineHeight, 0, "Distance from one line to another " +
+        "in a multi-line selection is greater than 0."),
+
+      ok(!sh.isSelectionActive(),
+        "test_selectionLineHeight finishes, selection should not be active."),
+    ]);
+  });
+}
+
+/* =================================================================================
+ *
+ * LTR Textarea test will create a single-line selection in the middle of the element
+ * and ensure that handle reversals are detected as expected.
+ *
+ * This tests what happens during focus handle down movements.
+ */
+function testLTR_moveFocusHandleDown() {
+  var sh = getSelectionHandler();
+  var element = document.getElementById("LTRTextarea");
+  var initialSelection = null;
+  var changedSelection = null;
+
+  // Select entire textarea, refine selection to midpoint string.
+  sh.startSelection(element);
+  var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+  element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                            midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+  // Note initial selection points.
+  initialSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Force selection focus to next lower line (estimate distance required).
+  sh.observe(null, "TextSelection:Move",
+    JSON.stringify({ handleType : FOCUS,
+      x : initialSelection.focusPt.x,
+      y : initialSelection.focusPt.y + EST_SEL_LINE_CHG_PTS
+    })
+  );
+  sh.observe(null, "TextSelection:Position",
+    JSON.stringify({ handleType : FOCUS })
+  );
+
+  // Note changed selection points after handle movement.
+  changedSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Complete test, and report.
+  sh.observe(null, "TextSelection:End", {});
+
+  return Promise.all([
+    ok(true, "testLTR_moveFocusHandleDown - Test Starts."),
+
+    selectionExists(initialSelection, "LTR Initial selection existed at points"),
+    is(initialSelection.anchorPt.y, initialSelection.focusPt.y,
+      "LTR Initial selection anchorPt.y should match focusPt.y"),
+    lessThan(initialSelection.anchorPt.x, initialSelection.focusPt.x,
+      "LTR Initial selection anchorPt.x should be less than (left of) focusPt.x"),
+
+    selectionExists(changedSelection, "LTR Changed selection existed at points"),
+    pointEquals(changedSelection.anchorPt, initialSelection.anchorPt,
+      "LTR Changed selection focus handle moving down " +
+      "should not change anchor handle."),
+    greaterThan(changedSelection.focusPt.y, changedSelection.anchorPt.y,
+      "LTR Changed selection focusPt.y " +
+      "should be greater than (below) changed anchorPt.y"),
+
+    greaterThan(changedSelection.focusPt.y, initialSelection.focusPt.y,
+      "LTR Changed selection focusPt.y " +
+      "should be greater than (below) Initial selection focusPt.y"),
+  ]);
+}
+
+/* =================================================================================
+ *
+ * LTR Textarea test will create a single-line selection in the middle of the element
+ * and ensure that handle reversals are detected as expected.
+ *
+ * This tests what happens during focus handle up movements.
+ */
+
+function testLTR_moveFocusHandleUp() {
+  var sh = getSelectionHandler();
+  var element = document.getElementById("LTRTextarea");
+  var initialSelection = null;
+  var changedSelection = null;
+
+  // Select entire textarea, refine selection to midpoint string.
+  sh.startSelection(element);
+  var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+  element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                            midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+  // Note initial selection points.
+  initialSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Force selection focus to next upper line (estimate distance required).
+  sh.observe(null, "TextSelection:Move",
+    JSON.stringify({ handleType : FOCUS,
+      x : initialSelection.focusPt.x,
+      y : initialSelection.focusPt.y - selectionLineHeight - EST_SEL_LINE_CHG_PTS
+    })
+  );
+  sh.observe(null, "TextSelection:Position",
+    JSON.stringify({ handleType : FOCUS })
+  );
+
+  // Note changed selection points after handle movement.
+  changedSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Complete test, and report.
+  sh.observe(null, "TextSelection:End", {});
+
+  return Promise.all([
+    ok(true, "testLTR_moveFocusHandleUp - Test Starts."),
+
+    selectionExists(initialSelection, "LTR Initial selection existed at points"),
+    is(initialSelection.anchorPt.y, initialSelection.focusPt.y,
+      "LTR Initial selection anchorPt.y should match focusPt.y"),
+    lessThan(initialSelection.anchorPt.x, initialSelection.focusPt.x,
+      "LTR Initial selection anchorPt.x should be less than (left of) focusPt.x"),
+
+    selectionExists(changedSelection, "LTR Changed selection existed at points"),
+    pointEquals(changedSelection.focusPt, initialSelection.anchorPt,
+      "LTR Reversed Changed selection focus handle moving up " +
+      "becomes new anchor handle, " +
+      "new focus handle is initial anchor handle."),
+    greaterThan(changedSelection.focusPt.y, changedSelection.anchorPt.y,
+      "LTR Reversed Changed selection focusPt.y " +
+      "should be greater than (below) changed anchorPt.y"),
+
+    is(changedSelection.focusPt.y, initialSelection.focusPt.y,
+      "LTR Reversed Changed selection focusPt.y " +
+      "should be equal-to Initial selection focusPt.y"),
+    lessThan(changedSelection.anchorPt.y, initialSelection.anchorPt.y,
+      "LTR Reversed Changed selection anchorPt.y " +
+      "should be less than (above) Initial selection anchorPt.y"),
+  ]);
+}
+
+/* =================================================================================
+ *
+ * LTR Textarea test will create a single-line selection in the middle of the element
+ * and ensure that handle reversals are detected as expected.
+ *
+ * This tests what happens during anchor handle up movements.
+ */
+function testLTR_moveAnchorHandleUp() {
+  var sh = getSelectionHandler();
+  var element = document.getElementById("LTRTextarea");
+  var initialSelection = null;
+  var changedSelection = null;
+
+  // Select entire textarea, refine selection to midpoint string.
+  sh.startSelection(element);
+  var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+  element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                            midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+  // Note initial selection points.
+  initialSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Force selection anchor to next upper line (estimate distance required).
+  sh.observe(null, "TextSelection:Move",
+    JSON.stringify({ handleType : ANCHOR,
+      x : initialSelection.anchorPt.x,
+      y : initialSelection.anchorPt.y - selectionLineHeight - EST_SEL_LINE_CHG_PTS
+    })
+  );
+  sh.observe(null, "TextSelection:Position",
+    JSON.stringify({ handleType : ANCHOR })
+  );
+
+  // Note changed selection points after handle movement.
+  changedSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Complete test, and report.
+  sh.observe(null, "TextSelection:End", {});
+
+  return Promise.all([
+    ok(true, "testLTR_moveAnchorHandleUp - Test Starts."),
+
+    selectionExists(initialSelection, "LTR Initial selection existed at points"),
+    is(initialSelection.anchorPt.y, initialSelection.focusPt.y,
+      "LTR Initial selection anchorPt.y should match focusPt.y"),
+    lessThan(initialSelection.anchorPt.x, initialSelection.focusPt.x,
+      "LTR Initial selection anchorPt.x should be less than (left of) focusPt.x"),
+
+    selectionExists(changedSelection, "LTR Changed selection existed at points"),
+    pointEquals(changedSelection.focusPt, initialSelection.focusPt,
+      "LTR Changed selection anchor handle moving up " +
+      "should not change focus handle."),
+    greaterThan(changedSelection.focusPt.y, changedSelection.anchorPt.y,
+      "LTR Changed selection focusPt.y " +
+      "should be greater than (below) changed anchorPt.y"),
+
+    lessThan(changedSelection.anchorPt.y, initialSelection.anchorPt.y,
+      "LTR Changed selection anchorPt.y " +
+      "should be less than (above) Initial selection anchorPt.y"),
+  ]);
+}
+
+/* =================================================================================
+ *
+ * LTR Textarea test will create a single-line selection in the middle of the element
+ * and ensure that handle reversals are detected as expected.
+ *
+ * This tests what happens during anchor handle down movements.
+ */
+function testLTR_moveAnchorHandleDown() {
+  var sh = getSelectionHandler();
+  var element = document.getElementById("LTRTextarea");
+  var initialSelection = null;
+  var changedSelection = null;
+
+  // Select entire textarea, refine selection to midpoint string.
+  sh.startSelection(element);
+  var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+  element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                            midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+  // Note initial selection points.
+  initialSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Force selection anchor to next lower line (estimate distance required).
+  sh.observe(null, "TextSelection:Move",
+    JSON.stringify({ handleType : ANCHOR,
+      x : initialSelection.anchorPt.x,
+      y : initialSelection.anchorPt.y + EST_SEL_LINE_CHG_PTS
+    })
+  );
+  sh.observe(null, "TextSelection:Position",
+    JSON.stringify({ handleType : ANCHOR })
+  );
+
+  // Note changed selection points after handle movement.
+  changedSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Complete test, and report.
+  sh.observe(null, "TextSelection:End", {});
+
+  return Promise.all([
+    ok(true, "testLTR_moveAnchorHandleDown - Test Starts."),
+
+    selectionExists(initialSelection, "LTR Initial selection existed at points"),
+    is(initialSelection.anchorPt.y, initialSelection.focusPt.y,
+      "LTR Initial selection anchorPt.y should match focusPt.y"),
+    lessThan(initialSelection.anchorPt.x, initialSelection.focusPt.x,
+      "LTR Initial selection anchorPt.x should be less than (left of) focusPt.x"),
+
+    selectionExists(changedSelection, "LTR Changed selection existed at points"),
+    pointEquals(changedSelection.anchorPt, initialSelection.focusPt,
+      "LTR Reversed Changed selection anchor handle moving down " +
+      "becomes new focus handle, " +
+      "new anchor handle is initial focus handle."),
+    greaterThan(changedSelection.focusPt.y, changedSelection.anchorPt.y,
+      "LTR Reversed Changed selection focusPt.y " +
+      "should be greater than (below) changed anchorPt.y"),
+
+    is(changedSelection.anchorPt.y, initialSelection.anchorPt.y,
+      "LTR Reversed Changed selection anchorPt.y " +
+      "should be equal to Initial selection anchorPt.y"),
+    greaterThan(changedSelection.focusPt.y, initialSelection.focusPt.y,
+      "LTR Reversed Changed selection focusPt.y " +
+      "should be greater than (below) Initial selection focusPt.y"),
+  ]);
+}
+
+/* =================================================================================
+ *
+ * RTL Textarea test will create a single-line selection in the middle of the element
+ * and ensure that handle reversals are detected as expected.
+ *
+ * This tests what happens during focus handle down movements.
+ */
+function testRTL_moveFocusHandleDown() {
+  var sh = getSelectionHandler();
+  var element = document.getElementById("RTLTextarea");
+  var initialSelection = null;
+  var changedSelection = null;
+
+  // Select entire textarea, refine selection to midpoint string.
+  sh.startSelection(element);
+  var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+  element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                            midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+  // Note initial selection points.
+  initialSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Force selection focus to next lower line (estimate distance required).
+  sh.observe(null, "TextSelection:Move",
+    JSON.stringify({ handleType : FOCUS,
+      x : initialSelection.focusPt.x,
+      y : initialSelection.focusPt.y + EST_SEL_LINE_CHG_PTS
+    })
+  );
+  sh.observe(null, "TextSelection:Position",
+    JSON.stringify({ handleType : FOCUS })
+  );
+
+  // Note changed selection points after handle movement.
+  changedSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Complete test, and report.
+  sh.observe(null, "TextSelection:End", {});
+
+  return Promise.all([
+    ok(true, "testRTL_moveFocusHandleDown - Test Starts."),
+
+    selectionExists(initialSelection, "RTL Initial selection existed at points"),
+    is(initialSelection.anchorPt.y, initialSelection.focusPt.y,
+      "RTL Initial selection anchorPt.y should match focusPt.y"),
+    greaterThan(initialSelection.anchorPt.x, initialSelection.focusPt.x,
+      "RTL Initial selection anchorPt.x should be greater than (right of) focusPt.x"),
+
+    selectionExists(changedSelection, "RTL Changed selection existed at points"),
+    todo(false, "testRTL_moveFocusHandleDown: " +
+    // pointEquals(changedSelection.anchorPt, initialSelection.anchorPt,
+       "RTL Changed selection focus handle moving down " +
+       "should not change anchor handle position."),
+    todo(false, "testRTL_moveFocusHandleDown: " +
+    // greaterThan(changedSelection.focusPt.y, changedSelection.anchorPt.y,
+       "RTL Changed selection focusPt.y " +
+       "should be greater than (below) changed anchorPt.y"),
+
+    todo(false, "testRTL_moveFocusHandleDown: " +
+    // greaterThan(changedSelection.focusPt.y, initialSelection.focusPt.y,
+       "RTL Changed selection focusPt.y " +
+       "should be greater than (below) Initial selection focusPt.y"),
+  ]);
+}
+
+/* =================================================================================
+ *
+ * RTL Textarea test will create a single-line selection in the middle of the element
+ * and ensure that handle reversals are detected as expected.
+ *
+ * This tests what happens during focus handle up movements.
+ */
+function testRTL_moveFocusHandleUp() {
+  var sh = getSelectionHandler();
+  var element = document.getElementById("RTLTextarea");
+  var initialSelection = null;
+  var changedSelection = null;
+
+  // Select entire textarea, refine selection to midpoint string.
+  sh.startSelection(element);
+  var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+  element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                            midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+  // Note initial selection points.
+  initialSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Force selection focus to next upper line (estimate distance required).
+  sh.observe(null, "TextSelection:Move",
+    JSON.stringify({ handleType : FOCUS,
+      x : initialSelection.focusPt.x,
+      y : initialSelection.focusPt.y - selectionLineHeight - EST_SEL_LINE_CHG_PTS
+    })
+  );
+  sh.observe(null, "TextSelection:Position",
+    JSON.stringify({ handleType : FOCUS })
+  );
+
+  // Note changed selection points after handle movement.
+  changedSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Complete test, and report.
+  sh.observe(null, "TextSelection:End", {});
+
+  return Promise.all([
+    ok(true, "testRTL_moveFocusHandleUp - Test Starts."),
+
+    selectionExists(initialSelection, "RTL Initial selection existed at points"),
+    is(initialSelection.anchorPt.y, initialSelection.focusPt.y,
+      "RTL Initial selection anchorPt.y should match focusPt.y"),
+    greaterThan(initialSelection.anchorPt.x, initialSelection.focusPt.x,
+      "RTL Initial selection anchorPt.x should be greater than (right of) focusPt.x"),
+
+    selectionExists(changedSelection, "RTL Changed selection existed at points"),
+    todo(false, "testRTL_moveFocusHandleUp: " +
+    // pointEquals(changedSelection.focusPt, initialSelection.anchorPt,
+       "RTL Reversed Changed selection focus handle moving up " +
+       "becomes new anchor handle, " +
+       "new focus handle is initial anchor handle."),
+    todo(false, "testRTL_moveFocusHandleUp: " +
+    // greaterThan(changedSelection.focusPt.y, changedSelection.anchorPt.y,
+       "RTL Reversed Changed selection focusPt.y " +
+       "should be greater than (below) changed anchorPt.y"),
+
+    todo(false, "testRTL_moveFocusHandleUp: " +
+    // is(changedSelection.focusPt.y, initialSelection.focusPt.y,
+       "RTL Reversed Changed selection focusPt.y " +
+       "should be equal to Initial selection focusPt.y"),
+    todo(false, "testRTL_moveFocusHandleUp: " +
+    // lessThan(changedSelection.anchorPt.y, initialSelection.anchorPt.y,
+       "RTL Reversed Changed selection anchorPt.y " +
+       "should be less than (above) Initial selection anchorPt.y"),
+  ]);
+}
+
+/* =================================================================================
+ *
+ * RTL Textarea test will create a single-line selection in the middle of the element
+ * and ensure that handle reversals are detected as expected.
+ *
+ * This tests what happens during anchor handle up movements.
+ */
+function testRTL_moveAnchorHandleUp() {
+  var sh = getSelectionHandler();
+  var element = document.getElementById("RTLTextarea");
+  var initialSelection = null;
+  var changedSelection = null;
+
+  // Select entire textarea, refine selection to midpoint string.
+  sh.startSelection(element);
+  var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+  element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                            midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+  // Note initial selection points.
+  initialSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Force selection anchor to next upper line (estimate distance required).
+  sh.observe(null, "TextSelection:Move",
+    JSON.stringify({ handleType : ANCHOR,
+      x : initialSelection.anchorPt.x,
+      y : initialSelection.anchorPt.y - selectionLineHeight - EST_SEL_LINE_CHG_PTS
+    })
+  );
+  sh.observe(null, "TextSelection:Position",
+    JSON.stringify({ handleType : ANCHOR })
+  );
+
+  // Note changed selection points after handle movement.
+  changedSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Complete test, and report.
+  sh.observe(null, "TextSelection:End", {});
+
+  return Promise.all([
+    ok(true, "testRTL_moveAnchorHandleUp - Test Starts."),
+
+    selectionExists(initialSelection, "RTL Initial selection existed at points"),
+    is(initialSelection.anchorPt.y, initialSelection.focusPt.y,
+      "RTL Initial selection anchorPt.y should match focusPt.y"),
+    greaterThan(initialSelection.anchorPt.x, initialSelection.focusPt.x,
+      "RTL Initial selection anchorPt.x should be greater than (right of) focusPt.x"),
+
+    selectionExists(changedSelection, "RTL Changed selection existed at points"),
+    todo(false, "testRTL_moveAnchorHandleUp: " +
+    // pointEquals(changedSelection.focusPt, initialSelection.focusPt,
+       "RTL Changed selection anchor handle moving up " +
+       "should not change focus handle position."),
+    todo(false, "testRTL_moveAnchorHandleUp: " +
+    // greaterThan(changedSelection.focusPt.y, changedSelection.anchorPt.y,
+       "RTL Changed selection focusPt.y " +
+       "should be greater than (below) changed anchorPt.y"),
+
+    todo(false, "testRTL_moveAnchorHandleUp: " +
+    // lessThan(changedSelection.anchorPt.y, initialSelection.anchorPt.y,
+       "RTL Changed selection anchorPt.y " +
+       "should be less than (above) Initial selection anchorPt.y"),
+  ]);
+}
+
+/* =================================================================================
+ *
+ * RTL Textarea test will create a single-line selection in the middle of the element
+ * and ensure that handle reversals are detected as expected.
+ *
+ * This tests what happens during anchor handle down movements.
+ */
+function testRTL_moveAnchorHandleDown() {
+  var sh = getSelectionHandler();
+  var element = document.getElementById("RTLTextarea");
+  var initialSelection = null;
+  var changedSelection = null;
+
+  // Select entire textarea, refine selection to midpoint string.
+  sh.startSelection(element);
+  var midpointSelCharOffset = (element.selectionStart + element.selectionEnd) / 2;
+  element.setSelectionRange(midpointSelCharOffset - EST_SEL_TEXT_BOUND_CHARS,
+                            midpointSelCharOffset + EST_SEL_TEXT_BOUND_CHARS);
+
+  // Note initial selection points.
+  initialSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Force selection anchor to next lower line (estimate distance required).
+  sh.observe(null, "TextSelection:Move",
+    JSON.stringify({ handleType : ANCHOR,
+      x : initialSelection.anchorPt.x,
+      y : initialSelection.anchorPt.y + EST_SEL_LINE_CHG_PTS
+    })
+  );
+  sh.observe(null, "TextSelection:Position",
+    JSON.stringify({ handleType : ANCHOR })
+  );
+
+  // Note changed selection points after handle movement.
+  changedSelection = { anchorPt : new Point(sh._cache.anchorPt.x, sh._cache.anchorPt.y),
+                       focusPt : new Point(sh._cache.focusPt.x, sh._cache.focusPt.y) };
+
+  // Complete test, and report.
+  sh.observe(null, "TextSelection:End", {});
+
+  return Promise.all([
+    ok(true, "testRTL_moveAnchorHandleDown - Test Starts."),
+
+    selectionExists(initialSelection, "RTL Initial selection existed at points"),
+    is(initialSelection.anchorPt.y, initialSelection.focusPt.y,
+      "RTL Initial selection anchorPt.y should match focusPt.y"),
+    greaterThan(initialSelection.anchorPt.x, initialSelection.focusPt.x,
+      "RTL Initial selection anchorPt.x should be greater than (right of) focusPt.x"),
+
+    selectionExists(changedSelection, "RTL Changed selection existed at points"),
+    todo(false, "testRTL_moveAnchorHandleDown: " +
+    // pointEquals(changedSelection.anchorPt, initialSelection.focusPt,
+       "RTL Reversed Changed selection anchor handle moving down " +
+       "becomes new focus handle, " +
+       "new anchor handle is initial focus handle."),
+    todo(false, "testRTL_moveAnchorHandleDown: " +
+    // greaterThan(changedSelection.focusPt.y, changedSelection.anchorPt.y,
+       "RTL Reversed Changed selection focusPt.y " +
+       "should be greater than (below) changed anchorPt.y"),
+
+    todo(false, "testRTL_moveAnchorHandleDown: " +
+    // is(changedSelection.anchorPt.y, initialSelection.anchorPt.y,
+       "RTL Reversed Changed selection anchorPt.y " +
+       "should be equal to Initial selection anchorPt.y"),
+    todo(false, "testRTL_moveAnchorHandleDown: " +
+    // greaterThan(changedSelection.focusPt.y, initialSelection.focusPt.y,
+       "RTL Reversed Changed selection focusPt.y " +
+       "should be greater than (below) Initial selection focusPt.y"),
+  ]);
+}
+
+/* =================================================================================
+ *
+ * After finish of all selection tests, wrap up and go home.
+ *
+ */
+function finishTests() {
+  Messaging.sendRequest({
+    type: "Robocop:testTextareaSelections",
+    result: true,
+    msg: "Done!",
+    done: true
+  });
+}
+
+/* ============================== Utility functions ======================
+ *
+ * Common functions available to all tests.
+ *
+ */
+function getSelectionHandler() {
+  return (!this._selectionHandler) ?
+    this._selectionHandler = Services.wm.getMostRecentWindow("navigator:browser").SelectionHandler :
+    this._selectionHandler;
+}
+
+function todo(result, msg) {
+  return Messaging.sendRequestForResult({
+    type: "Robocop:testTextareaSelections",
+    todo: result,
+    msg: msg
+  });
+}
+
+function ok(result, msg) {
+  return Messaging.sendRequestForResult({
+    type: "Robocop:testTextareaSelections",
+    result: result,
+    msg: msg
+  });
+}
+
+function is(one, two, msg) {
+  return Messaging.sendRequestForResult({
+    type: "Robocop:testTextareaSelections",
+    result: one === two,
+    msg: msg + " : " + one + " === " + two
+  });
+}
+
+function lessThan(n1, n2, msg) {
+  return Messaging.sendRequestForResult({
+    type: "Robocop:testTextareaSelections",
+    result: n1 < n2,
+    msg: msg + " : " + n1 + " < " + n2
+  });
+}
+
+function greaterThan(n1, n2, msg) {
+  return Messaging.sendRequestForResult({
+    type: "Robocop:testTextareaSelections",
+    result: n1 > n2,
+    msg: msg + " : " + n1 + " > " + n2
+  });
+}
+
+function pointEquals(p1, p2, msg) {
+  return Messaging.sendRequestForResult({
+    type: "Robocop:testTextareaSelections",
+    result: p1.equals(p2),
+    msg: msg + " : " + p1.toString() + " == " + p2.toString()
+  });
+}
+
+function pointNotEquals(p1, p2, msg) {
+  return Messaging.sendRequestForResult({
+    type: "Robocop:testTextareaSelections",
+    result: !p1.equals(p2),
+    msg: msg + " : " + p1.toString() + " == " + p2.toString()
+  });
+}
+
+function selectionExists(selection, msg) {
+  return Messaging.sendRequestForResult({
+    type: "Robocop:testTextareaSelections",
+    result: !selection.anchorPt.equals(selection.focusPt),
+    msg: msg + " : anchor:" + selection.anchorPt.toString() +
+      " focus:" + selection.focusPt.toString()
+  });
+}
+
+/* =================================================================================
+ *
+ * Page definition for all tests.
+ *
+ */
+    </script>
+  </head>
+
+  <body onload="startTests();">
+    <textarea id="LTRTextarea" style="direction: ltr;" rows="10" cols="40"
+      readonly="true">Under sufficiently extreme conditions, quarks may become deconfined and exist as free particles. In the course of asymptotic freedom, the strong interaction becomes weaker at higher temperatures. Eventually, color confinement would be lost and an extremely hot plasma of freely moving quarks and gluons would be formed. This theoretical phase of matter is called quark-gluon plasma.[81] The exact conditions needed to give rise to this state are unknown and have been the subject of a great deal of speculation and experimentation.</textarea>
+
+    <textarea id="RTLTextarea" style="direction: rtl;" rows="10" cols="40"
+      readonly="true">טטיאנה קוזמינה, שהייתה 18, תלמיד תיכון בעפולה, עלה לישראל לפני כשנים עם האמא שלה, שהיה נשואה לאזרח ישראלי, כאשר אביה הביולוגי חתם על מסמך המאשר את המהלך שלה לישראל. האמא שלה היא בתהליך של התאזרחות חשב שזה כולל גם את הבת שלה, אבל ברגע שהיא הבינה כבר לפני כמה חודשים שהחברה המאוחדת לא נכללה בו, דחה את הבקשה לעבד גם לבת שלה. ואז הם קיבלו את הגור.וחד-קרן הגיעה, אבל הם לא הצליחו למצוא את קשת אז כולם אכלו ספגטי וכנפיים בופל חמים.</textarea>
+  </body>
+
+</html>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testTextareaSelections.java
@@ -0,0 +1,50 @@
+package org.mozilla.gecko.tests;
+
+import org.mozilla.gecko.Actions;
+import org.mozilla.gecko.EventDispatcher;
+import org.mozilla.gecko.tests.helpers.GeckoHelper;
+import org.mozilla.gecko.tests.helpers.NavigationHelper;
+
+import android.util.Log;
+
+import org.json.JSONObject;
+
+
+public class testTextareaSelections extends UITest {
+
+    public void testTextareaSelections() {
+        GeckoHelper.blockForReady();
+
+        Actions.EventExpecter robocopTestExpecter =
+            getActions().expectGeckoEvent("Robocop:testTextareaSelections");
+        final String url = "chrome://roboextender/content/testTextareaSelections.html";
+        NavigationHelper.enterAndLoadUrl(url);
+        mToolbar.assertTitle(url);
+
+        while (!test(robocopTestExpecter)) {
+            // do nothing
+        }
+
+        robocopTestExpecter.unregisterListener();
+    }
+
+    private boolean test(Actions.EventExpecter expecter) {
+        final JSONObject eventData;
+        try {
+            eventData = new JSONObject(expecter.blockForEventData());
+        } catch(Exception ex) {
+            // Log and ignore
+            getAsserter().ok(false, "JS Test", "Error decoding data " + ex);
+            return false;
+        }
+
+        if (eventData.has("result")) {
+            getAsserter().ok(eventData.optBoolean("result"), "JS Test", eventData.optString("msg"));
+        } else if (eventData.has("todo")) {
+            getAsserter().todo(eventData.optBoolean("todo"), "JS TODO", eventData.optString("msg"));
+        }
+
+        EventDispatcher.sendResponse(eventData, new JSONObject());
+        return eventData.optBoolean("done", false);
+    }
+}
--- a/mobile/android/base/toolbar/ToolbarDisplayLayout.java
+++ b/mobile/android/base/toolbar/ToolbarDisplayLayout.java
@@ -198,20 +198,16 @@ public class ToolbarDisplayLayout extend
 
     @Override
     public void onAttachedToWindow() {
         mIsAttached = true;
 
         Button.OnClickListener faviconListener = new Button.OnClickListener() {
             @Override
             public void onClick(View view) {
-                if (mSiteSecurity.getVisibility() != View.VISIBLE) {
-                    return;
-                }
-
                 mSiteIdentityPopup.show();
             }
         };
 
         mFavicon.setOnClickListener(faviconListener);
         mSiteSecurity.setOnClickListener(faviconListener);
 
         mStop.setOnClickListener(new Button.OnClickListener() {
--- a/mobile/android/chrome/content/SelectionHandler.js
+++ b/mobile/android/chrome/content/SelectionHandler.js
@@ -1,18 +1,35 @@
 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 // Define elements that bound phone number containers.
 const PHONE_NUMBER_CONTAINERS = "td,div";
+const DEFER_CLOSE_TRIGGER_MS = 125; // Grace period delay before deferred _closeSelection()
 
 var SelectionHandler = {
+
+  // Successful startSelection() or attachCaret().
+  ERROR_NONE: "",
+
+  // Error codes returned during startSelection().
+  START_ERROR_INVALID_MODE: "Invalid selection mode requested.",
+  START_ERROR_NONTEXT_INPUT: "Target element by definition contains no text.",
+  START_ERROR_NO_WORD_SELECTED: "No word selected at point.",
+  START_ERROR_SELECT_WORD_FAILED: "Word selection at point failed.",
+  START_ERROR_SELECT_ALL_PARAGRAPH_FAILED: "Select-All Paragraph failed.",
+  START_ERROR_NO_SELECTION: "Selection performed, but nothing resulted.",
+  START_ERROR_PROXIMITY: "Selection target and result seem unrelated.",
+
+  // Error codes returned during attachCaret().
+  ATTACH_ERROR_INCOMPATIBLE: "Element disabled, handled natively, or not editable.",
+
   HANDLE_TYPE_ANCHOR: "ANCHOR",
   HANDLE_TYPE_CARET: "CARET",
   HANDLE_TYPE_FOCUS: "FOCUS",
 
   TYPE_NONE: 0,
   TYPE_CURSOR: 1,
   TYPE_SELECTION: 2,
 
@@ -21,16 +38,17 @@ var SelectionHandler = {
 
   // Keeps track of data about the dimensions of the selection. Coordinates
   // stored here are relative to the _contentWindow window.
   _cache: null,
   _activeType: 0, // TYPE_NONE
   _draggingHandles: false, // True while user drags text selection handles
   _ignoreCompositionChanges: false, // Persist caret during IME composition updates
   _prevHandlePositions: [], // Avoid issuing duplicate "TextSelection:Position" messages
+  _deferCloseTimer: null, // Used to defer _closeSelection() actions during programmatic changes
 
   // TargetElement changes (text <--> no text) trigger actionbar UI update
   _prevTargetElementHasText: null,
 
   // The window that holds the selection (can be a sub-frame)
   get _contentWindow() {
     if (this._contentWindowRef)
       return this._contentWindowRef.get();
@@ -84,16 +102,21 @@ var SelectionHandler = {
     Services.obs.removeObserver(this, "TextSelection:LayerReflow");
 
     BrowserApp.deck.removeEventListener("pagehide", this, false);
     BrowserApp.deck.removeEventListener("blur", this, true);
     BrowserApp.deck.removeEventListener("scroll", this, true);
   },
 
   observe: function sh_observe(aSubject, aTopic, aData) {
+    // Ignore all but selectionListener notifications during deferred _closeSelection().
+    if (this._deferCloseTimer) {
+      return;
+    }
+
     switch (aTopic) {
       // Update handle/caret position on page reflow (keyboard open/close,
       // dynamic DOM changes, orientation updates, etc).
       case "TextSelection:LayerReflow": {
         if (this._activeType == this.TYPE_SELECTION) {
           this._updateCacheForSelection();
         }
         if (this._activeType != this.TYPE_NONE) {
@@ -211,16 +234,21 @@ var SelectionHandler = {
   _stopDraggingHandles: function sh_stopDraggingHandles() {
     if (this._draggingHandles) {
       this._draggingHandles = false;
       Messaging.sendRequest({ type: "TextSelection:DraggingHandle", dragging: false });
     }
   },
 
   handleEvent: function sh_handleEvent(aEvent) {
+    // Ignore all but selectionListener notifications during deferred _closeSelection().
+    if (this._deferCloseTimer) {
+      return;
+    }
+
     switch (aEvent.type) {
       case "scroll":
         // Maintain position when top-level document is scrolled
         this._positionHandlesOnChange();
         break;
 
       case "pagehide": {
         // We only care about events on the selected tab.
@@ -263,33 +291,44 @@ var SelectionHandler = {
     this._contentWindow.top.QueryInterface(Ci.nsIInterfaceRequestor).
                             getInterface(Ci.nsIDOMWindowUtils).getScrollXY(false, scrollX, scrollY);
     return {
       X: scrollX.value,
       Y: scrollY.value
     };
   },
 
+  /**
+   * Observe and react to programmatic SelectionChange notifications.
+   */
   notifySelectionChanged: function sh_notifySelectionChanged(aDocument, aSelection, aReason) {
+    // Cancel any in-progress / deferred _closeSelection() action.
+    this._cancelDeferredCloseSelection();
+
     // Ignore selectionChange notifications during handle movements
     if (this._draggingHandles) {
       return;
     }
 
     // If the selection was collapsed to Start or to End, always close it
     if ((aReason & Ci.nsISelectionListener.COLLAPSETOSTART_REASON) ||
         (aReason & Ci.nsISelectionListener.COLLAPSETOEND_REASON)) {
       this._closeSelection();
       return;
     }
 
-    // If selected text no longer exists, close
+    // If selected text no longer exists, schedule a deferred close action.
     if (!aSelection.toString()) {
-      this._closeSelection();
+      this._deferCloseSelection();
+      return;
     }
+
+    // Update the selection handle positions.
+    this._updateCacheForSelection();
+    this._positionHandles();
   },
 
   /*
    * Called from browser.js when the user long taps on text or chooses
    * the "Select Word" context menu item. Initializes SelectionHandler,
    * starts a selection, and positions the text selection handles.
    *
    * @param aOptions list of options describing how to start selection
@@ -299,90 +338,102 @@ var SelectionHandler = {
    *                   x    - The x-coordinate for SELECT_AT_POINT.
    *                   y    - The y-coordinate for SELECT_AT_POINT.
    */
   startSelection: function sh_startSelection(aElement, aOptions = { mode: SelectionHandler.SELECT_ALL }) {
     // Clear out any existing active selection
     this._closeSelection();
 
     if (this._isNonTextInputElement(aElement)) {
-      return false;
+      return this.START_ERROR_NONTEXT_INPUT;
     }
 
     this._initTargetInfo(aElement, this.TYPE_SELECTION);
 
     // Perform the appropriate selection method, if we can't determine method, or it fails, return
-    if (!this._performSelection(aOptions)) {
+    let selectionResult = this._performSelection(aOptions);
+    if (selectionResult !== this.ERROR_NONE) {
       this._deactivate();
-      return false;
+      return selectionResult;
     }
 
     // Double check results of successful selection operation
     let selection = this._getSelection();
-    if (!selection || selection.rangeCount == 0 || selection.getRangeAt(0).collapsed) {
+    if (!selection ||
+        selection.rangeCount == 0 ||
+        selection.getRangeAt(0).collapsed ||
+        this._getSelectedText().length == 0) {
       this._deactivate();
-      return false;
+      return this.START_ERROR_NO_SELECTION;
     }
 
     // Add a listener to end the selection if it's removed programatically
     selection.QueryInterface(Ci.nsISelectionPrivate).addSelectionListener(this);
     this._activeType = this.TYPE_SELECTION;
 
     // Initialize the cache
     this._cache = { anchorPt: {}, focusPt: {}};
     this._updateCacheForSelection();
 
     let scroll = this._getScrollPos();
     // Figure out the distance between the selection and the click
     let positions = this._getHandlePositions(scroll);
 
-    if (aOptions.mode == this.SELECT_AT_POINT && !this._selectionNearClick(scroll.X + aOptions.x,
-                                                                      scroll.Y + aOptions.y,
-                                                                      positions)) {
+    if (aOptions.mode == this.SELECT_AT_POINT &&
+        !this._selectionNearClick(scroll.X + aOptions.x, scroll.Y + aOptions.y, positions)) {
         this._closeSelection();
-        return false;
+        return this.START_ERROR_PROXIMITY;
     }
 
     // Determine position and show handles, open actionbar
     this._positionHandles(positions);
     Messaging.sendRequest({
       type: "TextSelection:ShowHandles",
       handles: [this.HANDLE_TYPE_ANCHOR, this.HANDLE_TYPE_FOCUS]
     });
     this._updateMenu();
-    return true;
+    return this.ERROR_NONE;
   },
 
   /*
    * Called to perform a selection operation, given a target element, selection method, starting point etc.
    */
   _performSelection: function sh_performSelection(aOptions) {
     if (aOptions.mode == this.SELECT_AT_POINT) {
       // Clear any ranges selected outside SelectionHandler, by code such as Find-In-Page.
       this._contentWindow.getSelection().removeAllRanges();
-      if (!this._domWinUtils.selectAtPoint(aOptions.x, aOptions.y, Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE)) {
-        return false;
+      try {
+        if (!this._domWinUtils.selectAtPoint(aOptions.x, aOptions.y, Ci.nsIDOMWindowUtils.SELECT_WORDNOSPACE)) {
+          return this.START_ERROR_NO_WORD_SELECTED;
+        }
+      } catch (e) {
+        return this.START_ERROR_SELECT_WORD_FAILED;
       }
 
       // Perform additional phone-number "smart selection".
       if (this._isPhoneNumber(this._getSelection().toString())) {
         this._selectSmartPhoneNumber();
       }
 
-      return true;
+      return this.ERROR_NONE;
     }
 
+    // Only selectAll() assumed from this point.
     if (aOptions.mode != this.SELECT_ALL) {
-      Cu.reportError("SelectionHandler.js: _performSelection() Invalid selection mode " + aOptions.mode);
-      return false;
+      return this.START_ERROR_INVALID_MODE;
     }
 
     // HTMLPreElement is a #text node, SELECT_ALL implies entire paragraph
     if (this._targetElement instanceof HTMLPreElement)  {
-      return this._domWinUtils.selectAtPoint(1, 1, Ci.nsIDOMWindowUtils.SELECT_PARAGRAPH);
+      try {
+        this._domWinUtils.selectAtPoint(1, 1, Ci.nsIDOMWindowUtils.SELECT_PARAGRAPH);
+        return this.ERROR_NONE;
+      } catch (e) {
+        return this.START_ERROR_SELECT_ALL_PARAGRAPH_FAILED;
+      }
     }
 
     // Else default to selectALL Document
     let editor = this._getEditor();
     if (editor) {
       editor.selectAll();
     } else {
       this._getSelectionController().selectAll();
@@ -399,17 +450,17 @@ var SelectionHandler = {
       try {
         selection.extend(lastNode, lastNode.length);
       } catch (e) {
         Cu.reportError("SelectionHandler.js: _performSelection() whitespace trim fails: lastNode[" + lastNode +
           "] lastNode.length[" + lastNode.length + "]");
       }
     }
 
-    return true;
+    return this.ERROR_NONE;
   },
 
   /*
    * Called to expand a selection that appears to represent a phone number. This enhances the basic
    * SELECT_WORDNOSPACE logic employed in performSelection() in response to long-tap / selecting text.
    */
   _selectSmartPhoneNumber: function() {
     this._extendPhoneNumberSelection("forward");
@@ -703,17 +754,17 @@ var SelectionHandler = {
    * Called by BrowserEventHandler when the user taps in a form input.
    * Initializes SelectionHandler and positions the caret handle.
    *
    * @param aX, aY tap location in client coordinates.
    */
   attachCaret: function sh_attachCaret(aElement) {
     // Ensure it isn't disabled, isn't handled by Android native dialog, and is editable text element
     if (aElement.disabled || InputWidgetHelper.hasInputWidget(aElement) || !this.isElementEditableText(aElement)) {
-      return false;
+      return this.ATTACH_ERROR_INCOMPATIBLE;
     }
 
     this._initTargetInfo(aElement, this.TYPE_CURSOR);
 
     // Caret-specific observer/listeners
     BrowserApp.deck.addEventListener("keyup", this, false);
     BrowserApp.deck.addEventListener("compositionupdate", this, false);
     BrowserApp.deck.addEventListener("compositionend", this, false);
@@ -723,17 +774,17 @@ var SelectionHandler = {
     // Determine position and show caret, open actionbar
     this._positionHandles();
     Messaging.sendRequest({
       type: "TextSelection:ShowHandles",
       handles: [this.HANDLE_TYPE_CARET]
     });
     this._updateMenu();
 
-    return true;
+    return this.ERROR_NONE;
   },
 
   // Target initialization for both TYPE_CURSOR and TYPE_SELECTION
   _initTargetInfo: function sh_initTargetInfo(aElement, aSelectionType) {
     this._targetElement = aElement;
     if (aElement instanceof Ci.nsIDOMNSEditableElement) {
       if (aSelectionType === this.TYPE_SELECTION) {
         // Blur the targetElement to force IME code to undo previous style compositions
@@ -975,38 +1026,89 @@ var SelectionHandler = {
   callSelection: function sh_callSelection() {
     let selectedText = this._getSelectedPhoneNumber();
     if (selectedText) {
       BrowserApp.loadURI("tel:" + selectedText);
     }
     this._closeSelection();
   },
 
+  /**
+   * Deferred _closeSelection() actions allow for brief periods where programmatic
+   * selection changes have effectively closed the selection, but we anticipate further
+   * activity that may restore it.
+   *
+   * At this point, we hide the UI handles, and stop responding to messages until
+   * either the final _closeSelection() is triggered, or until our Gecko selectionListener
+   * notices a subsequent programmatic selection that results in a new selection.
+   */
+  _deferCloseSelection: function() {
+    // Schedule the deferred _closeSelection() action.
+    this._deferCloseTimer = setTimeout((function() {
+      // Time is up! Close the selection.
+      this._deferCloseTimer = null;
+      this._closeSelection();
+    }).bind(this), DEFER_CLOSE_TRIGGER_MS);
+
+    // Hide any handles while deferClosed.
+    if (this._prevHandlePositions.length) {
+      let positions = this._prevHandlePositions;
+      for (let i in positions) {
+        positions[i].hidden = true;
+      }
+
+      Messaging.sendRequest({
+        type: "TextSelection:PositionHandles",
+        positions: positions,
+        rtl: this._isRTL
+      });
+    }
+  },
+
+  /**
+   * Cancel any current deferred _closeSelection() action.
+   */
+  _cancelDeferredCloseSelection: function() {
+    if (this._deferCloseTimer) {
+      clearTimeout(this._deferCloseTimer);
+      this._deferCloseTimer = null;
+    }
+  },
+
   /*
    * Shuts SelectionHandler down.
    */
   _closeSelection: function sh_closeSelection() {
     // Bail if there's no active selection
     if (this._activeType == this.TYPE_NONE)
       return;
 
     if (this._activeType == this.TYPE_SELECTION)
       this._clearSelection();
 
     this._deactivate();
   },
 
   _clearSelection: function sh_clearSelection() {
+    // Cancel any in-progress / deferred _closeSelection() process.
+    this._cancelDeferredCloseSelection();
+
     let selection = this._getSelection();
     if (selection) {
       // Remove our listener before we clear the selection
       selection.QueryInterface(Ci.nsISelectionPrivate).removeSelectionListener(this);
-      // Clear selection without clearing the anchorNode or focusNode
+
+      // Remove the selection. For editables, we clear selection without losing
+      // element focus. For non-editables, just clear all.
       if (selection.rangeCount != 0) {
-        selection.collapseToStart();
+        if (this.isElementEditableText(this._targetElement)) {
+          selection.collapseToStart();
+        } else {
+          selection.removeAllRanges();
+        }
       }
     }
   },
 
   _deactivate: function sh_deactivate() {
     this._stopDraggingHandles();
     // Hide handle/caret, close actionbar
     Messaging.sendRequest({ type: "TextSelection:HideHandles" });
@@ -1165,16 +1267,21 @@ var SelectionHandler = {
     let currTargetElementHasText = (this._targetElement.textLength > 0);
     if (currTargetElementHasText != this._prevTargetElementHasText) {
       this._prevTargetElementHasText = currTargetElementHasText;
       this._updateMenu();
     }
   },
 
   subdocumentScrolled: function sh_subdocumentScrolled(aElement) {
+    // Ignore all but selectionListener notifications during deferred _closeSelection().
+    if (this._deferCloseTimer) {
+      return;
+    }
+
     if (this._activeType == this.TYPE_NONE) {
       return;
     }
     let scrollView = aElement.ownerDocument.defaultView;
     let view = this._contentWindow;
     while (true) {
       if (view == scrollView) {
         // The selection is in a view (or sub-view) of the view that scrolled.
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -2602,24 +2602,30 @@ var NativeWindow = {
       // If no context-menu for long-press event, it may be meant to trigger text-selection.
       this.menus = null;
       Services.obs.notifyObservers(
         {target: this._target, x: event.clientX, y: event.clientY}, "context-menu-not-shown", "");
 
       if (SelectionHandler.canSelect(this._target)) {
         // If textSelection WORD is successful,
         // consume / preventDefault the context menu event.
-        if (SelectionHandler.startSelection(this._target,
-          { mode: SelectionHandler.SELECT_AT_POINT, x: event.clientX, y: event.clientY })) {
+        let selectionResult = SelectionHandler.startSelection(this._target,
+          { mode: SelectionHandler.SELECT_AT_POINT,
+            x: event.clientX,
+            y: event.clientY
+          }
+        );
+        if (selectionResult === SelectionHandler.ERROR_NONE) {
           event.preventDefault();
           return;
         }
+
         // If textSelection caret-attachment is successful,
         // consume / preventDefault the context menu event.
-        if (SelectionHandler.attachCaret(this._target)) {
+        if (SelectionHandler.attachCaret(this._target) === SelectionHandler.ERROR_NONE) {
           event.preventDefault();
           return;
         }
       }
     },
 
     // Returns a title for a context menu. If no title attribute exists, will fall back to looking for a url
     _getTitle: function(node) {
@@ -5085,17 +5091,20 @@ var BrowserEventHandler = {
         this._cancelTapHighlight();
         break;
 
       case "Gesture:SingleTap": {
         try {
           // If the element was previously focused, show the caret attached to it.
           let element = this._highlightElement;
           if (element && element == BrowserApp.getFocusedInput(BrowserApp.selectedBrowser)) {
-            SelectionHandler.attachCaret(element);
+            let result = SelectionHandler.attachCaret(element);
+            if (result !== SelectionHandler.ERROR_NONE) {
+              dump("Unexpected failure during caret attach: " + result);
+            }
           }
         } catch(e) {
           Cu.reportError(e);
         }
 
         // The _highlightElement was chosen after fluffing the touch events
         // that led to this SingleTap, so by fluffing the mouse events, they
         // should find the same target since we fluff them again below.
--- a/security/manager/boot/src/StaticHPKPins.errors
+++ b/security/manager/boot/src/StaticHPKPins.errors
@@ -1,24 +1,17 @@
 Can't find hash in builtin certs for Chrome nickname RapidSSL, inserting GOOGLE_PIN_RapidSSL
 Can't find hash in builtin certs for Chrome nickname Entrust_G2, inserting GOOGLE_PIN_Entrust_G2
 Can't find hash in builtin certs for Chrome nickname Entrust_SSL, inserting GOOGLE_PIN_Entrust_SSL
 Can't find hash in builtin certs for Chrome nickname GTECyberTrustGlobalRoot, inserting GOOGLE_PIN_GTECyberTrustGlobalRoot
-Can't find hash in builtin certs for Chrome nickname Tor2web, inserting GOOGLE_PIN_Tor2web
-Can't find hash in builtin certs for Chrome nickname AlphaSSL_G2, inserting GOOGLE_PIN_AlphaSSL_G2
-Can't find hash in builtin certs for Chrome nickname CryptoCat1, inserting GOOGLE_PIN_CryptoCat1
-Can't find hash in builtin certs for Chrome nickname Libertylavabitcom, inserting GOOGLE_PIN_Libertylavabitcom
 Can't find hash in builtin certs for Chrome nickname EntrustRootEC1, inserting GOOGLE_PIN_EntrustRootEC1
 Can't find hash in builtin certs for Chrome nickname GoDaddySecure, inserting GOOGLE_PIN_GoDaddySecure
 Can't find hash in builtin certs for Chrome nickname ThawtePremiumServer, inserting GOOGLE_PIN_ThawtePremiumServer
 Can't find hash in builtin certs for Chrome nickname SymantecClass3EVG3, inserting GOOGLE_PIN_SymantecClass3EVG3
 Can't find hash in builtin certs for Chrome nickname DigiCertECCSecureServerCA, inserting GOOGLE_PIN_DigiCertECCSecureServerCA
 Writing pinset test
 Writing pinset google
 Writing pinset tor
 Writing pinset twitterCom
 Writing pinset twitterCDN
-Writing pinset tor2web
-Writing pinset cryptoCat
-Writing pinset lavabit
 Writing pinset dropbox
 Writing pinset facebook
 Writing pinset spideroak
--- a/security/manager/boot/src/StaticHPKPins.h
+++ b/security/manager/boot/src/StaticHPKPins.h
@@ -102,24 +102,16 @@ static const char kEquifax_Secure_Global
 /* Equifax Secure eBusiness CA 1 */
 static const char kEquifax_Secure_eBusiness_CA_1Fingerprint[] =
   "JsGNxu6m9jL2drzrodjCtINS8pwtX82oeOCdy4Mt1uU=";
 
 /* FacebookBackup */
 static const char kFacebookBackupFingerprint[] =
   "1ww8E0AYsR2oX5lndk2hwp2Uosk=";
 
-/* GOOGLE_PIN_AlphaSSL_G2 */
-static const char kGOOGLE_PIN_AlphaSSL_G2Fingerprint[] =
-  "yxgiWGK++SFB9ySwt3M3qpn5HO0ZLFY5D+h+G/vcT/c=";
-
-/* GOOGLE_PIN_CryptoCat1 */
-static const char kGOOGLE_PIN_CryptoCat1Fingerprint[] =
-  "vKaqtTLWmVuXPVJE+0OqN5sRc4VCcSQHI/W3XTDVR24=";
-
 /* GOOGLE_PIN_DigiCertECCSecureServerCA */
 static const char kGOOGLE_PIN_DigiCertECCSecureServerCAFingerprint[] =
   "PZXN3lRAy+8tBKk2Ox6F7jIlnzr2Yzmwqc3JnyfXoCw=";
 
 /* GOOGLE_PIN_EntrustRootEC1 */
 static const char kGOOGLE_PIN_EntrustRootEC1Fingerprint[] =
   "/qK31kX7pz11PB7Jp4cMQOH3sMVh6Se5hb9xGGbjbyI=";
 
@@ -134,36 +126,28 @@ static const char kGOOGLE_PIN_Entrust_SS
 /* GOOGLE_PIN_GTECyberTrustGlobalRoot */
 static const char kGOOGLE_PIN_GTECyberTrustGlobalRootFingerprint[] =
   "EGn6R6CqT4z3ERscrqNl7q7RC//zJmDe9uBhS/rnCHU=";
 
 /* GOOGLE_PIN_GoDaddySecure */
 static const char kGOOGLE_PIN_GoDaddySecureFingerprint[] =
   "MrZLZnJ6IGPkBm87lYywqu5Xal7O/ZUzmbuIdHMdlYc=";
 
-/* GOOGLE_PIN_Libertylavabitcom */
-static const char kGOOGLE_PIN_LibertylavabitcomFingerprint[] =
-  "WnKzsDXgqPtS1KvtImrhQPqcxfpmfssuI2cSJt4LMks=";
-
 /* GOOGLE_PIN_RapidSSL */
 static const char kGOOGLE_PIN_RapidSSLFingerprint[] =
   "lT09gPUeQfbYrlxRtpsHrjDblj9Rpz+u7ajfCrg4qDM=";
 
 /* GOOGLE_PIN_SymantecClass3EVG3 */
 static const char kGOOGLE_PIN_SymantecClass3EVG3Fingerprint[] =
   "gMxWOrX4PMQesK9qFNbYBxjBfjUvlkn/vN1n+L9lE5E=";
 
 /* GOOGLE_PIN_ThawtePremiumServer */
 static const char kGOOGLE_PIN_ThawtePremiumServerFingerprint[] =
   "9TwiBZgX3Zb0AGUWOdL4V+IQcKWavtkHlADZ9pVQaQA=";
 
-/* GOOGLE_PIN_Tor2web */
-static const char kGOOGLE_PIN_Tor2webFingerprint[] =
-  "99ogQzjMuUTBkG1ZP7FME0K4kvBEti8Buzu4nZjRItM=";
-
 /* GeoTrust Global CA */
 static const char kGeoTrust_Global_CAFingerprint[] =
   "h6801m+z8v3zbgkRHpq6L29Esgfzhj89C1SyUCOQmqU=";
 
 /* GeoTrust Global CA 2 */
 static const char kGeoTrust_Global_CA_2Fingerprint[] =
   "F3VaXClfPS1y5vAxofB/QAxYi55YKyLxfq4xoVkNEYU=";
 
@@ -652,57 +636,16 @@ static const StaticFingerprints kPinset_
   kPinset_twitterCDN_sha256_Data
 };
 
 static const StaticPinset kPinset_twitterCDN = {
   &kPinset_twitterCDN_sha1,
   &kPinset_twitterCDN_sha256
 };
 
-static const char* kPinset_tor2web_sha256_Data[] = {
-  kGOOGLE_PIN_Tor2webFingerprint,
-  kGOOGLE_PIN_AlphaSSL_G2Fingerprint,
-};
-static const StaticFingerprints kPinset_tor2web_sha256 = {
-  sizeof(kPinset_tor2web_sha256_Data) / sizeof(const char*),
-  kPinset_tor2web_sha256_Data
-};
-
-static const StaticPinset kPinset_tor2web = {
-  nullptr,
-  &kPinset_tor2web_sha256
-};
-
-static const char* kPinset_cryptoCat_sha256_Data[] = {
-  kDigiCert_High_Assurance_EV_Root_CAFingerprint,
-  kGOOGLE_PIN_CryptoCat1Fingerprint,
-};
-static const StaticFingerprints kPinset_cryptoCat_sha256 = {
-  sizeof(kPinset_cryptoCat_sha256_Data) / sizeof(const char*),
-  kPinset_cryptoCat_sha256_Data
-};
-
-static const StaticPinset kPinset_cryptoCat = {
-  nullptr,
-  &kPinset_cryptoCat_sha256
-};
-
-static const char* kPinset_lavabit_sha256_Data[] = {
-  kGOOGLE_PIN_LibertylavabitcomFingerprint,
-};
-static const StaticFingerprints kPinset_lavabit_sha256 = {
-  sizeof(kPinset_lavabit_sha256_Data) / sizeof(const char*),
-  kPinset_lavabit_sha256_Data
-};
-
-static const StaticPinset kPinset_lavabit = {
-  nullptr,
-  &kPinset_lavabit_sha256
-};
-
 static const char* kPinset_dropbox_sha256_Data[] = {
   kGOOGLE_PIN_EntrustRootEC1Fingerprint,
   kGOOGLE_PIN_ThawtePremiumServerFingerprint,
   kthawte_Primary_Root_CA___G3Fingerprint,
   kthawte_Primary_Root_CAFingerprint,
   kEntrust_net_Premium_2048_Secure_Server_CAFingerprint,
   kDigiCert_Assured_ID_Root_CAFingerprint,
   kGo_Daddy_Root_Certificate_Authority___G2Fingerprint,
@@ -795,32 +738,32 @@ static const TransportSecurityPreload kP
   { "api.accounts.firefox.com", true, false, true, 5, &kPinset_mozilla_services },
   { "api.twitter.com", true, false, false, -1, &kPinset_twitterCDN },
   { "apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "appengine.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "apps.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "appspot.com", true, false, false, -1, &kPinset_google_root_pems },
   { "aus4.mozilla.org", true, true, true, 3, &kPinset_mozilla },
   { "blog.torproject.org", true, false, false, -1, &kPinset_tor },
+  { "blogger.com", true, false, false, -1, &kPinset_google_root_pems },
   { "business.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "business.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "cdn.mozilla.net", true, false, true, -1, &kPinset_mozilla },
   { "cdn.mozilla.org", true, false, true, -1, &kPinset_mozilla },
   { "chart.apis.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "check.torproject.org", true, false, false, -1, &kPinset_tor },
   { "checkout.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "chrome-devtools-frontend.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
   { "chrome.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "chromiumcodereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
   { "cloud.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "code.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "code.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "codereview.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
   { "codereview.chromium.org", true, false, false, -1, &kPinset_google_root_pems },
-  { "crypto.cat", false, true, false, -1, &kPinset_cryptoCat },
   { "dev.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "developers.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "dist.torproject.org", true, false, false, -1, &kPinset_tor },
   { "dl.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "docs.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "domains.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "doubleclick.net", true, false, false, -1, &kPinset_google_root_pems },
   { "drive.google.com", true, false, false, -1, &kPinset_google_root_pems },
@@ -1063,17 +1006,16 @@ static const TransportSecurityPreload kP
   { "googleusercontent.com", true, false, false, -1, &kPinset_google_root_pems },
   { "goto.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "groups.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "gstatic.com", true, false, false, -1, &kPinset_google_root_pems },
   { "history.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "hostedtalkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "inbox.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "include-subdomains.pinning.example.com", true, false, false, -1, &kPinset_mozilla_test },
-  { "liberty.lavabit.com", true, true, false, -1, &kPinset_lavabit },
   { "login.corp.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "m.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "mail.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "market.android.com", true, false, false, -1, &kPinset_google_root_pems },
   { "mbasic.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "mobile.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "mtouch.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "oauth.twitter.com", true, false, false, -1, &kPinset_twitterCom },
@@ -1094,17 +1036,16 @@ static const TransportSecurityPreload kP
   { "spideroak.com", true, false, false, -1, &kPinset_spideroak },
   { "spreadsheets.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "ssl.google-analytics.com", true, false, false, -1, &kPinset_google_root_pems },
   { "t.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "tablet.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "talk.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "talkgadget.google.com", true, false, false, -1, &kPinset_google_root_pems },
   { "test-mode.pinning.example.com", true, true, false, -1, &kPinset_mozilla_test },
-  { "tor2web.org", true, true, false, -1, &kPinset_tor2web },
   { "torproject.org", false, false, false, -1, &kPinset_tor },
   { "touch.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "translate.googleapis.com", true, false, false, -1, &kPinset_google_root_pems },
   { "twimg.com", true, false, false, -1, &kPinset_twitterCDN },
   { "twitter.com", true, false, false, -1, &kPinset_twitterCDN },
   { "upload.facebook.com", true, false, false, -1, &kPinset_facebook },
   { "urchin.com", true, false, false, -1, &kPinset_google_root_pems },
   { "w-spotlight.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
@@ -1130,13 +1071,13 @@ static const TransportSecurityPreload kP
   { "www.twitter.com", true, false, false, -1, &kPinset_twitterCom },
   { "xbrlsuccess.appspot.com", true, false, false, -1, &kPinset_google_root_pems },
   { "youtu.be", true, false, false, -1, &kPinset_google_root_pems },
   { "youtube-nocookie.com", true, false, false, -1, &kPinset_google_root_pems },
   { "youtube.com", true, false, false, -1, &kPinset_google_root_pems },
   { "ytimg.com", true, false, false, -1, &kPinset_google_root_pems },
 };
 
-// Pinning Preload List Length = 348;
+// Pinning Preload List Length = 346;
 
 static const int32_t kUnknownId = -1;
 
-static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1428750717082000);
+static const PRTime kPreloadPKPinsExpirationTime = INT64_C(1430262504747000);
--- a/security/manager/boot/src/nsSTSPreloadList.errors
+++ b/security/manager/boot/src/nsSTSPreloadList.errors
@@ -1,50 +1,55 @@
 admin.google.com: did not receive HSTS header (error ignored - included regardless)
 adsfund.org: could not connect to host
+afp548.com: did not receive HSTS header
 airbnb.com: did not receive HSTS header
 aiticon.de: did not receive HSTS header
 amigogeek.net: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 api.mega.co.nz: could not connect to host
 api.recurly.com: did not receive HSTS header
 apis.google.com: did not receive HSTS header (error ignored - included regardless)
 app.manilla.com: could not connect to host
 appengine.google.com: did not receive HSTS header (error ignored - included regardless)
 apps.facebook.com: did not receive HSTS header
 appseccalifornia.org: did not receive HSTS header
 at.search.yahoo.com: did not receive HSTS header
+atavio.at: could not connect to host
+atavio.ch: could not connect to host
 au.search.yahoo.com: did not receive HSTS header
 az.search.yahoo.com: did not receive HSTS header
 azprep.us: could not connect to host
 bccx.com: could not connect to host
 be.search.yahoo.com: did not receive HSTS header
 bedeta.de: could not connect to host
 betnet.fr: could not connect to host
 bi.search.yahoo.com: did not receive HSTS header
 bigshinylock.minazo.net: could not connect to host
 bitfactory.ws: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 bitfarm-archiv.com: did not receive HSTS header
 bitfarm-archiv.de: did not receive HSTS header
 bitgo.com: did not receive HSTS header
+bizon.sk: did not receive HSTS header
 blog.lookout.com: did not receive HSTS header
 br.search.yahoo.com: did not receive HSTS header
 braintreegateway.com: did not receive HSTS header
 braintreepayments.com: did not receive HSTS header
 browserid.org: did not receive HSTS header
 business.facebook.com: did not receive HSTS header
 business.medbank.com.mt: did not receive HSTS header
 ca.search.yahoo.com: did not receive HSTS header
 calibreapp.com: did not receive HSTS header
 calyxinstitute.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
-carlolly.co.uk: did not receive HSTS header
-cartucce24.it: could not connect to host
+carlolly.co.uk: could not connect to host
 cd.search.yahoo.com: did not receive HSTS header
 cert.se: max-age too low: 2628001
 cg.search.yahoo.com: did not receive HSTS header
 ch.search.yahoo.com: did not receive HSTS header
+chainmonitor.com: could not connect to host
+chatbot.me: could not connect to host
 checkout.google.com: did not receive HSTS header (error ignored - included regardless)
 chfr.search.yahoo.com: did not receive HSTS header
 chit.search.yahoo.com: did not receive HSTS header
 chrome-devtools-frontend.appspot.com: did not receive HSTS header (error ignored - included regardless)
 chrome.google.com: did not receive HSTS header (error ignored - included regardless)
 cimballa.com: did not receive HSTS header
 cl.search.yahoo.com: did not receive HSTS header
 cloud.google.com: did not receive HSTS header (error ignored - included regardless)
@@ -54,54 +59,58 @@ code.facebook.com: did not receive HSTS 
 code.google.com: did not receive HSTS header (error ignored - included regardless)
 codereview.chromium.org: did not receive HSTS header (error ignored - included regardless)
 console.python.org: did not receive HSTS header
 coursella.com: did not receive HSTS header
 cr.search.yahoo.com: did not receive HSTS header
 crate.io: did not receive HSTS header
 crbug.com: did not receive HSTS header
 crowdcurity.com: did not receive HSTS header
+crowdjuris.com: could not connect to host
 crypto.is: did not receive HSTS header
 csawctf.poly.edu: did not receive HSTS header
 ct.search.yahoo.com: did not receive HSTS header
-curlybracket.co.uk: could not connect to host
+cujanovic.com: did not receive HSTS header
 cyanogenmod.xxx: could not connect to host
 cybershambles.com: could not connect to host
 daylightcompany.com: did not receive HSTS header
 de.search.yahoo.com: did not receive HSTS header
 decibelios.li: did not receive HSTS header
+destinationbijoux.fr: max-age too low: 2678400
 developers.facebook.com: did not receive HSTS header
 digitaldaddy.net: could not connect to host
 discovery.lookout.com: did not receive HSTS header
 dk.search.yahoo.com: did not receive HSTS header
 dl.google.com: did not receive HSTS header (error ignored - included regardless)
 do.search.yahoo.com: did not receive HSTS header
 docs.google.com: did not receive HSTS header (error ignored - included regardless)
 domaris.de: did not receive HSTS header
 download.jitsi.org: did not receive HSTS header
 drive.google.com: did not receive HSTS header (error ignored - included regardless)
 dropcam.com: did not receive HSTS header
 dzlibs.io: could not connect to host
 ed.gs: did not receive HSTS header
 edmodo.com: did not receive HSTS header
+elnutricionista.es: could not connect to host
 email.lookout.com: could not connect to host
 en-maktoob.search.yahoo.com: did not receive HSTS header
 encrypted.google.com: did not receive HSTS header (error ignored - included regardless)
 epoxate.com: did not receive HSTS header
 errors.zenpayroll.com: could not connect to host
 es.search.yahoo.com: did not receive HSTS header
+esec.rs: did not receive HSTS header
 espanol.search.yahoo.com: did not receive HSTS header
 espra.com: could not connect to host
 etsysecure.com: could not connect to host
 facebook.com: did not receive HSTS header
 fatzebra.com.au: did not receive HSTS header
 fi.search.yahoo.com: did not receive HSTS header
-filedir.com: did not receive HSTS header
 fixingdns.com: did not receive HSTS header
 fj.search.yahoo.com: did not receive HSTS header
+fonetiq.io: could not connect to host
 fr.search.yahoo.com: did not receive HSTS header
 gamesdepartment.co.uk: did not receive HSTS header
 get.zenpayroll.com: did not receive HSTS header
 getlantern.org: did not receive HSTS header
 gl.search.yahoo.com: did not receive HSTS header
 glass.google.com: did not receive HSTS header (error ignored - included regardless)
 gm.search.yahoo.com: did not receive HSTS header
 gmail.com: did not receive HSTS header (error ignored - included regardless)
@@ -109,75 +118,81 @@ golf-6.com: did not receive HSTS header
 golf3.de: did not receive HSTS header
 golf4.de: did not receive HSTS header
 googlemail.com: did not receive HSTS header (error ignored - included regardless)
 googleplex.com: could not connect to host
 googleplex.com: could not connect to host (error ignored - included regardless)
 goto.google.com: did not receive HSTS header (error ignored - included regardless)
 gparent.org: did not receive HSTS header
 gr.search.yahoo.com: did not receive HSTS header
+grandmascookieblog.com: did not receive HSTS header
 greplin.com: could not connect to host
 groups.google.com: did not receive HSTS header (error ignored - included regardless)
 hackerone-user-content.com: could not connect to host
 haste.ch: could not connect to host
 history.google.com: did not receive HSTS header (error ignored - included regardless)
 hk.search.yahoo.com: did not receive HSTS header
 hn.search.yahoo.com: did not receive HSTS header
 hoerbuecher-und-hoerspiele.de: did not receive HSTS header
 honeytracks.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
-horosho.in: did not receive HSTS header
+horosho.in: could not connect to host
 hostedtalkgadget.google.com: did not receive HSTS header (error ignored - included regardless)
 howrandom.org: could not connect to host
 hstspreload.appspot.com: did not receive HSTS header
 hu.search.yahoo.com: did not receive HSTS header
 iban.is: could not connect to host
 id.search.yahoo.com: did not receive HSTS header
 ie.search.yahoo.com: did not receive HSTS header
 ilmconpm.de: did not receive HSTS header
 in.search.yahoo.com: did not receive HSTS header
 inertianetworks.com: did not receive HSTS header
 intercom.io: did not receive HSTS header
 iop.intuit.com: max-age too low: 86400
 irccloud.com: did not receive HSTS header
 it.search.yahoo.com: did not receive HSTS header
 itriskltd.com: did not receive HSTS header
 jottit.com: could not connect to host
+keeleysam.com: did not receive HSTS header
 keymaster.lookout.com: did not receive HSTS header
+kingmanhall.org: could not connect to host
+kirkforcongress.com: did not receive HSTS header
+kirkforsenate.com: did not receive HSTS header
 kitsta.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 kiwiirc.com: max-age too low: 5256000
 klaxn.com: could not connect to host
 klaxn.org: could not connect to host
 kr.search.yahoo.com: did not receive HSTS header
 kryptera.se: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 kz.search.yahoo.com: did not receive HSTS header
 labina.com.tr: did not receive HSTS header
 ledgerscope.net: did not receive HSTS header
 li.search.yahoo.com: did not receive HSTS header
-liberty.lavabit.com: could not connect to host
+library.linode.com: did not receive HSTS header
 lifeguard.aecom.com: max-age too low: 86400
 lists.mayfirst.org: did not receive HSTS header
 login.corp.google.com: max-age too low: 7776000 (error ignored - included regardless)
 logotype.se: did not receive HSTS header
 lovelycorral.com: did not receive HSTS header
 lt.search.yahoo.com: did not receive HSTS header
 lu.search.yahoo.com: did not receive HSTS header
 lumi.do: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 luxus-russen.de: could not connect to host
 lv.search.yahoo.com: did not receive HSTS header
 m.facebook.com: did not receive HSTS header
 m.gparent.org: could not connect to host
 mail.google.com: did not receive HSTS header (error ignored - included regardless)
 maktoob.search.yahoo.com: did not receive HSTS header
 malaysia.search.yahoo.com: did not receive HSTS header
+man3s.jp: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 manage.zenpayroll.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 market.android.com: did not receive HSTS header (error ignored - included regardless)
+markusueberallassetmanagement.de: could not connect to host
 mbasic.facebook.com: did not receive HSTS header
 megashur.se: did not receive HSTS header
 megaxchange.com: did not receive HSTS header
-mf.cz: did not receive HSTS header
 minikneet.nl: did not receive HSTS header
 mirindadomo.ru: did not receive HSTS header
 mobilethreat.net: could not connect to host
 mobilethreatnetwork.net: could not connect to host
 mocloud.eu: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 movelaria.com.br: did not receive HSTS header
 mqas.net: could not connect to host
 mt.search.yahoo.com: did not receive HSTS header
@@ -185,29 +200,29 @@ mtouch.facebook.com: did not receive HST
 mu.search.yahoo.com: did not receive HSTS header
 mudcrab.us: could not connect to host
 mw.search.yahoo.com: did not receive HSTS header
 mx.search.yahoo.com: did not receive HSTS header
 my.alfresco.com: did not receive HSTS header
 mydigipass.com: did not receive HSTS header
 mykolab.com: did not receive HSTS header
 mykreuzfahrt.de: did not receive HSTS header
-myplaceonline.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
-neftaly.com: could not connect to host
+neftaly.com: did not receive HSTS header
 neonisi.com: could not connect to host
 netzpolitik.org: did not receive HSTS header
 nexth.de: could not connect to host
 nexth.net: could not connect to host
 nexth.us: could not connect to host
 ni.search.yahoo.com: did not receive HSTS header
 nl.search.yahoo.com: did not receive HSTS header
 no.search.yahoo.com: did not receive HSTS header
 noexpect.org: could not connect to host
 np.search.yahoo.com: did not receive HSTS header
 npw.net: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
+nutsandboltsmedia.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 nz.search.yahoo.com: did not receive HSTS header
 openshift.redhat.com: did not receive HSTS header
 otakurepublic.com: did not receive HSTS header
 ottospora.nl: could not connect to host
 pa.search.yahoo.com: did not receive HSTS header
 passwordbox.com: did not receive HSTS header
 passwords.google.com: did not receive HSTS header (error ignored - included regardless)
 payroll.xero.com: max-age too low: 3600
@@ -219,24 +234,26 @@ pixel.facebook.com: did not receive HSTS
 pk.search.yahoo.com: did not receive HSTS header
 pl.search.yahoo.com: did not receive HSTS header
 platform.lookout.com: could not connect to host
 play.google.com: did not receive HSTS header (error ignored - included regardless)
 pr.search.yahoo.com: did not receive HSTS header
 pressfreedomfoundation.org: did not receive HSTS header
 prodpad.com: did not receive HSTS header
 profiles.google.com: did not receive HSTS header (error ignored - included regardless)
+projektzentrisch.de: could not connect to host
 promecon-gmbh.de: did not receive HSTS header
 py.search.yahoo.com: did not receive HSTS header
 qc.search.yahoo.com: did not receive HSTS header
 rapidresearch.me: could not connect to host
 redlatam.org: did not receive HSTS header
 redports.org: could not connect to host
 regar42.fr: could not connect to host
 research.facebook.com: did not receive HSTS header
+riftnetwork.net: did not receive HSTS header
 riseup.net: did not receive HSTS header
 rme.li: did not receive HSTS header
 ro.search.yahoo.com: did not receive HSTS header
 roddis.net: did not receive HSTS header
 ru.search.yahoo.com: did not receive HSTS header
 rw.search.yahoo.com: did not receive HSTS header
 sah3.net: could not connect to host
 saturngames.co.uk: could not connect to host
@@ -245,17 +262,19 @@ se.search.yahoo.com: did not receive HST
 search.yahoo.com: did not receive HSTS header
 secure.facebook.com: did not receive HSTS header
 security.google.com: did not receive HSTS header (error ignored - included regardless)
 segu-info.com.ar: max-age too low: 60
 semenkovich.com: did not receive HSTS header
 seomobo.com: did not receive HSTS header
 seowarp.net: max-age too low: 1576800
 serverdensity.io: did not receive HSTS header
+seyahatsagliksigortalari.com: did not receive HSTS header
 sg.search.yahoo.com: did not receive HSTS header
+shohruh.uz: did not receive HSTS header
 shops.neonisi.com: could not connect to host
 siammedia.co: did not receive HSTS header
 silentcircle.org: could not connect to host
 simon.butcher.name: max-age too low: 2629743
 simplyfixit.co.uk: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 sites.google.com: did not receive HSTS header (error ignored - included regardless)
 sol.io: could not connect to host
 souyar.de: could not connect to host
@@ -272,62 +291,66 @@ stocktrade.de: could not connect to host
 sunshinepress.org: could not connect to host
 surfeasy.com: did not receive HSTS header
 sv.search.yahoo.com: did not receive HSTS header
 t.facebook.com: did not receive HSTS header
 tablet.facebook.com: did not receive HSTS header
 talk.google.com: could not connect to host
 talk.google.com: could not connect to host (error ignored - included regardless)
 talkgadget.google.com: did not receive HSTS header (error ignored - included regardless)
+taxsquirrel.com: did not receive HSTS header
+tc-bonito.de: did not receive HSTS header
 tektoria.de: did not receive HSTS header
 temehu.com: did not receive HSTS header
 terrax.berlin: could not connect to host
 th.search.yahoo.com: did not receive HSTS header
+thomasgriffin.io: did not receive HSTS header
+tonywebster.com: could not connect to host
 touch.facebook.com: did not receive HSTS header
 tr.search.yahoo.com: did not receive HSTS header
 translate.googleapis.com: did not receive HSTS header (error ignored - included regardless)
 translatoruk.co.uk: did not receive HSTS header
 triop.se: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 tv.search.yahoo.com: could not connect to host
 tw.search.yahoo.com: did not receive HSTS header
 ua.search.yahoo.com: did not receive HSTS header
 uk.search.yahoo.com: did not receive HSTS header
-unterfrankenclan.de: could not connect to host
 upload.facebook.com: did not receive HSTS header
 uprotect.it: could not connect to host
 uy.search.yahoo.com: did not receive HSTS header
 uz.search.yahoo.com: did not receive HSTS header
+uzstyle.com: did not receive HSTS header
 ve.search.yahoo.com: did not receive HSTS header
 viennan.net: did not receive HSTS header
 vn.search.yahoo.com: did not receive HSTS header
 wallet.google.com: did not receive HSTS header (error ignored - included regardless)
 webmail.mayfirst.org: did not receive HSTS header
 whonix.org: did not receive HSTS header
-wieninternational.at: could not connect to host
 wikidsystems.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
-wiz.biz: did not receive HSTS header
+wiz.biz: could not connect to host
 wohnungsbau-ludwigsburg.de: did not receive HSTS header
 www.apollo-auto.com: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 www.calyxinstitute.org: [Exception... "Component returned failure code: 0x80004005 (NS_ERROR_FAILURE) [nsISiteSecurityService.processHeader]"  nsresult: "0x80004005 (NS_ERROR_FAILURE)"  location: "JS frame :: /builds/slave/m-cen-l64-periodicupdate-00000/getHSTSPreloadList.js :: processStsHeader :: line 134"  data: no]
 www.cueup.com: could not connect to host
 www.developer.mydigipass.com: could not connect to host
 www.elanex.biz: did not receive HSTS header
 www.facebook.com: did not receive HSTS header
 www.gmail.com: did not receive HSTS header (error ignored - included regardless)
 www.googlemail.com: did not receive HSTS header (error ignored - included regardless)
 www.greplin.com: could not connect to host
 www.jitsi.org: did not receive HSTS header
 www.ledgerscope.net: did not receive HSTS header
 www.logentries.com: did not receive HSTS header
-www.moneybookers.com: did not receive HSTS header
+www.moneybookers.com: could not connect to host
 www.neonisi.com: could not connect to host
 www.paycheckrecords.com: max-age too low: 86400
 www.rme.li: did not receive HSTS header
 www.sandbox.mydigipass.com: could not connect to host
 www.surfeasy.com: did not receive HSTS header
 xa.search.yahoo.com: did not receive HSTS header
 xtream-hosting.com: could not connect to host
 xtream-hosting.de: could not connect to host
 xtream-hosting.eu: could not connect to host
 xtreamhosting.eu: could not connect to host
 za.search.yahoo.com: did not receive HSTS header
 zh.search.yahoo.com: did not receive HSTS header
-zoo24.de: did not receive HSTS header
+zoo24.de: could not connect to host
+zotero.org: did not receive HSTS header
--- a/security/manager/boot/src/nsSTSPreloadList.inc
+++ b/security/manager/boot/src/nsSTSPreloadList.inc
@@ -3,54 +3,58 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 /*****************************************************************************/
 /* This is an automatically generated file. If you're not                    */
 /* nsSiteSecurityService.cpp, you shouldn't be #including it.     */
 /*****************************************************************************/
 
 #include <stdint.h>
-const PRTime gPreloadListExpirationTime = INT64_C(1431169912161000);
+const PRTime gPreloadListExpirationTime = INT64_C(1432681700209000);
 
 class nsSTSPreload
 {
   public:
     const char *mHost;
     const bool mIncludeSubdomains;
 };
 
 static const nsSTSPreload kSTSPreloadList[] = {
+  { "007sascha.de", true },
   { "0x0a.net", true },
   { "17hats.com", true },
   { "18f.gsa.gov", true },
   { "1a-diamantscheiben.de", true },
   { "1a-vermessung.at", true },
   { "1a-werkstattgeraete.de", true },
+  { "2048game.co.uk", true },
+  { "302.nyc", true },
   { "accounts.firefox.com", true },
   { "accounts.google.com", true },
   { "aclu.org", false },
   { "activiti.alfresco.com", false },
   { "adamkostecki.de", true },
   { "addvocate.com", true },
   { "admin.google.com", true },
+  { "adorai.tk", true },
   { "adsfund.org", true },
   { "ahoyconference.com", true },
   { "aie.de", true },
   { "aiticon.com", true },
   { "aladdinschools.appspot.com", false },
   { "alainwolf.net", true },
   { "alecvannoten.be", true },
   { "alexsexton.com", true },
   { "alexyang.me", true },
   { "alpha.irccloud.com", false },
   { "anadoluefessporkulubu.org", true },
   { "andreasbreitenlohner.de", true },
   { "anetaben.nl", true },
   { "angularjs.org", true },
-  { "anime.my", true },
+  { "anime.my", false },
   { "animurecs.com", true },
   { "ankarakart.com.tr", true },
   { "annahmeschluss.de", true },
   { "annevankesteren.com", true },
   { "annevankesteren.nl", true },
   { "annevankesteren.org", true },
   { "ansdell.net", true },
   { "anycoin.me", true },
@@ -58,29 +62,31 @@ static const nsSTSPreload kSTSPreloadLis
   { "api.intercom.io", false },
   { "api.lookout.com", false },
   { "api.simple.com", false },
   { "api.xero.com", false },
   { "apis.google.com", true },
   { "apn-einstellungen.de", true },
   { "app.lookout.com", false },
   { "app.manilla.com", true },
-  { "app.recurly.com", false },
+  { "app.recurly.com", true },
   { "app.simpletax.ca", false },
   { "app.yinxiang.com", false },
   { "appengine.google.com", true },
   { "aprz.de", true },
   { "archlinux.de", true },
   { "arendburgers.nl", true },
   { "arguggi.co.uk", true },
   { "arivo.com.br", true },
   { "arlen.io", true },
   { "atavio.at", true },
   { "atavio.ch", true },
   { "atavio.de", true },
+  { "atlassian.net", true },
+  { "atte.fi", true },
   { "auf-feindgebiet.de", true },
   { "azabani.com", true },
   { "baer.im", true },
   { "balcan-underground.net", true },
   { "baldwinkoo.com", true },
   { "balikonos.cz", true },
   { "bank.simple.com", false },
   { "barcodeberlin.com", true },
@@ -97,22 +103,24 @@ static const nsSTSPreload kSTSPreloadLis
   { "beastowner.li", true },
   { "bedeta.de", true },
   { "bedreid.dk", true },
   { "beneathvt.com", true },
   { "benjamins.com", true },
   { "best-wedding-quotes.com", true },
   { "bgneuesheim.de", true },
   { "bhatia.at", true },
+  { "biathloncup.ru", true },
   { "big-andy.co.uk", true },
   { "bigbrownpromotions.com.au", true },
   { "bitbucket.org", false },
   { "bitfactory.ws", true },
   { "bitmex.com", true },
   { "bitmon.net", true },
+  { "bitpod.de", true },
   { "bjornjohansen.no", true },
   { "bl4ckb0x.com", true },
   { "bl4ckb0x.de", true },
   { "bl4ckb0x.info", true },
   { "bl4ckb0x.net", true },
   { "bl4ckb0x.org", true },
   { "blacklane.com", true },
   { "blessnet.jp", true },
@@ -124,32 +132,35 @@ static const nsSTSPreload kSTSPreloadLis
   { "blog.torproject.org", false },
   { "blubbablasen.de", true },
   { "bodo-wolff.de", true },
   { "bohramt.de", true },
   { "bonigo.de", true },
   { "bookingapp.nl", true },
   { "boxcryptor.com", true },
   { "brage.info", true },
+  { "brakemanpro.com", true },
+  { "broeselei.at", true },
   { "brunosouza.org", true },
   { "buddhistische-weisheiten.org", true },
   { "bugzil.la", true },
   { "bugzilla.mozilla.org", true },
   { "bulktrade.de", true },
   { "business.lookout.com", false },
   { "buttercoin.com", true },
   { "buzzconcert.com", true },
   { "bytepark.de", false },
   { "ca.gparent.org", false },
   { "cackette.com", true },
   { "call.me", true },
   { "camolist.com", true },
   { "caremad.io", true },
   { "carezone.com", false },
   { "cartouche24.eu", true },
+  { "cartucce24.it", true },
   { "cdnb.co", true },
   { "celltek-server.de", false },
   { "certible.com", true },
   { "certly.io", true },
   { "chahub.com", true },
   { "chainmonitor.com", true },
   { "chatbot.me", true },
   { "check.torproject.org", false },
@@ -179,49 +190,51 @@ static const nsSTSPreload kSTSPreloadLis
   { "coinapult.com", true },
   { "comdurav.com", true },
   { "comssa.org.au", true },
   { "config.schokokeks.org", true },
   { "conformal.com", true },
   { "conrad-kostecki.de", true },
   { "controlcenter.gigahost.dk", true },
   { "cor-ser.es", true },
+  { "cordial-restaurant.com", true },
   { "cotonea.de", true },
   { "crm.onlime.ch", false },
   { "crowdjuris.com", true },
   { "crypto.cat", false },
   { "cryptopartyatx.org", true },
+  { "cs50.harvard.edu", true },
   { "cspbuilder.info", true },
   { "cube.de", true },
-  { "cujanovic.com", true },
   { "cupcake.io", true },
   { "cupcake.is", true },
+  { "curiosity-driven.org", true },
   { "curlybracket.co.uk", true },
   { "cyanogenmod.xxx", true },
   { "cybershambles.com", true },
   { "cybozu.com", true },
   { "cyon.ch", true },
   { "cyphertite.com", true },
   { "czbix.com", true },
   { "daphne.informatik.uni-freiburg.de", true },
   { "darchoods.net", false },
   { "data-abundance.com", true },
   { "data.qld.gov.au", false },
   { "datenkeks.de", true },
+  { "daveoc64.co.uk", true },
   { "davidlyness.com", true },
   { "deadbeef.ninja", true },
   { "dealcruiser.nl", true },
   { "debtkit.co.uk", true },
   { "dedimax.de", true },
   { "dee.pe", true },
   { "denh.am", true },
   { "depechemode-live.com", true },
   { "derevtsov.com", false },
   { "derhil.de", true },
-  { "destinationbijoux.fr", true },
   { "detectify.com", false },
   { "developer.mydigipass.com", false },
   { "devh.de", true },
   { "diamante.ro", true },
   { "die-besten-weisheiten.de", true },
   { "dillonkorman.com", true },
   { "dinamoelektrik.com", true },
   { "dist.torproject.org", false },
@@ -240,27 +253,28 @@ static const nsSTSPreload kSTSPreloadLis
   { "easysimplecrm.com", false },
   { "ebanking.indovinabank.com.vn", false },
   { "ecdn.cz", true },
   { "ecosystem.atlassian.net", true },
   { "edit.yahoo.com", false },
   { "edyou.eu", true },
   { "ef.gy", true },
   { "eff.org", true },
+  { "egfl.org.uk", true },
+  { "eksisozluk.com", true },
   { "electronic-ignition-system.com", true },
   { "elnutricionista.es", true },
   { "emailprivacytester.com", true },
   { "encircleapp.com", true },
   { "encryptallthethings.net", true },
   { "encrypted.google.com", true },
   { "energy-drink-magazin.de", true },
   { "enorekcah.com", true },
   { "entropia.de", false },
   { "errors.zenpayroll.com", false },
-  { "esec.rs", true },
   { "espra.com", true },
   { "ethack.org", true },
   { "ethitter.com", true },
   { "eurotramp.com", true },
   { "everhome.de", true },
   { "evstatus.com", true },
   { "exiahost.com", true },
   { "explodie.org", true },
@@ -271,26 +285,34 @@ static const nsSTSPreload kSTSPreloadLis
   { "fairbill.com", true },
   { "fakturoid.cz", true },
   { "fant.dk", true },
   { "faq.lookout.com", false },
   { "fastcomcorp.net", true },
   { "fedorapeople.org", true },
   { "feedbin.com", false },
   { "ferienhaus-polchow-ruegen.de", false },
+  { "fewo-thueringer-wald.de", true },
   { "fiken.no", true },
+  { "filedir.com", false },
+  { "filip-prochazka.com", true },
   { "finn.io", false },
+  { "firebaseio-demo.com", true },
+  { "firebaseio.com", true },
+  { "firefart.at", true },
   { "firemail.io", true },
+  { "firstlook.org", true },
   { "fischer-its.com", true },
   { "fj.simple.com", false },
   { "flamer-scene.com", true },
   { "fleximus.org", false },
   { "floobits.com", true },
   { "flynn.io", true },
   { "fm83.nl", true },
+  { "food4health.guide", true },
   { "forewordreviews.com", true },
   { "forodeespanol.com", true },
   { "forum.linode.com", false },
   { "forum.quantifiedself.com", true },
   { "fralef.me", false },
   { "frederik-braun.com", true },
   { "freenetproject.org", true },
   { "freeshell.de", true },
@@ -316,39 +338,42 @@ static const nsSTSPreload kSTSPreloadLis
   { "gmantra.org", true },
   { "gmcd.co", true },
   { "go.xero.com", false },
   { "gocardless.com", true },
   { "googlemail.com", false },
   { "googleplex.com", true },
   { "goto.google.com", true },
   { "gplintegratedit.com", true },
-  { "grandmascookieblog.com", false },
   { "grc.com", false },
   { "greensolid.biz", true },
   { "grepular.com", true },
+  { "groetzner.net", true },
   { "groups.google.com", true },
   { "gtraxapp.com", true },
   { "gunnarhafdal.com", true },
   { "guphi.net", true },
   { "guthabenkarten-billiger.de", true },
+  { "gw2treasures.com", true },
   { "hack.li", true },
   { "hackerone.com", true },
   { "hansvaneijsden.com", true },
   { "harvestapp.com", true },
   { "hasilocke.de", true },
   { "haste.ch", true },
   { "haufschild.de", true },
   { "hausverbrauch.de", true },
+  { "heartlandrentals.com", true },
   { "heha.co", false },
   { "heid.ws", true },
   { "heijblok.com", true },
   { "helichat.de", true },
   { "help.simpletax.ca", false },
   { "helpium.de", true },
+  { "hemlockhillscabinrentals.com", true },
   { "henriknoerr.com", true },
   { "hex2013.com", true },
   { "hexony.com", true },
   { "hg.python.org", true },
   { "history.google.com", true },
   { "honeybadger.io", false },
   { "horseboners.xxx", true },
   { "horza.org", true },
@@ -356,61 +381,70 @@ static const nsSTSPreload kSTSPreloadLis
   { "hostinginnederland.nl", true },
   { "hostix.de", true },
   { "howrandom.org", true },
   { "howsmyssl.com", true },
   { "howsmytls.com", true },
   { "hpac-portal.com", true },
   { "hrackydomino.cz", true },
   { "hsmr.cc", true },
+  { "hstsfail.appspot.com", true },
   { "html5.org", true },
+  { "i5y.co.uk", true },
   { "iamcarrico.com", true },
   { "ian.sh", true },
   { "iban.is", true },
   { "id-co.in", true },
   { "id.atlassian.com", true },
   { "id.mayfirst.org", false },
   { "ideaweb.de", true },
   { "ihrlotto.de", true },
   { "ilikerainbows.co.uk", true },
   { "imaginary.ca", true },
-  { "imouto.my", true },
+  { "imouto.my", false },
   { "in.xero.com", false },
   { "inb4.us", true },
   { "inbox.google.com", true },
   { "inkbunny.net", true },
   { "inleaked.com", true },
+  { "innophate-security.com", true },
+  { "innophate-security.nl", true },
   { "insouciant.org", true },
   { "instasex.ch", true },
+  { "iranianlawschool.com", true },
   { "irische-segenswuensche.info", true },
   { "ironfistdesign.com", true },
   { "isitchristmas.com", true },
   { "it-schwerin.de", true },
   { "itsamurai.ru", true },
   { "itshost.ru", true },
   { "jackyyf.com", false },
   { "jakub-boucek.cz", true },
   { "janoberst.com", true },
   { "janus-engineering.de", true },
   { "jelmer.co.uk", true },
   { "jelmer.uk", true },
+  { "jettshome.org", true },
   { "jfreitag.de", true },
   { "jitsi.org", false },
   { "jmedved.com", true },
+  { "johners.me", true },
   { "jonas-keidel.de", true },
   { "jonaswitmer.ch", true },
   { "jonnybarnes.uk", true },
   { "julian-kipka.de", true },
   { "jwilsson.com", true },
   { "jwilsson.me", true },
   { "k-dev.de", true },
   { "kaheim.de", true },
   { "kardize24.pl", true },
+  { "karmaspa.se", true },
   { "kartonmodellbau.org", true },
   { "kdex.de", true },
+  { "kdyby.org", true },
   { "keepclean.me", true },
   { "keeperapp.com", true },
   { "keepersecurity.com", true },
   { "kernel-error.de", true },
   { "kevincox.ca", true },
   { "keycdn.com", true },
   { "keyerror.com", true },
   { "khanovaskola.cz", true },
@@ -429,64 +463,72 @@ static const nsSTSPreload kSTSPreloadLis
   { "koenvdheuvel.me", true },
   { "komandakovalchuk.com", true },
   { "konklone.com", true },
   { "koop-bremen.de", true },
   { "koordinate.net", true },
   { "kosho.org", true },
   { "kpebetka.net", true },
   { "kraken.io", true },
+  { "kupschke.net", true },
   { "kura.io", true },
   { "lagerauftrag.info", true },
   { "lasst-uns-beten.de", true },
   { "lastpass.com", false },
   { "launchkey.com", true },
   { "lavalite.de", true },
   { "lb-toner.de", true },
   { "leadbook.ru", true },
+  { "leibniz-remscheid.de", true },
   { "leonardcamacho.me", true },
-  { "library.linode.com", false },
+  { "les-corsaires.net", true },
+  { "libraryfreedomproject.org", true },
   { "liebel.org", true },
   { "lighting-centres.co.uk", true },
   { "lilpwny.com", true },
   { "limpid.nl", true },
   { "lingolia.com", true },
   { "linode.com", false },
+  { "linux-admin-california.com", true },
   { "linx.net", true },
   { "ljs.io", true },
+  { "lobste.rs", true },
   { "lockify.com", true },
   { "lodash.com", true },
   { "loenshotel.de", true },
   { "loftboard.eu", true },
   { "logentries.com", false },
   { "login.corp.google.com", true },
   { "login.launchpad.net", true },
   { "login.persona.org", true },
   { "login.sapo.pt", true },
   { "login.ubuntu.com", true },
   { "login.xero.com", false },
   { "login.yahoo.com", false },
   { "lolicore.ch", true },
   { "lookout.com", false },
   { "ludwig.im", true },
+  { "luelistan.net", true },
   { "lukonet.com", true },
   { "lumi.do", false },
   { "luneta.nearbuysystems.com", false },
   { "mach-politik.ch", true },
   { "mail.de", true },
   { "mail.google.com", true },
   { "mail.yahoo.com", false },
   { "mailbox.org", true },
+  { "makeitdynamic.com", true },
   { "makeyourlaws.org", true },
   { "malnex.de", true },
   { "man3s.jp", true },
   { "manage.zenpayroll.com", false },
   { "manageprojects.com", true },
   { "manager.linode.com", false },
   { "mandala-ausmalbilder.de", true },
+  { "manicode.com", true },
   { "market.android.com", true },
   { "markusueberallassetmanagement.de", true },
   { "marshut.net", true },
   { "matatall.com", true },
   { "mathiasbynens.be", true },
   { "matteomarescotti.it", true },
   { "mattmccutchen.net", true },
   { "mbp.banking.co.at", false },
@@ -501,34 +543,37 @@ static const nsSTSPreload kSTSPreloadLis
   { "members.mayfirst.org", false },
   { "members.nearlyfreespeech.net", false },
   { "miasarafina.de", true },
   { "michalspacek.cz", true },
   { "mig5.net", true },
   { "mike-bland.com", true },
   { "mikewest.org", true },
   { "miku.hatsune.my", true },
+  { "minecraftvoter.com", true },
   { "minez-nightswatch.com", true },
   { "minikneet.com", true },
   { "minnesotadata.com", true },
   { "mirrorx.com", true },
   { "miskatonic.org", true },
   { "mkcert.org", true },
+  { "mkw.st", true },
   { "mnsure.org", true },
   { "mobile.usaa.com", false },
   { "mondwandler.de", true },
   { "moriz.de", true },
   { "mothereff.in", true },
   { "mountainmusicpromotions.com", true },
   { "mountainroseherbs.com", true },
   { "movlib.org", true },
   { "mqas.net", true },
   { "msc-seereisen.net", true },
   { "mths.be", true },
   { "mudcrab.us", true },
+  { "mujadin.se", true },
   { "munich-rage.de", true },
   { "musicgamegalaxy.de", true },
   { "mutantmonkey.in", true },
   { "mutantmonkey.info", true },
   { "mutantmonkey.sexy", true },
   { "mwe.st", true },
   { "my.onlime.ch", false },
   { "my.usa.gov", true },
@@ -537,21 +582,21 @@ static const nsSTSPreload kSTSPreloadLis
   { "mylookout.com", false },
   { "myni.io", true },
   { "mynigma.org", true },
   { "myplaceonline.com", true },
   { "myvirtualserver.com", true },
   { "nachsenden.info", true },
   { "nameid.org", true },
   { "nectarleaf.com", true },
-  { "neftaly.com", true },
   { "neg9.org", false },
   { "neilwynne.com", false },
   { "net-safe.info", true },
   { "netzbit.de", true },
+  { "netztest.at", true },
   { "newstarnootropics.com", true },
   { "ng-security.com", true },
   { "nginxnudes.com", true },
   { "nmctest.net", true },
   { "nos-oignons.net", true },
   { "nouvelle-vague-saint-cast.fr", true },
   { "npw.net", true },
   { "nu3.at", true },
@@ -559,31 +604,35 @@ static const nsSTSPreload kSTSPreloadLis
   { "nu3.co.uk", true },
   { "nu3.com", true },
   { "nu3.de", true },
   { "nu3.dk", true },
   { "nu3.fi", true },
   { "nu3.fr", true },
   { "nu3.no", true },
   { "nu3.se", true },
+  { "null-sec.ru", true },
+  { "nwgh.org", true },
   { "oakslighting.co.uk", true },
   { "okmx.de", true },
   { "omitech.co.uk", true },
   { "onedot.nl", true },
   { "onedrive.com", true },
   { "onedrive.live.com", false },
   { "onsitemassageco.com", true },
+  { "ooonja.de", true },
   { "opendesk.cc", true },
   { "oplop.appspot.com", true },
   { "opsmate.com", false },
   { "optimus.io", true },
   { "orbograph-hrcm.com", true },
   { "oscarvk.ch", true },
   { "osterkraenzchen.de", true },
   { "otakuworld.de", true },
+  { "ouvirmusica.com.br", true },
   { "ovenapp.io", true },
   { "oversight.io", true },
   { "p.linode.com", false },
   { "packagist.org", false },
   { "pajonzeck.de", true },
   { "palava.tv", true },
   { "parent5446.us", true },
   { "partyvan.eu", true },
@@ -604,76 +653,92 @@ static const nsSTSPreload kSTSPreloadLis
   { "patt.us", true },
   { "pay.gigahost.dk", true },
   { "paymill.com", true },
   { "paymill.de", true },
   { "paypal.com", false },
   { "pdf.yt", true },
   { "peercraft.com", true },
   { "pentesterlab.com", true },
+  { "personaldatabasen.no", true },
   { "pestici.de", true },
   { "petrolplus.ru", true },
+  { "pharmaboard.de", true },
   { "phoenixlogan.com", true },
+  { "phurl.de", true },
   { "picksin.club", true },
+  { "pieperhome.de", true },
   { "pierre-schmitz.com", true },
   { "pixi.me", true },
   { "play.google.com", true },
   { "plothost.com", true },
   { "plus.google.com", false },
   { "plus.sandbox.google.com", false },
   { "portal.tirol.gv.at", true },
-  { "posteo.de", true },
+  { "posteo.de", false },
   { "powerplannerapp.com", true },
   { "prakharprasad.com", true },
   { "prefontaine.name", true },
+  { "privategiant.com", true },
   { "profiles.google.com", true },
+  { "progressiveplanning.com", true },
   { "projektzentrisch.de", true },
   { "propagandism.org", true },
   { "prowhisky.de", true },
   { "proximato.com", true },
+  { "puac.de", true },
   { "pubkey.is", true },
   { "publications.qld.gov.au", false },
   { "pult.co", false },
   { "pypa.io", true },
   { "pypi.python.org", true },
   { "python.org", false },
   { "qetesh.de", true },
   { "quuz.org", true },
   { "r3s1stanc3.me", true },
   { "rad-route.de", true },
+  { "rafaelcz.de", true },
   { "raiseyourflag.com", true },
+  { "rasing.me", true },
+  { "raspass.me", true },
   { "ravchat.com", true },
   { "redports.org", true },
   { "redteam-pentesting.de", true },
   { "reedloden.com", true },
   { "reishunger.de", true },
+  { "reliable-mail.de", true },
   { "reserve-online.net", true },
   { "residentsinsurance.co.uk", true },
   { "reviews.anime.my", true },
   { "riccy.org", true },
   { "riesenmagnete.de", true },
   { "rippleunion.com", true },
   { "rlalique.com", true },
   { "robteix.com", true },
   { "roland.io", true },
   { "romab.com", true },
+  { "romans-place.me.uk", true },
+  { "romulusapp.com", true },
   { "room-checkin24.de", true },
   { "rosenkeller.org", true },
   { "roundcube.mayfirst.org", false },
   { "ru-sprachstudio.ch", true },
+  { "rudloff.pro", true },
   { "ruudkoot.nl", true },
   { "rws-vertriebsportal.de", true },
   { "s-c.se", true },
   { "sakaki.anime.my", true },
   { "salaervergleich.com", true },
   { "sale4ru.ru", true },
   { "salserocafe.com", true },
+  { "samba.org", true },
   { "samizdat.cz", true },
   { "sandbox.mydigipass.com", false },
   { "savetheinternet.eu", true },
+  { "savvytime.com", true },
   { "schachburg.de", true },
   { "schokokeks.org", true },
   { "schreiber-netzwerk.eu", true },
   { "schwarzer.it", true },
   { "sciencex.com", true },
   { "scotthelme.co.uk", true },
   { "scrambl.is", true },
   { "scribe.systems", true },
@@ -684,65 +749,77 @@ static const nsSTSPreload kSTSPreloadLis
   { "security-carpet.com", true },
   { "security.google.com", true },
   { "securityheaders.com", true },
   { "secuvera.de", true },
   { "seifried.org", true },
   { "servergno.me", true },
   { "servethecity-karlsruhe.de", true },
   { "shaaaaaaaaaaaaa.com", true },
+  { "shakepeers.org", true },
   { "shenyuqi.com", true },
   { "sherbers.de", true },
   { "shiinko.com", false },
   { "shipard.com", true },
   { "shodan.io", true },
-  { "shohruh.uz", false },
   { "shopontarget.com", true },
   { "shortdiary.me", true },
   { "silentcircle.com", false },
   { "simbolo.co.uk", false },
   { "simple.com", false },
   { "simpletax.ca", false },
   { "simplia.cz", true },
   { "simplystudio.com", true },
   { "siraweb.org", true },
   { "siriad.com", true },
   { "sites.google.com", true },
+  { "sitesten.com", true },
+  { "skhosting.eu", true },
+  { "skogsbruket.fi", true },
+  { "skogskultur.fi", true },
   { "skydrive.live.com", false },
   { "slack.com", true },
   { "slattery.co", true },
   { "slevomat.cz", true },
   { "slidebatch.com", true },
+  { "smartcleaningcenter.nl", true },
   { "smartcoin.com.br", true },
   { "smartlend.se", true },
   { "smartship.co.jp", true },
+  { "snakehosting.dk", true },
+  { "sorz.org", true },
   { "sour.is", true },
   { "southside-crew.com", true },
   { "souvik.me", true },
   { "spartantheatre.org", true },
+  { "spawn.cz", true },
   { "spencerbaer.com", true },
   { "spideroak.com", true },
   { "spreadsheets.google.com", true },
+  { "spreed.me", true },
   { "sprueche-zum-valentinstag.de", true },
   { "sprueche-zur-geburt.info", true },
   { "sprueche-zur-hochzeit.de", true },
   { "sprueche-zur-konfirmation.de", true },
   { "squareup.com", false },
+  { "srevilak.net", true },
   { "sro.center", true },
   { "ssl.google-analytics.com", true },
   { "sslmate.com", true },
   { "stage.wepay.com", false },
   { "standardssuck.org", true },
   { "static.wepay.com", false },
   { "stationary-traveller.eu", true },
   { "steventress.com", true },
   { "stocktrade.de", false },
+  { "strasweb.fr", false },
   { "stretchmyan.us", true },
   { "stripe.com", true },
   { "strongest-privacy.com", true },
+  { "studienportal.eu", true },
   { "studydrive.net", true },
   { "subrosa.io", true },
   { "suite73.org", true },
   { "sunjaydhama.com", true },
   { "supplies24.at", true },
   { "supplies24.es", true },
   { "support.mayfirst.org", false },
   { "surkatty.org", true },
@@ -750,41 +827,44 @@ static const nsSTSPreload kSTSPreloadLis
   { "sylaps.com", true },
   { "sysctl.se", true },
   { "syss.de", true },
   { "tadigitalstore.com", true },
   { "tageau.com", true },
   { "talk.google.com", true },
   { "talkgadget.google.com", true },
   { "tapka.cz", true },
+  { "tas2580.net", true },
   { "tatort-fanpage.de", true },
   { "tauchkater.de", true },
-  { "taxsquirrel.com", true },
   { "techhipster.net", true },
   { "tegelsensanitaironline.nl", true },
   { "tekshrek.com", true },
   { "tent.io", true },
   { "testsuite.org", true },
   { "texte-zur-taufe.de", true },
   { "thecustomizewindows.com", true },
   { "thepaymentscompany.com", true },
   { "therapynotes.com", false },
   { "theshadestore.com", true },
   { "thorncreek.net", false },
   { "thusoy.com", true },
+  { "thyngster.com", true },
   { "tickopa.co.uk", true },
+  { "tid.jp", true },
   { "timtaubert.de", true },
   { "tinfoilsecurity.com", false },
   { "tinte24.de", true },
   { "tintenfix.net", true },
   { "tipps-fuer-den-haushalt.de", true },
   { "tittelbach.at", true },
   { "tls.li", true },
   { "tno.io", true },
   { "tobias-kluge.de", true },
+  { "todesschaf.org", true },
   { "tollmanz.com", true },
   { "tomfisher.eu", true },
   { "tomvote.com", true },
   { "toner24.at", true },
   { "toner24.co.uk", true },
   { "toner24.es", true },
   { "toner24.fr", true },
   { "toner24.it", true },
@@ -795,52 +875,55 @@ static const nsSTSPreload kSTSPreloadLis
   { "tonerjet.co.uk", true },
   { "tonerklick.de", true },
   { "tonerkurier.de", true },
   { "tonermaus.de", true },
   { "tonermonster.de", true },
   { "tonex.de", true },
   { "tonex.nl", true },
   { "topodin.com", true },
+  { "tor2web.org", true },
   { "torproject.org", false },
   { "toshnix.com", true },
   { "translate.googleapis.com", true },
   { "trauertexte.info", true },
   { "tresorit.com", true },
   { "tribut.de", true },
+  { "tucuxi.org", true },
   { "tunebitfm.de", true },
   { "twentymilliseconds.com", true },
+  { "twisto.cz", true },
   { "twitter.com", false },
   { "typingrevolution.com", true },
   { "ub3rk1tten.com", true },
   { "ubertt.org", true },
   { "ukdefencejournal.org.uk", true },
   { "ukhas.net", true },
   { "ukrainians.ch", true },
   { "unison.com", true },
   { "unterfrankenclan.de", true },
   { "uptrends.com", true },
   { "usaa.com", false },
-  { "uzstyle.com", false },
   { "vaddder.com", true },
   { "vhost.co.id", true },
   { "viasinc.com", false },
   { "visionless.me", false },
   { "vmoagents.com", false },
   { "vocaloid.my", true },
   { "vortexhobbies.com", true },
   { "vpnzoom.com", true },
   { "vrobert.fr", false },
   { "w-spotlight.appspot.com", true },
   { "wallet.google.com", true },
   { "warrencreative.com", false },
   { "watsonhall.uk", true },
   { "wbg-vs.de", true },
   { "webandmore.de", false },
   { "webandwords.com.au", true },
+  { "webassadors.com", false },
   { "webcollect.org.uk", true },
   { "webfilings-eu-mirror.appspot.com", true },
   { "webfilings-eu.appspot.com", true },
   { "webfilings-mirror-hrd.appspot.com", true },
   { "webfilings.appspot.com", true },
   { "weblogzwolle.nl", true },
   { "webmail.gigahost.dk", false },
   { "webmail.onlime.ch", false },
@@ -867,16 +950,17 @@ static const nsSTSPreload kSTSPreloadLis
   { "wiki.python.org", true },
   { "wildbee.org", true },
   { "willnorris.com", true },
   { "winhistory-forum.net", true },
   { "wpletter.de", true },
   { "writeapp.me", false },
   { "wubthecaptain.eu", true },
   { "wunderlist.com", true },
+  { "wundi.net", true },
   { "www.aclu.org", false },
   { "www.airbnb.com", true },
   { "www.apollo-auto.com", true },
   { "www.banking.co.at", false },
   { "www.braintreepayments.com", false },
   { "www.capitainetrain.com", false },
   { "www.cyveillance.com", true },
   { "www.dropbox.com", true },
@@ -926,10 +1010,9 @@ static const nsSTSPreload kSTSPreloadLis
   { "yoursecondphone.co", true },
   { "ypart.eu", true },
   { "z.ai", true },
   { "zenpayroll.com", false },
   { "zeplin.io", false },
   { "zeropush.com", true },
   { "zixiao.wang", true },
   { "zlavomat.sk", true },
-  { "zotero.org", true },
 };
--- a/toolkit/mozapps/extensions/content/extensions.xul
+++ b/toolkit/mozapps/extensions/content/extensions.xul
@@ -441,17 +441,18 @@
             <button id="update-selected-btn" hidden="true"
                     label="&updates.updateSelected.label;"
                     tooltiptext="&updates.updateSelected.tooltip;"/>
           </hbox>
           <richlistbox id="updates-list" class="list" flex="1"/>
         </vbox>
 
         <!-- detail view -->
-        <scrollbox id="detail-view" flex="1" class="view-pane addon-view" orient="vertical" tabindex="0">
+        <scrollbox id="detail-view" flex="1" class="view-pane addon-view" orient="vertical" tabindex="0"
+                   role="document">
           <!-- global warnings -->
           <hbox class="global-warning-container global-warning">
             <hbox class="global-warning-safemode" flex="1" align="center"
                   tooltiptext="&warning.safemode.label;">
               <image class="warning-icon"/>
               <label class="global-warning-text" flex="1" crop="end"
                      value="&warning.safemode.label;"/>
             </hbox>