Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Thu, 13 Nov 2014 16:23:31 +0100
changeset 215519 4e7a0c00c991eecc7ab315f2333362338ad6efbe
parent 215518 005e5bb697d31a05ad76b3b1998c4fe5b5a52128 (current diff)
parent 215500 ae27ae77e32f5c76f925daff1b29be21ec77f5e6 (diff)
child 215520 c39b72dd4d10b356c4005983c00497b61deeb785
push id27818
push userryanvm@gmail.com
push dateThu, 13 Nov 2014 20:19:09 +0000
treeherdermozilla-central@292ed84594c1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone36.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to mozilla-inbound
toolkit/themes/shared/in-content/dropdown-disabled.png
toolkit/themes/shared/in-content/dropdown-disabled@2x.png
toolkit/themes/shared/in-content/dropdown.png
toolkit/themes/shared/in-content/dropdown@2x.png
--- 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="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <!-- 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"/>
--- 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="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="67f2907bc340bad250b4ea6ce2902b52896c9ef0"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <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="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <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="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <!-- 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="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="67f2907bc340bad250b4ea6ce2902b52896c9ef0"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
   <project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
   <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="3ab0d9c70f0b2e1ededc679112c392303f037361">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <!-- 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="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <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": "", 
         "remote": "", 
         "branch": ""
     }, 
-    "revision": "2e0f2f070a2265b537e6a2ff4e0f2e1f2aca49c6", 
+    "revision": "c1bed74af46cb81a7092d6e80624134bae5d1bf0", 
     "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="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
   <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="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,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="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <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="be8b0151d2f9a4c41fc63952128e0b723cd1161d"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="a222358a6210c0bb94e53e036ec8c73dc2e3d4d0"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="45c54a55e31758f7e54e5eafe0d01d387f35897a"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
-  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="dfb7845803f3afcdcf157c5babec357bf9ce74eb"/>
+  <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0494efbb157e863dec6f1fd4d4652b0917ce263d"/>
   <project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
   <project name="platform/bionic" path="bionic" revision="cd5dfce80bc3f0139a56b58aca633202ccaee7f8"/>
   <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/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -345,23 +345,24 @@ pref("browser.urlbar.restrict.history", 
 pref("browser.urlbar.restrict.bookmark", "*");
 pref("browser.urlbar.restrict.tag", "+");
 pref("browser.urlbar.restrict.openpage", "%");
 pref("browser.urlbar.restrict.typed", "~");
 pref("browser.urlbar.match.title", "#");
 pref("browser.urlbar.match.url", "@");
 
 // The default behavior for the urlbar can be configured to use any combination
-// of the restrict or match filters with each additional filter restricting
-// more (intersection). Add the following values to set the behavior as the
-// default: 1: history, 2: bookmark, 4: tag, 8: title, 16: url, 32: typed,
-//          64: javascript, 128: tabs
-// E.g., 0 = show all results (no filtering), 1 = only visited pages in history,
-// 2 = only bookmarks, 3 = visited bookmarks, 1+16 = history matching in the url
-pref("browser.urlbar.default.behavior", 0);
+// of the match filters with each additional filter adding more results (union).
+pref("browser.urlbar.suggest.history",              true);
+pref("browser.urlbar.suggest.bookmark",             true);
+pref("browser.urlbar.suggest.openpage",             true);
+
+// Restrictions to current suggestions can also be applied (intersection).
+// Typed suggestion works only if history is set to true.
+pref("browser.urlbar.suggest.history.onlyTyped",    false);
 
 pref("browser.urlbar.formatting.enabled", true);
 pref("browser.urlbar.trimURLs", true);
 
 pref("browser.altClickSave", false);
 
 // Enable logging downloads operations to the Error Console.
 pref("browser.download.debug", false);
@@ -1257,17 +1258,16 @@ pref("services.sync.prefs.sync.browser.s
 pref("services.sync.prefs.sync.browser.search.update", true);
 pref("services.sync.prefs.sync.browser.sessionstore.restore_on_demand", true);
 pref("services.sync.prefs.sync.browser.startup.homepage", true);
 pref("services.sync.prefs.sync.browser.startup.page", true);
 pref("services.sync.prefs.sync.browser.tabs.loadInBackground", true);
 pref("services.sync.prefs.sync.browser.tabs.warnOnClose", true);
 pref("services.sync.prefs.sync.browser.tabs.warnOnOpen", true);
 pref("services.sync.prefs.sync.browser.urlbar.autocomplete.enabled", true);
-pref("services.sync.prefs.sync.browser.urlbar.default.behavior", true);
 pref("services.sync.prefs.sync.browser.urlbar.maxRichResults", true);
 pref("services.sync.prefs.sync.dom.disable_open_during_load", true);
 pref("services.sync.prefs.sync.dom.disable_window_flip", true);
 pref("services.sync.prefs.sync.dom.disable_window_move_resize", true);
 pref("services.sync.prefs.sync.dom.event.contextmenu.enabled", true);
 pref("services.sync.prefs.sync.extensions.personas.current", true);
 pref("services.sync.prefs.sync.extensions.update.enabled", true);
 pref("services.sync.prefs.sync.intl.accept_languages", true);
--- a/browser/base/content/newtab/customize.js
+++ b/browser/base/content/newtab/customize.js
@@ -38,20 +38,22 @@ let gCustomize = {
 
   showPanel: function() {
     let nodes = this._nodes;
     let {button, panel} = nodes;
     if (button.hasAttribute("active")) {
       return Promise.resolve(nodes);
     }
 
+    panel.hidden = false;
     panel.openPopup(button);
     button.setAttribute("active", true);
     panel.addEventListener("popuphidden", function onHidden() {
       panel.removeEventListener("popuphidden", onHidden);
+      panel.hidden = true;
       button.removeAttribute("active");
     });
 
     return new Promise(resolve => {
       panel.addEventListener("popupshown", function onShown() {
         panel.removeEventListener("popupshown", onShown);
         resolve(nodes);
       });
--- a/browser/base/content/newtab/intro.js
+++ b/browser/base/content/newtab/intro.js
@@ -15,37 +15,43 @@ let gIntro = {
   _nodes: {},
 
   init: function() {
     for (let idSuffix of this._nodeIDSuffixes) {
       this._nodes[idSuffix] = document.getElementById("newtab-intro-" + idSuffix);
     }
 
     this._nodes.panel.addEventListener("popupshowing", e => this._setUpPanel());
+    this._nodes.panel.addEventListener("popuphidden", e => this._hidePanel());
     this._nodes.what.addEventListener("click", e => this.showPanel());
   },
 
   showIfNecessary: function() {
     if (!Services.prefs.getBoolPref(PREF_INTRO_SHOWN)) {
       Services.prefs.setBoolPref(PREF_INTRO_SHOWN, true);
       this.showPanel();
     }
   },
 
   showPanel: function() {
     // Point the panel at the 'what' link
+    this._nodes.panel.hidden = false;
     this._nodes.panel.openPopup(this._nodes.what);
   },
 
   _setUpPanel: function() {
     // Build the panel if necessary
     if (this._nodes.panel.childNodes.length == 1) {
       ['<a href="' + TILES_INTRO_LINK + '">' + newTabString("learn.link") + "</a>",
        '<a href="' + TILES_PRIVACY_LINK + '">' + newTabString("privacy.link") + "</a>",
        '<input type="button" class="newtab-customize"/>',
       ].forEach((arg, index) => {
         let paragraph = document.createElementNS(HTML_NAMESPACE, "p");
         this._nodes.panel.appendChild(paragraph);
         paragraph.innerHTML = newTabString("intro.paragraph" + (index + 1), [arg]);
       });
     }
   },
+
+  _hidePanel: function() {
+    this._nodes.panel.hidden = true;
+  }
 };
--- a/browser/base/content/newtab/newTab.xul
+++ b/browser/base/content/newtab/newTab.xul
@@ -16,29 +16,29 @@
   %searchBarDTD;
 ]>
 
 <xul:window id="newtab-window" xmlns="http://www.w3.org/1999/xhtml"
             xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
             title="&newtab.pageTitle;">
 
   <xul:panel id="newtab-intro-panel" orient="vertical" type="arrow"
-             noautohide="true">
+             noautohide="true" hidden="true">
     <h1>&newtab.intro.header;</h1>
   </xul:panel>
 
   <xul:panel id="newtab-search-panel" orient="vertical" type="arrow"
-             noautohide="true">
+             noautohide="true" hidden="true">
     <xul:hbox id="newtab-search-manage" class="newtab-search-panel-engine">
       <xul:label>&cmd_engineManager.label;</xul:label>
     </xul:hbox>
   </xul:panel>
 
   <xul:panel id="newtab-customize-panel" orient="vertical" type="arrow"
-             noautohide="true">
+             noautohide="true" hidden="true">
     <xul:hbox id="newtab-customize-enhanced" class="newtab-customize-panel-item">
       <xul:label>&newtab.customize.enhanced;</xul:label>
     </xul:hbox>
     <xul:hbox id="newtab-customize-classic" class="newtab-customize-panel-item">
       <xul:label>&newtab.customize.classic;</xul:label>
     </xul:hbox>
     <xul:hbox id="newtab-customize-blank" class="newtab-customize-panel-item">
       <xul:label>&newtab.customize.blank;</xul:label>
--- a/browser/base/content/newtab/page.js
+++ b/browser/base/content/newtab/page.js
@@ -1,14 +1,17 @@
 #ifdef 0
 /* 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/. */
 #endif
 
+// The amount of time we wait while coalescing updates for hidden pages.
+const SCHEDULE_UPDATE_TIMEOUT_MS = 1000;
+
 /**
  * This singleton represents the whole 'New Tab Page' and takes care of
  * initializing all its components.
  */
 let gPage = {
   /**
    * Initializes the page.
    */
@@ -64,26 +67,49 @@ let gPage = {
         if (site && site.url === aData) {
           site.refreshThumbnail();
         }
       }
     }
   },
 
   /**
-   * Updates the whole page and the grid when the storage has changed.
-   * @param aOnlyIfHidden If true, the page is updated only if it's hidden in
-   *                      the preloader.
+   * Updates the page's grid right away for visible pages. If the page is
+   * currently hidden, i.e. in a background tab or in the preloader, then we
+   * batch multiple update requests and refresh the grid once after a short
+   * delay. Accepts a single parameter the specifies the reason for requesting
+   * a page update. The page may decide to delay or prevent a requested updated
+   * based on the given reason.
    */
-  update: function Page_update(aOnlyIfHidden=false) {
-    let skipUpdate = aOnlyIfHidden && !document.hidden;
-    // The grid might not be ready yet as we initialize it asynchronously.
-    if (gGrid.ready && !skipUpdate) {
-      gGrid.refresh();
+  update(reason = "") {
+    // Update immediately if we're visible.
+    if (!document.hidden) {
+      // Ignore updates where reason=links-changed as those signal that the
+      // provider's set of links changed. We don't want to update visible pages
+      // in that case, it is ok to wait until the user opens the next tab.
+      if (reason != "links-changed" && gGrid.ready) {
+        gGrid.refresh();
+      }
+
+      return;
     }
+
+    // Bail out if we scheduled before.
+    if (this._scheduleUpdateTimeout) {
+      return;
+    }
+
+    this._scheduleUpdateTimeout = setTimeout(() => {
+      // Refresh if the grid is ready.
+      if (gGrid.ready) {
+        gGrid.refresh();
+      }
+
+      this._scheduleUpdateTimeout = null;
+    }, SCHEDULE_UPDATE_TIMEOUT_MS);
   },
 
   /**
    * Internally initializes the page. This runs only when/if the feature
    * is/gets enabled.
    */
   _init: function Page_init() {
     if (this._initialized)
@@ -165,16 +191,25 @@ let gPage = {
         break;
       case "drop":
         if (gDrag.isValid(aEvent) && gDrag.draggedSite) {
           aEvent.preventDefault();
           aEvent.stopPropagation();
         }
         break;
       case "visibilitychange":
+        // Cancel any delayed updates for hidden pages now that we're visible.
+        if (this._scheduleUpdateTimeout) {
+          clearTimeout(this._scheduleUpdateTimeout);
+          this._scheduleUpdateTimeout = null;
+
+          // An update was pending so force an update now.
+          this.update();
+        }
+
         setTimeout(() => this.onPageFirstVisible());
         removeEventListener("visibilitychange", this);
         break;
     }
   },
 
   onPageFirstVisible: function () {
     // Record another page impression.
--- a/browser/base/content/newtab/search.js
+++ b/browser/base/content/newtab/search.js
@@ -16,20 +16,22 @@ let gSearch = {
 
     window.addEventListener("ContentSearchService", this);
     this._send("GetState");
   },
 
   showPanel: function () {
     let panel = this._nodes.panel;
     let logo = this._nodes.logo;
+    panel.hidden = false;
     panel.openPopup(logo);
     logo.setAttribute("active", "true");
     panel.addEventListener("popuphidden", function onHidden() {
       panel.removeEventListener("popuphidden", onHidden);
+      panel.hidden = true;
       logo.removeAttribute("active");
     });
   },
 
   search: function (event) {
     if (event) {
       event.preventDefault();
     }
--- a/browser/base/content/test/newtab/browser_newtab_bug752841.js
+++ b/browser/base/content/test/newtab/browser_newtab_bug752841.js
@@ -39,15 +39,19 @@ function runTests() {
 
     yield addNewTabPageTab();
     newTab = gBrowser.selectedTab;
     newTabGridLength = getGrid().cells.length;
     is(newTabGridLength, expectedValues[i],
       "New page grid is updated correctly.");
 
     gBrowser.removeTab(newTab);
+
+    // Wait until the original tab is visible again.
+    let doc = existingTab.linkedBrowser.contentDocument;
+    yield waitForCondition(() => !doc.hidden).then(TestRunner.next);
   }
 
   gBrowser.removeTab(existingTab);
 
   Services.prefs.clearUserPref(PREF_NEWTAB_ROWS);
   Services.prefs.clearUserPref(PREF_NEWTAB_COLUMNS);
 }
--- a/browser/base/content/test/newtab/browser_newtab_search.js
+++ b/browser/base/content/test/newtab/browser_newtab_search.js
@@ -349,18 +349,18 @@ let checkCurrentEngine = Task.async(func
     let objectURL = logo.style.backgroundImage.match(/^url\("([^"]*)"\)$/)[1];
     ok(objectURL, "ObjectURL should be there.");
 
     let blob = yield objectURLToBlob(objectURL);
     let base64 = yield blobToBase64(blob);
 
     ok(base64.startsWith(expectedLogoPrefix), "Checking image prefix.");
 
+    logo.click();
     let panel = searchPanel();
-    panel.openPopup(logo);
     yield promisePanelShown(panel);
 
     panel.hidePopup();
     for (let engineBox of panel.childNodes) {
       let engineName = engineBox.getAttribute("engine");
       if (engineName == engine.name) {
         is(engineBox.getAttribute("selected"), "true",
            "Engine box's selected attribute should be true for " +
--- a/browser/base/content/test/newtab/browser_newtab_update.js
+++ b/browser/base/content/test/newtab/browser_newtab_update.js
@@ -1,51 +1,49 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
+"use strict";
+
 /**
  * Checks that newtab is updated as its links change.
  */
-
 function runTests() {
   // First, start with an empty page.  setLinks will trigger a hidden page
   // update because it calls clearHistory.  We need to wait for that update to
   // happen so that the next time we wait for a page update below, we catch the
   // right update and not the one triggered by setLinks.
-  //
-  // Why this weird way of yielding?  First, these two functions don't return
-  // promises, they call TestRunner.next when done.  Second, the point at which
-  // setLinks is done is independent of when the page update will happen, so
-  // calling whenPagesUpdated cannot wait until that time.
-  setLinks([]);
-  whenPagesUpdated(null, true);
-  yield null;
-  yield null;
+  yield whenPagesUpdatedAnd(resolve => setLinks([], resolve));
 
   // Strategy: Add some visits, open a new page, check the grid, repeat.
-  fillHistory([link(1)]);
-  yield whenPagesUpdated(null, true);
+  yield fillHistoryAndWaitForPageUpdate([1]);
   yield addNewTabPageTab();
   checkGrid("1,,,,,,,,");
 
-  fillHistory([link(2)]);
-  yield whenPagesUpdated(null, true);
+  yield fillHistoryAndWaitForPageUpdate([2]);
   yield addNewTabPageTab();
   checkGrid("2,1,,,,,,,");
 
-  fillHistory([link(1)]);
-  yield whenPagesUpdated(null, true);
+  yield fillHistoryAndWaitForPageUpdate([1]);
   yield addNewTabPageTab();
   checkGrid("1,2,,,,,,,");
 
-  // Wait for fillHistory to add all links before waiting for an update
-  yield fillHistory([link(2), link(3), link(4)], TestRunner.next);
-  yield whenPagesUpdated(null, true);
+  yield fillHistoryAndWaitForPageUpdate([2, 3, 4]);
   yield addNewTabPageTab();
   checkGrid("2,1,3,4,,,,,");
 
   // Make sure these added links have the right type
   is(getCell(1).site.link.type, "history", "added link is history");
 }
 
+function fillHistoryAndWaitForPageUpdate(links) {
+  return whenPagesUpdatedAnd(resolve => fillHistory(links.map(link), resolve));
+}
+
+function whenPagesUpdatedAnd(promiseConstructor) {
+  let promise1 = new Promise(whenPagesUpdated);
+  let promise2 = new Promise(promiseConstructor);
+  return Promise.all([promise1, promise2]).then(TestRunner.next);
+}
+
 function link(id) {
   return { url: "http://example" + id + ".com/", title: "site#" + id };
 }
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -209,17 +209,17 @@ function getCell(aIndex) {
  *
  * Example: setLinks("-1,0,1,2,3")
  * Result: [{url: "http://example.com/", title: "site#-1"},
  *          {url: "http://example0.com/", title: "site#0"},
  *          {url: "http://example1.com/", title: "site#1"},
  *          {url: "http://example2.com/", title: "site#2"},
  *          {url: "http://example3.com/", title: "site#3"}]
  */
-function setLinks(aLinks) {
+function setLinks(aLinks, aCallback = TestRunner.next) {
   let links = aLinks;
 
   if (typeof links == "string") {
     links = aLinks.split(/\s*,\s*/).map(function (id) {
       return {url: "http://example" + (id != "-1" ? id : "") + ".com/",
               title: "site#" + id};
     });
   }
@@ -228,33 +228,33 @@ function setLinks(aLinks) {
   // currently in progress has ended. We clear the history, fill it with the
   // given entries and call populateCache() now again to make sure the cache
   // has the desired contents.
   NewTabUtils.links.populateCache(function () {
     clearHistory(function () {
       fillHistory(links, function () {
         NewTabUtils.links.populateCache(function () {
           NewTabUtils.allPages.update();
-          TestRunner.next();
+          aCallback();
         }, true);
       });
     });
   });
 }
 
 function clearHistory(aCallback) {
   Services.obs.addObserver(function observe(aSubject, aTopic, aData) {
     Services.obs.removeObserver(observe, aTopic);
     executeSoon(aCallback);
   }, PlacesUtils.TOPIC_EXPIRATION_FINISHED, false);
 
   PlacesUtils.history.removeAllPages();
 }
 
-function fillHistory(aLinks, aCallback) {
+function fillHistory(aLinks, aCallback = TestRunner.next) {
   let numLinks = aLinks.length;
   if (!numLinks) {
     if (aCallback)
       executeSoon(aCallback);
     return;
   }
 
   let transitionLink = Ci.nsINavHistoryService.TRANSITION_LINK;
@@ -319,16 +319,43 @@ function setPinnedLinks(aLinks) {
  * Restore the grid state.
  */
 function restore() {
   whenPagesUpdated();
   NewTabUtils.restore();
 }
 
 /**
+ * Wait until a given condition becomes true.
+ */
+function waitForCondition(aConditionFn, aMaxTries=50, aCheckInterval=100) {
+  return new Promise((resolve, reject) => {
+    let tries = 0;
+
+    function tryNow() {
+      tries++;
+
+      if (aConditionFn()) {
+        resolve();
+      } else if (tries < aMaxTries) {
+        tryAgain();
+      } else {
+        reject("Condition timed out: " + aConditionFn.toSource());
+      }
+    }
+
+    function tryAgain() {
+      setTimeout(tryNow, aCheckInterval);
+    }
+
+    tryAgain();
+  });
+}
+
+/**
  * Creates a new tab containing 'about:newtab'.
  */
 function addNewTabPageTab() {
   addNewTabPageTabPromise().then(TestRunner.next);
 }
 
 function addNewTabPageTabPromise() {
   let deferred = Promise.defer();
@@ -344,17 +371,17 @@ function addNewTabPageTabPromise() {
       });
     } else {
       deferred.resolve();
     }
   }
 
   // The new tab page might have been preloaded in the background.
   if (browser.contentDocument.readyState == "complete") {
-    whenNewTabLoaded();
+    waitForCondition(() => !browser.contentDocument.hidden).then(whenNewTabLoaded);
     return deferred.promise;
   }
 
   // Wait for the new tab page to be loaded.
   browser.addEventListener("load", function onLoad() {
     browser.removeEventListener("load", onLoad, true);
     whenNewTabLoaded();
   }, true);
@@ -612,28 +639,24 @@ function createDragEvent(aEventType, aDa
                       false, false, false, false, 0, null, dataTransfer);
 
   return event;
 }
 
 /**
  * Resumes testing when all pages have been updated.
  * @param aCallback Called when done. If not specified, TestRunner.next is used.
- * @param aOnlyIfHidden If true, this resumes testing only when an update that
- *                      applies to pre-loaded, hidden pages is observed.  If
- *                      false, this resumes testing when any update is observed.
  */
-function whenPagesUpdated(aCallback, aOnlyIfHidden=false) {
+function whenPagesUpdated(aCallback = TestRunner.next) {
   let page = {
     observe: _ => _,
-    update: function (onlyIfHidden=false) {
-      if (onlyIfHidden == aOnlyIfHidden) {
-        NewTabUtils.allPages.unregister(this);
-        executeSoon(aCallback || TestRunner.next);
-      }
+
+    update() {
+      NewTabUtils.allPages.unregister(this);
+      executeSoon(aCallback);
     }
   };
 
   NewTabUtils.allPages.register(page);
   registerCleanupFunction(function () {
     NewTabUtils.allPages.unregister(page);
   });
 }
--- a/browser/components/loop/content/shared/css/conversation.css
+++ b/browser/components/loop/content/shared/css/conversation.css
@@ -738,26 +738,29 @@ html, .fx-embedded, #main,
 }
 
 /* Standalone rooms */
 
 .standalone .room-conversation-wrapper {
   position: relative;
 }
 
-.standalone .room-inner-action-area {
+.standalone .room-inner-info-area {
   position: absolute;
   top: 35%;
+  left: 0;
+  right: 25%;
   z-index: 1000;
   margin: 0 auto;
   width: 50%;
+  color: #fff;
+  font-weight: bold;
 }
 
-.standalone .room-inner-action-area button {
-  position: absolute;
+.standalone .room-inner-info-area button {
   border-radius: 3px;
   font-size: 1.2em;
   padding: .2em 1.2em;
   cursor: pointer;
 }
 
 .standalone .room-conversation h2.room-name {
   position: absolute;
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.js
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.js
@@ -124,42 +124,56 @@ loop.standaloneRoomViews = (function(moz
      * @return {Boolean}
      */
     _roomIsActive: function() {
       return this.state.roomState === ROOM_STATES.JOINED            ||
              this.state.roomState === ROOM_STATES.SESSION_CONNECTED ||
              this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS;
     },
 
-    _renderActionButtons: function() {
+    _renderContextualRoomInfo: function() {
+      switch(this.state.roomState) {
+        case ROOM_STATES.INIT:
+        case ROOM_STATES.READY: {
+          // Join button
+          return (
+            React.DOM.div({className: "room-inner-info-area"}, 
+              React.DOM.button({className: "btn btn-join btn-info", onClick: this.joinRoom}, 
+                mozL10n.get("rooms_room_join_label")
+              )
+            )
+          );
+        }
+        case ROOM_STATES.JOINED:
+        case ROOM_STATES.SESSION_CONNECTED: {
+          // Empty room message
+          return (
+            React.DOM.div({className: "room-inner-info-area"}, 
+              React.DOM.p({className: "empty-room-message"}, 
+                mozL10n.get("rooms_only_occupant_label")
+              )
+            )
+          );
+        }
+      }
       // XXX Render "Start your own" button when room is over capacity (see
       //     bug 1074709)
-      if (this.state.roomState === ROOM_STATES.INIT ||
-          this.state.roomState === ROOM_STATES.READY) {
-        return (
-          React.DOM.div({className: "room-inner-action-area"}, 
-            React.DOM.button({className: "btn btn-join btn-info", onClick: this.joinRoom}, 
-              mozL10n.get("rooms_room_join_label")
-            )
-          )
-        );
-      }
     },
 
     render: function() {
       var localStreamClasses = React.addons.classSet({
         hide: !this._roomIsActive(),
         local: true,
         "local-stream": true,
         "local-stream-audio": false
       });
 
       return (
         React.DOM.div({className: "room-conversation-wrapper"}, 
-          this._renderActionButtons(), 
+          this._renderContextualRoomInfo(), 
           React.DOM.div({className: "video-layout-wrapper"}, 
             React.DOM.div({className: "conversation room-conversation"}, 
               React.DOM.h2({className: "room-name"}, this.state.roomName), 
               React.DOM.div({className: "media nested"}, 
                 React.DOM.div({className: "video_wrapper remote_wrapper"}, 
                   React.DOM.div({className: "video_inner remote"})
                 ), 
                 React.DOM.div({className: localStreamClasses})
--- a/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
+++ b/browser/components/loop/standalone/content/js/standaloneRoomViews.jsx
@@ -124,42 +124,56 @@ loop.standaloneRoomViews = (function(moz
      * @return {Boolean}
      */
     _roomIsActive: function() {
       return this.state.roomState === ROOM_STATES.JOINED            ||
              this.state.roomState === ROOM_STATES.SESSION_CONNECTED ||
              this.state.roomState === ROOM_STATES.HAS_PARTICIPANTS;
     },
 
-    _renderActionButtons: function() {
+    _renderContextualRoomInfo: function() {
+      switch(this.state.roomState) {
+        case ROOM_STATES.INIT:
+        case ROOM_STATES.READY: {
+          // Join button
+          return (
+            <div className="room-inner-info-area">
+              <button className="btn btn-join btn-info" onClick={this.joinRoom}>
+                {mozL10n.get("rooms_room_join_label")}
+              </button>
+            </div>
+          );
+        }
+        case ROOM_STATES.JOINED:
+        case ROOM_STATES.SESSION_CONNECTED: {
+          // Empty room message
+          return (
+            <div className="room-inner-info-area">
+              <p className="empty-room-message">
+                {mozL10n.get("rooms_only_occupant_label")}
+              </p>
+            </div>
+          );
+        }
+      }
       // XXX Render "Start your own" button when room is over capacity (see
       //     bug 1074709)
-      if (this.state.roomState === ROOM_STATES.INIT ||
-          this.state.roomState === ROOM_STATES.READY) {
-        return (
-          <div className="room-inner-action-area">
-            <button className="btn btn-join btn-info" onClick={this.joinRoom}>
-              {mozL10n.get("rooms_room_join_label")}
-            </button>
-          </div>
-        );
-      }
     },
 
     render: function() {
       var localStreamClasses = React.addons.classSet({
         hide: !this._roomIsActive(),
         local: true,
         "local-stream": true,
         "local-stream-audio": false
       });
 
       return (
         <div className="room-conversation-wrapper">
-          {this._renderActionButtons()}
+          {this._renderContextualRoomInfo()}
           <div className="video-layout-wrapper">
             <div className="conversation room-conversation">
               <h2 className="room-name">{this.state.roomName}</h2>
               <div className="media nested">
                 <div className="video_wrapper remote_wrapper">
                   <div className="video_inner remote"></div>
                 </div>
                 <div className={localStreamClasses}></div>
--- a/browser/components/loop/test/standalone/standaloneRoomViews_test.js
+++ b/browser/components/loop/test/standalone/standaloneRoomViews_test.js
@@ -97,16 +97,42 @@ describe("loop.standaloneRoomViews", fun
 
     describe("#render", function() {
       var view;
 
       beforeEach(function() {
         view = mountTestComponent();
       });
 
+      describe("Empty room message", function() {
+        it("should display an empty room message on JOINED",
+          function() {
+            activeRoomStore.setStoreState({roomState: ROOM_STATES.JOINED});
+
+            expect(view.getDOMNode().querySelector(".empty-room-message"))
+              .not.eql(null);
+          });
+
+        it("should display an empty room message on SESSION_CONNECTED",
+          function() {
+            activeRoomStore.setStoreState({roomState: ROOM_STATES.SESSION_CONNECTED});
+
+            expect(view.getDOMNode().querySelector(".empty-room-message"))
+              .not.eql(null);
+          });
+
+        it("shouldn't display an empty room message on HAS_PARTICIPANTS",
+          function() {
+            activeRoomStore.setStoreState({roomState: ROOM_STATES.HAS_PARTICIPANTS});
+
+            expect(view.getDOMNode().querySelector(".empty-room-message"))
+              .eql(null);
+          });
+      });
+
       describe("Join button", function() {
         function getJoinButton(view) {
           return view.getDOMNode().querySelector(".btn-join");
         }
 
         it("should render the Join button when room isn't active", function() {
           activeRoomStore.setStoreState({roomState: ROOM_STATES.READY});
 
--- a/browser/components/loop/ui/ui-showcase.js
+++ b/browser/components/loop/ui/ui-showcase.js
@@ -564,16 +564,25 @@
               React.DOM.div({className: "standalone"}, 
                 StandaloneRoomView({
                   dispatcher: dispatcher, 
                   activeRoomStore: activeRoomStore, 
                   roomState: ROOM_STATES.READY})
               )
             ), 
 
+            Example({summary: "Standalone room conversation (joined)"}, 
+              React.DOM.div({className: "standalone"}, 
+                StandaloneRoomView({
+                  dispatcher: dispatcher, 
+                  activeRoomStore: activeRoomStore, 
+                  roomState: ROOM_STATES.JOINED})
+              )
+            ), 
+
             Example({summary: "Standalone room conversation (has-participants)"}, 
               React.DOM.div({className: "standalone"}, 
                 StandaloneRoomView({
                   dispatcher: dispatcher, 
                   activeRoomStore: activeRoomStore, 
                   roomState: ROOM_STATES.HAS_PARTICIPANTS})
               )
             )
--- a/browser/components/loop/ui/ui-showcase.jsx
+++ b/browser/components/loop/ui/ui-showcase.jsx
@@ -564,16 +564,25 @@
               <div className="standalone">
                 <StandaloneRoomView
                   dispatcher={dispatcher}
                   activeRoomStore={activeRoomStore}
                   roomState={ROOM_STATES.READY} />
               </div>
             </Example>
 
+            <Example summary="Standalone room conversation (joined)">
+              <div className="standalone">
+                <StandaloneRoomView
+                  dispatcher={dispatcher}
+                  activeRoomStore={activeRoomStore}
+                  roomState={ROOM_STATES.JOINED} />
+              </div>
+            </Example>
+
             <Example summary="Standalone room conversation (has-participants)">
               <div className="standalone">
                 <StandaloneRoomView
                   dispatcher={dispatcher}
                   activeRoomStore={activeRoomStore}
                   roomState={ROOM_STATES.HAS_PARTICIPANTS} />
               </div>
             </Example>
--- a/browser/components/nsBrowserGlue.js
+++ b/browser/components/nsBrowserGlue.js
@@ -225,17 +225,17 @@ BrowserGlue.prototype = {
     }
     delay = delay <= MAX_DELAY ? delay : MAX_DELAY;
 
     Cu.import("resource://services-sync/main.js");
     Weave.Service.scheduler.delayedAutoConnect(delay);
   },
 #endif
 
-  // nsIObserver implementation 
+  // nsIObserver implementation
   observe: function BG_observe(subject, topic, data) {
     switch (topic) {
       case "prefservice:after-app-defaults":
         this._onAppDefaults();
         break;
       case "final-ui-startup":
         this._finalUIStartup();
         break;
@@ -1454,17 +1454,17 @@ BrowserGlue.prototype = {
     var notifyBox = win.gBrowser.getNotificationBox();
     var notification = notifyBox.appendNotification(text, title, null,
                                                     notifyBox.PRIORITY_CRITICAL_MEDIUM,
                                                     buttons);
     notification.persistence = -1; // Until user closes it
   },
 
   _migrateUI: function BG__migrateUI() {
-    const UI_VERSION = 25;
+    const UI_VERSION = 26;
     const BROWSER_DOCURL = "chrome://browser/content/browser.xul";
     let currentUIVersion = 0;
     try {
       currentUIVersion = Services.prefs.getIntPref("browser.migration.version");
     } catch(ex) {}
     if (currentUIVersion >= UI_VERSION)
       return;
 
@@ -1730,16 +1730,50 @@ BrowserGlue.prototype = {
             Services.prefs.getIntPref("privacy.donottrackheader.value") != 1) {
           Services.prefs.clearUserPref("privacy.donottrackheader.enabled");
           Services.prefs.clearUserPref("privacy.donottrackheader.value");
         }
       }
       catch (ex) {}
     }
 
+    if (currentUIVersion < 26) {
+      // Refactor urlbar suggestion preferences to make it extendable and
+      // allow new suggestion types (e.g: search suggestions).
+      let types = ["history", "bookmark", "openpage"];
+      let defaultBehavior = 0;
+      try {
+        defaultBehavior = Services.prefs.getIntPref("browser.urlbar.default.behavior");
+      } catch (ex) {}
+      try {
+        let autocompleteEnabled = Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled");
+        if (!autocompleteEnabled) {
+          defaultBehavior = -1;
+        }
+      } catch (ex) {}
+
+      // If the default behavior is:
+      //    -1  - all new "...suggest.*" preferences will be false
+      //     0  - all new "...suggest.*" preferences will use the default values
+      //   > 0  - all new "...suggest.*" preferences will be inherited
+      for (let type of types) {
+        let prefValue = defaultBehavior == 0;
+        if (defaultBehavior > 0) {
+          prefValue = !!(defaultBehavior & Ci.mozIPlacesAutoComplete["BEHAVIOR_" + type.toUpperCase()]);
+        }
+        Services.prefs.setBoolPref("browser.urlbar.suggest." + type, prefValue);
+      }
+
+      // Typed behavior will be used only for results from history.
+      if (defaultBehavior != -1 &&
+          !!(defaultBehavior & Ci.mozIPlacesAutoComplete["BEHAVIOR_TYPED"])) {
+        Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", true);
+      }
+    }
+
     // Update the migration version.
     Services.prefs.setIntPref("browser.migration.version", UI_VERSION);
   },
 
   // ------------------------------
   // public nsIBrowserGlue members
   // ------------------------------
 
new file mode 100644
--- /dev/null
+++ b/browser/components/places/tests/unit/test_browserGlue_urlbar_defaultbehavior_migration.js
@@ -0,0 +1,150 @@
+/* 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/. */
+
+const UI_VERSION = 26;
+const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
+const TOPICDATA_BROWSERGLUE_TEST = "force-ui-migration";
+const DEFAULT_BEHAVIOR_PREF = "browser.urlbar.default.behavior";
+const AUTOCOMPLETE_PREF = "browser.urlbar.autocomplete.enabled";
+
+let gBrowserGlue = Cc["@mozilla.org/browser/browserglue;1"]
+                     .getService(Ci.nsIObserver);
+let gGetBoolPref = Services.prefs.getBoolPref;
+
+function run_test() {
+  run_next_test();
+};
+
+do_register_cleanup(cleanup);
+
+function cleanup() {
+  let prefix = "browser.urlbar.suggest.";
+  for (let type of ["history", "bookmark", "openpage", "history.onlyTyped"]) {
+    Services.prefs.clearUserPref(prefix + type);
+  }
+  Services.prefs.clearUserPref("browser.migration.version");
+  Services.prefs.clearUserPref(AUTOCOMPLETE_PREF);
+};
+
+function setupBehaviorAndMigrate(aDefaultBehavior, aAutocompleteEnabled = true) {
+  cleanup();
+  // Migrate browser.urlbar.default.behavior preference.
+  Services.prefs.setIntPref("browser.migration.version", UI_VERSION - 1);
+  Services.prefs.setIntPref(DEFAULT_BEHAVIOR_PREF, aDefaultBehavior);
+  Services.prefs.setBoolPref(AUTOCOMPLETE_PREF, aAutocompleteEnabled);
+  // Simulate a migration.
+  gBrowserGlue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_BROWSERGLUE_TEST);
+};
+
+add_task(function*() {
+  do_log_info("Migrate default.behavior = 0");
+  setupBehaviorAndMigrate(0);
+
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+    "History preference should be true.");
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
+    "Bookmark preference should be true.");
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.openpage"),
+    "Openpage preference should be true.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+    "Typed preference should be false.");
+});
+
+add_task(function*() {
+  do_log_info("Migrate default.behavior = 1");
+  setupBehaviorAndMigrate(1);
+
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+    "History preference should be true.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
+    "Bookmark preference should be false.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+    "Openpage preference should be false");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+    "Typed preference should be false");
+});
+
+add_task(function*() {
+  do_log_info("Migrate default.behavior = 2");
+  setupBehaviorAndMigrate(2);
+
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false,
+    "History preference should be false.");
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
+    "Bookmark preference should be true.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+    "Openpage preference should be false");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+    "Typed preference should be false");
+});
+
+add_task(function*() {
+  do_log_info("Migrate default.behavior = 3");
+  setupBehaviorAndMigrate(3);
+
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+    "History preference should be true.");
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
+    "Bookmark preference should be true.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+    "Openpage preference should be false");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+    "Typed preference should be false");
+});
+
+add_task(function*() {
+  do_log_info("Migrate default.behavior = 19");
+  setupBehaviorAndMigrate(19);
+
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+    "History preference should be true.");
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.bookmark"),
+    "Bookmark preference should be true.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+    "Openpage preference should be false");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+    "Typed preference should be false");
+});
+
+add_task(function*() {
+  do_log_info("Migrate default.behavior = 33");
+  setupBehaviorAndMigrate(33);
+
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+    "History preference should be true.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
+    "Bookmark preference should be false.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+    "Openpage preference should be false");
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"),
+    "Typed preference should be true");
+});
+
+add_task(function*() {
+  do_log_info("Migrate default.behavior = 129");
+  setupBehaviorAndMigrate(129);
+
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.history"),
+    "History preference should be true.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
+    "Bookmark preference should be false.");
+  Assert.ok(gGetBoolPref("browser.urlbar.suggest.openpage"),
+    "Openpage preference should be true");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+    "Typed preference should be false");
+});
+
+add_task(function*() {
+  do_log_info("Migrate default.behavior = 0, autocomplete.enabled = false");
+  setupBehaviorAndMigrate(0, false);
+
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.history"), false,
+    "History preference should be false.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.bookmark"), false,
+    "Bookmark preference should be false.");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.openpage"), false,
+    "Openpage preference should be false");
+  Assert.equal(gGetBoolPref("browser.urlbar.suggest.history.onlyTyped"), false,
+    "Typed preference should be false");
+});
--- a/browser/components/places/tests/unit/xpcshell.ini
+++ b/browser/components/places/tests/unit/xpcshell.ini
@@ -14,11 +14,12 @@ support-files =
 [test_browserGlue_corrupt.js]
 [test_browserGlue_corrupt_nobackup.js]
 [test_browserGlue_corrupt_nobackup_default.js]
 [test_browserGlue_distribution.js]
 [test_browserGlue_migrate.js]
 [test_browserGlue_prefs.js]
 [test_browserGlue_restore.js]
 [test_browserGlue_smartBookmarks.js]
+[test_browserGlue_urlbar_defaultbehavior_migration.js]
 [test_clearHistory_shutdown.js]
 [test_leftpane_corruption_handling.js]
 [test_PUIU_makeTransaction.js]
--- a/browser/components/preferences/in-content/privacy.js
+++ b/browser/components/preferences/in-content/privacy.js
@@ -28,16 +28,35 @@ var gPrivacyPane = {
     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
     link.setAttribute("href", url);
 
     document.getElementById("trackingprotectionbox").hidden = false;
   },
 #endif
 
   /**
+   * Initialize autocomplete to ensure prefs are in sync.
+   */
+  _initAutocomplete: function () {
+    let unifiedCompletePref = false;
+    try {
+      unifiedCompletePref =
+        Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
+    } catch (ex) {}
+
+    if (unifiedCompletePref) {
+      Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
+                .getService(Components.interfaces.mozIPlacesAutoComplete);
+    } else {
+      Components.classes["@mozilla.org/autocomplete/search;1?name=history"]
+                .getService(Components.interfaces.mozIPlacesAutoComplete);
+    }
+  },
+
+  /**
    * Sets up the UI for the number of days of history to keep, and updates the
    * label of the "Clear Now..." button.
    */
   init: function ()
   {
     function setEventListener(aId, aEventType, aCallback)
     {
       document.getElementById(aId)
@@ -47,21 +66,18 @@ var gPrivacyPane = {
     this._updateSanitizeSettingsButton();
     this.initializeHistoryMode();
     this.updateHistoryModePane();
     this.updatePrivacyMicroControls();
     this.initAutoStartPrivateBrowsingReverter();
 #ifdef NIGHTLY_BUILD
     this._initTrackingProtection();
 #endif
+    this._initAutocomplete();
 
-    setEventListener("browser.urlbar.default.behavior", "change",
-      document.getElementById('browser.urlbar.autocomplete.enabled')
-              .updateElements
-    );
     setEventListener("privacy.sanitize.sanitizeOnShutdown", "change",
                      gPrivacyPane._updateSanitizeSettingsButton);
     setEventListener("browser.privatebrowsing.autostart", "change",
                      gPrivacyPane.updatePrivacyMicroControls);
     setEventListener("historyMode", "command", function () {
       gPrivacyPane.updateHistoryModePane();
       gPrivacyPane.updateHistoryModePrefs();
       gPrivacyPane.updatePrivacyMicroControls();
@@ -323,50 +339,26 @@ var gPrivacyPane = {
       mode.doCommand();
 
       this._shouldPromptForRestart = true;
   },
 
   // HISTORY
 
   /**
-   * Read the location bar enabled and suggestion prefs
-   * @return Int value for suggestion menulist
+   * Update browser.urlbar.autocomplete.enabled when a
+   * browser.urlbar.suggest.* pref is changed from the ui.
    */
-  readSuggestionPref: function PPP_readSuggestionPref()
-  {
-    let getVal = function(aPref)
-      document.getElementById("browser.urlbar." + aPref).value;
-
-    // Suggest nothing if autocomplete is not enabled
-    if (!getVal("autocomplete.enabled"))
-      return -1;
-
-    // Bottom 2 bits of default.behavior specify history/bookmark
-    return getVal("default.behavior") & 3;
-  },
-
-  /**
-   * Write the location bar enabled and suggestion prefs when necessary
-   * @return Bool value for enabled pref
-   */
-  writeSuggestionPref: function PPP_writeSuggestionPref()
-  {
-    let menuVal = document.getElementById("locationBarSuggestion").value;
-    let enabled = menuVal != -1;
-
-    // Only update default.behavior if we're giving suggestions
-    if (enabled) {
-      // Put the selected menu item's value directly into the bottom 2 bits
-      let behavior = document.getElementById("browser.urlbar.default.behavior");
-      behavior.value = behavior.value >> 2 << 2 | menuVal;
+  writeSuggestionPref: function () {
+    let getVal = (aPref) => {
+      return document.getElementById("browser.urlbar.suggest." + aPref).value;
     }
-
-    // Always update the enabled pref
-    return enabled;
+    // autocomplete.enabled is true if any of the suggestions is true
+    let enabled = ["history", "bookmark", "openpage"].map(getVal).some(v => v);
+    Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled);
   },
 
   /*
    * Preferences:
    *
    * places.history.enabled
    * - whether history is enabled or not
    * browser.formfill.enable
--- a/browser/components/preferences/in-content/privacy.xul
+++ b/browser/components/preferences/in-content/privacy.xul
@@ -24,19 +24,25 @@
   <preference id="pref.privacy.disable_button.view_cookies"
               name="pref.privacy.disable_button.view_cookies"
               type="bool"/>
 
   <!-- Location Bar -->
   <preference id="browser.urlbar.autocomplete.enabled"
               name="browser.urlbar.autocomplete.enabled"
               type="bool"/>
-  <preference id="browser.urlbar.default.behavior"
-              name="browser.urlbar.default.behavior"
-              type="int"/>
+  <preference id="browser.urlbar.suggest.bookmark"
+              name="browser.urlbar.suggest.bookmark"
+              type="bool"/>
+  <preference id="browser.urlbar.suggest.history"
+              name="browser.urlbar.suggest.history"
+              type="bool"/>
+  <preference id="browser.urlbar.suggest.openpage"
+              name="browser.urlbar.suggest.openpage"
+              type="bool"/>
 
   <!-- History -->
   <preference id="places.history.enabled"
               name="places.history.enabled"
               type="bool"/>
   <preference id="browser.formfill.enable"
               name="browser.formfill.enable"
               type="bool"/>
@@ -226,28 +232,26 @@
           </hbox>
         </vbox>
       </vbox>
     </vbox>
   </deck>
 </groupbox>
 
 <!-- Location Bar -->
-<groupbox id="locationBarGroup" data-category="panePrivacy" hidden="true">
+<groupbox id="locationBarGroup"
+          data-category="panePrivacy"
+          hidden="true">
   <caption><label>&locationBar.label;</label></caption>
-  <hbox align="center">
-    <label id="locationBarSuggestionLabel"
-           control="locationBarSuggestion"
-           accesskey="&locbar.pre.accessKey;">&locbar.pre.label;</label>
-    <menulist id="locationBarSuggestion"
-              preference="browser.urlbar.autocomplete.enabled"
-              onsyncfrompreference="return gPrivacyPane.readSuggestionPref();"
-              onsynctopreference="return gPrivacyPane.writeSuggestionPref();">
-      <menupopup>
-        <menuitem label="&locbar.both.label;" value="0"/>
-        <menuitem label="&locbar.history.label;" value="1"/>
-        <menuitem label="&locbar.bookmarks.label;" value="2"/>
-        <menuitem label="&locbar.nothing.label;" value="-1"/>
-      </menupopup>
-    </menulist>
-    <label>&locbar.post.label;</label>
-  </hbox>
+  <label id="locationBarSuggestionLabel">&locbar.pre.label;</label>
+  <checkbox id="historySuggestion" label="&locbar.history.label;"
+            onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
+            accesskey="&locbar.history.accesskey;"
+            preference="browser.urlbar.suggest.history"/>
+  <checkbox id="bookmarkSuggestion" label="&locbar.bookmarks.label;"
+            onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
+            accesskey="&locbar.bookmarks.accesskey;"
+            preference="browser.urlbar.suggest.bookmark"/>
+  <checkbox id="openpageSuggestion" label="&locbar.openpage.label;"
+            onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
+            accesskey="&locbar.openpage.accesskey;"
+            preference="browser.urlbar.suggest.openpage"/>
 </groupbox>
--- a/browser/components/preferences/in-content/tests/browser_privacypane_5.js
+++ b/browser/components/preferences/in-content/tests/browser_privacypane_5.js
@@ -8,18 +8,18 @@ function test() {
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
   loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
 
   run_test_subset([
-    test_locbar_suggestion_retention(-1, undefined),
-    test_locbar_suggestion_retention(1, -1),
-    test_locbar_suggestion_retention(2, 1),
-    test_locbar_suggestion_retention(0, 2),
-    test_locbar_suggestion_retention(0, 0),
+    test_locbar_suggestion_retention("history", true),
+    test_locbar_suggestion_retention("bookmark", true),
+    test_locbar_suggestion_retention("openpage", false),
+    test_locbar_suggestion_retention("history", true),
+    test_locbar_suggestion_retention("history", false),
 
     // reset all preferences to their default values once we're done
     reset_preferences
   ]);
-}
+}
\ No newline at end of file
--- a/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
+++ b/browser/components/preferences/in-content/tests/privacypane_tests_perwindow.js
@@ -296,28 +296,24 @@ function test_custom_retention(controlTo
     case "menulist":
       controlToChange.value = valueIncrement;
       break;
     }
     controlChanged(controlToChange);
   };
 }
 
-function test_locbar_suggestion_retention(mode, expect) {
+function test_locbar_suggestion_retention(suggestion, autocomplete) {
   return function(win) {
-    let locbarsuggest = win.document.getElementById("locationBarSuggestion");
-    ok(locbarsuggest, "location bar suggestion menulist should exist");
+    let elem = win.document.getElementById(suggestion + "Suggestion");
+    ok(elem, "Suggest " + suggestion + " checkbox should exist.");
+    elem.click();
 
-    if (expect !== undefined) {
-      is(locbarsuggest.value, expect,
-        "location bar suggestion is expected to remain " + expect);
-    }
-
-    locbarsuggest.value = mode;
-    controlChanged(locbarsuggest);
+    is(Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled"), autocomplete,
+       "browser.urlbar.autocomplete.enabled pref should be " + autocomplete);
   };
 }
 
 function reset_preferences(win) {
   let prefs = win.document.querySelectorAll("#privacyPreferences > preference");
   for (let i = 0; i < prefs.length; ++i)
     if (prefs[i].hasUserValue)
       prefs[i].reset();
--- a/browser/components/preferences/privacy.js
+++ b/browser/components/preferences/privacy.js
@@ -31,29 +31,49 @@ var gPrivacyPane = {
     let url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "tracking-protection";
     link.setAttribute("href", url);
 
     document.getElementById("trackingprotectionbox").hidden = false;
   },
 #endif
 
   /**
+   * Initialize autocomplete to ensure prefs are in sync.
+   */
+  _initAutocomplete: function () {
+    let unifiedCompletePref = false;
+    try {
+      unifiedCompletePref =
+        Services.prefs.getBoolPref("browser.urlbar.unifiedcomplete");
+    } catch (ex) {}
+
+    if (unifiedCompletePref) {
+      Components.classes["@mozilla.org/autocomplete/search;1?name=unifiedcomplete"]
+                .getService(Components.interfaces.mozIPlacesAutoComplete);
+    } else {
+      Components.classes["@mozilla.org/autocomplete/search;1?name=history"]
+                .getService(Components.interfaces.mozIPlacesAutoComplete);
+    }
+  },
+
+  /**
    * Sets up the UI for the number of days of history to keep, and updates the
    * label of the "Clear Now..." button.
    */
   init: function ()
   {
     this._updateSanitizeSettingsButton();
     this.initializeHistoryMode();
     this.updateHistoryModePane();
     this.updatePrivacyMicroControls();
     this.initAutoStartPrivateBrowsingReverter();
 #ifdef NIGHTLY_BUILD
     this._initTrackingProtection();
 #endif
+    this._initAutocomplete();
   },
 
   // HISTORY MODE
 
   /**
    * The list of preferences which affect the initial history mode settings.
    * If the auto start private browsing mode pref is active, the initial
    * history mode would be set to "Don't remember anything".
@@ -286,50 +306,26 @@ var gPrivacyPane = {
       mode.doCommand();
 
       this._shouldPromptForRestart = true;
   },
 
   // HISTORY
 
   /**
-   * Read the location bar enabled and suggestion prefs
-   * @return Int value for suggestion menulist
+   * Update browser.urlbar.autocomplete.enabled when a
+   * browser.urlbar.suggest.* pref is changed from the ui.
    */
-  readSuggestionPref: function PPP_readSuggestionPref()
-  {
-    let getVal = function(aPref)
-      document.getElementById("browser.urlbar." + aPref).value;
-
-    // Suggest nothing if autocomplete is not enabled
-    if (!getVal("autocomplete.enabled"))
-      return -1;
-
-    // Bottom 2 bits of default.behavior specify history/bookmark
-    return getVal("default.behavior") & 3;
-  },
-
-  /**
-   * Write the location bar enabled and suggestion prefs when necessary
-   * @return Bool value for enabled pref
-   */
-  writeSuggestionPref: function PPP_writeSuggestionPref()
-  {
-    let menuVal = document.getElementById("locationBarSuggestion").value;
-    let enabled = menuVal != -1;
-
-    // Only update default.behavior if we're giving suggestions
-    if (enabled) {
-      // Put the selected menu item's value directly into the bottom 2 bits
-      let behavior = document.getElementById("browser.urlbar.default.behavior");
-      behavior.value = behavior.value >> 2 << 2 | menuVal;
+  writeSuggestionPref: function PPP_writeSuggestionPref() {
+    let getVal = (aPref) => {
+      return document.getElementById("browser.urlbar.suggest." + aPref).value;
     }
-
-    // Always update the enabled pref
-    return enabled;
+    // autocomplete.enabled is true if any of the suggestions is true
+    let enabled = ["history", "bookmark", "openpage"].map(getVal).some(v => v);
+    Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled);
   },
 
   /*
    * Preferences:
    *
    * places.history.enabled
    * - whether history is enabled or not
    * browser.formfill.enable
--- a/browser/components/preferences/privacy.xul
+++ b/browser/components/preferences/privacy.xul
@@ -37,20 +37,25 @@
       <preference id="pref.privacy.disable_button.view_cookies"
                   name="pref.privacy.disable_button.view_cookies"
                   type="bool"/>
 
       <!-- Location Bar -->
       <preference id="browser.urlbar.autocomplete.enabled"
                   name="browser.urlbar.autocomplete.enabled"
                   type="bool"/>
-      <preference id="browser.urlbar.default.behavior"
-                  name="browser.urlbar.default.behavior"
-                  type="int"
-                  onchange="document.getElementById('browser.urlbar.autocomplete.enabled').updateElements();"/>
+      <preference id="browser.urlbar.suggest.bookmark"
+                  name="browser.urlbar.suggest.bookmark"
+                  type="bool"/>
+      <preference id="browser.urlbar.suggest.history"
+                  name="browser.urlbar.suggest.history"
+                  type="bool"/>
+      <preference id="browser.urlbar.suggest.openpage"
+                  name="browser.urlbar.suggest.openpage"
+                  type="bool"/>
 
       <!-- History -->
       <preference id="places.history.enabled"
                   name="places.history.enabled"
                   type="bool"/>
       <preference id="browser.formfill.enable"
                   name="browser.formfill.enable"
                   type="bool"/>
@@ -249,30 +254,29 @@
       </deck>
 
     </groupbox>
 
     <!-- Location Bar -->
     <groupbox id="locationBarGroup">
       <caption label="&locationBar.label;"/>
 
-      <hbox align="center">
-        <label id="locationBarSuggestionLabel"
-               control="locationBarSuggestion"
-               accesskey="&locbar.pre.accessKey;">&locbar.pre.label;</label>
-        <menulist id="locationBarSuggestion"
-                  preference="browser.urlbar.autocomplete.enabled"
-                  onsyncfrompreference="return gPrivacyPane.readSuggestionPref();"
-                  onsynctopreference="return gPrivacyPane.writeSuggestionPref();">
-          <menupopup>
-            <menuitem label="&locbar.both.label;" value="0"/>
-            <menuitem label="&locbar.history.label;" value="1"/>
-            <menuitem label="&locbar.bookmarks.label;" value="2"/>
-            <menuitem label="&locbar.nothing.label;" value="-1"/>
-          </menupopup>
-        </menulist>
-        <label>&locbar.post.label;</label>
-      </hbox>
+      <label id="locationBarSuggestionLabel">&locbar.pre.label;</label>
+
+      <vbox id="tabPrefsBox" align="start" flex="1">
+        <checkbox id="historySuggestion" label="&locbar.history.label;"
+                  onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
+                  accesskey="&locbar.history.accesskey;"
+                  preference="browser.urlbar.suggest.history"/>
+        <checkbox id="bookmarkSuggestion" label="&locbar.bookmarks.label;"
+                  onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
+                  accesskey="&locbar.bookmarks.accesskey;"
+                  preference="browser.urlbar.suggest.bookmark"/>
+        <checkbox id="openpageSuggestion" label="&locbar.openpage.label;"
+                  onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
+                  accesskey="&locbar.openpage.accesskey;"
+                  preference="browser.urlbar.suggest.openpage"/>
+      </vbox>
     </groupbox>
 
   </prefpane>
 
 </overlay>
--- a/browser/components/preferences/tests/browser.ini
+++ b/browser/components/preferences/tests/browser.ini
@@ -14,10 +14,9 @@ skip-if = e10s # Bug 941459 - pushPrefEn
 [browser_healthreport.js]
 skip-if = !healthreport || (os == 'linux' && debug)
 [browser_permissions.js]
 [browser_privacypane_1.js]
 [browser_privacypane_3.js]
 [browser_privacypane_4.js]
 skip-if = e10s # leaks windows
 [browser_privacypane_5.js]
-skip-if = e10s # leaks windows
 [browser_privacypane_8.js]
--- a/browser/components/preferences/tests/browser_privacypane_5.js
+++ b/browser/components/preferences/tests/browser_privacypane_5.js
@@ -1,26 +1,25 @@
-/* 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/. */
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 function test() {
   let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
                getService(Ci.mozIJSSubScriptLoader);
   let rootDir = getRootDirectory(gTestPath);
   let jar = getJar(rootDir);
   if (jar) {
     let tmpdir = extractJarToTmp(jar);
     rootDir = "file://" + tmpdir.path + '/';
   }
   loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
 
   run_test_subset([
-    test_locbar_suggestion_retention(-1, undefined),
-    test_locbar_suggestion_retention(1, -1),
-    test_locbar_suggestion_retention(2, 1),
-    test_locbar_suggestion_retention(0, 2),
-    test_locbar_suggestion_retention(0, 0),
+    test_locbar_suggestion_retention("history", true),
+    test_locbar_suggestion_retention("bookmark", true),
+    test_locbar_suggestion_retention("openpage", false),
+    test_locbar_suggestion_retention("history", true),
+    test_locbar_suggestion_retention("history", false),
 
     // reset all preferences to their default values once we're done
     reset_preferences
   ]);
-}
+}
\ No newline at end of file
--- a/browser/components/preferences/tests/privacypane_tests_perwindow.js
+++ b/browser/components/preferences/tests/privacypane_tests_perwindow.js
@@ -305,28 +305,24 @@ function test_custom_retention(controlTo
     case "menulist":
       controlToChange.value = valueIncrement;
       break;
     }
     controlChanged(controlToChange);
   };
 }
 
-function test_locbar_suggestion_retention(mode, expect) {
+function test_locbar_suggestion_retention(suggestion, autocomplete) {
   return function(win) {
-    let locbarsuggest = win.document.getElementById("locationBarSuggestion");
-    ok(locbarsuggest, "location bar suggestion menulist should exist");
+    let elem = win.document.getElementById(suggestion + "Suggestion");
+    ok(elem, "Suggest " + suggestion + " checkbox should exist.");
+    elem.click();
 
-    if (expect !== undefined) {
-      is(locbarsuggest.value, expect,
-        "location bar suggestion is expected to remain " + expect);
-    }
-
-    locbarsuggest.value = mode;
-    controlChanged(locbarsuggest);
+    is(Services.prefs.getBoolPref("browser.urlbar.autocomplete.enabled"), autocomplete,
+       "browser.urlbar.autocomplete.enabled pref should be " + autocomplete);
   };
 }
 
 function reset_preferences(win) {
   let prefs = win.document.getElementsByTagName("preference");
   for (let i = 0; i < prefs.length; ++i)
     if (prefs[i].hasUserValue)
       prefs[i].reset();
new file mode 100644
--- /dev/null
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.js
@@ -0,0 +1,58 @@
+/* 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/. */
+
+const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
+
+Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
+
+var stringBundle = Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci.nsIStringBundleService)
+                                                         .createBundle("chrome://browser/locale/aboutPrivateBrowsing.properties");
+
+if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
+  document.title = stringBundle.GetStringFromName("title.normal");
+  setFavIcon("chrome://global/skin/icons/question-16.png");
+} else {
+  document.title = stringBundle.GetStringFromName("title");
+  setFavIcon("chrome://browser/skin/Privacy-16.png");
+}
+
+var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIWebNavigation)
+                       .QueryInterface(Ci.nsIDocShellTreeItem)
+                       .rootTreeItem
+                       .QueryInterface(Ci.nsIInterfaceRequestor)
+                       .getInterface(Ci.nsIDOMWindow);
+
+function setFavIcon(url) {
+  var icon = document.createElement("link");
+  icon.setAttribute("rel", "icon");
+  icon.setAttribute("type", "image/png");
+  icon.setAttribute("href", url);
+  var head = document.getElementsByTagName("head")[0];
+  head.insertBefore(icon, head.firstChild);
+}
+
+document.addEventListener("DOMContentLoaded", function () {
+  if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
+    document.body.setAttribute("class", "normal");
+  }
+
+  // Set up the help link
+  let learnMoreURL = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
+                     .getService(Ci.nsIURLFormatter)
+                     .formatURLPref("app.support.baseURL");
+  let learnMore = document.getElementById("learnMore");
+  if (learnMore) {
+    learnMore.setAttribute("href", learnMoreURL + "private-browsing");
+  }
+  
+  let startPrivateBrowsing = document.getElementById("startPrivateBrowsing");
+  if (startPrivateBrowsing) {
+    startPrivateBrowsing.addEventListener("command", openPrivateWindow);
+  }
+}, false);
+
+function openPrivateWindow() {
+  mainWindow.OpenBrowserWindow({private: true});
+}
--- a/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
+++ b/browser/components/privatebrowsing/content/aboutPrivateBrowsing.xhtml
@@ -15,84 +15,35 @@
   %browserDTD;
   <!ENTITY % aboutPrivateBrowsingDTD SYSTEM "chrome://browser/locale/aboutPrivateBrowsing.dtd">
   %aboutPrivateBrowsingDTD;
 ]>
 
 <html xmlns="http://www.w3.org/1999/xhtml">
   <head>
     <link rel="stylesheet" href="chrome://browser/content/aboutPrivateBrowsing.css" type="text/css" media="all"/>
-    <script type="application/javascript;version=1.7"><![CDATA[
-      const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
-
-      Cu.import("resource://gre/modules/PrivateBrowsingUtils.jsm");
-
-      if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
-        document.title = "]]>&aboutPrivateBrowsing.title.normal;<![CDATA[";
-        setFavIcon("chrome://global/skin/icons/question-16.png");
-      } else {
-#ifndef XP_MACOSX
-        document.title = "]]>&aboutPrivateBrowsing.title;<![CDATA[";
-#endif
-        setFavIcon("chrome://browser/skin/Privacy-16.png");
-      }
-
-      var mainWindow = window.QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsIWebNavigation)
-                             .QueryInterface(Ci.nsIDocShellTreeItem)
-                             .rootTreeItem
-                             .QueryInterface(Ci.nsIInterfaceRequestor)
-                             .getInterface(Ci.nsIDOMWindow);
-
-      function setFavIcon(url) {
-        var icon = document.createElement("link");
-        icon.setAttribute("rel", "icon");
-        icon.setAttribute("type", "image/png");
-        icon.setAttribute("href", url);
-        var head = document.getElementsByTagName("head")[0];
-        head.insertBefore(icon, head.firstChild);
-      }
-
-      document.addEventListener("DOMContentLoaded", function () {
-        if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
-          document.body.setAttribute("class", "normal");
-        }
-
-        // Set up the help link
-        let learnMoreURL = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
-                           .getService(Ci.nsIURLFormatter)
-                           .formatURLPref("app.support.baseURL");
-        let learnMore = document.getElementById("learnMore");
-        if (learnMore) {
-          learnMore.setAttribute("href", learnMoreURL + "private-browsing");
-        }
-      }, false);
-
-      function openPrivateWindow() {
-        mainWindow.OpenBrowserWindow({private: true});
-      }
-    ]]></script>
+    <script type="application/javascript;version=1.7" src="chrome://browser/content/aboutPrivateBrowsing.js"></script>
   </head>
 
   <body dir="&locale.dir;" class="private">
     <div id="pageContainer">
       <h1 class="titleText showPrivate">&aboutPrivateBrowsing.title;</h1>
       <h1 class="titleText showNormal">&aboutPrivateBrowsing.title.normal;</h1>
 
       <p class="subtitleText showPrivate">&aboutPrivateBrowsing.subtitle;</p>
       <p class="subtitleText showNormal">&aboutPrivateBrowsing.subtitle.normal;</p>
 
       <p class="descriptionText">&aboutPrivateBrowsing.description;</p>
 
       <p class="notPrivateText showNormal">&aboutPrivateBrowsing.notPrivate;</p>
 
       <button xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+              id="startPrivateBrowsing"
               class="openPrivate showNormal"
               label="&privatebrowsingpage.openPrivateWindow.label;"
-              accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;"
-              oncommand="openPrivateWindow();"/>
+              accesskey="&privatebrowsingpage.openPrivateWindow.accesskey;"/>
       <div class="showPrivate">
         <p class="moreInfoText">&aboutPrivateBrowsing.moreInfo;</p>
         <p><a id="learnMore" target="_blank">&aboutPrivateBrowsing.learnMore;</a></p>
       </div>
     </div>
   </body>
 </html>
--- a/browser/components/privatebrowsing/jar.mn
+++ b/browser/components/privatebrowsing/jar.mn
@@ -1,7 +1,8 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 browser.jar:
 *   content/browser/aboutPrivateBrowsing.css              (content/aboutPrivateBrowsing.css)
-*   content/browser/aboutPrivateBrowsing.xhtml            (content/aboutPrivateBrowsing.xhtml) 
+    content/browser/aboutPrivateBrowsing.xhtml            (content/aboutPrivateBrowsing.xhtml) 
+*   content/browser/aboutPrivateBrowsing.js               (content/aboutPrivateBrowsing.js)
\ No newline at end of file
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_windowtitle.js
@@ -22,17 +22,17 @@ function test() {
   let pb_page_without_title;
   let pb_about_pb_title;
   if (isOSX) {
     page_with_title = test_title;
     page_without_title = app_name;
     about_pb_title = "Open a private window?";
     pb_page_with_title = test_title + " - (Private Browsing)";
     pb_page_without_title = app_name + " - (Private Browsing)";
-    pb_about_pb_title = pb_page_without_title;
+    pb_about_pb_title = "You're browsing privately - (Private Browsing)";
   }
   else {
     page_with_title = test_title + " - " + app_name;
     page_without_title = app_name;
     about_pb_title = "Open a private window?" + " - " + app_name;
     pb_page_with_title = test_title + " - " + app_name + " (Private Browsing)";
     pb_page_without_title = app_name + " (Private Browsing)";
     pb_about_pb_title = "You're browsing privately - " + app_name + " (Private Browsing)";
--- a/browser/devtools/styleeditor/StyleEditorUI.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUI.jsm
@@ -534,22 +534,22 @@ StyleEditorUI.prototype = {
               return;
             }
 
             let href = csscoverage.sheetToUrl(editor.styleSheet);
             usage.createEditorReport(href).then(data => {
               editor.removeAllUnusedRegions();
 
               if (data.reports.length > 0) {
-                // So there is some coverage markup, but can we apply it?
-                let text = editor.sourceEditor.getText() + "\r";
-                // If the CSS text contains a '}' with some non-whitespace
-                // after then we assume this is compressed CSS and stop
-                // marking-up.
-                if (!/}\s*\S+\s*\r/.test(text)) {
+                // Only apply if this file isn't compressed. We detect a
+                // compressed file if there are more rules than lines.
+                let text = editor.sourceEditor.getText();
+                let lineCount = text.split("\n").length;
+                let ruleCount = editor.styleSheet.ruleCount;
+                if (lineCount >= ruleCount) {
                   editor.addUnusedRegions(data.reports);
                 }
                 else {
                   this.emit("error", { key: "error-compressed", level: "info" });
                 }
               }
             });
           }, console.error);
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/aboutPrivateBrowsing.properties
@@ -0,0 +1,2 @@
+title=You're browsing privately
+title.normal=Open a private window?
\ No newline at end of file
--- a/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
+++ b/browser/locales/en-US/chrome/browser/preferences/privacy.dtd
@@ -11,22 +11,22 @@
 <!ENTITY trackingProtectionLearnMore.label "Learn more">
 <!ENTITY doNotTrackInfo.label           "Learn More">
 
 <!ENTITY  history.label                 "History">
 
 <!ENTITY  locationBar.label             "Location Bar">
 
 <!ENTITY  locbar.pre.label              "When using the location bar, suggest:">
-<!ENTITY  locbar.pre.accessKey          "u">
-<!ENTITY  locbar.post.label             "">
-<!ENTITY  locbar.both.label             "History and Bookmarks">
 <!ENTITY  locbar.history.label          "History">
+<!ENTITY  locbar.history.accesskey      "i">
 <!ENTITY  locbar.bookmarks.label        "Bookmarks">
-<!ENTITY  locbar.nothing.label          "Nothing">
+<!ENTITY  locbar.bookmarks.accesskey    "d">
+<!ENTITY  locbar.openpage.label         "Open tabs">
+<!ENTITY  locbar.openpage.accesskey     "g">
 
 <!ENTITY  acceptCookies.label           "Accept cookies from sites">
 <!ENTITY  acceptCookies.accesskey       "A">
 
 <!ENTITY  acceptThirdParty.pre.label      "Accept third-party cookies:">
 <!ENTITY  acceptThirdParty.pre.accesskey  "c">
 <!ENTITY  acceptThirdParty.always.label   "Always">
 <!ENTITY  acceptThirdParty.never.label    "Never">
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -5,16 +5,17 @@
 
 
 @AB_CD@.jar:
 % locale browser @AB_CD@ %locale/browser/
     locale/browser/aboutAccounts.dtd               (%chrome/browser/aboutAccounts.dtd)
     locale/browser/aboutCertError.dtd              (%chrome/browser/aboutCertError.dtd)
     locale/browser/aboutDialog.dtd                 (%chrome/browser/aboutDialog.dtd)
     locale/browser/aboutPrivateBrowsing.dtd        (%chrome/browser/aboutPrivateBrowsing.dtd)
+    locale/browser/aboutPrivateBrowsing.properties (%chrome/browser/aboutPrivateBrowsing.properties)
     locale/browser/aboutRobots.dtd                 (%chrome/browser/aboutRobots.dtd)
     locale/browser/aboutHome.dtd                   (%chrome/browser/aboutHome.dtd)
 #ifdef MOZ_SERVICES_HEALTHREPORT
     locale/browser/aboutHealthReport.dtd           (%chrome/browser/aboutHealthReport.dtd)
 #endif
     locale/browser/aboutSessionRestore.dtd         (%chrome/browser/aboutSessionRestore.dtd)
 #ifdef MOZ_SERVICES_SYNC
     locale/browser/syncProgress.dtd                (%chrome/browser/syncProgress.dtd)
--- a/browser/metro/profile/metro.js
+++ b/browser/metro/profile/metro.js
@@ -300,24 +300,31 @@ pref("browser.search.noCurrentEngine", t
 #ifdef MOZ_OFFICIAL_BRANDING
 // {moz:official} expands to "official"
 pref("browser.search.official", true);
 #endif
 
 // enable xul error pages
 pref("browser.xul.error_pages.enabled", true);
 
-// Specify emptyRestriction = 0 so that bookmarks appear in the list by default
-pref("browser.urlbar.default.behavior", 0);
-pref("browser.urlbar.default.behavior.emptyRestriction", 0);
-
 // Let the faviconservice know that we display favicons as 25x25px so that it
 // uses the right size when optimizing favicons
 pref("places.favicons.optimizeToDimension", 25);
 
+// The default behavior for the urlbar can be configured to use any combination
+// of the match filters with each additional filter adding more results (union).
+pref("browser.urlbar.suggest.history",              true);
+pref("browser.urlbar.suggest.bookmark",             true);
+pref("browser.urlbar.suggest.openpage",             true);
+pref("browser.urlbar.suggest.search",               true);
+
+// Restrictions to current suggestions can also be applied (intersection).
+// Typed suggestion works only if history is set to true.
+pref("browser.urlbar.suggest.history.onlyTyped",    false);
+
 // various and sundry awesomebar prefs (should remove/re-evaluate
 // these once bug 447900 is fixed)
 pref("browser.urlbar.trimURLs", true);
 pref("browser.urlbar.formatting.enabled", true);
 pref("browser.urlbar.clickSelectsAll", true);
 pref("browser.urlbar.doubleClickSelectsAll", true);
 pref("browser.urlbar.autoFill", false);
 pref("browser.urlbar.matchOnlyTyped", false);
--- a/browser/modules/BrowserNewTabPreloader.jsm
+++ b/browser/modules/BrowserNewTabPreloader.jsm
@@ -295,16 +295,22 @@ HiddenBrowser.prototype = {
     HostFrame.get().then(aFrame => {
       let doc = aFrame.document;
       this._browser = doc.createElementNS(XUL_NS, "browser");
       this._browser.setAttribute("type", "content");
       this._browser.setAttribute("src", NEWTAB_URL);
       this._applySize();
       doc.getElementById("win").appendChild(this._browser);
 
+      // The browser might not have a docShell here if the HostFrame was
+      // destroyed while the promise was resolved. Simply bail out.
+      if (!this._browser.docShell) {
+        return;
+      }
+
       // Let the docShell be inactive so that document.hidden=true.
       this._browser.docShell.isActive = false;
 
       this._browser.messageManager.loadFrameScript(BROWSER_CONTENT_SCRIPT,
                                                    true);
     });
   },
 
--- a/dom/telephony/test/marionette/head.js
+++ b/dom/telephony/test/marionette/head.js
@@ -31,69 +31,69 @@ let emulator = (function() {
     throw "Use emulator.runCmdWithCallback(cmd, callback) instead of runEmulatorCmd";
   };
 
   // Overwritten it so people could not call this function directly.
   runEmulatorShell = function() {
     throw "Use emulator.runShellCmd(cmd, callback) instead of runEmulatorShell";
   };
 
+  /**
+   * @return Promise
+   */
   function runCmd(cmd) {
-    let deferred = Promise.defer();
-
-    pendingCmdCount++;
-    originalRunEmulatorCmd(cmd, function(result) {
-      pendingCmdCount--;
-      if (result[result.length - 1] === "OK") {
-        deferred.resolve(result);
-      } else {
-        is(result[result.length - 1], "OK", "emulator command result.");
-        deferred.reject();
-      }
+    return new Promise(function(resolve, reject) {
+      pendingCmdCount++;
+      originalRunEmulatorCmd(cmd, function(result) {
+        pendingCmdCount--;
+        if (result[result.length - 1] === "OK") {
+          resolve(result);
+        } else {
+          is(result[result.length - 1], "OK", "emulator command result.");
+          reject();
+        }
+      });
     });
-
-    return deferred.promise;
   }
 
+  /**
+   * @return Promise
+   */
   function runCmdWithCallback(cmd, callback) {
-    runCmd(cmd).then(result => {
+    return runCmd(cmd).then(result => {
       if (callback && typeof callback === "function") {
         callback(result);
       }
     });
   }
 
   /**
    * @return Promise
    */
   function runShellCmd(aCommands) {
-    let deferred = Promise.defer();
-
-    ++pendingShellCount;
-    originalRunEmulatorShell(aCommands, function(aResult) {
-      --pendingShellCount;
-      deferred.resolve(aResult);
+    return new Promise(function(resolve, reject) {
+      ++pendingShellCount;
+      originalRunEmulatorShell(aCommands, function(aResult) {
+        --pendingShellCount;
+        resolve(aResult);
+      });
     });
-
-    return deferred.promise;
   }
 
   /**
    * @return Promise
    */
   function waitFinish() {
-    let deferred = Promise.defer();
-
-    waitFor(function() {
-      deferred.resolve();
-    }, function() {
-      return pendingCmdCount === 0 && pendingShellCount === 0;
+    return new Promise(function(resolve, reject) {
+      waitFor(function() {
+        resolve();
+      }, function() {
+        return pendingCmdCount === 0 && pendingShellCount === 0;
+      });
     });
-
-    return deferred.promise;
   }
 
   return {
     runCmd: runCmd,
     runCmdWithCallback: runCmdWithCallback,
     runShellCmd: runShellCmd,
     waitFinish: waitFinish
   };
@@ -102,42 +102,140 @@ let emulator = (function() {
 /**
  * Telephony related helper functions.
  */
 (function() {
   /**
    * @return Promise
    */
   function delay(ms) {
-    let deferred = Promise.defer();
+    return new Promise(function(resolve, reject) {
+      let startTime = Date.now();
+      waitFor(function() {
+        resolve();
+      },function() {
+        let duration = Date.now() - startTime;
+        return (duration >= ms);
+      });
+    });
+  }
+
+  /**
+   * Wait for one named event.
+   *
+   * @param aTarget
+   *        A event target.
+   * @param aEventName
+   *        A string event name.
+   * @param aPredicate [optional]
+   *        A predicate function, resolve the promise if aPredicate(event)
+   *        return true
+   * @return Promise<DOMEvent>
+   */
+  function waitForEvent(aTarget, aEventName, aPredicate) {
+    return new Promise(function(resolve, reject) {
+      aTarget.addEventListener(aEventName, function onevent(aEvent) {
+        if (aPredicate === undefined || aPredicate(aEvent)) {
+          aTarget.removeEventListener(aEventName, onevent);
+
+          let label = "X";
+          if (aTarget instanceof TelephonyCall) {
+            label = "Call (" + aTarget.id.number + ")";
+          } else if (aTarget instanceof TelephonyCallGroup) {
+            label = "Conference";
+          } else if (aTarget instanceof Telephony) {
+            label = "Telephony";
+          }
+
+          log(label + " received event '" + aEventName + "'");
+          resolve(aEvent);
+        }
+      });
+    });
+  }
 
-    let startTime = Date.now();
-    waitFor(function() {
-      deferred.resolve();
-    },function() {
-      let duration = Date.now() - startTime;
-      return (duration >= ms);
-    });
+  /**
+   * Wait for callschanged event with event.call == aExpectedCall
+   *
+   * @param aTarget
+   *        A event target.
+   * @param aExpectedCall
+   *        Expected call for event.call
+   * @return Promise<DOMEvent>
+   */
+  function waitForCallsChangedEvent(aTarget, aExpectedCall) {
+    return waitForEvent(aTarget, "callschanged",
+                        event => event.call == aExpectedCall);
+  }
 
-    return deferred.promise;
+  /**
+   * Wait for call state event, e.g., "connected", "disconnected", ...
+   *
+   * @param aTarget
+   *        A event target.
+   * @param aState
+   *        State name
+   * @return Promise<TelephonyCall>
+   */
+  function waitForNamedStateEvent(aTarget, aState) {
+    return waitForEvent(aTarget, aState)
+      .then(event => {
+        if (aTarget instanceof TelephonyCall) {
+          is(aTarget, event.call, "event.call");
+        }
+        is(aTarget.state, aState, "check state");
+        return aTarget;
+      });
+  }
+
+  /**
+   * Wait for groupchange event.
+   *
+   * @param aCall
+   *        A TelephonyCall object.
+   * @param aGroup
+   *        The new group
+   * @return Promise<TelephonyCall>
+   */
+  function waitForGroupChangeEvent(aCall, aGroup) {
+    return waitForEvent(aCall, "groupchange")
+      .then(() => {
+        is(aCall.group, aGroup, "call group");
+        return aCall;
+      });
+  }
+
+  /**
+   * Wait for statechange event.
+   *
+   * @param aTarget
+   *        A event target.
+   * @param aState
+   *        The desired new state. Check it.
+   * @return Promise<DOMEvent>
+   */
+  function waitForStateChangeEvent(aTarget, aState) {
+    return waitForEvent(aTarget, "statechange")
+      .then(() => {
+        is(aTarget.state, aState);
+        return aTarget;
+      });
   }
 
   /**
    * @return Promise
    */
   function waitForNoCall() {
-    let deferred = Promise.defer();
-
-    waitFor(function() {
-      deferred.resolve();
-    }, function() {
-      return telephony.calls.length === 0;
+    return new Promise(function(resolve, reject) {
+      waitFor(function() {
+        resolve();
+      }, function() {
+        return telephony.calls.length === 0;
+      });
     });
-
-    return deferred.promise;
   }
 
   /**
    * @return Promise
    */
   function clearCalls() {
     log("Clear existing calls.");
 
@@ -209,164 +307,33 @@ let emulator = (function() {
 
   /**
    * Check utility functions.
    */
 
   function checkInitialState() {
     log("Verify initial state.");
     ok(telephony.calls, 'telephony.call');
-    checkTelephonyActiveAndCalls(null, []);
     ok(conference.calls, 'conference.calls');
-    checkConferenceStateAndCalls('', []);
-  }
-
-  /**
-   * Convenient helper to compare a TelephonyCall and a received call event.
-   */
-  function checkEventCallState(event, call, state) {
-    is(call, event.call, "event.call");
-    is(call.state, state, "call state");
-  }
-
-  /**
-   * Convenient helper to compare two call lists. Size should be the same and
-   * order is not important.
-   */
-  function checkCalls(actualCalls, expectedCalls) {
-    if (actualCalls.length == expectedCalls.length) {
-      let expectedSet = new Set(expectedCalls);
-      for (let i = 0; i < actualCalls.length; ++i) {
-        ok(expectedSet.has(actualCalls[i]), "should contain the call");
-      }
-    }
-  }
-
-  /**
-   * Convenient helper to check mozTelephony.active and mozTelephony.calls.
-   */
-  function checkTelephonyActiveAndCalls(active, calls) {
-    is(telephony.active, active, "telephony.active");
-    is(telephony.calls.length, calls.length, "telephony.calls");
-    checkCalls(telephony.calls, calls);
-  }
-
-  /**
-   * Convenient helper to check mozTelephony.conferenceGroup.state and
-   * .conferenceGroup.calls.
-   */
-  function checkConferenceStateAndCalls(state, calls) {
-    is(conference.state, state, "conference.state");
-    is(conference.calls.length, calls.length, "conference.calls");
-    checkCalls(conference.calls, calls);
+    checkState(null, [], "", []);
   }
 
   /**
-   * Convenient helper to handle *.oncallschanged event.
-   *
-   * @param container
-   *        Representation of "mozTelephony" or "mozTelephony.conferenceGroup."
-   * @param containerName
-   *        Name of container. Could be an arbitrary string, used for debug
-   *        messages only.
-   * @param expectedCalls
-   *        An array of calls.
-   * @param callback
-   *        A callback function.
+   * Convenient helper to compare two call lists (order is not important).
    */
-  function check_oncallschanged(container, containerName, expectedCalls,
-                                callback) {
-    container.oncallschanged = function(event) {
-      log("Received 'callschanged' event for the " + containerName);
-
-      ok(event.call);
-
-      let index = expectedCalls.indexOf(event.call);
-      ok(index != -1);
-      expectedCalls.splice(index, 1);
-
-      if (expectedCalls.length === 0) {
-        container.oncallschanged = null;
-        callback();
-      }
-    };
-  }
-
-  /**
-   * Convenient helper to handle *.ongroupchange event.
-   *
-   * @param call
-   *        A TelephonyCall object.
-   * @param callName
-   *        Name of a call. Could be an arbitrary string, used for debug messages
-   *        only.
-   * @param group
-   *        Representation of mozTelephony.conferenceGroup.
-   * @param callback
-   *        A callback function.
-   */
-  function check_ongroupchange(call, callName, group, callback) {
-    call.ongroupchange = function(event) {
-      log("Received 'groupchange' event for the " + callName);
-      call.ongroupchange = null;
+  function checkCalls(actualCalls, expectedCalls) {
+    if (actualCalls.length != expectedCalls.length) {
+      ok(false, "check calls.length");
+      return;
+    }
 
-      is(call.group, group);
-      callback();
-    };
-  }
-
-  /**
-   * Convenient helper to handle *.onstatechange event.
-   *
-   * @param container
-   *        Representation of a TelephonyCall or mozTelephony.conferenceGroup.
-   * @param containerName
-   *        Name of container. Could be an arbitrary string, used for debug messages
-   *        only.
-   * @param state
-   *        A string.
-   * @param callback
-   *        A callback function.
-   */
-  function check_onstatechange(container, containerName, state, callback) {
-    container.onstatechange = function(event) {
-      log("Received 'statechange' event for the " + containerName);
-      container.onstatechange = null;
-
-      is(container.state, state);
-      callback();
-    };
-  }
-
-  /**
-   * Convenient helper to check the sequence of call state and event handlers.
-   *
-   * @param state
-   *        A string of the expected call state.
-   * @param previousEvent
-   *        A string of the event that should come before the expected state.
-   */
-  function StateEventChecker(state, previousEvent) {
-    let event = 'on' + state;
-
-    return function(call, callName, callback) {
-      call[event] = function() {
-        log("Received '" + state + "' event for the " + callName);
-        call[event] = null;
-
-        if (previousEvent) {
-          // We always clear the event handler when the event is received.
-          // Therefore, if the corresponding handler is not existed, the expected
-          // previous event has been already received.
-          ok(!call[previousEvent]);
-        }
-        is(call.state, state);
-        callback();
-      };
-    };
+    let expectedSet = new Set(expectedCalls);
+    for (let i = 0; i < actualCalls.length; ++i) {
+      ok(expectedSet.has(actualCalls[i]), "should contain the call");
+    }
   }
 
   /**
    * Convenient helper to check the expected call number and name.
    *
    * @param number
    *        A string sent to modem.
    * @param numberPresentation
@@ -396,17 +363,17 @@ let emulator = (function() {
     is(receivedName, expectedName, "check name per number/namePresentation");
   }
 
   /**
    * Convenient helper to check the call list existing in the emulator.
    *
    * @param expectedCallList
    *        An array of call info with the format of "callStrPool()[state]".
-   * @return A deferred promise.
+   * @return Promise
    */
   function checkEmulatorCallList(expectedCallList) {
     return emulator.runCmd("gsm list").then(result => {
       log("Call list is now: " + result);
       for (let i = 0; i < expectedCallList.length; ++i) {
         is(result[i], expectedCallList[i], "emulator calllist");
       }
     });
@@ -423,18 +390,20 @@ let emulator = (function() {
    *        mozTelephony.calls.
    * @param conferenceState
    *        A string. Should be the expected conference state.
    * @param conferenceCalls
    *        An array of TelephonyCall objects. Should be the expected list of
    *        mozTelephony.conferenceGroup.calls.
    */
   function checkState(active, calls, conferenceState, conferenceCalls) {
-    checkTelephonyActiveAndCalls(active, calls);
-    checkConferenceStateAndCalls(conferenceState, conferenceCalls);
+    is(telephony.active, active, "telephony.active");
+    checkCalls(telephony.calls, calls);
+    is(conference.state, conferenceState, "conference.state");
+    checkCalls(conference.calls, conferenceCalls);
   }
 
   /**
    * Super convenient helper to check calls and state of mozTelephony and
    * mozTelephony.conferenceGroup as well as the calls existing in the emulator.
    *
    * @param active
    *        A TelephonyCall object. Should be the expected active call.
@@ -443,715 +412,483 @@ let emulator = (function() {
    *        mozTelephony.calls.
    * @param conferenceState
    *        A string. Should be the expected conference state.
    * @param conferenceCalls
    *        An array of TelephonyCall objects. Should be the expected list of
    *        mozTelephony.conferenceGroup.calls.
    * @param callList
    *        An array of call info with the format of "callStrPool()[state]".
-   * @return A deferred promise.
+   * @return Promise
    */
   function checkAll(active, calls, conferenceState, conferenceCalls, callList) {
     checkState(active, calls, conferenceState, conferenceCalls);
     return checkEmulatorCallList(callList);
   }
 
   /**
    * Request utility functions.
    */
 
   /**
-   * Make sure there's no pending event before we jump to the next action.
-   *
-   * @param received
-   *        A string of the received event.
-   * @param pending
-   *        An array of the pending events.
-   * @param nextAction
-   *        A callback function that is called when there's no pending event.
-   */
-  function receivedPending(received, pending, nextAction) {
-    let index = pending.indexOf(received);
-    if (index != -1) {
-      pending.splice(index, 1);
-    }
-    if (pending.length === 0) {
-      nextAction();
-    }
-  }
-
-  /**
    * Make an outgoing call.
    *
    * @param number
    *        A string.
    * @param serviceId [optional]
    *        Identification of a service. 0 is set as default.
-   * @return A deferred promise.
+   * @return Promise<TelephonyCall>
    */
   function dial(number, serviceId) {
     serviceId = typeof serviceId !== "undefined" ? serviceId : 0;
     log("Make an outgoing call: " + number + ", serviceId: " + serviceId);
 
-    let deferred = Promise.defer();
-
-    telephony.dial(number, serviceId).then(call => {
-      ok(call);
-      is(call.id.number, number);
-      is(call.state, "dialing");
-      is(call.serviceId, serviceId);
+    return telephony.dial(number, serviceId)
+      .then(call => {
+        ok(call);
+        is(call.id.number, number);
+        is(call.state, "dialing");
+        is(call.serviceId, serviceId);
 
-      call.onalerting = function onalerting(event) {
-        call.onalerting = null;
-        log("Received 'onalerting' call event.");
-        checkEventCallState(event, call, "alerting");
-        deferred.resolve(call);
-      };
-    }, cause => {
-      deferred.reject(cause);
-    });
-
-    return deferred.promise;
+        return waitForNamedStateEvent(call, "alerting");
+      });
   }
 
   /**
    * Make an outgoing emergency call.
    *
    * @param number
    *        A string.
-   * @return A deferred promise.
+   * @return Promise<TelephonyCall>
    */
   function dialEmergency(number) {
     log("Make an outgoing emergency call: " + number);
 
-    let deferred = Promise.defer();
-
-    telephony.dialEmergency(number).then(call => {
-      ok(call);
-      is(call.id.number, number);
-      is(call.state, "dialing");
+    return telephony.dialEmergency(number)
+      .then(call => {
+        ok(call);
+        is(call.id.number, number);
+        is(call.state, "dialing");
 
-      call.onalerting = function onalerting(event) {
-        call.onalerting = null;
-        log("Received 'onalerting' call event.");
-        checkEventCallState(event, call, "alerting");
-        deferred.resolve(call);
-      };
-    }, cause => {
-      deferred.reject(cause);
-    });
-
-    return deferred.promise;
+        return waitForNamedStateEvent(call, "alerting");
+      });
   }
 
   /**
    * Answer an incoming call.
    *
    * @param call
    *        An incoming TelephonyCall object.
    * @param conferenceStateChangeCallback [optional]
    *        A callback function which is called if answering an incoming call
    *        triggers conference state change.
-   * @return A deferred promise.
+   * @return Promise<TelephonyCall>
    */
   function answer(call, conferenceStateChangeCallback) {
     log("Answering the incoming call.");
 
-    let deferred = Promise.defer();
-    let done = function() {
-      deferred.resolve(call);
-    };
+    let promises = [];
 
-    let pending = ["call.onconnected"];
-    let receive = function(name) {
-      receivedPending(name, pending, done);
-    };
+    let promise = waitForNamedStateEvent(call, "connecting")
+      .then(() => waitForNamedStateEvent(call, "connected"));
+
+    promises.push(promise);
 
-    // When there's already a connected conference call, answering a new incoming
-    // call triggers conference state change. We should wait for
-    // |conference.onstatechange| before checking the state of the conference call.
+    // incoming call triggers conference state change. We should wait for
+    // |conference.onstatechange| before checking the state of the conference
+    // call.
     if (conference.state === "connected") {
-      pending.push("conference.onstatechange");
-      check_onstatechange(conference, "conference", "held", function() {
-        if (typeof conferenceStateChangeCallback === "function") {
-          conferenceStateChangeCallback();
-        }
-        receive("conference.onstatechange");
-      });
+      let promise = waitForStateChangeEvent(conference, "held")
+        .then(() => {
+          if (typeof conferenceStateChangeCallback === "function") {
+            conferenceStateChangeCallback();
+          }
+        });
+
+      promises.push(promise);
     }
 
-    call.onconnecting = function onconnectingIn(event) {
-      log("Received 'connecting' call event for incoming call.");
-      call.onconnecting = null;
-      checkEventCallState(event, call, "connecting");
-    };
-
-    call.onconnected = function onconnectedIn(event) {
-      log("Received 'connected' call event for incoming call.");
-      call.onconnected = null;
-      checkEventCallState(event, call, "connected");
-      ok(!call.onconnecting);
-      receive("call.onconnected");
-    };
     call.answer();
 
-    return deferred.promise;
+    return Promise.all(promises).then(() => call);
   }
 
   /**
    * Hold a call.
    *
    * @param call
    *        A TelephonyCall object.
-   * @return A deferred promise.
+   * @return Promise<TelephonyCall>
    */
   function hold(call) {
     log("Putting the call on hold.");
 
-    let deferred = Promise.defer();
+    let promise = waitForNamedStateEvent(call, "holding")
+      .then(() => waitForNamedStateEvent(call, "held"));
 
-    let gotHolding = false;
-    call.onholding = function onholding(event) {
-      log("Received 'holding' call event");
-      call.onholding = null;
-      checkEventCallState(event, call, "holding");
-      gotHolding = true;
-    };
-
-    call.onheld = function onheld(event) {
-      log("Received 'held' call event");
-      call.onheld = null;
-      checkEventCallState(event, call, "held");
-      ok(gotHolding);
-      deferred.resolve(call);
-    };
     call.hold();
 
-    return deferred.promise;
+    return promise;
   }
 
   /**
    * Resume a call.
    *
    * @param call
    *        A TelephonyCall object.
-   * @return A deferred promise.
+   * @return Promise<TelephonyCall>
    */
   function resume(call) {
     log("Resuming the held call.");
 
-    let deferred = Promise.defer();
+    let promise = waitForNamedStateEvent(call, "resuming")
+      .then(() => waitForNamedStateEvent(call, "connected"));
 
-    let gotResuming = false;
-    call.onresuming = function onresuming(event) {
-      log("Received 'resuming' call event");
-      call.onresuming = null;
-      checkEventCallState(event, call, "resuming");
-      gotResuming = true;
-    };
-
-    call.onconnected = function onconnected(event) {
-      log("Received 'connected' call event");
-      call.onconnected = null;
-      checkEventCallState(event, call, "connected");
-      ok(gotResuming);
-      deferred.resolve(call);
-    };
     call.resume();
 
-    return deferred.promise;
+    return promise;
   }
 
   /**
    * Locally hang up a call.
    *
    * @param call
    *        A TelephonyCall object.
-   * @return A deferred promise.
+   * @return Promise<TelephonyCall>
    */
   function hangUp(call) {
-    let deferred = Promise.defer();
+    log("Local hanging up the call: " + call.id.number);
 
-    call.ondisconnected = function(event) {
-      log("Received 'disconnected' call event");
-      call.ondisconnected = null;
-      checkEventCallState(event, call, "disconnected");
-      deferred.resolve(call);
-    };
+    let promise = waitForNamedStateEvent(call, "disconnecting")
+      .then(() => waitForNamedStateEvent(call, "disconnected"));
+
     call.hangUp();
 
-    return deferred.promise;
+    return promise;
   }
 
   /**
    * Simulate an incoming call.
    *
    * @param number
    *        A string.
    * @param numberPresentation [optional]
    *        An unsigned short integer.
    * @param name [optional]
    *        A string.
    * @param namePresentation [optional]
    *        An unsigned short integer.
-   * @return A deferred promise.
+   * @return Promise<TelephonyCall>
    */
   function remoteDial(number, numberPresentation, name, namePresentation) {
     log("Simulating an incoming call.");
 
-    let deferred = Promise.defer();
-
-    telephony.onincoming = function onincoming(event) {
-      log("Received 'incoming' call event.");
-      telephony.onincoming = null;
-
-      let call = event.call;
-
-      ok(call);
-      is(call.state, "incoming");
-      checkCallId(number, numberPresentation, name, namePresentation,
-                  call.id.number, call.id.name);
-      deferred.resolve(call);
-    };
-
     numberPresentation = numberPresentation || "";
     name = name || "";
     namePresentation = namePresentation || "";
     emulator.runCmd("gsm call " + number + "," + numberPresentation + "," + name +
                  "," + namePresentation);
-    return deferred.promise;
+
+    return waitForEvent(telephony, "incoming")
+      .then(event => {
+        let call = event.call;
+
+        ok(call);
+        is(call.state, "incoming");
+        checkCallId(number, numberPresentation, name, namePresentation,
+                    call.id.number, call.id.name);
+
+        return call;
+      });
   }
 
   /**
    * Remote party answers the call.
    *
    * @param call
    *        A TelephonyCall object.
-   * @return A deferred promise.
+   * @return Promise<TelephonyCall>
    */
   function remoteAnswer(call) {
-    log("Remote answering the call.");
-
-    let deferred = Promise.defer();
+    log("Remote answering the call: " + call.id.number);
 
-    call.onconnected = function onconnected(event) {
-      log("Received 'connected' call event.");
-      call.onconnected = null;
-      checkEventCallState(event, call, "connected");
-      deferred.resolve(call);
-    };
     emulator.runCmd("gsm accept " + call.id.number);
 
-    return deferred.promise;
+    return waitForNamedStateEvent(call, "connected");
   }
 
   /**
    * Remote party hangs up the call.
    *
    * @param call
    *        A TelephonyCall object.
-   * @return A deferred promise.
+   * @return Promise<TelephonyCall>
    */
   function remoteHangUp(call) {
-    log("Remote hanging up the call.");
-
-    let deferred = Promise.defer();
+    log("Remote hanging up the call: " + call.id.number);
 
-    call.ondisconnected = function ondisconnected(event) {
-      log("Received 'disconnected' call event.");
-      call.ondisconnected = null;
-      checkEventCallState(event, call, "disconnected");
-      deferred.resolve(call);
-    };
     emulator.runCmd("gsm cancel " + call.id.number);
 
-    return deferred.promise;
+    return waitForNamedStateEvent(call, "disconnected");
   }
 
   /**
    * Remote party hangs up all the calls.
    *
    * @param calls
    *        An array of TelephonyCall objects.
-   * @return A deferred promise.
+   * @return Promise
    */
   function remoteHangUpCalls(calls) {
-    let promise = Promise.resolve();
-
-    for (let call of calls) {
-      promise = promise.then(remoteHangUp.bind(null, call));
-    }
-
-    return promise;
+    let promises = calls.map(remoteHangUp);
+    return Promise.all(promises);
   }
 
   /**
    * Add calls to conference.
    *
    * @param callsToAdd
    *        An array of TelephonyCall objects to be added into conference. The
    *        length of the array should be 1 or 2.
    * @param connectedCallback [optional]
    *        A callback function which is called when conference state becomes
    *        connected.
    * @param twice [optional]
    *        To send conference request twice. It is only used for special test.
-   * @return A deferred promise.
+   * @return Promise<[TelephonyCall ...]>
    */
   function addCallsToConference(callsToAdd, connectedCallback, twice) {
     log("Add " + callsToAdd.length + " calls into conference.");
 
-    let deferred = Promise.defer();
-    let done = function() {
-      deferred.resolve();
-    };
-
-    let pending = ["conference.oncallschanged", "conference.onconnected"];
-    let receive = function(name) {
-      receivedPending(name, pending, done);
-    };
-
-    let check_onconnected  = StateEventChecker('connected', 'onresuming');
+    let promises = [];
 
     for (let call of callsToAdd) {
-      let callName = "callToAdd (" + call.id.number + ')';
-
-      let ongroupchange = callName + ".ongroupchange";
-      pending.push(ongroupchange);
-      check_ongroupchange(call, callName, conference,
-                          receive.bind(null, ongroupchange));
-
-      let onstatechange = callName + ".onstatechange";
-      pending.push(onstatechange);
-      check_onstatechange(call, callName, 'connected',
-                          receive.bind(null, onstatechange));
+      promises.push(waitForCallsChangedEvent(conference, call));
+      promises.push(waitForGroupChangeEvent(call, conference));
+      promises.push(waitForNamedStateEvent(call, "connected"));
+      promises.push(waitForStateChangeEvent(call, "connected"));
     }
 
-    check_oncallschanged(conference, 'conference', callsToAdd,
-                         receive.bind(null, "conference.oncallschanged"));
-
-    check_onconnected(conference, "conference", function() {
-      ok(!conference.oncallschanged);
-      if (typeof connectedCallback === 'function') {
-        connectedCallback();
-      }
-      receive("conference.onconnected");
-    });
+    let promise = waitForNamedStateEvent(conference, "connected")
+      .then(() => {
+        if (typeof connectedCallback === "function") {
+          connectedCallback();
+        }
+      });
+    promises.push(promise);
 
     // Cannot use apply() through webidl, so just separate the cases to handle.
     let requestCount = twice ? 2 : 1;
     for (let i = 0; i < requestCount; ++i) {
       if (callsToAdd.length == 2) {
         conference.add(callsToAdd[0], callsToAdd[1]);
       } else {
         conference.add(callsToAdd[0]);
       }
     }
 
-    return deferred.promise;
+    return Promise.all(promises).then(() => conference.calls);
   }
 
   /**
    * Hold the conference.
    *
-   * @param calls
+   * @param callsInConference
    *        An array of TelephonyCall objects existing in conference.
    * @param heldCallback [optional]
    *        A callback function which is called when conference state becomes
    *        held.
-   * @return A deferred promise.
+   * @return Promise<[TelephonyCall ...]>
    */
-  function holdConference(calls, heldCallback) {
+  function holdConference(callsInConference, heldCallback) {
     log("Holding the conference call.");
 
-    let deferred = Promise.defer();
-    let done = function() {
-      deferred.resolve();
-    };
-
-    let pending = ["conference.onholding", "conference.onheld"];
-    let receive = function(name) {
-      receivedPending(name, pending, done);
-    };
+    let promises = [];
 
-    let check_onholding = StateEventChecker('holding', null);
-    let check_onheld = StateEventChecker('held', 'onholding');
-
-    for (let call of calls) {
-      let callName = "call (" + call.id.number + ')';
-
-      let onholding = callName + ".onholding";
-      pending.push(onholding);
-      check_onholding(call, callName, receive.bind(null, onholding));
-
-      let onheld = callName + ".onheld";
-      pending.push(onheld);
-      check_onheld(call, callName, receive.bind(null, onheld));
+    for (let call of callsInConference) {
+      let promise = waitForNamedStateEvent(call, "holding")
+        .then(() => waitForNamedStateEvent(call, "held"));
+      promises.push(promise);
     }
 
-    check_onholding(conference, "conference",
-                    receive.bind(null, "conference.onholding"));
-
-    check_onheld(conference, "conference", function() {
-      if (typeof heldCallback === 'function') {
-        heldCallback();
-      }
-      receive("conference.onheld");
-    });
+    let promise = waitForNamedStateEvent(conference, "holding")
+      .then(() => waitForNamedStateEvent(conference, "held"))
+      .then(() => {
+        if (typeof heldCallback === "function") {
+          heldCallback();
+        }
+      });
+    promises.push(promise);
 
     conference.hold();
 
-    return deferred.promise;
+    return Promise.all(promises).then(() => conference.calls);
   }
 
   /**
    * Resume the conference.
    *
-   * @param calls
+   * @param callsInConference
    *        An array of TelephonyCall objects existing in conference.
    * @param connectedCallback [optional]
    *        A callback function which is called when conference state becomes
    *        connected.
-   * @return A deferred promise.
+   * @return Promise<[TelephonyCall ...]>
    */
-  function resumeConference(calls, connectedCallback) {
+  function resumeConference(callsInConference, connectedCallback) {
     log("Resuming the held conference call.");
 
-    let deferred = Promise.defer();
-    let done = function() {
-      deferred.resolve();
-    };
-
-    let pending = ["conference.onresuming", "conference.onconnected"];
-    let receive = function(name) {
-      receivedPending(name, pending, done);
-    };
+    let promises = [];
 
-    let check_onresuming   = StateEventChecker('resuming', null);
-    let check_onconnected  = StateEventChecker('connected', 'onresuming');
-
-    for (let call of calls) {
-      let callName = "call (" + call.id.number + ')';
-
-      let onresuming = callName + ".onresuming";
-      pending.push(onresuming);
-      check_onresuming(call, callName, receive.bind(null, onresuming));
-
-      let onconnected = callName + ".onconnected";
-      pending.push(onconnected);
-      check_onconnected(call, callName, receive.bind(null, onconnected));
+    for (let call of callsInConference) {
+      let promise = waitForNamedStateEvent(call, "resuming")
+        .then(() => waitForNamedStateEvent(call, "connected"));
+      promises.push(promise);
     }
 
-    check_onresuming(conference, "conference",
-                     receive.bind(null, "conference.onresuming"));
-
-    check_onconnected(conference, "conference", function() {
-      if (typeof connectedCallback === 'function') {
-        connectedCallback();
-      }
-      receive("conference.onconnected");
-    });
+    let promise = waitForNamedStateEvent(conference, "resuming")
+      .then(() => waitForNamedStateEvent(conference, "connected"))
+      .then(() => {
+        if (typeof connectedCallback === "function") {
+          connectedCallback();
+        }
+      });
+    promises.push(promise);
 
     conference.resume();
 
-    return deferred.promise;
+    return Promise.all(promises).then(() => conference.calls);
   }
 
   /**
    * Remove a call out of conference.
    *
    * @param callToRemove
    *        A TelephonyCall object existing in conference.
    * @param autoRemovedCalls
    *        An array of TelephonyCall objects which is going to be automatically
    *        removed. The length of the array should be 0 or 1.
    * @param remainedCalls
    *        An array of TelephonyCall objects which remain in conference.
    * @param stateChangeCallback [optional]
    *        A callback function which is called when conference state changes.
-   * @return A deferred promise.
+   * @return Promise<[TelephonyCall ...]>
    */
   function removeCallInConference(callToRemove, autoRemovedCalls, remainedCalls,
                                   statechangeCallback) {
     log("Removing a participant from the conference call.");
 
     is(conference.state, 'connected');
 
-    let deferred = Promise.defer();
-    let done = function() {
-      deferred.resolve();
-    };
+    let promises = [];
 
-    let pending = ["callToRemove.ongroupchange", "telephony.oncallschanged",
-                   "conference.oncallschanged", "conference.onstatechange"];
-    let receive = function(name) {
-      receivedPending(name, pending, done);
-    };
-
-    // Remained call in conference will be held.
-    for (let call of remainedCalls) {
-      let callName = "remainedCall (" + call.id.number + ')';
-
-      let onstatechange = callName + ".onstatechange";
-      pending.push(onstatechange);
-      check_onstatechange(call, callName, 'held',
-                          receive.bind(null, onstatechange));
-    }
+    // callToRemove.
+    promises.push(waitForCallsChangedEvent(telephony, callToRemove));
+    promises.push(waitForCallsChangedEvent(conference, callToRemove));
+    promises.push(waitForGroupChangeEvent(callToRemove, null).then(() => {
+      is(callToRemove.state, 'connected');
+    }));
 
     // When a call is removed from conference with 2 calls, another one will be
     // automatically removed from group and be put on hold.
     for (let call of autoRemovedCalls) {
-      let callName = "autoRemovedCall (" + call.id.number + ')';
-
-      let ongroupchange = callName + ".ongroupchange";
-      pending.push(ongroupchange);
-      check_ongroupchange(call, callName, null,
-                          receive.bind(null, ongroupchange));
-
-      let onstatechange = callName + ".onstatechange";
-      pending.push(onstatechange);
-      check_onstatechange(call, callName, 'held',
-                          receive.bind(null, onstatechange));
+      promises.push(waitForCallsChangedEvent(telephony, call));
+      promises.push(waitForCallsChangedEvent(conference, call));
+      promises.push(waitForGroupChangeEvent(call, null));
+      promises.push(waitForStateChangeEvent(call, "held"));
     }
 
-    check_ongroupchange(callToRemove, "callToRemove", null, function() {
-      is(callToRemove.state, 'connected');
-      receive("callToRemove.ongroupchange");
-    });
-
-    check_oncallschanged(telephony, 'telephony',
-                         autoRemovedCalls.concat(callToRemove),
-                         receive.bind(null, "telephony.oncallschanged"));
+    // Remained call in conference will be held.
+    for (let call of remainedCalls) {
+      promises.push(waitForStateChangeEvent(call, "held"));
+    }
 
-    check_oncallschanged(conference, 'conference',
-                         autoRemovedCalls.concat(callToRemove), function() {
-      is(conference.calls.length, remainedCalls.length);
-      receive("conference.oncallschanged");
-    });
-
-    check_onstatechange(conference, 'conference',
-                        (remainedCalls.length ? 'held' : ''), function() {
-      ok(!conference.oncallschanged);
-      if (typeof statechangeCallback === 'function') {
-        statechangeCallback();
-      }
-      receive("conference.onstatechange");
-    });
+    let finalConferenceState = remainedCalls.length ? "held" : "";
+    let promise = waitForStateChangeEvent(conference, finalConferenceState)
+      .then(() => {
+        if (typeof statechangeCallback === 'function') {
+          statechangeCallback();
+        }
+      });
+    promises.push(promise);
 
     conference.remove(callToRemove);
 
-    return deferred.promise;
+    return Promise.all(promises)
+      .then(() => checkCalls(conference.calls, remainedCalls))
+      .then(() => conference.calls);
   }
 
   /**
    * Hangup a call in conference.
    *
    * @param callToHangUp
    *        A TelephonyCall object existing in conference.
    * @param autoRemovedCalls
    *        An array of TelephonyCall objects which is going to be automatically
    *        removed. The length of the array should be 0 or 1.
    * @param remainedCalls
    *        An array of TelephonyCall objects which remain in conference.
    * @param stateChangeCallback [optional]
    *        A callback function which is called when conference state changes.
-   * @return A deferred promise.
+   * @return Promise<[TelephonyCall ...]>
    */
   function hangUpCallInConference(callToHangUp, autoRemovedCalls, remainedCalls,
                                   statechangeCallback) {
     log("Release one call in conference.");
 
-    let deferred = Promise.defer();
-    let done = function() {
-      deferred.resolve();
-    };
+    let promises = [];
 
-    let pending = ["conference.oncallschanged", "remoteHangUp"];
-    let receive = function(name) {
-      receivedPending(name, pending, done);
-    };
+    // callToHangUp.
+    promises.push(waitForCallsChangedEvent(conference, callToHangUp));
 
-    // When a call is hang up from conference with 2 calls, another one will be
+    // When a call is removed from conference with 2 calls, another one will be
     // automatically removed from group.
     for (let call of autoRemovedCalls) {
-      let callName = "autoRemovedCall (" + call.id.number + ')';
-
-      let ongroupchange = callName + ".ongroupchange";
-      pending.push(ongroupchange);
-      check_ongroupchange(call, callName, null,
-                          receive.bind(null, ongroupchange));
+      promises.push(waitForCallsChangedEvent(telephony, call));
+      promises.push(waitForCallsChangedEvent(conference, call));
+      promises.push(waitForGroupChangeEvent(call, null));
     }
 
-    if (autoRemovedCalls.length) {
-      pending.push("telephony.oncallschanged");
-      check_oncallschanged(telephony, 'telephony',
-                           autoRemovedCalls,
-                           receive.bind(null, "telephony.oncallschanged"));
+    if (remainedCalls.length === 0) {
+      let promise = waitForStateChangeEvent(conference, "")
+        .then(() => {
+          if (typeof statechangeCallback === 'function') {
+            statechangeCallback();
+          }
+        });
+      promises.push(promise);
     }
 
-    check_oncallschanged(conference, 'conference',
-                         autoRemovedCalls.concat(callToHangUp), function() {
-      is(conference.calls.length, remainedCalls.length);
-      receive("conference.oncallschanged");
-    });
+    promises.push(remoteHangUp(callToHangUp));
 
-    if (remainedCalls.length === 0) {
-      pending.push("conference.onstatechange");
-      check_onstatechange(conference, 'conference', '', function() {
-        ok(!conference.oncallschanged);
-        if (typeof statechangeCallback === 'function') {
-          statechangeCallback();
-        }
-        receive("conference.onstatechange");
-      });
-    }
-
-    remoteHangUp(callToHangUp)
-      .then(receive.bind(null, "remoteHangUp"));
-
-    return deferred.promise;
+    return Promise.all(promises)
+      .then(() => checkCalls(conference.calls, remainedCalls))
+      .then(() => conference.calls);
   }
 
   /**
    * Hangup conference.
    *
-   * @return A deferred promise.
+   * @return Promise
    */
   function hangUpConference() {
     log("Hangup conference.");
 
-    let deferred = Promise.defer();
-    let done = function() {
-      deferred.resolve();
-    };
+    let promises = [];
 
-    let pending = ["conference.hangUp", "conference.onstatechange"];
-    let receive = function(name) {
-      receivedPending(name, pending, done);
-    };
+    promises.push(waitForStateChangeEvent(conference, ""));
 
     for (let call of conference.calls) {
-      let callName = "Call (" + call.id.number + ')';
-
-      let onstatechange = callName + ".onstatechange";
-      pending.push(onstatechange);
-      check_onstatechange(call, callName, 'disconnected',
-                          receive.bind(null, onstatechange));
+      promises.push(waitForNamedStateEvent(call, "disconnected"));
     }
 
-    check_onstatechange(conference, 'conference', '', function() {
-      receive("conference.onstatechange");
+    return conference.hangUp().then(() => {
+      return Promise.all(promises);
     });
-
-    conference.hangUp().then(() => {
-      receive("conference.hangUp");
-    });
-
-    return deferred.promise;
   }
 
   /**
    * Create a conference with an outgoing call and an incoming call.
    *
    * @param outNumber
    *        Number of an outgoing call.
    * @param inNumber
@@ -1282,16 +1019,17 @@ let emulator = (function() {
     });
   }
 
   /**
    * Public members.
    */
 
   this.gDelay = delay;
+  this.gWaitForEvent = waitForEvent;
   this.gCheckInitialState = checkInitialState;
   this.gClearCalls = clearCalls;
   this.gOutCallStrPool = outCallStrPool;
   this.gInCallStrPool = inCallStrPool;
   this.gCheckState = checkState;
   this.gCheckAll = checkAll;
   this.gSendMMI = sendMMI;
   this.gDial = dial;
@@ -1306,17 +1044,16 @@ let emulator = (function() {
   this.gRemoteHangUpCalls = remoteHangUpCalls;
   this.gAddCallsToConference = addCallsToConference;
   this.gHoldConference = holdConference;
   this.gResumeConference = resumeConference;
   this.gRemoveCallInConference = removeCallInConference;
   this.gHangUpCallInConference = hangUpCallInConference;
   this.gHangUpConference = hangUpConference;
   this.gSetupConference = setupConference;
-  this.gReceivedPending = receivedPending;
 }());
 
 function _startTest(permissions, test) {
   function permissionSetUp() {
     SpecialPowers.setBoolPref("dom.mozSettings.enabled", true);
     for (let per of permissions) {
       SpecialPowers.addPermission(per, true, document);
     }
@@ -1366,28 +1103,21 @@ function _startTest(permissions, test) {
         .then(function() {
           originalFinish.apply(this, arguments);
         });
     }
 
     return tearDown.bind(this);
   }());
 
-  function mainTest() {
-    setUp()
-      .then(function onSuccess() {
-        log("== Test Start ==");
-        test();
-      }, function onError(error) {
-        SpecialPowers.Cu.reportError(error);
-        ok(false, "SetUp error");
-      });
-  }
-
-  mainTest();
+  setUp().then(() => {
+    log("== Test Start ==");
+    test();
+  })
+  .catch(error => ok(false, error));
 }
 
 function startTest(test) {
   _startTest(["telephony"], test);
 }
 
 function startTestWithPermissions(permissions, test) {
   _startTest(permissions.concat("telephony"), test);
--- a/dom/telephony/test/marionette/test_audiomanager_phonestate.js
+++ b/dom/telephony/test/marionette/test_audiomanager_phonestate.js
@@ -84,13 +84,11 @@ startTest(function() {
     // Conference
     .then(() => gAddCallsToConference([outCall, inCall]))
     .then(() => check(PHONE_STATE_IN_CALL))
     // Hang up all
     .then(() => gRemoteHangUpCalls([outCall, inCall]))
     .then(() => check(PHONE_STATE_NORMAL))
 
     // End
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_add_error.js
+++ b/dom/telephony/test/marionette/test_conference_add_error.js
@@ -59,13 +59,11 @@ function testConferenceAddError() {
     .then(() => handleConferenceAddError(inCall5))
     .then(() => gRemoteHangUpCalls([outCall, inCall, inCall2, inCall3, inCall4,
                                     inCall5]));
 }
 
 // Start the test
 startTest(function() {
   testConferenceAddError()
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_add_twice_error.js
+++ b/dom/telephony/test/marionette/test_conference_add_twice_error.js
@@ -36,13 +36,11 @@ function testConferenceTwoCallsTwice() {
     .then(() => gCheckAll(conference, [], 'connected', [outCall, inCall],
                           [outInfo.active, inInfo.active]))
     .then(() => gRemoteHangUpCalls([outCall, inCall]));
 }
 
 // Start the test
 startTest(function() {
   testConferenceTwoCallsTwice()
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_remove_error.js
+++ b/dom/telephony/test/marionette/test_conference_remove_error.js
@@ -56,13 +56,11 @@ function testConferenceRemoveError() {
     // calls.
     .then(() => handleConferenceRemoveError(outCall))
     .then(() => gRemoteHangUpCalls([outCall, inCall, inCall2]));
 }
 
 // Start the test
 startTest(function() {
   testConferenceRemoveError()
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_three_hangup_one.js
+++ b/dom/telephony/test/marionette/test_conference_three_hangup_one.js
@@ -25,13 +25,11 @@ function testConferenceThreeAndHangupOne
     .then(() => gCheckAll(conference, [], 'connected', [inCall, inCall2],
                           [inInfo.active, inInfo2.active]))
     .then(() => gRemoteHangUpCalls([inCall, inCall2]));
 }
 
 // Start the test
 startTest(function() {
   testConferenceThreeAndHangupOne()
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_three_remove_one.js
+++ b/dom/telephony/test/marionette/test_conference_three_remove_one.js
@@ -29,13 +29,11 @@ function testConferenceThreeAndRemoveOne
     .then(() => gCheckAll(outCall, [outCall], 'held', [inCall, inCall2],
                           [outInfo.active, inInfo.held, inInfo2.held]))
     .then(() => gRemoteHangUpCalls([outCall, inCall, inCall2]));
 }
 
 // Start the test
 startTest(function() {
   testConferenceThreeAndRemoveOne()
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_two_calls.js
+++ b/dom/telephony/test/marionette/test_conference_two_calls.js
@@ -18,13 +18,11 @@ function testConferenceTwoCalls() {
       [outCall, inCall] = calls;
     })
     .then(() => gRemoteHangUpCalls([outCall, inCall]));
 }
 
 // Start the test
 startTest(function() {
   testConferenceTwoCalls()
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_two_hangup_all.js
+++ b/dom/telephony/test/marionette/test_conference_two_hangup_all.js
@@ -44,13 +44,11 @@ function testConferenceHangUpBackground(
     .then(() => gHangUpConference())
     .then(() => gCheckAll(null, [], '', [], []));
 }
 
 // Start the test
 startTest(function() {
   testConferenceHangUpForeground()
     .then(() => testConferenceHangUpBackground())
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_two_hangup_one.js
+++ b/dom/telephony/test/marionette/test_conference_two_hangup_one.js
@@ -23,13 +23,11 @@ function testConferenceTwoAndHangupOne()
     }))
     .then(() => gCheckAll(inCall, [inCall], '', [], [inInfo.active]))
     .then(() => gRemoteHangUpCalls([inCall]));
 }
 
 // Start the test
 startTest(function() {
   testConferenceTwoAndHangupOne()
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_two_hold_resume.js
+++ b/dom/telephony/test/marionette/test_conference_two_hold_resume.js
@@ -30,13 +30,11 @@ function testConferenceHoldAndResume() {
     .then(() => gCheckAll(conference, [], 'connected', [outCall, inCall],
                           [outInfo.active, inInfo.active]))
     .then(() => gRemoteHangUpCalls([outCall, inCall]));
 }
 
 // Start the test
 startTest(function() {
   testConferenceHoldAndResume()
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_conference_two_remove_one.js
+++ b/dom/telephony/test/marionette/test_conference_two_remove_one.js
@@ -25,13 +25,11 @@ function testConferenceTwoAndRemoveOne()
     .then(() => gCheckAll(outCall, [outCall, inCall], '', [],
                           [outInfo.active, inInfo.held]))
     .then(() => gRemoteHangUpCalls([outCall, inCall]));
 }
 
 // Start the test
 startTest(function() {
   testConferenceTwoAndRemoveOne()
-    .then(null, error => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_dsds_connection_conflict.js
+++ b/dom/telephony/test/marionette/test_dsds_connection_conflict.js
@@ -41,13 +41,11 @@ function testNewCallWhenOtherConnectionI
     })
     .then(() => gRemoteHangUp(outCall));
 }
 
 startDSDSTest(function() {
   testNewCallWhenOtherConnectionInUse(0, 1)
     .then(() => testNewCallWhenOtherConnectionInUse(1, 0))
     .then(() => muxModem(0))
-    .then(null, () => {
-      ok(false, "promise rejects during test.");
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_dsds_normal_call.js
+++ b/dom/telephony/test/marionette/test_dsds_normal_call.js
@@ -68,13 +68,11 @@ function testIncomingCall() {
     .then(() => muxModem(1))
     .then(() => testIncomingCallForServiceId("0912345001", 1))
     .then(() => muxModem(0));
 }
 
 startDSDSTest(function() {
   testOutgoingCall()
     .then(testIncomingCall)
-    .then(null, () => {
-      ok(false, "promise rejects during test.");
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_emergency_label.js
+++ b/dom/telephony/test/marionette/test_emergency_label.js
@@ -73,13 +73,11 @@ startTest(function() {
     .then(() => {
       eccList = "777,119";
       return setEccListProperty(eccList);
     })
     .then(() => testEmergencyLabel("777", eccList))
     .then(() => testEmergencyLabel("119", eccList))
     .then(() => testEmergencyLabel("112", eccList))
     .then(() => setEccListProperty(origEccList))
-    .then(null, error => {
-      ok(false, 'promise rejects during test: ' + error);
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_mmi.js
+++ b/dom/telephony/test/marionette/test_mmi.js
@@ -16,13 +16,11 @@ function testGettingIMEI() {
     is(aResult.statusMessage, "000000000000000", "Emulator IMEI");
     is(aResult.additionalInformation, undefined, "No additional information");
   });
 }
 
 // Start test
 startTest(function() {
   testGettingIMEI()
-    .then(null, cause => {
-      ok(false, 'promise rejects during test: ' + cause);
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_mmi_call_forwarding.js
+++ b/dom/telephony/test/marionette/test_mmi_call_forwarding.js
@@ -180,13 +180,11 @@ startTestWithPermissions(['mobileconnect
   for (let i = 0; i < TEST_DATA.length; i++) {
     let data = TEST_DATA[i];
     promise = promise.then(() => testSetCallForwarding(data))
                      .then(() => testGetCallForwarding(data));
   }
 
   // reset call forwarding settings.
   return promise.then(() => clearAllCallForwardingSettings())
-    .then(null, cause => {
-      ok(false, 'promise rejects during test: ' + cause);
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_mmi_change_pin.js
+++ b/dom/telephony/test/marionette/test_mmi_change_pin.js
@@ -103,13 +103,11 @@ startTest(function() {
     let data = TEST_DATA[i];
     promise = promise.then(() => testChangePin(data.pin,
                                                data.newPin,
                                                data.newPinAgain,
                                                data.expectedError));
   }
 
   return promise
-    .then(null, cause => {
-      ok(false, 'promise rejects during test: ' + cause);
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_outgoing_answer_radio_off.js
+++ b/dom/telephony/test/marionette/test_outgoing_answer_radio_off.js
@@ -93,13 +93,11 @@ function testOutgoingCallRadioOff() {
     .then(call => remoteAnswer(call))
     .then(call => disableRadioAndWaitForCallEvent(call))
     .then(() => setRadioEnabled(true));
 }
 
 // Start test
 startTestWithPermissions(['mobileconnection'], function() {
   testOutgoingCallRadioOff()
-    .then(null, () => {
-      ok(false, "promise rejects during test.");
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_outgoing_auto_hold.js
+++ b/dom/telephony/test/marionette/test_outgoing_auto_hold.js
@@ -40,13 +40,11 @@ function testAutoHoldConferenceCall() {
       is(conference.state, "held");
     })
     .then(() => gRemoteHangUpCalls([subCall1, subCall2, outCall]));
 }
 
 startTest(function() {
   testAutoHoldCall()
     .then(() => testAutoHoldConferenceCall())
-    .then(null, () => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_outgoing_emergency_in_airplane_mode.js
+++ b/dom/telephony/test/marionette/test_outgoing_emergency_in_airplane_mode.js
@@ -31,13 +31,11 @@ function setRadioEnabled(enabled) {
 startTestWithPermissions(['mobileconnection'], function() {
   let outCall;
   setRadioEnabled(false)
     .then(() => gDial("112"))
     .then(call => { outCall = call; })
     .then(() => gRemoteAnswer(outCall))
     .then(() => gDelay(1000))  // See Bug 1018051 for the purpose of the delay.
     .then(() => gRemoteHangUp(outCall))
-    .then(null, () => {
-      ok(false, "promise rejects during test.");
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_outgoing_radio_off.js
+++ b/dom/telephony/test/marionette/test_outgoing_radio_off.js
@@ -1,70 +1,42 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 MARIONETTE_TIMEOUT = 60000;
 MARIONETTE_HEAD_JS = 'head.js';
 
 let connection;
 
-function setRadioEnabled(enabled, callback) {
-  let request  = connection.setRadioEnabled(enabled);
+function setRadioEnabled(enabled) {
   let desiredRadioState = enabled ? 'enabled' : 'disabled';
-
-  let pending = ['onradiostatechange', 'onsuccess'];
-  let done = callback;
-
-  connection.onradiostatechange = function() {
-    let state = connection.radioState;
-    log("Received 'radiostatechange' event, radioState: " + state);
+  log("Set radio: " + desiredRadioState);
 
-    if (state == desiredRadioState) {
-      gReceivedPending('onradiostatechange', pending, done);
-    }
-  };
-
-  request.onsuccess = function onsuccess() {
-    gReceivedPending('onsuccess', pending, done);
-  };
-
-  request.onerror = function onerror() {
-    ok(false, "setRadioEnabled should be ok");
-  };
-}
+  let promises = [];
 
-function dial(number) {
-  // Verify initial state before dial.
-  ok(telephony);
-  is(telephony.active, null);
-  ok(telephony.calls);
-  is(telephony.calls.length, 0);
-
-  log("Make an outgoing call.");
-
-  telephony.dial(number).then(null, cause => {
-    log("Received promise 'reject'");
+  let promise = gWaitForEvent(connection, "radiostatechange", event => {
+    let state = connection.radioState;
+    log("current radioState: " + state);
+    return state == desiredRadioState;
+  });
+  promises.push(promise);
 
-    is(telephony.active, null);
-    is(telephony.calls.length, 0);
-    is(cause, "RadioNotAvailable");
-
-    emulator.runCmdWithCallback("gsm list", function(result) {
-      log("Initial call list: " + result);
+  promises.push(connection.setRadioEnabled(enabled));
 
-      setRadioEnabled(true, cleanUp);
-    });
-  });
-}
-
-function cleanUp() {
-  finish();
+  return Promise.all(promises);
 }
 
 startTestWithPermissions(['mobileconnection'], function() {
   connection = navigator.mozMobileConnections[0];
   ok(connection instanceof MozMobileConnection,
      "connection is instanceof " + connection.constructor);
 
-  setRadioEnabled(false, function() {
-    dial("0912345678");
-  });
+  setRadioEnabled(false)
+    .then(() => gDial("0912345678"))
+    .catch(cause => {
+      is(telephony.active, null);
+      is(telephony.calls.length, 0);
+      is(cause, "RadioNotAvailable");
+    })
+    .then(() => setRadioEnabled(true))
+    .catch(error => ok(false, "Promise reject: " + error))
+    .then(finish);
 });
--- a/dom/telephony/test/marionette/test_outgoing_when_two_calls_on_line.js
+++ b/dom/telephony/test/marionette/test_outgoing_when_two_calls_on_line.js
@@ -22,13 +22,11 @@ function testReject3rdCall() {
       log("Reject 3rd call, cuase: " + cause);
       is(cause, "InvalidStateError");
     })
     .then(() => gRemoteHangUpCalls([outCall1, outCall2]));
 }
 
 startTest(function() {
   testReject3rdCall()
-    .then(null, () => {
-      ok(false, 'promise rejects during test.');
-    })
+    .catch(error => ok(false, "Promise reject: " + error))
     .then(finish);
 });
--- a/dom/telephony/test/marionette/test_temporary_clir.js
+++ b/dom/telephony/test/marionette/test_temporary_clir.js
@@ -24,14 +24,11 @@ startTest(function() {
       log("Received 'onalerting' call event.");
       is(call.id.number, number);
       deferred.resolve(call);
     };
 
     return deferred.promise;
   })
   .then(() => gRemoteHangUp(outCall))
-  // End
-  .then(null, error => {
-    ok(false, 'promise rejects during test.');
-  })
+  .catch(error => ok(false, "Promise reject: " + error))
   .then(finish);
 });
--- a/hal/Hal.cpp
+++ b/hal/Hal.cpp
@@ -1067,29 +1067,27 @@ EnableFMRadio(const FMRadioSettings& aIn
 void
 DisableFMRadio() {
   AssertMainThread();
   PROXY_IF_SANDBOXED(DisableFMRadio());
 }
 
 void
 FMRadioSeek(const FMRadioSeekDirection& aDirection) {
-  AssertMainThread();
   PROXY_IF_SANDBOXED(FMRadioSeek(aDirection));
 }
 
 void
 GetFMRadioSettings(FMRadioSettings* aInfo) {
   AssertMainThread();
   PROXY_IF_SANDBOXED(GetFMRadioSettings(aInfo));
 }
 
 void
 SetFMRadioFrequency(const uint32_t aFrequency) {
-  AssertMainThread();
   PROXY_IF_SANDBOXED(SetFMRadioFrequency(aFrequency));
 }
 
 uint32_t
 GetFMRadioFrequency() {
   AssertMainThread();
   RETURN_PROXY_IF_SANDBOXED(GetFMRadioFrequency(), 0);
 }
--- a/mobile/android/base/EventDispatcher.java
+++ b/mobile/android/base/EventDispatcher.java
@@ -67,19 +67,17 @@ public final class EventDispatcher {
                         listenersMap.put(event, listeners);
                     }
                     if (!AppConstants.RELEASE_BUILD && listeners.contains(listener)) {
                         throw new IllegalStateException("Already registered " + event);
                     }
                     listeners.add(listener);
                 }
             }
-        } catch (final IllegalAccessException e) {
-            throw new IllegalArgumentException("Invalid new list type", e);
-        } catch (final InstantiationException e) {
+        } catch (final IllegalAccessException | InstantiationException e) {
             throw new IllegalArgumentException("Invalid new list type", e);
         }
     }
 
     private <T> void checkNotRegistered(final Map<String, List<T>> listenersMap,
                                         final String[] events) {
         synchronized (listenersMap) {
             for (final String event: events) {
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -382,23 +382,23 @@ public abstract class GeckoApp
     }
 
     @Override
     public View onCreatePanelView(int featureId) {
         if (Versions.feature11Plus && featureId == Window.FEATURE_OPTIONS_PANEL) {
             if (mMenuPanel == null) {
                 mMenuPanel = new MenuPanel(this, null);
             } else {
-                // Prepare the panel everytime before showing the menu.
+                // Prepare the panel every time before showing the menu.
                 onPreparePanel(featureId, mMenuPanel, mMenu);
             }
 
             return mMenuPanel; 
         }
-  
+
         return super.onCreatePanelView(featureId);
     }
 
     @Override
     public boolean onCreatePanelMenu(int featureId, Menu menu) {
         if (Versions.feature11Plus && featureId == Window.FEATURE_OPTIONS_PANEL) {
             if (mMenuPanel == null) {
                 mMenuPanel = (MenuPanel) onCreatePanelView(featureId);
--- a/mobile/android/base/GeckoAppShell.java
+++ b/mobile/android/base/GeckoAppShell.java
@@ -522,17 +522,17 @@ public class GeckoAppShell
     @WrapElementForJNI
     public static void acknowledgeEvent() {
         synchronized (sEventAckLock) {
             sWaitingForEventAck = false;
             sEventAckLock.notifyAll();
         }
     }
 
-    private static Runnable sCallbackRunnable = new Runnable() {
+    private static final Runnable sCallbackRunnable = new Runnable() {
         @Override
         public void run() {
             ThreadUtils.assertOnUiThread();
             long nextDelay = runUiThreadCallback();
             if (nextDelay >= 0) {
                 ThreadUtils.getUiHandler().postDelayed(this, nextDelay);
             }
         }
@@ -1846,17 +1846,17 @@ public class GeckoAppShell
      */
     public static final String PLUGIN_ACTION = "android.webkit.PLUGIN";
     public static final String PLUGIN_PERMISSION = "android.webkit.permission.PLUGIN";
 
     private static final String PLUGIN_SYSTEM_LIB = "/system/lib/plugins/";
 
     private static final String PLUGIN_TYPE = "type";
     private static final String TYPE_NATIVE = "native";
-    static public ArrayList<PackageInfo> mPackageInfoCache = new ArrayList<PackageInfo>();
+    public static final ArrayList<PackageInfo> mPackageInfoCache = new ArrayList<>();
 
     // Returns null if plugins are blocked on the device.
     static String[] getPluginDirectories() {
 
         // An awful hack to detect Tegra devices. Easiest way to do it without spinning up a EGL context.
         boolean isTegra = (new File("/system/lib/hw/gralloc.tegra.so")).exists() ||
                           (new File("/system/lib/hw/gralloc.tegra3.so")).exists();
         if (isTegra) {
@@ -2122,17 +2122,17 @@ public class GeckoAppShell
     public static void setGeckoInterface(GeckoInterface aGeckoInterface) {
         sGeckoInterface = aGeckoInterface;
     }
 
     public static android.hardware.Camera sCamera;
 
     static native void cameraCallbackBridge(byte[] data);
 
-    static int kPreferedFps = 25;
+    static final int kPreferredFPS = 25;
     static byte[] sCameraBuffer;
 
 
     @WrapElementForJNI(stubName = "InitCameraWrapper")
     static int[] initCamera(String aContentType, int aCamera, int aWidth, int aHeight) {
         ThreadUtils.postToUiThread(new Runnable() {
                 @Override
                 public void run() {
@@ -2161,23 +2161,23 @@ public class GeckoAppShell
             params.setPreviewFormat(ImageFormat.NV21);
 
             // use the preview fps closest to 25 fps.
             int fpsDelta = 1000;
             try {
                 Iterator<Integer> it = params.getSupportedPreviewFrameRates().iterator();
                 while (it.hasNext()) {
                     int nFps = it.next();
-                    if (Math.abs(nFps - kPreferedFps) < fpsDelta) {
-                        fpsDelta = Math.abs(nFps - kPreferedFps);
+                    if (Math.abs(nFps - kPreferredFPS) < fpsDelta) {
+                        fpsDelta = Math.abs(nFps - kPreferredFPS);
                         params.setPreviewFrameRate(nFps);
                     }
                 }
             } catch(Exception e) {
-                params.setPreviewFrameRate(kPreferedFps);
+                params.setPreviewFrameRate(kPreferredFPS);
             }
 
             // set up the closest preview size available
             Iterator<android.hardware.Camera.Size> sit = params.getSupportedPreviewSizes().iterator();
             int sizeDelta = 10000000;
             int bufferSize = 0;
             while (sit.hasNext()) {
                 android.hardware.Camera.Size size = sit.next();
@@ -2192,19 +2192,17 @@ public class GeckoAppShell
                 if (getGeckoInterface() != null) {
                     View cameraView = getGeckoInterface().getCameraView();
                     if (cameraView instanceof SurfaceView) {
                         sCamera.setPreviewDisplay(((SurfaceView)cameraView).getHolder());
                     } else if (cameraView instanceof TextureView) {
                         sCamera.setPreviewTexture(((TextureView)cameraView).getSurfaceTexture());
                     }
                 }
-            } catch(IOException e) {
-                Log.w(LOGTAG, "Error setPreviewXXX:", e);
-            } catch(RuntimeException e) {
+            } catch (IOException | RuntimeException e) {
                 Log.w(LOGTAG, "Error setPreviewXXX:", e);
             }
 
             sCamera.setParameters(params);
             sCameraBuffer = new byte[(bufferSize * 12) / 8];
             sCamera.addCallbackBuffer(sCameraBuffer);
             sCamera.setPreviewCallbackWithBuffer(new android.hardware.Camera.PreviewCallback() {
                 @Override
--- a/mobile/android/base/TelemetryContract.java
+++ b/mobile/android/base/TelemetryContract.java
@@ -74,16 +74,19 @@ public interface TelemetryContract {
         SEARCH_RESTORE_DEFAULTS("search.restoredefaults.1"),
 
         // Set default search engine.
         SEARCH_SET_DEFAULT("search.setdefault.1"),
 
         // Sharing content.
         SHARE("share.1"),
 
+        // Show a UI element.
+        SHOW("show.1"),
+
         // Undoing a user action.
         // Note: Only used in JavaScript for now, but here for completeness.
         UNDO("undo.1"),
 
         // Unpinning an item.
         UNPIN("unpin.1"),
 
         // Stop holding a resource (reader, bookmark, etc) for viewing later.
@@ -156,16 +159,19 @@ public interface TelemetryContract {
 
         // Action triggered from a pageaction in the URLBar.
         // Note: Only used in JavaScript for now, but here for completeness.
         PAGEACTION("pageaction"),
 
         // Action triggered from a settings screen.
         SETTINGS("settings"),
 
+        // Actions triggered from the share overlay.
+        SHARE_OVERLAY("shareoverlay"),
+
         // Action triggered from a suggestion provided to the user.
         SUGGESTION("suggestion"),
 
         // Action triggered from a SuperToast.
         // Note: Only used in JavaScript for now, but here for completeness.
         TOAST("toast"),
 
         // Action triggerred by pressing a SearchWidget button
--- a/mobile/android/base/background/BackgroundService.java
+++ b/mobile/android/base/background/BackgroundService.java
@@ -98,17 +98,13 @@ public abstract class BackgroundService 
       broadcastSnippetsPref.invoke(null, context);
       return;
     } catch (ClassNotFoundException e) {
       Logger.error(LOG_TAG, "Class " + className + " not found!");
       return;
     } catch (NoSuchMethodException e) {
       Logger.error(LOG_TAG, "Method " + className + "/" + methodName + " not found!");
       return;
-    } catch (IllegalArgumentException e) {
-      Logger.error(LOG_TAG, "Got exception invoking " + methodName + ".");
-    } catch (IllegalAccessException e) {
-      Logger.error(LOG_TAG, "Got exception invoking " + methodName + ".");
-    } catch (InvocationTargetException e) {
+    } catch (IllegalArgumentException | InvocationTargetException | IllegalAccessException e) {
       Logger.error(LOG_TAG, "Got exception invoking " + methodName + ".");
     }
   }
 }
--- a/mobile/android/base/background/healthreport/EnvironmentV1.java
+++ b/mobile/android/base/background/healthreport/EnvironmentV1.java
@@ -118,23 +118,21 @@ public abstract class EnvironmentV1 {
    * @return the integer ID to use in subsequent DB insertions.
    */
   public abstract int register();
 
   protected EnvironmentAppender getAppender() {
     EnvironmentAppender appender = null;
     try {
       appender = appenderClass.newInstance();
-    } catch (InstantiationException ex) {
-      // Should never happen, but...
-      Logger.warn(LOG_TAG,  "Could not compute hash.", ex);
-    } catch (IllegalAccessException ex) {
+    } catch (InstantiationException | IllegalAccessException ex) {
       // Should never happen, but...
       Logger.warn(LOG_TAG,  "Could not compute hash.", ex);
     }
+
     return appender;
   }
 
   protected void appendHash(EnvironmentAppender appender) {
     appender.append(profileCreation);
     appender.append(cpuCount);
     appender.append(memoryMB);
     appender.append(architecture);
--- a/mobile/android/base/browserid/DSACryptoImplementation.java
+++ b/mobile/android/base/browserid/DSACryptoImplementation.java
@@ -196,37 +196,33 @@ public class DSACryptoImplementation {
       throw new InvalidKeySpecException("algorithm must equal DS, was " + algorithm);
     }
     try {
       BigInteger x = new BigInteger(o.getString("x"), SERIALIZATION_BASE);
       BigInteger p = new BigInteger(o.getString("p"), SERIALIZATION_BASE);
       BigInteger q = new BigInteger(o.getString("q"), SERIALIZATION_BASE);
       BigInteger g = new BigInteger(o.getString("g"), SERIALIZATION_BASE);
       return createPrivateKey(x, p, q, g);
-    } catch (NullPointerException e) {
-      throw new InvalidKeySpecException("x, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
-    } catch (NumberFormatException e) {
+    } catch (NullPointerException | NumberFormatException e) {
       throw new InvalidKeySpecException("x, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
     }
   }
 
   public static VerifyingPublicKey createPublicKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
     String algorithm = o.getString("algorithm");
     if (!"DS".equals(algorithm)) {
       throw new InvalidKeySpecException("algorithm must equal DS, was " + algorithm);
     }
     try {
       BigInteger y = new BigInteger(o.getString("y"), SERIALIZATION_BASE);
       BigInteger p = new BigInteger(o.getString("p"), SERIALIZATION_BASE);
       BigInteger q = new BigInteger(o.getString("q"), SERIALIZATION_BASE);
       BigInteger g = new BigInteger(o.getString("g"), SERIALIZATION_BASE);
       return createPublicKey(y, p, q, g);
-    } catch (NullPointerException e) {
-      throw new InvalidKeySpecException("y, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
-    } catch (NumberFormatException e) {
+    } catch (NullPointerException | NumberFormatException e) {
       throw new InvalidKeySpecException("y, p, q, and g must be integers encoded as strings, base " + SERIALIZATION_BASE);
     }
   }
 
   public static BrowserIDKeyPair fromJSONObject(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
     try {
       ExtendedJSONObject privateKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PRIVATEKEY);
       ExtendedJSONObject publicKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PUBLICKEY);
--- a/mobile/android/base/browserid/RSACryptoImplementation.java
+++ b/mobile/android/base/browserid/RSACryptoImplementation.java
@@ -140,35 +140,31 @@ public class RSACryptoImplementation {
     String algorithm = o.getString("algorithm");
     if (!"RS".equals(algorithm)) {
       throw new InvalidKeySpecException("algorithm must equal RS, was " + algorithm);
     }
     try {
       BigInteger n = new BigInteger(o.getString("n"), SERIALIZATION_BASE);
       BigInteger d = new BigInteger(o.getString("d"), SERIALIZATION_BASE);
       return createPrivateKey(n, d);
-    } catch (NullPointerException e) {
-      throw new InvalidKeySpecException("n and d must be integers encoded as strings, base " + SERIALIZATION_BASE);
-    } catch (NumberFormatException e) {
+    } catch (NullPointerException | NumberFormatException e) {
       throw new InvalidKeySpecException("n and d must be integers encoded as strings, base " + SERIALIZATION_BASE);
     }
   }
 
   public static VerifyingPublicKey createPublicKey(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
     String algorithm = o.getString("algorithm");
     if (!"RS".equals(algorithm)) {
       throw new InvalidKeySpecException("algorithm must equal RS, was " + algorithm);
     }
     try {
       BigInteger n = new BigInteger(o.getString("n"), SERIALIZATION_BASE);
       BigInteger e = new BigInteger(o.getString("e"), SERIALIZATION_BASE);
       return createPublicKey(n, e);
-    } catch (NullPointerException e) {
-      throw new InvalidKeySpecException("n and e must be integers encoded as strings, base " + SERIALIZATION_BASE);
-    } catch (NumberFormatException e) {
+    } catch (NullPointerException | NumberFormatException e) {
       throw new InvalidKeySpecException("n and e must be integers encoded as strings, base " + SERIALIZATION_BASE);
     }
   }
 
   public static BrowserIDKeyPair fromJSONObject(ExtendedJSONObject o) throws InvalidKeySpecException, NoSuchAlgorithmException {
     try {
       ExtendedJSONObject privateKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PRIVATEKEY);
       ExtendedJSONObject publicKey = o.getObject(BrowserIDKeyPair.JSON_KEY_PUBLICKEY);
--- a/mobile/android/base/db/LocalBrowserDB.java
+++ b/mobile/android/base/db/LocalBrowserDB.java
@@ -412,18 +412,22 @@ public class LocalBrowserDB {
         try {
             Class<?> drawablesClass = R.raw.class;
 
             // Look for a favicon with the id R.raw.bookmarkdefaults_favicon_*.
             Field faviconField = drawablesClass.getField(name.replace("_title_", "_favicon_"));
             faviconField.setAccessible(true);
 
             return faviconField.getInt(null);
-        } catch (IllegalAccessException | NoSuchFieldException  ex) {
-            Log.wtf(LOGTAG, "Reflection error fetching favicon: " + name, ex);
+        } catch (IllegalAccessException | NoSuchFieldException e) {
+            // We'll end up here for any default bookmark that doesn't have a favicon in
+            // resources/raw/ (i.e., about:firefox). When this happens, the Favicons service will
+            // fall back to the default branding icon for about pages. Non-about pages should always
+            // specify an icon; otherwise, the placeholder globe favicon will be used.
+            Log.d(LOGTAG, "No raw favicon resource found for " + name);
         }
 
         Log.e(LOGTAG, "Failed to find favicon resource ID for " + name);
         return FAVICON_ID_NOT_FOUND;
     }
 
     /**
      * Load a favicon from the omnijar.
--- a/mobile/android/base/menu/GeckoMenuInflater.java
+++ b/mobile/android/base/menu/GeckoMenuInflater.java
@@ -56,19 +56,17 @@ public class GeckoMenuInflater extends M
 
         XmlResourceParser parser = null;
         try {
             parser = mContext.getResources().getXml(menuRes);
             AttributeSet attrs = Xml.asAttributeSet(parser);
 
             parseMenu(parser, attrs, menu);
 
-        } catch (XmlPullParserException e) {
-            throw new InflateException("Error inflating menu XML", e);
-        } catch (IOException e) {
+        } catch (XmlPullParserException | IOException e) {
             throw new InflateException("Error inflating menu XML", e);
         } finally {
             if (parser != null)
                 parser.close();
         }
     }
 
     private void parseMenu(XmlResourceParser parser, AttributeSet attrs, Menu menu)
--- a/mobile/android/base/overlays/ui/SendTabList.java
+++ b/mobile/android/base/overlays/ui/SendTabList.java
@@ -7,16 +7,18 @@ package org.mozilla.gecko.overlays.ui;
 import static org.mozilla.gecko.overlays.ui.SendTabList.State.LIST;
 import static org.mozilla.gecko.overlays.ui.SendTabList.State.LOADING;
 import static org.mozilla.gecko.overlays.ui.SendTabList.State.SHOW_DEVICES;
 
 import java.util.Arrays;
 
 import org.mozilla.gecko.Assert;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.overlays.service.sharemethods.ParcelableClientRecord;
 
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
 import android.widget.ListAdapter;
@@ -138,16 +140,22 @@ public class SendTabList extends ListVie
         }
 
         builder.setTitle(R.string.overlay_share_select_device)
                .setItems(dialogElements, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int index) {
                        listener.onSendTabTargetSelected(records[index].guid);
                    }
+                })
+               .setOnCancelListener(new DialogInterface.OnCancelListener() {
+                   @Override
+                   public void onCancel(DialogInterface dialogInterface) {
+                       Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.SHARE_OVERLAY, "device_selection_cancel");
+                   }
                });
 
         return builder.create();
     }
 
     /**
      * Prevent scrolling of this ListView.
      */
--- a/mobile/android/base/overlays/ui/ShareDialog.java
+++ b/mobile/android/base/overlays/ui/ShareDialog.java
@@ -7,16 +7,18 @@ package org.mozilla.gecko.overlays.ui;
 
 import java.net.URISyntaxException;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.Assert;
 import org.mozilla.gecko.GeckoProfile;
 import org.mozilla.gecko.LocaleAware;
 import org.mozilla.gecko.R;
+import org.mozilla.gecko.Telemetry;
+import org.mozilla.gecko.TelemetryContract;
 import org.mozilla.gecko.db.LocalBrowserDB;
 import org.mozilla.gecko.overlays.OverlayConstants;
 import org.mozilla.gecko.overlays.service.OverlayActionService;
 import org.mozilla.gecko.overlays.service.sharemethods.ParcelableClientRecord;
 import org.mozilla.gecko.overlays.service.sharemethods.SendTab;
 import org.mozilla.gecko.overlays.service.sharemethods.ShareMethod;
 import org.mozilla.gecko.sync.setup.activities.WebURLFinder;
 import org.mozilla.gecko.mozglue.ContextUtils;
@@ -142,22 +144,27 @@ public class ShareDialog extends LocaleA
         // Have the service start any initialisation work that's necessary for us to show the correct
         // UI. The results of such work will come in via the BroadcastListener.
         Intent serviceStartupIntent = new Intent(this, OverlayActionService.class);
         serviceStartupIntent.setAction(OverlayConstants.ACTION_PREPARE_SHARE);
         startService(serviceStartupIntent);
 
         // If provided, we use the subject text to give us something nice to display.
         // If not, we wing it with the URL.
+
         // TODO: Consider polling Fennec databases to find better information to display.
         String subjectText = intent.getStringExtra(Intent.EXTRA_SUBJECT);
+
+        String telemetryExtras = "title=" + (subjectText != null);
         if (subjectText != null) {
             ((TextView) findViewById(R.id.title)).setText(subjectText);
         }
 
+        Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.SHARE_OVERLAY, telemetryExtras);
+
         title = subjectText;
         url = pageUrl;
 
         // Set the subtitle text on the view and cause it to marquee if it's too long (which it will
         // be, since it's a URL).
         TextView subtitleView = (TextView) findViewById(R.id.subtitle);
         subtitleView.setText(pageUrl);
         subtitleView.setEllipsize(TextUtils.TruncateAt.MARQUEE);
@@ -316,26 +323,32 @@ public class ShareDialog extends LocaleA
 
         // Future: Handle multiple-selection. Bug 1061297.
         extraParameters.putStringArray(SendTab.SEND_TAB_TARGET_DEVICES, new String[] { targetGUID });
 
         serviceIntent.putExtra(OverlayConstants.EXTRA_PARAMETERS, extraParameters);
 
         startService(serviceIntent);
         slideOut();
+
+        Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.SHARE_OVERLAY, "sendtab");
     }
 
     public void addToReadingList() {
         startService(getServiceIntent(ShareMethod.Type.ADD_TO_READING_LIST));
         slideOut();
+
+        Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.SHARE_OVERLAY, "reading_list");
     }
 
     public void addBookmark() {
         startService(getServiceIntent(ShareMethod.Type.ADD_BOOKMARK));
         slideOut();
+
+        Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.SHARE_OVERLAY, "bookmark");
     }
 
     public void launchBrowser() {
         try {
             // This can launch in the guest profile. Sorry.
             final Intent i = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
             i.setClassName(AppConstants.ANDROID_PACKAGE_NAME, AppConstants.BROWSER_INTENT_CLASS_NAME);
             startActivity(i);
@@ -381,19 +394,21 @@ public class ShareDialog extends LocaleA
     }
 
     /**
      * Close the dialog if back is pressed.
      */
     @Override
     public void onBackPressed() {
         slideOut();
+        Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.SHARE_OVERLAY);
     }
 
     /**
      * Close the dialog if the anything that isn't a button is tapped.
      */
     @Override
     public boolean onTouchEvent(MotionEvent event) {
         slideOut();
+        Telemetry.sendUIEvent(TelemetryContract.Event.CANCEL, TelemetryContract.Method.SHARE_OVERLAY);
         return true;
     }
 }
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -389,17 +389,16 @@
   <string name="filepicker_audio_title">&filepicker_audio_title;</string>
   <string name="filepicker_image_title">&filepicker_image_title;</string>
   <string name="filepicker_video_title">&filepicker_video_title;</string>
 
   <!-- Default bookmarks. We used to use bookmark titles shared with XUL from mobile's
        profile/bookmarks.inc (see bug 964946). Don't expose the URLs to L10N. -->
   <string name="bookmarkdefaults_title_aboutfirefox">@bookmarks_aboutBrowser@</string>
   <string name="bookmarkdefaults_url_aboutfirefox">about:firefox</string>
-  <string name="bookmarkdefaults_favicon_aboutfirefox">chrome/chrome/content/branding/favicon64.png</string>
 
   <!-- Icon is automatically generated from R.drawable.bookmarkdefaults_favicon_addons -->
   <string name="bookmarkdefaults_title_addons">@bookmarks_addons@</string>
   <string name="bookmarkdefaults_url_addons">https://addons.mozilla.org/android/</string>
 
   <!-- Icon is automatically generated from R.drawable.bookmarkdefaults_favicon_support -->
   <string name="bookmarkdefaults_title_support">@bookmarks_support@</string>
   <string name="bookmarkdefaults_url_support">https://support.mozilla.org/products/mobile</string>
--- a/mobile/android/base/sync/crypto/CryptoInfo.java
+++ b/mobile/android/base/sync/crypto/CryptoInfo.java
@@ -133,22 +133,20 @@ public class CryptoInfo {
    * @return encrypted/decrypted message
    * @throws CryptoException
    */
   private static byte[] commonCrypto(Cipher cipher, byte[] inputMessage)
                         throws CryptoException {
     byte[] outputMessage = null;
     try {
       outputMessage = cipher.doFinal(inputMessage);
-    } catch (IllegalBlockSizeException e) {
-      throw new CryptoException(e);
-    } catch (BadPaddingException e) {
+    } catch (IllegalBlockSizeException | BadPaddingException e) {
       throw new CryptoException(e);
     }
-    return outputMessage;
+      return outputMessage;
   }
 
   /**
    * Encrypt a CryptoInfo in-place.
    *
    * @throws CryptoException
    */
   public void encrypt() throws CryptoException {
@@ -171,19 +169,17 @@ public class CryptoInfo {
     // Encrypt.
     byte[] encryptedBytes = commonCrypto(cipher, getMessage());
     byte[] iv = cipher.getIV();
 
     byte[] hmac;
     // Generate HMAC.
     try {
       hmac = generatedHMACFor(encryptedBytes, keys);
-    } catch (NoSuchAlgorithmException e) {
-      throw new CryptoException(e);
-    } catch (InvalidKeyException e) {
+    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
       throw new CryptoException(e);
     }
 
     // Update in place.  keys is already set.
     this.setHMAC(hmac);
     this.setIV(iv);
     this.setMessage(encryptedBytes);
   }
@@ -195,19 +191,17 @@ public class CryptoInfo {
    */
   public void decrypt() throws CryptoException {
 
     // Check HMAC.
     try {
       if (!generatedHMACIsHMAC()) {
         throw new HMACVerificationException();
       }
-    } catch (NoSuchAlgorithmException e) {
-      throw new CryptoException(e);
-    } catch (InvalidKeyException e) {
+    } catch (NoSuchAlgorithmException | InvalidKeyException e) {
       throw new CryptoException(e);
     }
 
     Cipher cipher = CryptoInfo.getCipher(TRANSFORMATION);
     try {
       byte[] encryptionKey = getKeys().getEncryptionKey();
       SecretKeySpec spec = new SecretKeySpec(encryptionKey, KEY_ALGORITHM_SPEC);
       cipher.init(Cipher.DECRYPT_MODE, spec, new IvParameterSpec(getIV()));
@@ -226,15 +220,13 @@ public class CryptoInfo {
   /**
    * Helper to get a Cipher object.
    *
    * @param transformation The type of Cipher to get.
    */
   private static Cipher getCipher(String transformation) throws CryptoException {
     try {
       return Cipher.getInstance(transformation);
-    } catch (NoSuchAlgorithmException e) {
-      throw new CryptoException(e);
-    } catch (NoSuchPaddingException e) {
+    } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
       throw new CryptoException(e);
     }
   }
 }
--- a/mobile/android/base/sync/crypto/KeyBundle.java
+++ b/mobile/android/base/sync/crypto/KeyBundle.java
@@ -39,31 +39,27 @@ public class KeyBundle {
         throw new IllegalArgumentException("No sync key provided.");
       }
       if (username == null || username.equals("")) {
         throw new IllegalArgumentException("No username provided.");
       }
       // Hash appropriately.
       try {
         username = Utils.usernameFromAccount(username);
-      } catch (NoSuchAlgorithmException e) {
-        throw new IllegalArgumentException("Invalid username.");
-      } catch (UnsupportedEncodingException e) {
+      } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
         throw new IllegalArgumentException("Invalid username.");
       }
 
       byte[] syncKey = Utils.decodeFriendlyBase32(base32SyncKey);
       byte[] user    = username.getBytes();
 
       Mac hmacHasher;
       try {
         hmacHasher = HKDF.makeHMACHasher(syncKey);
-      } catch (NoSuchAlgorithmException e) {
-        throw new CryptoException(e);
-      } catch (InvalidKeyException e) {
+      } catch (NoSuchAlgorithmException | InvalidKeyException e) {
         throw new CryptoException(e);
       }
       assert(hmacHasher != null); // If makeHMACHasher doesn't throw, then hmacHasher is non-null.
 
       byte[] encrBytes = Utils.concatAll(EMPTY_BYTES, HKDF.HMAC_INPUT, user, ENCR_INPUT_BYTES);
       byte[] encrKey   = HKDF.digestBytes(encrBytes, hmacHasher);
       byte[] hmacBytes = Utils.concatAll(encrKey, HKDF.HMAC_INPUT, user, HMAC_INPUT_BYTES);
 
--- a/mobile/android/base/sync/jpake/JPakeClient.java
+++ b/mobile/android/base/sync/jpake/JPakeClient.java
@@ -64,30 +64,30 @@ public class JPakeClient {
   public String theirSignerId;
   public String jpakeServer;
 
   // J-PAKE state.
   public boolean paired;
   public boolean finished;
 
   // J-PAKE values.
-  public int jpakePollInterval;
+  public final int jpakePollInterval;
   public int jpakeMaxTries;
   public String channel;
   public volatile String channelUrl;
 
   // J-PAKE session data.
   public KeyBundle myKeyBundle;
   public JSONObject jCreds;
 
   public ExtendedJSONObject jOutgoing;
   public ExtendedJSONObject jIncoming;
 
   public JPakeParty jParty;
-  public JPakeNumGenerator numGen;
+  public final JPakeNumGenerator numGen;
 
   public int pollTries;
 
   // UI controller.
   private final SetupSyncActivity controllerActivity;
   private Queue<JPakeStage> stages;
 
   public JPakeClient(SetupSyncActivity activity) {
@@ -415,21 +415,17 @@ public class JPakeClient {
    *
    * @param payload Credentials data to be encrypted.
    */
   private void encryptData(KeyBundle keyBundle, String payload) {
     Logger.debug(LOG_TAG, "Encrypting data.");
     ExtendedJSONObject jPayload = null;
     try {
       jPayload = encryptPayload(payload, keyBundle, true);
-    } catch (UnsupportedEncodingException e) {
-      Logger.error(LOG_TAG, "Failed to encrypt data.", e);
-      abort(Constants.JPAKE_ERROR_INTERNAL);
-      return;
-    } catch (CryptoException e) {
+    } catch (UnsupportedEncodingException | CryptoException e) {
       Logger.error(LOG_TAG, "Failed to encrypt data.", e);
       abort(Constants.JPAKE_ERROR_INTERNAL);
       return;
     }
     jOutgoing = new ExtendedJSONObject();
     jOutgoing.put(Constants.JSON_KEY_TYPE, mySignerId + "3");
     jOutgoing.put(Constants.JSON_KEY_VERSION, KEYEXCHANGE_VERSION);
     jOutgoing.put(Constants.JSON_KEY_PAYLOAD, jPayload.object);
--- a/mobile/android/base/sync/jpake/stage/DecryptDataStage.java
+++ b/mobile/android/base/sync/jpake/stage/DecryptDataStage.java
@@ -39,37 +39,33 @@ public class DecryptDataStage extends JP
     } catch (NonObjectJSONException e1) {
       Logger.error(LOG_TAG, "Invalid round 3 data.", e1);
       jClient.abort(Constants.JPAKE_ERROR_WRONGMESSAGE);
       return;
     }
     Logger.debug(LOG_TAG, "Decrypting data.");
     String cleartext = null;
     try {
-      cleartext = new String(decryptPayload(iPayload, jClient.myKeyBundle), "UTF-8");
+      cleartext = new String(decryptPayload(iPayload, jClient.myKeyBundle), "UTF-8");
     } catch (UnsupportedEncodingException e) {
       Logger.error(LOG_TAG, "Failed to decrypt data.", e);
       jClient.abort(Constants.JPAKE_ERROR_INTERNAL);
       return;
     } catch (CryptoException e) {
       Logger.error(LOG_TAG, "Failed to decrypt data.", e);
       jClient.abort(Constants.JPAKE_ERROR_KEYMISMATCH);
       return;
     }
     try {
       jClient.jCreds = ExtendedJSONObject.parseJSONObject(cleartext).object;
     } catch (IOException e) {
       Logger.error(LOG_TAG, "I/O exception while creating JSON object.", e);
       jClient.abort(Constants.JPAKE_ERROR_INVALID);
       return;
-    } catch (ParseException e) {
-      Logger.error(LOG_TAG, "JSON parse error.", e);
-      jClient.abort(Constants.JPAKE_ERROR_INVALID);
-      return;
-    } catch (NonObjectJSONException e) {
+    } catch (ParseException | NonObjectJSONException e) {
       Logger.error(LOG_TAG, "JSON parse error.", e);
       jClient.abort(Constants.JPAKE_ERROR_INVALID);
       return;
     }
 
     // Check that credentials were actually sent over.
     if (!checkCredentials(jClient.jCreds)) {
       Logger.error(LOG_TAG, "Credentials contain nulls, setup cannot be completed.");
--- a/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java
+++ b/mobile/android/base/sync/middleware/Crypto5MiddlewareRepositorySession.java
@@ -157,19 +157,16 @@ public class Crypto5MiddlewareRepository
   public void store(Record record) throws NoStoreDelegateException {
     if (delegate == null) {
       throw new NoStoreDelegateException();
     }
     CryptoRecord rec = record.getEnvelope();
     rec.keyBundle = this.keyBundle;
     try {
       rec.encrypt();
-    } catch (UnsupportedEncodingException e) {
-      delegate.onRecordStoreFailed(e, record.guid);
-      return;
-    } catch (CryptoException e) {
+    } catch (UnsupportedEncodingException | CryptoException e) {
       delegate.onRecordStoreFailed(e, record.guid);
       return;
     }
     // Allow the inner session to do delegate handling.
     inner.store(rec);
   }
 }
--- a/mobile/android/base/sync/net/BaseResource.java
+++ b/mobile/android/base/sync/net/BaseResource.java
@@ -60,26 +60,26 @@ import ch.boye.httpclientandroidlib.util
 public class BaseResource implements Resource {
   private static final String ANDROID_LOOPBACK_IP = "10.0.2.2";
 
   private static final int MAX_TOTAL_CONNECTIONS     = 20;
   private static final int MAX_CONNECTIONS_PER_ROUTE = 10;
 
   private boolean retryOnFailedRequest = true;
 
-  public static boolean rewriteLocalhost = true;
+  public static final boolean rewriteLocalhost = true;
 
   private static final String LOG_TAG = "BaseResource";
 
   protected final URI uri;
   protected BasicHttpContext context;
   protected DefaultHttpClient client;
   public    ResourceDelegate delegate;
   protected HttpRequestBase request;
-  public String charset = "utf-8";
+  public final String charset = "utf-8";
 
   protected static WeakReference<HttpResponseObserver> httpResponseObserver = null;
 
   public BaseResource(String uri) throws URISyntaxException {
     this(uri, rewriteLocalhost);
   }
 
   public BaseResource(URI uri) {
@@ -293,20 +293,16 @@ public class BaseResource implements Res
     }
     this.request = request;
     try {
       this.prepareClient();
     } catch (KeyManagementException e) {
       Logger.error(LOG_TAG, "Couldn't prepare client.", e);
       delegate.handleTransportException(e);
       return;
-    } catch (NoSuchAlgorithmException e) {
-      Logger.error(LOG_TAG, "Couldn't prepare client.", e);
-      delegate.handleTransportException(e);
-      return;
     } catch (GeneralSecurityException e) {
       Logger.error(LOG_TAG, "Couldn't prepare client.", e);
       delegate.handleTransportException(e);
       return;
     } catch (Exception e) {
       // Bug 740731: Don't let an exception fall through. Wrapping isn't
       // optimal, but often the exception is treated as an Exception anyway.
       delegate.handleTransportException(new GeneralSecurityException(e));
--- a/mobile/android/base/sync/net/HMACAuthHeaderProvider.java
+++ b/mobile/android/base/sync/net/HMACAuthHeaderProvider.java
@@ -66,23 +66,19 @@ public class HMACAuthHeaderProvider impl
   @Override
   public Header getAuthHeader(HttpRequestBase request, BasicHttpContext context, DefaultHttpClient client) throws GeneralSecurityException {
     long timestamp = System.currentTimeMillis() / 1000;
     String nonce = Base64.encodeBase64String(Utils.generateRandomBytes(NONCE_LENGTH_IN_BYTES));
     String extra = "";
 
     try {
       return getAuthHeader(request, context, client, timestamp, nonce, extra);
-    } catch (InvalidKeyException e) {
+    } catch (InvalidKeyException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
       // We lie a little and make every exception a GeneralSecurityException.
       throw new GeneralSecurityException(e);
-    } catch (UnsupportedEncodingException e) {
-      throw new GeneralSecurityException(e);
-    } catch (NoSuchAlgorithmException e) {
-      throw new GeneralSecurityException(e);
     }
   }
 
   /**
    * Test if input is a <code>plain-string</code>.
    * <p>
    * A plain-string is defined by the MAC Authentication spec as
    * <code>plain-string   = 1*( %x20-21 / %x23-5B / %x5D-7E )</code>.
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserBookmarksRepositorySession.java
@@ -107,17 +107,17 @@ public class AndroidBrowserBookmarksRepo
   private final AndroidBrowserBookmarksDataAccessor dataAccessor;
 
   protected BookmarksDeletionManager deletionManager;
   protected BookmarksInsertionManager insertionManager;
 
   /**
    * An array of known-special GUIDs.
    */
-  public static String[] SPECIAL_GUIDS = new String[] {
+  public static final String[] SPECIAL_GUIDS = new String[] {
     // Mobile and desktop places roots have to come first.
     "places",
     "mobile",
     "toolbar",
     "menu",
     "unfiled"
   };
 
@@ -564,19 +564,16 @@ public class AndroidBrowserBookmarksRepo
       Logger.debug(LOG_TAG, "Check and build special GUIDs.");
       dataAccessor.checkAndBuildSpecialGuids();
       cur = dataAccessor.getGuidsIDsForFolders();
       Logger.debug(LOG_TAG, "Got GUIDs for folders.");
     } catch (android.database.sqlite.SQLiteConstraintException e) {
       Logger.error(LOG_TAG, "Got sqlite constraint exception working with Fennec bookmark DB.", e);
       delegate.onBeginFailed(e);
       return;
-    } catch (NullCursorException e) {
-      delegate.onBeginFailed(e);
-      return;
     } catch (Exception e) {
       delegate.onBeginFailed(e);
       return;
     }
 
     // To deal with parent mapping of bookmarks we have to do some
     // hairy stuff. Here's the setup for it.
 
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserHistoryRepositorySession.java
@@ -29,17 +29,17 @@ public class AndroidBrowserHistoryReposi
 
   public static final String KEY_DATE = "date";
   public static final String KEY_TYPE = "type";
   public static final long DEFAULT_VISIT_TYPE = 1;
 
   /**
    * The number of records to queue for insertion before writing to databases.
    */
-  public static int INSERT_RECORD_THRESHOLD = 50;
+  public static final int INSERT_RECORD_THRESHOLD = 50;
 
   public AndroidBrowserHistoryRepositorySession(Repository repository, Context context) {
     super(repository);
     dbHelper = new AndroidBrowserHistoryDataAccessor(context);
   }
 
   @Override
   public void begin(RepositorySessionBeginDelegate delegate) throws InvalidSessionTransitionException {
@@ -149,17 +149,17 @@ public class AndroidBrowserHistoryReposi
   public void finish(final RepositorySessionFinishDelegate delegate) throws InactiveSessionException {
     if (dbHelper != null) {
       ((AndroidBrowserHistoryDataAccessor) dbHelper).closeExtender();
       dbHelper = null;
     }
     super.finish(delegate);
   }
 
-  protected Object recordsBufferMonitor = new Object();
+  protected final Object recordsBufferMonitor = new Object();
   protected ArrayList<HistoryRecord> recordsBuffer = new ArrayList<HistoryRecord>();
 
   /**
    * Queue record for insertion, possibly flushing the queue.
    * <p>
    * Must be called on <code>storeWorkQueue</code> thread! But this is only
    * called from <code>store</code>, which is called on the queue thread.
    *
@@ -218,20 +218,17 @@ public class AndroidBrowserHistoryReposi
       return;
     }
 
     // All good, everybody succeeded.
     for (HistoryRecord succeeded : outgoing) {
       try {
         // Does not use androidID -- just GUID -> String map.
         updateBookkeeping(succeeded);
-      } catch (NoGuidForIdException e) {
-        // Should not happen.
-        throw new NullCursorException(e);
-      } catch (ParentNotFoundException e) {
+      } catch (NoGuidForIdException | ParentNotFoundException e) {
         // Should not happen.
         throw new NullCursorException(e);
       } catch (NullCursorException e) {
         throw e;
       }
       trackRecord(succeeded);
       delegate.onRecordStoreSucceeded(succeeded.guid); // At this point, we are really inserted.
     }
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserRepository.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserRepository.java
@@ -37,19 +37,16 @@ public abstract class AndroidBrowserRepo
       this.delegate = delegate;
       this.context = context;
     }
 
     @Override
     public void run() {
       try {
         getDataAccessor(context).purgeDeleted();
-      } catch (NullCursorException e) {
-        delegate.onCleanFailed(AndroidBrowserRepository.this, e);
-        return;
       } catch (Exception e) {
         delegate.onCleanFailed(AndroidBrowserRepository.this, e);
         return;
       }
       delegate.onCleaned(AndroidBrowserRepository.this);
     }
   }
 
--- a/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java
+++ b/mobile/android/base/sync/repositories/android/AndroidBrowserRepositorySession.java
@@ -150,19 +150,16 @@ public abstract class AndroidBrowserRepo
       // We do this check here even though it results in one extra call to the DB
       // because if we didn't, we have to do a check on every other call since there
       // is no way of knowing which call would be hit first.
       checkDatabase();
     } catch (ProfileDatabaseException e) {
       Logger.error(LOG_TAG, "ProfileDatabaseException from begin. Fennec must be launched once until this error is fixed");
       deferredDelegate.onBeginFailed(e);
       return;
-    } catch (NullCursorException e) {
-      deferredDelegate.onBeginFailed(e);
-      return;
     } catch (Exception e) {
       deferredDelegate.onBeginFailed(e);
       return;
     }
     storeTracker = createStoreTracker();
     deferredDelegate.onBeginSucceeded(this);
   }
 
@@ -214,19 +211,16 @@ public abstract class AndroidBrowserRepo
       if (!isActive()) {
         delegate.onGuidsSinceFailed(new InactiveSessionException(null));
         return;
       }
 
       Cursor cur;
       try {
         cur = dbHelper.getGUIDsSince(timestamp);
-      } catch (NullCursorException e) {
-        delegate.onGuidsSinceFailed(e);
-        return;
       } catch (Exception e) {
         delegate.onGuidsSinceFailed(e);
         return;
       }
 
       ArrayList<String> guids;
       try {
         if (!cur.moveToFirst()) {
@@ -252,17 +246,17 @@ public abstract class AndroidBrowserRepo
   @Override
   public void fetch(String[] guids,
                     RepositorySessionFetchRecordsDelegate delegate) throws InactiveSessionException {
     FetchRunnable command = new FetchRunnable(guids, now(), null, delegate);
     executeDelegateCommand(command);
   }
 
   abstract class FetchingRunnable implements Runnable {
-    protected RepositorySessionFetchRecordsDelegate delegate;
+    protected final RepositorySessionFetchRecordsDelegate delegate;
 
     public FetchingRunnable(RepositorySessionFetchRecordsDelegate delegate) {
       this.delegate = delegate;
     }
 
     protected void fetchFromCursor(Cursor cursor, RecordFilter filter, long end) {
       Logger.debug(LOG_TAG, "Fetch from cursor:");
       try {
@@ -533,20 +527,16 @@ public abstract class AndroidBrowserRepo
         } catch (MultipleRecordsForGuidException e) {
           Logger.error(LOG_TAG, "Multiple records returned for given guid: " + record.guid);
           delegate.onRecordStoreFailed(e, record.guid);
           return;
         } catch (NoGuidForIdException e) {
           Logger.error(LOG_TAG, "Store failed for " + record.guid, e);
           delegate.onRecordStoreFailed(e, record.guid);
           return;
-        } catch (NullCursorException e) {
-          Logger.error(LOG_TAG, "Store failed for " + record.guid, e);
-          delegate.onRecordStoreFailed(e, record.guid);
-          return;
         } catch (Exception e) {
           Logger.error(LOG_TAG, "Store failed for " + record.guid, e);
           delegate.onRecordStoreFailed(e, record.guid);
           return;
         }
       }
     };
     storeWorkQueue.execute(command);
--- a/mobile/android/base/sync/repositories/android/FormHistoryRepositorySession.java
+++ b/mobile/android/base/sync/repositories/android/FormHistoryRepositorySession.java
@@ -32,17 +32,17 @@ import android.content.ContentProviderCl
 import android.content.ContentValues;
 import android.content.Context;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.RemoteException;
 
 public class FormHistoryRepositorySession extends
     StoreTrackingRepositorySession {
-  public static String LOG_TAG = "FormHistoryRepoSess";
+  public static final String LOG_TAG = "FormHistoryRepoSess";
 
   /**
    * Number of records to insert in one batch.
    */
   public static final int INSERT_ITEM_THRESHOLD = 200;
 
   private static final Uri FORM_HISTORY_CONTENT_URI = BrowserContractHelpers.FORM_HISTORY_CONTENT_URI;
   private static final Uri DELETED_FORM_HISTORY_CONTENT_URI = BrowserContractHelpers.DELETED_FORM_HISTORY_CONTENT_URI;
@@ -114,17 +114,17 @@ public class FormHistoryRepositorySessio
 
   @Override
   public void finish(final RepositorySessionFinishDelegate delegate)
       throws InactiveSessionException {
     releaseProviders();
     super.finish(delegate);
   }
 
-  protected static String[] GUID_COLUMNS = new String[] { FormHistory.GUID };
+  protected static final String[] GUID_COLUMNS = new String[] { FormHistory.GUID };
 
   @Override
   public void guidsSince(final long timestamp, final RepositorySessionGuidsSinceDelegate delegate) {
     Runnable command = new Runnable() {
       @Override
       public void run() {
         if (!isActive()) {
           delegate.onGuidsSinceFailed(new InactiveSessionException(null));
@@ -137,39 +137,33 @@ public class FormHistoryRepositorySessio
         Cursor cur = null;
         try {
           cur = regularHelper.safeQuery(formsProvider, "", GUID_COLUMNS, regularBetween(timestamp, sharedEnd), null, null);
           cur.moveToFirst();
           while (!cur.isAfterLast()) {
             guids.add(cur.getString(0));
             cur.moveToNext();
           }
-        } catch (RemoteException e) {
-          delegate.onGuidsSinceFailed(e);
-          return;
-        } catch (NullCursorException e) {
+        } catch (RemoteException | NullCursorException e) {
           delegate.onGuidsSinceFailed(e);
           return;
         } finally {
           if (cur != null) {
             cur.close();
           }
         }
 
         try {
           cur = deletedHelper.safeQuery(formsProvider, "", GUID_COLUMNS, deletedBetween(timestamp, sharedEnd), null, null);
           cur.moveToFirst();
           while (!cur.isAfterLast()) {
             guids.add(cur.getString(0));
             cur.moveToNext();
           }
-        } catch (RemoteException e) {
-          delegate.onGuidsSinceFailed(e);
-          return;
-        } catch (NullCursorException e) {
+        } catch (RemoteException | NullCursorException e) {
           delegate.onGuidsSinceFailed(e);
           return;
         } finally {
           if (cur != null) {
             cur.close();
           }
         }
 
@@ -438,17 +432,17 @@ public class FormHistoryRepositorySessio
     ContentValues cv = new ContentValues();
     cv.put(FormHistory.GUID, record.guid);
     cv.put(FormHistory.FIELD_NAME, record.fieldName);
     cv.put(FormHistory.VALUE, record.fieldValue);
     cv.put(FormHistory.FIRST_USED, 1000 * record.lastModified); // Microseconds.
     return cv;
   }
 
-  protected Object recordsBufferMonitor = new Object();
+  protected final Object recordsBufferMonitor = new Object();
   protected ArrayList<ContentValues> recordsBuffer = new ArrayList<ContentValues>();
 
   protected void enqueueRegularRecord(Record record) {
     synchronized (recordsBufferMonitor) {
       if (recordsBuffer.size() >= INSERT_ITEM_THRESHOLD) {
         // Insert the existing contents, then enqueue.
         try {
           flushInsertQueue();
--- a/mobile/android/base/sync/repositories/android/PasswordsRepositorySession.java
+++ b/mobile/android/base/sync/repositories/android/PasswordsRepositorySession.java
@@ -265,23 +265,20 @@ public class PasswordsRepositorySession 
         if (guid == null) {
           delegate.onRecordStoreFailed(new RuntimeException("Can't store record with null GUID."), record.guid);
           return;
         }
 
         PasswordRecord existingRecord;
         try {
           existingRecord = retrieveByGUID(guid);
-        } catch (NullCursorException e) {
+        } catch (NullCursorException | RemoteException e) {
           // Indicates a serious problem.
           delegate.onRecordStoreFailed(e, record.guid);
           return;
-        } catch (RemoteException e) {
-          delegate.onRecordStoreFailed(e, record.guid);
-          return;
         }
 
         long lastLocalRetrieval  = 0;      // lastSyncTimestamp?
         long lastRemoteRetrieval = 0;      // TODO: adjust for clock skew.
         boolean remotelyModified = remoteRecord.lastModified > lastRemoteRetrieval;
 
         // Check deleted state first.
         if (remoteRecord.deleted) {
--- a/mobile/android/base/sync/setup/SyncAccounts.java
+++ b/mobile/android/base/sync/setup/SyncAccounts.java
@@ -432,19 +432,17 @@ public class SyncAccounts {
    * @return Sync account parameters, always non-null; fields username,
    *         password, serverURL, and syncKey always non-null.
    */
   public static SyncAccountParameters blockingFromAndroidAccountV0(final Context context, final AccountManager accountManager, final Account account)
       throws CredentialException {
     String username;
     try {
       username = Utils.usernameFromAccount(account.name);
-    } catch (NoSuchAlgorithmException e) {
-      throw new CredentialException.MissingCredentialException("username");
-    } catch (UnsupportedEncodingException e) {
+    } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
       throw new CredentialException.MissingCredentialException("username");
     }
 
     /*
      * If we are accessing an Account that we don't own, Android will throw an
      * unchecked <code>SecurityException</code> saying
      * "W FxSync(XXXX) java.lang.SecurityException: caller uid XXXXX is different than the authenticator's uid".
      * We catch that error and throw accordingly.
--- a/mobile/android/base/sync/setup/SyncAuthenticatorService.java
+++ b/mobile/android/base/sync/setup/SyncAuthenticatorService.java
@@ -109,20 +109,17 @@ public class SyncAuthenticatorService ex
     result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
 
     // Username after hashing.
     try {
       String username = Utils.usernameFromAccount(account.name);
       Logger.pii(LOG_TAG, "Account " + account.name + " hashes to " + username + ".");
       Logger.debug(LOG_TAG, "Setting username. Null? " + (username == null));
       result.putString(Constants.OPTION_USERNAME, username);
-    } catch (NoSuchAlgorithmException e) {
-      // Do nothing. Calling code must check for missing value.
-      Logger.debug(LOG_TAG, "Exception in account lookup: " + e);
-    } catch (UnsupportedEncodingException e) {
+    } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
       // Do nothing. Calling code must check for missing value.
       Logger.debug(LOG_TAG, "Exception in account lookup: " + e);
     }
 
     // Sync key.
     final String syncKey = am.getUserData(account, Constants.OPTION_SYNCKEY);
     Logger.debug(LOG_TAG, "Setting sync key. Null? " + (syncKey == null));
     result.putString(Constants.OPTION_SYNCKEY, syncKey);
--- a/mobile/android/base/sync/setup/auth/AuthenticateAccountStage.java
+++ b/mobile/android/base/sync/setup/auth/AuthenticateAccountStage.java
@@ -51,25 +51,23 @@ public class AuthenticateAccountStage im
         aa.abort(AuthenticationResult.FAILURE_OTHER, new Exception(response.getStatusLine().getStatusCode() + " error."));
         if (response.getEntity() == null) {
           // No cleanup necessary.
           return;
         }
         try {
           BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent(), "UTF-8"));
           BaseResource.consumeReader(reader);
-        } catch (IllegalStateException e) {
+        } catch (IllegalStateException | IOException e) {
           Logger.debug(LOG_TAG, "Error reading content.", e);
         } catch (RuntimeException e) {
           Logger.debug(LOG_TAG, "Unexpected exception.", e);
           if (httpRequest != null) {
             httpRequest.abort();
           }
-        } catch (IOException e) {
-          Logger.debug(LOG_TAG, "Error reading content.", e);
         }
       }
 
       @Override
       public void handleError(Exception e) {
         Logger.debug(LOG_TAG, "handleError", e);
         aa.abort(AuthenticationResult.FAILURE_OTHER, e);
       }
--- a/mobile/android/base/sync/setup/auth/FetchUserNodeStage.java
+++ b/mobile/android/base/sync/setup/auth/FetchUserNodeStage.java
@@ -91,19 +91,17 @@ public class FetchUserNodeStage implemen
         case 200:
           try {
             InputStream content = response.getEntity().getContent();
             BufferedReader reader = new BufferedReader(new InputStreamReader(content, "UTF-8"), 1024);
             String server = reader.readLine();
             callbackDelegate.handleSuccess(server);
             BaseResource.consumeReader(reader);
             reader.close();
-          } catch (IllegalStateException e) {
-            callbackDelegate.handleError(e);
-          } catch (IOException e) {
+          } catch (IllegalStateException | IOException e) {
             callbackDelegate.handleError(e);
           }
           break;
         case 404: // Does not support auth nodes, use server instead.
           callbackDelegate.handleSuccess(null);
           break;
         default:
           // No other acceptable states.
--- a/mobile/android/base/sync/stage/EnsureClusterURLStage.java
+++ b/mobile/android/base/sync/stage/EnsureClusterURLStage.java
@@ -118,21 +118,17 @@ public class EnsureClusterURLStage exten
             }
             String output = null;
             try {
               InputStream content = entity.getContent();
               BufferedReader reader = new BufferedReader(new InputStreamReader(content, "UTF-8"), 1024);
               output = reader.readLine();
               BaseResource.consumeReader(reader);
               reader.close();
-            } catch (IllegalStateException e) {
-              delegate.handleError(e);
-              BaseResource.consumeEntity(response);
-              return;
-            } catch (IOException e) {
+            } catch (IllegalStateException | IOException e) {
               delegate.handleError(e);
               BaseResource.consumeEntity(response);
               return;
             }
 
             if (output == null || output.equals("null")) {
               delegate.handleThrottled();
               return;
--- a/mobile/android/base/sync/stage/ServerSyncStage.java
+++ b/mobile/android/base/sync/stage/ServerSyncStage.java
@@ -544,23 +544,17 @@ public abstract class ServerSyncStage ex
     try {
       synchronizer = this.getConfiguredSynchronizer(session);
     } catch (NoCollectionKeysSetException e) {
       session.abort(e, "No CollectionKeys.");
       return;
     } catch (URISyntaxException e) {
       session.abort(e, "Invalid URI syntax for server repository.");
       return;
-    } catch (NonObjectJSONException e) {
-      session.abort(e, "Invalid persisted JSON for config.");
-      return;
-    } catch (IOException e) {
-      session.abort(e, "Invalid persisted JSON for config.");
-      return;
-    } catch (ParseException e) {
+    } catch (NonObjectJSONException | ParseException | IOException e) {
       session.abort(e, "Invalid persisted JSON for config.");
       return;
     }
 
     Logger.debug(LOG_TAG, "Invoking synchronizer.");
     synchronizer.synchronize(session.getContext(), this);
     Logger.debug(LOG_TAG, "Reached end of execute.");
   }
--- a/mobile/android/base/tabs/TabCurve.java
+++ b/mobile/android/base/tabs/TabCurve.java
@@ -24,17 +24,19 @@ public class TabCurve {
     }
 
     // Curve's aspect ratio
     private static final float ASPECT_RATIO = 0.729f;
 
     // Width multipliers
     private static final float W_M1 = 0.343f;
     private static final float W_M2 = 0.514f;
-    private static final float W_M3 = 0.723f;
+    private static final float W_M3 = 0.49f;
+    private static final float W_M4 = 0.545f;
+    private static final float W_M5 = 0.723f;
 
     // Height multipliers
     private static final float H_M1 = 0.25f;
     private static final float H_M2 = 0.5f;
     private static final float H_M3 = 0.72f;
     private static final float H_M4 = 0.961f;
 
     private TabCurve() {
@@ -43,26 +45,26 @@ public class TabCurve {
     public static float getWidthForHeight(float height) {
         return (int) (height * ASPECT_RATIO);
     }
 
     public static void drawFromTop(Path path, float from, float height, Direction dir) {
         final float width = getWidthForHeight(height);
 
         path.cubicTo(from + width * W_M1 * dir.value, 0.0f,
-                     from + width * W_M2 * dir.value, height * H_M1,
+                     from + width * W_M3 * dir.value, height * H_M1,
                      from + width * W_M2 * dir.value, height * H_M2);
-        path.cubicTo(from + width * W_M2 * dir.value, height * H_M3,
-                     from + width * W_M3 * dir.value, height * H_M4,
+        path.cubicTo(from + width * W_M4 * dir.value, height * H_M3,
+                     from + width * W_M5 * dir.value, height * H_M4,
                      from + width * dir.value, height);
     }
 
     public static void drawFromBottom(Path path, float from, float height, Direction dir) {
         final float width = getWidthForHeight(height);
 
-        path.cubicTo(from + width * (1f - W_M3) * dir.value, height * H_M4,
-                     from + width * (1f - W_M2) * dir.value, height * H_M3,
+        path.cubicTo(from + width * (1f - W_M5) * dir.value, height * H_M4,
+                     from + width * (1f - W_M4) * dir.value, height * H_M3,
                      from + width * (1f - W_M2) * dir.value, height * H_M2);
-        path.cubicTo(from + width * (1f - W_M2) * dir.value, height * H_M1,
+        path.cubicTo(from + width * (1f - W_M3) * dir.value, height * H_M1,
                      from + width * (1f - W_M1) * dir.value, 0.0f,
                      from + width * dir.value, 0.0f);
     }
 }
--- a/mobile/android/base/util/GeckoJarReader.java
+++ b/mobile/android/base/util/GeckoJarReader.java
@@ -41,19 +41,17 @@ public final class GeckoJarReader {
         NativeZip zip = null;
         try {
             // Load the initial jar file as a zip
             zip = getZipFile(jarUrls.pop());
             inputStream = getStream(zip, jarUrls, url);
             if (inputStream != null) {
                 bitmap = new BitmapDrawable(resources, inputStream);
             }
-        } catch (IOException ex) {
-            Log.e(LOGTAG, "Exception ", ex);
-        } catch (URISyntaxException ex) {
+        } catch (IOException | URISyntaxException ex) {
             Log.e(LOGTAG, "Exception ", ex);
         } finally {
             if (inputStream != null) {
                 try {
                     inputStream.close();
                 } catch(IOException ex) {
                     Log.e(LOGTAG, "Error closing stream", ex);
                 }
@@ -74,19 +72,17 @@ public final class GeckoJarReader {
         String text = null;
         try {
             zip = getZipFile(jarUrls.pop());
             InputStream input = getStream(zip, jarUrls, url);
             if (input != null) {
                 reader = new BufferedReader(new InputStreamReader(input));
                 text = reader.readLine();
             }
-        } catch (IOException ex) {
-            Log.e(LOGTAG, "Exception ", ex);
-        } catch (URISyntaxException ex) {
+        } catch (IOException | URISyntaxException ex) {
             Log.e(LOGTAG, "Exception ", ex);
         } finally {
             if (reader != null) {
                 try {
                     reader.close();
                 } catch(IOException ex) {
                     Log.e(LOGTAG, "Error closing reader", ex);
                 }
--- a/mobile/android/base/widget/ActivityChooserModel.java
+++ b/mobile/android/base/widget/ActivityChooserModel.java
@@ -1120,20 +1120,18 @@ public class ActivityChooserModel extend
                 if (DEBUG) {
                     Log.i(LOG_TAG, "Read " + readRecord.toString());
                 }
             }
 
             if (DEBUG) {
                 Log.i(LOG_TAG, "Read " + historicalRecords.size() + " historical records.");
             }
-        } catch (XmlPullParserException xppe) {
+        } catch (XmlPullParserException | IOException xppe) {
             Log.e(LOG_TAG, "Error reading historical record file: " + mHistoryFileName, xppe);
-        } catch (IOException ioe) {
-            Log.e(LOG_TAG, "Error reading historical record file: " + mHistoryFileName, ioe);
         } finally {
             if (fis != null) {
                 try {
                     fis.close();
                 } catch (IOException ioe) {
                     /* ignore */
                 }
             }
@@ -1185,22 +1183,18 @@ public class ActivityChooserModel extend
                 }
 
                 serializer.endTag(null, TAG_HISTORICAL_RECORDS);
                 serializer.endDocument();
 
                 if (DEBUG) {
                     Log.i(LOG_TAG, "Wrote " + recordCount + " historical records.");
                 }
-            } catch (IllegalArgumentException iae) {
-                Log.e(LOG_TAG, "Error writing historical record file: " + mHistoryFileName, iae);
-            } catch (IllegalStateException ise) {
-                Log.e(LOG_TAG, "Error writing historical record file: " + mHistoryFileName, ise);
-            } catch (IOException ioe) {
-                Log.e(LOG_TAG, "Error writing historical record file: " + mHistoryFileName, ioe);
+            } catch (IllegalArgumentException | IOException | IllegalStateException e) {
+                Log.e(LOG_TAG, "Error writing historical record file: " + mHistoryFileName, e);
             } finally {
                 mCanReadHistoricalData = true;
                 if (fos != null) {
                     try {
                         fos.close();
                     } catch (IOException e) {
                         /* ignore */
                     }
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -271,26 +271,29 @@ function resolveGeckoURI(aURI) {
     return handler.resolveURI(Services.io.newURI(aURI, null, null));
   }
   return aURI;
 }
 
 /**
  * Cache of commonly used string bundles.
  */
-var Strings = {};
-[
-  ["brand",      "chrome://branding/locale/brand.properties"],
-  ["browser",    "chrome://browser/locale/browser.properties"]
-].forEach(function (aStringBundle) {
-  let [name, bundle] = aStringBundle;
-  XPCOMUtils.defineLazyGetter(Strings, name, function() {
-    return Services.strings.createBundle(bundle);
-  });
-});
+let Strings = {
+  init: function () {
+    XPCOMUtils.defineLazyGetter(Strings, "brand", () => Services.strings.createBundle("chrome://branding/locale/brand.properties"));
+    XPCOMUtils.defineLazyGetter(Strings, "browser", () => Services.strings.createBundle("chrome://browser/locale/browser.properties"));
+  },
+
+  flush: function () {
+    Services.strings.flushBundles();
+    this.init();
+  },
+};
+
+Strings.init();
 
 const kFormHelperModeDisabled = 0;
 const kFormHelperModeEnabled = 1;
 const kFormHelperModeDynamic = 2;   // disabled on tablets
 
 var BrowserApp = {
   _tabs: [],
   _selectedTab: null,
@@ -546,19 +549,24 @@ var BrowserApp = {
 
   _initRuntime: function(status, url, callback) {
     let sandbox = {};
     Services.scriptloader.loadSubScript("chrome://browser/content/WebappRT.js", sandbox);
     window.WebappRT = sandbox.WebappRT;
     WebappRT.init(status, url, callback);
   },
 
-  initContextMenu: function ba_initContextMenu() {
+  initContextMenu: function () {
+    // We pass a thunk in place of a raw label string. This allows the
+    // context menu to automatically accommodate locale changes without
+    // having to be rebuilt.
+    let stringGetter = name => () => Strings.browser.GetStringFromName(name);
+
     // TODO: These should eventually move into more appropriate classes
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.openInNewTab"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.openInNewTab"),
       NativeWindow.contextmenus.linkOpenableNonPrivateContext,
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_open_new_tab");
         UITelemetry.addEvent("loadurl.1", "contextmenu", null);
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         ContentAreaUtils.urlSecurityCheck(url, aTarget.ownerDocument.nodePrincipal);
         let tab = BrowserApp.addTab(url, { selected: false, parentId: BrowserApp.selectedTab.id });
@@ -570,17 +578,17 @@ var BrowserApp = {
           button: {
             icon: "drawable://switch_button_icon",
             label: buttonLabel,
             callback: () => { BrowserApp.selectTab(tab); },
           }
         });
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.openInPrivateTab"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.openInPrivateTab"),
       NativeWindow.contextmenus.linkOpenableContext,
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_open_private_tab");
         UITelemetry.addEvent("loadurl.1", "contextmenu", null);
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         ContentAreaUtils.urlSecurityCheck(url, aTarget.ownerDocument.nodePrincipal);
         let tab = BrowserApp.addTab(url, { selected: false, parentId: BrowserApp.selectedTab.id, isPrivate: true });
@@ -592,63 +600,63 @@ var BrowserApp = {
           button: {
             icon: "drawable://switch_button_icon",
             label: buttonLabel,
             callback: () => { BrowserApp.selectTab(tab); },
           }
         });
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.copyLink"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.copyLink"),
       NativeWindow.contextmenus.linkCopyableContext,
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_copy_link");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         NativeWindow.contextmenus._copyStringToDefaultClipboard(url);
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.copyEmailAddress"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.copyEmailAddress"),
       NativeWindow.contextmenus.emailLinkContext,
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_copy_email");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         let emailAddr = NativeWindow.contextmenus._stripScheme(url);
         NativeWindow.contextmenus._copyStringToDefaultClipboard(emailAddr);
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.copyPhoneNumber"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.copyPhoneNumber"),
       NativeWindow.contextmenus.phoneNumberLinkContext,
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_copy_phone");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         let phoneNumber = NativeWindow.contextmenus._stripScheme(url);
         NativeWindow.contextmenus._copyStringToDefaultClipboard(phoneNumber);
       });
 
     NativeWindow.contextmenus.add({
-      label: Strings.browser.GetStringFromName("contextmenu.shareLink"),
+      label: stringGetter("contextmenu.shareLink"),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1, // Show above HTML5 menu items
       selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.linkShareableContext),
       showAsActions: function(aElement) {
         return {
           title: aElement.textContent.trim() || aElement.title.trim(),
           uri: NativeWindow.contextmenus._getLinkURL(aElement),
         };
       },
       icon: "drawable://ic_menu_share",
       callback: function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_link");
       }
     });
 
     NativeWindow.contextmenus.add({
-      label: Strings.browser.GetStringFromName("contextmenu.shareEmailAddress"),
+      label: stringGetter("contextmenu.shareEmailAddress"),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
       selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.emailLinkContext),
       showAsActions: function(aElement) {
         let url = NativeWindow.contextmenus._getLinkURL(aElement);
         let emailAddr = NativeWindow.contextmenus._stripScheme(url);
         let title = aElement.textContent || aElement.title;
         return {
           title: title,
@@ -657,17 +665,17 @@ var BrowserApp = {
       },
       icon: "drawable://ic_menu_share",
       callback: function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_email");
       }
     });
 
     NativeWindow.contextmenus.add({
-      label: Strings.browser.GetStringFromName("contextmenu.sharePhoneNumber"),
+      label: stringGetter("contextmenu.sharePhoneNumber"),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
       selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.phoneNumberLinkContext),
       showAsActions: function(aElement) {
         let url = NativeWindow.contextmenus._getLinkURL(aElement);
         let phoneNumber = NativeWindow.contextmenus._stripScheme(url);
         let title = aElement.textContent || aElement.title;
         return {
           title: title,
@@ -675,77 +683,77 @@ var BrowserApp = {
         };
       },
       icon: "drawable://ic_menu_share",
       callback: function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_phone");
       }
     });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.addToContacts"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.addToContacts"),
       NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.emailLinkContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_email");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         Messaging.sendRequest({
           type: "Contact:Add",
           email: url
         });
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.addToContacts"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.addToContacts"),
       NativeWindow.contextmenus._disableRestricted("ADD_CONTACT", NativeWindow.contextmenus.phoneNumberLinkContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_phone");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         Messaging.sendRequest({
           type: "Contact:Add",
           phone: url
         });
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.bookmarkLink"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.bookmarkLink"),
       NativeWindow.contextmenus._disableRestricted("BOOKMARK", NativeWindow.contextmenus.linkBookmarkableContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_bookmark");
 
         let url = NativeWindow.contextmenus._getLinkURL(aTarget);
         let title = aTarget.textContent || aTarget.title || url;
         Messaging.sendRequest({
           type: "Bookmark:Insert",
           url: url,
           title: title
         });
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.playMedia"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.playMedia"),
       NativeWindow.contextmenus.mediaContext("media-paused"),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_play");
         aTarget.play();
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.pauseMedia"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.pauseMedia"),
       NativeWindow.contextmenus.mediaContext("media-playing"),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_pause");
         aTarget.pause();
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.showControls2"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.showControls2"),
       NativeWindow.contextmenus.mediaContext("media-hidingcontrols"),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_controls_media");
         aTarget.setAttribute("controls", true);
       });
 
     NativeWindow.contextmenus.add({
-      label: Strings.browser.GetStringFromName("contextmenu.shareMedia"),
+      label: stringGetter("contextmenu.shareMedia"),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1,
       selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.SelectorContext("video")),
       showAsActions: function(aElement) {
         let url = (aElement.currentSrc || aElement.src);
         let title = aElement.textContent || aElement.title;
         return {
           title: title,
           uri: url,
@@ -753,48 +761,48 @@ var BrowserApp = {
         };
       },
       icon: "drawable://ic_menu_share",
       callback: function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_media");
       }
     });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.fullScreen"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.fullScreen"),
       NativeWindow.contextmenus.SelectorContext("video:not(:-moz-full-screen)"),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_fullscreen");
         aTarget.mozRequestFullScreen();
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.mute"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.mute"),
       NativeWindow.contextmenus.mediaContext("media-unmuted"),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_mute");
         aTarget.muted = true;
       });
   
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.unmute"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.unmute"),
       NativeWindow.contextmenus.mediaContext("media-muted"),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_unmute");
         aTarget.muted = false;
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.copyImageLocation"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.copyImageLocation"),
       NativeWindow.contextmenus.imageLocationCopyableContext,
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_copy_image");
 
         let url = aTarget.src;
         NativeWindow.contextmenus._copyStringToDefaultClipboard(url);
       });
 
     NativeWindow.contextmenus.add({
-      label: Strings.browser.GetStringFromName("contextmenu.shareImage"),
+      label: stringGetter("contextmenu.shareImage"),
       selector: NativeWindow.contextmenus._disableRestricted("SHARE", NativeWindow.contextmenus.imageSaveableContext),
       order: NativeWindow.contextmenus.DEFAULT_HTML5_ORDER - 1, // Show above HTML5 menu items
       showAsActions: function(aTarget) {
         let doc = aTarget.ownerDocument;
         let imageCache = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools)
                                                          .getImgCacheForDocument(doc);
         let props = imageCache.findEntryProperties(aTarget.currentURI, doc.characterSet);
         let src = aTarget.src;
@@ -806,27 +814,27 @@ var BrowserApp = {
       },
       icon: "drawable://ic_menu_share",
       menu: true,
       callback: function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_share_image");
       }
     });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.saveImage"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.saveImage"),
       NativeWindow.contextmenus.imageSaveableContext,
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_save_image");
 
         ContentAreaUtils.saveImageURL(aTarget.currentURI.spec, null, "SaveImageTitle",
                                       false, true, aTarget.ownerDocument.documentURIObject,
                                       aTarget.ownerDocument);
       });
 
-    NativeWindow.contextmenus.add(Strings.browser.GetStringFromName("contextmenu.setImageAs"),
+    NativeWindow.contextmenus.add(stringGetter("contextmenu.setImageAs"),
       NativeWindow.contextmenus._disableRestricted("SET_IMAGE", NativeWindow.contextmenus.imageSaveableContext),
       function(aTarget) {
         UITelemetry.addEvent("action.1", "contextmenu", null, "web_background_image");
 
         let src = aTarget.src;
         Messaging.sendRequest({
           type: "Image:SetAs",
           url: src
@@ -1822,17 +1830,17 @@ var BrowserApp = {
         Services.prefs.setBoolPref("intl.locale.matchOS", !aData);
 
         // Ensure that this choice is immediately persisted, because
         // Gecko won't be told again if it forgets.
         Services.prefs.savePrefFile(null);
 
         // Blow away the string cache so that future lookups get the
         // correct locale.
-        Services.strings.flushBundles();
+        Strings.flush();
 
         // Make sure we use the right Accept-Language header.
         let osLocale;
         try {
           // This should never not be set at this point, but better safe than sorry.
           osLocale = Services.prefs.getCharPref("intl.locale.os");
         } catch (e) {
         }
--- a/mobile/android/search/java/org/mozilla/search/providers/SearchEngineManager.java
+++ b/mobile/android/search/java/org/mozilla/search/providers/SearchEngineManager.java
@@ -181,19 +181,17 @@ public class SearchEngineManager impleme
         }
 
         try {
             try {
                 return new SearchEngine(identifier, in);
             } finally {
                 in.close();
             }
-        } catch (IOException e) {
-            Log.e(LOG_TAG, "Exception creating search engine", e);
-        } catch (XmlPullParserException e) {
+        } catch (IOException | XmlPullParserException e) {
             Log.e(LOG_TAG, "Exception creating search engine", e);
         }
 
         return null;
     }
 
     /**
      * Reads a file from the searchplugins directory in the Gecko jar. This will only work
--- a/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Hex.java
+++ b/mobile/android/thirdparty/org/mozilla/apache/commons/codec/binary/Hex.java
@@ -269,19 +269,17 @@ public class Hex implements BinaryEncode
      * @throws EncoderException
      *             Thrown if the given object is not a String or byte[]
      * @see #encodeHex(byte[])
      */
     public Object encode(Object object) throws EncoderException {
         try {
             byte[] byteArray = object instanceof String ? ((String) object).getBytes(getCharsetName()) : (byte[]) object;
             return encodeHex(byteArray);
-        } catch (ClassCastException e) {
-            throw new EncoderException(e.getMessage(), e);
-        } catch (UnsupportedEncodingException e) {
+        } catch (ClassCastException | UnsupportedEncodingException e) {
             throw new EncoderException(e.getMessage(), e);
         }
     }
 
     /**
      * Gets the charset name.
      * 
      * @return the charset name.
--- a/toolkit/components/places/SQLFunctions.cpp
+++ b/toolkit/components/places/SQLFunctions.cpp
@@ -354,26 +354,35 @@ namespace places {
     }
 
     int32_t visitCount = aArguments->AsInt32(kArgIndexVisitCount);
     bool typed = aArguments->AsInt32(kArgIndexTyped) ? true : false;
     bool bookmark = aArguments->AsInt32(kArgIndexBookmark) ? true : false;
     nsAutoCString tags;
     (void)aArguments->GetUTF8String(kArgIndexTags, tags);
     int32_t openPageCount = aArguments->AsInt32(kArgIndexOpenPageCount);
+    bool matches = false;
+    if (HAS_BEHAVIOR(RESTRICT)) {
+      // Make sure we match all the filter requirements.  If a given restriction
+      // is active, make sure the corresponding condition is not true.
+      matches = (!HAS_BEHAVIOR(HISTORY) || visitCount > 0) &&
+                (!HAS_BEHAVIOR(TYPED) || typed) &&
+                (!HAS_BEHAVIOR(BOOKMARK) || bookmark) &&
+                (!HAS_BEHAVIOR(TAG) || !tags.IsVoid()) &&
+                (!HAS_BEHAVIOR(OPENPAGE) || openPageCount > 0);
+    } else {
+      // Make sure that we match all the filter requirements and that the
+      // corresponding condition is true if at least a given restriction is active.
+      matches = (HAS_BEHAVIOR(HISTORY) && visitCount > 0) ||
+                (HAS_BEHAVIOR(TYPED) && typed) ||
+                (HAS_BEHAVIOR(BOOKMARK) && bookmark) ||
+                (HAS_BEHAVIOR(TAG) && !tags.IsVoid()) ||
+                (HAS_BEHAVIOR(OPENPAGE) && openPageCount > 0);
+    }
 
-    // Make sure we match all the filter requirements.  If a given restriction
-    // is active, make sure the corresponding condition is not true.
-    bool matches = !(
-      (HAS_BEHAVIOR(HISTORY) && visitCount == 0) ||
-      (HAS_BEHAVIOR(TYPED) && !typed) ||
-      (HAS_BEHAVIOR(BOOKMARK) && !bookmark) ||
-      (HAS_BEHAVIOR(TAG) && tags.IsVoid()) ||
-      (HAS_BEHAVIOR(OPENPAGE) && openPageCount == 0)
-    );
     if (!matches) {
       NS_ADDREF(*_result = new IntegerVariant(0));
       return NS_OK;
     }
 
     // Obtain our search function.
     searchFunctionPtr searchFunction = getSearchFunction(matchBehavior);
 
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -6,43 +6,40 @@
 
 "use strict";
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Constants
 
 const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
 
-const TOPIC_PREFCHANGED = "nsPref:changed";
-
-const DEFAULT_BEHAVIOR = 0;
-
 const PREF_BRANCH = "browser.urlbar.";
 
 // Prefs are defined as [pref name, default value].
 const PREF_ENABLED =                [ "autocomplete.enabled",   true ];
 const PREF_AUTOFILL =               [ "autoFill",               true ];
 const PREF_AUTOFILL_TYPED =         [ "autoFill.typed",         true ];
 const PREF_AUTOFILL_SEARCHENGINES = [ "autoFill.searchEngines", true ];
 const PREF_DELAY =                  [ "delay",                  50 ];
 const PREF_BEHAVIOR =               [ "matchBehavior", MATCH_BOUNDARY_ANYWHERE ];
-const PREF_DEFAULT_BEHAVIOR =       [ "default.behavior", DEFAULT_BEHAVIOR ];
-const PREF_EMPTY_BEHAVIOR =         [ "default.behavior.emptyRestriction",
-                                      Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY |
-                                      Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED ];
 const PREF_FILTER_JS =              [ "filter.javascript",      true ];
 const PREF_MAXRESULTS =             [ "maxRichResults",         25 ];
 const PREF_RESTRICT_HISTORY =       [ "restrict.history",       "^" ];
 const PREF_RESTRICT_BOOKMARKS =     [ "restrict.bookmark",      "*" ];
 const PREF_RESTRICT_TYPED =         [ "restrict.typed",         "~" ];
 const PREF_RESTRICT_TAG =           [ "restrict.tag",           "+" ];
 const PREF_RESTRICT_SWITCHTAB =     [ "restrict.openpage",      "%" ];
 const PREF_MATCH_TITLE =            [ "match.title",            "#" ];
 const PREF_MATCH_URL =              [ "match.url",              "@" ];
 
+const PREF_SUGGEST_HISTORY =        [ "suggest.history",        true ];
+const PREF_SUGGEST_BOOKMARK =       [ "suggest.bookmark",       true ];
+const PREF_SUGGEST_OPENPAGE =       [ "suggest.openpage",       true ];
+const PREF_SUGGEST_HISTORY_ONLYTYPED = [ "suggest.history.onlyTyped", false ];
+
 // Match type constants.
 // These indicate what type of search function we should be using.
 const MATCH_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_ANYWHERE;
 const MATCH_BOUNDARY_ANYWHERE = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY_ANYWHERE;
 const MATCH_BOUNDARY = Ci.mozIPlacesAutoComplete.MATCH_BOUNDARY;
 const MATCH_BEGINNING = Ci.mozIPlacesAutoComplete.MATCH_BEGINNING;
 const MATCH_BEGINNING_CASE_SENSITIVE = Ci.mozIPlacesAutoComplete.MATCH_BEGINNING_CASE_SENSITIVE;
 
@@ -113,29 +110,16 @@ function defaultQuery(conditions = "") {
                               bookmarked, t.open_count,
                               :matchBehavior, :searchBehavior)
        ${conditions}
      ORDER BY h.frecency DESC, h.id DESC
      LIMIT :maxResults`;
   return query;
 }
 
-const SQL_DEFAULT_QUERY = defaultQuery();
-
-// Enforce ignoring the visit_count index, since the frecency one is much
-// faster in this case.  ANALYZE helps the query planner to figure out the
-// faster path, but it may not have up-to-date information yet.
-const SQL_HISTORY_QUERY = defaultQuery("AND +h.visit_count > 0");
-
-const SQL_BOOKMARK_QUERY = defaultQuery("AND bookmarked");
-
-const SQL_TAGS_QUERY = defaultQuery("AND tags NOTNULL");
-
-const SQL_TYPED_QUERY = defaultQuery("AND h.typed = 1");
-
 const SQL_SWITCHTAB_QUERY =
   `SELECT :query_type, t.url, t.url, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
           t.open_count, NULL
    FROM moz_openpages_temp t
    LEFT JOIN moz_places h ON h.url = t.url
    WHERE h.id IS NULL
      AND AUTOCOMPLETE_MATCH(:searchString, t.url, t.url, NULL,
                             NULL, NULL, NULL, t.open_count,
@@ -232,31 +216,31 @@ function urlQuery(conditions = "") {
     `/* do not warn (bug no): cannot use an index */
      SELECT :query_type, h.url, NULL,
             NULL, foreign_count > 0 AS bookmarked, NULL, NULL, NULL, NULL, NULL, NULL, h.frecency
      FROM moz_places h
      WHERE h.frecency <> 0
      ${conditions}
      AND AUTOCOMPLETE_MATCH(:searchString, h.url,
      h.title, '',
-     h.visit_count, h.typed, 0, 0,
+     h.visit_count, h.typed, bookmarked, 0,
      :matchBehavior, :searchBehavior)
      ORDER BY h.frecency DESC, h.id DESC
      LIMIT 1`;
   return query;
 }
 
 const SQL_URL_QUERY = urlQuery();
 
-const SQL_TYPED_URL_QUERY = urlQuery("AND typed = 1");
+const SQL_TYPED_URL_QUERY = urlQuery("AND h.typed = 1");
 
 // TODO (bug 1045924): use foreign_count once available.
 const SQL_BOOKMARKED_URL_QUERY = urlQuery("AND bookmarked");
 
-const SQL_BOOKMARKED_TYPED_URL_QUERY = urlQuery("AND bookmarked AND typed = 1");
+const SQL_BOOKMARKED_TYPED_URL_QUERY = urlQuery("AND bookmarked AND h.typed = 1");
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Getters
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
@@ -360,37 +344,90 @@ XPCOMUtils.defineLazyGetter(this, "Switc
   }
 }));
 
 /**
  * This helper keeps track of preferences and keeps their values up-to-date.
  */
 XPCOMUtils.defineLazyGetter(this, "Prefs", () => {
   let prefs = new Preferences(PREF_BRANCH);
+  let types = ["History", "Bookmark", "Openpage", "Typed"];
 
-  function loadPrefs() {
+  function syncEnabledPref(init = false) {
+    let suggestPrefs = [PREF_SUGGEST_HISTORY, PREF_SUGGEST_BOOKMARK, PREF_SUGGEST_OPENPAGE];
+
+    if (init) {
+      // Make sure to initialize the properties when first called with init = true.
+      store.enabled = prefs.get(...PREF_ENABLED);
+      store.suggestHistory = prefs.get(...PREF_SUGGEST_HISTORY);
+      store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK);
+      store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
+      store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
+    }
+
+    if (store.enabled) {
+      // If the autocomplete preference is active, set to default value all suggest
+      // preferences only if all of them are false.
+      if (types.every(type => store["suggest" + type] == false)) {
+        for (let type of suggestPrefs) {
+          prefs.set(...type);
+        }
+      }
+    } else {
+      // If the preference was deactivated, deactivate all suggest preferences.
+      for (let type of suggestPrefs) {
+        prefs.set(type[0], false);
+      }
+    }
+  }
+
+  function loadPrefs(subject, topic, data) {
     store.enabled = prefs.get(...PREF_ENABLED);
     store.autofill = prefs.get(...PREF_AUTOFILL);
     store.autofillTyped = prefs.get(...PREF_AUTOFILL_TYPED);
     store.autofillSearchEngines = prefs.get(...PREF_AUTOFILL_SEARCHENGINES);
     store.delay = prefs.get(...PREF_DELAY);
     store.matchBehavior = prefs.get(...PREF_BEHAVIOR);
     store.filterJavaScript = prefs.get(...PREF_FILTER_JS);
     store.maxRichResults = prefs.get(...PREF_MAXRESULTS);
     store.restrictHistoryToken = prefs.get(...PREF_RESTRICT_HISTORY);
     store.restrictBookmarkToken = prefs.get(...PREF_RESTRICT_BOOKMARKS);
     store.restrictTypedToken = prefs.get(...PREF_RESTRICT_TYPED);
     store.restrictTagToken = prefs.get(...PREF_RESTRICT_TAG);
     store.restrictOpenPageToken = prefs.get(...PREF_RESTRICT_SWITCHTAB);
     store.matchTitleToken = prefs.get(...PREF_MATCH_TITLE);
     store.matchURLToken = prefs.get(...PREF_MATCH_URL);
-    store.defaultBehavior = prefs.get(...PREF_DEFAULT_BEHAVIOR);
+    store.suggestHistory = prefs.get(...PREF_SUGGEST_HISTORY);
+    store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK);
+    store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
+    store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
+
+    // If history is not set, onlyTyped value should be ignored.
+    if (!store.suggestHistory) {
+      store.suggestTyped = false;
+    }
+    store.defaultBehavior = types.reduce((memo, type) => {
+      let prefValue = store["suggest" + type];
+      return memo | (prefValue &&
+                     Ci.mozIPlacesAutoComplete["BEHAVIOR_" + type.toUpperCase()]);
+    }, 0);
+
     // Further restrictions to apply for "empty searches" (i.e. searches for "").
-    store.emptySearchDefaultBehavior = store.defaultBehavior |
-                                       prefs.get(...PREF_EMPTY_BEHAVIOR);
+    // The empty behavior is typed history, if history is enabled. Otherwise,
+    // it is bookmarks, if they are enabled. If both history and bookmarks are disabled,
+    // it defaults to open pages.
+    store.emptySearchDefaultBehavior = Ci.mozIPlacesAutoComplete.BEHAVIOR_RESTRICT;
+    if (store.suggestHistory) {
+      store.emptySearchDefaultBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY |
+                                          Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED;
+    } else if (store.suggestBookmark) {
+      store.emptySearchDefaultBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_BOOKMARK;
+    } else {
+      store.emptySearchDefaultBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_OPENPAGE;
+    }
 
     // Validate matchBehavior; default to MATCH_BOUNDARY_ANYWHERE.
     if (store.matchBehavior != MATCH_ANYWHERE &&
         store.matchBehavior != MATCH_BOUNDARY &&
         store.matchBehavior != MATCH_BEGINNING) {
       store.matchBehavior = MATCH_BOUNDARY_ANYWHERE;
     }
 
@@ -398,24 +435,32 @@ XPCOMUtils.defineLazyGetter(this, "Prefs
       [ store.restrictHistoryToken, "history" ],
       [ store.restrictBookmarkToken, "bookmark" ],
       [ store.restrictTagToken, "tag" ],
       [ store.restrictOpenPageToken, "openpage" ],
       [ store.matchTitleToken, "title" ],
       [ store.matchURLToken, "url" ],
       [ store.restrictTypedToken, "typed" ]
     ]);
+
+    // Synchronize suggest.* prefs with autocomplete.enabled every time
+    // autocomplete.enabled is changed.
+    if (data == PREF_BRANCH + PREF_ENABLED[0]) {
+      syncEnabledPref();
+    }
   }
 
   let store = {
-    observe: function (subject, topic, data) {
-      loadPrefs();
-    },
+    observe: loadPrefs,
     QueryInterface: XPCOMUtils.generateQI([ Ci.nsIObserver ])
   };
+
+  // Synchronize suggest.* prefs with autocomplete.enabled at initialization
+  syncEnabledPref(true);
+
   loadPrefs();
   prefs.observe("", store);
 
   return Object.seal(store);
 });
 
 ////////////////////////////////////////////////////////////////////////////////
 //// Helper functions
@@ -560,18 +605,24 @@ function Search(searchString, searchPara
 Search.prototype = {
   /**
    * Enables the desired AutoComplete behavior.
    *
    * @param type
    *        The behavior type to set.
    */
   setBehavior: function (type) {
+    type = type.toUpperCase();
     this._behavior |=
-      Ci.mozIPlacesAutoComplete["BEHAVIOR_" + type.toUpperCase()];
+      Ci.mozIPlacesAutoComplete["BEHAVIOR_" + type];
+
+    // Setting the "typed" behavior should also set the "history" behavior.
+    if (type == "TYPED") {
+      this.setBehavior("history");
+    }
   },
 
   /**
    * Determines if the specified AutoComplete behavior is set.
    *
    * @param aType
    *        The behavior type to test for.
    * @return true if the behavior is set, false otherwise.
@@ -601,22 +652,31 @@ Search.prototype = {
    * Given an array of tokens, this function determines which query should be
    * ran.  It also removes any special search tokens.
    *
    * @param tokens
    *        An array of search tokens.
    * @return the filtered list of tokens to search with.
    */
   filterTokens: function (tokens) {
+    let foundToken = false;
     // Set the proper behavior while filtering tokens.
     for (let i = tokens.length - 1; i >= 0; i--) {
       let behavior = Prefs.tokenToBehaviorMap.get(tokens[i]);
       // Don't remove the token if it didn't match, or if it's an action but
       // actions are not enabled.
       if (behavior && (behavior != "openpage" || this._enableActions)) {
+        // Don't use the suggest preferences if it is a token search and
+        // set the restrict bit to 1 (to intersect the search results).
+        if (!foundToken) {
+          foundToken = true;
+          // Do not take into account previous behavior (e.g.: history, bookmark)
+          this._behavior = 0;
+          this.setBehavior("restrict");
+        }
         this.setBehavior(behavior);
         tokens.splice(i, 1);
       }
     }
 
     // Set the right JavaScript behavior based on our preference.  Note that the
     // preference is whether or not we should filter JavaScript, and the
     // behavior is if we should search it or not.
@@ -684,19 +744,24 @@ Search.prototype = {
     // enabled, the first result is always a special result (resulting from one
     // of the queries between (1) and (6) inclusive). As such, the UI is
     // expected to auto-select the first result when actions are enabled. If the
     // first result is an inline completion result, that will also be the
     // default result and therefore be autofilled (this also happens if actions
     // are not enabled).
 
     // Get the final query, based on the tokens found in the search string.
-    let queries = [ this._adaptiveQuery,
-                    this._switchToTabQuery,
-                    this._searchQuery ];
+    let queries = [ this._adaptiveQuery ];
+
+    // "openpage" behavior is supported by the default query.
+    // _switchToTabQuery instead returns only pages not supported by history.
+    if (this.hasBehavior("openpage")) {
+      queries.push(this._switchToTabQuery);
+    }
+    queries.push(this._searchQuery);
 
     // When actions are enabled, we run a series of heuristics to determine what
     // the first result should be - which is always a special result.
     // |hasFirstResult| is used to keep track of whether we've obtained such a
     // result yet, so we can skip further heuristics and not add any additional
     // special results.
     let hasFirstResult = false;
 
@@ -1153,17 +1218,17 @@ Search.prototype = {
       row.getResultByIndex(QUERYINDEX_BOOKMARKTITLE) : null;
     let tags = row.getResultByIndex(QUERYINDEX_TAGS) || "";
     let frecency = row.getResultByIndex(QUERYINDEX_FRECENCY);
 
     // If actions are enabled and the page is open, add only the switch-to-tab
     // result.  Otherwise, add the normal result.
     let url = escapedURL;
     let action = null;
-    if (this._enableActions && openPageCount > 0) {
+    if (this._enableActions && openPageCount > 0 && this.hasBehavior("openpage")) {
       url = makeActionURL("switchtab", {url: escapedURL});
       action = "switchtab";
     }
 
     // Always prefer the bookmark title unless it is empty
     let title = bookmarkTitle || historyTitle;
 
     if (queryType == QUERYTYPE_KEYWORD) {
@@ -1186,20 +1251,20 @@ Search.prototype = {
           title = historyTitle;
         }
       }
     }
 
     // We will always prefer to show tags if we have them.
     let showTags = !!tags;
 
-    // However, we'll act as if a page is not bookmarked or tagged if the user
-    // only wants only history and not bookmarks or tags.
-    if (this.hasBehavior("history") &&
-        !(this.hasBehavior("bookmark") || this.hasBehavior("tag"))) {
+    // However, we'll act as if a page is not bookmarked if the user wants
+    // only history and not bookmarks and there are no tags.
+    if (this.hasBehavior("history") && !this.hasBehavior("bookmark") &&
+        !showTags) {
       showTags = false;
       match.style = "favicon";
     }
 
     // If we have tags and should show them, we need to add them to the title.
     if (showTags) {
       title += TITLE_TAGS_SEPARATOR + tags;
     }
@@ -1229,34 +1294,55 @@ Search.prototype = {
                               .getFaviconLinkForIcon(NetUtil.newURI(iconurl)).spec;
     }
     match.frecency = frecency;
 
     return match;
   },
 
   /**
+   * @return a string consisting of the search query to be used based on the
+   * previously set urlbar suggestion preferences.
+   */
+  get _suggestionPrefQuery() {
+    if (!this.hasBehavior("restrict") && this.hasBehavior("history") &&
+        this.hasBehavior("bookmark")) {
+      return this.hasBehavior("typed") ? defaultQuery("AND h.typed = 1")
+                                       : defaultQuery();
+    }
+    let conditions = [];
+    if (this.hasBehavior("history")) {
+      // Enforce ignoring the visit_count index, since the frecency one is much
+      // faster in this case.  ANALYZE helps the query planner to figure out the
+      // faster path, but it may not have up-to-date information yet.
+      conditions.push("+h.visit_count > 0");
+    }
+    if (this.hasBehavior("typed")) {
+      conditions.push("h.typed = 1");
+    }
+    if (this.hasBehavior("bookmark")) {
+      conditions.push("bookmarked");
+    }
+    if (this.hasBehavior("tag")) {
+      conditions.push("tags NOTNULL");
+    }
+
+    return conditions.length ? defaultQuery("AND " + conditions.join(" AND "))
+                             : defaultQuery();
+  },
+
+  /**
    * Obtains the search query to be used based on the previously set search
-   * behaviors (accessed by this.hasBehavior).
+   * preferences (accessed by this.hasBehavior).
    *
    * @return an array consisting of the correctly optimized query to search the
    *         database with and an object containing the params to bound.
    */
   get _searchQuery() {
-    // We use more optimized queries for restricted searches, so we will always
-    // return the most restrictive one to the least restrictive one if more than
-    // one token is found.
-    // Note: "openpages" behavior is supported by the default query.
-    //       _switchToTabQuery instead returns only pages not supported by
-    //       history and it is always executed.
-    let query = this.hasBehavior("tag") ? SQL_TAGS_QUERY :
-                this.hasBehavior("bookmark") ? SQL_BOOKMARK_QUERY :
-                this.hasBehavior("typed") ? SQL_TYPED_QUERY :
-                this.hasBehavior("history") ? SQL_HISTORY_QUERY :
-                SQL_DEFAULT_QUERY;
+    let query = this._suggestionPrefQuery;
 
     return [
       query,
       {
         parent: PlacesUtils.tagsFolderId,
         query_type: QUERYTYPE_FILTERED,
         matchBehavior: this._matchBehavior,
         searchBehavior: this._behavior,
@@ -1343,31 +1429,24 @@ Search.prototype = {
   get _shouldAutofill() {
     // First of all, check for the autoFill pref.
     if (!Prefs.autofill)
       return false;
 
     if (!this._searchTokens.length == 1)
       return false;
 
-    // Then, we should not try to autofill if the behavior is not the default.
-    // TODO (bug 751709): Ideally we should have a more fine-grained behavior
-    // here, but for now it's enough to just check for default behavior.
-    if (Prefs.defaultBehavior != DEFAULT_BEHAVIOR) {
-      // autoFill can only cope with history or bookmarks entries
-      // (typed or not).
-      if (!this.hasBehavior("typed") &&
-          !this.hasBehavior("history") &&
-          !this.hasBehavior("bookmark"))
-        return false;
+    // autoFill can only cope with history or bookmarks entries.
+    if (!this.hasBehavior("history") &&
+        !this.hasBehavior("bookmark"))
+      return false;
 
-      // autoFill doesn't search titles or tags.
-      if (this.hasBehavior("title") || this.hasBehavior("tags"))
-        return false;
-    }
+    // autoFill doesn't search titles or tags.
+    if (this.hasBehavior("title") || this.hasBehavior("tag"))
+      return false;
 
     // Don't try to autofill if the search term includes any whitespace.
     // This may confuse completeDefaultIndex cause the AUTOCOMPLETE_MATCH
     // tokenizer ends up trimming the search string and returning a value
     // that doesn't match it, or is even shorter.
     if (/\s/.test(this._originalSearchString))
       return false;
 
@@ -1380,17 +1459,17 @@ Search.prototype = {
   /**
    * Obtains the query to search for autoFill host results.
    *
    * @return an array consisting of the correctly optimized query to search the
    *         database with and an object containing the params to bound.
    */
   get _hostQuery() {
     let typed = Prefs.autofillTyped || this.hasBehavior("typed");
-    let bookmarked =  this.hasBehavior("bookmark");
+    let bookmarked = this.hasBehavior("bookmark") && !this.hasBehavior("history");
 
     return [
       bookmarked ? typed ? SQL_BOOKMARKED_TYPED_HOST_QUERY
                          : SQL_BOOKMARKED_HOST_QUERY
                  : typed ? SQL_TYPED_HOST_QUERY
                          : SQL_HOST_QUERY,
       {
         query_type: QUERYTYPE_AUTOFILL_HOST,
@@ -1425,28 +1504,42 @@ Search.prototype = {
   /**
    * Obtains the query to search for autoFill url results.
    *
    * @return an array consisting of the correctly optimized query to search the
    *         database with and an object containing the params to bound.
    */
   get _urlQuery()  {
     let typed = Prefs.autofillTyped || this.hasBehavior("typed");
-    let bookmarked =  this.hasBehavior("bookmark");
+    let bookmarked = this.hasBehavior("bookmark") && !this.hasBehavior("history");
+    let searchBehavior = Ci.mozIPlacesAutoComplete.BEHAVIOR_URL;
+
+    // Enable searches in typed history if autofillTyped pref or typed behavior
+    // is true.
+    if (typed) {
+      searchBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY |
+                        Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED;
+    } else {
+      // Search in entire history.
+      searchBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY;
+    }
+    if (bookmarked) {
+      searchBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_BOOKMARK;
+    }
 
     return [
       bookmarked ? typed ? SQL_BOOKMARKED_TYPED_URL_QUERY
                          : SQL_BOOKMARKED_URL_QUERY
                  : typed ? SQL_TYPED_URL_QUERY
                          : SQL_URL_QUERY,
       {
         query_type: QUERYTYPE_AUTOFILL_URL,
         searchString: this._autofillUrlSearchString,
         matchBehavior: MATCH_BEGINNING_CASE_SENSITIVE,
-        searchBehavior: Ci.mozIPlacesAutoComplete.BEHAVIOR_URL
+        searchBehavior: searchBehavior
       }
     ];
   },
 
  /**
    * Notifies the listener about results.
    *
    * @param searchOngoing
@@ -1463,16 +1556,21 @@ Search.prototype = {
   },
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 //// UnifiedComplete class
 //// component @mozilla.org/autocomplete/search;1?name=unifiedcomplete
 
 function UnifiedComplete() {
+  // Make sure the preferences are initialized as soon as possible.
+  // If the value of browser.urlbar.autocomplete.enabled is set to false,
+  // then all the other suggest preferences for history, bookmarks and
+  // open pages should be set to false.
+  Prefs;
 }
 
 UnifiedComplete.prototype = {
   //////////////////////////////////////////////////////////////////////////////
   //// Database handling
 
   /**
    * Promise resolved when the database initialization has completed, or null
--- a/toolkit/components/places/mozIPlacesAutoComplete.idl
+++ b/toolkit/components/places/mozIPlacesAutoComplete.idl
@@ -8,17 +8,17 @@
 
 interface nsIURI;
 
 /**
  * This interface provides some constants used by the Places AutoComplete
  * search provider as well as methods to track opened pages for AutoComplete
  * purposes.
  */
-[scriptable, uuid(3bf895a0-d6d9-4ba7-b8db-f2f0e0f32d23)]
+[scriptable, uuid(6e252399-77f9-4f11-98cc-6fa9fab96f92)]
 interface mozIPlacesAutoComplete : nsISupports
 {
   //////////////////////////////////////////////////////////////////////////////
   //// Matching Constants
 
   /**
    * Match anywhere in each searchable term.
    */
@@ -92,16 +92,22 @@ interface mozIPlacesAutoComplete : nsISu
 
   /**
    * Search for pages that have been marked as being opened, such as a tab
    * in a tabbrowser.
    */
   const long BEHAVIOR_OPENPAGE = 1 << 7;
 
   /**
+   * Use intersection between history, typed, bookmark, tag and openpage
+   * instead of union, when the restrict bit is set.
+   */
+  const long BEHAVIOR_RESTRICT = 1 << 8;
+
+  /**
    * Mark a page as being currently open.
    *
    * @note Pages will not be automatically unregistered when Private Browsing
    *       mode is entered or exited.  Therefore, consumers MUST unregister or
    *       register themselves.
    *
    * @param aURI
    *        The URI to register as an open page.
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -3268,23 +3268,27 @@ nsNavHistory::QueryToSelectClause(nsNavH
     // end time
     if (NS_SUCCEEDED(aQuery->GetHasEndTime(&hasIt)) && hasIt)
       clause.Condition("visit_date <=").Param(":end_time");
     clause.Str(" LIMIT 1)");
   }
 
   // search terms
   bool hasSearchTerms;
+  int32_t searchBehavior = mozIPlacesAutoComplete::BEHAVIOR_HISTORY |
+                           mozIPlacesAutoComplete::BEHAVIOR_BOOKMARK;
   if (NS_SUCCEEDED(aQuery->GetHasSearchTerms(&hasSearchTerms)) && hasSearchTerms) {
-    // Re-use the autocomplete_match function.  Setting the behavior to 0
-    // it can match everything and work as a nice case insensitive comparator.
+    // Re-use the autocomplete_match function.  Setting the behavior to match
+    // history or typed history or bookmarks or open pages will match almost
+    // everything.
     clause.Condition("AUTOCOMPLETE_MATCH(").Param(":search_string")
           .Str(", h.url, page_title, tags, ")
-          .Str(nsPrintfCString("0, 0, 0, 0, %d, 0)",
-                               mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED).get());
+          .Str(nsPrintfCString("1, 1, 1, 1, %d, %d)",
+                               mozIPlacesAutoComplete::MATCH_ANYWHERE_UNMODIFIED,
+                               searchBehavior).get());
     // Serching by terms implicitly exclude queries.
     excludeQueries = true;
   }
 
   // min and max visit count
   if (aQuery->MinVisits() >= 0)
     clause.Condition("h.visit_count >=").Param(":min_visits");
 
@@ -3372,16 +3376,18 @@ nsNavHistory::QueryToSelectClause(nsNavH
                              "WHERE visit_type = ")
           .Param(param.get())
           .Str(")");
   }
 
   // folders
   const nsTArray<int64_t>& folders = aQuery->Folders();
   if (folders.Length() > 0) {
+    aOptions->SetQueryType(nsNavHistoryQueryOptions::QUERY_TYPE_BOOKMARKS);
+
     nsTArray<int64_t> includeFolders;
     includeFolders.AppendElements(folders);
 
     nsNavBookmarks* bookmarks = nsNavBookmarks::GetBookmarksService();
     NS_ENSURE_STATE(bookmarks);
 
     for (nsTArray<int64_t>::size_type i = 0; i < folders.Length(); ++i) {
       nsTArray<int64_t> subFolders;
--- a/toolkit/components/places/nsPlacesAutoComplete.js
+++ b/toolkit/components/places/nsPlacesAutoComplete.js
@@ -340,16 +340,20 @@ function nsPlacesAutoComplete()
       stmt.executeAsync();
       stmt.finalize();
       delete this._openPagesCache;
     }
 
     return db;
   });
 
+  this._customQuery = (conditions = "") => {
+    return this._db.createAsyncStatement(baseQuery(conditions));
+  };
+
   XPCOMUtils.defineLazyGetter(this, "_defaultQuery", function() {
     return this._db.createAsyncStatement(baseQuery());
   });
 
   XPCOMUtils.defineLazyGetter(this, "_historyQuery", function() {
     // Enforce ignoring the visit_count index, since the frecency one is much
     // faster in this case.  ANALYZE helps the query planner to figure out the
     // faster path, but it may not have run yet.
@@ -458,16 +462,17 @@ function nsPlacesAutoComplete()
 
   //////////////////////////////////////////////////////////////////////////////
   //// Initialization
 
   // load preferences
   this._prefs = Cc["@mozilla.org/preferences-service;1"].
                 getService(Ci.nsIPrefService).
                 getBranch(kBrowserUrlbarBranch);
+  this._syncEnabledPref(true);
   this._loadPrefs(true);
 
   // register observers
   this._os = Cc["@mozilla.org/observer-service;1"].
               getService(Ci.nsIObserverService);
   this._os.addObserver(this, kTopicShutdown, false);
 
 }
@@ -521,18 +526,23 @@ nsPlacesAutoComplete.prototype = {
     // 3) open pages not supported by history (this._openPagesQuery)
     // 4) query from this._getSearch
     // (1) only gets ran if we get any filtered tokens from this._getSearch,
     // since if there are no tokens, there is nothing to match, so there is no
     // reason to run the query).
     let {query, tokens} =
       this._getSearch(this._getUnfilteredSearchTokens(this._currentSearchString));
     let queries = tokens.length ?
-      [this._getBoundKeywordQuery(tokens), this._getBoundAdaptiveQuery(), this._getBoundOpenPagesQuery(tokens), query] :
-      [this._getBoundAdaptiveQuery(), this._getBoundOpenPagesQuery(tokens), query];
+      [this._getBoundKeywordQuery(tokens), this._getBoundAdaptiveQuery()] :
+      [this._getBoundAdaptiveQuery()];
+
+    if (this._hasBehavior("openpage")) {
+      queries.push(this._getBoundOpenPagesQuery(tokens));
+    }
+    queries.push(query);
 
     // Start executing our queries.
     this._telemetryStartTime = Date.now();
     this._executeQueries(queries);
 
     // Set up our persistent state for the duration of the search.
     this._searchTokens = tokens;
     this._usedPlaces = {};
@@ -683,17 +693,17 @@ nsPlacesAutoComplete.prototype = {
         }
       }
 
       if (this._databaseInitialized) {
         this._db.asyncClose();
       }
     }
     else if (aTopic == kPrefChanged) {
-      this._loadPrefs();
+      this._loadPrefs(aSubject, aTopic, aData);
     }
   },
 
   //////////////////////////////////////////////////////////////////////////////
   //// nsPlacesAutoComplete
 
   get _databaseInitialized()
     Object.getOwnPropertyDescriptor(this, "_db").value !== undefined,
@@ -792,23 +802,61 @@ nsPlacesAutoComplete.prototype = {
           Components.utils.reportError("Unable to report telemetry.");
         }
       }
       this._telemetryStartTime = null;
     }
   },
 
   /**
+   * Synchronize suggest.* prefs with autocomplete.enabled.
+   */
+  _syncEnabledPref: function PAC_syncEnabledPref(init = false)
+  {
+    let suggestPrefs = ["suggest.history", "suggest.bookmark", "suggest.openpage"];
+    let types = ["History", "Bookmark", "Openpage", "Typed"];
+
+    if (init) {
+      // Make sure to initialize the properties when first called with init = true.
+      this._enabled = safePrefGetter(this._prefs, kBrowserUrlbarAutocompleteEnabledPref,
+                                     true);
+      this._suggestHistory = safePrefGetter(this._prefs, "suggest.history", true);
+      this._suggestBookmark = safePrefGetter(this._prefs, "suggest.bookmark", true);
+      this._suggestOpenpage = safePrefGetter(this._prefs, "suggest.openpage", true);
+      this._suggestTyped = safePrefGetter(this._prefs, "suggest.history.onlyTyped", false);
+    }
+
+    if (this._enabled) {
+      // If the autocomplete preference is active, activate all suggest
+      // preferences only if all of them are false.
+      if (types.every(type => this["_suggest" + type] == false)) {
+        for (let type of suggestPrefs) {
+          this._prefs.setBoolPref(type, true);
+        }
+      }
+    } else {
+      // If the preference was deactivated, deactivate all suggest preferences.
+      for (let type of suggestPrefs) {
+        this._prefs.setBoolPref(type, false);
+      }
+    }
+  },
+
+  /**
    * Loads the preferences that we care about.
    *
    * @param [optional] aRegisterObserver
    *        Indicates if the preference observer should be added or not.  The
    *        default value is false.
+   * @param [optional] aTopic
+   *        Observer's topic, if any.
+   * @param [optional] aSubject
+   *        Observer's subject, if any.
    */
-  _loadPrefs: function PAC_loadPrefs(aRegisterObserver)
+  _loadPrefs: function PAC_loadPrefs(aRegisterObserver, aTopic, aData)
   {
     this._enabled = safePrefGetter(this._prefs,
                                    kBrowserUrlbarAutocompleteEnabledPref,
                                    true);
     this._matchBehavior = safePrefGetter(this._prefs,
                                          "matchBehavior",
                                          MATCH_BOUNDARY_ANYWHERE);
     this._filterJavaScript = safePrefGetter(this._prefs, "filter.javascript", true);
@@ -818,76 +866,115 @@ nsPlacesAutoComplete.prototype = {
     this._restrictBookmarkToken = safePrefGetter(this._prefs,
                                                  "restrict.bookmark", "*");
     this._restrictTypedToken = safePrefGetter(this._prefs, "restrict.typed", "~");
     this._restrictTagToken = safePrefGetter(this._prefs, "restrict.tag", "+");
     this._restrictOpenPageToken = safePrefGetter(this._prefs,
                                                  "restrict.openpage", "%");
     this._matchTitleToken = safePrefGetter(this._prefs, "match.title", "#");
     this._matchURLToken = safePrefGetter(this._prefs, "match.url", "@");
-    this._defaultBehavior = safePrefGetter(this._prefs, "default.behavior", 0);
+
+    this._suggestHistory = safePrefGetter(this._prefs, "suggest.history", true);
+    this._suggestBookmark = safePrefGetter(this._prefs, "suggest.bookmark", true);
+    this._suggestOpenpage = safePrefGetter(this._prefs, "suggest.openpage", true);
+    this._suggestTyped = safePrefGetter(this._prefs, "suggest.history.onlyTyped", false);
+
+    // If history is not set, onlyTyped value should be ignored.
+    if (!this._suggestHistory) {
+      this._suggestTyped = false;
+    }
+    let types = ["History", "Bookmark", "Openpage", "Typed"];
+    this._defaultBehavior = types.reduce((memo, type) => {
+      let prefValue = this["_suggest" + type];
+      return memo | (prefValue &&
+                     Ci.mozIPlacesAutoComplete["BEHAVIOR_" + type.toUpperCase()]);
+    }, 0);
+
     // Further restrictions to apply for "empty searches" (i.e. searches for "").
-    this._emptySearchDefaultBehavior =
-      this._defaultBehavior |
-      safePrefGetter(this._prefs, "default.behavior.emptyRestriction",
-                     Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY |
-                     Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED);
+    // The empty behavior is typed history, if history is enabled. Otherwise,
+    // it is bookmarks, if they are enabled. If both history and bookmarks are disabled,
+    // it defaults to open pages.
+    this._emptySearchDefaultBehavior = Ci.mozIPlacesAutoComplete.BEHAVIOR_RESTRICT;
+    if (this._suggestHistory) {
+      this._emptySearchDefaultBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY |
+                                          Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED;
+    } else if (this._suggestBookmark) {
+      this._emptySearchDefaultBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_BOOKMARK;
+    } else {
+      this._emptySearchDefaultBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_OPENPAGE;
+    }
 
     // Validate matchBehavior; default to MATCH_BOUNDARY_ANYWHERE.
     if (this._matchBehavior != MATCH_ANYWHERE &&
         this._matchBehavior != MATCH_BOUNDARY &&
         this._matchBehavior != MATCH_BEGINNING) {
       this._matchBehavior = MATCH_BOUNDARY_ANYWHERE;
     }
     // register observer
     if (aRegisterObserver) {
       this._prefs.addObserver("", this, false);
     }
+
+    // Synchronize suggest.* prefs with autocomplete.enabled every time
+    // autocomplete.enabled is changed.
+    if (aData == kBrowserUrlbarAutocompleteEnabledPref) {
+      this._syncEnabledPref();
+    }
   },
 
   /**
    * Given an array of tokens, this function determines which query should be
    * ran.  It also removes any special search tokens.
    *
    * @param aTokens
    *        An array of search tokens.
    * @return an object with two properties:
    *         query: the correctly optimized, bound query to search the database
    *                with.
    *         tokens: the filtered list of tokens to search with.
    */
   _getSearch: function PAC_getSearch(aTokens)
   {
+    let foundToken = false;
+    let restrict = (behavior) => {
+      if (!foundToken) {
+        this._behavior = 0;
+        this._setBehavior("restrict");
+        foundToken = true;
+      }
+      this._setBehavior(behavior);
+    };
+
     // Set the proper behavior so our call to _getBoundSearchQuery gives us the
     // correct query.
     for (let i = aTokens.length - 1; i >= 0; i--) {
       switch (aTokens[i]) {
         case this._restrictHistoryToken:
-          this._setBehavior("history");
+          restrict("history");
           break;
         case this._restrictBookmarkToken:
-          this._setBehavior("bookmark");
+          restrict("bookmark");
           break;
         case this._restrictTagToken:
-          this._setBehavior("tag");
+          restrict("tag");
           break;
         case this._restrictOpenPageToken:
           if (!this._enableActions) {
             continue;
           }
-          this._setBehavior("openpage");
+          restrict("openpage");
           break;
         case this._matchTitleToken:
-          this._setBehavior("title");
+          restrict("title");
           break;
         case this._matchURLToken:
-          this._setBehavior("url");
+          restrict("url");
           break;
         case this._restrictTypedToken:
-          this._setBehavior("typed");
+          restrict("typed");
           break;
         default:
           // We do not want to remove the token if we did not match.
           continue;
       };
 
       aTokens.splice(i, 1);
     }
@@ -901,43 +988,65 @@ nsPlacesAutoComplete.prototype = {
 
     return {
       query: this._getBoundSearchQuery(this._matchBehavior, aTokens),
       tokens: aTokens
     };
   },
 
   /**
+   * @return a string consisting of the search query to be used based on the
+   * previously set urlbar suggestion preferences.
+   */
+  _getSuggestionPrefQuery: function PAC_getSuggestionPrefQuery()
+  {
+    if (!this._hasBehavior("restrict") && this._hasBehavior("history") &&
+        this._hasBehavior("bookmark")) {
+      return this._hasBehavior("typed") ? this._customQuery("AND h.typed = 1")
+                                        : this._defaultQuery;
+    }
+    let conditions = [];
+    if (this._hasBehavior("history")) {
+      // Enforce ignoring the visit_count index, since the frecency one is much
+      // faster in this case.  ANALYZE helps the query planner to figure out the
+      // faster path, but it may not have up-to-date information yet.
+      conditions.push("+h.visit_count > 0");
+    }
+    if (this._hasBehavior("typed")) {
+      conditions.push("h.typed = 1");
+    }
+    if (this._hasBehavior("bookmark")) {
+      conditions.push("bookmarked");
+    }
+    if (this._hasBehavior("tag")) {
+      conditions.push("tags NOTNULL");
+    }
+
+    return conditions.length ? this._customQuery("AND " + conditions.join(" AND "))
+                             : this._defaultQuery;
+  },
+
+  /**
    * Obtains the search query to be used based on the previously set search
    * behaviors (accessed by this._hasBehavior).  The query is bound and ready to
    * execute.
    *
    * @param aMatchBehavior
    *        How this query should match its tokens to the search string.
    * @param aTokens
    *        An array of search tokens.
    * @return the correctly optimized query to search the database with and the
    *         new list of tokens to search with.  The query has all the needed
    *         parameters bound, so consumers can execute it without doing any
    *         additional work.
    */
   _getBoundSearchQuery: function PAC_getBoundSearchQuery(aMatchBehavior,
                                                          aTokens)
   {
-    // We use more optimized queries for restricted searches, so we will always
-    // return the most restrictive one to the least restrictive one if more than
-    // one token is found.
-    // Note: "openpages" behavior is supported by the default query.
-    //       _openPagesQuery instead returns only pages not supported by
-    //       history and it is always executed.
-    let query = this._hasBehavior("tag") ? this._tagsQuery :
-                this._hasBehavior("bookmark") ? this._bookmarkQuery :
-                this._hasBehavior("typed") ? this._typedQuery :
-                this._hasBehavior("history") ? this._historyQuery :
-                this._defaultQuery;
+    let query = this._getSuggestionPrefQuery();
 
     // Bind the needed parameters to the query so consumers can use it.
     let (params = query.params) {
       params.parent = PlacesUtils.tagsFolderId;
       params.query_type = kQueryTypeFiltered;
       params.matchBehavior = aMatchBehavior;
       params.searchBehavior = this._behavior;
 
@@ -1041,17 +1150,17 @@ nsPlacesAutoComplete.prototype = {
   {
     // Before we do any work, make sure this entry isn't already in our results.
     let entryId = aRow.getResultByIndex(kQueryIndexPlaceId);
     let escapedEntryURL = aRow.getResultByIndex(kQueryIndexURL);
     let openPageCount = aRow.getResultByIndex(kQueryIndexOpenPageCount) || 0;
 
     // If actions are enabled and the page is open, add only the switch-to-tab
     // result.  Otherwise, add the normal result.
-    let [url, action] = this._enableActions && openPageCount > 0 ?
+    let [url, action] = this._enableActions && openPageCount > 0 && this._hasBehavior("openpage") ?
                         ["moz-action:switchtab," + escapedEntryURL, "action "] :
                         [escapedEntryURL, ""];
 
     if (this._inResults(entryId, url)) {
       return false;
     }
 
     let entryTitle = aRow.getResultByIndex(kQueryIndexTitle) || "";
@@ -1076,20 +1185,20 @@ nsPlacesAutoComplete.prototype = {
       else {
         title = entryTitle;
       }
     }
 
     // We will always prefer to show tags if we have them.
     let showTags = !!entryTags;
 
-    // However, we'll act as if a page is not bookmarked or tagged if the user
-    // only wants only history and not bookmarks or tags.
-    if (this._hasBehavior("history") &&
-        !(this._hasBehavior("bookmark") || this._hasBehavior("tag"))) {
+    // However, we'll act as if a page is not bookmarked if the user wants
+    // only history and not bookmarks and there are no tags.
+    if (this._hasBehavior("history") && !this._hasBehavior("bookmark") &&
+        !showTags) {
       showTags = false;
       style = "favicon";
     }
 
     // If we have tags and should show them, we need to add them to the title.
     if (showTags) {
       title += kTitleTagsSeparator + entryTags;
     }
@@ -1427,17 +1536,19 @@ urlInlineComplete.prototype = {
       this._originalSearchString.slice(pathIndex)
     );
 
     // Within the standard autocomplete query, we only search the beginning
     // of URLs for 1 result.
     let query = this._urlQuery;
     let (params = query.params) {
       params.matchBehavior = MATCH_BEGINNING_CASE_SENSITIVE;
-      params.searchBehavior = Ci.mozIPlacesAutoComplete["BEHAVIOR_URL"];
+      params.searchBehavior |= Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY |
+                               Ci.mozIPlacesAutoComplete.BEHAVIOR_TYPED |
+                               Ci.mozIPlacesAutoComplete.BEHAVIOR_URL;
       params.searchString = this._currentSearchString;
     }
 
     // Execute the query.
     let ac = this;
     let wrapper = new AutoCompleteStatementCallbackWrapper(this, {
       handleResult: function(aResultSet) {
         let row = aResultSet.getNextRow();
--- a/toolkit/components/places/tests/autocomplete/head_autocomplete.js
+++ b/toolkit/components/places/tests/autocomplete/head_autocomplete.js
@@ -241,18 +241,20 @@ function task_addPageBook(aURI, aTitle, 
   }
 
   print("\nAdding page/book/tag: " + out.join(", "));
 }
 
 function run_test() {
   print("\n");
   // always search in history + bookmarks, no matter what the default is
-  prefs.setIntPref("browser.urlbar.search.sources", 3);
-  prefs.setIntPref("browser.urlbar.default.behavior", 0);
+  prefs.setBoolPref("browser.urlbar.suggest.history", true);
+  prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
+  prefs.setBoolPref("browser.urlbar.suggest.openpage", true);
+  prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", false);
 
   // Search is asynchronous, so don't let the test finish immediately
   do_test_pending();
 
   // Load the test and print a description then run the test
   let [description, search, expected, func] = gTests[current_test];
   print(description);
 
--- a/toolkit/components/places/tests/autocomplete/test_empty_search.js
+++ b/toolkit/components/places/tests/autocomplete/test_empty_search.js
@@ -39,25 +39,31 @@ removePages([4,5]);
 // pages that should match; optional function to be run before the test
 let gTests = [
   ["0: Match everything",
    "foo", [0,1,2,3,4,5]],
   ["1: Match only typed history",
    "foo ^ ~", [2,3]],
   ["2: Drop-down empty search matches only typed history",
    "", [2,3]],
-  ["3: Drop-down empty search matches everything",
-   "", [0,1,2,3,4,5], function () setEmptyPref(0)],
+  ["3: Drop-down empty search matches only bookmarks",
+   "", [2,3], matchBookmarks],
   ["4: Drop-down empty search matches only typed",
-   "", [2,3,5], function () setEmptyPref(32)],
-  ["5: Drop-down empty search matches only typed history",
-   "", [2,3], clearEmptyPref],
+   "", [2,3], matchTyped],
 ];
 
-function setEmptyPref(aValue)
-  prefs.setIntPref("browser.urlbar.default.behavior.emptyRestriction", aValue);
-
-function clearEmptyPref()
-{
-  if (prefs.prefHasUserValue("browser.urlbar.default.behavior.emptyRestriction"))
-    prefs.clearUserPref("browser.urlbar.default.behavior.emptyRestriction");
+function matchBookmarks() {
+  prefs.setBoolPref("browser.urlbar.suggest.history", false);
+  prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
+  clearPrefs();
 }
 
+function matchTyped() {
+  prefs.setBoolPref("browser.urlbar.suggest.history", true);
+  prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", true);
+  clearPrefs();
+}
+
+function clearPrefs() {
+  prefs.clearUserPref("browser.urlbar.suggest.history");
+  prefs.clearUserPref("browser.urlbar.suggest.bookmark");
+  prefs.clearUserPref("browser.urlbar.suggest.history.onlyTyped");
+}
--- a/toolkit/components/places/tests/autocomplete/test_special_search.js
+++ b/toolkit/components/places/tests/autocomplete/test_special_search.js
@@ -55,35 +55,35 @@ removePages([4,6,7,8,9,11]);
 markTyped([0,10], 0);
 markTyped([3], 1);
 
 // Provide for each test: description; search terms; array of gPages indices of
 // pages that should match; optional function to be run before the test
 let gTests = [
   // Test restricting searches
   ["0: History restrict",
-   "^", [0,1,2,3,5,10], ignoreTags],
+   "^", [0,1,2,3,5,10]],
   ["1: Star restrict",
    "*", [4,5,6,7,8,9,10,11]],
   ["2: Tag restrict",
    "+", [8,9,10,11]],
 
   // Test specials as any word position
   ["3: Special as first word",
-   "^ foo bar", [1,2,3,5,10], ignoreTags],
+   "^ foo bar", [1,2,3,5,10]],
   ["4: Special as middle word",
-   "foo ^ bar", [1,2,3,5,10], ignoreTags],
+   "foo ^ bar", [1,2,3,5,10]],
   ["5: Special as last word",
-   "foo bar ^", [1,2,3,5,10], ignoreTags],
+   "foo bar ^", [1,2,3,5,10]],
 
   // Test restricting and matching searches with a term
   ["6.1: foo ^ -> history",
-   "foo ^", [1,2,3,5,10], ignoreTags],
+   "foo ^", [1,2,3,5,10]],
   ["6.2: foo | -> history (change pref)",
-   "foo |", [1,2,3,5,10], function() {ignoreTags(); changeRestrict("history", "|"); }],
+   "foo |", [1,2,3,5,10], function() { changeRestrict("history", "|"); }],
   ["7.1: foo * -> is star",
    "foo *", [5,6,7,8,9,10,11], function() resetRestrict("history")],
   ["7.2: foo | -> is star (change pref)",
    "foo |", [5,6,7,8,9,10,11], function() changeRestrict("bookmark", "|")],
   ["8.1: foo # -> in title",
    "foo #", [1,3,5,7,8,9,10,11], function() resetRestrict("bookmark")],
   ["8.2: foo | -> in title (change pref)",
    "foo |", [1,3,5,7,8,9,10,11], function() changeRestrict("title", "|")],
@@ -99,23 +99,23 @@ let gTests = [
    "foo ~", [3,10], function() resetRestrict("tag")],
   ["10.4: foo | -> is typed (change pref)",
    "foo |", [3,10], function() changeRestrict("typed", "|")],
 
   // Test various pairs of special searches
   ["11: foo ^ * -> history, is star",
    "foo ^ *", [5,10], function() resetRestrict("typed")],
   ["12: foo ^ # -> history, in title",
-   "foo ^ #", [1,3,5,10], ignoreTags],
+   "foo ^ #", [1,3,5,10]],
   ["13: foo ^ @ -> history, in url",
-   "foo ^ @", [2,3,10], ignoreTags],
+   "foo ^ @", [2,3,10]],
   ["14: foo ^ + -> history, is tag",
    "foo ^ +", [10]],
   ["14.1: foo ^ ~ -> history, is typed",
-   "foo ^ ~", [3,10], ignoreTags],
+   "foo ^ ~", [3,10]],
   ["15: foo * # -> is star, in title",
    "foo * #", [5,7,8,9,10,11]],
   ["16: foo * @ -> is star, in url",
    "foo * @", [6,7,10,11]],
   ["17: foo * + -> same as +",
    "foo * +", [8,9,10,11]],
   ["17.1: foo * ~ -> is star, is typed",
    "foo * ~", [10]],
@@ -129,37 +129,36 @@ let gTests = [
    "foo @ +", [10,11]],
   ["20.1: foo @ ~ -> in url, is typed",
    "foo @ ~", [3,10]],
   ["20.2: foo + ~ -> is tag, is typed",
    "foo + ~", [10]],
 
   // Test default usage by setting certain bits of default.behavior to 1
   ["21: foo -> default history",
-   "foo", [1,2,3,5,10], function() makeDefault(1)],
-  ["22: foo -> default history, is star",
-   "foo", [5,10], function() makeDefault(3)],
-  ["22.1: foo -> default history, is star, is typed",
-   "foo", [10], function() makeDefault(35)],
-  ["23: foo -> default history, is star, in url",
-   "foo", [10], function() makeDefault(19)],
+   "foo", [1,2,3,5,10], function () { setPref({ history: true }); }],
+  ["22: foo -> default history or is star",
+   "foo", [1,2,3,5,6,7,8,9,10,11], function() setPref({ history: true, bookmark: true })],
+  ["22.1: foo -> default history or is star, is typed",
+   "foo", [3,10], function() setPref({ history: true, bookmark: true, "history.onlyTyped": true })],
 
-  // Change the default to be less restrictive to make sure we find more
-  ["24: foo -> default is star, in url",
-   "foo", [6,7,10,11], function() makeDefault(18)],
-  ["25: foo -> default in url",
-   "foo", [2,3,6,7,10,11], function() makeDefault(16)],
 ];
 
-function makeDefault(aDefault) {
-  // We want to ignore tags if we're restricting to history unless we're showing
-  if ((aDefault & 1) && !((aDefault & 2) || (aDefault & 4)))
-    ignoreTags();
+function setPref(aTypes) {
+  clearSuggestPrefs();
+  for (let type in aTypes) {
+    prefs.setBoolPref("browser.urlbar.suggest." + type, aTypes[type]);
+  }
+}
 
-  prefs.setIntPref("browser.urlbar.default.behavior", aDefault);
+function clearSuggestPrefs() {
+  prefs.setBoolPref("browser.urlbar.suggest.history", false);
+  prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
+  prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", false);
+  prefs.setBoolPref("browser.urlbar.suggest.openpage", false);
 }
 
 function changeRestrict(aType, aChar)
 {
   let branch = "browser.urlbar.";
   // "title" and "url" are different from everything else, so special case them.
   if (aType == "title" || aType == "url")
     branch += "match.";
--- a/toolkit/components/places/tests/inline/test_casing.js
+++ b/toolkit/components/places/tests/inline/test_casing.js
@@ -2,101 +2,101 @@
  * 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/. */
 
 add_autocomplete_test([
   "Searching for cased entry 1",
   "MOZ",
   { autoFilled: "MOZilla.org/", completed: "mozilla.org/" },
   function () {
-    addBookmark({ url: "http://mozilla.org/test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/test/") });
   }
 ]);
 
 add_autocomplete_test([
   "Searching for cased entry 2",
   "mozilla.org/T",
   { autoFilled: "mozilla.org/T", completed: "mozilla.org/T" },
   function () {
-    addBookmark({ url: "http://mozilla.org/test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/test/") });
   }
 ]);
 
 add_autocomplete_test([
   "Searching for cased entry 3",
   "mozilla.org/T",
   { autoFilled: "mozilla.org/Test/", completed: "http://mozilla.org/Test/" },
   function () {
-    addBookmark({ url: "http://mozilla.org/Test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/") });
   }
 ]);
 
 add_autocomplete_test([
   "Searching for cased entry 4",
   "mOzilla.org/t",
   { autoFilled: "mOzilla.org/t", completed: "mOzilla.org/t" },
   function () {
-    addBookmark({ url: "http://mozilla.org/Test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/") });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for cased entry 5",
   "mOzilla.org/T",
   { autoFilled: "mOzilla.org/Test/", completed: "http://mozilla.org/Test/" },
   function () {
-    addBookmark({ url: "http://mozilla.org/Test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/") });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for untrimmed cased entry",
   "http://mOz",
   { autoFilled: "http://mOzilla.org/", completed: "http://mozilla.org/" },
   function () {
-    addBookmark({ url: "http://mozilla.org/Test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/") });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for untrimmed cased entry with www",
   "http://www.mOz",
   { autoFilled: "http://www.mOzilla.org/", completed: "http://www.mozilla.org/" },
   function () {
-    addBookmark({ url: "http://www.mozilla.org/Test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://www.mozilla.org/Test/") });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for untrimmed cased entry with path",
   "http://mOzilla.org/t",
   { autoFilled: "http://mOzilla.org/t", completed: "http://mOzilla.org/t" },
   function () {
-    addBookmark({ url: "http://mozilla.org/Test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/") });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for untrimmed cased entry with path 2",
   "http://mOzilla.org/T",
   { autoFilled: "http://mOzilla.org/Test/", completed: "http://mozilla.org/Test/" },
   function () {
-    addBookmark({ url: "http://mozilla.org/Test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://mozilla.org/Test/") });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for untrimmed cased entry with www and path",
   "http://www.mOzilla.org/t",
   { autoFilled: "http://www.mOzilla.org/t", completed: "http://www.mOzilla.org/t" },
   function () {
-    addBookmark({ url: "http://www.mozilla.org/Test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://www.mozilla.org/Test/") });
   },
 ]);
 
 add_autocomplete_test([
   "Searching for untrimmed cased entry with www and path 2",
   "http://www.mOzilla.org/T",
   { autoFilled: "http://www.mOzilla.org/Test/", completed: "http://www.mozilla.org/Test/" },
   function () {
-    addBookmark({ url: "http://www.mozilla.org/Test/" });
+    promiseAddVisits({ uri: NetUtil.newURI("http://www.mozilla.org/Test/") });
   },
 ]);
new file mode 100644
--- /dev/null
+++ b/toolkit/components/places/tests/queries/test_queryMultipleFolder.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+function run_test() {
+  run_next_test();
+}
+
+add_task(function* test_queryMultipleFolders() {
+  // adding bookmarks in the folders
+  let folderIds = [];
+  let bookmarkIds = [];
+  for (let i = 0; i < 3; ++i) {
+    folderIds.push(PlacesUtils.bookmarks.createFolder(PlacesUtils.bookmarksMenuFolderId,
+                                                      "Folder"+ i, PlacesUtils.bookmarks.DEFAULT_INDEX));
+    for(let j = 0; j < 7; ++j) {
+      bookmarkIds.push(PlacesUtils.bookmarks.insertBookmark(folderIds[i],
+                                                            uri("http://Bookmark" + i + "_" + j + ".com"),
+                                                            PlacesUtils.bookmarks.DEFAULT_INDEX, ""));
+    }
+  }
+
+  // using queryStringToQueries
+  let query = {};
+  let options = {};
+  let maxResults = 20;
+  let queryString = "place:" + folderIds.map((id) => {
+    return "folder=" + id;
+  }).join('&') + "&sort=5&maxResults=" + maxResults;
+  PlacesUtils.history.queryStringToQueries(queryString, query, {}, options);
+  let rootNode = PlacesUtils.history.executeQuery(query.value[0],options.value).root;
+  rootNode.containerOpen = true;
+  let resultLength = rootNode.childCount;
+  Assert.equal(resultLength, maxResults);
+  for (let i = 0; i < resultLength; ++i) {
+    let node = rootNode.getChild(i);
+    Assert.equal(bookmarkIds[i], node.itemId, node.uri);
+  }
+  rootNode.containerOpen = false;
+
+  // using getNewQuery and getNewQueryOptions
+  query = PlacesUtils.history.getNewQuery();
+  options = PlacesUtils.history.getNewQueryOptions();
+  query.setFolders(folderIds, folderIds.length);
+  options.sortingMode = options.SORT_BY_URI_ASCENDING;
+  options.maxResults = maxResults;
+  rootNode = PlacesUtils.history.executeQuery(query, options).root;
+  rootNode.containerOpen = true;
+  resultLength = rootNode.childCount;
+  Assert.equal(resultLength, maxResults);
+  for (let i = 0; i < resultLength; ++i) {
+    let node = rootNode.getChild(i);
+    Assert.equal(bookmarkIds[i], node.itemId, node.uri);
+  }
+  rootNode.containerOpen = false;
+});
\ No newline at end of file
--- a/toolkit/components/places/tests/queries/xpcshell.ini
+++ b/toolkit/components/places/tests/queries/xpcshell.ini
@@ -6,16 +6,17 @@ skip-if = toolkit == 'android' || toolki
 [test_415716.js]
 [test_abstime-annotation-domain.js]
 [test_abstime-annotation-uri.js]
 [test_async.js]
 [test_containersQueries_sorting.js]
 [test_history_queries_tags_liveUpdate.js]
 [test_history_queries_titles_liveUpdate.js]
 [test_onlyBookmarked.js]
+[test_queryMultipleFolder.js]
 [test_querySerialization.js]
 [test_redirects.js]
 # Bug 676989: test hangs consistently on Android
 skip-if = os == "android"
 [test_results-as-tag-contents-query.js]
 [test_results-as-visit.js]
 [test_searchterms-domain.js]
 [test_searchterms-uri.js]
--- a/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
+++ b/toolkit/components/places/tests/unifiedcomplete/head_autocomplete.js
@@ -23,16 +23,19 @@ function run_test() {
   run_next_test();
 }
 
 function* cleanup() {
   Services.prefs.clearUserPref("browser.urlbar.autocomplete.enabled");
   Services.prefs.clearUserPref("browser.urlbar.autoFill");
   Services.prefs.clearUserPref("browser.urlbar.autoFill.typed");
   Services.prefs.clearUserPref("browser.urlbar.autoFill.searchEngines");
+  for (let type of ["history", "bookmark", "history.onlyTyped", "openpage"]) {
+    Services.prefs.clearUserPref("browser.urlbar.suggest." + type);
+  }
   remove_all_bookmarks();
   yield promiseClearHistory();
 }
 do_register_cleanup(cleanup);
 
 /**
  * @param aSearches
  *        Array of AutoCompleteSearch names.
--- a/toolkit/components/places/tests/unifiedcomplete/test_autoFill_default_behavior.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_autoFill_default_behavior.js
@@ -6,27 +6,31 @@
  * Test autoFill for different default behaviors.
  */
 
 add_task(function* test_default_behavior_host() {
   let uri1 = NetUtil.newURI("http://typed/");
   let uri2 = NetUtil.newURI("http://visited/");
   let uri3 = NetUtil.newURI("http://bookmarked/");
   let uri4 = NetUtil.newURI("http://tpbk/");
+  let uri5 = NetUtil.newURI("http://tagged/");
 
   yield promiseAddVisits([
     { uri: uri1, title: "typed", transition: TRANSITION_TYPED },
     { uri: uri2, title: "visited" },
     { uri: uri4, title: "tpbk", transition: TRANSITION_TYPED },
   ]);
   addBookmark( { uri: uri3, title: "bookmarked" } );
   addBookmark( { uri: uri4, title: "tpbk" } );
+  addBookmark( { uri: uri5, title: "title", tags: ["foo"] } );
 
   // RESTRICT TO HISTORY.
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 1);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", false);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
 
   do_log_info("Restrict history, common visit, should not autoFill");
   yield check_autocomplete({
     search: "vi",
     matches: [ { uri: uri2, title: "visited" } ],
     autofilled: "vi",
     completed: "vi"
   });
@@ -75,17 +79,17 @@ add_task(function* test_default_behavior
     search: "vi",
     matches: [ { uri: uri2, title: "visited", style: [ "autofill" ] } ],
     autofilled: "visited/",
     completed: "visited/"
   });
 
   // RESTRICT TO TYPED.
   // This should basically ignore autoFill.typed and acts as if it would be set.
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 32);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", true);
 
   // Typed behavior basically acts like history, but filters on typed.
   do_log_info("Restrict typed, common visit, autoFill.typed = false, should not autoFill");
   yield check_autocomplete({
     search: "vi",
     matches: [ ],
     autofilled: "vi",
     completed: "vi"
@@ -111,17 +115,18 @@ add_task(function* test_default_behavior
   yield check_autocomplete({
     search: "tp",
     matches: [ { uri: uri4, title: "tpbk", style: [ "autofill" ] } ],
     autofilled: "tpbk/",
     completed: "tpbk/"
   });
 
   // RESTRICT BOOKMARKS.
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 2);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", false);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
   Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true);
 
   do_log_info("Restrict bookmarks, common visit, should not autoFill");
   yield check_autocomplete({
     search: "vi",
     matches: [ ],
     autofilled: "vi",
     completed: "vi"
@@ -158,16 +163,34 @@ add_task(function* test_default_behavior
   do_log_info("Restrict bookmarks, bookmark, autofill.typed = false, should autoFill");
   yield check_autocomplete({
     search: "bo",
     matches: [ { uri: uri3, title: "bookmarked", style: [ "autofill" ] } ],
     autofilled: "bookmarked/",
     completed: "bookmarked/"
   });
 
+  // Don't autofill because it's a title.
+  do_log_info("Restrict bookmarks, title, autofill.typed = false, should not autoFill");
+  yield check_autocomplete({
+    search: "# ta",
+    matches: [ ],
+    autofilled: "# ta",
+    completed: "# ta"
+  });
+
+  // Don't autofill because it's a tag.
+  do_log_info("Restrict bookmarks, tag, autofill.typed = false, should not autoFill");
+  yield check_autocomplete({
+    search: "+ ta",
+    matches: [ { uri: uri5, title: "title", tags: [ "foo" ], style: [ "tag" ] } ],
+    autofilled: "+ ta",
+    completed: "+ ta"
+  });
+
   yield cleanup();
 });
 
 add_task(function* test_default_behavior_url() {
   let uri1 = NetUtil.newURI("http://typed/ty/");
   let uri2 = NetUtil.newURI("http://visited/vi/");
   let uri3 = NetUtil.newURI("http://bookmarked/bo/");
   let uri4 = NetUtil.newURI("http://tpbk/tp/");
@@ -176,17 +199,19 @@ add_task(function* test_default_behavior
     { uri: uri1, title: "typed", transition: TRANSITION_TYPED },
     { uri: uri2, title: "visited" },
     { uri: uri4, title: "tpbk", transition: TRANSITION_TYPED },
   ]);
   addBookmark( { uri: uri3, title: "bookmarked" } );
   addBookmark( { uri: uri4, title: "tpbk" } );
 
   // RESTRICT TO HISTORY.
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 1);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", false);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
   Services.prefs.setBoolPref("browser.urlbar.autoFill.typed", true);
   Services.prefs.setBoolPref("browser.urlbar.autoFill.searchEngines", false);
 
   do_log_info("URL: Restrict history, common visit, should not autoFill");
   yield check_autocomplete({
     search: "visited/v",
     matches: [ { uri: uri2, title: "visited" } ],
     autofilled: "visited/v",
@@ -215,17 +240,18 @@ add_task(function* test_default_behavior
   yield check_autocomplete({
     search: "tpbk/t",
     matches: [ { uri: uri4, title: "tpbk/tp/", style: [ "autofill" ] } ],
     autofilled: "tpbk/tp/",
     completed: "http://tpbk/tp/"
   });
 
   // RESTRICT BOOKMARKS.
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 2);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", false);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
 
   do_log_info("URL: Restrict bookmarks, common visit, should not autoFill");
   yield check_autocomplete({
     search: "visited/v",
     matches: [ ],
     autofilled: "visited/v",
     completed: "visited/v"
   });
--- a/toolkit/components/places/tests/unifiedcomplete/test_autocomplete_functional.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_autocomplete_functional.js
@@ -1,17 +1,19 @@
 /* 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/. */
 
 // Functional tests for inline autocomplete
 
+const PREF_AUTOCOMPLETE_ENABLED = "browser.urlbar.autocomplete.enabled";
+
 add_task(function* test_disabling_autocomplete() {
   do_log_info("Check disabling autocomplete disables autofill");
-  Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", false);
+  Services.prefs.setBoolPref(PREF_AUTOCOMPLETE_ENABLED, false);
   yield promiseAddVisits({ uri: NetUtil.newURI("http://visit.mozilla.org"),
                            transition: TRANSITION_TYPED });
   yield check_autocomplete({
     search: "vis",
     autofilled: "vis",
     completed: "vis"
   });
   yield cleanup();
@@ -140,8 +142,28 @@ add_task(function* test_complete_fragmen
   yield promiseAddVisits(NetUtil.newURI("http://smokey.mozilla.org/foo?bacon=delicious#bar"));
   yield check_autocomplete({
     search: "smokey.mozilla.org/foo?bacon=delicious#bar",
     autofilled: "smokey.mozilla.org/foo?bacon=delicious#bar",
     completed: "http://smokey.mozilla.org/foo?bacon=delicious#bar",
   });
   yield cleanup();
 });
+
+add_task(function* test_autocomplete_enabled_pref() {
+  Services.prefs.setBoolPref(PREF_AUTOCOMPLETE_ENABLED, false);
+  let types = ["history", "bookmark", "openpage"];
+  for (type of types) {
+    do_check_eq(Services.prefs.getBoolPref("browser.urlbar.suggest." + type), false,
+                "suggest." + type + "pref should be false");
+  }
+  Services.prefs.setBoolPref(PREF_AUTOCOMPLETE_ENABLED, true);
+  for (type of types) {
+    do_check_eq(Services.prefs.getBoolPref("browser.urlbar.suggest." + type), true,
+                "suggest." + type + "pref should be true");
+  }
+
+  // Clear prefs.
+  Services.prefs.clearUserPref(PREF_AUTOCOMPLETE_ENABLED);
+  for (type of types) {
+    Services.prefs.clearUserPref("browser.urlbar.suggest." + type);
+  }
+});
--- a/toolkit/components/places/tests/unifiedcomplete/test_empty_search.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_empty_search.js
@@ -9,86 +9,85 @@
 
 add_task(function* test_javascript_match() {
   let uri1 = NetUtil.newURI("http://t.foo/0");
   let uri2 = NetUtil.newURI("http://t.foo/1");
   let uri3 = NetUtil.newURI("http://t.foo/2");
   let uri4 = NetUtil.newURI("http://t.foo/3");
   let uri5 = NetUtil.newURI("http://t.foo/4");
   let uri6 = NetUtil.newURI("http://t.foo/5");
+  let uri7 = NetUtil.newURI("http://t.foo/6");
 
   yield promiseAddVisits([ { uri: uri1, title: "title" },
                            { uri: uri2, title: "title" },
                            { uri: uri3, title: "title",
                              transition: TRANSITION_TYPED},
                            { uri: uri4, title: "title",
                              transition: TRANSITION_TYPED },
                            { uri: uri6, title: "title",
-                             transition: TRANSITION_TYPED } ]);
+                             transition: TRANSITION_TYPED },
+                           { uri: uri7, title: "title" } ]);
 
   addBookmark({ uri: uri2,
                 title: "title" });
   addBookmark({ uri: uri4,
                 title: "title" });
   addBookmark({ uri: uri5,
                 title: "title" });
   addBookmark({ uri: uri6,
                 title: "title" });
 
-  // Now remove page 6 from history, so it is an unvisited, typed bookmark.
+  addOpenPages(uri7, 1);
+
+  // Now remove page 6 from history, so it is an unvisited bookmark.
   PlacesUtils.history.removePage(uri6);
 
   do_log_info("Match everything");
   yield check_autocomplete({
     search: "foo",
+    searchParam: "enable-actions",
     matches: [ { uri: uri1, title: "title" },
                { uri: uri2, title: "title", style: ["bookmark"] },
                { uri: uri3, title: "title" },
                { uri: uri4, title: "title", style: ["bookmark"] },
                { uri: uri5, title: "title", style: ["bookmark"] },
-               { uri: uri6, title: "title", style: ["bookmark"] } ]
+               { uri: uri6, title: "title", style: ["bookmark"] },
+               { uri: makeActionURI("switchtab", {url: "http://t.foo/6"}), title: "title", style: [ "action,switchtab" ] }, ]
   });
 
   do_log_info("Match only typed history");
   yield check_autocomplete({
     search: "foo ^ ~",
     matches: [ { uri: uri3, title: "title" },
                { uri: uri4, title: "title" } ]
   });
 
   do_log_info("Drop-down empty search matches only typed history");
   yield check_autocomplete({
     search: "",
     matches: [ { uri: uri3, title: "title" },
                { uri: uri4, title: "title" } ]
   });
 
-  do_log_info("Drop-down empty search matches everything");
-  Services.prefs.setIntPref("browser.urlbar.default.behavior.emptyRestriction", 0);
+  do_log_info("Drop-down empty search matches only bookmarks");
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", false);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
   yield check_autocomplete({
     search: "",
-    matches: [ { uri: uri1, title: "title" },
-               { uri: uri2, title: "title", style: ["bookmark"] },
-               { uri: uri3, title: "title" },
+    matches: [ { uri: uri2, title: "title", style: ["bookmark"] },
                { uri: uri4, title: "title", style: ["bookmark"] },
                { uri: uri5, title: "title", style: ["bookmark"] },
                { uri: uri6, title: "title", style: ["bookmark"] } ]
   });
 
-  do_log_info("Drop-down empty search matches only typed");
-  Services.prefs.setIntPref("browser.urlbar.default.behavior.emptyRestriction", 32);
+  do_log_info("Drop-down empty search matches only open tabs");
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
   yield check_autocomplete({
     search: "",
-    matches: [ { uri: uri3, title: "title" },
-               { uri: uri4, title: "title", style: ["bookmark"] },
-               { uri: uri6, title: "title", style: ["bookmark"] } ]
+    searchParam: "enable-actions",
+    matches: [ { uri: makeActionURI("switchtab", {url: "http://t.foo/6"}), title: "title", style: [ "action,switchtab" ] }, ]
   });
 
-  do_log_info("Drop-down empty search matches only typed history");
-  Services.prefs.clearUserPref("browser.urlbar.default.behavior.emptyRestriction");
-  yield check_autocomplete({
-    search: "",
-    matches: [ { uri: uri3, title: "title" },
-               { uri: uri4, title: "title" } ]
-  });
+  Services.prefs.clearUserPref("browser.urlbar.suggest.history");
+  Services.prefs.clearUserPref("browser.urlbar.suggest.bookmark");
 
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unifiedcomplete/test_special_search.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_special_search.js
@@ -5,16 +5,22 @@
 /**
  * Test for bug 395161 that allows special searches that restrict results to
  * history/bookmark/tagged items and title/url matches.
  *
  * Test 485122 by making sure results don't have tags when restricting result
  * to just history either by default behavior or dynamic query restrict.
  */
 
+function setSuggestPrefsToFalse() {
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", false);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", false);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
+}
+
 add_task(function* test_special_searches() {
   let uri1 = NetUtil.newURI("http://url/");
   let uri2 = NetUtil.newURI("http://url/2");
   let uri3 = NetUtil.newURI("http://foo.bar/");
   let uri4 = NetUtil.newURI("http://foo.bar/2");
   let uri5 = NetUtil.newURI("http://url/star");
   let uri6 = NetUtil.newURI("http://url/star/2");
   let uri7 = NetUtil.newURI("http://foo.bar/star");
@@ -42,30 +48,30 @@ add_task(function* test_special_searches
   do_log_info("History restrict");
   yield check_autocomplete({
     search: "^",
     matches: [ { uri: uri1, title: "title" },
                { uri: uri2, title: "foo.bar" },
                { uri: uri3, title: "title" },
                { uri: uri4, title: "foo.bar" },
                { uri: uri6, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("Star restrict");
   yield check_autocomplete({
     search: "*",
     matches: [ { uri: uri5, title: "title", style: [ "bookmark" ] },
                { uri: uri6, title: "foo.bar", style: [ "bookmark" ] },
                { uri: uri7, title: "title", style: [ "bookmark" ] },
                { uri: uri8, title: "foo.bar", style: [ "bookmark" ] },
                { uri: uri9, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] },
                { uri: uri10, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] },
                { uri: uri11, title: "title", tags: [ "foo.bar"], style: [ "tag" ] },
-               { uri: uri12, title: "foo.bar", tags: ["foo.bar"], style: [ "tag" ] } ]
+               { uri: uri12, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("Tag restrict");
   yield check_autocomplete({
     search: "+",
     matches: [ { uri: uri9, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] },
                { uri: uri10, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] },
                { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] },
@@ -75,59 +81,59 @@ add_task(function* test_special_searches
   // Test specials as any word position
   do_log_info("Special as first word");
   yield check_autocomplete({
     search: "^ foo bar",
     matches: [ { uri: uri2, title: "foo.bar" },
                { uri: uri3, title: "title" },
                { uri: uri4, title: "foo.bar" },
                { uri: uri6, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("Special as middle word");
   yield check_autocomplete({
     search: "foo ^ bar",
     matches: [ { uri: uri2, title: "foo.bar" },
                { uri: uri3, title: "title" },
                { uri: uri4, title: "foo.bar" },
                { uri: uri6, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("Special as last word");
   yield check_autocomplete({
     search: "foo bar ^",
     matches: [ { uri: uri2, title: "foo.bar" },
                { uri: uri3, title: "title" },
                { uri: uri4, title: "foo.bar" },
                { uri: uri6, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   // Test restricting and matching searches with a term
   do_log_info("foo ^ -> history");
   yield check_autocomplete({
     search: "foo ^",
     matches: [ { uri: uri2, title: "foo.bar" },
                { uri: uri3, title: "title" },
                { uri: uri4, title: "foo.bar" },
                { uri: uri6, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("foo | -> history (change pref)");
   changeRestrict("history", "|");
   yield check_autocomplete({
     search: "foo |",
     matches: [ { uri: uri2, title: "foo.bar" },
                { uri: uri3, title: "title" },
                { uri: uri4, title: "foo.bar" },
                { uri: uri6, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("foo * -> is star");
   resetRestrict("history");
   yield check_autocomplete({
     search: "foo *",
     matches: [ { uri: uri6, title: "foo.bar", style: [ "bookmark" ] },
                { uri: uri7, title: "title", style: [ "bookmark" ] },
@@ -249,38 +255,38 @@ add_task(function* test_special_searches
   });
 
   do_log_info("foo ^ # -> history, in title");
   yield check_autocomplete({
     search: "foo ^ #",
     matches: [ { uri: uri2, title: "foo.bar" },
                { uri: uri4, title: "foo.bar" },
                { uri: uri6, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("foo ^ @ -> history, in url");
   yield check_autocomplete({
     search: "foo ^ @",
     matches: [ { uri: uri3, title: "title" },
                { uri: uri4, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("foo ^ + -> history, is tag");
   yield check_autocomplete({
     search: "foo ^ +",
     matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("foo ^ ~ -> history, is typed");
   yield check_autocomplete({
     search: "foo ^ ~",
     matches: [ { uri: uri4, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("foo * # -> is star, in title");
   yield check_autocomplete({
     search: "foo * #",
     matches: [ { uri: uri6, title: "foo.bar", style: [ "bookmark" ] },
                { uri: uri8, title: "foo.bar", style: [ "bookmark" ] },
                { uri: uri9, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] },
@@ -357,67 +363,83 @@ add_task(function* test_special_searches
     search: "foo + ~",
     matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   // Disable autoFill for the next tests, see test_autoFill_default_behavior.js
   // for specific tests.
   Services.prefs.setBoolPref("browser.urlbar.autoFill", false);
 
-  // Test default usage by setting certain bits of default.behavior to 1
+  // Test default usage by setting certain browser.urlbar.suggest.* prefs
   do_log_info("foo -> default history");
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 1);
+  setSuggestPrefsToFalse();
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
   yield check_autocomplete({
     search: "foo",
     matches: [ { uri: uri2, title: "foo.bar" },
                { uri: uri3, title: "title" },
                { uri: uri4, title: "foo.bar" },
                { uri: uri6, title: "foo.bar" },
-               { uri: uri11, title: "title" } ]
+               { uri: uri11, title: "title", tags: ["foo.bar"], style: [ "tag" ] } ]
   });
 
   do_log_info("foo -> default history, is star");
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 3);
+  setSuggestPrefsToFalse();
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
   yield check_autocomplete({
     search: "foo",
-    matches: [ { uri: uri6, title: "foo.bar", style: [ "bookmark" ] },
-               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
+    matches: [ { uri: uri2, title: "foo.bar" },
+               { uri: uri3, title: "title" },
+               { uri: uri4, title: "foo.bar" },
+               { uri: uri6, title: "foo.bar", style: [ "bookmark" ] },
+               { uri: uri7, title: "title", style: [ "bookmark" ] },
+               { uri: uri8, title: "foo.bar", style: [ "bookmark" ] },
+               { uri: uri9, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] },
+               { uri: uri10, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] },
+               { uri: uri11, title: "title", tags: [ "foo.bar"], style: [ "tag" ] },
+               { uri: uri12, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
   do_log_info("foo -> default history, is star, is typed");
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 35);
-  yield check_autocomplete({
-    search: "foo",
-    matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
-  });
-
-  do_log_info("foo -> default history, is star, in url");
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 19);
+  setSuggestPrefsToFalse();
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", true);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
   yield check_autocomplete({
     search: "foo",
-    matches: [ { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
+    matches: [ { uri: uri4, title: "foo.bar" },
+               { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
-  // Change the default to be less restrictive to make sure we find more
-  do_log_info("foo -> default is star, in url");
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 18);
+  do_log_info("foo -> is star");
+  setSuggestPrefsToFalse();
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history", false);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
   yield check_autocomplete({
     search: "foo",
-    matches: [ { uri: uri7, title: "title", style: [ "bookmark" ] },
+    matches: [ { uri: uri6, title: "foo.bar", style: [ "bookmark" ] },
+               { uri: uri7, title: "title", style: [ "bookmark" ] },
                { uri: uri8, title: "foo.bar", style: [ "bookmark" ] },
+               { uri: uri9, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] },
+               { uri: uri10, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] },
                { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] },
                { uri: uri12, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] } ]
   });
 
-  do_log_info("foo -> default in url");
-  Services.prefs.setIntPref("browser.urlbar.default.behavior", 16);
+  do_log_info("foo -> is star, is typed");
+  setSuggestPrefsToFalse();
+  // only typed should be ignored
+  Services.prefs.setBoolPref("browser.urlbar.suggest.history.onlyTyped", true);
+  Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
   yield check_autocomplete({
     search: "foo",
-    matches: [ { uri: uri3, title: "title" },
-               { uri: uri4, title: "foo.bar" },
+    matches: [ { uri: uri6, title: "foo.bar", style: [ "bookmark" ] },
                { uri: uri7, title: "title", style: [ "bookmark" ] },
                { uri: uri8, title: "foo.bar", style: [ "bookmark" ] },
+               { uri: uri9, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] },
+               { uri: uri10, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] },
                { uri: uri11, title: "title", tags: [ "foo.bar" ], style: [ "tag" ] },
-               { uri: uri12, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] } ]
+               { uri: uri12, title: "foo.bar", tags: [ "foo.bar" ], style: [ "tag" ] }  ]
   });
 
   yield cleanup();
 });
--- a/toolkit/components/places/tests/unit/test_adaptive.js
+++ b/toolkit/components/places/tests/unit/test_adaptive.js
@@ -160,20 +160,20 @@ let observer = {
     ensure_results(this.results, this.search);
   }
 };
 Services.obs.addObserver(observer, PlacesUtils.TOPIC_FEEDBACK_UPDATED, false);
 
 /**
  * Make the result object for a given URI that will be passed to ensure_results.
  */
-function makeResult(aURI) {
+function makeResult(aURI, aStyle = "favicon") {
   return {
     uri: aURI,
-    style: "favicon",
+    style: aStyle,
   };
 }
 
 let tests = [
   // Test things without a search term.
   function() {
     print("Test 0 same count, diff rank, same term; no search");
     observer.results = [
@@ -309,36 +309,39 @@ let tests = [
       makeResult(uri2),
     ];
     observer.search = s1;
     observer.runCount = c1 + c1;
     yield task_setCountRank(uri2, c1, c1, s1);
     doAdaptiveDecay();
     yield task_setCountRank(uri1, c1, c1, s1);
   },
-  // Test that bookmarks or tags are hidden if the preferences are set right.
+  // Test that bookmarks are hidden if the preferences are set right.
   function() {
     print("Test 12 same count, diff rank, same term; no search; history only");
-    Services.prefs.setIntPref("browser.urlbar.matchBehavior",
-                              Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY);
+    Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
+    Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
+    Services.prefs.setBoolPref("browser.urlbar.suggest.openpage", false);
     observer.results = [
       makeResult(uri1),
       makeResult(uri2),
     ];
     observer.search = s0;
     observer.runCount = c1 + c2;
     yield task_setCountRank(uri1, c1, c1, s2, "bookmark");
     yield task_setCountRank(uri2, c1, c2, s2);
   },
+  // Test that tags are shown if the preferences are set right.
   function() {
     print("Test 13 same count, diff rank, same term; no search; history only with tag");
-    Services.prefs.setIntPref("browser.urlbar.matchBehavior",
-                              Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY);
+    Services.prefs.setBoolPref("browser.urlbar.suggest.history", true);
+    Services.prefs.setBoolPref("browser.urlbar.suggest.bookmark", false);
+    Services.prefs.setBoolPref("browser.urlbar.suggest.openpage", false);
     observer.results = [
-      makeResult(uri1),
+      makeResult(uri1, "tag"),
       makeResult(uri2),
     ];
     observer.search = s0;
     observer.runCount = c1 + c2;
     yield task_setCountRank(uri1, c1, c1, s2, "tag");
     yield task_setCountRank(uri2, c1, c2, s2);
   },
 ];
@@ -360,16 +363,21 @@ function run_test()
 add_task(function test_adaptive()
 {
   for (let [, test] in Iterator(tests)) {
     // Cleanup.
     PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.unfiledBookmarksFolderId);
     PlacesUtils.bookmarks.removeFolderChildren(PlacesUtils.tagsFolderId);
     observer.runCount = -1;
 
+    let types = ["history", "bookmark", "openpage"];
+    for (let type of types) {
+      Services.prefs.clearUserPref("browser.urlbar.suggest." + type);
+    }
+
     yield promiseClearHistory();
 
     deferEnsureResults = Promise.defer();
     yield test();
     yield deferEnsureResults.promise;
   }
 
   Services.obs.removeObserver(observer, PlacesUtils.TOPIC_FEEDBACK_UPDATED);
--- a/toolkit/components/places/tests/unit/test_adaptive_bug527311.js
+++ b/toolkit/components/places/tests/unit/test_adaptive_bug527311.js
@@ -1,31 +1,38 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* 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/. */
 
 const TEST_URL = "http://adapt.mozilla.org/";
 const SEARCH_STRING = "adapt";
+const SUGGEST_TYPES = ["history", "bookmark", "openpage"];
 
 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 let hs = Cc["@mozilla.org/browser/nav-history-service;1"].
          getService(Ci.nsINavHistoryService);
 let bs = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
          getService(Ci.nsINavBookmarksService);
 let os = Cc["@mozilla.org/observer-service;1"].
          getService(Ci.nsIObserverService);
 let ps = Cc["@mozilla.org/preferences-service;1"].
          getService(Ci.nsIPrefBranch);
 
 const PLACES_AUTOCOMPLETE_FEEDBACK_UPDATED_TOPIC =
   "places-autocomplete-feedback-updated";
 
+function cleanup() {
+  for (let type of SUGGEST_TYPES) {
+    ps.clearUserPref("browser.urlbar.suggest." + type);
+  }
+}
+
 function AutoCompleteInput(aSearches) {
   this.searches = aSearches;
 }
 AutoCompleteInput.prototype = {
   constructor: AutoCompleteInput,
   searches: null,
   minResultsForPopup: 0,
   timeout: 10,
@@ -78,16 +85,17 @@ function check_results() {
   controller.input = input;
 
   input.onSearchComplete = function() {
     do_check_eq(controller.searchStatus,
                 Ci.nsIAutoCompleteController.STATUS_COMPLETE_NO_MATCH);
     do_check_eq(controller.matchCount, 0);
 
     remove_all_bookmarks();
+    cleanup();
     do_test_finished();
  };
 
   controller.startSearch(SEARCH_STRING);
 }
 
 
 function addAdaptiveFeedback(aUrl, aSearch, aCallback) {
@@ -117,13 +125,16 @@ function addAdaptiveFeedback(aUrl, aSear
 
 function run_test() {
   do_test_pending();
 
   // Add a bookmark to our url.
   bs.insertBookmark(bs.unfiledBookmarksFolder, uri(TEST_URL),                   
                     bs.DEFAULT_INDEX, "test_book");
   // We want to search only history.
-  ps.setIntPref("browser.urlbar.default.behavior",
-                Ci.mozIPlacesAutoComplete.BEHAVIOR_HISTORY);
+  for (let type of SUGGEST_TYPES) {
+    type == "history" ? ps.setBoolPref("browser.urlbar.suggest." + type, true)
+                      : ps.setBoolPref("browser.urlbar.suggest." + type, false);
+  }
+
   // Add an adaptive entry.
   addAdaptiveFeedback(TEST_URL, SEARCH_STRING, check_results);
 }
--- a/toolkit/components/places/tests/unit/test_frecency.js
+++ b/toolkit/components/places/tests/unit/test_frecency.js
@@ -276,19 +276,24 @@ function run_test()
   run_next_test();
 }
 
 add_task(function test_frecency()
 {
   // always search in history + bookmarks, no matter what the default is
   var prefs = Cc["@mozilla.org/preferences-service;1"].
               getService(Ci.nsIPrefBranch);
-  prefs.setIntPref("browser.urlbar.search.sources", 3);
-  prefs.setIntPref("browser.urlbar.default.behavior", 0);
+
+  prefs.setBoolPref("browser.urlbar.suggest.history", true);
+  prefs.setBoolPref("browser.urlbar.suggest.bookmark", true);
+  prefs.setBoolPref("browser.urlbar.suggest.openpage", false);
   for (let [, test] in Iterator(tests)) {
     remove_all_bookmarks();
     yield promiseClearHistory();
 
     deferEnsureResults = Promise.defer();
     yield test();
     yield deferEnsureResults.promise;
   }
+  for (let type of ["history", "bookmark", "openpage"]) {
+    prefs.clearUserPref("browser.urlbar.suggest." + type);
+  }
 });
--- a/toolkit/devtools/Loader.jsm
+++ b/toolkit/devtools/Loader.jsm
@@ -37,31 +37,16 @@ this.EXPORTED_SYMBOLS = ["DevToolsLoader
                          "SrcdirProvider"];
 
 /**
  * Providers are different strategies for loading the devtools.
  */
 
 let Timer = Cu.import("resource://gre/modules/Timer.jsm", {});
 
-let loaderGlobals = {
-  isWorker: false,
-  reportError: Cu.reportError,
-
-  btoa: btoa,
-  console: console,
-  _Iterator: Iterator,
-  loader: {
-    lazyGetter: (...args) => devtools.lazyGetter.apply(devtools, args),
-    lazyImporter: (...args) => devtools.lazyImporter.apply(devtools, args),
-    lazyServiceGetter: (...args) => devtools.lazyServiceGetter.apply(devtools, args),
-    lazyRequireGetter: (...args) => devtools.lazyRequireGetter.apply(devtools, args)
-  },
-};
-
 let loaderModules = {
   "Debugger": Debugger,
   "Services": Object.create(Services),
   "Timer": Object.create(Timer),
   "toolkit/loader": loader,
   "xpcInspector": xpcInspector,
   "promise": promise,
   "PromiseDebugging": PromiseDebugging
@@ -106,17 +91,17 @@ BuiltinProvider.prototype = {
         "acorn": "resource://gre/modules/devtools/acorn",
         "acorn/util/walk": "resource://gre/modules/devtools/acorn/walk.js",
         "tern": "resource://gre/modules/devtools/tern",
         "source-map": "resource://gre/modules/devtools/SourceMap.jsm",
 
         // Allow access to xpcshell test items from the loader.
         "xpcshell-test": "resource://test"
       },
-      globals: loaderGlobals,
+      globals: this.globals,
       invisibleToDebugger: this.invisibleToDebugger,
       sharedGlobal: true,
       sharedGlobalBlacklist: sharedGlobalBlacklist
     });
 
     return promise.resolve(undefined);
   },
 
@@ -185,17 +170,17 @@ SrcdirProvider.prototype = {
         "devtools/content-observer": contentObserverURI,
         "gcli": gcliURI,
         "projecteditor": projecteditorURI,
         "acorn": acornURI,
         "acorn/util/walk": acornWalkURI,
         "tern": ternURI,
         "source-map": sourceMapURI,
       },
-      globals: loaderGlobals,
+      globals: this.globals,
       invisibleToDebugger: this.invisibleToDebugger,
       sharedGlobal: true,
       sharedGlobalBlacklist: sharedGlobalBlacklist
     });
 
     return this._writeManifest(devtoolsDir).then(null, Cu.reportError);
   },
 
@@ -357,17 +342,33 @@ DevToolsLoader.prototype = {
 
     if (this._provider) {
       var events = this.require("sdk/system/events");
       events.emit("devtools-unloaded", {});
       delete this.require;
       this._provider.unload("newprovider");
     }
     this._provider = provider;
+
+    // Pass through internal loader settings specific to this loader instance
     this._provider.invisibleToDebugger = this.invisibleToDebugger;
+    this._provider.globals = {
+      isWorker: false,
+      reportError: Cu.reportError,
+      btoa: btoa,
+      console: console,
+      _Iterator: Iterator,
+      loader: {
+        lazyGetter: this.lazyGetter,
+        lazyImporter: this.lazyImporter,
+        lazyServiceGetter: this.lazyServiceGetter,
+        lazyRequireGetter: this.lazyRequireGetter
+      },
+    };
+
     this._provider.load();
     this.require = loader.Require(this._provider.loader, { id: "devtools" });
 
     if (this._mainid) {
       this.main(this._mainid);
     }
   },
 
new file mode 100644
--- /dev/null
+++ b/toolkit/devtools/tests/unit/exposeLoader.js
@@ -0,0 +1,8 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+exports.exerciseLazyRequire = (name, path) => {
+  const o = {};
+  loader.lazyRequireGetter(o, name, path);
+  return o;
+};
--- a/toolkit/devtools/tests/unit/test_require_lazy.js
+++ b/toolkit/devtools/tests/unit/test_require_lazy.js
@@ -1,14 +1,29 @@
 /* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 // Test devtools.lazyRequireGetter
 
 function run_test() {
+  const name = "asyncUtils";
+  const path = "devtools/async-utils";
   const o = {};
-  devtools.lazyRequireGetter(o, "asyncUtils", "devtools/async-utils");
-  const asyncUtils = devtools.require("devtools/async-utils");
+  devtools.lazyRequireGetter(o, name, path);
+  const asyncUtils = devtools.require(path);
   // XXX: do_check_eq only works on primitive types, so we have this
   // do_check_true of an equality expression.
   do_check_true(o.asyncUtils === asyncUtils);
+
+  // A non-main loader should get a new object via |lazyRequireGetter|, just
+  // as it would via a direct |require|.
+  const o2 = {};
+  let loader = new DevToolsLoader();
+  loader.lazyRequireGetter(o2, name, path);
+  do_check_true(o2.asyncUtils !== asyncUtils);
+
+  // A module required via a non-main loader that then uses |lazyRequireGetter|
+  // should also get the same object from that non-main loader.
+  let exposeLoader = loader.require("xpcshell-test/exposeLoader");
+  const o3 = exposeLoader.exerciseLazyRequire(name, path);
+  do_check_true(o3.asyncUtils === o2.asyncUtils);
 }
--- a/toolkit/devtools/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/tests/unit/xpcshell.ini
@@ -1,12 +1,14 @@
 [DEFAULT]
 head = head_devtools.js
 tail =
 skip-if = toolkit == 'android' || toolkit == 'gonk'
+support-files =
+  exposeLoader.js
 
 [test_independent_loaders.js]
 [test_invisible_loader.js]
 [test_safeErrorString.js]
 [test_defineLazyPrototypeGetter.js]
 [test_async-utils.js]
 [test_consoleID.js]
 [test_require_lazy.js]
--- a/toolkit/modules/NewTabUtils.jsm
+++ b/toolkit/modules/NewTabUtils.jsm
@@ -17,20 +17,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
   "resource://gre/modules/PlacesUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PageThumbs",
   "resource://gre/modules/PageThumbs.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "BinarySearch",
   "resource://gre/modules/BinarySearch.jsm");
 
-XPCOMUtils.defineLazyGetter(this, "Timer", () => {
-  return Cu.import("resource://gre/modules/Timer.jsm", {});
-});
-
 XPCOMUtils.defineLazyGetter(this, "gPrincipal", function () {
   let uri = Services.io.newURI("about:newtab", null, null);
   return Services.scriptSecurityManager.getNoAppCodebasePrincipal(uri);
 });
 
 XPCOMUtils.defineLazyGetter(this, "gCryptoHash", function () {
   return Cc["@mozilla.org/security/hash;1"].createInstance(Ci.nsICryptoHash);
 });
@@ -56,19 +52,16 @@ const PREF_NEWTAB_COLUMNS = "browser.new
 const HISTORY_RESULTS_LIMIT = 100;
 
 // The maximum number of links Links.getLinks will return.
 const LINKS_GET_LINKS_LIMIT = 100;
 
 // The gather telemetry topic.
 const TOPIC_GATHER_TELEMETRY = "gather-telemetry";
 
-// The amount of time we wait while coalescing updates for hidden pages.
-const SCHEDULE_UPDATE_TIMEOUT_MS = 1000;
-
 /**
  * Calculate the MD5 hash for a string.
  * @param aValue
  *        The string to convert.
  * @return The base64 representation of the MD5 hash.
  */
 function toHash(aValue) {
   let value = gUnicodeConverter.convertToByteArray(aValue);
@@ -276,41 +269,27 @@ let AllPages = {
    */
   get length() {
     return this._pages.length;
   },
 
   /**
    * Updates all currently active pages but the given one.
    * @param aExceptPage The page to exclude from updating.
-   * @param aHiddenPagesOnly If true, only pages hidden in the preloader are
-   *                         updated.
+   * @param aReason The reason for updating all pages.
    */
-  update: function AllPages_update(aExceptPage, aHiddenPagesOnly=false) {
+  update(aExceptPage, aReason = "") {
     this._pages.forEach(function (aPage) {
-      if (aExceptPage != aPage)
-        aPage.update(aHiddenPagesOnly);
+      if (aExceptPage != aPage) {
+        aPage.update(aReason);
+      }
     });
   },
 
   /**
-   * Many individual link changes may happen in a small amount of time over
-   * multiple turns of the event loop.  This method coalesces updates by waiting
-   * a small amount of time before updating hidden pages.
-   */
-  scheduleUpdateForHiddenPages: function AllPages_scheduleUpdateForHiddenPages() {
-    if (!this._scheduleUpdateTimeout) {
-      this._scheduleUpdateTimeout = Timer.setTimeout(() => {
-        delete this._scheduleUpdateTimeout;
-        this.update(null, true);
-      }, SCHEDULE_UPDATE_TIMEOUT_MS);
-    }
-  },
-
-  /**
    * Implements the nsIObserver interface to get notified when the preference
    * value changes or when a new copy of a page thumbnail is available.
    */
   observe: function AllPages_observe(aSubject, aTopic, aData) {
     if (aTopic == "nsPref:changed") {
       // Clear the cached value.
       switch (aData) {
         case PREF_NEWTAB_ENABLED:
@@ -1011,26 +990,27 @@ let Links = {
       sortedLinks.splice(idx, 0, insertionLink);
       if (sortedLinks.length > aProvider.maxNumLinks) {
         let lastLink = sortedLinks.pop();
         linkMap.delete(lastLink.url);
       }
       updatePages = true;
     }
 
-    if (updatePages)
-      AllPages.scheduleUpdateForHiddenPages();
+    if (updatePages) {
+      AllPages.update(null, "links-changed");
+    }
   },
 
   /**
    * Called by a provider to notify us when many links change.
    */
   onManyLinksChanged: function Links_onManyLinksChanged(aProvider) {
     this._populateProviderCache(aProvider, () => {
-      AllPages.scheduleUpdateForHiddenPages();
+      AllPages.update(null, "links-changed");
     }, true);
   },
 
   _indexOf: function Links__indexOf(aArray, aLink) {
     return this._binsearch(aArray, aLink, "indexOf");
   },
 
   _insertionIndexOf: function Links__insertionIndexOf(aArray, aLink) {
--- a/toolkit/themes/linux/global/jar.mn
+++ b/toolkit/themes/linux/global/jar.mn
@@ -53,16 +53,13 @@ toolkit.jar:
 +  skin/classic/global/icons/sslWarning.png                    (icons/sslWarning.png)
 +  skin/classic/global/icons/wrap.png                          (icons/wrap.png)
 +  skin/classic/global/icons/webapps-16.png                    (icons/webapps-16.png)
 +  skin/classic/global/icons/webapps-64.png                    (icons/webapps-64.png)
    skin/classic/global/menu/shared-menu-check.png              (../../shared/menu-check.png)
 *  skin/classic/global/in-content/common.css                   (in-content/common.css)
    skin/classic/global/in-content/check.png                    (../../shared/in-content/check.png)
    skin/classic/global/in-content/check@2x.png                 (../../shared/in-content/check@2x.png)
-   skin/classic/global/in-content/dropdown.png                 (../../shared/in-content/dropdown.png)
-   skin/classic/global/in-content/dropdown@2x.png              (../../shared/in-content/dropdown@2x.png)
-   skin/classic/global/in-content/dropdown-disabled.png        (../../shared/in-content/dropdown-disabled.png)
-   skin/classic/global/in-content/dropdown-disabled@2x.png     (../../shared/in-content/dropdown-disabled@2x.png)
+   skin/classic/global/in-content/dropdown.svg                 (../../shared/in-content/dropdown.svg)
    skin/classic/global/in-content/help-glyph.svg               (../../shared/in-content/help-glyph.svg)
    skin/classic/global/in-content/sorter.png                   (../../shared/in-content/sorter.png)
    skin/classic/global/in-content/sorter@2x.png                (../../shared/in-content/sorter@2x.png)
 +  skin/classic/global/toolbar/spring.png                      (toolbar/spring.png)
--- a/toolkit/themes/osx/global/jar.mn
+++ b/toolkit/themes/osx/global/jar.mn
@@ -183,20 +183,17 @@ toolkit.jar:
   skin/classic/global/media/volume-full@2x.png                       (media/volume-full@2x.png)
   skin/classic/global/media/clicktoplay-bgtexture.png                (media/clicktoplay-bgtexture.png)
   skin/classic/global/media/videoClickToPlayButton.svg               (media/videoClickToPlayButton.svg)
   skin/classic/global/menu/shared-menu-check.png                     (../../shared/menu-check.png)
   skin/classic/global/menu/shared-menu-check@2x.png                  (../../shared/menu-check@2x.png)
 * skin/classic/global/in-content/common.css                          (in-content/common.css)
   skin/classic/global/in-content/check.png                           (../../shared/in-content/check.png)
   skin/classic/global/in-content/check@2x.png                        (../../shared/in-content/check@2x.png)
-  skin/classic/global/in-content/dropdown.png                        (../../shared/in-content/dropdown.png)
-  skin/classic/global/in-content/dropdown@2x.png                     (../../shared/in-content/dropdown@2x.png)
-  skin/classic/global/in-content/dropdown-disabled.png               (../../shared/in-content/dropdown-disabled.png)
-  skin/classic/global/in-content/dropdown-disabled@2x.png            (../../shared/in-content/dropdown-disabled@2x.png)
+  skin/classic/global/in-content/dropdown.svg                        (../../shared/in-content/dropdown.svg)
   skin/classic/global/in-content/help-glyph.svg                      (../../shared/in-content/help-glyph.svg)
   skin/classic/global/in-content/sorter.png                          (../../shared/in-content/sorter.png)
   skin/classic/global/in-content/sorter@2x.png                       (../../shared/in-content/sorter@2x.png)
   skin/classic/global/scale/scale-tray-horiz.gif                     (scale/scale-tray-horiz.gif)
   skin/classic/global/scale/scale-tray-vert.gif                      (scale/scale-tray-vert.gif)
   skin/classic/global/splitter/dimple.png                            (splitter/dimple.png)
   skin/classic/global/splitter/grip-bottom.gif                       (splitter/grip-bottom.gif)
   skin/classic/global/splitter/grip-top.gif                          (splitter/grip-top.gif)
--- a/toolkit/themes/shared/in-content/common.inc.css
+++ b/toolkit/themes/shared/in-content/common.inc.css
@@ -128,17 +128,16 @@ html|button {
 
 *|button,
 xul|colorpicker[type="button"],
 xul|menulist {
   -moz-appearance: none;
   height: 30px;
   color: #333;
   line-height: 20px;
-  text-shadow: 0 1px 1px #fefffe;
   border: 1px solid #c1c1c1;
   -moz-border-top-colors: none !important;
   -moz-border-right-colors: none !important;
   -moz-border-bottom-colors: none !important;
   -moz-border-left-colors: none !important;
   border-radius: 2px;
   background-color: #fbfbfb;
 }
@@ -180,17 +179,17 @@ xul|button[type="menu"] > xul|*.button-b
   -moz-appearance: none;
   margin: 1px 0;
   -moz-margin-start: 10px;
   padding: 0;
   width: 10px;
   height: 16px;
   border: none;
   background-color: transparent;
-  list-style-image: url("chrome://global/skin/in-content/dropdown.png");
+  list-style-image: url("chrome://global/skin/in-content/dropdown.svg#dropdown");
 }
 
 xul|*.help-button {
   min-width: 30px;
   border-radius: 2px;
   border: 1px solid #c1c1c1;
   background-color: #ffcb00;
   background-image: none;
@@ -257,42 +256,30 @@ xul|*.spinbuttons-down > xul|*.button-bo
 }
 
 xul|*.spinbuttons-down[disabled="true"] > xul|*.button-box > xul|*.button-icon {
   list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
 }
 
 xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker {
   -moz-appearance: none;
-  -moz-margin-end: 10px;
+  -moz-margin-end: 4px;
   padding: 0;
   border: none;
   background-color: transparent;
-  list-style-image: url("chrome://global/skin/in-content/dropdown.png");
+  list-style-image: url("chrome://global/skin/in-content/dropdown.svg#dropdown");
+}
+
+xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker > xul|*.dropmarker-icon {
+  width: 18px;
+  height: 18px;
 }
 
 xul|menulist[disabled="true"]:not([editable="true"]) > xul|*.menulist-dropmarker {
-  list-style-image: url("chrome://global/skin/in-content/dropdown-disabled.png")
-}
-
-@media (min-resolution: 2dppx) {
-  xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker,
-  xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker {
-    list-style-image: url("chrome://global/skin/in-content/dropdown@2x.png");
-  }
-
-  xul|menulist[disabled="true"]:not([editable="true"]) > xul|*.menulist-dropmarker {
-    list-style-image: url("chrome://global/skin/in-content/dropdown-disabled@2x.png")
-  }
-
-  xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker > xul|*.dropmarker-icon,
-  xul|button[type="menu"] > xul|*.button-box > xul|*.button-menu-dropmarker > xul|*.dropmarker-icon {
-    width: 10px;
-    height: 16px;
-  }
+  list-style-image: url("chrome://global/skin/in-content/dropdown.svg#dropdown-disabled")
 }
 
 xul|menulist > xul|menupopup,
 xul|button[type="menu"] > xul|menupopup {
   -moz-appearance: none;
   border: 1px solid rgba(23,50,77,0.4);
   border-radius: 2px;
   background-color: #fff;
@@ -300,37 +287,36 @@ xul|button[type="menu"] > xul|menupopup 
 
 xul|menulist > xul|menupopup xul|menu,
 xul|menulist > xul|menupopup xul|menuitem,
 xul|button[type="menu"] > xul|menupopup xul|menu,
 xul|button[type="menu"] > xul|menupopup xul|menuitem {
   -moz-appearance: none;
   font-size: 1.25rem;
   line-height: 22px;
-  height: 40px;
+  height: 30px;
   color: #333;
   -moz-padding-start: 10px;
   -moz-padding-end: 30px;
 }
 
 xul|menulist > xul|menupopup > xul|menu[_moz-menuactive="true"],
 xul|menulist > xul|menupopup > xul|menuitem[_moz-menuactive="true"],
 xul|button[type="menu"] > xul|menupopup > xul|menu[_moz-menuactive="true"],
 xul|button[type="menu"] > xul|menupopup > xul|menuitem[_moz-menuactive="true"] {
   color: #333;
-  background-color: transparent;
-  background-image: linear-gradient(rgba(76,177,255,0.25), rgba(23,146,229,0.25));
+  background-color: rgba(0,149,221,0.25);
 }
 
 xul|menulist > xul|menupopup > xul|menu[selected="true"],
 xul|menulist > xul|menupopup > xul|menuitem[selected="true"],
 xul|button[type="menu"] > xul|menupopup > xul|menu[selected="true"],
 xul|button[type="menu"] > xul|menupopup > xul|menuitem[selected="true"] {
   color: #fff;
-  background-image: linear-gradient(#4cb1ff, #1792e5);
+  background-color: #0095DD;
 }
 
 xul|menulist > xul|menupopup xul|menuseparator,
 xul|button[type="menu"] > xul|menupopup xul|menuseparator {
   -moz-appearance: none;
   margin-top: 2px;
   margin-bottom: 2px;
   padding: 0;
deleted file mode 100644
index d92c55d9765a045683a4118f09419f45f9e074e7..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 1b432f7575c026ce087100cdfe39c5d50ee6c160..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 2e0c0e9f3260cbc9c77ef69b4174a452e4f174c3..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/toolkit/themes/shared/in-content/dropdown.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+   viewBox="0 0 16 16" enable-background="new 0 0 16 16" xml:space="preserve">
+  <style>
+    use:not(:target) {
+      display: none;
+    }
+    use {
+      fill: -moz-DialogText;
+    }
+    use[id$="-disabled"] {
+      fill: GrayText;
+    }
+  </style>
+  <defs style="display: none;">
+    <path id="dropdown-shape" fill-rule="evenodd" clip-rule="evenodd" d="M12,6l-4.016,4L4,6H12z"/>
+  </defs>
+  <use id="dropdown" xlink:href="#dropdown-shape"/>
+  <use id="dropdown-disabled" xlink:href="#dropdown-shape"/>
+</svg>
deleted file mode 100644
index be56b20bb2fc9ab54d0426a728cd3c9f8ea3c87d..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
--- a/toolkit/themes/windows/global/jar.mn
+++ b/toolkit/themes/windows/global/jar.mn
@@ -168,20 +168,17 @@ toolkit.jar:
         skin/classic/global/media/volume-full.png                (media/volume-full.png)
         skin/classic/global/media/error.png                      (media/error.png)
         skin/classic/global/media/clicktoplay-bgtexture.png      (media/clicktoplay-bgtexture.png)
         skin/classic/global/media/videoClickToPlayButton.svg     (media/videoClickToPlayButton.svg)
         skin/classic/global/menu/shared-menu-check.png           (../../shared/menu-check.png)
 *       skin/classic/global/in-content/common.css                (in-content/common.css)
         skin/classic/global/in-content/check.png                 (../../shared/in-content/check.png)
         skin/classic/global/in-content/check@2x.png              (../../shared/in-content/check@2x.png)
-        skin/classic/global/in-content/dropdown.png              (../../shared/in-content/dropdown.png)
-        skin/classic/global/in-content/dropdown@2x.png           (../../shared/in-content/dropdown@2x.png)
-        skin/classic/global/in-content/dropdown-disabled.png     (../../shared/in-content/dropdown-disabled.png)
-        skin/classic/global/in-content/dropdown-disabled@2x.png  (../../shared/in-content/dropdown-disabled@2x.png)
+        skin/classic/global/in-content/dropdown.svg              (../../shared/in-content/dropdown.svg)
         skin/classic/global/in-content/help-glyph.svg            (../../shared/in-content/help-glyph.svg)
         skin/classic/global/in-content/sorter.png                (../../shared/in-content/sorter.png)
         skin/classic/global/in-content/sorter@2x.png             (../../shared/in-content/sorter@2x.png)
         skin/classic/global/printpreview/arrow-left.png          (printpreview/arrow-left.png)
         skin/classic/global/printpreview/arrow-left-end.png      (printpreview/arrow-left-end.png)
         skin/classic/global/printpreview/arrow-right.png         (printpreview/arrow-right.png)
         skin/classic/global/printpreview/arrow-right-end.png     (printpreview/arrow-right-end.png)
         skin/classic/global/radio/radio-check.gif                (radio/radio-check.gif)
@@ -363,20 +360,17 @@ toolkit.jar:
         skin/classic/aero/global/media/volume-full.png                   (media/volume-full.png)
         skin/classic/aero/global/media/error.png                         (media/error.png)
         skin/classic/aero/global/media/clicktoplay-bgtexture.png         (media/clicktoplay-bgtexture.png)
         skin/classic/aero/global/media/videoClickToPlayButton.svg        (media/videoClickToPlayButton.svg)
         skin/classic/aero/global/menu/shared-menu-check.png              (../../shared/menu-check.png)
 *       skin/classic/aero/global/in-content/common.css                   (in-content/common.css)
         skin/classic/aero/global/in-content/check.png                    (../../shared/in-content/check.png)
         skin/classic/aero/global/in-content/check@2x.png                 (../../shared/in-content/check@2x.png)
-        skin/classic/aero/global/in-content/dropdown.png                 (../../shared/in-content/dropdown.png)
-        skin/classic/aero/global/in-content/dropdown@2x.png              (../../shared/in-content/dropdown@2x.png)
-        skin/classic/aero/global/in-content/dropdown-disabled.png        (../../shared/in-content/dropdown-disabled.png)
-        skin/classic/aero/global/in-content/dropdown-disabled@2x.png     (../../shared/in-content/dropdown-disabled@2x.png)
+        skin/classic/aero/global/in-content/dropdown.svg                 (../../shared/in-content/dropdown.svg)
         skin/classic/aero/global/in-content/help-glyph.svg               (../../shared/in-content/help-glyph.svg)
         skin/classic/aero/global/in-content/sorter.png                   (../../shared/in-content/sorter.png)
         skin/classic/aero/global/in-content/sorter@2x.png                (../../shared/in-content/sorter@2x.png)
         skin/classic/aero/global/printpreview/arrow-left.png             (printpreview/arrow-left-aero.png)
         skin/classic/aero/global/printpreview/arrow-left-end.png         (printpreview/arrow-left-end-aero.png)
         skin/classic/aero/global/printpreview/arrow-right.png            (printpreview/arrow-right-aero.png)
         skin/classic/aero/global/printpreview/arrow-right-end.png        (printpreview/arrow-right-end-aero.png)
         skin/classic/aero/global/radio/radio-check.gif                   (radio/radio-check.gif)