Merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 15 Dec 2015 14:52:14 +0100
changeset 276462 98d65afd982627842d25ee9877700f58606ca307
parent 276414 d447860f455055780dc9e14a9b80dc7a1133a20c (current diff)
parent 276461 cb66ffeb6725e8344818e8e2f707ae2eaeb953b4 (diff)
child 276463 4ebd9b147a2a49abe0a9b4144c4d8b1d3c9193ce
push id69162
push usercbook@mozilla.com
push dateTue, 15 Dec 2015 13:52:23 +0000
treeherdermozilla-inbound@98d65afd9826 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone46.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
dom/browser-element/mochitest/browserElement_AudioChannel_nested.js
dom/browser-element/mochitest/file_browserElement_AudioChannel_nested.html
dom/browser-element/mochitest/test_browserElement_inproc_AudioChannel_nested.html
dom/browser-element/mochitest/test_browserElement_oop_AudioChannel_nested.html
mobile/android/base/resources/drawable-hdpi/ic_menu_back.png
mobile/android/base/resources/drawable-hdpi/ic_menu_bookmark_add.png
mobile/android/base/resources/drawable-hdpi/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-hdpi/ic_menu_forward.png
mobile/android/base/resources/drawable-hdpi/ic_menu_new_private_tab.png
mobile/android/base/resources/drawable-hdpi/ic_menu_new_tab.png
mobile/android/base/resources/drawable-hdpi/ic_menu_reload.png
mobile/android/base/resources/drawable-xhdpi/ic_menu_back.png
mobile/android/base/resources/drawable-xhdpi/ic_menu_bookmark_add.png
mobile/android/base/resources/drawable-xhdpi/ic_menu_bookmark_remove.png
mobile/android/base/resources/drawable-xhdpi/ic_menu_forward.png
mobile/android/base/resources/drawable-xhdpi/ic_menu_new_private_tab.png
mobile/android/base/resources/drawable-xhdpi/ic_menu_new_tab.png
mobile/android/base/resources/drawable-xhdpi/ic_menu_reload.png
--- a/b2g/config/aries/sources.xml
+++ b/b2g/config/aries/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gecko and Gaia -->
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <!-- Gonk-specific things and forks -->
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
   <project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2d70fdfc0244a7217df1cfa7df9f4798cbfa3af6"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2d70fdfc0244a7217df1cfa7df9f4798cbfa3af6"/>
--- a/b2g/config/emulator-l/sources.xml
+++ b/b2g/config/emulator-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2d70fdfc0244a7217df1cfa7df9f4798cbfa3af6"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was git://github.com/mozilla/-->
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gecko and Gaia -->
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <!-- Gonk-specific things and forks -->
   <project name="platform_bionic" path="bionic" remote="b2g" revision="e2b3733ba3fa5e3f404e983d2e4142b1f6b1b846"/>
   <project name="platform_build" path="build" remote="b2g" revision="1b0db93fb6b870b03467aff50d6419771ba0d88c">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
   <project name="android-development" path="development" remote="b2g" revision="2bdf22305b523af644e1891b4ddfd9229336d0ce"/>
   <project name="platform_external_apriori" path="external/apriori" remote="b2g" revision="11816ad0406744f963537b23d68ed9c2afb412bd"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "ad15b4f1f50f4e28170a069f049e5bdeec8d15cc", 
+        "git_revision": "ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "f9f1dd1ec78eac04af587f54518e6d557209ca75", 
+    "revision": "6d6e0ff3866ac3a8f0a79efdb0188ebafca6f8be", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4-kk/sources.xml
+++ b/b2g/config/nexus-4-kk/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="8d83715f08b7849f16a0dfc88f78d5c3a89c0a54">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -13,17 +13,17 @@
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="660169a3d7e034a892359e39135e8c2785a6ad6f">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="2d70fdfc0244a7217df1cfa7df9f4798cbfa3af6"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
   <project name="platform_hardware_libhardware_moz" path="hardware/libhardware_moz" remote="b2g" revision="fdf3a143dc777e5f9d33a88373af7ea161d3b440"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
--- a/b2g/config/nexus-5-l/sources.xml
+++ b/b2g/config/nexus-5-l/sources.xml
@@ -10,17 +10,17 @@
   <!--original fetch url was git://codeaurora.org/-->
   <remote fetch="https://git.mozilla.org/external/caf" name="caf"/>
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="c9d4fe680662ee44a4bdea42ae00366f5df399cf">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="ad15b4f1f50f4e28170a069f049e5bdeec8d15cc"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="ee43a95f2bbe39c5ac8c9fcdec76749a56c02f2e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="fake-qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="939b377d55a2f081d94029a30a75d05e5a20daf3"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="4a962bdab532e18f53e9d2d114c349983262c6b7"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="ac7e9ae8a24ab4a3f3da801ca53f95f39a32b89f"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="5f931350fbc87c3df9db8b0ceb37734b8b471593"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="48d8c7c950745f1b166b42125e6f0d3293d71636"/>
--- a/devtools/client/debugger/debugger-view.js
+++ b/devtools/client/debugger/debugger-view.js
@@ -1,16 +1,15 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE = 1048576; // 1 MB in bytes
 const SOURCE_URL_DEFAULT_MAX_LENGTH = 64; // chars
 const STACK_FRAMES_SOURCE_URL_MAX_LENGTH = 15; // chars
 const STACK_FRAMES_SOURCE_URL_TRIM_SECTION = "center";
 const STACK_FRAMES_SCROLL_DELAY = 100; // ms
 const BREAKPOINT_SMALL_WINDOW_WIDTH = 850; // px
 const RESULTS_PANEL_POPUP_POSITION = "before_end";
 const RESULTS_PANEL_MAX_RESULTS = 10;
 const FILE_SEARCH_ACTION_MAX_DELAY = 300; // ms
@@ -42,16 +41,23 @@ var actions = Object.assign(
 );
 var queries = require('./content/queries');
 var constants = require('./content/constants');
 
 /**
  * Object defining the debugger view components.
  */
 var DebuggerView = {
+
+  /**
+   * This is attached so tests can change it without needing to load an
+   * actual large file in automation
+   */
+  LARGE_FILE_SIZE: 1048576, // 1 MB in bytes
+
   /**
    * Initializes the debugger view.
    *
    * @return object
    *         A promise that is resolved when the view finishes initializing.
    */
   initialize: function() {
     if (this._hasStartup) {
@@ -403,22 +409,16 @@ var DebuggerView = {
    * @param string aUrl
    *        The source url.
    * @param string aContentType [optional]
    *        The source content type.
    * @param string aTextContent [optional]
    *        The source text content.
    */
   _setEditorMode: function(aUrl, aContentType = "", aTextContent = "") {
-    // Avoid setting the editor mode for very large files.
-    // Is this still necessary? See bug 929225.
-    if (aTextContent.length >= SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) {
-      return void this.editor.setMode(Editor.modes.text);
-    }
-
     // Use JS mode for files with .js and .jsm extensions.
     if (SourceUtils.isJavaScript(aUrl, aContentType)) {
       return void this.editor.setMode(Editor.modes.js);
     }
 
     // Use HTML mode for files in which the first non whitespace character is
     // &lt;, regardless of extension.
     if (aTextContent.match(/^\s*</)) {
--- a/devtools/client/debugger/test/mochitest/browser.ini
+++ b/devtools/client/debugger/test/mochitest/browser.ini
@@ -441,16 +441,17 @@ skip-if = e10s # Bug 1093535
 skip-if = e10s && debug
 [browser_dbg_sources-eval-01.js]
 skip-if = true # non-named eval sources turned off for now, bug 1124106
 [browser_dbg_sources-eval-02.js]
 [browser_dbg_sources-keybindings.js]
 skip-if = e10s && debug
 [browser_dbg_sources-labels.js]
 skip-if = e10s && debug
+[browser_dbg_sources-large.js]
 [browser_dbg_sources-sorting.js]
 skip-if = e10s && debug
 [browser_dbg_sources-bookmarklet.js]
 skip-if = e10s && debug
 [browser_dbg_split-console-paused-reload.js]
 skip-if = e10s && debug
 [browser_dbg_stack-01.js]
 skip-if = e10s && debug
new file mode 100644
--- /dev/null
+++ b/devtools/client/debugger/test/mochitest/browser_dbg_sources-large.js
@@ -0,0 +1,75 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that large files are treated differently in the debugger:
+ *   1) No parsing to determine current symbol is attempted when
+ *      starting a search
+ */
+
+const TAB_URL = EXAMPLE_URL + "doc_function-search.html";
+
+function test() {
+  initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => {
+    const gTab = aTab;
+    const gDebuggee = aDebuggee;
+    const gPanel = aPanel;
+    const gDebugger = gPanel.panelWin;
+
+    const gEditor = gDebugger.DebuggerView.editor;
+    const gSources = gDebugger.DebuggerView.Sources;
+    const Filtering = gDebugger.DebuggerView.Filtering;
+
+    // Setting max size so that code_function-search-01.js will be
+    // considered a large file on first load
+    gDebugger.DebuggerView.LARGE_FILE_SIZE = 1;
+
+    function testLargeFile() {
+      ok(gEditor.getText().length > gDebugger.DebuggerView.LARGE_FILE_SIZE,
+         "First source is considered a large file.");
+      is(gEditor.getMode().name, "javascript",
+         "Editor is syntax highlighting.");
+      ok(gEditor.getText().includes("First source!"),
+         "Editor text contents appears to be correct.");
+
+      // Press ctrl+f with the cursor in a token
+      gEditor.focus();
+      gEditor.setCursor({ line: 3, ch: 10});
+      synthesizeKeyFromKeyTag(gDebugger.document.getElementById("tokenSearchKey"));
+      is(Filtering._searchbox.value, "#",
+        "Search box is NOT prefilled with current token");
+    }
+
+    function testSmallFile() {
+      ok(gEditor.getText().length < gDebugger.DebuggerView.LARGE_FILE_SIZE,
+         "Second source is considered a small file.");
+      is(gEditor.getMode().name, "javascript",
+         "Editor is syntax highlighting.");
+      ok(gEditor.getText().includes("First source!"),
+         "Editor text contents appears to be correct.");
+
+      // Press ctrl+f with the cursor in a token
+      gEditor.focus();
+      gEditor.setCursor({ line: 3, ch: 10});
+      synthesizeKeyFromKeyTag(gDebugger.document.getElementById("tokenSearchKey"));
+      is(Filtering._searchbox.value, "#test",
+        "Search box is prefilled with current token");
+    }
+
+    Task.spawn(function*() {
+      yield waitForSourceShown(gPanel, "-01.js");
+      yield testLargeFile();
+
+      info("Making it appear as a small file and then reselecting 01.js");
+      gDebugger.DebuggerView.LARGE_FILE_SIZE = 1000;
+      gSources.selectedIndex = 1;
+      yield waitForSourceShown(gPanel, "-02.js");
+      gSources.selectedIndex = 0;
+      yield waitForSourceShown(gPanel, "-01.js");
+
+      yield testSmallFile();
+
+      closeDebuggerAndFinish(gPanel);
+    });
+  });
+}
--- a/devtools/client/debugger/views/filter-view.js
+++ b/devtools/client/debugger/views/filter-view.js
@@ -438,19 +438,21 @@ FilterView.prototype = {
     if (aText) {
       this._searchbox.value = aOperator + aText;
       return;
     }
     if (this.DebuggerView.editor.somethingSelected()) {
       this._searchbox.value = aOperator + this.DebuggerView.editor.getSelection();
       return;
     }
-    if (SEARCH_AUTOFILL.indexOf(aOperator) != -1) {
+
+    let content = this.DebuggerView.editor.getText();
+    if (content.length < this.DebuggerView.LARGE_FILE_SIZE &&
+        SEARCH_AUTOFILL.indexOf(aOperator) != -1) {
       let cursor = this.DebuggerView.editor.getCursor();
-      let content = this.DebuggerView.editor.getText();
       let location = this.DebuggerView.Sources.selectedItem.attachment.source.url;
       let source = this.Parser.get(content, location);
       let identifier = source.getIdentifierAt({ line: cursor.line+1, column: cursor.ch });
 
       if (identifier && identifier.name) {
         this._searchbox.value = aOperator + identifier.name;
         this._searchbox.select();
         this._searchbox.selectionStart += aOperator.length;
--- a/devtools/client/locales/en-US/netmonitor.dtd
+++ b/devtools/client/locales/en-US/netmonitor.dtd
@@ -19,17 +19,17 @@
 <!-- LOCALIZATION NOTE (netmonitorUI.reload1/2/3): These are the labels displayed
   -  in the network table when empty to start logging network requests. -->
 <!ENTITY netmonitorUI.reloadNotice1       "• Perform a request or">
 <!ENTITY netmonitorUI.reloadNotice2       "Reload">
 <!ENTITY netmonitorUI.reloadNotice3       "the page to see detailed information about network activity.">
 
 <!-- LOCALIZATION NOTE (netmonitorUI.toolbar.status2): This is the label displayed
   -  in the network table toolbar, above the "status" column. -->
-<!ENTITY netmonitorUI.toolbar.status2     "✓">
+<!ENTITY netmonitorUI.toolbar.status3     "Status">
 
 <!-- LOCALIZATION NOTE (netmonitorUI.toolbar.method): This is the label displayed
   -  in the network table toolbar, above the "method" column. -->
 <!ENTITY netmonitorUI.toolbar.method      "Method">
 
 <!-- LOCALIZATION NOTE (netmonitorUI.toolbar.file): This is the label displayed
   -  in the network table toolbar, above the "file" column. -->
 <!ENTITY netmonitorUI.toolbar.file        "File">
--- a/devtools/client/netmonitor/netmonitor.css
+++ b/devtools/client/netmonitor/netmonitor.css
@@ -34,13 +34,12 @@
 }
 
 /* Responsive sidebar */
 @media (max-width: 700px) {
   #toolbar-spacer,
   #details-pane-toggle,
   #details-pane[pane-collapsed],
   .requests-menu-waterfall,
-  .requests-menu-footer-label,
-  .requests-menu-status-code {
+  .requests-menu-footer-label {
     display: none;
   }
 }
--- a/devtools/client/netmonitor/netmonitor.xul
+++ b/devtools/client/netmonitor/netmonitor.xul
@@ -102,17 +102,17 @@
                    align="center">
             <hbox id="toolbar-labels" flex="1">
               <hbox id="requests-menu-status-header-box"
                     class="requests-menu-header requests-menu-status"
                     align="center">
                 <button id="requests-menu-status-button"
                         class="requests-menu-header-button requests-menu-status"
                         data-key="status"
-                        label="&netmonitorUI.toolbar.status2;"
+                        label="&netmonitorUI.toolbar.status3;"
                         flex="1">
                 </button>
               </hbox>
               <hbox id="requests-menu-method-header-box"
                     class="requests-menu-header requests-menu-method"
                     align="center">
                 <button id="requests-menu-method-button"
                         class="requests-menu-header-button requests-menu-method"
--- a/devtools/client/netmonitor/test/browser_net_timeline_ticks.js
+++ b/devtools/client/netmonitor/test/browser_net_timeline_ticks.js
@@ -4,17 +4,17 @@
 /**
  * Tests if timeline correctly displays interval divisions.
  */
 
 function test() {
   initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
     info("Starting test... ");
 
-    let { document, L10N, NetMonitorView } = aMonitor.panelWin;
+    let { document, L10N, NetMonitorView, NetMonitorController } = aMonitor.panelWin;
     let { RequestsMenu } = NetMonitorView;
 
     // Disable transferred size column support for this test.
     // Without this, the waterfall only has enough room for one division, which
     // would remove most of the value of this test.
     document.querySelector("#requests-menu-transferred-header-box").hidden = true;
     document.querySelector("#requests-menu-item-template .requests-menu-transferred").hidden = true;
 
@@ -26,16 +26,21 @@ function test() {
       "No tick labels should be displayed when the frontend is opened.");
 
     ok(!RequestsMenu._canvas,
       "No canvas should be created when the frontend is opened.");
     ok(!RequestsMenu._ctx,
       "No 2d context should be created when the frontend is opened.");
 
     waitForNetworkEvents(aMonitor, 1).then(() => {
+      // Make sure the DOMContentLoaded and load markers don't interfere with
+      // this test by removing them and redrawing the waterfall (bug 1224088).
+      NetMonitorController.NetworkEventsHandler.clearMarkers();
+      RequestsMenu._flushWaterfallViews(true);
+
       ok(!document.querySelector("#requests-menu-waterfall-label"),
         "The timeline label should be hidden after the first request.");
       ok(document.querySelectorAll(".requests-menu-timings-division").length >= 3,
         "There should be at least 3 tick labels in the network requests header.");
 
       is(document.querySelectorAll(".requests-menu-timings-division")[0]
         .getAttribute("value"), L10N.getFormatStr("networkMenu.millisecond", 0),
         "The first tick label has an incorrect value");
--- a/devtools/client/performance/modules/widgets/tree-view.js
+++ b/devtools/client/performance/modules/widgets/tree-view.js
@@ -230,17 +230,18 @@ CallView.prototype = Heritage.extend(Abs
    * Functions creating each cell in this call view.
    * Invoked by `_displaySelf`.
    */
   _createCell: function (doc, value, type) {
     let cell = doc.createElement("description");
     cell.className = "plain call-tree-cell";
     cell.setAttribute("type", type);
     cell.setAttribute("crop", "end");
-    cell.setAttribute("value", value);
+    // Add a tabulation to the cell text in case it's is selected and copied.
+    cell.textContent = value + "\t";
     return cell;
   },
 
   _createFunctionCell: function(doc, arrowNode, frameName, frameInfo, frameLevel) {
     let cell = doc.createElement("hbox");
     cell.className = "call-tree-cell";
     cell.style.MozMarginStart = (frameLevel * CALL_TREE_INDENTATION) + "px";
     cell.setAttribute("type", "function");
@@ -256,76 +257,87 @@ CallView.prototype = Heritage.extend(Abs
 
     // Don't render a name label node if there's no function name. A different
     // location label node will be rendered instead.
     if (frameName) {
       let nameNode = doc.createElement("description");
       nameNode.className = "plain call-tree-name";
       nameNode.setAttribute("flex", "1");
       nameNode.setAttribute("crop", "end");
-      nameNode.setAttribute("value", frameName);
+      nameNode.textContent = frameName;
       cell.appendChild(nameNode);
     }
 
     // Don't render detailed labels for meta category frames
     if (!frameInfo.isMetaCategory) {
       this._appendFunctionDetailsCells(doc, cell, frameInfo);
     }
 
     // Don't render an expando-arrow for leaf nodes.
     let hasDescendants = Object.keys(this.frame.calls).length > 0;
     if (!hasDescendants) {
       arrowNode.setAttribute("invisible", "");
     }
 
+    // Add a line break to the last description of the row in case it's selected
+    // and copied.
+    let lastDescription = cell.querySelector('description:last-of-type');
+    lastDescription.textContent = lastDescription.textContent + "\n";
+
+    // Add spaces as frameLevel indicators in case the row is selected and
+    // copied. These spaces won't be displayed in the cell content.
+    let firstDescription = cell.querySelector('description:first-of-type');
+    let levelIndicator = frameLevel > 0 ? " ".repeat(frameLevel) : "";
+    firstDescription.textContent = levelIndicator + firstDescription.textContent;
+
     return cell;
   },
 
   _appendFunctionDetailsCells: function(doc, cell, frameInfo) {
     if (frameInfo.fileName) {
       let urlNode = doc.createElement("description");
       urlNode.className = "plain call-tree-url";
       urlNode.setAttribute("flex", "1");
       urlNode.setAttribute("crop", "end");
-      urlNode.setAttribute("value", frameInfo.fileName);
+      urlNode.textContent = frameInfo.fileName;
       urlNode.setAttribute("tooltiptext", URL_LABEL_TOOLTIP + " → " + frameInfo.url);
       urlNode.addEventListener("mousedown", this._onUrlClick);
       cell.appendChild(urlNode);
     }
 
     if (frameInfo.line) {
       let lineNode = doc.createElement("description");
       lineNode.className = "plain call-tree-line";
-      lineNode.setAttribute("value", ":" + frameInfo.line);
+      lineNode.textContent = ":" + frameInfo.line;
       cell.appendChild(lineNode);
     }
 
     if (frameInfo.column) {
       let columnNode = doc.createElement("description");
       columnNode.className = "plain call-tree-column";
-      columnNode.setAttribute("value", ":" + frameInfo.column);
+      columnNode.textContent = ":" + frameInfo.column;
       cell.appendChild(columnNode);
     }
 
     if (frameInfo.host) {
       let hostNode = doc.createElement("description");
       hostNode.className = "plain call-tree-host";
-      hostNode.setAttribute("value", frameInfo.host);
+      hostNode.textContent = frameInfo.host;
       cell.appendChild(hostNode);
     }
 
     let spacerNode = doc.createElement("spacer");
     spacerNode.setAttribute("flex", "10000");
     cell.appendChild(spacerNode);
 
     if (frameInfo.categoryData.label) {
       let categoryNode = doc.createElement("description");
       categoryNode.className = "plain call-tree-category";
       categoryNode.style.color = frameInfo.categoryData.color;
-      categoryNode.setAttribute("value", frameInfo.categoryData.label);
+      categoryNode.textContent = frameInfo.categoryData.label;
       cell.appendChild(categoryNode);
     }
   },
 
   /**
    * Gets the data displayed about this tree item, based on the FrameNode
    * model associated with this view.
    *
--- a/devtools/client/performance/test/browser_perf-categories-js-calltree.js
+++ b/devtools/client/performance/test/browser_perf-categories-js-calltree.js
@@ -2,37 +2,46 @@
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /**
  * Tests that the categories are shown in the js call tree when platform data
  * is enabled.
  */
 function* spawnTest() {
   let { panel } = yield initPerformance(SIMPLE_URL);
-  let { EVENTS, $, DetailsView, JsCallTreeView } = panel.panelWin;
+  let { EVENTS, $, $$, DetailsView, JsCallTreeView } = panel.panelWin;
 
   // Enable platform data to show the categories.
   Services.prefs.setBoolPref(PLATFORM_DATA_PREF, true);
 
   yield startRecording(panel);
   yield busyWait(100);
 
   let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
   yield stopRecording(panel);
   yield DetailsView.selectView("js-calltree");
   yield rendered;
 
   is($(".call-tree-cells-container").hasAttribute("categories-hidden"), false,
     "The call tree cells container should show the categories now.");
-  ok($(".call-tree-category[value=Gecko]"),
-    "A category node with the label `Gecko` is displayed in the tree.");
+  ok(geckoCategoryPresent($$),
+    "A category node with the text `Gecko` is displayed in the tree.");
 
   // Disable platform data to show the categories.
   Services.prefs.setBoolPref(PLATFORM_DATA_PREF, false);
 
   is($(".call-tree-cells-container").getAttribute("categories-hidden"), "",
     "The call tree cells container should hide the categories now.");
-  ok(!$(".call-tree-category[value=Gecko]"),
-    "A category node with the label `Gecko` doesn't exist in the tree anymore.");
+  ok(!geckoCategoryPresent($$),
+    "A category node with the text `Gecko` doesn't exist in the tree anymore.");
 
   yield teardown(panel);
   finish();
 }
+
+function geckoCategoryPresent($$) {
+  for (let elem of $$('.call-tree-category')) {
+    if (elem.textContent.trim() == 'Gecko') {
+      return true
+    }
+  }
+  return false
+}
--- a/devtools/client/performance/test/browser_profiler_tree-view-01.js
+++ b/devtools/client/performance/test/browser_profiler_tree-view-01.js
@@ -26,37 +26,37 @@ function test() {
 
   is(container.childNodes[0].childNodes.length, 6,
     "The root node in the tree has the correct number of children.");
   is(container.childNodes[0].querySelectorAll(".call-tree-cell").length, 6,
     "The root node in the tree has only 6 'call-tree-cell' children.");
 
   is(container.childNodes[0].childNodes[0].getAttribute("type"), "duration",
     "The root node in the tree has a duration cell.");
-  is(container.childNodes[0].childNodes[0].getAttribute("value"), "20 ms",
+  is(container.childNodes[0].childNodes[0].textContent.trim(), "20 ms",
     "The root node in the tree has the correct duration cell value.");
 
   is(container.childNodes[0].childNodes[1].getAttribute("type"), "percentage",
     "The root node in the tree has a percentage cell.");
-  is(container.childNodes[0].childNodes[1].getAttribute("value"), "100%",
+  is(container.childNodes[0].childNodes[1].textContent.trim(), "100%",
     "The root node in the tree has the correct percentage cell value.");
 
   is(container.childNodes[0].childNodes[2].getAttribute("type"), "self-duration",
     "The root node in the tree has a self-duration cell.");
-  is(container.childNodes[0].childNodes[2].getAttribute("value"), "0 ms",
+  is(container.childNodes[0].childNodes[2].textContent.trim(), "0 ms",
     "The root node in the tree has the correct self-duration cell value.");
 
   is(container.childNodes[0].childNodes[3].getAttribute("type"), "self-percentage",
     "The root node in the tree has a self-percentage cell.");
-  is(container.childNodes[0].childNodes[3].getAttribute("value"), "0%",
+  is(container.childNodes[0].childNodes[3].textContent.trim(), "0%",
     "The root node in the tree has the correct self-percentage cell value.");
 
   is(container.childNodes[0].childNodes[4].getAttribute("type"), "samples",
     "The root node in the tree has an samples cell.");
-  is(container.childNodes[0].childNodes[4].getAttribute("value"), "0",
+  is(container.childNodes[0].childNodes[4].textContent.trim(), "0",
     "The root node in the tree has the correct samples cell value.");
 
   is(container.childNodes[0].childNodes[5].getAttribute("type"), "function",
     "The root node in the tree has a function cell.");
   is(container.childNodes[0].childNodes[5].style.MozMarginStart, "0px",
     "The root node in the tree has the correct indentation.");
 
   finish();
--- a/devtools/client/performance/test/browser_profiler_tree-view-02.js
+++ b/devtools/client/performance/test/browser_profiler_tree-view-02.js
@@ -28,23 +28,23 @@ function test() {
   let $$perc = i => container.querySelectorAll(".call-tree-cell[type=percentage]")[i];
   let $$sampl = i => container.querySelectorAll(".call-tree-cell[type=samples]")[i];
 
   is(container.childNodes.length, 1,
     "The container node should have one child available.");
   is(container.childNodes[0].className, "call-tree-item",
     "The root node in the tree has the correct class name.");
 
-  is($$dur(0).getAttribute("value"), "20 ms",
+  is($$dur(0).textContent.trim(), "20 ms",
     "The root's duration cell displays the correct value.");
-  is($$perc(0).getAttribute("value"), "100%",
+  is($$perc(0).textContent.trim(), "100%",
     "The root's percentage cell displays the correct value.");
-  is($$sampl(0).getAttribute("value"), "0",
+  is($$sampl(0).textContent.trim(), "0",
     "The root's samples cell displays the correct value.");
-  is($$fun(".call-tree-name")[0].getAttribute("value"), "(root)",
+  is($$fun(".call-tree-name")[0].textContent.trim(), "(root)",
     "The root's function cell displays the correct name.");
   is($$fun(".call-tree-url")[0], null,
     "The root's function cell displays no url.");
   is($$fun(".call-tree-line")[0], null,
     "The root's function cell displays no line.");
   is($$fun(".call-tree-host")[0], null,
     "The root's function cell displays the correct host.");
   is($$fun(".call-tree-category")[0], null,
@@ -54,81 +54,81 @@ function test() {
 
   is(container.childNodes.length, 2,
     "The container node should have two children available.");
   is(container.childNodes[0].className, "call-tree-item",
     "The root node in the tree has the correct class name.");
   is(container.childNodes[1].className, "call-tree-item",
     "The .A node in the tree has the correct class name.");
 
-  is($$dur(1).getAttribute("value"), "20 ms",
+  is($$dur(1).textContent.trim(), "20 ms",
     "The .A node's duration cell displays the correct value.");
-  is($$perc(1).getAttribute("value"), "100%",
+  is($$perc(1).textContent.trim(), "100%",
     "The .A node's percentage cell displays the correct value.");
-  is($$sampl(1).getAttribute("value"), "0",
+  is($$sampl(1).textContent.trim(), "0",
     "The .A node's samples cell displays the correct value.");
-  is($fun(".call-tree-name", $$(".call-tree-item")[1]).getAttribute("value"), "A",
+  is($fun(".call-tree-name", $$(".call-tree-item")[1]).textContent.trim(), "A",
     "The .A node's function cell displays the correct name.");
-  is($fun(".call-tree-url", $$(".call-tree-item")[1]).getAttribute("value"), "baz",
+  is($fun(".call-tree-url", $$(".call-tree-item")[1]).textContent.trim(), "baz",
     "The .A node's function cell displays the correct url.");
   ok($fun(".call-tree-url", $$(".call-tree-item")[1]).getAttribute("tooltiptext").includes("http://foo/bar/baz"),
     "The .A node's function cell displays the correct url tooltiptext.");
-  is($fun(".call-tree-line", $$(".call-tree-item")[1]).getAttribute("value"), ":12",
+  is($fun(".call-tree-line", $$(".call-tree-item")[1]).textContent.trim(), ":12",
     "The .A node's function cell displays the correct line.");
-  is($fun(".call-tree-host", $$(".call-tree-item")[1]).getAttribute("value"), "foo",
+  is($fun(".call-tree-host", $$(".call-tree-item")[1]).textContent.trim(), "foo",
     "The .A node's function cell displays the correct host.");
-  is($fun(".call-tree-category", $$(".call-tree-item")[1]).getAttribute("value"), "Gecko",
+  is($fun(".call-tree-category", $$(".call-tree-item")[1]).textContent.trim(), "Gecko",
     "The .A node's function cell displays the correct category.");
 
   let A = treeRoot.getChild();
   A.expand();
 
   is(container.childNodes.length, 4,
     "The container node should have four children available.");
   is(container.childNodes[2].className, "call-tree-item",
     "The .B node in the tree has the correct class name.");
   is(container.childNodes[3].className, "call-tree-item",
     "The .E node in the tree has the correct class name.");
 
-  is($$dur(2).getAttribute("value"), "15 ms",
+  is($$dur(2).textContent.trim(), "15 ms",
     "The .A.B node's duration cell displays the correct value.");
-  is($$perc(2).getAttribute("value"), "75%",
+  is($$perc(2).textContent.trim(), "75%",
     "The .A.B node's percentage cell displays the correct value.");
-  is($$sampl(2).getAttribute("value"), "0",
+  is($$sampl(2).textContent.trim(), "0",
     "The .A.B node's samples cell displays the correct value.");
-  is($fun(".call-tree-name", $$(".call-tree-item")[2]).getAttribute("value"), "B",
+  is($fun(".call-tree-name", $$(".call-tree-item")[2]).textContent.trim(), "B",
     "The .A.B node's function cell displays the correct name.");
-  is($fun(".call-tree-url", $$(".call-tree-item")[2]).getAttribute("value"), "baz",
+  is($fun(".call-tree-url", $$(".call-tree-item")[2]).textContent.trim(), "baz",
     "The .A.B node's function cell displays the correct url.");
   ok($fun(".call-tree-url", $$(".call-tree-item")[2]).getAttribute("tooltiptext").includes("http://foo/bar/baz"),
     "The .A.B node's function cell displays the correct url tooltiptext.");
-  is($fun(".call-tree-line", $$(".call-tree-item")[2]).getAttribute("value"), ":34",
+  is($fun(".call-tree-line", $$(".call-tree-item")[2]).textContent.trim(), ":34",
     "The .A.B node's function cell displays the correct line.");
-  is($fun(".call-tree-host", $$(".call-tree-item")[2]).getAttribute("value"), "foo",
+  is($fun(".call-tree-host", $$(".call-tree-item")[2]).textContent.trim(), "foo",
     "The .A.B node's function cell displays the correct host.");
-  is($fun(".call-tree-category", $$(".call-tree-item")[2]).getAttribute("value"), "Styles",
+  is($fun(".call-tree-category", $$(".call-tree-item")[2]).textContent.trim(), "Styles",
     "The .A.B node's function cell displays the correct category.");
 
-  is($$dur(3).getAttribute("value"), "5 ms",
+  is($$dur(3).textContent.trim(), "5 ms",
     "The .A.E node's duration cell displays the correct value.");
-  is($$perc(3).getAttribute("value"), "25%",
+  is($$perc(3).textContent.trim(), "25%",
     "The .A.E node's percentage cell displays the correct value.");
-  is($$sampl(3).getAttribute("value"), "0",
+  is($$sampl(3).textContent.trim(), "0",
     "The .A.E node's samples cell displays the correct value.");
-  is($fun(".call-tree-name", $$(".call-tree-item")[3]).getAttribute("value"), "E",
+  is($fun(".call-tree-name", $$(".call-tree-item")[3]).textContent.trim(), "E",
     "The .A.E node's function cell displays the correct name.");
-  is($fun(".call-tree-url", $$(".call-tree-item")[3]).getAttribute("value"), "baz",
+  is($fun(".call-tree-url", $$(".call-tree-item")[3]).textContent.trim(), "baz",
     "The .A.E node's function cell displays the correct url.");
   ok($fun(".call-tree-url", $$(".call-tree-item")[3]).getAttribute("tooltiptext").includes("http://foo/bar/baz"),
     "The .A.E node's function cell displays the correct url tooltiptext.");
-  is($fun(".call-tree-line", $$(".call-tree-item")[3]).getAttribute("value"), ":90",
+  is($fun(".call-tree-line", $$(".call-tree-item")[3]).textContent.trim(), ":90",
     "The .A.E node's function cell displays the correct line.");
-  is($fun(".call-tree-host", $$(".call-tree-item")[3]).getAttribute("value"), "foo",
+  is($fun(".call-tree-host", $$(".call-tree-item")[3]).textContent.trim(), "foo",
     "The .A.E node's function cell displays the correct host.");
-  is($fun(".call-tree-category", $$(".call-tree-item")[3]).getAttribute("value"), "GC",
+  is($fun(".call-tree-category", $$(".call-tree-item")[3]).textContent.trim(), "GC",
     "The .A.E node's function cell displays the correct category.");
 
   finish();
 }
 
 var gThread = synthesizeProfileForTest([{
   time: 5,
   frames: [
--- a/devtools/client/performance/test/browser_profiler_tree-view-03.js
+++ b/devtools/client/performance/test/browser_profiler_tree-view-03.js
@@ -37,44 +37,44 @@ function test() {
     "The .A.B.D node's function cell has the correct indentation.");
   is($$fun(4).style.MozMarginStart, "48px",
     "The .A.B.C node's function cell has the correct indentation.");
   is($$fun(5).style.MozMarginStart, "32px",
     "The .A.E node's function cell has the correct indentation.");
   is($$fun(6).style.MozMarginStart, "48px",
     "The .A.E.F node's function cell has the correct indentation.");
 
-  is($$name(0).getAttribute("value"), "(root)",
+  is($$name(0).textContent.trim(), "(root)",
     "The root node's function cell displays the correct name.");
-  is($$name(1).getAttribute("value"), "A",
+  is($$name(1).textContent.trim(), "A",
     "The .A node's function cell displays the correct name.");
-  is($$name(2).getAttribute("value"), "B",
+  is($$name(2).textContent.trim(), "B",
     "The .A.B node's function cell displays the correct name.");
-  is($$name(3).getAttribute("value"), "D",
+  is($$name(3).textContent.trim(), "D",
     "The .A.B.D node's function cell displays the correct name.");
-  is($$name(4).getAttribute("value"), "C",
+  is($$name(4).textContent.trim(), "C",
     "The .A.B.C node's function cell displays the correct name.");
-  is($$name(5).getAttribute("value"), "E",
+  is($$name(5).textContent.trim(), "E",
     "The .A.E node's function cell displays the correct name.");
-  is($$name(6).getAttribute("value"), "F",
+  is($$name(6).textContent.trim(), "F",
     "The .A.E.F node's function cell displays the correct name.");
 
-  is($$duration(0).getAttribute("value"), "20 ms",
+  is($$duration(0).textContent.trim(), "20 ms",
     "The root node's function cell displays the correct duration.");
-  is($$duration(1).getAttribute("value"), "20 ms",
+  is($$duration(1).textContent.trim(), "20 ms",
     "The .A node's function cell displays the correct duration.");
-  is($$duration(2).getAttribute("value"), "15 ms",
+  is($$duration(2).textContent.trim(), "15 ms",
     "The .A.B node's function cell displays the correct duration.");
-  is($$duration(3).getAttribute("value"), "10 ms",
+  is($$duration(3).textContent.trim(), "10 ms",
     "The .A.B.D node's function cell displays the correct duration.");
-  is($$duration(4).getAttribute("value"), "5 ms",
+  is($$duration(4).textContent.trim(), "5 ms",
     "The .A.B.C node's function cell displays the correct duration.");
-  is($$duration(5).getAttribute("value"), "5 ms",
+  is($$duration(5).textContent.trim(), "5 ms",
     "The .A.E node's function cell displays the correct duration.");
-  is($$duration(6).getAttribute("value"), "5 ms",
+  is($$duration(6).textContent.trim(), "5 ms",
     "The .A.E.F node's function cell displays the correct duration.");
 
   finish();
 }
 
 var gThread = synthesizeProfileForTest([{
   time: 5,
   frames: [
@@ -103,9 +103,8 @@ var gThread = synthesizeProfileForTest([
   time: 5 + 1 + 2 + 7,
   frames: [
     { category: 8,   location: "(root)" },
     { category: 8,   location: "A (http://foo/bar/baz:12)" },
     { category: 128, location: "E (http://foo/bar/baz:90)" },
     { category: 256, location: "F (http://foo/bar/baz:99)" }
   ]
 }]);
-
--- a/devtools/client/performance/test/browser_profiler_tree-view-08.js
+++ b/devtools/client/performance/test/browser_profiler_tree-view-08.js
@@ -38,31 +38,31 @@ function test() {
   let JS = treeRoot.getChild(1);
   let GC = A.getChild(1);
   let JS2 = A.getChild(2).getChild().getChild();
 
   is(JS.target.getAttribute("category"), "js",
     "Generalized JS node has correct category");
   is(JS.target.getAttribute("tooltiptext"), "JIT",
     "Generalized JS node has correct category");
-  is(JS.target.querySelector(".call-tree-name").getAttribute("value"), "JIT",
+  is(JS.target.querySelector(".call-tree-name").textContent.trim(), "JIT",
     "Generalized JS node has correct display value as just the category name.");
 
   is(JS2.target.getAttribute("category"), "js",
     "Generalized second JS node has correct category");
   is(JS2.target.getAttribute("tooltiptext"), "JIT",
     "Generalized second JS node has correct category");
-  is(JS2.target.querySelector(".call-tree-name").getAttribute("value"), "JIT",
+  is(JS2.target.querySelector(".call-tree-name").textContent.trim(), "JIT",
     "Generalized second JS node has correct display value as just the category name.");
 
   is(GC.target.getAttribute("category"), "gc",
     "Generalized GC node has correct category");
   is(GC.target.getAttribute("tooltiptext"), "GC",
     "Generalized GC node has correct category");
-  is(GC.target.querySelector(".call-tree-name").getAttribute("value"), "GC",
+  is(GC.target.querySelector(".call-tree-name").textContent.trim(), "GC",
     "Generalized GC node has correct display value as just the category name.");
 
   finish();
 }
 
 var gThread = synthesizeProfileForTest([{
   time: 5,
   frames: [
--- a/devtools/client/performance/test/browser_profiler_tree-view-10.js
+++ b/devtools/client/performance/test/browser_profiler_tree-view-10.js
@@ -58,19 +58,19 @@ function test() {
     [ 40,  0, "    A"],
     [ 10, 10, "B"],
     [ 10,  0, "  A"],
   ].forEach(function (def, i) {
     info(`Checking ${i}th tree item`);
     let [total, self, name] = def;
     name = name.trim();
 
-    is($$name(i).getAttribute("value"), name, `${name} has correct name.`);
-    is($$percentage(i).getAttribute("value"), `${total}%`, `${name} has correct total percent.`);
-    is($$selfpercentage(i).getAttribute("value"), `${self}%`, `${name} has correct self percent.`);
+    is($$name(i).textContent.trim(), name, `${name} has correct name.`);
+    is($$percentage(i).textContent.trim(), `${total}%`, `${name} has correct total percent.`);
+    is($$selfpercentage(i).textContent.trim(), `${self}%`, `${name} has correct self percent.`);
   });
 
   finish();
 }
 
 var gThread = synthesizeProfileForTest([{
   time: 5,
   frames: [
--- a/devtools/client/performance/test/browser_profiler_tree-view-11.js
+++ b/devtools/client/performance/test/browser_profiler_tree-view-11.js
@@ -26,17 +26,17 @@ function* spawnTest() {
 
   yield DetailsView.selectView("js-calltree");
 
   yield injectAndRenderProfilerData();
 
   let rows = $$("#js-calltree-view .call-tree-item");
   is(rows.length, 4, "4 call tree rows exist");
   for (let row of rows) {
-    let name = $(".call-tree-name", row).value;
+    let name = $(".call-tree-name", row).textContent.trim();
     switch (name) {
       case "A":
         ok($(".opt-icon", row), "found an opt icon on a leaf node with opt data");
         break;
       case "C":
         ok(!$(".opt-icon", row), "frames without opt data do not have an icon");
         break;
       case "Gecko":
--- a/devtools/client/shared/widgets/AbstractTreeItem.jsm
+++ b/devtools/client/shared/widgets/AbstractTreeItem.jsm
@@ -517,17 +517,16 @@ AbstractTreeItem.prototype = {
       this.collapse();
     }
   },
 
   /**
    * Handler for the "click" event on the element displaying this tree item.
    */
   _onClick: function(e) {
-    e.preventDefault();
     e.stopPropagation();
     this.focus();
   },
 
   /**
    * Handler for the "dblclick" event on the element displaying this tree item.
    */
   _onDoubleClick: function(e) {
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -111,21 +111,17 @@
 
 .requests-menu-header-button[sorted],
 .requests-menu-header[active] + .requests-menu-header .requests-menu-header-button {
   border-image: linear-gradient(var(--theme-splitter-color), var(--theme-splitter-color)) 1 1;
 }
 
 /* Network requests table: specific column dimensions */
 
-.requests-menu-status {
-  max-width: 4em;
-  width: 4vw;
-}
-
+.requests-menu-status,
 .requests-menu-method-box,
 .requests-menu-method {
   max-width: 6em;
   text-align: center;
   width: 10vw;
 }
 
 .requests-menu-icon-and-file {
@@ -178,20 +174,16 @@
 .requests-menu-type,
 .requests-menu-transferred,
 .requests-menu-size {
   max-width: 8em;
   text-align: center;
   width: 8vw;
 }
 
-.requests-menu-transferred {
-  width: 8vw;
-}
-
 /* Network requests table: status codes */
 
 .requests-menu-status-code {
   -moz-margin-start: 3px !important;
   width: 3em;
   -moz-margin-end: -3em !important;
 }
 
@@ -790,44 +782,45 @@
   }
 
   #details-pane {
     margin: 0 !important;
     /* To prevent all the margin hacks to hide the sidebar. */
   }
 
   .requests-menu-status {
-    width: 4vw;
+    max-width: none;
+    width: 12vw;
   }
 
-  #requests-menu-status-button {
-    min-width: 26px;
+  .requests-menu-status-code {
+    width: auto;
   }
 
   .requests-menu-method,
   .requests-menu-method-box {
     max-width: none;
     width: 14vw;
   }
 
   .requests-menu-icon-and-file {
-    width: 30vw;
+    width: 22vw;
   }
 
   .requests-menu-security-and-domain {
-    width: 28vw;
+    width: 18vw;
   }
 
-  .requests-menu-type,
-  .requests-menu-transferred {
-    width: 12vw;
+  .requests-menu-type {
+    width: 10vw;
   }
 
+  .requests-menu-transferred,
   .requests-menu-size {
-    width: 16vw;
+    width: 12vw;
   }
 }
 
 /* Platform overrides (copied in from the old platform specific files) */
 %ifdef XP_WIN
 .requests-menu-header-button > .button-box {
   padding: 0;
 }
--- a/devtools/client/themes/performance.css
+++ b/devtools/client/themes/performance.css
@@ -264,16 +264,26 @@
 .call-tree-cell:not(:last-child) {
   text-align: end;
 }
 
 .call-tree-header {
   background-color: var(--theme-tab-toolbar-background);
 }
 
+.call-tree-item .call-tree-cell,
+.call-tree-item .call-tree-cell[type=function] description {
+  -moz-user-select: text;
+}
+
+.call-tree-item .call-tree-cell::-moz-selection,
+.call-tree-item .call-tree-cell[type=function] description::-moz-selection {
+  background-color: var(--theme-highlight-orange);
+}
+
 .call-tree-item:last-child {
   border-bottom: 1px solid var(--cell-border-color);
 }
 
 .call-tree-item:nth-child(2n) {
   background-color: var(--row-alt-background-color);
 }
 
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -23,16 +23,17 @@
 #include "mozilla/dom/workers/ServiceWorkerManager.h"
 #include "mozilla/EventStateManager.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/StartupTimeline.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/unused.h"
+#include "Navigator.h"
 #include "URIUtils.h"
 
 #include "nsIContent.h"
 #include "nsIContentInlines.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMElement.h"
 
@@ -3123,16 +3124,48 @@ NS_IMETHODIMP
 nsDocShell::NameEquals(const char16_t* aName, bool* aResult)
 {
   NS_ENSURE_ARG_POINTER(aName);
   NS_ENSURE_ARG_POINTER(aResult);
   *aResult = mName.Equals(aName);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDocShell::GetCustomUserAgent(nsAString& aCustomUserAgent)
+{
+  aCustomUserAgent = mCustomUserAgent;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShell::SetCustomUserAgent(const nsAString& aCustomUserAgent)
+{
+  mCustomUserAgent = aCustomUserAgent;
+  RefPtr<nsGlobalWindow> win = mScriptGlobal ?
+    mScriptGlobal->GetCurrentInnerWindowInternal() : nullptr;
+  if (win) {
+    ErrorResult ignored;
+    Navigator* navigator = win->GetNavigator(ignored);
+    ignored.SuppressException();
+    if (navigator) {
+      navigator->ClearUserAgentCache();
+    }
+  }
+
+  uint32_t childCount = mChildList.Length();
+  for (uint32_t i = 0; i < childCount; ++i) {
+    nsCOMPtr<nsIDocShell> childShell = do_QueryInterface(ChildAt(i));
+    if (childShell) {
+      childShell->SetCustomUserAgent(aCustomUserAgent);
+    }
+  }
+  return NS_OK;
+}
+
 /* virtual */ int32_t
 nsDocShell::ItemType()
 {
   return mItemType;
 }
 
 NS_IMETHODIMP
 nsDocShell::GetItemType(int32_t* aItemType)
@@ -3250,16 +3283,17 @@ nsDocShell::SetDocLoaderParent(nsDocLoad
   }
 
   // Curse ambiguous nsISupports inheritance!
   nsISupports* parent = GetAsSupports(aParent);
 
   // If parent is another docshell, we inherit all their flags for
   // allowing plugins, scripting etc.
   bool value;
+  nsString customUserAgent;
   nsCOMPtr<nsIDocShell> parentAsDocShell(do_QueryInterface(parent));
   if (parentAsDocShell) {
     if (mAllowPlugins && NS_SUCCEEDED(parentAsDocShell->GetAllowPlugins(&value))) {
       SetAllowPlugins(value);
     }
     if (mAllowJavascript && NS_SUCCEEDED(parentAsDocShell->GetAllowJavascript(&value))) {
       SetAllowJavascript(value);
     }
@@ -3279,16 +3313,20 @@ nsDocShell::SetDocLoaderParent(nsDocLoad
     SetAllowContentRetargeting(mAllowContentRetargeting &&
       parentAsDocShell->GetAllowContentRetargetingOnChildren());
     if (NS_SUCCEEDED(parentAsDocShell->GetIsActive(&value))) {
       SetIsActive(value);
     }
     if (parentAsDocShell->GetIsPrerendered()) {
       SetIsPrerendered(true);
     }
+    if (NS_SUCCEEDED(parentAsDocShell->GetCustomUserAgent(customUserAgent)) &&
+        !customUserAgent.IsEmpty()) {
+      SetCustomUserAgent(customUserAgent);
+    }
     if (NS_FAILED(parentAsDocShell->GetAllowDNSPrefetch(&value))) {
       value = false;
     }
     SetAllowDNSPrefetch(mAllowDNSPrefetch && value);
     value = parentAsDocShell->GetAffectPrivateSessionLifetime();
     SetAffectPrivateSessionLifetime(value);
     uint32_t flags;
     if (NS_SUCCEEDED(parentAsDocShell->GetDefaultLoadFlags(&flags))) {
--- a/docshell/base/nsDocShell.h
+++ b/docshell/base/nsDocShell.h
@@ -788,16 +788,17 @@ protected:
   FrameType GetInheritedFrameType();
 
   bool HasUnloadedParent();
 
   // Dimensions of the docshell
   nsIntRect mBounds;
   nsString mName;
   nsString mTitle;
+  nsString mCustomUserAgent;
 
   /**
    * Content-Type Hint of the most-recently initiated load. Used for
    * session history entries.
    */
   nsCString mContentTypeHint;
   nsIntPoint mDefaultScrollbarPref; // persistent across doc loads
 
--- a/docshell/base/nsIDocShell.idl
+++ b/docshell/base/nsIDocShell.idl
@@ -38,17 +38,17 @@ interface nsIPrincipal;
 interface nsIWebBrowserPrint;
 interface nsIPrivacyTransitionObserver;
 interface nsIReflowObserver;
 interface nsIScrollObserver;
 interface nsITabParent;
 
 typedef unsigned long nsLoadFlags;
 
-[scriptable, builtinclass, uuid(63adb599-6dc9-4746-972e-c22e9018020b)]
+[scriptable, builtinclass, uuid(bc3524bd-023c-4fc8-ace1-472bc999fb12)]
 interface nsIDocShell : nsIDocShellTreeItem
 {
   /**
    * Loads a given URI.  This will give priority to loading the requested URI
    * in the object implementing	this interface.  If it can't be loaded here
    * however, the URL dispatcher will go through its normal process of content
    * loading.
    *
@@ -233,29 +233,34 @@ interface nsIDocShell : nsIDocShellTreeI
 
   /**
    * This attribute allows chrome to tie in to handle DOM events that may
    * be of interest to chrome.
    */
   attribute nsIDOMEventTarget chromeEventHandler;
 
   /**
+    * This allows chrome to set a custom User agent on a specific docshell
+    */
+  attribute DOMString customUserAgent;
+
+  /**
    * Whether to allow plugin execution
    */
   attribute boolean allowPlugins;
 
   /**
    * Whether to allow Javascript execution
    */
   attribute boolean allowJavascript;
 
   /**
    * Attribute stating if refresh based redirects can be allowed
    */
-  attribute  boolean allowMetaRedirects;
+  attribute boolean allowMetaRedirects;
 
   /**
    * Attribute stating if it should allow subframes (framesets/iframes) or not
    */
   attribute boolean allowSubframes;
 
   /**
    * Attribute stating whether or not images should be loaded.
--- a/docshell/test/browser/browser.ini
+++ b/docshell/test/browser/browser.ini
@@ -79,13 +79,14 @@ skip-if = e10s # Bug 1220927 - Test trie
 [browser_bug852909.js]
 [browser_bug92473.js]
 [browser_uriFixupIntegration.js]
 [browser_loadDisallowInherit.js]
 [browser_loadURI.js]
 [browser_multiple_pushState.js]
 [browser_onbeforeunload_navigation.js]
 [browser_search_notification.js]
+[browser_ua_emulation.js]
 [browser_timelineMarkers-01.js]
 [browser_timelineMarkers-02.js]
 [browser_timelineMarkers-03.js]
 [browser_timelineMarkers-04.js]
 [browser_timelineMarkers-05.js]
new file mode 100644
--- /dev/null
+++ b/docshell/test/browser/browser_ua_emulation.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// Test that the docShell UA emulation works
+add_task(function*() {
+  yield openUrl("data:text/html;charset=utf-8,<iframe id='test-iframe'></iframe>");
+
+  let docshell = content.QueryInterface(Ci.nsIInterfaceRequestor)
+                        .getInterface(Ci.nsIWebNavigation)
+                        .QueryInterface(Ci.nsIDocShell);
+  is(docshell.customUserAgent, "", "There should initially be no customUserAgent");
+
+  docshell.customUserAgent = "foo";
+  is(content.navigator.userAgent, "foo", "The user agent should be changed to foo");
+
+  let frameWin = content.document.querySelector("#test-iframe").contentWindow;
+  is(frameWin.navigator.userAgent, "foo", "The UA should be passed on to frames.");
+
+  let newFrame = content.document.createElement("iframe");
+  content.document.body.appendChild(newFrame);
+
+  let newFrameWin = newFrame.contentWindow;
+  is(newFrameWin.navigator.userAgent, "foo", "Newly created frames should use the new UA");
+
+  newFrameWin.location.reload();
+  yield waitForEvent(newFrameWin, "load");
+
+  is(newFrameWin.navigator.userAgent, "foo", "New UA should persist across reloads");
+  gBrowser.removeCurrentTab();
+});
+
+function waitForEvent(target, event) {
+  return new Promise(function(resolve) {
+    target.addEventListener(event, resolve);
+  });
+}
+
+function openUrl(url) {
+  return new Promise(function(resolve, reject) {
+    window.focus();
+
+    let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
+    let linkedBrowser = tab.linkedBrowser;
+
+    linkedBrowser.addEventListener("load", function onload() {
+      linkedBrowser.removeEventListener("load", onload, true);
+      resolve(tab);
+    }, true);
+  });
+}
--- a/dom/audiochannel/AudioChannelAgent.cpp
+++ b/dom/audiochannel/AudioChannelAgent.cpp
@@ -1,21 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "AudioChannelAgent.h"
 #include "AudioChannelService.h"
-#include "mozilla/Preferences.h"
-#include "nsIAppsService.h"
-#include "nsIDocument.h"
 #include "nsIDOMWindow.h"
-#include "nsIPrincipal.h"
 #include "nsPIDOMWindow.h"
 #include "nsXULAppAPI.h"
 
 using namespace mozilla::dom;
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioChannelAgent)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioChannelAgent)
@@ -77,91 +73,16 @@ AudioChannelAgent::InitWithWeakCallback(
                                         int32_t aChannelType,
                                         nsIAudioChannelAgentCallback *aCallback)
 {
   return InitInternal(aWindow, aChannelType, aCallback,
                       /* useWeakRef = */ true);
 }
 
 nsresult
-AudioChannelAgent::FindCorrectWindow(nsIDOMWindow* aWindow)
-{
-  nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
-  MOZ_ASSERT(window->IsInnerWindow());
-
-  mWindow = window->GetScriptableTop();
-  if (NS_WARN_IF(!mWindow)) {
-    return NS_OK;
-  }
-
-  mWindow = mWindow->GetOuterWindow();
-  if (NS_WARN_IF(!mWindow)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  // From here we do an hack for nested iframes.
-  // The system app doesn't have access to the nested iframe objects so it
-  // cannot control the volume of the agents running in nested apps. What we do
-  // here is to assign those Agents to the top scriptable window of the parent
-  // iframe (what is controlled by the system app).
-  // For doing this we go recursively back into the chain of windows until we
-  // find apps that are not the system one.
-  window = mWindow->GetParent();
-  if (!window || window == mWindow) {
-    return NS_OK;
-  }
-
-  window = window->GetCurrentInnerWindow();
-  if (!window) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
-  if (!doc) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal();
-
-  uint32_t appId;
-  nsresult rv = principal->GetAppId(&appId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (appId == nsIScriptSecurityManager::NO_APP_ID ||
-      appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) {
-    return NS_OK;
-  }
-
-  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
-  if (NS_WARN_IF(!appsService)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsAdoptingString systemAppManifest =
-    mozilla::Preferences::GetString("b2g.system_manifest_url");
-  if (!systemAppManifest) {
-    return NS_OK;
-  }
-
-  uint32_t systemAppId;
-  rv = appsService->GetAppLocalIdByManifestURL(systemAppManifest, &systemAppId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  if (systemAppId == appId) {
-    return NS_OK;
-  }
-
-  return FindCorrectWindow(window);
-}
-
-nsresult
 AudioChannelAgent::InitInternal(nsIDOMWindow* aWindow, int32_t aChannelType,
                                 nsIAudioChannelAgentCallback *aCallback,
                                 bool aUseWeakRef)
 {
   // We syncd the enum of channel type between nsIAudioChannelAgent.idl and
   // AudioChannelBinding.h the same.
   MOZ_ASSERT(int(AUDIO_AGENT_CHANNEL_NORMAL) == int(AudioChannel::Normal) &&
              int(AUDIO_AGENT_CHANNEL_CONTENT) == int(AudioChannel::Content) &&
@@ -182,19 +103,28 @@ AudioChannelAgent::InitInternal(nsIDOMWi
   if (NS_WARN_IF(!aWindow)) {
     return NS_OK;
   }
 
   nsCOMPtr<nsPIDOMWindow> pInnerWindow = do_QueryInterface(aWindow);
   MOZ_ASSERT(pInnerWindow->IsInnerWindow());
   mInnerWindowID = pInnerWindow->WindowID();
 
-  nsresult rv = FindCorrectWindow(aWindow);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
+  nsCOMPtr<nsPIDOMWindow> topWindow = pInnerWindow->GetScriptableTop();
+  if (NS_WARN_IF(!topWindow)) {
+    return NS_OK;
+  }
+
+  mWindow = do_QueryInterface(topWindow);
+  if (mWindow) {
+    mWindow = mWindow->GetOuterWindow();
+  }
+
+  if (NS_WARN_IF(!mWindow)) {
+    return NS_ERROR_FAILURE;
   }
 
   mAudioChannelType = aChannelType;
 
   if (aUseWeakRef) {
     mWeakCallback = do_GetWeakReference(aCallback);
   } else {
     mCallback = aCallback;
--- a/dom/audiochannel/AudioChannelAgent.h
+++ b/dom/audiochannel/AudioChannelAgent.h
@@ -51,18 +51,16 @@ private:
   already_AddRefed<nsIAudioChannelAgentCallback> GetCallback();
 
   nsresult InitInternal(nsIDOMWindow* aWindow, int32_t aAudioAgentType,
                         nsIAudioChannelAgentCallback* aCallback,
                         bool aUseWeakRef);
 
   void Shutdown();
 
-  nsresult FindCorrectWindow(nsIDOMWindow* aWindow);
-
   nsCOMPtr<nsPIDOMWindow> mWindow;
   nsCOMPtr<nsIAudioChannelAgentCallback> mCallback;
 
   nsWeakPtr mWeakCallback;
 
   int32_t mAudioChannelType;
   uint64_t mInnerWindowID;
   bool mIsRegToService;
--- a/dom/audiochannel/AudioChannelService.cpp
+++ b/dom/audiochannel/AudioChannelService.cpp
@@ -9,17 +9,16 @@
 #include "base/basictypes.h"
 
 #include "mozilla/Services.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/unused.h"
 
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/ContentParent.h"
-#include "mozilla/dom/TabParent.h"
 
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsISupportsPrimitives.h"
 #include "nsThreadUtils.h"
 #include "nsHashPropertyBag.h"
 #include "nsComponentManagerUtils.h"
 #include "nsPIDOMWindow.h"
@@ -215,17 +214,16 @@ AudioChannelService::Shutdown()
         // To monitor the volume settings based on audio channel.
         obs->RemoveObserver(gAudioChannelService, "mozsettings-changed");
 #endif
       }
     }
 
     gAudioChannelService->mWindows.Clear();
     gAudioChannelService->mPlayingChildren.Clear();
-    gAudioChannelService->mTabParents.Clear();
 #ifdef MOZ_WIDGET_GONK
     gAudioChannelService->mSpeakerManager.Clear();
 #endif
 
     gAudioChannelService = nullptr;
   }
 }
 
@@ -339,31 +337,16 @@ AudioChannelService::UnregisterAudioChan
       new MediaPlaybackRunnable(aAgent->Window(), false /* active */);
     NS_DispatchToCurrentThread(runnable);
   }
 
   MaybeSendStatusUpdate();
 }
 
 void
-AudioChannelService::RegisterTabParent(TabParent* aTabParent)
-{
-  MOZ_ASSERT(aTabParent);
-  MOZ_ASSERT(!mTabParents.Contains(aTabParent));
-  mTabParents.AppendElement(aTabParent);
-}
-
-void
-AudioChannelService::UnregisterTabParent(TabParent* aTabParent)
-{
-  MOZ_ASSERT(aTabParent);
-  mTabParents.RemoveElement(aTabParent);
-}
-
-void
 AudioChannelService::GetState(nsPIDOMWindow* aWindow, uint32_t aAudioChannel,
                               float* aVolume, bool* aMuted)
 {
   MOZ_ASSERT(!aWindow || aWindow->IsOuterWindow());
   MOZ_ASSERT(aVolume && aMuted);
   MOZ_ASSERT(aAudioChannel < NUMBER_OF_AUDIO_CHANNELS);
 
 
@@ -573,42 +556,16 @@ AudioChannelService::Observe(nsISupports
 
     RemoveChildStatus(childID);
   }
 
   return NS_OK;
 }
 
 void
-AudioChannelService::RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel,
-                                                     nsPIDOMWindow* aWindow)
-{
-  MOZ_ASSERT(aWindow);
-  MOZ_ASSERT(aWindow->IsOuterWindow());
-
-  nsCOMPtr<nsPIDOMWindow> topWindow = aWindow->GetScriptableTop();
-  if (!topWindow) {
-    return;
-  }
-
-  AudioChannelWindow* winData = GetWindowData(topWindow->WindowID());
-  if (!winData) {
-    return;
-  }
-
-  for (uint32_t i = 0; i < mTabParents.Length(); ++i) {
-    mTabParents[i]->AudioChannelChangeNotification(aWindow, aAudioChannel,
-                                                   winData->mChannels[(uint32_t)aAudioChannel].mVolume,
-                                                   winData->mChannels[(uint32_t)aAudioChannel].mMuted);
-  }
-
-  RefreshAgentsVolume(aWindow);
-}
-
-void
 AudioChannelService::RefreshAgentsVolume(nsPIDOMWindow* aWindow)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
   nsCOMPtr<nsPIDOMWindow> topWindow = aWindow->GetScriptableTop();
   if (!topWindow) {
     return;
@@ -789,17 +746,17 @@ AudioChannelService::SetAudioChannelVolu
   MOZ_ASSERT(aWindow->IsOuterWindow());
 
   MOZ_LOG(GetAudioChannelLog(), LogLevel::Debug,
          ("AudioChannelService, SetAudioChannelVolume, window = %p, type = %d, "
           "volume = %d\n", aWindow, aAudioChannel, aVolume));
 
   AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
   winData->mChannels[(uint32_t)aAudioChannel].mVolume = aVolume;
-  RefreshAgentsVolumeAndPropagate(aAudioChannel, aWindow);
+  RefreshAgentsVolume(aWindow);
 }
 
 NS_IMETHODIMP
 AudioChannelService::SetAudioChannelVolume(nsIDOMWindow* aWindow,
                                            unsigned short aAudioChannel,
                                            float aVolume)
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -852,17 +809,17 @@ AudioChannelService::SetAudioChannelMute
 
   if (aAudioChannel == AudioChannel::System) {
     // Workaround for bug1183033, system channel type can always playback.
     return;
   }
 
   AudioChannelWindow* winData = GetOrCreateWindowData(aWindow);
   winData->mChannels[(uint32_t)aAudioChannel].mMuted = aMuted;
-  RefreshAgentsVolumeAndPropagate(aAudioChannel, aWindow);
+  RefreshAgentsVolume(aWindow);
 }
 
 NS_IMETHODIMP
 AudioChannelService::SetAudioChannelMuted(nsIDOMWindow* aWindow,
                                           unsigned short aAudioChannel,
                                           bool aMuted)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/audiochannel/AudioChannelService.h
+++ b/dom/audiochannel/AudioChannelService.h
@@ -18,23 +18,20 @@
 #include "mozilla/dom/AudioChannelBinding.h"
 
 class nsIRunnable;
 class nsPIDOMWindow;
 struct PRLogModuleInfo;
 
 namespace mozilla {
 namespace dom {
-
 #ifdef MOZ_WIDGET_GONK
 class SpeakerManagerService;
 #endif
 
-class TabParent;
-
 #define NUMBER_OF_AUDIO_CHANNELS (uint32_t)AudioChannel::EndGuard_
 
 class AudioChannelService final : public nsIAudioChannelService
                                 , public nsIObserver
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIOBSERVER
@@ -62,22 +59,16 @@ public:
   /**
    * Any audio channel agent that stops playing should unregister itself to
    * this service.
    */
   void UnregisterAudioChannelAgent(AudioChannelAgent* aAgent,
                                    uint32_t aNotifyPlayback);
 
   /**
-   * For nested iframes.
-   */
-  void RegisterTabParent(TabParent* aTabParent);
-  void UnregisterTabParent(TabParent* aTabParent);
-
-  /**
    * Return the state to indicate this audioChannel for his window should keep
    * playing/muted.
    */
   void GetState(nsPIDOMWindow* aWindow, uint32_t aChannel,
                 float* aVolume, bool* aMuted);
 
   /* Methods for the BrowserElementAudioChannel */
   float GetAudioChannelVolume(nsPIDOMWindow* aWindow, AudioChannel aChannel);
@@ -112,19 +103,16 @@ public:
    */
   virtual void SetDefaultVolumeControlChannel(int32_t aChannel,
                                               bool aVisible);
 
   bool AnyAudioChannelIsActive();
 
   void RefreshAgentsVolume(nsPIDOMWindow* aWindow);
 
-  void RefreshAgentsVolumeAndPropagate(AudioChannel aAudioChannel,
-                                       nsPIDOMWindow* aWindow);
-
   // This method needs to know the inner window that wants to capture audio. We
   // group agents per top outer window, but we can have multiple innerWindow per
   // top outerWindow (subiframes, etc.) and we have to identify all the agents
   // just for a particular innerWindow.
   void RefreshAgentsCapture(nsPIDOMWindow* aWindow,
                             uint64_t aInnerWindowID);
 
 
@@ -230,19 +218,16 @@ private:
   nsTObserverArray<nsAutoPtr<AudioChannelWindow>> mWindows;
 
   nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>> mPlayingChildren;
 
 #ifdef MOZ_WIDGET_GONK
   nsTArray<SpeakerManagerService*>  mSpeakerManager;
 #endif
 
-  // Raw pointers because TabParents must unregister themselves.
-  nsTArray<TabParent*> mTabParents;
-
   nsCOMPtr<nsIRunnable> mRunnable;
 
   uint64_t mDefChannelChildID;
 
   // These boolean are used to know if we have to send an status update to the
   // service running in the main process.
   bool mTelephonyChannel;
   bool mContentOrNormalChannel;
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -367,21 +367,32 @@ Navigator::Invalidate()
 //*****************************************************************************
 
 NS_IMETHODIMP
 Navigator::GetUserAgent(nsAString& aUserAgent)
 {
   nsCOMPtr<nsIURI> codebaseURI;
   nsCOMPtr<nsPIDOMWindow> window;
 
-  if (mWindow && mWindow->GetDocShell()) {
+  if (mWindow) {
     window = mWindow;
-    nsIDocument* doc = mWindow->GetExtantDoc();
-    if (doc) {
-      doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
+    nsIDocShell* docshell = window->GetDocShell();
+    nsString customUserAgent;
+    if (docshell) {
+      docshell->GetCustomUserAgent(customUserAgent);
+
+      if (!customUserAgent.IsEmpty()) {
+        aUserAgent = customUserAgent;
+        return NS_OK;
+      }
+
+      nsIDocument* doc = mWindow->GetExtantDoc();
+      if (doc) {
+        doc->NodePrincipal()->GetURI(getter_AddRefs(codebaseURI));
+      }
     }
   }
 
   return GetUserAgent(window, codebaseURI, nsContentUtils::IsCallerChrome(),
                       aUserAgent);
 }
 
 NS_IMETHODIMP
@@ -2736,16 +2747,22 @@ Navigator::AppName(nsAString& aAppName, 
       aAppName = override;
       return;
     }
   }
 
   aAppName.AssignLiteral("Netscape");
 }
 
+void
+Navigator::ClearUserAgentCache()
+{
+  NavigatorBinding::ClearCachedUserAgentValue(this);
+}
+
 nsresult
 Navigator::GetUserAgent(nsPIDOMWindow* aWindow, nsIURI* aURI,
                         bool aIsCallerChrome,
                         nsAString& aUserAgent)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!aIsCallerChrome) {
--- a/dom/base/Navigator.h
+++ b/dom/base/Navigator.h
@@ -177,16 +177,20 @@ public:
   static nsresult GetAppVersion(nsAString& aAppVersion,
                                 bool aUsePrefOverriddenValue);
 
   static nsresult GetUserAgent(nsPIDOMWindow* aWindow,
                                nsIURI* aURI,
                                bool aIsCallerChrome,
                                nsAString& aUserAgent);
 
+  // Clears the user agent cache by calling:
+  // NavigatorBinding::ClearCachedUserAgentValue(this);
+  void ClearUserAgentCache();
+
   already_AddRefed<Promise> GetDataStores(const nsAString& aName,
                                           const nsAString& aOwner,
                                           ErrorResult& aRv);
 
   // Feature Detection API
   already_AddRefed<Promise> GetFeature(const nsAString& aName,
                                        ErrorResult& aRv);
 
--- a/dom/base/nsIFrameLoader.idl
+++ b/dom/base/nsIFrameLoader.idl
@@ -1,16 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
-interface mozIApplication;
 interface nsFrameLoader;
 interface nsIDocShell;
 interface nsIURI;
 interface nsIFrame;
 interface nsSubDocumentFrame;
 interface nsIMessageSender;
 interface nsIVariant;
 interface nsIDOMElement;
@@ -210,32 +209,26 @@ interface nsIFrameLoader : nsISupports
 };
 
 %{C++
 class nsFrameLoader;
 %}
 
 native alreadyAddRefed_nsFrameLoader(already_AddRefed<nsFrameLoader>);
 
-[scriptable, uuid(adc1b3ba-8deb-4943-8045-e6de0044f2ce)]
+[scriptable, uuid(c4abebcf-55f3-47d4-af15-151311971255)]
 interface nsIFrameLoaderOwner : nsISupports
 {
   /**
    * The frame loader owned by this nsIFrameLoaderOwner
    */
   readonly attribute nsIFrameLoader frameLoader;
   [noscript, notxpcom] alreadyAddRefed_nsFrameLoader GetFrameLoader();
 
   /**
-   * The principal of parent mozIApplication in case of nested mozbrowser
-   * iframes.
-   */
-  readonly attribute mozIApplication parentApplication;
-
-  /**
    * Puts the FrameLoaderOwner in prerendering mode.
    */
   void setIsPrerendered();
 
   /**
    * Swap frame loaders with the given nsIFrameLoaderOwner.  This may
    * only be posible in a very limited range of circumstances, or
    * never, depending on the object implementing this interface.
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1211,27 +1211,16 @@ nsObjectLoadingContent::GetFrameLoader(n
 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
 nsObjectLoadingContent::GetFrameLoader()
 {
   RefPtr<nsFrameLoader> loader = mFrameLoader;
   return loader.forget();
 }
 
 NS_IMETHODIMP
-nsObjectLoadingContent::GetParentApplication(mozIApplication** aApplication)
-{
-  if (!aApplication) {
-    return NS_ERROR_FAILURE;
-  }
-
-  *aApplication = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsObjectLoadingContent::SetIsPrerendered()
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsObjectLoadingContent::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherLoader)
 {
--- a/dom/browser-element/BrowserElementAudioChannel.cpp
+++ b/dom/browser-element/BrowserElementAudioChannel.cpp
@@ -2,34 +2,42 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "BrowserElementAudioChannel.h"
 
 #include "mozilla/Services.h"
 #include "mozilla/dom/BrowserElementAudioChannelBinding.h"
 #include "mozilla/dom/DOMRequest.h"
-#include "mozilla/dom/Element.h"
-#include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/Promise.h"
 #include "mozilla/dom/PromiseNativeHandler.h"
 #include "mozilla/dom/ToJSValue.h"
 #include "AudioChannelService.h"
 #include "nsIBrowserElementAPI.h"
 #include "nsIDocShell.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDOMRequest.h"
 #include "nsIObserverService.h"
 #include "nsISupportsPrimitives.h"
 #include "nsISystemMessagesInternal.h"
 #include "nsITabParent.h"
 #include "nsNetUtil.h"
 #include "nsPIDOMWindow.h"
 #include "nsServiceManagerUtils.h"
 
+namespace {
+
+void
+AssertIsInMainProcess()
+{
+  MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
+}
+
+} // anonymous namespace
+
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_ADDREF_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper)
 NS_IMPL_RELEASE_INHERITED(BrowserElementAudioChannel, DOMEventTargetHelper)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(BrowserElementAudioChannel)
   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
@@ -76,16 +84,17 @@ BrowserElementAudioChannel::BrowserEleme
   : DOMEventTargetHelper(aWindow)
   , mFrameLoader(aFrameLoader)
   , mBrowserElementAPI(aAPI)
   , mAudioChannel(aAudioChannel)
   , mManifestURL(aManifestURL)
   , mState(eStateUnknown)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     nsAutoString name;
     AudioChannelService::GetAudioChannelString(aAudioChannel, name);
 
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
@@ -93,16 +102,17 @@ BrowserElementAudioChannel::BrowserEleme
 
     obs->AddObserver(this, topic.get(), true);
   }
 }
 
 BrowserElementAudioChannel::~BrowserElementAudioChannel()
 {
   MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
 
   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
   if (obs) {
     nsAutoString name;
     AudioChannelService::GetAudioChannelString(mAudioChannel, name);
 
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
@@ -158,16 +168,18 @@ BrowserElementAudioChannel::WrapObject(J
 {
   return BrowserElementAudioChannelBinding::Wrap(aCx, this, aGivenProto);
 }
 
 AudioChannel
 BrowserElementAudioChannel::Name() const
 {
   MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
+
   return mAudioChannel;
 }
 
 namespace {
 
 class BaseRunnable : public nsRunnable
 {
 protected:
@@ -344,16 +356,17 @@ RespondSuccessHandler::RejectedCallback(
 }
 
 } // anonymous namespace
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::GetVolume(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->GetAudioChannelVolume((uint32_t)mAudioChannel,
                                                     getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
@@ -369,16 +382,17 @@ BrowserElementAudioChannel::GetVolume(Er
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::SetVolume(float aVolume, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->SetAudioChannelVolume((uint32_t)mAudioChannel,
                                                     aVolume,
                                                     getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
@@ -401,16 +415,17 @@ BrowserElementAudioChannel::SetVolume(fl
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::GetMuted(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->GetAudioChannelMuted((uint32_t)mAudioChannel,
                                                    getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
     }
@@ -426,16 +441,17 @@ BrowserElementAudioChannel::GetMuted(Err
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::SetMuted(bool aMuted, ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
 
   if (!mFrameWindow) {
     nsCOMPtr<nsIDOMDOMRequest> request;
     aRv = mBrowserElementAPI->SetAudioChannelMuted((uint32_t)mAudioChannel,
                                                    aMuted,
                                                    getter_AddRefs(request));
     if (NS_WARN_IF(aRv.Failed())) {
       return nullptr;
@@ -458,16 +474,17 @@ BrowserElementAudioChannel::SetMuted(boo
 
   return domRequest.forget();
 }
 
 already_AddRefed<dom::DOMRequest>
 BrowserElementAudioChannel::IsActive(ErrorResult& aRv)
 {
   MOZ_ASSERT(NS_IsMainThread());
+  AssertIsInMainProcess();
 
   if (mState != eStateUnknown) {
     RefPtr<DOMRequest> domRequest = new DOMRequest(GetOwner());
 
     nsCOMPtr<nsIRunnable> runnable =
       new IsActiveRunnable(GetOwner(), mFrameWindow, domRequest, mAudioChannel,
                            mState == eStateActive);
     NS_DispatchToMainThread(runnable);
@@ -571,39 +588,18 @@ BrowserElementAudioChannel::Observe(nsIS
     if (mTabParent == aSubject) {
       ProcessStateChanged(aData);
     }
 
     return NS_OK;
   }
 
   nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
-  // This can be a nested iframe.
-  if (!wrapper) {
-    nsCOMPtr<nsITabParent> iTabParent = do_QueryInterface(aSubject);
-    if (!iTabParent) {
-      return NS_ERROR_FAILURE;
-    }
-
-    RefPtr<TabParent> tabParent = TabParent::GetFrom(iTabParent);
-    if (!tabParent) {
-      return NS_ERROR_FAILURE;
-    }
-
-    Element* element = tabParent->GetOwnerElement();
-    if (!element) {
-      return NS_ERROR_FAILURE;
-    }
-
-    nsCOMPtr<nsPIDOMWindow> window = element->OwnerDoc()->GetWindow();
-    if (window == mFrameWindow) {
-      ProcessStateChanged(aData);
-    }
-
-    return NS_OK;
+  if (NS_WARN_IF(!wrapper)) {
+    return NS_ERROR_FAILURE;
   }
 
   uint64_t windowID;
   nsresult rv = wrapper->GetData(&windowID);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
--- a/dom/browser-element/mochitest/browserElement_AudioChannel.js
+++ b/dom/browser-element/mochitest/browserElement_AudioChannel.js
@@ -187,15 +187,12 @@ function runTests() {
     return;
   }
 
   var test = tests.shift();
   test();
 }
 
 
-addEventListener('testready', function() {
-  SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
-                            function() {
-    SimpleTest.executeSoon(runTests);
-  });
+addEventListener('load', function() {
+  SimpleTest.executeSoon(runTests);
 });
 
deleted file mode 100644
--- a/dom/browser-element/mochitest/browserElement_AudioChannel_nested.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/* Any copyright is dedicated to the public domain.
-   http://creativecommons.org/publicdomain/zero/1.0/ */
-
-// Bug 1113086 - tests for AudioChannel API into BrowserElement
-
-"use strict";
-
-SimpleTest.waitForExplicitFinish();
-browserElementTestHelpers.setEnabledPref(true);
-browserElementTestHelpers.addPermission();
-
-function runTests() {
-  var iframe = document.createElement('iframe');
-  iframe.setAttribute('mozbrowser', 'true');
-  iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
-
-  var listener = function(e) {
-    var message = e.detail.message;
-    if (/^OK/.exec(message)) {
-      ok(true, "Message from app: " + message);
-    } else if (/^KO/.exec(message)) {
-      ok(false, "Message from app: " + message);
-    } else if (/DONE/.exec(message)) {
-      ok(true, "Messaging from app complete");
-      iframe.removeEventListener('mozbrowsershowmodalprompt', listener);
-    }
-  }
-
-  function audio_loadend() {
-    ok("mute" in iframe, "iframe.mute exists");
-    ok("unmute" in iframe, "iframe.unmute exists");
-    ok("getMuted" in iframe, "iframe.getMuted exists");
-    ok("getVolume" in iframe, "iframe.getVolume exists");
-    ok("setVolume" in iframe, "iframe.setVolume exists");
-
-    ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
-    var channels = iframe.allowedAudioChannels;
-    is(channels.length, 1, "1 audio channel by default");
-
-    var ac = channels[0];
-
-    ok(ac instanceof BrowserElementAudioChannel, "Correct class");
-    ok("getVolume" in ac, "ac.getVolume exists");
-    ok("setVolume" in ac, "ac.setVolume exists");
-    ok("getMuted" in ac, "ac.getMuted exists");
-    ok("setMuted" in ac, "ac.setMuted exists");
-    ok("isActive" in ac, "ac.isActive exists");
-
-    info("Setting the volume...");
-    ac.setVolume(0.5);
-
-    ac.onactivestatechanged = function() {
-      ok(true, "activestatechanged event received.");
-      ac.onactivestatechanged = null;
-      SimpleTest.finish();
-    }
-  }
-
-  iframe.addEventListener('mozbrowserloadend', audio_loadend);
-  iframe.addEventListener('mozbrowsershowmodalprompt', listener, false);
-  document.body.appendChild(iframe);
-
-  var context = { 'url': 'http://example.org',
-                  'appId': SpecialPowers.Ci.nsIScriptSecurityManager.NO_APP_ID,
-                  'isInBrowserElement': true };
-  SpecialPowers.pushPermissions([
-    {'type': 'browser', 'allow': 1, 'context': context},
-    {'type': 'embed-apps', 'allow': 1, 'context': context}
-  ], function() {
-    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_browserElement_AudioChannel_nested.html';
-  });
-}
-
-addEventListener('testready', function() {
-  SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
-                            function() {
-    SimpleTest.executeSoon(runTests);
-  });
-});
--- a/dom/browser-element/mochitest/browserElement_AudioPlayback.js
+++ b/dom/browser-element/mochitest/browserElement_AudioPlayback.js
@@ -63,14 +63,9 @@ function runTest() {
     ok(false,
        'mozbrowseraudioplaybackchange should dispatch to the correct browser');
   });
 
   // Load a simple page to get the process started.
   iframe.src = browserElementTestHelpers.emptyPage1;
 }
 
-addEventListener('testready', function() {
-  SpecialPowers.pushPrefEnv({'set': [["b2g.system_manifest_url", "http://mochi.test:8888/manifest.webapp"]]},
-                            function() {
-    SimpleTest.executeSoon(runTest);
-  });
-});
+addEventListener('testready', runTest);
deleted file mode 100644
--- a/dom/browser-element/mochitest/file_browserElement_AudioChannel_nested.html
+++ /dev/null
@@ -1,63 +0,0 @@
-<html>
-<head>
-<script type="text/javascript">
-
-  function ok(a, msg) {
-    alert((!!a ? "OK" : "KO") + " " + msg);
-  }
-
-  function is(a, b, msg) {
-    ok(a === b, msg);
-  }
-
-  function finish(a, b, msg) {
-    alert("DONE");
-  }
-
-  addEventListener('load', function(e) {
-    var iframe = document.createElement('iframe');
-    iframe.setAttribute('mozbrowser', 'true');
-    // set 'remote' to true here will make the the iframe remote in _inproc_
-    // test and in-process in _oop_  test.
-    iframe.setAttribute('remote', 'true');
-    iframe.setAttribute('mozapp', 'http://example.org/manifest.webapp');
-
-    iframe.addEventListener('mozbrowserloadend', function(e) {
-      ok("mute" in iframe, "iframe.mute exists");
-      ok("unmute" in iframe, "iframe.unmute exists");
-      ok("getMuted" in iframe, "iframe.getMuted exists");
-      ok("getVolume" in iframe, "iframe.getVolume exists");
-      ok("setVolume" in iframe, "iframe.setVolume exists");
-
-      ok("allowedAudioChannels" in iframe, "allowedAudioChannels exist");
-      var channels = iframe.allowedAudioChannels;
-      is(channels.length, 1, "1 audio channel by default");
-
-      var ac = channels[0];
-
-      ok(ac instanceof BrowserElementAudioChannel, "Correct class");
-      ok("getVolume" in ac, "ac.getVolume exists");
-      ok("setVolume" in ac, "ac.setVolume exists");
-      ok("getMuted" in ac, "ac.getMuted exists");
-      ok("setMuted" in ac, "ac.setMuted exists");
-      ok("isActive" in ac, "ac.isActive exists");
-
-      ac.onactivestatechanged = function() {
-        ok("activestatechanged event received.");
-
-        ac.getVolume().onsuccess = function(e) {
-          ok(e.target.result, 1, "Default volume is 1");
-        };
-
-        finish();
-      }
-    });
-
-    document.body.appendChild(iframe);
-    iframe.src = 'http://example.org/tests/dom/browser-element/mochitest/file_audio.html';
-  });
-</script>
-</head>
-<body>
-</body>
-</html>
--- a/dom/browser-element/mochitest/mochitest-oop.ini
+++ b/dom/browser-element/mochitest/mochitest-oop.ini
@@ -116,13 +116,12 @@ disabled = bug 930449
 [test_browserElement_oop_CloseFromOpener.html]
 disabled = bug 924771
 [test_browserElement_oop_CloseApp.html]
 disabled = bug 924771
 [test_browserElement_oop_ExposableURI.html]
 disabled = bug 924771
 [test_browserElement_oop_GetContentDimensions.html]
 [test_browserElement_oop_AudioChannel.html]
-[test_browserElement_oop_AudioChannel_nested.html]
 [test_browserElement_oop_SetNFCFocus.html]
 [test_browserElement_oop_getWebManifest.html]
 [test_browserElement_oop_OpenWindowEmpty.html]
 skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
--- a/dom/browser-element/mochitest/mochitest.ini
+++ b/dom/browser-element/mochitest/mochitest.ini
@@ -78,23 +78,21 @@ support-files =
   browserElement_VisibilityChange.js
   browserElement_XFrameOptions.js
   browserElement_XFrameOptionsAllowFrom.js
   browserElement_XFrameOptionsDeny.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_XFrameOptionsSameOrigin.js
   browserElement_GetContentDimensions.js
   browserElement_AudioChannel.js
-  browserElement_AudioChannel_nested.js
   file_browserElement_AlertInFrame.html
   file_browserElement_AlertInFrame_Inner.html
   file_browserElement_AllowEmbedAppsInNestedOOIframe.html
   file_browserElement_AppFramePermission.html
   file_browserElement_AppWindowNamespace.html
-  file_browserElement_AudioChannel_nested.html
   file_browserElement_Viewmode.html
   file_browserElement_ThemeColor.html
   file_browserElement_BrowserWindowNamespace.html
   file_browserElement_CloseApp.html
   file_browserElement_CloseFromOpener.html
   file_browserElement_CookiesNotThirdParty.html
   file_browserElement_DisallowEmbedAppsInOOP.html
   file_browserElement_ExecuteScript.html
@@ -247,13 +245,12 @@ skip-if = (toolkit == 'gonk' && !debug)
 [test_browserElement_inproc_XFrameOptionsSameOrigin.html]
 [test_browserElement_oop_NextPaint.html]
 skip-if = (toolkit == 'android' && processor == 'x86') #x86 only bug 936226
 # Disabled due to https://bugzilla.mozilla.org/show_bug.cgi?id=774100
 [test_browserElement_inproc_Reload.html]
 disabled = bug 774100
 [test_browserElement_inproc_GetContentDimensions.html]
 [test_browserElement_inproc_AudioChannel.html]
-[test_browserElement_inproc_AudioChannel_nested.html]
 [test_browserElement_inproc_SetNFCFocus.html]
 [test_browserElement_inproc_getStructuredData.html]
 [test_browserElement_inproc_OpenWindowEmpty.html]
 skip-if = (toolkit == 'gonk') # Test doesn't work on B2G emulator
deleted file mode 100644
--- a/dom/browser-element/mochitest/test_browserElement_inproc_AudioChannel_nested.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test of browser element audioChannel in nested mozbrowser iframes.</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<script type="application/javascript;version=1.7" src="browserElement_AudioChannel_nested.js">
-</script>
-</body>
-</html>
deleted file mode 100644
--- a/dom/browser-element/mochitest/test_browserElement_oop_AudioChannel_nested.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test of browser element audioChannel.</title>
-  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
-  <script type="application/javascript" src="browserElementTestHelpers.js"></script>
-  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
-</head>
-<body>
-<script type="application/javascript;version=1.7" src="browserElement_AudioChannel_nested.js">
-</script>
-</body>
-</html>
--- a/dom/html/nsBrowserElement.cpp
+++ b/dom/html/nsBrowserElement.cpp
@@ -558,43 +558,36 @@ nsBrowserElement::GetAllowedAudioChannel
     }
 
     nsAutoString manifestURL;
     aRv = mozBrowserFrame->GetAppManifestURL(manifestURL);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
 
-    nsCOMPtr<mozIApplication> parentApp;
-    aRv = GetParentApplication(getter_AddRefs(parentApp));
-    if (NS_WARN_IF(aRv.Failed())) {
-      return;
-    }
-
     MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
             ("nsBrowserElement, GetAllowedAudioChannels, this = %p\n", this));
 
     GenerateAllowedAudioChannels(window, frameLoader, mBrowserElementAPI,
-                                 manifestURL, parentApp,
-                                 mBrowserElementAudioChannels, aRv);
+                                 manifestURL, mBrowserElementAudioChannels,
+                                 aRv);
     if (NS_WARN_IF(aRv.Failed())) {
       return;
     }
   }
 
   aAudioChannels.AppendElements(mBrowserElementAudioChannels);
 }
 
 /* static */ void
 nsBrowserElement::GenerateAllowedAudioChannels(
                  nsPIDOMWindow* aWindow,
                  nsIFrameLoader* aFrameLoader,
                  nsIBrowserElementAPI* aAPI,
                  const nsAString& aManifestURL,
-                 mozIApplication* aParentApp,
                  nsTArray<RefPtr<BrowserElementAudioChannel>>& aAudioChannels,
                  ErrorResult& aRv)
 {
   MOZ_ASSERT(aAudioChannels.IsEmpty());
 
   nsCOMPtr<nsIAppsService> appsService =
     do_GetService("@mozilla.org/AppsService;1");
   if (NS_WARN_IF(!appsService)) {
@@ -627,29 +620,16 @@ nsBrowserElement::GenerateAllowedAudioCh
 
     bool allowed;
     nsAutoCString permissionName;
 
     for (uint32_t i = 0; audioChannelTable && audioChannelTable[i].tag; ++i) {
       permissionName.AssignASCII("audio-channel-");
       permissionName.AppendASCII(audioChannelTable[i].tag);
 
-      // In case of nested iframes we want to check if the parent has the
-      // permission to use this AudioChannel.
-      if (aParentApp) {
-        aRv = aParentApp->HasPermission(permissionName.get(), &allowed);
-        if (NS_WARN_IF(aRv.Failed())) {
-          return;
-        }
-
-        if (!allowed) {
-          continue;
-        }
-      }
-
       aRv = app->HasPermission(permissionName.get(), &allowed);
       if (NS_WARN_IF(aRv.Failed())) {
         return;
       }
 
       if (allowed) {
         RefPtr<BrowserElementAudioChannel> ac =
           BrowserElementAudioChannel::Create(aWindow, aFrameLoader, aAPI,
--- a/dom/html/nsBrowserElement.h
+++ b/dom/html/nsBrowserElement.h
@@ -120,24 +120,21 @@ public:
                    ErrorResult& aRv);
 
   // Helper
   static void GenerateAllowedAudioChannels(
                  nsPIDOMWindow* aWindow,
                  nsIFrameLoader* aFrameLoader,
                  nsIBrowserElementAPI* aAPI,
                  const nsAString& aManifestURL,
-                 mozIApplication* aParentApp,
                  nsTArray<RefPtr<dom::BrowserElementAudioChannel>>& aAudioChannels,
                  ErrorResult& aRv);
 
 protected:
   NS_IMETHOD_(already_AddRefed<nsFrameLoader>) GetFrameLoader() = 0;
-  NS_IMETHOD GetParentApplication(mozIApplication** aApplication) = 0;
-
   void InitBrowserElementAPI();
   nsCOMPtr<nsIBrowserElementAPI> mBrowserElementAPI;
   nsTArray<RefPtr<dom::BrowserElementAudioChannel>> mBrowserElementAudioChannels;
 
 private:
   bool IsBrowserElementOrThrow(ErrorResult& aRv);
   bool IsNotWidgetOrThrow(ErrorResult& aRv);
   bool mOwnerIsWidget;
--- a/dom/html/nsGenericHTMLFrameElement.cpp
+++ b/dom/html/nsGenericHTMLFrameElement.cpp
@@ -187,45 +187,16 @@ nsGenericHTMLFrameElement::GetFrameLoade
 NS_IMETHODIMP_(already_AddRefed<nsFrameLoader>)
 nsGenericHTMLFrameElement::GetFrameLoader()
 {
   RefPtr<nsFrameLoader> loader = mFrameLoader;
   return loader.forget();
 }
 
 NS_IMETHODIMP
-nsGenericHTMLFrameElement::GetParentApplication(mozIApplication** aApplication)
-{
-  if (!aApplication) {
-    return NS_ERROR_FAILURE;
-  }
-
-  *aApplication = nullptr;
-
-  uint32_t appId;
-  nsIPrincipal *principal = NodePrincipal();
-  nsresult rv = principal->GetAppId(&appId);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
-  if (NS_WARN_IF(!appsService)) {
-    return NS_ERROR_FAILURE;
-  }
-
-  rv = appsService->GetAppByLocalId(appId, aApplication);
-  if (NS_WARN_IF(NS_FAILED(rv))) {
-    return rv;
-  }
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsGenericHTMLFrameElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
 {
   // We don't support this yet
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
 nsGenericHTMLFrameElement::SetIsPrerendered()
--- a/dom/interfaces/base/nsITabParent.idl
+++ b/dom/interfaces/base/nsITabParent.idl
@@ -1,16 +1,16 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 
 #include "domstubs.idl"
 
-[builtinclass, scriptable, uuid(8e49f7b0-1f98-4939-bf91-e9c39cd56434)]
+[scriptable, uuid(7615408c-1fb3-4128-8dd5-a3e2f3fa8842)]
 interface nsITabParent : nsISupports
 {
   void injectTouchEvent(in AString aType,
                         [array, size_is(count)] in uint32_t aIdentifiers,
                         [array, size_is(count)] in int32_t aXs,
                         [array, size_is(count)] in int32_t aYs,
                         [array, size_is(count)] in uint32_t aRxs,
                         [array, size_is(count)] in uint32_t aRys,
--- a/dom/ipc/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -755,23 +755,16 @@ child:
      * using the list of provided charCodes.
      *
      * @param charCode array of potential character codes
      * @param isTrusted true if triggered by a trusted key event
      * @param modifierMask indicates which accesskey modifiers are pressed
      */
     HandleAccessKey(uint32_t[] charCodes, bool isTrusted, int32_t modifierMask);
 
-    /**
-     * Propagate a refresh to the child process
-     */
-    AudioChannelChangeNotification(uint32_t aAudioChannel,
-                                   float aVolume,
-                                   bool aMuted);
-
 /*
  * FIXME: write protocol!
 
 state LIVE:
     send LoadURL goto LIVE;
 //etc.
     send Destroy goto DYING;
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -2250,37 +2250,16 @@ TabChild::RecvHandleAccessKey(nsTArray<u
       pc->EventStateManager()->HandleAccessKey(pc, aCharCodes, aIsTrusted, aModifierMask);
     }
   }
 
   return true;
 }
 
 bool
-TabChild::RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel,
-                                             const float& aVolume,
-                                             const bool& aMuted)
-{
-  nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(WebNavigation());
-  if (window) {
-    RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
-    MOZ_ASSERT(service);
-
-    service->SetAudioChannelVolume(window,
-                                   static_cast<AudioChannel>(aAudioChannel),
-                                   aVolume);
-    service->SetAudioChannelMuted(window,
-                                  static_cast<AudioChannel>(aAudioChannel),
-                                  aMuted);
-  }
-
-  return true;
-}
-
-bool
 TabChild::RecvDestroy()
 {
   MOZ_ASSERT(mDestroyed == false);
   mDestroyed = true;
 
   nsTArray<PContentPermissionRequestChild*> childArray =
       nsContentPermissionUtils::GetContentPermissionRequestChildById(GetTabId());
 
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -499,20 +499,16 @@ public:
     virtual bool RecvUIResolutionChanged(const float& aDpi, const double& aScale) override;
 
     virtual bool RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) override;
 
     virtual bool RecvHandleAccessKey(nsTArray<uint32_t>&& aCharCodes,
                                      const bool& aIsTrusted,
                                      const int32_t& aModifierMask) override;
 
-    virtual bool RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel,
-                                                    const float& aVolume,
-                                                    const bool& aMuted) override;
-
     /**
      * Native widget remoting protocol for use with windowed plugins with e10s.
      */
     PPluginWidgetChild* AllocPPluginWidgetChild() override;
     bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) override;
     nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut);
 
     LayoutDeviceIntPoint GetChromeDisplacement() { return mChromeDisp; };
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -179,17 +179,17 @@ private:
             FileDescriptor::PlatformHandleType handle =
                 FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(mFD));
             fd = FileDescriptor(handle);
         }
 
         // Our TabParent may have been destroyed already.  If so, don't send any
         // fds over, just go back to the IO thread and close them.
         if (!tabParent->IsDestroyed()) {
-            Unused << tabParent->SendCacheFileDescriptor(mPath, fd);
+            mozilla::Unused << tabParent->SendCacheFileDescriptor(mPath, fd);
         }
 
         if (!mFD) {
             return;
         }
 
         nsCOMPtr<nsIEventTarget> eventTarget;
         mEventTarget.swap(eventTarget);
@@ -227,17 +227,17 @@ private:
         OpenBlobImpl();
 
         if (NS_FAILED(NS_DispatchToMainThread(this))) {
             NS_WARNING("Failed to dispatch to main thread!");
 
             // Intentionally leak the runnable (but not the fd) rather
             // than crash when trying to release a main thread object
             // off the main thread.
-            Unused << mTabParent.forget();
+            mozilla::Unused << mTabParent.forget();
             CloseFile();
         }
     }
 
     void CloseFile()
     {
         // It's possible for this to happen on the main thread if the dispatch
         // to the stream service fails after we've already opened the file so
@@ -382,21 +382,16 @@ TabParent::AddWindowListeners()
         eventTarget->AddEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
                                       this, false, false);
       }
     }
     if (nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell()) {
       mPresShellWithRefreshListener = shell;
       shell->AddPostRefreshObserver(this);
     }
-
-    RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate();
-    if (acs) {
-      acs->RegisterTabParent(this);
-    }
   }
 }
 
 void
 TabParent::RemoveWindowListeners()
 {
   if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) {
     nsCOMPtr<nsPIDOMWindow> window = mFrameElement->OwnerDoc()->GetWindow();
@@ -405,21 +400,16 @@ TabParent::RemoveWindowListeners()
       eventTarget->RemoveEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
                                        this, false);
     }
   }
   if (mPresShellWithRefreshListener) {
     mPresShellWithRefreshListener->RemovePostRefreshObserver(this);
     mPresShellWithRefreshListener = nullptr;
   }
-
-  RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate();
-  if (acs) {
-    acs->UnregisterTabParent(this);
-  }
 }
 
 void
 TabParent::DidRefresh()
 {
   if (mChromeOffset != -GetChildProcessOffset()) {
     UpdatePosition();
   }
@@ -2641,16 +2631,17 @@ TabParent::RecvAudioChannelActivityNotif
                                                 const bool& aActive)
 {
   if (aAudioChannel >= NUMBER_OF_AUDIO_CHANNELS) {
     return false;
   }
 
   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
   if (os) {
+    RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
     nsAutoCString topic;
     topic.Assign("audiochannel-activity-");
     topic.Append(AudioChannelService::GetAudioChannelTable()[aAudioChannel].tag);
 
     os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this),
                         topic.get(),
                         aActive ? MOZ_UTF16("active") : MOZ_UTF16("inactive"));
   }
@@ -3401,43 +3392,16 @@ TabParent::GetShowInfo()
     return ShowInfo(name, allowFullscreen, isPrivate, false,
                     mDPI, mDefaultScale.scale);
   }
 
   return ShowInfo(EmptyString(), false, false, false,
                   mDPI, mDefaultScale.scale);
 }
 
-void
-TabParent::AudioChannelChangeNotification(nsPIDOMWindow* aWindow,
-                                          AudioChannel aAudioChannel,
-                                          float aVolume,
-                                          bool aMuted)
-{
-  if (!mFrameElement || !mFrameElement->OwnerDoc()) {
-    return;
-  }
-
-  nsCOMPtr<nsPIDOMWindow> window = mFrameElement->OwnerDoc()->GetWindow();
-  while (window) {
-    if (window == aWindow) {
-      Unused << SendAudioChannelChangeNotification(static_cast<uint32_t>(aAudioChannel),
-                                                   aVolume, aMuted);
-      break;
-    }
-
-    nsCOMPtr<nsPIDOMWindow> win = window->GetScriptableParent();
-    if (window == win) {
-      break;
-    }
-
-    window = win;
-  }
-}
-
 NS_IMETHODIMP
 FakeChannel::OnAuthAvailable(nsISupports *aContext, nsIAuthInformation *aAuthInfo)
 {
   nsAuthInformationHolder* holder =
     static_cast<nsAuthInformationHolder*>(aAuthInfo);
 
   if (!net::gNeckoChild->SendOnAuthAvailable(mCallbackId,
                                              holder->User(),
--- a/dom/ipc/TabParent.h
+++ b/dom/ipc/TabParent.h
@@ -4,17 +4,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_tabs_TabParent_h
 #define mozilla_tabs_TabParent_h
 
 #include "js/TypeDecls.h"
 #include "mozilla/ContentCache.h"
-#include "mozilla/dom/AudioChannelBinding.h"
 #include "mozilla/dom/ipc/IdType.h"
 #include "mozilla/dom/PBrowserParent.h"
 #include "mozilla/dom/PContent.h"
 #include "mozilla/dom/PFilePickerParent.h"
 #include "mozilla/dom/TabContext.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/File.h"
 #include "mozilla/RefPtr.h"
@@ -451,21 +450,16 @@ public:
                                int32_t& aDragAreaX, int32_t& aDragAreaY);
     layout::RenderFrameParent* GetRenderFrame();
 
     // Called by HttpChannelParent. The function may use a new process to
     // reload the URI associated with the given channel.
     void OnStartSignedPackageRequest(nsIChannel* aChannel,
                                      const nsACString& aPackageId);
 
-    void AudioChannelChangeNotification(nsPIDOMWindow* aWindow,
-                                        AudioChannel aAudioChannel,
-                                        float aVolume,
-                                        bool aMuted);
-
 protected:
     bool ReceiveMessage(const nsString& aMessage,
                         bool aSync,
                         ipc::StructuredCloneData* aData,
                         mozilla::jsipc::CpowHolder* aCpows,
                         nsIPrincipal* aPrincipal,
                         nsTArray<ipc::StructuredCloneData>* aJSONRetVal = nullptr);
 
--- a/dom/system/gonk/AudioChannelManager.cpp
+++ b/dom/system/gonk/AudioChannelManager.cpp
@@ -208,16 +208,16 @@ AudioChannelManager::GetAllowedAudioChan
 
   nsAutoString manifestURL;
   aRv = appsService->GetManifestURLByLocalId(appId, manifestURL);
   if (NS_WARN_IF(aRv.Failed())) {
     return;
   }
 
   nsBrowserElement::GenerateAllowedAudioChannels(window, nullptr, nullptr,
-                                                 manifestURL, nullptr,
-                                                 aAudioChannels, aRv);
+                                                 manifestURL, aAudioChannels,
+                                                 aRv);
   NS_WARN_IF(aRv.Failed());
 }
 
 } // namespace system
 } // namespace dom
 } // namespace mozilla
--- a/dom/webidl/Navigator.webidl
+++ b/dom/webidl/Navigator.webidl
@@ -36,17 +36,17 @@ interface NavigatorID {
   [Constant, Cached]
   readonly attribute DOMString appCodeName; // constant "Mozilla"
   [Constant, Cached]
   readonly attribute DOMString appName;
   [Constant, Cached]
   readonly attribute DOMString appVersion;
   [Constant, Cached]
   readonly attribute DOMString platform;
-  [Constant, Cached, Throws=Workers]
+  [Pure, Cached, Throws=Workers]
   readonly attribute DOMString userAgent;
   [Constant, Cached]
   readonly attribute DOMString product; // constant "Gecko"
 
   // Everyone but WebKit/Blink supports this.  See bug 679971.
   [Exposed=Window]
   boolean taintEnabled(); // constant false
 };
--- a/dom/xul/nsXULElement.cpp
+++ b/dom/xul/nsXULElement.cpp
@@ -1612,27 +1612,16 @@ nsXULElement::GetFrameLoader()
     if (!slots)
         return nullptr;
 
     RefPtr<nsFrameLoader> loader = slots->mFrameLoader;
     return loader.forget();
 }
 
 nsresult
-nsXULElement::GetParentApplication(mozIApplication** aApplication)
-{
-    if (!aApplication) {
-        return NS_ERROR_FAILURE;
-    }
-
-    *aApplication = nullptr;
-    return NS_OK;
-}
-
-nsresult
 nsXULElement::SetIsPrerendered()
 {
   return SetAttr(kNameSpaceID_None, nsGkAtoms::prerendered, nullptr,
                  NS_LITERAL_STRING("true"), true);
 }
 
 nsresult
 nsXULElement::SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner)
--- a/dom/xul/nsXULElement.h
+++ b/dom/xul/nsXULElement.h
@@ -407,17 +407,16 @@ public:
 
     // nsIDOMXULElement
     NS_DECL_NSIDOMXULELEMENT
 
     virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const override;
     virtual mozilla::EventStates IntrinsicState() const override;
 
     nsresult GetFrameLoader(nsIFrameLoader** aFrameLoader);
-    nsresult GetParentApplication(mozIApplication** aApplication);
     nsresult SetIsPrerendered();
     nsresult SwapFrameLoaders(nsIFrameLoaderOwner* aOtherOwner);
 
     virtual void RecompileScriptEventListeners() override;
 
     // This function should ONLY be used by BindToTree implementations.
     // The function exists solely because XUL elements store the binding
     // parent as a member instead of in the slots, as Element does.
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -3026,27 +3026,31 @@ public class BrowserApp extends GeckoApp
 
         final boolean inGuestMode = GeckoProfile.get(this).inGuestMode();
 
         final boolean isAboutReader = AboutPages.isAboutReader(tab.getURL());
         bookmark.setEnabled(!isAboutReader);
         bookmark.setVisible(!inGuestMode);
         bookmark.setCheckable(true);
         bookmark.setChecked(tab.isBookmark());
-        bookmark.setIcon(resolveBookmarkIconID(tab.isBookmark()));
         bookmark.setTitle(resolveBookmarkTitleID(tab.isBookmark()));
 
         reader.setEnabled(isAboutReader || !AboutPages.isAboutPage(tab.getURL()));
         reader.setVisible(!inGuestMode);
         reader.setCheckable(true);
         final boolean isPageInReadingList = tab.isInReadingList();
         reader.setChecked(isPageInReadingList);
-        reader.setIcon(resolveReadingListIconID(isPageInReadingList));
         reader.setTitle(resolveReadingListTitleID(isPageInReadingList));
 
+        if (Versions.feature11Plus) {
+            // We don't use icons on GB builds so not resolving icons might conserve resources.
+            bookmark.setIcon(resolveBookmarkIconID(tab.isBookmark()));
+            reader.setIcon(resolveReadingListIconID(isPageInReadingList));
+        }
+
         back.setEnabled(tab.canDoBack());
         forward.setEnabled(tab.canDoForward());
         desktopMode.setChecked(tab.getDesktopMode());
 
         View backButtonView = MenuItemCompat.getActionView(back);
 
         if (backButtonView != null) {
             backButtonView.setOnLongClickListener(new Button.OnLongClickListener() {
@@ -3228,41 +3232,53 @@ public class BrowserApp extends GeckoApp
         mBrowserToolbar.cancelEdit();
 
         if (itemId == R.id.bookmark) {
             tab = Tabs.getInstance().getSelectedTab();
             if (tab != null) {
                 if (item.isChecked()) {
                     Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, "bookmark");
                     tab.removeBookmark();
-                    item.setIcon(resolveBookmarkIconID(false));
                     item.setTitle(resolveBookmarkTitleID(false));
+                    if (Versions.feature11Plus) {
+                        // We don't use icons on GB builds so not resolving icons might conserve resources.
+                        item.setIcon(resolveBookmarkIconID(false));
+                    }
                 } else {
                     Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "bookmark");
                     tab.addBookmark();
-                    item.setIcon(resolveBookmarkIconID(true));
                     item.setTitle(resolveBookmarkTitleID(true));
+                    if (Versions.feature11Plus) {
+                        // We don't use icons on GB builds so not resolving icons might conserve resources.
+                        item.setIcon(resolveBookmarkIconID(true));
+                    }
                 }
             }
             return true;
         }
 
         if (itemId == R.id.reading_list) {
             tab = Tabs.getInstance().getSelectedTab();
             if (tab != null) {
                 if (item.isChecked()) {
                     Telemetry.sendUIEvent(TelemetryContract.Event.UNSAVE, TelemetryContract.Method.MENU, "reading_list");
                     tab.removeFromReadingList();
-                    item.setIcon(resolveReadingListIconID(false));
                     item.setTitle(resolveReadingListTitleID(false));
+                    if (Versions.feature11Plus) {
+                        // We don't use icons on GB builds so not resolving icons might conserve resources.
+                        item.setIcon(resolveReadingListIconID(false));
+                    }
                 } else {
                     Telemetry.sendUIEvent(TelemetryContract.Event.SAVE, TelemetryContract.Method.MENU, "reading_list");
                     tab.addToReadingList();
-                    item.setIcon(resolveReadingListIconID(true));
                     item.setTitle(resolveReadingListTitleID(true));
+                    if (Versions.feature11Plus) {
+                        // We don't use icons on GB builds so not resolving icons might conserve resources.
+                        item.setIcon(resolveReadingListIconID(true));
+                    }
                 }
             }
             return true;
         }
 
         if (itemId == R.id.share) {
             tab = Tabs.getInstance().getSelectedTab();
             if (tab != null) {
--- a/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/GeckoApp.java
@@ -344,21 +344,17 @@ public abstract class GeckoApp
 
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.gecko_app_menu, mMenu);
         return true;
     }
 
     @Override
     public MenuInflater getMenuInflater() {
-        if (Versions.feature11Plus) {
-            return new GeckoMenuInflater(this);
-        } else {
-            return super.getMenuInflater();
-        }
+        return new GeckoMenuInflater(this);
     }
 
     public MenuPanel getMenuPanel() {
         if (mMenuPanel == null) {
             onCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, null);
             invalidateOptionsMenu();
         }
         return mMenuPanel;
@@ -399,33 +395,33 @@ public abstract class GeckoApp
 
     @Override
     public void closeMenu() {
         closeOptionsMenu();
     }
 
     @Override
     public View onCreatePanelView(int featureId) {
-        if (Versions.feature11Plus && featureId == Window.FEATURE_OPTIONS_PANEL) {
+        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
             if (mMenuPanel == null) {
                 mMenuPanel = new MenuPanel(this, null);
             } else {
                 // 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 (featureId == Window.FEATURE_OPTIONS_PANEL) {
             if (mMenuPanel == null) {
                 mMenuPanel = (MenuPanel) onCreatePanelView(featureId);
             }
 
             GeckoMenu gMenu = new GeckoMenu(this, null);
             gMenu.setCallback(this);
             gMenu.setMenuPresenter(this);
             menu = gMenu;
@@ -434,31 +430,31 @@ public abstract class GeckoApp
             return onCreateOptionsMenu(menu);
         }
 
         return super.onCreatePanelMenu(featureId, menu);
     }
 
     @Override
     public boolean onPreparePanel(int featureId, View view, Menu menu) {
-        if (Versions.feature11Plus && featureId == Window.FEATURE_OPTIONS_PANEL) {
+        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
             return onPrepareOptionsMenu(menu);
         }
 
         return super.onPreparePanel(featureId, view, menu);
     }
 
     @Override
     public boolean onMenuOpened(int featureId, Menu menu) {
         // exit full-screen mode whenever the menu is opened
         if (mLayerView != null && mLayerView.isFullScreen()) {
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FullScreen:Exit", null));
         }
 
-        if (Versions.feature11Plus && featureId == Window.FEATURE_OPTIONS_PANEL) {
+        if (featureId == Window.FEATURE_OPTIONS_PANEL) {
             if (mMenu == null) {
                 // getMenuPanel() will force the creation of the menu as well
                 MenuPanel panel = getMenuPanel();
                 onPreparePanel(featureId, panel, mMenu);
             }
 
             // Scroll custom menu to the top
             if (mMenuPanel != null)
@@ -513,20 +509,18 @@ public abstract class GeckoApp
             return true;
         }
 
         return super.onOptionsItemSelected(item);
     }
 
     @Override
     public void onOptionsMenuClosed(Menu menu) {
-        if (Versions.feature11Plus) {
-            mMenuPanel.removeAllViews();
-            mMenuPanel.addView((GeckoMenu) mMenu);
-        }
+        mMenuPanel.removeAllViews();
+        mMenuPanel.addView((GeckoMenu) mMenu);
     }
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         // Handle hardware menu key presses separately so that we can show a custom menu in some cases.
         if (keyCode == KeyEvent.KEYCODE_MENU) {
             openOptionsMenu();
             return true;
@@ -2156,16 +2150,17 @@ public abstract class GeckoApp
             "DOMFullScreen:Start",
             "DOMFullScreen:Stop",
             "Image:SetAs",
             "Locale:Set",
             "Permissions:Data",
             "PrivateBrowsing:Data",
             "Session:StatePurged",
             "Share:Text",
+            "Snackbar:Show",
             "SystemUI:Visibility",
             "ToggleChrome:Focus",
             "ToggleChrome:Hide",
             "ToggleChrome:Show",
             "Update:Check",
             "Update:Download",
             "Update:Install");
 
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/GeckoPreferences.java
@@ -132,17 +132,16 @@ OnSharedPreferenceChangeListener
     private static final String PREFS_TRACKING_PROTECTION = "privacy.trackingprotection.state";
     private static final String PREFS_TRACKING_PROTECTION_PB = "privacy.trackingprotection.pbmode.enabled";
     public static final String PREFS_OPEN_URLS_IN_PRIVATE = NON_PREF_PREFIX + "openExternalURLsPrivately";
     public static final String PREFS_VOICE_INPUT_ENABLED = NON_PREF_PREFIX + "voice_input_enabled";
     public static final String PREFS_QRCODE_ENABLED = NON_PREF_PREFIX + "qrcode_enabled";
     private static final String PREFS_ADVANCED = NON_PREF_PREFIX + "advanced.enabled";
     private static final String PREFS_ACCESSIBILITY = NON_PREF_PREFIX + "accessibility.enabled";
     private static final String PREFS_CUSTOMIZE_HOME = NON_PREF_PREFIX + "customize_home";
-    private static final String PREFS_CUSTOMIZE_IMAGE_BLOCKING = "browser.image_blocking";
     private static final String PREFS_TRACKING_PROTECTION_PRIVATE_BROWSING = "privacy.trackingprotection.pbmode.enabled";
     private static final String PREFS_TRACKING_PROTECTION_LEARN_MORE = NON_PREF_PREFIX + "trackingprotection.learn_more";
     private static final String PREFS_CLEAR_PRIVATE_DATA = NON_PREF_PREFIX + "privacy.clear";
     private static final String PREFS_CLEAR_PRIVATE_DATA_EXIT = NON_PREF_PREFIX + "history.clear_on_exit";
     private static final String PREFS_SCREEN_ADVANCED = NON_PREF_PREFIX + "advanced_screen";
     private static final String PREFS_CATEGORY_HOMEPAGE = NON_PREF_PREFIX + "category_homepage";
     public static final String PREFS_HOMEPAGE = NON_PREF_PREFIX + "homepage";
     public static final String PREFS_HISTORY_SAVED_SEARCH = NON_PREF_PREFIX + "search.search_history.enabled";
@@ -834,22 +833,16 @@ OnSharedPreferenceChangeListener
                         continue;
                     }
                 } else if (PREFS_MP_ENABLED.equals(key)) {
                     if (!Restrictions.isAllowed(this, Restrictable.MASTER_PASSWORD)) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
-                } else if (PREFS_CUSTOMIZE_IMAGE_BLOCKING.equals(key)) {
-                    if (!AppConstants.NIGHTLY_BUILD) {
-                        preferences.removePreference(pref);
-                        i--;
-                        continue;
-                    }
                 } else if (PREFS_CLEAR_PRIVATE_DATA.equals(key) || PREFS_CLEAR_PRIVATE_DATA_EXIT.equals(key)) {
                     if (!Restrictions.isAllowed(this, Restrictable.CLEAR_HISTORY)) {
                         preferences.removePreference(pref);
                         i--;
                         continue;
                     }
                 } else if (PREFS_HOMEPAGE.equals(key)) {
                         String setUrl = GeckoSharedPrefs.forProfile(getBaseContext()).getString(PREFS_HOMEPAGE, AboutPages.HOME);
--- a/mobile/android/base/java/org/mozilla/gecko/tabs/TabsPanel.java
+++ b/mobile/android/base/java/org/mozilla/gecko/tabs/TabsPanel.java
@@ -362,23 +362,18 @@ public class TabsPanel extends LinearLay
 
             default:
                 throw new IllegalArgumentException("Unknown panel type " + panelToShow);
         }
         mPanel.show();
 
         mAddTab.setVisibility(View.VISIBLE);
 
-        if (!HardwareUtils.hasMenuButton()) {
-            mMenuButton.setVisibility(View.VISIBLE);
-            mMenuButton.setEnabled(true);
-            mPopupMenu.setAnchor(mMenuButton);
-        } else {
-            mPopupMenu.setAnchor(mAddTab);
-        }
+        mMenuButton.setEnabled(true);
+        mPopupMenu.setAnchor(mMenuButton);
     }
 
     public int getVerticalPanelHeight() {
         final int actionBarHeight = mContext.getResources().getDimensionPixelSize(R.dimen.browser_toolbar_height);
         final int height = actionBarHeight + getTabContainerHeight(mTabsContainer);
         return height;
     }
 
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbar.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbar.java
@@ -124,17 +124,16 @@ public abstract class BrowserToolbar ext
     protected final List<View> focusOrder;
 
     private OnActivateListener activateListener;
     private OnFocusChangeListener focusChangeListener;
     private OnStartEditingListener startEditingListener;
     private OnStopEditingListener stopEditingListener;
 
     protected final BrowserApp activity;
-    protected boolean hasSoftMenuButton;
 
     protected UIMode uiMode;
     protected TabHistoryController tabHistoryController;
 
     private final Paint shadowPaint;
     private final int shadowColor;
     private final int shadowPrivateColor;
     private final int shadowSize;
@@ -189,17 +188,16 @@ public abstract class BrowserToolbar ext
         tabsButton = (ThemedImageButton) findViewById(R.id.tabs);
         tabsCounter = (TabCounter) findViewById(R.id.tabs_counter);
         if (Versions.feature11Plus) {
             tabsCounter.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
         }
 
         menuButton = (ThemedFrameLayout) findViewById(R.id.menu);
         menuIcon = (ThemedImageView) findViewById(R.id.menu_icon);
-        hasSoftMenuButton = !HardwareUtils.hasMenuButton();
 
         // The focusOrder List should be filled by sub-classes.
         focusOrder = new ArrayList<View>();
 
         final Resources res = getResources();
         shadowSize = res.getDimensionPixelSize(R.dimen.browser_toolbar_shadow_size);
 
         shadowPaint = new Paint();
@@ -316,27 +314,24 @@ public abstract class BrowserToolbar ext
                 // panel open does not go to the editing field.
                 urlEditLayout.clearFocus();
 
                 toggleTabs();
             }
         });
         tabsButton.setImageLevel(0);
 
-        if (hasSoftMenuButton) {
-            menuButton.setVisibility(View.VISIBLE);
-            menuButton.setOnClickListener(new Button.OnClickListener() {
-                @Override
-                public void onClick(View view) {
-                    // Drop the soft keyboard.
-                    urlEditLayout.clearFocus();
-                    activity.openOptionsMenu();
-                }
-            });
-        }
+        menuButton.setOnClickListener(new Button.OnClickListener() {
+            @Override
+            public void onClick(View view) {
+                // Drop the soft keyboard.
+                urlEditLayout.clearFocus();
+                activity.openOptionsMenu();
+            }
+        });
     }
 
     @Override
     public void onDetachedFromWindow() {
         super.onDetachedFromWindow();
 
         prefs.close();
     }
@@ -655,17 +650,17 @@ public abstract class BrowserToolbar ext
 
         if (needsNewFocus) {
             requestFocus();
         }
     }
 
     public void setToolBarButtonsAlpha(float alpha) {
         ViewHelper.setAlpha(tabsCounter, alpha);
-        if (hasSoftMenuButton && !HardwareUtils.isTablet()) {
+        if (!HardwareUtils.isTablet()) {
             ViewHelper.setAlpha(menuIcon, alpha);
         }
     }
 
     public void onEditSuggestion(String suggestion) {
         if (!isEditing()) {
             return;
         }
@@ -850,20 +845,16 @@ public abstract class BrowserToolbar ext
     }
 
     public void onDestroy() {
         Tabs.unregisterOnTabsChangedListener(this);
         urlDisplayLayout.destroy();
     }
 
     public boolean openOptionsMenu() {
-        if (!hasSoftMenuButton) {
-            return false;
-        }
-
         // Initialize the popup.
         if (menuPopup == null) {
             View panel = activity.getMenuPanel();
             menuPopup = new MenuPopup(activity);
             menuPopup.setPanelView(panel);
 
             menuPopup.setOnDismissListener(new PopupWindow.OnDismissListener() {
                 @Override
@@ -877,20 +868,16 @@ public abstract class BrowserToolbar ext
         if (!menuPopup.isShowing()) {
             menuPopup.showAsDropDown(menuButton);
         }
 
         return true;
     }
 
     public boolean closeOptionsMenu() {
-        if (!hasSoftMenuButton) {
-            return false;
-        }
-
         if (menuPopup != null && menuPopup.isShowing()) {
             menuPopup.dismiss();
         }
 
         return true;
     }
 
     @Override
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarPhone.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarPhone.java
@@ -113,20 +113,16 @@ class BrowserToolbarPhone extends Browse
                         PropertyAnimator.Property.TRANSLATION_X,
                         entryTranslation);
         animator.attach(tabsButton,
                         PropertyAnimator.Property.TRANSLATION_X,
                         curveTranslation);
         animator.attach(tabsCounter,
                         PropertyAnimator.Property.TRANSLATION_X,
                         curveTranslation);
-
-        if (!HardwareUtils.hasMenuButton()) {
-            animator.attach(menuButton,
-                            PropertyAnimator.Property.TRANSLATION_X,
-                            curveTranslation);
-
-            animator.attach(menuIcon,
-                            PropertyAnimator.Property.TRANSLATION_X,
-                            curveTranslation);
-        }
+        animator.attach(menuButton,
+                        PropertyAnimator.Property.TRANSLATION_X,
+                        curveTranslation);
+        animator.attach(menuIcon,
+                        PropertyAnimator.Property.TRANSLATION_X,
+                        curveTranslation);
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarPhoneBase.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarPhoneBase.java
@@ -122,34 +122,28 @@ abstract class BrowserToolbarPhoneBase e
             canvas.drawPath(roundCornerShape, roundCornerPaint);
         }
     }
 
     @Override
     public void triggerTabsPanelTransition(final PropertyAnimator animator, final boolean areTabsShown) {
         if (areTabsShown) {
             ViewHelper.setAlpha(tabsCounter, 0.0f);
-            if (hasSoftMenuButton) {
-                ViewHelper.setAlpha(menuIcon, 0.0f);
-            }
+            ViewHelper.setAlpha(menuIcon, 0.0f);
             return;
         }
 
         final PropertyAnimator buttonsAnimator =
                 new PropertyAnimator(animator.getDuration(), buttonsInterpolator);
-
         buttonsAnimator.attach(tabsCounter,
                                PropertyAnimator.Property.ALPHA,
                                1.0f);
-        if (hasSoftMenuButton) {
-            buttonsAnimator.attach(menuIcon,
-                                   PropertyAnimator.Property.ALPHA,
-                                   1.0f);
-        }
-
+        buttonsAnimator.attach(menuIcon,
+                               PropertyAnimator.Property.ALPHA,
+                               1.0f);
         buttonsAnimator.start();
     }
 
     /**
      * Returns the number of pixels the url bar translating edge
      * needs to translate to the right to enter its editing mode state.
      * A negative value means the edge must translate to the left.
      */
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarPreHC.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarPreHC.java
@@ -49,22 +49,17 @@ class BrowserToolbarPreHC extends Browse
             entryTranslation = getUrlBarEntryTranslation();
         } else {
             curveTranslation = 0;
             entryTranslation = 0;
         }
 
         // Prevent taps through the editing mode cancel button (bug 1001243).
         tabsButton.setEnabled(!isEditing);
+        menuButton.setEnabled(!isEditing);
 
         ViewHelper.setTranslationX(urlBarTranslatingEdge, entryTranslation);
         ViewHelper.setTranslationX(tabsButton, curveTranslation);
         ViewHelper.setTranslationX(tabsCounter, curveTranslation);
-
-        if (!HardwareUtils.hasMenuButton()) {
-            // Prevent tabs through the editing mode cancel button (bug 1001243).
-            menuButton.setEnabled(!isEditing);
-
-            ViewHelper.setTranslationX(menuButton, curveTranslation);
-            ViewHelper.setTranslationX(menuIcon, curveTranslation);
-        }
+        ViewHelper.setTranslationX(menuButton, curveTranslation);
+        ViewHelper.setTranslationX(menuIcon, curveTranslation);
     }
 }
--- a/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarTabletBase.java
+++ b/mobile/android/base/java/org/mozilla/gecko/toolbar/BrowserToolbarTabletBase.java
@@ -64,17 +64,17 @@ abstract class BrowserToolbarTabletBase 
         focusOrder.addAll(Arrays.asList(actionItemBar, menuButton));
 
         urlDisplayLayout.updateSiteIdentityAnchor(backButton);
 
         privateBrowsingTabletMenuItemColorFilter = new PorterDuffColorFilter(
                 ColorUtils.getColor(context, R.color.tabs_tray_icon_grey), PorterDuff.Mode.SRC_IN);
 
         menuButtonMarginView = findViewById(R.id.menu_margin);
-        if (menuButtonMarginView != null && !HardwareUtils.hasMenuButton()) {
+        if (menuButtonMarginView != null) {
             menuButtonMarginView.setVisibility(View.VISIBLE);
         }
     }
 
     private void initButtonListeners() {
         backButton.setOnClickListener(new Button.OnClickListener() {
             @Override
             public void onClick(View view) {
--- a/mobile/android/base/java/org/mozilla/gecko/util/HardwareUtils.java
+++ b/mobile/android/base/java/org/mozilla/gecko/util/HardwareUtils.java
@@ -31,48 +31,40 @@ public final class HardwareUtils {
                                                     Build.MODEL.startsWith("KF"));
 
     private static volatile boolean sInited;
 
     // These are all set once, during init.
     private static volatile boolean sIsLargeTablet;
     private static volatile boolean sIsSmallTablet;
     private static volatile boolean sIsTelevision;
-    private static volatile boolean sHasMenuButton;
 
     private HardwareUtils() {
     }
 
     public static void init(Context context) {
         if (sInited) {
             // This is unavoidable, given that HardwareUtils is called from background services.
             Log.d(LOGTAG, "HardwareUtils already inited.");
             return;
         }
 
         // Pre-populate common flags from the context.
         final int screenLayoutSize = context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK;
         if (Build.VERSION.SDK_INT >= 11) {
-            sHasMenuButton = false;
             if (screenLayoutSize == Configuration.SCREENLAYOUT_SIZE_XLARGE) {
                 sIsLargeTablet = true;
             } else if (screenLayoutSize == Configuration.SCREENLAYOUT_SIZE_LARGE) {
                 sIsSmallTablet = true;
             }
-            if (Build.VERSION.SDK_INT >= 14) {
-                sHasMenuButton = ViewConfiguration.get(context).hasPermanentMenuKey();
-
-                if (Build.VERSION.SDK_INT >= 16) {
-                    if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
-                        sIsTelevision = true;
-                    }
+            if (Build.VERSION.SDK_INT >= 16) {
+                if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_TELEVISION)) {
+                    sIsTelevision = true;
                 }
             }
-        } else {
-            sHasMenuButton = true;
         }
 
         sInited = true;
     }
 
     public static boolean isTablet() {
         return sIsLargeTablet || sIsSmallTablet;
     }
@@ -84,20 +76,16 @@ public final class HardwareUtils {
     public static boolean isSmallTablet() {
         return sIsSmallTablet;
     }
 
     public static boolean isTelevision() {
         return sIsTelevision;
     }
 
-    public static boolean hasMenuButton() {
-        return sHasMenuButton;
-    }
-
     public static int getMemSize() {
         return SysInfo.getMemSize();
     }
 
     public static boolean isLowMemoryPlatform() {
         final int memSize = getMemSize();
 
         // Fallback to false if we fail to read meminfo
--- a/mobile/android/base/locales/en-US/sync_strings.dtd
+++ b/mobile/android/base/locales/en-US/sync_strings.dtd
@@ -265,8 +265,13 @@
 <!-- Localization note: the format string below will be replaced
      with the Firefox Account's email address. -->
 <!ENTITY fxaccount_sync_sign_in_error_notification_text2 'Tap to sign in as &formatS;'>
 
 <!ENTITY fxaccount_sync_finish_migrating_notification_title 'Finish upgrading &syncBrand.shortName.label;?'>
 <!-- Localization note: the format string below will be replaced
      with the Firefox Account's email address. -->
 <!ENTITY fxaccount_sync_finish_migrating_notification_text 'Tap to sign in as &formatS;'>
+
+<!-- Localization note: the following strings are used in a
+     notification and should be kept as short as possible. -->
+<!ENTITY old_sync_deprecated_notification_title 'Sign in to continue syncing'>
+<!ENTITY old_sync_deprecated_notification_content 'Your account is no longer supported'>
rename from mobile/android/base/resources/drawable-hdpi/ic_menu_back.png
rename to mobile/android/base/resources/drawable-hdpi-v11/ic_menu_back.png
deleted file mode 100644
index 03a57c998cbf1c1060d09b0094ef3b0e2e574bbb..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 9e23244e2691906078732018e834458aa075bfe5..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 99138a56b96cc09b5fb50700e2056707f77c3a68..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 496ff3517df516bade75d9229cef5d37a14754db..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index d7c6dfa117f8d36e91324afad86e529ca1308932..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 24026f9c070ad71f4686629cb5af99d78ebb6528..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
rename from mobile/android/base/resources/drawable-xhdpi/ic_menu_back.png
rename to mobile/android/base/resources/drawable-xhdpi-v11/ic_menu_back.png
deleted file mode 100644
index 042dee803279815ebc8e93a7d849ec32d1a5e98a..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 50d992e7395a89043752f486305bfc5c95d58f96..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 51fea1bc49250dbc4d670f0a2f124cdb771286f1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 0ca97ab7ed5152eb9c17e0835816000f8e3a5da1..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 3b2622ec767c66787b040cdf3ae396f2e10fd55e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
deleted file mode 100644
index 712916f45d07295d1a55a07095ebdc3df9348bd6..0000000000000000000000000000000000000000
GIT binary patch
literal 0
Hc$@<O00001
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/drawable/ic_menu_bookmark_add.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- This asset is properly available in large-* dirs so this null
+     reference exists for build time on API 9 builds. -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+
+    <solid android:color="@android:color/transparent"/>
+
+</shape>
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/resources/drawable/ic_menu_bookmark_remove.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+   - License, v. 2.0. If a copy of the MPL was not distributed with this
+   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+
+<!-- This asset is properly available in large-* dirs so this null
+     reference exists for build time on API 9 builds. -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+       android:shape="rectangle">
+
+    <solid android:color="@android:color/transparent"/>
+
+</shape>
--- a/mobile/android/base/resources/layout-large-v11/browser_toolbar.xml
+++ b/mobile/android/base/resources/layout-large-v11/browser_toolbar.xml
@@ -119,18 +119,17 @@
           android:visibility="gone"/>
 
     <org.mozilla.gecko.widget.themed.ThemedFrameLayout
             android:id="@+id/menu"
             style="@style/UrlBar.ImageButton"
             android:layout_toLeftOf="@id/menu_margin"
             android:layout_alignWithParentIfMissing="true"
             android:contentDescription="@string/menu"
-            android:background="@drawable/browser_toolbar_action_bar_button"
-            android:visibility="gone">
+            android:background="@drawable/browser_toolbar_action_bar_button">
 
         <org.mozilla.gecko.widget.themed.ThemedImageView
                 android:id="@+id/menu_icon"
                 style="@style/UrlBar.ImageButton.BrowserToolbarColors"
                 android:layout_height="@dimen/browser_toolbar_menu_icon_height"
                 android:layout_width="wrap_content"
                 android:scaleType="centerInside"
                 android:src="@drawable/menu"
--- a/mobile/android/base/resources/layout/browser_toolbar.xml
+++ b/mobile/android/base/resources/layout/browser_toolbar.xml
@@ -36,18 +36,17 @@
                android:src="@drawable/url_bar_translating_edge"
                android:scaleType="fitXY"/>
 
     <org.mozilla.gecko.toolbar.ShapedButtonFrameLayout
             android:id="@+id/menu"
             style="@style/UrlBar.ImageButton"
             android:layout_alignParentRight="true"
             android:contentDescription="@string/menu"
-            android:background="@drawable/shaped_button"
-            android:visibility="gone">
+            android:background="@drawable/shaped_button">
 
         <org.mozilla.gecko.widget.themed.ThemedImageView
                 android:id="@+id/menu_icon"
                 style="@style/UrlBar.ImageButton"
                 android:layout_height="@dimen/browser_toolbar_menu_icon_height"
                 android:layout_width="wrap_content"
                 android:scaleType="centerInside"
                 android:layout_gravity="center"
--- a/mobile/android/base/resources/layout/tabs_panel_default.xml
+++ b/mobile/android/base/resources/layout/tabs_panel_default.xml
@@ -48,18 +48,17 @@
                          android:src="@drawable/tab_new"
                          android:contentDescription="@string/new_tab"
                          android:background="@drawable/action_bar_button_inverse"/>
 
             <FrameLayout android:id="@+id/menu"
                          style="@style/UrlBar.ImageButton"
                          android:layout_width="@dimen/tabs_panel_button_width"
                          android:background="@drawable/action_bar_button_inverse"
-                         android:contentDescription="@string/menu"
-                         android:visibility="gone">
+                         android:contentDescription="@string/menu">
 
                 <ImageButton
                         style="@style/UrlBar.ImageButton"
                         android:layout_width="wrap_content"
                         android:layout_height="@dimen/browser_toolbar_menu_icon_height"
                         android:layout_gravity="center"
                         android:scaleType="centerInside"
                         android:src="@drawable/menu"
--- a/mobile/android/base/resources/menu/browser_app_menu.xml
+++ b/mobile/android/base/resources/menu/browser_app_menu.xml
@@ -1,99 +1,104 @@
 <?xml version="1.0" encoding="utf-8"?>
 <!-- This Source Code Form is subject to the terms of the Mozilla Public
    - License, v. 2.0. If a copy of the MPL was not distributed with this
    - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
 
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
 
     <item android:id="@+id/reload"
-          android:icon="@drawable/ic_menu_reload"
           android:title="@string/reload"/>
 
+    <!-- We keep the reference so calls to findView don't fail. Hide
+         to avoid taking up real estate on the users' screen. -->
     <item android:id="@+id/back"
-          android:icon="@drawable/ic_menu_back"
           android:title="@string/back"
           android:visible="false"/>
 
     <item android:id="@+id/forward"
-          android:icon="@drawable/ic_menu_forward"
           android:title="@string/forward"/>
 
     <item android:id="@+id/bookmark"
-          android:icon="@drawable/ic_menu_bookmark_add"
           android:title="@string/bookmark"/>
 
     <item android:id="@+id/new_tab"
-          android:icon="@drawable/ic_menu_new_tab"
           android:title="@string/new_tab"/>
 
     <item android:id="@+id/new_private_tab"
-          android:icon="@drawable/ic_menu_new_private_tab"
           android:title="@string/new_private_tab"/>
 
     <item android:id="@+id/share"
           android:title="@string/share" />
 
     <item android:id="@+id/reading_list"
           android:title="@string/overlay_share_reading_list_btn_label" />
 
-    <item android:id="@+id/save_as_pdf"
-          android:title="@string/save_as_pdf" />
-
-    <item android:id="@+id/print"
-          android:visible="false"
-          android:title="@string/print" />
-
     <item android:id="@+id/find_in_page"
           android:title="@string/find_in_page" />
 
     <item android:id="@+id/desktop_mode"
           android:title="@string/desktop_mode"
           android:checkable="true" />
 
-    <item android:id="@+id/addons"
-          android:title="@string/addons"/>
+    <item android:id="@+id/page"
+          android:title="@string/page">
+
+        <menu>
+            <item android:id="@+id/subscribe"
+                  android:title="@string/contextmenu_subscribe"/>
+
+            <item android:id="@+id/save_as_pdf"
+                  android:title="@string/save_as_pdf"/>
+
+            <item android:id="@+id/print"
+                  android:title="@string/print"/>
+
+            <item android:id="@+id/add_search_engine"
+                  android:title="@string/contextmenu_add_search_engine"/>
+
+            <item android:id="@+id/add_to_launcher"
+                  android:title="@string/contextmenu_add_to_launcher"/>
+        </menu>
 
-    <item android:id="@+id/downloads"
-          android:title="@string/downloads"/>
+    </item>
+
+    <item android:id="@+id/tools"
+          android:title="@string/tools">
 
-    <item android:id="@+id/logins"
-          android:title="@string/logins"/>
+        <menu>
+            <item android:id="@+id/downloads"
+                  android:title="@string/downloads"/>
+            <item android:id="@+id/addons"
+                  android:title="@string/addons"/>
+            <item android:id="@+id/logins"
+                  android:title="@string/logins"/>
+            <item android:id="@+id/new_guest_session"
+                  android:visible="false"
+                  android:title="@string/new_guest_session"/>
+            <item android:id="@+id/exit_guest_session"
+                  android:visible="false"
+                  android:title="@string/exit_guest_session"/>
+        </menu>
+
+    </item>
 
     <item android:id="@+id/char_encoding"
           android:visible="false"
           android:title="@string/char_encoding"/>
 
     <item android:id="@+id/settings"
           android:title="@string/settings" />
 
     <item android:id="@+id/help"
           android:title="@string/help_menu" />
 
-    <item android:id="@+id/new_guest_session"
-          android:visible="false"
-          android:title="@string/new_guest_session"/>
-
-    <item android:id="@+id/exit_guest_session"
-          android:visible="false"
-          android:title="@string/exit_guest_session"/>
-
     <!-- Android will eliminate v11+ resource files from pre-11 builds.
          Those files are the only place in which certain IDs are defined.
          This causes compilation errors.
          To avoid nasty code workarounds, we define stub IDs here, but
          hide the menu entries. -->
 
     <item android:id="@+id/quickshare"
           android:visible="false"
           android:enabled="false" />
 
-    <item android:id="@+id/page"
-          android:visible="false"
-          android:enabled="false"
-          android:title="@string/page" />
-
-    <item android:id="@+id/tools"
-          android:visible="false"
-          android:enabled="false"
-          android:title="@string/tools" />
 </menu>
--- a/mobile/android/components/SessionStore.js
+++ b/mobile/android/components/SessionStore.js
@@ -998,17 +998,22 @@ SessionStore.prototype = {
       }
       aHistory.addEntry(this._deserializeHistoryEntry(aTabData.entries[i], idMap, docIdentMap), true);
     }
 
     // We need to force set the active history item and cause it to reload since
     // we stop the load above
     let activeIndex = (aTabData.index || aTabData.entries.length) - 1;
     aHistory.getEntryAtIndex(activeIndex, true);
-    aHistory.QueryInterface(Ci.nsISHistory).reloadCurrentEntry();
+
+    try {
+      aHistory.QueryInterface(Ci.nsISHistory).reloadCurrentEntry();
+    } catch (e) {
+      // This will throw if the current entry is an error page.
+    }
   },
 
   /**
   * Takes serialized form text data and restores it into the given browser.
   */
   _restoreTextData: function ss_restoreTextData(aTabData, aBrowser) {
     let formdata = aTabData.formdata;
     if (formdata) {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/healthreport/upload/HealthReportUploadService.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/healthreport/upload/HealthReportUploadService.java
@@ -63,17 +63,22 @@ public class HealthReportUploadService e
       Logger.warn(LOG_TAG, "Got intent without profilePath or profileName. Ignoring.");
       return;
     }
 
     if (!intent.hasExtra("uploadEnabled")) {
       Logger.warn(LOG_TAG, "Got intent without uploadEnabled. Ignoring.");
       return;
     }
-    boolean uploadEnabled = intent.getBooleanExtra("uploadEnabled", false);
+
+    // We disabled Health Report uploads in Bug 1230206, because the service is being decommissioned.
+    // We chose this specific place to turn uploads off because we wish to preserve deletions in the
+    // interim, and this is the tested code path for when a user turns off upload, but still expects
+    // deletions to work.
+    boolean uploadEnabled = false;
 
     // Don't do anything if the device can't talk to the server.
     if (!backgroundDataIsEnabled()) {
       Logger.debug(LOG_TAG, "Background data is not enabled; skipping.");
       return;
     }
 
     Logger.pii(LOG_TAG, "Ticking policy for profile " + profileName + " at " + profilePath + ".");
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/background/healthreport/upload/SubmissionPolicy.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/background/healthreport/upload/SubmissionPolicy.java
@@ -98,19 +98,21 @@ public class SubmissionPolicy {
       // We only delete (rather than mark as obsolete during upload) when
       // uploading is disabled. We try to delete aggressively, since the volume
       // of deletes should be very low. But we don't want to send too many
       // delete requests at the same time, so we process these one at a time. In
       // the future (Bug 872756), we will be able to delete multiple documents
       // with one request.
       final String obsoleteId = tracker.getNextObsoleteId();
       if (obsoleteId == null) {
+        Logger.debug(LOG_TAG, "Upload disabled and nothing to delete.");
         return false;
       }
 
+      Logger.info(LOG_TAG, "Upload disabled. Deleting obsolete document.");
       Editor editor = editor();
       editor.setLastDeleteRequested(localTime); // Write committed by delegate.
       client.delete(localTime, obsoleteId, new DeleteDelegate(editor));
       return true;
     }
 
     long firstRun = getFirstRunLocalTime();
     if (firstRun < 0) {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/fxa/activities/FxAccountStatusFragment.java
@@ -208,20 +208,16 @@ public class FxAccountStatusFragment
 
     syncServerPreference = ensureFindPreference("sync_server");
     morePreference = ensureFindPreference("more");
     morePreference.setOnPreferenceClickListener(this);
 
     syncNowPreference = ensureFindPreference("sync_now");
     syncNowPreference.setEnabled(true);
     syncNowPreference.setOnPreferenceClickListener(this);
-
-    if (HardwareUtils.hasMenuButton()) {
-      syncCategory.removePreference(morePreference);
-    }
   }
 
   /**
    * We intentionally don't refresh here. Our owning activity is responsible for
    * providing an AndroidFxAccount to our refresh method in its onResume method.
    */
   @Override
   public void onResume() {
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/SyncConstants.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/SyncConstants.java
@@ -49,9 +49,11 @@ public class SyncConstants {
    * Account type.
    */
   public static final String PER_ACCOUNT_TYPE_PERMISSION = AppConstants.MOZ_ANDROID_SHARED_ACCOUNT_TYPE + ".permission.PER_ACCOUNT_TYPE";
 
   public static final String DEFAULT_AUTH_SERVER = "https://auth.services.mozilla.com/";
 
   // Used for BackoffHandler storage for Sync 1.1's SyncAdapter.
   public static final String BACKOFF_PREF_SUFFIX_11 = "sync";
+
+  public static final String SYNC_ACCOUNT_DEPRECATED_ACTION = AppConstants.MOZ_ANDROID_SHARED_ACCOUNT_TYPE + ".accounts.SYNC_ACCOUNT_DEPRECATED_ACTION";
 }
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/receivers/SyncAccountDeletedService.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/receivers/SyncAccountDeletedService.java
@@ -1,28 +1,33 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync.receivers;
 
+import org.mozilla.gecko.fxa.FirefoxAccounts;
+import org.mozilla.gecko.fxa.FxAccountConstants;
+import org.mozilla.gecko.fxa.activities.FxAccountWebFlowActivity;
 import org.mozilla.gecko.sync.ExtendedJSONObject;
 import org.mozilla.gecko.background.common.GlobalConstants;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.Sync11Configuration;
 import org.mozilla.gecko.sync.SyncConstants;
 import org.mozilla.gecko.sync.SyncConfiguration;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.config.AccountPickler;
 import org.mozilla.gecko.sync.config.ClientRecordTerminator;
 import org.mozilla.gecko.sync.net.BasicAuthHeaderProvider;
 import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
 import org.mozilla.gecko.sync.setup.Constants;
+import org.mozilla.gecko.sync.setup.SyncAccounts;
 import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
 
+import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.app.IntentService;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 
 public class SyncAccountDeletedService extends IntentService {
   public static final String LOG_TAG = "SyncAccountDeletedService";
@@ -36,16 +41,37 @@ public class SyncAccountDeletedService e
     // Intent can, in theory, be null. Bug 1025937.
     if (intent == null) {
       Logger.debug(LOG_TAG, "Short-circuiting on null intent.");
       return;
     }
 
     final Context context = this;
 
+    if (SyncConstants.SYNC_ACCOUNT_DEPRECATED_ACTION.equals(intent.getAction())) {
+      // Delete Old Sync account.
+      final Account[] accounts = SyncAccounts.syncAccounts(this);
+      for (Account account : accounts) {
+        AccountManager.get(this).removeAccount(account, null, null);
+      }
+
+      // Offer signin for Firefox Account if we don't already have one.
+      if (!FirefoxAccounts.firefoxAccountsExist(this)) {
+        final Intent fxAccountWebIntent = new Intent(FxAccountConstants.ACTION_FXA_GET_STARTED);
+        fxAccountWebIntent.putExtra(FxAccountWebFlowActivity.EXTRA_ENDPOINT, FxAccountConstants.ENDPOINT_PREFERENCES);
+        fxAccountWebIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        fxAccountWebIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+        context.startActivity(fxAccountWebIntent);
+      }
+
+      // Return early. The SYNC_ACCOUNT_DELETED_ACTION spawn by the account
+      // deletion above will remove the pickle file.
+      return;
+    }
+
     long intentVersion = intent.getLongExtra(Constants.JSON_KEY_VERSION, 0);
     long expectedVersion = SyncConstants.SYNC_ACCOUNT_DELETED_INTENT_VERSION;
     if (intentVersion != expectedVersion) {
       Logger.warn(LOG_TAG, "Intent malformed: version " + intentVersion + " given but version " + expectedVersion + "expected. " +
           "Not cleaning up after deleted Account.");
       return;
     }
 
--- a/mobile/android/services/src/main/java/org/mozilla/gecko/sync/receivers/UpgradeReceiver.java
+++ b/mobile/android/services/src/main/java/org/mozilla/gecko/sync/receivers/UpgradeReceiver.java
@@ -1,44 +1,57 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko.sync.receivers;
 
+import org.mozilla.gecko.R;
 import org.mozilla.gecko.background.common.GlobalConstants;
 import org.mozilla.gecko.background.common.log.Logger;
 import org.mozilla.gecko.sync.CredentialException;
 import org.mozilla.gecko.sync.SyncConfiguration;
+import org.mozilla.gecko.sync.SyncConstants;
 import org.mozilla.gecko.sync.ThreadPool;
 import org.mozilla.gecko.sync.Utils;
 import org.mozilla.gecko.sync.config.ConfigurationMigrator;
 import org.mozilla.gecko.sync.setup.Constants;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
 import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
 import android.content.BroadcastReceiver;
 import android.content.Context;
 import android.content.Intent;
+import android.support.v4.app.NotificationCompat.Builder;
 
 public class UpgradeReceiver extends BroadcastReceiver {
   private static final String LOG_TAG = "UpgradeReceiver";
 
   @Override
   public void onReceive(final Context context, Intent intent) {
     Logger.debug(LOG_TAG, "Broadcast received.");
 
     // This unpickles any pickled accounts.
     if (!SyncAccounts.syncAccountsExist(context)) {
       Logger.info(LOG_TAG, "No Sync Accounts found; not upgrading anything.");
       return;
     }
 
+    // Show account not supported notification.
+    ThreadPool.run(new Runnable() {
+      @Override
+      public void run() {
+        notifyAccountDeprecation(context);
+      }
+    });
+
     // Should filter for specific MY_PACKAGE_REPLACED intent, but Android does
     // not expose it.
     ThreadPool.run(new Runnable() {
       @Override
       public void run() {
         final AccountManager accountManager = AccountManager.get(context);
         final Account[] accounts = SyncAccounts.syncAccounts(context);
 
@@ -89,9 +102,29 @@ public class UpgradeReceiver extends Bro
           } catch (Exception e) {
             Logger.warn(LOG_TAG, "Caught exception trying to migrate preferences; ignoring.", e);
             continue;
           }
         }
       }
     });
   }
+
+  /**
+   * Show a persistent notification telling the user that their Old Sync account is deprecated.
+   */
+  private void notifyAccountDeprecation(final Context context) {
+    final Intent notificationIntent = new Intent(SyncConstants.SYNC_ACCOUNT_DEPRECATED_ACTION);
+    notificationIntent.setClass(context, SyncAccountDeletedService.class);
+    final PendingIntent pendingIntent = PendingIntent.getService(context, 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+    final Builder builder = new Builder(context)
+        .setSmallIcon(R.drawable.ic_status_logo)
+        .setContentTitle(context.getString(R.string.old_sync_deprecated_notification_title))
+        .setContentText(context.getString(R.string.old_sync_deprecated_notification_content))
+        .setAutoCancel(true)
+        .setOngoing(true)
+        .setContentIntent(pendingIntent);
+
+    final NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
+    final int notificationID = SyncConstants.SYNC_ACCOUNT_DEPRECATED_ACTION.hashCode();
+    notificationManager.notify(notificationID, builder.build());
+  }
 }
--- a/mobile/android/services/strings.xml.in
+++ b/mobile/android/services/strings.xml.in
@@ -237,8 +237,12 @@
 <string name="fxaccount_remove_account_dialog_message">&fxaccount_remove_account_dialog_message;</string>
 <string name="fxaccount_remove_account_toast">&fxaccount_remove_account_toast;</string>
 
 <string name="fxaccount_sync_finish_migrating_notification_title">&fxaccount_sync_finish_migrating_notification_title;</string>
 <string name="fxaccount_sync_finish_migrating_notification_text">&fxaccount_sync_finish_migrating_notification_text;</string>
 
 <!-- Log Personal information -->
 <string name="fxaccount_enable_debug_mode">&fxaccount_enable_debug_mode;</string>
+
+<!-- Old Sync Account deprecated notification. -->
+<string name="old_sync_deprecated_notification_title">&old_sync_deprecated_notification_title;</string>
+<string name="old_sync_deprecated_notification_content">&old_sync_deprecated_notification_content;</string>
--- a/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
+++ b/mobile/android/tests/browser/robocop/src/org/mozilla/gecko/tests/components/AppMenuComponent.java
@@ -6,37 +6,37 @@ package org.mozilla.gecko.tests.componen
 
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertEquals;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertFalse;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertNotNull;
 import static org.mozilla.gecko.tests.helpers.AssertionHelper.fAssertTrue;
 
 import java.util.List;
 
+import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.R;
 import org.mozilla.gecko.menu.MenuItemActionBar;
 import org.mozilla.gecko.menu.MenuItemDefault;
 import org.mozilla.gecko.tests.UITestContext;
 import org.mozilla.gecko.tests.helpers.DeviceHelper;
 import org.mozilla.gecko.tests.helpers.WaitHelper;
-import org.mozilla.gecko.util.HardwareUtils;
 
 import android.text.TextUtils;
 import android.view.View;
 import android.widget.TextView;
 
 import com.jayway.android.robotium.solo.Condition;
 import com.jayway.android.robotium.solo.RobotiumUtils;
 import com.jayway.android.robotium.solo.Solo;
 
 /**
  * A class representing any interactions that take place on the app menu.
  */
 public class AppMenuComponent extends BaseComponent {
-    private static final long MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS = 1000L;
+    private static final long MAX_WAITTIME_FOR_MENU_UPDATE_IN_MS = 7500L;
 
     private Boolean hasLegacyMenu = null;
 
     public enum MenuItem {
         FORWARD(R.string.forward),
         NEW_TAB(R.string.new_tab),
         PAGE(R.string.page),
         RELOAD(R.string.reload);
@@ -238,19 +238,19 @@ public class AppMenuComponent extends Ba
 
     public void pressMenuItem(final PageMenuItem pageMenuItem) {
         pressSubMenuItem(PageMenuItem.PARENT_MENU.getString(mSolo), pageMenuItem.getString(mSolo));
     }
 
     private void openAppMenu() {
         assertMenuIsNotOpen();
 
-        // This is a hack needed for tablets where the OverflowMenuButton is always in the GONE state,
+        // This is a hack needed for tablets & GB where the OverflowMenuButton is always in the GONE state,
         // so we press the menu key instead.
-        if (HardwareUtils.hasMenuButton() || DeviceHelper.isTablet()) {
+        if (DeviceHelper.isTablet() || AppConstants.Versions.preHC) {
             mSolo.sendKey(Solo.MENU);
         } else {
             pressOverflowMenuButton();
         }
 
         waitForMenuOpen();
     }
 
@@ -278,22 +278,24 @@ public class AppMenuComponent extends Ba
 
         fAssertTrue("The overflow menu button is enabled", overflowMenuButton.isEnabled());
         fAssertEquals("The overflow menu button is visible", View.VISIBLE, overflowMenuButton.getVisibility());
 
         mSolo.clickOnView(overflowMenuButton, true);
     }
 
     /**
-    * Determines whether the app menu is open by searching for the text "New tab".
+    * Determines whether the app menu is open by searching for items in the menu.
     *
     * @return true if app menu is open.
     */
     private boolean isMenuOpen() {
-        return isMenuOpen(MenuItem.NEW_TAB.getString(mSolo));
+        // We choose these options because New Tab is near the top of the menu and Page is near the middle/bottom.
+        // Intermittently, the menu doesn't scroll to top so we can't just use the first item in the list.
+        return isMenuOpen(MenuItem.NEW_TAB.getString(mSolo)) || isMenuOpen(MenuItem.PAGE.getString(mSolo));
     }
 
     private boolean isLegacyMoreMenuOpen() {
         // Check if the first menu option is visible.
         return mSolo.searchText(mSolo.getString(R.string.share), true);
     }
 
     /**
--- a/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java
+++ b/mobile/android/thirdparty/com/keepsafe/switchboard/SwitchBoard.java
@@ -216,17 +216,17 @@ public class SwitchBoard {
 			}
 			
 			//load config, includes all experiments
 			String serverUrl = Preferences.getDynamicConfigServerUrl(c);
 			
 			if(serverUrl != null) {
 				String params = "uuid="+uuid+"&device="+device+"&lang="+lang+"&country="+country
 						+"&manufacturer="+manufacturer+"&appId="+packageName+"&version="+versionName;
-				if(DEBUG) Log.d(TAG, "Read from server URL: " + serverUrl + params);
+				if(DEBUG) Log.d(TAG, "Read from server URL: " + serverUrl + "?" + params);
 				String serverConfig = readFromUrlGET(serverUrl, params);
 				
 				if(DEBUG) Log.d(TAG, serverConfig);
 				
 				//store experiments in shared prefs (one variable)
 				if(serverConfig != null)
 					Preferences.setDynamicConfigJson(c, serverConfig);
 			}
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -32,16 +32,17 @@
 #include "nsICookieService.h"
 #include "nsIStreamConverterService.h"
 #include "nsCRT.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsIObserverService.h"
 #include "nsProxyRelease.h"
 #include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
 #include "nsPerformance.h"
 #include "nsINetworkInterceptController.h"
 #include "mozIThirdPartyUtil.h"
 #include "nsStreamUtils.h"
 #include "nsContentSecurityManager.h"
 #include "nsIChannelEventSink.h"
 #include "nsILoadGroupChild.h"
 #include "mozilla/ConsoleReportCollector.h"
@@ -248,17 +249,17 @@ HttpBaseChannel::GetLoadGroup(nsILoadGro
   NS_IF_ADDREF(*aLoadGroup);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetLoadGroup(nsILoadGroup *aLoadGroup)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
-  
+
   if (!CanSetLoadGroup(aLoadGroup)) {
     return NS_ERROR_FAILURE;
   }
 
   mLoadGroup = aLoadGroup;
   mProgressSink = nullptr;
   mPrivateBrowsing = NS_UsePrivateBrowsing(this);
   return NS_OK;
@@ -290,16 +291,57 @@ HttpBaseChannel::SetLoadFlags(nsLoadFlag
     aLoadFlags &= ~LOAD_ANONYMOUS;
   }
 
   mLoadFlags = aLoadFlags;
   mForceMainDocumentChannel = (aLoadFlags & LOAD_DOCUMENT_URI);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpBaseChannel::SetDocshellUserAgentOverride()
+{
+  // This sets the docshell specific user agent override, it will be overwritten
+  // by UserAgentOverrides.jsm if site-specific user agent overrides are set.
+  nsresult rv;
+  nsCOMPtr<nsILoadContext> loadContext;
+  NS_QueryNotificationCallbacks(this, loadContext);
+  if (!loadContext) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsIDOMWindow> domWindow;
+  loadContext->GetAssociatedWindow(getter_AddRefs(domWindow));
+  if (!domWindow) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsPIDOMWindow> pDomWindow = do_QueryInterface(domWindow);
+  if (!pDomWindow) {
+    return NS_OK;
+  }
+
+  nsIDocShell* docshell = pDomWindow->GetDocShell();
+  if (!docshell) {
+    return NS_OK;
+  }
+
+  nsString customUserAgent;
+  docshell->GetCustomUserAgent(customUserAgent);
+  if (customUserAgent.IsEmpty()) {
+    return NS_OK;
+  }
+
+  NS_ConvertUTF16toUTF8 utf8CustomUserAgent(customUserAgent);
+  rv = SetRequestHeader(NS_LITERAL_CSTRING("User-Agent"), utf8CustomUserAgent, false);
+  if (NS_FAILED(rv)) return rv;
+
+  return NS_OK;
+}
+
 //-----------------------------------------------------------------------------
 // HttpBaseChannel::nsIChannel
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpBaseChannel::GetOriginalURI(nsIURI **aOriginalURI)
 {
   NS_ENSURE_ARG_POINTER(aOriginalURI);
@@ -364,17 +406,17 @@ HttpBaseChannel::GetNotificationCallback
   NS_IF_ADDREF(*aCallbacks);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 HttpBaseChannel::SetNotificationCallbacks(nsIInterfaceRequestor *aCallbacks)
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
-  
+
   if (!CanSetCallbacks(aCallbacks)) {
     return NS_ERROR_FAILURE;
   }
 
   mCallbacks = aCallbacks;
   mProgressSink = nullptr;
 
   mPrivateBrowsing = NS_UsePrivateBrowsing(this);
@@ -1374,17 +1416,17 @@ HttpBaseChannel::SetReferrerWithPolicy(n
     nsAutoCString currentDomain = currentHost;
     nsAutoCString referrerDomain = referrerHost;
     uint32_t extraDomains = 0;
     nsCOMPtr<nsIEffectiveTLDService> eTLDService = do_GetService(
       NS_EFFECTIVETLDSERVICE_CONTRACTID);
     if (eTLDService) {
       rv = eTLDService->GetBaseDomain(mURI, extraDomains, currentDomain);
       if (NS_FAILED(rv)) return rv;
-      rv = eTLDService->GetBaseDomain(clone, extraDomains, referrerDomain); 
+      rv = eTLDService->GetBaseDomain(clone, extraDomains, referrerDomain);
       if (NS_FAILED(rv)) return rv;
     }
 
     // check policy for sending only when effective top level domain matches.
     // this falls back on using host if eTLDService does not work
     if (!currentDomain.Equals(referrerDomain))
       return NS_OK;
   }
@@ -2441,17 +2483,17 @@ HttpBaseChannel::SetNewListener(nsIStrea
 //-----------------------------------------------------------------------------
 // HttpBaseChannel helpers
 //-----------------------------------------------------------------------------
 
 void
 HttpBaseChannel::ReleaseListeners()
 {
   MOZ_ASSERT(NS_IsMainThread(), "Should only be called on the main thread.");
-  
+
   mListener = nullptr;
   mListenerContext = nullptr;
   mCallbacks = nullptr;
   mProgressSink = nullptr;
   mCompressListener = nullptr;
 }
 
 void
@@ -3108,9 +3150,8 @@ HttpBaseChannel::SetCorsPreflightParamet
   MOZ_RELEASE_ASSERT(!mRequestObserversCalled);
 
   mRequireCORSPreflight = true;
   mUnsafeHeaders = aUnsafeHeaders;
 }
 
 } // namespace net
 } // namespace mozilla
-
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -95,16 +95,17 @@ public:
   // nsIRequest
   NS_IMETHOD GetName(nsACString& aName) override;
   NS_IMETHOD IsPending(bool *aIsPending) override;
   NS_IMETHOD GetStatus(nsresult *aStatus) override;
   NS_IMETHOD GetLoadGroup(nsILoadGroup **aLoadGroup) override;
   NS_IMETHOD SetLoadGroup(nsILoadGroup *aLoadGroup) override;
   NS_IMETHOD GetLoadFlags(nsLoadFlags *aLoadFlags) override;
   NS_IMETHOD SetLoadFlags(nsLoadFlags aLoadFlags) override;
+  NS_IMETHOD SetDocshellUserAgentOverride();
 
   // nsIChannel
   NS_IMETHOD GetOriginalURI(nsIURI **aOriginalURI) override;
   NS_IMETHOD SetOriginalURI(nsIURI *aOriginalURI) override;
   NS_IMETHOD GetURI(nsIURI **aURI) override;
   NS_IMETHOD GetOwner(nsISupports **aOwner) override;
   NS_IMETHOD SetOwner(nsISupports *aOwner) override;
   NS_IMETHOD GetLoadInfo(nsILoadInfo **aLoadInfo) override;
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1738,16 +1738,19 @@ HttpChannelChild::AsyncOpen(nsIStreamLis
   if (mCanceled) {
     // We may have been canceled already, either by on-modify-request
     // listeners or by load group observers; in that case, don't create IPDL
     // connection. See nsHttpChannel::AsyncOpen().
     AsyncAbort(mStatus);
     return NS_OK;
   }
 
+  // Set user agent override
+  HttpBaseChannel::SetDocshellUserAgentOverride();
+
   if (ShouldIntercept()) {
     mResponseCouldBeSynthesized = true;
 
     nsCOMPtr<nsINetworkInterceptController> controller;
     GetCallback(controller);
 
     mInterceptListener = new InterceptStreamListener(this, mListenerContext);
 
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -5033,16 +5033,19 @@ nsHttpChannel::AsyncOpen(nsIStreamListen
 
     AddCookiesToRequest();
 
     // notify "http-on-opening-request" observers, but not if this is a redirect
     if (!(mLoadFlags & LOAD_REPLACE)) {
         gHttpHandler->OnOpeningRequest(this);
     }
 
+    // Set user agent override
+    HttpBaseChannel::SetDocshellUserAgentOverride();
+
     mIsPending = true;
     mWasOpened = true;
 
     mListener = listener;
     mListenerContext = context;
 
     // add ourselves to the load group.  from this point forward, we'll report
     // all failures asynchronously.
--- a/python/mozboot/mozboot/fedora.py
+++ b/python/mozboot/mozboot/fedora.py
@@ -1,25 +1,26 @@
 # 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/.
 
+import os
+
 from mozboot.base import BaseBootstrapper
 
 
 class FedoraBootstrapper(BaseBootstrapper):
     def __init__(self, version, dist_id, **kwargs):
         BaseBootstrapper.__init__(self, **kwargs)
 
         self.version = version
         self.dist_id = dist_id
 
         self.group_packages = [
-            'Development Tools',
-            'Development Libraries',
+            'C Development Tools and Libraries',
         ]
 
         self.packages = [
             'autoconf213',
             'mercurial',
         ]
 
         self.browser_group_packages = [
@@ -38,18 +39,47 @@ class FedoraBootstrapper(BaseBootstrappe
             'libstdc++-static',
             'libXt-devel',
             'mesa-libGL-devel',
             'pulseaudio-libs-devel',
             'wireless-tools-devel',
             'yasm',
         ]
 
+        self.mobile_android_packages = [
+            'ant',
+            'ncurses-devel.i686',
+            'libstdc++.i686',
+            'zlib-devel.i686',
+        ]
+
     def install_system_packages(self):
         self.dnf_groupinstall(*self.group_packages)
         self.dnf_install(*self.packages)
 
     def install_browser_packages(self):
         self.dnf_groupinstall(*self.browser_group_packages)
         self.dnf_install(*self.browser_packages)
 
+    def install_mobile_android_packages(self):
+        import android
+
+        # Install Android specific packages.
+        self.dnf_install(*self.mobile_android_packages)
+
+        # Fetch Android SDK and NDK.
+        mozbuild_path = os.environ.get('MOZBUILD_STATE_PATH', os.path.expanduser(os.path.join('~', '.mozbuild')))
+        self.sdk_path = os.environ.get('ANDROID_SDK_HOME', os.path.join(mozbuild_path, 'android-sdk-linux'))
+        self.ndk_path = os.environ.get('ANDROID_NDK_HOME', os.path.join(mozbuild_path, 'android-ndk-r10e'))
+        self.sdk_url = 'https://dl.google.com/android/android-sdk_r24.0.1-linux.tgz'
+        self.ndk_url = android.android_ndk_url('linux')
+
+        android.ensure_android_sdk_and_ndk(path=mozbuild_path,
+                                           sdk_path=self.sdk_path, sdk_url=self.sdk_url,
+                                           ndk_path=self.ndk_path, ndk_url=self.ndk_url)
+
+    def suggest_mobile_android_mozconfig(self):
+        import android
+        android.suggest_mozconfig(sdk_path=self.sdk_path,
+                                  ndk_path=self.ndk_path)
+
     def upgrade_mercurial(self, current):
         self.dnf_update('mercurial')
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -2299,21 +2299,22 @@ var AddonManagerInternal = {
       if (this.installListeners[pos] == aListener)
         this.installListeners.splice(pos, 1);
       else
         pos++;
     }
   },
 
   /**
-   * Starts installation of a temporary add-on from a local directory.
-   * @param  aDirectory
-   *         The directory of the add-on to be temporarily installed
-   * @return a Promise that rejects if the add-on is not restartless
-   *         or an add-on with the same ID is already temporarily installed
+   * Installs a temporary add-on from a local file or directory.
+   * @param  aFile
+   *         An nsIFile for the file or directory of the add-on to be
+   *         temporarily installed.
+   * @return a Promise that rejects if the add-on is not a valid restartless
+   *         add-on or if the same ID is already temporarily installed.
    */
   installTemporaryAddon: function(aFile) {
     if (!gStarted)
       throw Components.Exception("AddonManager is not initialized",
                                  Cr.NS_ERROR_NOT_INITIALIZED);
 
     if (!(aFile instanceof Ci.nsIFile))
       throw Components.Exception("aFile must be a nsIFile",
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -3820,28 +3820,28 @@ this.XPIProvider = {
       if (aInstall)
         aCallback(aInstall.wrapper);
       else
         aCallback(null);
     }, aFile);
   },
 
   /**
-   * Temporarily installs add-on from local directory.
+   * Temporarily installs add-on from a local XPI file or directory.
    * As this is intended for development, the signature is not checked and
    * the add-on does not persist on application restart.
    *
-   * @param aDirectory
-   *        The directory containing the unpacked add-on directory or XPI file
+   * @param aFile
+   *        An nsIFile for the unpacked add-on directory or XPI file.
    *
-   * @return a Promise that rejects if the add-on is not restartless
-   *         or an add-on with the same ID is already temporarily installed
+   * @return a Promise that rejects if the add-on is not a valid restartless
+   *         add-on or if the same ID is already temporarily installed
    */
-  installTemporaryAddon: Task.async(function*(aDirectory) {
-    let addon = yield loadManifestFromFile(aDirectory, TemporaryInstallLocation);
+  installTemporaryAddon: Task.async(function*(aFile) {
+    let addon = yield loadManifestFromFile(aFile, TemporaryInstallLocation);
 
     if (!addon.bootstrap) {
       throw new Error("Only restartless (bootstrap) add-ons"
                     + " can be temporarily installed:", addon.id);
     }
     let oldAddon = yield new Promise(
                    resolve => XPIDatabase.getVisibleAddonForID(addon.id, resolve));
     if (oldAddon) {
@@ -3882,17 +3882,17 @@ this.XPIProvider = {
       }
     }
 
     let file = addon._sourceBundle;
 
     XPIProvider.callBootstrapMethod(addon, file, "install",
                                     BOOTSTRAP_REASONS.ADDON_INSTALL);
     addon.state = AddonManager.STATE_INSTALLED;
-    logger.debug("Install of temporary addon in " + aDirectory.path + " completed.");
+    logger.debug("Install of temporary addon in " + aFile.path + " completed.");
     addon.visible = true;
     addon.enabled = true;
     addon.active = true;
 
     addon = XPIDatabase.addAddonMetadata(addon, file.persistentDescriptor);
 
     XPIStates.addAddon(addon);
     XPIDatabase.saveChanges();
@@ -7972,18 +7972,17 @@ Object.assign(SystemAddonInstallLocation
     }
 
     this._saveAddonSet(state);
     this._nextDir = newDir;
   }),
 });
 
 /**
- * An object which identifies a directory install location for temporary
- * add-ons.
+ * An object which identifies an install location for temporary add-ons.
  */
 const TemporaryInstallLocation = {
   locked: false,
   name: KEY_APP_TEMPORARY,
   scope: AddonManager.SCOPE_TEMPORARY,
   getAddonLocations: () => [],
   isLinkedAddon: () => false,
   installAddon: () => {},
--- a/widget/android/nsWindow.cpp
+++ b/widget/android/nsWindow.cpp
@@ -2358,17 +2358,16 @@ nsWindow::Natives::OnImeReplaceText(int3
         window.RemoveIMEComposition();
 
         {
             WidgetSelectionEvent event(true, eSetSelection, &window);
             window.InitEvent(event, nullptr);
             event.mOffset = uint32_t(aStart);
             event.mLength = uint32_t(aEnd - aStart);
             event.mExpandToClusterBoundary = false;
-            event.mReason = nsISelectionListener::IME_REASON;
             window.DispatchEvent(&event);
         }
 
         if (!mIMEKeyEvents.IsEmpty()) {
             nsEventStatus status;
             for (uint32_t i = 0; i < mIMEKeyEvents.Length(); i++) {
                 const auto event = static_cast<WidgetGUIEvent*>(
                         mIMEKeyEvents[i].get());
@@ -2487,17 +2486,16 @@ nsWindow::Natives::OnImeUpdateCompositio
 
         WidgetSelectionEvent selEvent(true, eSetSelection, &window);
         window.InitEvent(selEvent, nullptr);
 
         selEvent.mOffset = std::min(aStart, aEnd);
         selEvent.mLength = std::max(aStart, aEnd) - selEvent.mOffset;
         selEvent.mReversed = aStart > aEnd;
         selEvent.mExpandToClusterBoundary = false;
-        selEvent.mReason = nsISelectionListener::IME_REASON;
 
         window.DispatchEvent(&selEvent);
         return;
     }
 
     /*
         Update the composition from aStart to aEnd using
           information from added ranges. This is only used for