merge mozilla-central to mozilla-inbound
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Tue, 12 May 2015 17:07:10 +0200
changeset 243546 ed36c68799441edb22044213af40e406d8d1f131
parent 243545 c6a3db6682e069ea30dc496e2ef39be03768c73f (current diff)
parent 243484 bedce1b405a38273d190853039000b78cc183bb6 (diff)
child 243547 060f9a5bdcaf2e7fc9cf941bf183c58bb7b7f6ac
push id28741
push userkwierso@gmail.com
push dateTue, 12 May 2015 23:24:40 +0000
treeherdermozilla-central@d476776d920d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone41.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
browser/base/content/sync/progress.js
browser/base/content/sync/progress.xhtml
browser/base/content/test/general/browser.ini
browser/base/content/test/general/browser_aboutSyncProgress.js
browser/locales/en-US/chrome/browser/syncProgress.dtd
browser/themes/linux/syncProgress.css
browser/themes/osx/syncProgress.css
browser/themes/windows/syncProgress.css
dom/base/MultipartBlobImpl.cpp
dom/base/MultipartBlobImpl.h
dom/ipc/ContentChild.cpp
toolkit/components/viewsource/test/browser/browser_viewsourceprefs_nonhtml.js
--- a/.hgtags
+++ b/.hgtags
@@ -112,8 +112,9 @@ 0000000000000000000000000000000000000000
 6047f510fb73c7dbe9866066fb01ddda3c170c9c FIREFOX_AURORA_37_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_36_BASE
 b297a6727acfd21e757ddd38cd61894812666265 FIREFOX_AURORA_36_BASE
 0000000000000000000000000000000000000000 FIREFOX_AURORA_37_BASE
 2c951493eef5b50b8085ef78ffe0d7902ff3d593 FIREFOX_AURORA_37_BASE
 98086da94ccdc88f6de86774ce3d1fa258dc7c44 FIREFOX_AURORA_38_BASE
 1b6bf6612c0f4d4fee81d18bf18016e692f874e1 FIREFOX_AURORA_39_BASE
+66a95a483d2c77dfc387019336d18093acd6aac2 FIREFOX_AURORA_40_BASE
--- a/CLOBBER
+++ b/CLOBBER
@@ -17,9 +17,9 @@
 #
 # Modifying this file will now automatically clobber the buildbot machines \o/
 #
 
 # Are you updating CLOBBER because you think it's needed for your WebIDL
 # changes to stick? As of bug 928195, this shouldn't be necessary! Please
 # don't change CLOBBER for WebIDL changes any more.
 
-Bug 1155494 seems to need a clobber to pick up a change the ipdl parser.
+Merge day clobber
\ No newline at end of file
--- a/b2g/config/dolphin/sources.xml
+++ b/b2g/config/dolphin/sources.xml
@@ -7,20 +7,20 @@
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <!--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="2eda36a4795012a5d1275b77ebb20ac377c8cd26">
+  <project name="platform_build" path="build" remote="b2g" revision="a8ace1361d702eef293e48f2ea525dac686daa86">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
@@ -107,17 +107,17 @@
   <project name="platform/frameworks/wilhelm" path="frameworks/wilhelm" revision="aac6c4bb59a6577c97cbda68699829b507b7490d"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="fbeca55f4695dd07c0291213403533b8fbca4885"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="92605aa35361ae4ae1e473781e40c1f6929f4ec4"/>
   <project name="platform/libcore" path="libcore" revision="e195beab082c09217318fc19250caeaf4c1bd800"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="feeb36c2bd4adfe285f98f5de92e0f3771b2c115"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="23404f05422c6bf3c39573325a4f4909167671b4"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
-  <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
+  <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="cfcef469537869947abb9aa1d656774cc2678d4c"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
   <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
   <project name="platform/system/extras" path="system/extras" revision="10e78a05252b3de785f88c2d0b9ea8a428009c50"/>
   <project name="platform/system/media" path="system/media" revision="7ff72c2ea2496fa50b5e8a915e56e901c3ccd240"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/netd" path="system/netd" revision="3ae56364946d4a5bf5a5f83f12f9a45a30398e33"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8c2d32bccc7061e9ca0165135457c3fd53e7107e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -12,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="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -7,20 +7,20 @@
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <!--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="2eda36a4795012a5d1275b77ebb20ac377c8cd26">
+  <project name="platform_build" path="build" remote="b2g" revision="a8ace1361d702eef293e48f2ea525dac686daa86">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
@@ -107,32 +107,34 @@
   <project name="platform/frameworks/wilhelm" path="frameworks/wilhelm" revision="eb6077f10ae6255a72337124188f0e08dcd10e3e"/>
   <project name="platform/hardware/libhardware" path="hardware/libhardware" revision="3b80c725cea54132df05d208930d91e00d19f999"/>
   <project name="platform/hardware/libhardware_legacy" path="hardware/libhardware_legacy" revision="01f436c51dc68aec7cc1c85fda6e6792b2a95066"/>
   <project name="platform/libcore" path="libcore" revision="9877ade9617bb0db6e59aa2a54719a9bc92600f3"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="46c96ace65eb1ccab05bf15b9bf8e53e443039af"/>
   <project name="platform/ndk" path="ndk" revision="cb5519af32ae7b4a9c334913a612462ecd04c5d0"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="23404f05422c6bf3c39573325a4f4909167671b4"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="6aa61f8557a22039a30b42b7f283996381fd625d"/>
-  <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
+  <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="b562b01c93de9578d5db537b6a602a38e1aaa0ce"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="387f03e815f57d536dd922706db1622bddba8d81"/>
   <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
   <project name="platform/system/extras" path="system/extras" revision="5356165f67f4a81c2ef28671c13697f1657590df"/>
   <project name="platform/system/media" path="system/media" revision="be0e2fe59a8043fa5200f75697df9220a99abe9d"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/netd" path="system/netd" revision="36704b0da24debcab8090156568ac236315036bb"/>
   <project name="platform/system/security" path="system/security" revision="583374f69f531ba68fc3dcbff1f74893d2a96406"/>
   <project name="platform/system/vold" path="system/vold" revision="d4455b8cf361f8353e8aebac15ffd64b4aedd2b9"/>
   <project name="platform/external/icu4c" path="external/icu4c" remote="aosp" revision="b4c6379528887dc25ca9991a535a8d92a61ad6b6"/>
   <project name="platform_frameworks_av" path="frameworks/av" remote="b2g" revision="f3cedd7fd9b1649aa5107d466be9078bb7602af6"/>
   <project name="platform_system_core" path="system/core" remote="b2g" revision="9395eb5aa885cf6d305a202de6e9694a58a89717"/>
   <default remote="caf" revision="refs/tags/android-4.4.2_r1" sync-j="4"/>
   <!-- Emulator specific things -->
   <project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="72ffdf71c68a96309212eb13d63560d66db14c9e"/>
-  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="0c967fc587b614ffe391eb24e0b7f9f3bc31147b"/>
+  <project name="device_generic_goldfish" path="device/generic/goldfish" remote="b2g" revision="d4377e11659d54b41c51526547e971ba0cb11754"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="fbd2becab3825c49e756db5149552f85049c66e2"/>
+  <project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="f37bd545063039e30a92f2550ae78c0e6e4e2d08"/>
   <project name="platform_external_wpa_supplicant_8" path="external/wpa_supplicant_8" remote="b2g" revision="9a725b0f5aa715282f8854ac56e5b63efc9cd077"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="0c2ffe181ca64a6cad9c5f93ddea623f080c6434"/>
+  <project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="d82e00fb6380b4f6cea7a96213913ee9eb441239"/>
   <project name="platform/development" path="development" revision="5968ff4e13e0d696ad8d972281fc27ae5a12829b"/>
   <project name="android-sdk" path="sdk" remote="b2g" revision="0951179277915335251c5e11d242e4e1a8c2236f"/>
   <project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
 </manifest>
--- 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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -14,17 +14,17 @@
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <default remote="caf" revision="refs/tags/android-4.0.4_r2.1" sync-j="4"/>
   <!-- Gonk specific things and forks -->
   <project name="platform_build" path="build" remote="b2g" revision="173b3104bfcbd23fc9dccd4b0035fc49aae3d444">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
-  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia.git" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9a9797062c6001d6346504161c51187a2968466b"/>
   <project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="8c2d32bccc7061e9ca0165135457c3fd53e7107e"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="34ea6163f9f0e0122fb0bb03607eccdca31ced7a"/>
   <!-- Stock Android things -->
   <project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
--- a/b2g/config/flame-kk/sources.xml
+++ b/b2g/config/flame-kk/sources.xml
@@ -7,20 +7,20 @@
   <remote fetch="https://git.mozilla.org/b2g" name="mozilla"/>
   <!--original fetch url was git://github.com/apitrace/-->
   <remote fetch="https://git.mozilla.org/external/apitrace" name="apitrace"/>
   <!--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="2eda36a4795012a5d1275b77ebb20ac377c8cd26">
+  <project name="platform_build" path="build" remote="b2g" revision="a8ace1361d702eef293e48f2ea525dac686daa86">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
@@ -101,17 +101,17 @@
   <project name="platform/external/zlib" path="external/zlib" revision="6eb3570ff8fa71bd83bb375b4bf09804c6089fed"/>
   <project name="platform/frameworks/opt/emoji" path="frameworks/opt/emoji" revision="dbbe673145107e99883f62bafd70c5f43f11065c"/>
   <project name="platform/frameworks/wilhelm" path="frameworks/wilhelm" revision="f0c3b4edf597c40aae4ea311575f39c8bcf203df"/>
   <project name="platform/libcore" path="libcore" revision="baf7d8068dd501cfa338d3a8b1b87216d6ce0571"/>
   <project name="platform/libnativehelper" path="libnativehelper" revision="50c4430e32849530ced32680fd6ee98963b3f7ac"/>
   <project name="platform/ndk" path="ndk" revision="e58ef003be4306bb53a8c11331146f39e4eab31f"/>
   <project name="platform_prebuilts_misc" path="prebuilts/misc" remote="b2g" revision="23404f05422c6bf3c39573325a4f4909167671b4"/>
   <project name="platform/prebuilts/ndk" path="prebuilts/ndk" revision="c792f0bd9fff7aea2887c60bbb3a9bbdb534ffa3"/>
-  <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="f7d9bf71cf6693474f3f2a81a4ba62c0fc5646aa"/>
+  <project name="platform_prebuilts_qemu-kernel" path="prebuilts/qemu-kernel" remote="b2g" revision="6a1bb59af65b6485b1090522f66fac95c3f9e22c"/>
   <project name="platform/prebuilts/sdk" path="prebuilts/sdk" revision="69d524e80cdf3981006627c65ac85f3a871238a3"/>
   <project name="platform/prebuilts/tools" path="prebuilts/tools" revision="5a48c04c4bb5f079bc757e29864a42427378e051"/>
   <project name="platform_system_bluetoothd" path="system/bluetoothd" remote="b2g" revision="48d2332e6d8400cdc0de273ceff2abe8aaababf8"/>
   <project name="platform/system/extras" path="system/extras" revision="576f57b6510de59c08568b53c0fb60588be8689e"/>
   <project name="platform_system_libfdio" path="system/libfdio" remote="b2g" revision="3c5405863d2002f665ef2b901abb3853c420129b"/>
   <project name="platform/system/netd" path="system/netd" revision="a6531f7befb49b1c81bc0de7e51c5482b308e1c5"/>
   <project name="platform/system/security" path="system/security" revision="ee8068b9e7bfb2770635062fc9c2035be2142bd8"/>
   <project name="platform/system/vold" path="system/vold" revision="42fa2a0f14f965970a4b629a176bbd2666edf017"/>
--- a/b2g/config/flame/sources.xml
+++ b/b2g/config/flame/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="e95b4ce22c825da44d14299e1190ea39a5260bde"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="471afab478649078ad7c75ec6b252481a59e19b8"/>
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -1,9 +1,9 @@
 {
     "git": {
-        "git_revision": "6089234ace8b294a8feef064387604bae16254e3", 
+        "git_revision": "3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad", 
         "remote": "https://git.mozilla.org/releases/gaia.git", 
         "branch": ""
     }, 
-    "revision": "4bcdce164d361f57742d042ff2742e3e6d53d7dd", 
+    "revision": "68c31354e0dec7651bb120f3429a83b224a40821", 
     "repo_path": "integration/gaia-central"
 }
--- a/b2g/config/nexus-4/sources.xml
+++ b/b2g/config/nexus-4/sources.xml
@@ -12,17 +12,17 @@
   <!--original fetch url was https://git.mozilla.org/releases-->
   <remote fetch="https://git.mozilla.org/releases" name="mozillaorg"/>
   <!-- B2G specific things. -->
   <project name="platform_build" path="build" remote="b2g" revision="4efd19d199ae52656604f794c5a77518400220fd">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <!-- Stock Android things -->
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.1" path="prebuilts/clang/linux-x86/3.1" revision="5c45f43419d5582949284eee9cef0c43d866e03b"/>
   <project groups="linux" name="platform/prebuilts/clang/linux-x86/3.2" path="prebuilts/clang/linux-x86/3.2" revision="3748b4168e7bd8d46457d4b6786003bc6a5223ce"/>
--- a/b2g/config/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="61e82f99bb8bc78d52b5717e9a2481ec7267fa33">
     <copyfile dest="Makefile" src="core/root.mk"/>
   </project>
-  <project name="gaia" path="gaia" remote="mozillaorg" revision="6089234ace8b294a8feef064387604bae16254e3"/>
+  <project name="gaia" path="gaia" remote="mozillaorg" revision="3654ec1d7ce1e0a56a34d5c3b06f6a9b33ff79ad"/>
   <project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
   <project name="gonk-misc" path="gonk-misc" remote="b2g" revision="d3868ff4bb3a4b81382795e2784258c210fe6cb8"/>
   <project name="librecovery" path="librecovery" remote="b2g" revision="1b3591a50ed352fc6ddb77462b7b35d0bfa555a3"/>
   <project name="moztt" path="external/moztt" remote="b2g" revision="adb24954bf8068f21705b570450475d183336b2d"/>
   <project name="rilproxy" path="rilproxy" remote="b2g" revision="5ef30994f4778b4052e58a4383dbe7890048c87e"/>
   <project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
   <project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
   <project name="apitrace" path="external/apitrace" remote="apitrace" revision="0627790166dccd8dd370fa7d9f434ed9fc027fb4"/>
--- a/b2g/confvars.sh
+++ b/b2g/confvars.sh
@@ -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/.
 
 MOZ_APP_BASENAME=B2G
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=40.0a1
+MOZ_APP_VERSION=41.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_UA_OS_AGNOSTIC=1
 
 MOZ_B2G_VERSION=3.0.0.0-prerelease
 MOZ_B2G_OS_NAME=Boot2Gecko
 
 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1912,9 +1912,9 @@ pref("reader.parse-node-limit", 0);
 pref("dom.serviceWorkers.enabled", true);
 #endif
 
 pref("browser.pocket.enabled", true);
 pref("browser.pocket.api", "api.getpocket.com");
 pref("browser.pocket.site", "getpocket.com");
 pref("browser.pocket.oAuthConsumerKey", "40249-e88c401e1b1f2242d9e441c4");
 pref("browser.pocket.useLocaleList", true);
-pref("browser.pocket.enabledLocales", "en-US");
+pref("browser.pocket.enabledLocales", "en-US de es-ES ja ru");
--- a/browser/base/content/browser-context.inc
+++ b/browser/base/content/browser-context.inc
@@ -372,17 +372,17 @@
                 label="&viewPartialSourceForMathMLCmd.label;"
                 accesskey="&viewPartialSourceCmd.accesskey;"
                 oncommand="gContextMenu.viewPartialSource('mathml');"
                 observes="isImage"/>
       <menuseparator id="context-sep-viewsource"/>
       <menuitem id="context-viewsource"
                 label="&viewPageSourceCmd.label;"
                 accesskey="&viewPageSourceCmd.accesskey;"
-                oncommand="BrowserViewSourceOfDocument(gContextMenu.browser.contentDocumentAsCPOW);"
+                oncommand="BrowserViewSource(gContextMenu.browser);"
                 observes="isImage"/>
       <menuitem id="context-viewinfo"
                 label="&viewPageInfoCmd.label;"
                 accesskey="&viewPageInfoCmd.accesskey;"
                 oncommand="gContextMenu.viewInfo();"/>
       <menuseparator id="spell-separator"/>
       <menuitem id="spell-check-enabled"
                 label="&spellCheckToggle.label;"
--- a/browser/base/content/browser-sets.inc
+++ b/browser/base/content/browser-sets.inc
@@ -33,17 +33,17 @@
     <command id="cmd_close" oncommand="BrowserCloseTabOrWindow()" reserved="true"/>
     <command id="cmd_closeWindow" oncommand="BrowserTryToCloseWindow()" reserved="true"/>
     <command id="cmd_CustomizeToolbars" oncommand="BrowserCustomizeToolbar()"/>
     <command id="cmd_quitApplication" oncommand="goQuitApplication()" reserved="true"/>
 
 
     <commandset id="editMenuCommands"/>
 
-    <command id="View:PageSource" oncommand="BrowserViewSourceOfDocument(window.gBrowser.selectedBrowser.contentDocumentAsCPOW);" observes="isImage"/>
+    <command id="View:PageSource" oncommand="BrowserViewSource(window.gBrowser.selectedBrowser);" observes="isImage"/>
     <command id="View:PageInfo" oncommand="BrowserPageInfo();"/>
     <command id="View:FullScreen" oncommand="BrowserFullScreen();"/>
     <command id="View:ReaderView" oncommand="ReaderParent.toggleReaderMode(event);"/>
     <command id="cmd_find"
              oncommand="gFindBar.onFindCommand();"
              observes="isImage"/>
     <command id="cmd_findAgain"
              oncommand="gFindBar.onFindAgainCommand(false);"
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -2277,72 +2277,81 @@ function readFromClipboard()
       url = data.data.substring(0, dataLen.value / 2);
     }
   } catch (ex) {
   }
 
   return url;
 }
 
-function BrowserViewSourceOfDocument(aDocument)
-{
-  var pageCookie;
-  var webNav;
-
-  // Get the document charset
-  var docCharset = "charset=" + aDocument.characterSet;
-
-  // Get the nsIWebNavigation associated with the document
-  try {
-      var win;
-      var ifRequestor;
-
-      // Get the DOMWindow for the requested document.  If the DOMWindow
-      // cannot be found, then just use the content window...
-      //
-      // XXX:  This is a bit of a hack...
-      win = aDocument.defaultView;
-      if (win == window) {
-        win = content;
-      }
-      ifRequestor = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor);
-
-      webNav = ifRequestor.getInterface(nsIWebNavigation);
-  } catch(err) {
-      // If nsIWebNavigation cannot be found, just get the one for the whole
-      // window...
-      webNav = gBrowser.webNavigation;
-  }
-  //
-  // Get the 'PageDescriptor' for the current document. This allows the
-  // view-source to access the cached copy of the content rather than
-  // refetching it from the network...
-  //
-  try {
-
-#ifdef E10S_TESTING_ONLY
-    // Workaround for bug 988133, which causes a crash if we attempt to load
-    // the document from the cache when the document is a CPOW (which occurs
-    // if we're using remote tabs). This causes us to reload the document from
-    // the network in this case, so it's not a permanent solution, hence hiding
-    // it behind the E10S_TESTING_ONLY ifdef. This is just a band-aid fix until
-    // we can find something better - see bug 1025146.
-    if (!Cu.isCrossProcessWrapper(aDocument)) {
-#endif
-      var PageLoader = webNav.QueryInterface(Components.interfaces.nsIWebPageDescriptor);
-
-      pageCookie = PageLoader.currentDescriptor;
-#ifdef E10S_TESTING_ONLY
-    }
-#endif
-  } catch(err) {
-    // If no page descriptor is available, just use the view-source URL...
-  }
-
-  top.gViewSourceUtils.viewSource(webNav.currentURI.spec, pageCookie, aDocument);
+/**
+ * Open the View Source dialog.
+ *
+ * @param aArgsOrDocument
+ *        Either an object or a Document. Passing a Document is deprecated,
+ *        and is not supported with e10s. This function will throw if
+ *        aArgsOrDocument is a CPOW.
+ *
+ *        If aArgsOrDocument is an object, that object can take the
+ *        following properties:
+ *
+ *        browser:
+ *          The browser containing the document that we would like to view the
+ *          source of.
+ *        URL:
+ *          A string URL for the page we'd like to view the source of.
+ *        outerWindowID (optional):
+ *          The outerWindowID of the content window containing the document that
+ *          we want to view the source of. You only need to provide this if you
+ *          want to attempt to retrieve the document source from the network
+ *          cache.
+ *        lineNumber (optional):
+ *          The line number to focus on once the source is loaded.
+ */
+function BrowserViewSourceOfDocument(aArgsOrDocument) {
+  let args;
+
+  if (aArgsOrDocument instanceof Document) {
+    let doc = aArgsOrDocument;
+    // Deprecated API - callers should pass args object instead.
+    if (Cu.isCrossProcessWrapper(doc)) {
+      throw new Error("BrowserViewSourceOfDocument cannot accept a CPOW " +
+                      "as a document.");
+    }
+
+    let requestor = doc.defaultView
+                       .QueryInterface(Ci.nsIInterfaceRequestor);
+    let browser = requestor.getInterface(Ci.nsIWebNavigation)
+                           .QueryInterface(Ci.nsIDocShell)
+                           .chromeEventHandler;
+    let outerWindowID = requestor.getInterface(Ci.nsIDOMWindowUtils)
+                                 .outerWindowID;
+    let URL = browser.currentURI.spec;
+    args = { browser, outerWindowID, URL };
+  } else {
+    args = aArgsOrDocument;
+  }
+
+  top.gViewSourceUtils.viewSource(args);
+}
+
+/**
+ * Opens the View Source dialog for the source loaded in the root
+ * top-level document of the browser. This is really just a
+ * convenience wrapper around BrowserViewSourceOfDocument.
+ *
+ * @param browser
+ *        The browser that we want to load the source of.
+ */
+function BrowserViewSource(browser) {
+  BrowserViewSourceOfDocument({
+    browser: browser,
+    outerWindowID: browser.outerWindowID,
+    URL: browser.currentURI.spec,
+  });
 }
 
 // doc - document to use for source, or null for this window's document
 // initialTab - name of the initial tab to display, or null for the first tab
 // imageElement - image to load in the Media Tab of the Page Info window; can be null/omitted
 function BrowserPageInfo(doc, initialTab, imageElement) {
   var args = {doc: doc, initialTab: initialTab, imageElement: imageElement};
   var windows = Services.wm.getEnumerator("Browser:page-info");
--- a/browser/base/content/content.js
+++ b/browser/base/content/content.js
@@ -26,16 +26,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 XPCOMUtils.defineLazyModuleGetter(this, "PluginContent",
   "resource:///modules/PluginContent.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
   "resource://gre/modules/PrivateBrowsingUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FormSubmitObserver",
   "resource:///modules/FormSubmitObserver.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PageMetadata",
   "resource://gre/modules/PageMetadata.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUIUtils",
+  "resource:///modules/PlacesUIUtils.jsm");
 XPCOMUtils.defineLazyGetter(this, "PageMenuChild", function() {
   let tmp = {};
   Cu.import("resource://gre/modules/PageMenu.jsm", tmp);
   return new tmp.PageMenuChild();
 });
 
 // TabChildGlobal
 var global = this;
@@ -638,8 +640,15 @@ addMessageListener("ContextMenu:ReloadFr
   message.objects.target.ownerDocument.location.reload();
 });
 
 addMessageListener("ContextMenu:ReloadImage", (message) => {
   let image = message.objects.target;
   if (image instanceof Ci.nsIImageLoadingContent)
     image.forceReload();
 });
+
+addMessageListener("ContextMenu:BookmarkFrame", (message) => {
+  let frame = message.objects.target.ownerDocument;
+  sendAsyncMessage("ContextMenu:BookmarkFrame:Result",
+                   { title: frame.title,
+                     description: PlacesUIUtils.getDescriptionFromDocument(frame) });
+});
--- a/browser/base/content/nsContextMenu.js
+++ b/browser/base/content/nsContextMenu.js
@@ -988,17 +988,21 @@ nsContextMenu.prototype = {
     var docUrl = null;
     window.openDialog("chrome://global/content/viewPartialSource.xul",
                       "_blank", "scrollbars,resizable,chrome,dialog=no",
                       docUrl, docCharset, reference, aContext);
   },
 
   // Open new "view source" window with the frame's URL.
   viewFrameSource: function() {
-    BrowserViewSourceOfDocument(this.target.ownerDocument);
+    BrowserViewSourceOfDocument({
+      browser: this.browser,
+      URL: gContextMenuContentData.docLocation,
+      outerWindowID: gContextMenuContentData.frameOuterWindowID,
+    });
   },
 
   viewInfo: function() {
     BrowserPageInfo(this.target.ownerDocument.defaultView.top.document);
   },
 
   viewImageInfo: function() {
     BrowserPageInfo(this.target.ownerDocument.defaultView.top.document,
@@ -1594,40 +1598,45 @@ nsContextMenu.prototype = {
   },
 
   bookmarkLink: function CM_bookmarkLink() {
     window.top.PlacesCommandHook.bookmarkLink(PlacesUtils.bookmarksMenuFolderId,
                                               this.linkURL, this.linkTextStr);
   },
 
   addBookmarkForFrame: function CM_addBookmarkForFrame() {
-    var doc = this.target.ownerDocument;
-    var uri = doc.documentURIObject;
+    let uri = gContextMenuContentData.documentURIObject;
+    let mm = this.browser.messageManager;
+
+    let onMessage = (message) => {
+      mm.removeMessageListener("ContextMenu:BookmarkFrame:Result", onMessage);
 
-    var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
-    if (itemId == -1) {
-      var title = doc.title;
-      var description = PlacesUIUtils.getDescriptionFromDocument(doc);
-      PlacesUIUtils.showBookmarkDialog({ action: "add"
-                                       , type: "bookmark"
-                                       , uri: uri
-                                       , title: title
-                                       , description: description
-                                       , hiddenRows: [ "description"
-                                                     , "location"
-                                                     , "loadInSidebar"
-                                                     , "keyword" ]
-                                       }, window.top);
-    }
-    else {
-      PlacesUIUtils.showBookmarkDialog({ action: "edit"
-                                       , type: "bookmark"
-                                       , itemId: itemId
-                                       }, window.top);
-    }
+      let itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
+      if (itemId == -1) {
+        PlacesUIUtils.showBookmarkDialog({ action: "add"
+                                         , type: "bookmark"
+                                         , uri: uri
+                                         , title: message.data.title
+                                         , description: message.data.description
+                                         , hiddenRows: [ "description"
+                                                       , "location"
+                                                       , "loadInSidebar"
+                                                       , "keyword" ]
+                                         }, window.top);
+      }
+      else {
+        PlacesUIUtils.showBookmarkDialog({ action: "edit"
+                                         , type: "bookmark"
+                                         , itemId: itemId
+                                         }, window.top);
+      }
+    };
+    mm.addMessageListener("ContextMenu:BookmarkFrame:Result", onMessage);
+
+    mm.sendAsyncMessage("ContextMenu:BookmarkFrame", null, { target: this.target });
   },
   markLink: function CM_markLink(origin) {
     // send link to social, if it is the page url linkURI will be null
     SocialMarks.markLink(origin, this.linkURI ? this.linkURI.spec : null, this.target);
   },
   shareLink: function CM_shareLink() {
     SocialShare.sharePage(null, { url: this.linkURI.spec }, this.target);
   },
deleted file mode 100644
--- a/browser/base/content/sync/progress.js
+++ /dev/null
@@ -1,71 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://services-sync/main.js");
-
-let gProgressBar;
-let gCounter = 0;
-
-function onLoad(event) {
-  Services.obs.addObserver(onEngineSync, "weave:engine:sync:finish", false);
-  Services.obs.addObserver(onEngineSync, "weave:engine:sync:error", false);
-  Services.obs.addObserver(onServiceSync, "weave:service:sync:finish", false);
-  Services.obs.addObserver(onServiceSync, "weave:service:sync:error", false);
-
-  gProgressBar = document.getElementById('uploadProgressBar');
-
-  if (Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
-    gProgressBar.hidden = false;
-  }
-  else {
-    gProgressBar.hidden = true;
-  }
-}
-
-function onUnload(event) {
-  cleanUpObservers();
-}
-
-function cleanUpObservers() {
-  try {
-    Services.obs.removeObserver(onEngineSync, "weave:engine:sync:finish");
-    Services.obs.removeObserver(onEngineSync, "weave:engine:sync:error");
-    Services.obs.removeObserver(onServiceSync, "weave:service:sync:finish");
-    Services.obs.removeObserver(onServiceSync, "weave:service:sync:error");
-  }
-  catch (e) {
-    // may be double called by unload & exit. Ignore.
-  }
-}
-
-function onEngineSync(subject, topic, data) {
-  // The Clients engine syncs first. At this point we don't necessarily know
-  // yet how many engines will be enabled, so we'll ignore the Clients engine
-  // and evaluate how many engines are enabled when the first "real" engine
-  // syncs.
-  if (data == "clients") {
-    return;
-  }
-
-  if (!gCounter &&
-      Services.prefs.getPrefType("services.sync.firstSync") != Ci.nsIPrefBranch.PREF_INVALID) {
-    gProgressBar.max = Weave.Service.engineManager.getEnabled().length;
-  }
-
-  gCounter += 1;
-  gProgressBar.setAttribute("value", gCounter);
-}
-
-function onServiceSync(subject, topic, data) {
-  // To address the case where 0 engines are synced, we will fill the
-  // progress bar so the user knows that the sync has finished.
-  gProgressBar.setAttribute("value", gProgressBar.max);
-  cleanUpObservers();
-}
-
-function closeTab() {
-  window.close();
-}
deleted file mode 100644
--- a/browser/base/content/sync/progress.xhtml
+++ /dev/null
@@ -1,55 +0,0 @@
-<?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/. -->
-
-<!DOCTYPE html [
-  <!ENTITY % htmlDTD
-    PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
-    "DTD/xhtml1-strict.dtd">
-  %htmlDTD;
-  <!ENTITY % syncProgressDTD
-    SYSTEM "chrome://browser/locale/syncProgress.dtd">
-    %syncProgressDTD;
-  <!ENTITY % syncSetupDTD
-    SYSTEM "chrome://browser/locale/syncSetup.dtd">
-    %syncSetupDTD;
-  <!ENTITY % globalDTD 
-    SYSTEM "chrome://global/locale/global.dtd">
-    %globalDTD;
-]>
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-  <head>
-    <title>&syncProgress.pageTitle;</title>
-
-    <link rel="stylesheet" type="text/css" media="all"
-          href="chrome://browser/skin/syncProgress.css"/>
-
-    <link rel="icon" type="image/png" id="favicon"
-          href="chrome://browser/skin/sync-16.png"/>
-
-    <script type="text/javascript;version=1.8"
-            src="chrome://browser/content/sync/progress.js"/>
-  </head>
-  <body onload="onLoad(event)" onunload="onUnload(event)" dir="&locale.dir;">
-    <title>&setup.successPage.title;</title>
-    <div id="floatingBox" class="main-content">
-      <div id="title">
-        <h1>&setup.successPage.title;</h1>
-      </div>
-      <div id="successLogo">
-        <img id="brandSyncLogo" src="chrome://browser/skin/sync-128.png" alt="&syncProgress.logoAltText;" />
-      </div>
-      <div id="loadingText">
-        <p id="blurb">&syncProgress.textBlurb; </p>
-      </div>
-      <div id="progressBar">
-        <progress id="uploadProgressBar" value="0"/>
-      </div>
-      <div id="bottomRow">
-        <button id="closeButton" onclick="closeTab()">&syncProgress.closeButton; </button>
-      </div>
-    </div>
-  </body>
-</html>
--- a/browser/base/content/sync/setup.js
+++ b/browser/base/content/sync/setup.js
@@ -552,18 +552,16 @@ var gSyncSetup = {
         Weave.Svc.Prefs.set(prefs[i], isChecked(prefs[i]));
       }
       this._handleNoScript(false);
       if (Weave.Svc.Prefs.get("firstSync", "") == "notReady")
         Weave.Svc.Prefs.reset("firstSync");
 
       Weave.Service.persistLogin();
       Weave.Svc.Obs.notify("weave:service:setup-complete");
-
-      gSyncUtils.openFirstSyncProgressPage();
     }
     Weave.Utils.nextTick(Weave.Service.sync, Weave.Service);
     window.close();
   },
 
   onWizardCancel: function () {
     if (this._resettingSync)
       return;
--- a/browser/base/content/sync/utils.js
+++ b/browser/base/content/sync/utils.js
@@ -82,20 +82,16 @@ let gSyncUtils = {
     this._openLink(Weave.Svc.Prefs.get(root + "termsURL"));
   },
 
   openPrivacyPolicy: function () {
     let root = this.fxAccountsEnabled ? "fxa." : "";
     this._openLink(Weave.Svc.Prefs.get(root + "privacyURL"));
   },
 
-  openFirstSyncProgressPage: function () {
-    this._openLink("about:sync-progress");
-  },
-
   /**
    * Prepare an invisible iframe with the passphrase backup document.
    * Used by both the print and saving methods.
    *
    * @param elid : ID of the form element containing the passphrase.
    * @param callback : Function called once the iframe has loaded.
    */
   _preparePPiframe: function(elid, callback) {
@@ -125,17 +121,17 @@ let gSyncUtils = {
       el.firstChild.nodeValue = privacyURL;
 
       callback(iframe);
     }, false);
   },
 
   /**
    * Print passphrase backup document.
-   * 
+   *
    * @param elid : ID of the form element containing the passphrase.
    */
   passphrasePrint: function(elid) {
     this._preparePPiframe(elid, function(iframe) {
       let webBrowserPrint = iframe.contentWindow
                                   .QueryInterface(Ci.nsIInterfaceRequestor)
                                   .getInterface(Ci.nsIWebBrowserPrint);
       let printSettings = PrintUtils.getPrintSettings();
@@ -153,17 +149,17 @@ let gSyncUtils = {
       } catch (ex) {
         // print()'s return codes are expressed as exceptions. Ignore.
       }
     });
   },
 
   /**
    * Save passphrase backup document to disk as HTML file.
-   * 
+   *
    * @param elid : ID of the form element containing the passphrase.
    */
   passphraseSave: function(elid) {
     let dialogTitle = this.bundle.GetStringFromName("save.recoverykey.title");
     let defaultSaveName = this.bundle.GetStringFromName("save.recoverykey.defaultfilename");
     this._preparePPiframe(elid, function(iframe) {
       let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
       let fpCallback = function fpCallback_done(aResult) {
@@ -191,17 +187,17 @@ let gSyncUtils = {
     });
   },
 
   /**
    * validatePassword
    *
    * @param el1 : the first textbox element in the form
    * @param el2 : the second textbox element, if omitted it's an update form
-   * 
+   *
    * returns [valid, errorString]
    */
   validatePassword: function (el1, el2) {
     let valid = false;
     let val1 = el1.value;
     let val2 = el2 ? el2.value : "";
     let error = "";
 
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -116,17 +116,16 @@ skip-if = (os == "linux" || os == "mac")
 skip-if = os == "linux" # Bug 958026
 support-files =
   content_aboutAccounts.js
 [browser_aboutSupport_newtab_security_state.js]
 [browser_aboutHealthReport.js]
 skip-if = os == "linux" # Bug 924307
 [browser_aboutHome.js]
 skip-if = e10s # Bug 1093153 - no about:home support yet
-[browser_aboutSyncProgress.js]
 [browser_action_keyword.js]
 [browser_action_keyword_override.js]
 [browser_action_searchengine.js]
 [browser_action_searchengine_alias.js]
 [browser_addKeywordSearch.js]
 [browser_search_favicon.js]
 [browser_alltabslistener.js]
 [browser_autocomplete_a11y_label.js]
deleted file mode 100644
--- a/browser/base/content/test/general/browser_aboutSyncProgress.js
+++ /dev/null
@@ -1,102 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-Cu.import("resource://services-sync/main.js");
-
-let gTests = [ {
-  desc: "Makes sure the progress bar appears if firstSync pref is set",
-  setup: function () {
-    Services.prefs.setCharPref("services.sync.firstSync", "newAccount");
-  },
-  run: function () {
-    let doc = gBrowser.selectedBrowser.contentDocument;
-    let progressBar = doc.getElementById("uploadProgressBar");
-
-    let win = doc.defaultView;
-    isnot(win.getComputedStyle(progressBar).display, "none", "progress bar should be visible");
-    executeSoon(runNextTest);
-  }
-},
-
-{
-  desc: "Makes sure the progress bar is hidden if firstSync pref is not set",
-  setup: function () {
-    Services.prefs.clearUserPref("services.sync.firstSync");
-    is(Services.prefs.getPrefType("services.sync.firstSync"),
-       Ci.nsIPrefBranch.PREF_INVALID, "pref DNE" );
-  },
-  run: function () {
-    let doc = gBrowser.selectedBrowser.contentDocument;
-    let progressBar = doc.getElementById("uploadProgressBar");
-
-    let win = doc.defaultView;
-    is(win.getComputedStyle(progressBar).display, "none",
-       "progress bar should not be visible");
-    executeSoon(runNextTest);
-  }
-},
-{
-  desc: "Makes sure the observer updates are reflected in the progress bar",
-  setup: function () {
-  },
-  run: function () {
-     let doc = gBrowser.selectedBrowser.contentDocument;
-     let progressBar = doc.getElementById("uploadProgressBar");
-
-     Services.obs.notifyObservers(null, "weave:engine:sync:finish", null);
-     Services.obs.notifyObservers(null, "weave:engine:sync:error", null);
-
-     let received = progressBar.getAttribute("value");
-
-     is(received, 2, "progress bar received correct notifications");
-     executeSoon(runNextTest);
-  }
-},
-{
-  desc: "Close button should close tab",
-  setup: function (){
-  },
-  run: function () {
-    function onTabClosed() {
-      ok(true, "received TabClose notification");
-      gBrowser.tabContainer.removeEventListener("TabClose", onTabClosed, false);
-      executeSoon(runNextTest);
-    }
-    let doc = gBrowser.selectedBrowser.contentDocument;
-    let button = doc.getElementById('closeButton');
-    let window = doc.defaultView;
-    gBrowser.tabContainer.addEventListener("TabClose", onTabClosed, false);
-    EventUtils.sendMouseEvent({type: "click"}, button, window);
-  }
-},
-];
-
-function test () {
-  waitForExplicitFinish();
-  executeSoon(runNextTest);
-}
-
-function runNextTest()
-{
-  while (gBrowser.tabs.length > 1) {
-    gBrowser.removeCurrentTab();
-  }
-
-  if (gTests.length) {
-    let test = gTests.shift();
-    info(test.desc);
-    test.setup();
-    let tab = gBrowser.selectedTab = gBrowser.addTab("about:sync-progress");
-    tab.linkedBrowser.addEventListener("load", function (event) {
-      tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
-      // Some part of the page is populated on load, so enqueue on it.
-      executeSoon(test.run);
-    }, true);
-  }
-  else {
-    finish();
-  }
-}
-
--- a/browser/base/content/test/general/browser_parsable_css.js
+++ b/browser/base/content/test/general/browser_parsable_css.js
@@ -19,18 +19,16 @@ const kWhitelist = [
    errorMessage: /Unknown pseudo-class.*(fullscreen|selection)/i},
   // Tracked in bug 1004428.
   {sourceName: /aboutaccounts\/(main|normalize)\.css/i},
   // TokBox SDK assets, see bug 1032469.
   {sourceName: /loop\/.*sdk-content\/.*\.css$/i},
   // Highlighter CSS uses chrome-only pseudo-class, see bug 985597.
   {sourceName: /highlighter\.css/i,
    errorMessage: /Unknown pseudo-class.*moz-native-anonymous/i},
-  // Tracked in bug 1160629.
-  {sourceName: /pocket\/panels\/css\/.*\.css/i},
 ];
 
 let moduleLocation = gTestPath.replace(/\/[^\/]*$/i, "/parsingTestHelpers.jsm");
 let {generateURIsFromDirTree} = Cu.import(moduleLocation, {});
 
 /**
  * Check if an error should be ignored due to matching one of the whitelist
  * objects defined in kWhitelist
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -1144,17 +1144,21 @@ file, You can obtain one at http://mozil
           this.setAttribute("showonlysettings", "true");
 
           // Setting this with an xbl-inherited attribute gets overridden the
           // second time the user clicks the glass icon for some reason...
           tree.collapsed = true;
         }
         else {
           this.removeAttribute("showonlysettings");
-          tree.collapsed = false;
+          // Uncollapse as long as we have a tree with a view which has >= 1 row.
+          // The autocomplete binding itself will take care of uncollapsing later,
+          // if we currently have no rows but end up having some in the future
+          // when the search string changes
+          tree.collapsed = !tree.view || !tree.view.rowCount;
         }
 
         // Show the current default engine in the top header of the panel.
         this.updateHeader();
 
         // Update the 'Search for <keywords> with:" header.
         let headerSearchText =
           document.getAnonymousElementByAttribute(this, "anonid",
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -129,18 +129,16 @@ browser.jar:
 *       content/browser/sync/setup.js                 (content/sync/setup.js)
         content/browser/sync/genericChange.xul        (content/sync/genericChange.xul)
         content/browser/sync/genericChange.js         (content/sync/genericChange.js)
         content/browser/sync/key.xhtml                (content/sync/key.xhtml)
         content/browser/sync/notification.xml         (content/sync/notification.xml)
         content/browser/sync/quota.xul                (content/sync/quota.xul)
         content/browser/sync/quota.js                 (content/sync/quota.js)
         content/browser/sync/utils.js                 (content/sync/utils.js)
-        content/browser/sync/progress.js              (content/sync/progress.js)
-        content/browser/sync/progress.xhtml           (content/sync/progress.xhtml)
 *       content/browser/sync/customize.xul            (content/sync/customize.xul)
         content/browser/sync/customize.js             (content/sync/customize.js)
         content/browser/sync/customize.css            (content/sync/customize.css)
 #endif
         content/browser/safeMode.css                  (content/safeMode.css)
         content/browser/safeMode.js                   (content/safeMode.js)
         content/browser/safeMode.xul                  (content/safeMode.xul)
 *       content/browser/sanitize.js                   (content/sanitize.js)
--- a/browser/components/about/AboutRedirector.cpp
+++ b/browser/components/about/AboutRedirector.cpp
@@ -74,18 +74,16 @@ static RedirEntry kRedirMap[] = {
   { "robots", "chrome://browser/content/aboutRobots.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::ALLOW_SCRIPT },
   { "sessionrestore", "chrome://browser/content/aboutSessionRestore.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
   { "welcomeback", "chrome://browser/content/aboutWelcomeBack.xhtml",
     nsIAboutModule::ALLOW_SCRIPT },
 #ifdef MOZ_SERVICES_SYNC
-  { "sync-progress", "chrome://browser/content/sync/progress.xhtml",
-    nsIAboutModule::ALLOW_SCRIPT },
   { "sync-tabs", "chrome://browser/content/sync/aboutSyncTabs.xul",
     nsIAboutModule::ALLOW_SCRIPT },
 #endif
   { "home", "chrome://browser/content/abouthome/aboutHome.xhtml",
     nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
     nsIAboutModule::URI_MUST_LOAD_IN_CHILD |
     nsIAboutModule::ALLOW_SCRIPT |
     nsIAboutModule::ENABLE_INDEXED_DB },
--- a/browser/components/build/nsModule.cpp
+++ b/browser/components/build/nsModule.cpp
@@ -95,17 +95,16 @@ static const mozilla::Module::ContractID
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "feeds", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "privatebrowsing", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "rights", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "robots", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sessionrestore", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "welcomeback", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_SYNC
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-tabs", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
-    { NS_ABOUT_MODULE_CONTRACTID_PREFIX "sync-progress", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #endif
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "home", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "newtab", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "permissions", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "preferences", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "downloads", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
     { NS_ABOUT_MODULE_CONTRACTID_PREFIX "accounts", &kNS_BROWSER_ABOUT_REDIRECTOR_CID },
 #ifdef MOZ_SERVICES_HEALTHREPORT
--- a/browser/components/loop/content/js/panel.js
+++ b/browser/components/loop/content/js/panel.js
@@ -230,28 +230,39 @@ loop.panel = (function(_, mozL10n) {
       this.closeWindow();
     },
 
     handleGuestClick: function(event) {
       this.props.mozLoop.logOutFromFxA();
     },
 
     render: function() {
+      var shortname = mozL10n.get("clientShortname2");
+      var line1 = mozL10n.get("sign_in_again_title_line_one", {
+        clientShortname2: shortname
+      });
+      var line2 = mozL10n.get("sign_in_again_title_line_two2", {
+        clientShortname2: shortname
+      });
+      var useGuestString = mozL10n.get("sign_in_again_use_as_guest_button2", {
+        clientSuperShortname: mozL10n.get("clientSuperShortname")
+      });
+
       return (
         React.createElement("div", {className: "sign-in-request"}, 
-          React.createElement("h1", null, mozL10n.get("sign_in_again_title_line_one")), 
-          React.createElement("h2", null, mozL10n.get("sign_in_again_title_line_two")), 
+          React.createElement("h1", null, line1), 
+          React.createElement("h2", null, line2), 
           React.createElement("div", null, 
             React.createElement("button", {className: "btn btn-info sign-in-request-button", 
                     onClick: this.handleSignInClick}, 
               mozL10n.get("sign_in_again_button")
             )
           ), 
           React.createElement("a", {onClick: this.handleGuestClick}, 
-            mozL10n.get("sign_in_again_use_as_guest_button")
+            useGuestString
           )
         )
       );
     }
   });
 
   var ToSView = React.createClass({displayName: "ToSView",
     mixins: [sharedMixins.WindowCloseMixin],
--- a/browser/components/loop/content/js/panel.jsx
+++ b/browser/components/loop/content/js/panel.jsx
@@ -230,28 +230,39 @@ loop.panel = (function(_, mozL10n) {
       this.closeWindow();
     },
 
     handleGuestClick: function(event) {
       this.props.mozLoop.logOutFromFxA();
     },
 
     render: function() {
+      var shortname = mozL10n.get("clientShortname2");
+      var line1 = mozL10n.get("sign_in_again_title_line_one", {
+        clientShortname2: shortname
+      });
+      var line2 = mozL10n.get("sign_in_again_title_line_two2", {
+        clientShortname2: shortname
+      });
+      var useGuestString = mozL10n.get("sign_in_again_use_as_guest_button2", {
+        clientSuperShortname: mozL10n.get("clientSuperShortname")
+      });
+
       return (
         <div className="sign-in-request">
-          <h1>{mozL10n.get("sign_in_again_title_line_one")}</h1>
-          <h2>{mozL10n.get("sign_in_again_title_line_two")}</h2>
+          <h1>{line1}</h1>
+          <h2>{line2}</h2>
           <div>
             <button className="btn btn-info sign-in-request-button"
                     onClick={this.handleSignInClick}>
               {mozL10n.get("sign_in_again_button")}
             </button>
           </div>
           <a onClick={this.handleGuestClick}>
-            {mozL10n.get("sign_in_again_use_as_guest_button")}
+            {useGuestString}
           </a>
         </div>
       );
     }
   });
 
   var ToSView = React.createClass({
     mixins: [sharedMixins.WindowCloseMixin],
--- a/browser/components/places/content/editBookmarkOverlay.js
+++ b/browser/components/places/content/editBookmarkOverlay.js
@@ -190,17 +190,17 @@ let gEditItemOverlay = {
       this._namePicker.readOnly = this.readOnly;
     }
 
     // In some cases we want to hide the location field, since it's not
     // human-readable, but we still want to initialize it.
     showOrCollapse("locationRow", isURI, "location");
     if (isURI) {
       this._initLocationField();
-      this._locationField.readOnly = !this.readOnly;
+      this._locationField.readOnly = this.readOnly;
     }
 
     // hide the description field for
     if (showOrCollapse("descriptionRow", isItem && !this.readOnly,
                        "description")) {
       this._initDescriptionField();
       this._descriptionField.readOnly = this.readOnly;
     }
--- a/browser/components/places/content/editBookmarkOverlay.xul
+++ b/browser/components/places/content/editBookmarkOverlay.xul
@@ -9,17 +9,17 @@
 
 <?xml-stylesheet href="chrome://browser/skin/places/editBookmarkOverlay.css"?>
 <?xml-stylesheet href="chrome://browser/skin/places/places.css"?>
 
 <overlay id="editBookmarkOverlay"
          xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <vbox id="editBookmarkPanelContent" flex="1">
-    <hbox id="editBMPanel_selectionCount" hidden="true" pack="center">
+    <hbox id="editBMPanel_selectionCount" pack="center">
       <label id="editBMPanel_itemsCountText"/>
     </hbox>
 
     <grid id="editBookmarkPanelGrid" flex="1">
       <columns id="editBMPanel_columns">
         <column id="editBMPanel_labelColumn" />
         <column flex="1" id="editBMPanel_editColumn" />
       </columns>
--- a/browser/components/pocket/panels/js/saved.js
+++ b/browser/components/pocket/panels/js/saved.js
@@ -54,17 +54,17 @@ var PKT_SAVED_OVERLAY = function (option
         {
             myself.suggestedTagsLoaded = true;
             myself.startCloseTimer();
             return;
         }
 
         thePKT_SAVED.sendMessage("getSuggestedTags",
         {
-            url: myself.savedUrl || window.location.toString()
+            url: myself.savedUrl
         }, function(resp) 
         {
             $('.pkt_ext_suggestedtag_detail').removeClass('pkt_ext_suggestedtag_detail_loading');
             if (resp.status == 'success') 
             {
                 var newtags = [];
                 for (var i = 0; i < resp.value.suggestedTags.length; i++)
                 {
@@ -309,17 +309,17 @@ var PKT_SAVED_OVERLAY = function (option
                 if (text.length)
                 {
                     originaltags.push(text);
                 }
             });
 
             thePKT_SAVED.sendMessage("addTags",
             {
-                url: myself.savedUrl || window.location.toString(),
+                url: myself.savedUrl,
                 tags: originaltags   
             }, function(resp)
             {
                 if (resp.status == 'success') 
                 {
                     myself.showStateFinalMsg(myself.dictJSON.tagssaved);
                 }
                 else if (resp.status == 'error') 
@@ -406,17 +406,17 @@ var PKT_SAVED_OVERLAY = function (option
         });
     };
     this.showStateSaved = function(initobj) {
         this.wrapper.find('.pkt_ext_detail h2').text(this.dictJSON.pagesaved);
         this.wrapper.find('.pkt_ext_btn').addClass('pkt_ext_btn_disabled');
         if (typeof initobj.item == 'object')
         {
             this.savedItemId = initobj.item.item_id;
-            this.savedUrl = initobj.item.resolved_url;
+            this.savedUrl = initobj.item.given_url;
         }
         $('.pkt_ext_containersaved').addClass('pkt_ext_container_detailactive').removeClass('pkt_ext_container_finalstate');
 
         myself.fillUserTags();
         if (myself.suggestedTagsLoaded) {
             myself.startCloseTimer();
         }
         else {
--- a/browser/components/search/content/search.xml
+++ b/browser/components/search/content/search.xml
@@ -633,16 +633,17 @@
                    anonid="searchbar-textbox"
                    type="autocomplete"
                    flex="1"
                    autocompletepopup="PopupSearchAutoComplete"
                    autocompletesearch="search-autocomplete"
                    autocompletesearchparam="searchbar-history"
                    maxrows="10"
                    completeselectedindex="true"
+                   minresultsforpopup="0"
                    xbl:inherits="disabled,disableautocomplete,searchengine,src,newlines">
         <!--
         Empty <box> to properly position the icon within the autocomplete
         binding's anonymous children (the autocomplete binding positions <box>
         children differently)
         -->
         <xul:box>
           <xul:hbox class="searchbar-search-button-container">
@@ -1082,19 +1083,20 @@
         <body><![CDATA[
           let popup = this.popup;
           let list = document.getAnonymousElementByAttribute(popup, "anonid",
                                                              "search-panel-one-offs");
           let selectedButton = this.selectedButton;
           let buttons = this.getSelectableButtons(aCycleEngines);
 
           let suggestionsHidden;
-          if (!aSkipSuggestions && popup.hasAttribute("showonlysettings")) {
-            aSkipSuggestions = true;
-            suggestionsHidden = true;
+          if (!aSkipSuggestions) {
+            let suggestions = document.getAnonymousElementByAttribute(popup, "anonid", "tree");
+            suggestionsHidden = suggestions.getAttribute("collapsed") == "true";
+            aSkipSuggestions = suggestionsHidden;
           }
 
           // If the last suggestion is selected, DOWN selects the first button.
           if (!aSkipSuggestions && aForward &&
               popup.selectedIndex + 1 == popup.view.rowCount) {
             this.selectedButton = buttons[0];
             return false;
           }
--- a/browser/components/search/test/browser_searchbar_openpopup.js
+++ b/browser/components/search/test/browser_searchbar_openpopup.js
@@ -8,16 +8,17 @@ this._scriptLoader = Cc["@mozilla.org/mo
                      getService(Ci.mozIJSSubScriptLoader);
 this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
 
 const searchbar = document.getElementById("searchbar");
 const searchIcon = document.getAnonymousElementByAttribute(searchbar, "anonid", "searchbar-search-button");
 const goButton = document.getAnonymousElementByAttribute(searchbar, "anonid", "search-go-button");
 const textbox = searchbar._textbox;
 const searchPopup = document.getElementById("PopupSearchAutoComplete");
+const kValues = ["long text", "long text 2", "long text 3"];
 
 const isWindows = Services.appinfo.OS == "WINNT";
 const mouseDown = isWindows ? 2 : 1;
 const mouseUp = isWindows ? 4 : 2;
 const utils = window.QueryInterface(Ci.nsIInterfaceRequestor)
                     .getInterface(Ci.nsIDOMWindowUtils);
 const scale = utils.screenPixelsPerCSSPixel;
 
@@ -39,16 +40,47 @@ function* synthesizeNativeMouseClick(aEl
 
     utils.sendNativeMouseEvent(x * scale, y * scale, mouseDown, 0, null);
     utils.sendNativeMouseEvent(x * scale, y * scale, mouseUp, 0, null);
   });
 }
 
 add_task(function* init() {
   yield promiseNewEngine("testEngine.xml");
+
+  // First cleanup the form history in case other tests left things there.
+  yield new Promise((resolve, reject) => {
+    info("cleanup the search history");
+    searchbar.FormHistory.update({op: "remove", fieldname: "searchbar-history"},
+                                 {handleCompletion: resolve,
+                                  handleError: reject});
+  });
+
+  yield new Promise((resolve, reject) => {
+    info("adding search history values: " + kValues);
+    let ops = kValues.map(value => { return {op: "add",
+                                             fieldname: "searchbar-history",
+                                             value: value}
+                                   });
+    searchbar.FormHistory.update(ops, {
+      handleCompletion: function() {
+        registerCleanupFunction(() => {
+          info("removing search history values: " + kValues);
+          let ops =
+            kValues.map(value => { return {op: "remove",
+                                           fieldname: "searchbar-history",
+                                           value: value}
+                                 });
+          searchbar.FormHistory.update(ops);
+        });
+        resolve();
+      },
+      handleError: reject
+    });
+  });
 });
 
 // Adds a task that shouldn't show the search suggestions popup.
 function add_no_popup_task(task) {
   add_task(function*() {
     let sawPopup = false;
     function listener() {
       sawPopup = true;
--- a/browser/config/version.txt
+++ b/browser/config/version.txt
@@ -1,1 +1,1 @@
-40.0a1
+41.0a1
--- a/browser/devtools/debugger/test/browser.ini
+++ b/browser/devtools/debugger/test/browser.ini
@@ -11,16 +11,17 @@ support-files =
   code_binary_search.js
   code_binary_search.map
   code_blackboxing_blackboxme.js
   code_blackboxing_one.js
   code_blackboxing_three.js
   code_blackboxing_two.js
   code_breakpoints-break-on-last-line-of-script-on-reload.js
   code_breakpoints-other-tabs.js
+  code_bug-896139.js
   code_frame-script.js
   code_function-search-01.js
   code_function-search-02.js
   code_function-search-03.js
   code_location-changes.js
   code_math.js
   code_math.map
   code_math.min.js
@@ -42,16 +43,17 @@ support-files =
   code_ugly-8^headers^
   doc_auto-pretty-print-01.html
   doc_auto-pretty-print-02.html
   doc_binary_search.html
   doc_blackboxing.html
   doc_breakpoints-break-on-last-line-of-script-on-reload.html
   doc_breakpoints-other-tabs.html
   doc_breakpoints-reload.html
+  doc_bug-896139.html
   doc_closures.html
   doc_closure-optimized-out.html
   doc_cmd-break.html
   doc_cmd-dbg.html
   doc_breakpoint-move.html
   doc_conditional-breakpoints.html
   doc_domnode-variables.html
   doc_editor-mode.html
@@ -159,16 +161,18 @@ skip-if = e10s && debug
 [browser_dbg_breakpoints-new-script.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-other-tabs.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-pane.js]
 skip-if = e10s && debug
 [browser_dbg_breakpoints-reload.js]
 skip-if = e10s && debug
+[browser_dbg_bug-896139.js]
+skip-if = e10s && debug
 [browser_dbg_chrome-create.js]
 skip-if = e10s && debug
 [browser_dbg_chrome-debugging.js]
 skip-if = e10s && debug
 [browser_dbg_clean-exit-window.js]
 skip-if = true # Bug 933950 (leaky test)
 [browser_dbg_clean-exit.js]
 skip-if = true # Bug 1044985 (racy test)
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/browser_dbg_bug-896139.js
@@ -0,0 +1,40 @@
+/* Any copyright is dedicated to the Public Domain.
+   http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Bug 896139 - Breakpoints not triggering when reloading script.
+ */
+
+const TAB_URL = "doc_bug-896139.html";
+const SCRIPT_URL = "code_bug-896139.js";
+
+function test() {
+  Task.spawn(function* () {
+    function testBreakpoint() {
+      let promise = waitForDebuggerEvents(panel, win.EVENTS.FETCHED_SCOPES);
+      callInTab(tab, "f");
+      return promise.then(() => doResume(panel));
+    }
+
+    let [tab,, panel] = yield initDebugger(EXAMPLE_URL + TAB_URL);
+    let win = panel.panelWin;
+    yield waitForSourceShown(panel, SCRIPT_URL);
+    yield panel.addBreakpoint({
+      actor: getSourceActor(win.DebuggerView.Sources, EXAMPLE_URL + SCRIPT_URL),
+      line: 6
+    });
+
+    // Race condition: the setBreakpoint request sometimes leaves the
+    // debugger in paused state for a bit because we are called before
+    // that request finishes (see bug 1156531 for plans to fix)
+    if(panel.panelWin.gThreadClient.state !== "attached") {
+      yield waitForThreadEvents(panel, "resumed");
+    }
+
+    yield testBreakpoint();
+    yield reloadActiveTab(panel, win.EVENTS.SOURCE_SHOWN);
+    yield testBreakpoint();
+
+    yield closeDebuggerAndFinish(panel);
+  });
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/code_bug-896139.js
@@ -0,0 +1,8 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+function f() {
+  var a = 1;
+  var b = 2;
+  var c = 3;
+}
new file mode 100644
--- /dev/null
+++ b/browser/devtools/debugger/test/doc_bug-896139.html
@@ -0,0 +1,18 @@
+<!-- Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/ -->
+
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="utf-8"/>
+    <script>
+      window.onload = function () {
+        var script = document.createElement("script");
+        script.setAttribute("src", "code_bug-896139.js");
+        document.body.appendChild(script);
+      }
+    </script>
+  </head>
+  <body>
+  </body>
+</html>
--- a/browser/devtools/inspector/inspector-panel.js
+++ b/browser/devtools/inspector/inspector-panel.js
@@ -13,16 +13,26 @@ let EventEmitter = require("devtools/too
 let clipboard = require("sdk/clipboard");
 let {HostType} = require("devtools/framework/toolbox").Toolbox;
 
 loader.lazyGetter(this, "MarkupView", () => require("devtools/markupview/markup-view").MarkupView);
 loader.lazyGetter(this, "HTMLBreadcrumbs", () => require("devtools/inspector/breadcrumbs").HTMLBreadcrumbs);
 loader.lazyGetter(this, "ToolSidebar", () => require("devtools/framework/sidebar").ToolSidebar);
 loader.lazyGetter(this, "SelectorSearch", () => require("devtools/inspector/selector-search").SelectorSearch);
 
+loader.lazyGetter(this, "strings", () => {
+  return Services.strings.createBundle("chrome://browser/locale/devtools/inspector.properties");
+});
+loader.lazyGetter(this, "toolboxStrings", () => {
+  return Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties");
+});
+loader.lazyGetter(this, "clipboardHelper", () => {
+  return Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
+});
+
 const LAYOUT_CHANGE_TIMER = 250;
 
 /**
  * Represents an open instance of the Inspector for a tab.
  * The inspector controls the breadcrumbs, the markup view, and the sidebar
  * (computed view, rule view, font view and layout view).
  *
  * Events:
@@ -72,17 +82,17 @@ function InspectorPanel(iframeWindow, to
 }
 
 exports.InspectorPanel = InspectorPanel;
 
 InspectorPanel.prototype = {
   /**
    * open is effectively an asynchronous constructor
    */
-  open: function InspectorPanel_open() {
+  open: function() {
     return this.target.makeRemote().then(() => {
       return this._getPageStyle();
     }).then(() => {
       return this._getDefaultNodeForSelection();
     }).then(defaultSelection => {
       return this._deferredOpen(defaultSelection);
     }).then(null, console.error);
   },
@@ -156,17 +166,17 @@ InspectorPanel.prototype = {
       // Show a warning when the debugger is paused.
       // We show the warning only when the inspector
       // is selected.
       this.updateDebuggerPausedWarning = () => {
         let notificationBox = this._toolbox.getNotificationBox();
         let notification = notificationBox.getNotificationWithValue("inspector-script-paused");
         if (!notification && this._toolbox.currentToolId == "inspector" &&
             this.target.isThreadPaused) {
-          let message = this.strings.GetStringFromName("debuggerPausedWarning.message");
+          let message = strings.GetStringFromName("debuggerPausedWarning.message");
           notificationBox.appendNotification(message,
             "inspector-script-paused", "", notificationBox.PRIORITY_WARNING_HIGH);
         }
 
         if (notification && this._toolbox.currentToolId != "inspector") {
           notificationBox.removeNotification(notification);
         }
 
@@ -283,37 +293,37 @@ InspectorPanel.prototype = {
     this._target = value;
   },
 
   /**
    * Indicate that a tool has modified the state of the page.  Used to
    * decide whether to show the "are you sure you want to navigate"
    * notification.
    */
-  markDirty: function InspectorPanel_markDirty() {
+  markDirty: function() {
     this.isDirty = true;
   },
 
   /**
    * Hooks the searchbar to show result and auto completion suggestions.
    */
-  setupSearchBox: function InspectorPanel_setupSearchBox() {
+  setupSearchBox: function() {
     // Initiate the selectors search object.
     if (this.searchSuggestions) {
       this.searchSuggestions.destroy();
       this.searchSuggestions = null;
     }
     this.searchBox = this.panelDoc.getElementById("inspector-searchbox");
     this.searchSuggestions = new SelectorSearch(this, this.searchBox);
   },
 
   /**
    * Build the sidebar.
    */
-  setupSidebar: function InspectorPanel_setupSidebar() {
+  setupSidebar: function() {
     let tabbox = this.panelDoc.querySelector("#inspector-sidebar");
     this.sidebar = new ToolSidebar(tabbox, this, "inspector", {
       showAllTabsMenu: true
     });
 
     let defaultTab = Services.prefs.getCharPref("devtools.inspector.activeSidebar");
 
     this._setDefaultSidebar = (event, toolId) => {
@@ -360,17 +370,17 @@ InspectorPanel.prototype = {
     this._paneToggleButton.addEventListener("mousedown",
       this.onPaneToggleButtonClicked);
     this.updatePaneToggleButton();
   },
 
   /**
    * Reset the inspector on new root mutation.
    */
-  onNewRoot: function InspectorPanel_onNewRoot() {
+  onNewRoot: function() {
     this._defaultNode = null;
     this.selection.setNodeFront(null);
     this._destroyMarkup();
     this.isDirty = false;
 
     let onNodeSelected = defaultNode => {
       // Cancel this promise resolution as a new one had
       // been queued up.
@@ -423,17 +433,17 @@ InspectorPanel.prototype = {
     } else {
       return null;
     }
   },
 
   /**
    * When a new node is selected.
    */
-  onNewSelection: function InspectorPanel_onNewSelection(event, value, reason) {
+  onNewSelection: function(event, value, reason) {
     if (reason === "selection-destroy") {
       return;
     }
 
     this.cancelLayoutChange();
 
     // Wait for all the known tools to finish updating and then let the
     // client know.
@@ -517,39 +527,38 @@ InspectorPanel.prototype = {
    */
   cancelUpdate: function() {
     this._updateProgress = null;
   },
 
   /**
    * When a new node is selected, before the selection has changed.
    */
-  onBeforeNewSelection: function InspectorPanel_onBeforeNewSelection(event,
-                                                                     node) {
+  onBeforeNewSelection: function(event, node) {
     if (this.breadcrumbs.indexOf(node) == -1) {
       // only clear locks if we'd have to update breadcrumbs
       this.clearPseudoClasses();
     }
   },
 
   /**
    * When a node is deleted, select its parent node or the defaultNode if no
    * parent is found (may happen when deleting an iframe inside which the
    * node was selected).
    */
-  onDetached: function InspectorPanel_onDetached(event, parentNode) {
+  onDetached: function(event, parentNode) {
     this.cancelLayoutChange();
     this.breadcrumbs.cutAfter(this.breadcrumbs.indexOf(parentNode));
     this.selection.setNodeFront(parentNode ? parentNode : this._defaultNode, "detached");
   },
 
   /**
    * Destroy the inspector.
    */
-  destroy: function InspectorPanel__destroy() {
+  destroy: function() {
     if (this._panelDestroyer) {
       return this._panelDestroyer;
     }
 
     if (this.walker) {
       this.walker.off("new-root", this.onNewRoot);
       this.pageStyle = null;
     }
@@ -602,49 +611,49 @@ InspectorPanel.prototype = {
     ]);
 
     return this._panelDestroyer;
   },
 
   /**
    * Show the node menu.
    */
-  showNodeMenu: function InspectorPanel_showNodeMenu(aButton, aPosition, aExtraItems) {
+  showNodeMenu: function(aButton, aPosition, aExtraItems) {
     if (aExtraItems) {
       for (let item of aExtraItems) {
         this.nodemenu.appendChild(item);
       }
     }
     this.nodemenu.openPopup(aButton, aPosition, 0, 0, true, false);
   },
 
-  hideNodeMenu: function InspectorPanel_hideNodeMenu() {
+  hideNodeMenu: function() {
     this.nodemenu.hidePopup();
   },
 
   /**
    * Returns the clipboard content if it is appropriate for pasting
    * into the current node's outer HTML, otherwise returns null.
    */
-  _getClipboardContentForPaste: function Inspector_getClipboardContentForPaste() {
+  _getClipboardContentForPaste: function() {
     let flavors = clipboard.currentFlavors;
     if (flavors.indexOf("text") != -1 ||
         (flavors.indexOf("html") != -1 && flavors.indexOf("image") == -1)) {
       let content = clipboard.get();
       if (content && content.trim().length > 0) {
         return content;
       }
     }
     return null;
   },
 
   /**
    * Disable the delete item if needed. Update the pseudo classes.
    */
-  _setupNodeMenu: function InspectorPanel_setupNodeMenu() {
+  _setupNodeMenu: function() {
     let isSelectionElement = this.selection.isElementNode() &&
                              !this.selection.isPseudoElementNode();
     let isEditableElement = isSelectionElement &&
                             !this.selection.isAnonymousNode();
 
     // Set the pseudo classes
     for (let name of ["hover", "active", "focus"]) {
       let menu = this.panelDoc.getElementById("node-menu-pseudo-" + name);
@@ -740,29 +749,29 @@ InspectorPanel.prototype = {
     let markupContainer = this.markup.getContainer(this.selection.nodeFront);
     if (isSelectionElement && markupContainer && markupContainer.isPreviewable()) {
       copyImageData.removeAttribute("disabled");
     } else {
       copyImageData.setAttribute("disabled", "true");
     }
   },
 
-  _resetNodeMenu: function InspectorPanel_resetNodeMenu() {
+  _resetNodeMenu: function() {
     // Remove any extra items
     while (this.lastNodemenuItem.nextSibling) {
       let toDelete = this.lastNodemenuItem.nextSibling;
       toDelete.parentNode.removeChild(toDelete);
     }
   },
 
   /**
    * Link menu items can be shown or hidden depending on the context and
    * selected node, and their labels can vary.
    */
-  _setupNodeLinkMenu: function InspectorPanel_setupNodeLinkMenu() {
+  _setupNodeLinkMenu: function() {
     let linkSeparator = this.panelDoc.getElementById("node-menu-link-separator");
     let linkFollow = this.panelDoc.getElementById("node-menu-link-follow");
     let linkCopy = this.panelDoc.getElementById("node-menu-link-copy");
 
     // Hide all by default.
     linkSeparator.setAttribute("hidden", "true");
     linkFollow.setAttribute("hidden", "true");
     linkCopy.setAttribute("hidden", "true");
@@ -781,41 +790,41 @@ InspectorPanel.prototype = {
           return;
         }
 
         linkSeparator.removeAttribute("hidden");
 
         // Links can't be opened in new tabs in the browser toolbox.
         if (type === "uri" && !this.target.chrome) {
           linkFollow.removeAttribute("hidden");
-          linkFollow.setAttribute("label", this.strings.GetStringFromName(
+          linkFollow.setAttribute("label", strings.GetStringFromName(
             "inspector.menu.openUrlInNewTab.label"));
         } else if (type === "cssresource") {
           linkFollow.removeAttribute("hidden");
-          linkFollow.setAttribute("label", this.toolboxStrings.GetStringFromName(
+          linkFollow.setAttribute("label", toolboxStrings.GetStringFromName(
             "toolbox.viewCssSourceInStyleEditor.label"));
         } else if (type === "jsresource") {
           linkFollow.removeAttribute("hidden");
-          linkFollow.setAttribute("label", this.toolboxStrings.GetStringFromName(
+          linkFollow.setAttribute("label", toolboxStrings.GetStringFromName(
             "toolbox.viewJsSourceInDebugger.label"));
         }
 
         linkCopy.removeAttribute("hidden");
-        linkCopy.setAttribute("label", this.strings.GetStringFromName(
+        linkCopy.setAttribute("label", strings.GetStringFromName(
           "inspector.menu.copyUrlToClipboard.label"));
       }, console.error);
     } else if (type === "idref") {
       linkSeparator.removeAttribute("hidden");
       linkFollow.removeAttribute("hidden");
-      linkFollow.setAttribute("label", this.strings.formatStringFromName(
+      linkFollow.setAttribute("label", strings.formatStringFromName(
         "inspector.menu.selectElement.label", [popupNode.dataset.link], 1));
     }
   },
 
-  _initMarkup: function InspectorPanel_initMarkup() {
+  _initMarkup: function() {
     let doc = this.panelDoc;
 
     this._markupBox = doc.getElementById("markup-box");
 
     // create tool iframe
     this._markupFrame = doc.createElement("iframe");
     this._markupFrame.setAttribute("flex", "1");
     this._markupFrame.setAttribute("tooltip", "aHTMLTooltip");
@@ -823,34 +832,34 @@ InspectorPanel.prototype = {
 
     // This is needed to enable tooltips inside the iframe document.
     this._boundMarkupFrameLoad = this._onMarkupFrameLoad.bind(this);
     this._markupFrame.addEventListener("load", this._boundMarkupFrameLoad, true);
 
     this._markupBox.setAttribute("collapsed", true);
     this._markupBox.appendChild(this._markupFrame);
     this._markupFrame.setAttribute("src", "chrome://browser/content/devtools/markup-view.xhtml");
-    this._markupFrame.setAttribute("aria-label", this.strings.GetStringFromName("inspector.panelLabel.markupView"));
+    this._markupFrame.setAttribute("aria-label", strings.GetStringFromName("inspector.panelLabel.markupView"));
   },
 
-  _onMarkupFrameLoad: function InspectorPanel__onMarkupFrameLoad() {
+  _onMarkupFrameLoad: function() {
     this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
     delete this._boundMarkupFrameLoad;
 
     this._markupFrame.contentWindow.focus();
 
     this._markupBox.removeAttribute("collapsed");
 
     let controllerWindow = this._toolbox.doc.defaultView;
     this.markup = new MarkupView(this, this._markupFrame, controllerWindow);
 
     this.emit("markuploaded");
   },
 
-  _destroyMarkup: function InspectorPanel__destroyMarkup() {
+  _destroyMarkup: function() {
     let destroyPromise;
 
     if (this._boundMarkupFrameLoad) {
       this._markupFrame.removeEventListener("load", this._boundMarkupFrameLoad, true);
       this._boundMarkupFrameLoad = null;
     }
 
     if (this.markup) {
@@ -895,198 +904,191 @@ InspectorPanel.prototype = {
     ViewHelpers.togglePane({
       visible: !isVisible,
       animated: true,
       delayed: true
     }, sidePane);
 
     if (isVisible) {
       button.setAttribute("pane-collapsed", "");
-      button.setAttribute("tooltiptext",
-        this.strings.GetStringFromName("inspector.expandPane"));
+      button.setAttribute("tooltiptext", strings.GetStringFromName("inspector.expandPane"));
     } else {
       button.removeAttribute("pane-collapsed");
-      button.setAttribute("tooltiptext",
-        this.strings.GetStringFromName("inspector.collapsePane"));
+      button.setAttribute("tooltiptext", strings.GetStringFromName("inspector.collapsePane"));
     }
   },
 
   /**
    * Update the pane toggle button visibility depending on the toolbox host type.
    */
   updatePaneToggleButton: function() {
     this._paneToggleButton.setAttribute("hidden",
       this._toolbox.hostType === HostType.SIDE);
   },
 
   /**
    * Toggle a pseudo class.
    */
-  togglePseudoClass: function InspectorPanel_togglePseudoClass(aPseudo) {
+  togglePseudoClass: function(aPseudo) {
     if (this.selection.isElementNode()) {
       let node = this.selection.nodeFront;
       if (node.hasPseudoClassLock(aPseudo)) {
         return this.walker.removePseudoClassLock(node, aPseudo, {parents: true});
       }
 
       let hierarchical = aPseudo == ":hover" || aPseudo == ":active";
       return this.walker.addPseudoClassLock(node, aPseudo, {parents: hierarchical});
     }
   },
 
   /**
    * Show DOM properties
    */
-  showDOMProperties: function InspectorPanel_showDOMProperties() {
+  showDOMProperties: function() {
     this._toolbox.openSplitConsole().then(() => {
       let panel = this._toolbox.getPanel("webconsole");
       let jsterm = panel.hud.jsterm;
 
       jsterm.execute("inspect($0)");
       jsterm.inputNode.focus();
     });
   },
 
   /**
    * Clear any pseudo-class locks applied to the current hierarchy.
    */
-  clearPseudoClasses: function InspectorPanel_clearPseudoClasses() {
+  clearPseudoClasses: function() {
     if (!this.walker) {
       return;
     }
     return this.walker.clearPseudoClassLocks().then(null, console.error);
   },
 
   /**
    * Edit the outerHTML of the selected Node.
    */
-  editHTML: function InspectorPanel_editHTML() {
+  editHTML: function() {
     if (!this.selection.isNode()) {
       return;
     }
     if (this.markup) {
       this.markup.beginEditingOuterHTML(this.selection.nodeFront);
     }
   },
 
   /**
    * Paste the contents of the clipboard into the selected Node's outer HTML.
    */
-  pasteOuterHTML: function InspectorPanel_pasteOuterHTML() {
+  pasteOuterHTML: function() {
     let content = this._getClipboardContentForPaste();
     if (!content)
       return promise.reject("No clipboard content for paste");
 
     let node = this.selection.nodeFront;
     return this.markup.getNodeOuterHTML(node).then(oldContent => {
       this.markup.updateNodeOuterHTML(node, content, oldContent);
     });
   },
 
   /**
    * Paste the contents of the clipboard into the selected Node's inner HTML.
    */
-  pasteInnerHTML: function InspectorPanel_pasteInnerHTML() {
+  pasteInnerHTML: function() {
     let content = this._getClipboardContentForPaste();
     if (!content)
       return promise.reject("No clipboard content for paste");
 
     let node = this.selection.nodeFront;
     return this.markup.getNodeInnerHTML(node).then(oldContent => {
       this.markup.updateNodeInnerHTML(node, content, oldContent);
     });
   },
 
   /**
    * Paste the contents of the clipboard as adjacent HTML to the selected Node.
    * @param position The position as specified for Element.insertAdjacentHTML
    *        (i.e. "beforeBegin", "afterBegin", "beforeEnd", "afterEnd").
    */
-  pasteAdjacentHTML: function InspectorPanel_pasteAdjacent(position) {
+  pasteAdjacentHTML: function(position) {
     let content = this._getClipboardContentForPaste();
     if (!content)
       return promise.reject("No clipboard content for paste");
 
     let node = this.selection.nodeFront;
     return this.markup.insertAdjacentHTMLToNode(node, position, content);
   },
 
   /**
    * Copy the innerHTML of the selected Node to the clipboard.
    */
-  copyInnerHTML: function InspectorPanel_copyInnerHTML() {
+  copyInnerHTML: function() {
     if (!this.selection.isNode()) {
       return;
     }
     this._copyLongStr(this.walker.innerHTML(this.selection.nodeFront));
   },
 
   /**
    * Copy the outerHTML of the selected Node to the clipboard.
    */
-  copyOuterHTML: function InspectorPanel_copyOuterHTML()
-  {
+  copyOuterHTML: function() {
     if (!this.selection.isNode()) {
       return;
     }
 
     this._copyLongStr(this.walker.outerHTML(this.selection.nodeFront));
   },
 
   /**
    * Copy the data-uri for the currently selected image in the clipboard.
    */
-  copyImageDataUri: function InspectorPanel_copyImageDataUri()
-  {
+  copyImageDataUri: function() {
     let container = this.markup.getContainer(this.selection.nodeFront);
     if (container && container.isPreviewable()) {
       container.copyImageDataUri();
     }
   },
 
-  _copyLongStr: function InspectorPanel_copyLongStr(promise)
-  {
+  _copyLongStr: function(promise) {
     return promise.then(longstr => {
       return longstr.string().then(toCopy => {
         longstr.release().then(null, console.error);
         clipboardHelper.copyString(toCopy);
       });
     }).then(null, console.error);
   },
 
   /**
    * Copy a unique selector of the selected Node to the clipboard.
    */
-  copyUniqueSelector: function InspectorPanel_copyUniqueSelector()
-  {
+  copyUniqueSelector: function() {
     if (!this.selection.isNode()) {
       return;
     }
 
     this.selection.nodeFront.getUniqueSelector().then((selector) => {
       clipboardHelper.copyString(selector);
     }).then(null, console.error);
   },
 
   /**
    * Scroll the node into view.
    */
-  scrollNodeIntoView: function InspectorPanel_scrollNodeIntoView()
-  {
+  scrollNodeIntoView: function() {
     if (!this.selection.isNode()) {
       return;
     }
 
     this.selection.nodeFront.scrollIntoView();
   },
 
   /**
    * Delete the selected node.
    */
-  deleteNode: function IUI_deleteNode() {
+  deleteNode: function() {
     if (!this.selection.isNode() ||
          this.selection.isRoot()) {
       return;
     }
 
     // If the markup panel is active, use the markup panel to delete
     // the node, making this an undoable action.
     if (this.markup) {
@@ -1097,17 +1099,17 @@ InspectorPanel.prototype = {
     }
   },
 
   /**
    * This method is here for the benefit of the node-menu-link-follow menu item
    * in the inspector contextual-menu. It's behavior depends on which node was
    * right-clicked when the menu was opened.
    */
-  followAttributeLink: function InspectorPanel_followLink(e) {
+  followAttributeLink: function(e) {
     let type = this.panelDoc.popupNode.dataset.type;
     let link = this.panelDoc.popupNode.dataset.link;
 
     if (type === "uri" || type === "cssresource" || type === "jsresource") {
       // Open link in a new tab.
       // When the inspector menu was setup on click (see _setupNodeLinkMenu), we
       // already checked that resolveRelativeURL existed.
       this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
@@ -1134,78 +1136,53 @@ InspectorPanel.prototype = {
     }
   },
 
   /**
    * This method is here for the benefit of the node-menu-link-copy menu item
    * in the inspector contextual-menu. It's behavior depends on which node was
    * right-clicked when the menu was opened.
    */
-  copyAttributeLink: function InspectorPanel_copyLink(e) {
+  copyAttributeLink: function(e) {
     let link = this.panelDoc.popupNode.dataset.link;
     // When the inspector menu was setup on click (see _setupNodeLinkMenu), we
     // already checked that resolveRelativeURL existed.
     this.inspector.resolveRelativeURL(link, this.selection.nodeFront).then(url => {
       clipboardHelper.copyString(url);
     }, console.error);
   },
 
   /**
-  * Trigger a high-priority layout change for things that need to be
-  * updated immediately
-  */
-  immediateLayoutChange: function Inspector_immediateLayoutChange()
-  {
+   * Trigger a high-priority layout change for things that need to be
+   * updated immediately
+   */
+  immediateLayoutChange: function() {
     this.emit("layout-change");
   },
 
   /**
    * Schedule a low-priority change event for things like paint
    * and resize.
    */
-  scheduleLayoutChange: function Inspector_scheduleLayoutChange(event)
-  {
+  scheduleLayoutChange: function(event) {
     // Filter out non browser window resize events (i.e. triggered by iframes)
     if (this.browser.contentWindow === event.target) {
       if (this._timer) {
         return null;
       }
       this._timer = this.panelWin.setTimeout(() => {
         this.emit("layout-change");
         this._timer = null;
       }, LAYOUT_CHANGE_TIMER);
     }
   },
 
   /**
    * Cancel a pending low-priority change event if any is
    * scheduled.
    */
-  cancelLayoutChange: function Inspector_cancelLayoutChange()
-  {
+  cancelLayoutChange: function() {
     if (this._timer) {
       this.panelWin.clearTimeout(this._timer);
       delete this._timer;
     }
   }
 };
-
-/////////////////////////////////////////////////////////////////////////
-//// Initializers
-
-loader.lazyGetter(InspectorPanel.prototype, "strings", function () {
-  return Services.strings.createBundle(
-    "chrome://browser/locale/devtools/inspector.properties");
-});
-
-loader.lazyGetter(InspectorPanel.prototype, "toolboxStrings", function () {
-  return Services.strings.createBundle(
-    "chrome://browser/locale/devtools/toolbox.properties");
-});
-
-loader.lazyGetter(this, "clipboardHelper", function() {
-  return Cc["@mozilla.org/widget/clipboardhelper;1"].
-    getService(Ci.nsIClipboardHelper);
-});
-
-loader.lazyGetter(this, "DOMUtils", function () {
-  return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
-});
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -495,19 +495,19 @@ MarkupView.prototype = {
 
     switch(aEvent.keyCode) {
       case Ci.nsIDOMKeyEvent.DOM_VK_H:
         if (aEvent.metaKey || aEvent.shiftKey) {
           handled = false;
         } else {
           let node = this._selectedContainer.node;
           if (node.hidden) {
-            this.walker.unhideNode(node).then(() => this.nodeChanged(node));
+            this.walker.unhideNode(node);
           } else {
-            this.walker.hideNode(node).then(() => this.nodeChanged(node));
+            this.walker.hideNode(node);
           }
         }
         break;
       case Ci.nsIDOMKeyEvent.DOM_VK_DELETE:
         this.deleteNode(this._selectedContainer.node);
         break;
       case Ci.nsIDOMKeyEvent.DOM_VK_BACK_SPACE:
         this.deleteNode(this._selectedContainer.node, true);
@@ -1201,25 +1201,16 @@ MarkupView.prototype = {
   unmarkSelectedNode: function() {
     if (this._selectedContainer) {
       this._selectedContainer.selected = false;
       this._selectedContainer = null;
     }
   },
 
   /**
-   * Called when the markup panel initiates a change on a node.
-   */
-  nodeChanged: function(aNode) {
-    if (aNode === this._inspector.selection.nodeFront) {
-      this._inspector.change("markupview");
-    }
-  },
-
-  /**
    * Check if the current selection is a descendent of the container.
    * if so, make sure it's among the visible set for the container,
    * and set the dirty flag if needed.
    * @returns The node that should be made visible, if any.
    */
   _checkSelectionVisible: function(aContainer) {
     let centered = null;
     let node = this._inspector.selection.nodeFront;
@@ -2268,23 +2259,19 @@ function TextEditor(aContainer, aNode, a
       if (!aCommit) {
         return;
       }
       this.node.getNodeValue().then(longstr => {
         longstr.string().then(oldValue => {
           longstr.release().then(null, console.error);
 
           this.container.undo.do(() => {
-            this.node.setNodeValue(aVal).then(() => {
-              this.markup.nodeChanged(this.node);
-            });
+            this.node.setNodeValue(aVal);
           }, () => {
-            this.node.setNodeValue(oldValue).then(() => {
-              this.markup.nodeChanged(this.node);
-            });
+            this.node.setNodeValue(oldValue);
           });
         });
       });
     }
   });
 
   this.update();
 }
--- a/browser/devtools/netmonitor/netmonitor-controller.js
+++ b/browser/devtools/netmonitor/netmonitor-controller.js
@@ -515,72 +515,72 @@ NetworkEventsHandler.prototype = {
    * @param string type
    *        Message type.
    * @param object packet
    *        The message received from the server.
    * @param object networkInfo
    *        The network request information.
    */
   _onNetworkEventUpdate: function(type, { packet, networkInfo }) {
-    let actor = networkInfo.actor;
+    let { actor, request: { url } } = networkInfo;
 
     switch (packet.updateType) {
       case "requestHeaders":
         this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
-        window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
+        window.emit(EVENTS.UPDATING_REQUEST_HEADERS, [actor, url]);
         break;
       case "requestCookies":
         this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
-        window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
+        window.emit(EVENTS.UPDATING_REQUEST_COOKIES, [actor, url]);
         break;
       case "requestPostData":
         this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData);
-        window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
+        window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, [actor, url]);
         break;
       case "securityInfo":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           securityState: networkInfo.securityInfo,
         });
         this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
-        window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
+        window.emit(EVENTS.UPDATING_SECURITY_INFO, [actor, url]);
         break;
       case "responseHeaders":
         this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders);
-        window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
+        window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, [actor, url]);
         break;
       case "responseCookies":
         this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies);
-        window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
+        window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, [actor, url]);
         break;
       case "responseStart":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           httpVersion: networkInfo.response.httpVersion,
           remoteAddress: networkInfo.response.remoteAddress,
           remotePort: networkInfo.response.remotePort,
           status: networkInfo.response.status,
           statusText: networkInfo.response.statusText,
           headersSize: networkInfo.response.headersSize
         });
-        window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
+        window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, [actor, url]);
         break;
       case "responseContent":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           contentSize: networkInfo.response.bodySize,
           transferredSize: networkInfo.response.transferredSize,
           mimeType: networkInfo.response.content.mimeType
         });
         this.webConsoleClient.getResponseContent(actor, this._onResponseContent);
-        window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
+        window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, [actor, url]);
         break;
       case "eventTimings":
         NetMonitorView.RequestsMenu.updateRequest(actor, {
           totalTime: networkInfo.totalTime
         });
         this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
-        window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
+        window.emit(EVENTS.UPDATING_EVENT_TIMINGS, [actor, url]);
         break;
     }
   },
 
   /**
    * Handles additional information received for a "requestHeaders" packet.
    *
    * @param object aResponse
--- a/browser/devtools/netmonitor/test/head.js
+++ b/browser/devtools/netmonitor/test/head.js
@@ -188,17 +188,16 @@ function teardown(aMonitor) {
   return deferred.promise;
 }
 
 function waitForNetworkEvents(aMonitor, aGetRequests, aPostRequests = 0) {
   let deferred = promise.defer();
 
   let panel = aMonitor.panelWin;
   let events = panel.EVENTS;
-  let menu = panel.NetMonitorView.RequestsMenu;
 
   let progress = {};
   let genericEvents = 0;
   let postEvents = 0;
 
   let awaitedEventsToListeners = [
     ["UPDATING_REQUEST_HEADERS", onGenericEvent],
     ["RECEIVED_REQUEST_HEADERS", onGenericEvent],
@@ -233,23 +232,22 @@ function waitForNetworkEvents(aMonitor, 
     maybeResolve(event, actor);
   }
 
   function onPostEvent(event, actor) {
     postEvents++;
     maybeResolve(event, actor);
   }
 
-  function maybeResolve(event, actor) {
+  function maybeResolve(event, [actor, url]) {
     info("> Network events progress: " +
       genericEvents + "/" + ((aGetRequests + aPostRequests) * 13) + ", " +
       postEvents + "/" + (aPostRequests * 2) + ", " +
       "got " + event + " for " + actor);
 
-    let url = menu.getItemByValue(actor).attachment.url;
     updateProgressForURL(url, event);
     info("> Current state: " + JSON.stringify(progress, null, 2));
 
     // There are 15 updates which need to be fired for a request to be
     // considered finished. The "requestPostData" packet isn't fired for
     // non-POST requests.
     if (genericEvents == (aGetRequests + aPostRequests) * 13 &&
         postEvents == aPostRequests * 2) {
--- a/browser/devtools/performance/test/browser_perf-refresh.js
+++ b/browser/devtools/performance/test/browser_perf-refresh.js
@@ -4,39 +4,48 @@
 /**
  * Rough test that the recording still continues after a refresh.
  */
 function spawnTest () {
   let { panel, target } = yield initPerformance(SIMPLE_URL);
   let { EVENTS, PerformanceController } = panel.panelWin;
 
   // Enable memory to test all the overview graphs.
-  Services.prefs.setBoolPref(MEMORY_PREF, true);
+  // TODO intermittent bug 1157523 is tracking a leak in this test
+  // disable memory to see if that narrows down the leaking.
+  // Services.prefs.setBoolPref(MEMORY_PREF, true);
+  Services.prefs.setBoolPref(MEMORY_PREF, false);
+  // TODO intermittent bug 1157523 also disable framerate ticks to further narrow it down
+  Services.prefs.setBoolPref(FRAMERATE_PREF, false);
 
   yield startRecording(panel);
 
-  yield reload(target)
+  yield reload(target);
 
   let rec = PerformanceController.getCurrentRecording();
   let { markers, memory, ticks } = rec.getAllData();
   // Store current length of items
   let markersLength = markers.length;
   let memoryLength = memory.length;
   let ticksLength = ticks.length;
 
   ok(rec.isRecording(), "RecordingModel should still be recording after reload");
 
   yield busyWait(100);
   yield waitUntil(() => rec.getMarkers().length > markersLength);
-  yield waitUntil(() => rec.getMemory().length > memoryLength);
-  yield waitUntil(() => rec.getTicks().length > ticksLength);
+  // TODO bug 1157523
+  // yield waitUntil(() => rec.getMemory().length > memoryLength);
+  // TODO bug 1157523
+  // yield waitUntil(() => rec.getTicks().length > ticksLength);
   ok("Markers, memory and ticks continue after reload");
 
   yield stopRecording(panel);
 
   let { allocations, profile, frames } = rec.getAllData();
-  ok(allocations, "allocations exist after refresh");
+  // TODO bug 1157523
+  // ok(allocations, "allocations exist after refresh");
   ok(profile, "profile exists after refresh");
-  ok(frames, "frames exist after refresh");
+  // TODO bug 1157523
+  // ok(frames, "frames exist after refresh");
 
   yield teardown(panel);
   finish();
 }
--- a/browser/devtools/projecteditor/test/browser.ini
+++ b/browser/devtools/projecteditor/test/browser.ini
@@ -7,16 +7,17 @@ support-files =
   helper_edits.js
 
 [browser_projecteditor_app_options.js]
 skip-if = buildapp == 'mulet'
 [browser_projecteditor_confirm_unsaved.js]
 [browser_projecteditor_contextmenu_01.js]
 [browser_projecteditor_contextmenu_02.js]
 [browser_projecteditor_delete_file.js]
+skip-if = e10s # Frequent failures in e10s - Bug 1020027
 [browser_projecteditor_rename_file.js]
 skip-if = buildapp == 'mulet'
 [browser_projecteditor_editing_01.js]
 skip-if = buildapp == 'mulet'
 [browser_projecteditor_editors_image.js]
 [browser_projecteditor_external_change.js]
 [browser_projecteditor_immediate_destroy.js]
 [browser_projecteditor_init.js]
--- a/browser/devtools/shared/test/browser_outputparser.js
+++ b/browser/devtools/shared/test/browser_outputparser.js
@@ -13,90 +13,121 @@ add_task(function*() {
 
 function* performTest() {
   let [host, win, doc] = yield createHost("bottom", "data:text/html," +
     "<h1>browser_outputParser.js</h1><div></div>");
 
   let parser = new OutputParser();
   testParseCssProperty(doc, parser);
   testParseCssVar(doc, parser);
-  testParseHTMLAttribute(doc, parser);
-  testParseNonCssHTMLAttribute(doc, parser);
 
   host.destroy();
 }
 
+// Class name used in color swatch.
+let COLOR_TEST_CLASS = 'test-class';
+
+// Create a new CSS color-parsing test.  |name| is the name of the CSS
+// property.  |value| is the CSS text to use.  |segments| is an array
+// describing the expected result.  If an element of |segments| is a
+// string, it is simply appended to the expected string.  Otherwise,
+// it must be an object with a |value| property and a |name| property.
+// These describe the color and are both used in the generated
+// expected output -- |name| is the color name as it appears in the
+// input (e.g., "red"); and |value| is the hash-style numeric value
+// for the color, which parseCssProperty emits in some spots (e.g.,
+// "#F00").
+//
+// This approach is taken to reduce boilerplate and to make it simpler
+// to modify the test when the parseCssProperty output changes.
+function makeColorTest(name, value, segments) {
+  let result = {
+    name,
+    value,
+    expected: ''
+  };
+
+  for (let segment of segments) {
+    if (typeof(segment) === 'string') {
+      result.expected += segment;
+    } else {
+      result.expected += '<span data-color="' + segment.value + '">' +
+        '<span style="background-color:' + segment.name +
+        '" class="' + COLOR_TEST_CLASS + '"></span><span>' +
+        segment.value + '</span></span>';
+    }
+  }
+
+  result.desc = "Testing " + name + ": " + value;
+
+  return result;
+}
+
 function testParseCssProperty(doc, parser) {
-  let frag = parser.parseCssProperty("border", "1px solid red", {
-    colorSwatchClass: "test-colorswatch"
-  });
+  let tests = [
+    makeColorTest("border", "1px solid red",
+                  ["1px solid ", {name: "red", value: "#F00"}]),
+
+    makeColorTest("background-image",
+                  "linear-gradient(to right, #F60 10%, rgba(0,0,0,1))",
+                  ["linear-gradient(to right, ", {name: "#F60", value: "#F60"},
+                   " 10%, ", {name: "rgba(0,0,0,1)", value: "#000"},
+                   ")"]),
+
+    // In "arial black", "black" is a font, not a color.
+    makeColorTest("font-family", "arial black", ["arial black"]),
+
+    makeColorTest("box-shadow", "0 0 1em red",
+                  ["0 0 1em ", {name: "red", value: "#F00"}]),
+
+    makeColorTest("box-shadow",
+                  "0 0 1em red, 2px 2px 0 0 rgba(0,0,0,.5)",
+                  ["0 0 1em ", {name: "red", value: "#F00"},
+                   ", 2px 2px 0 0 ",
+                   {name: "rgba(0,0,0,.5)", value: "rgba(0,0,0,.5)"}]),
+
+    makeColorTest("content", '"red"', ['"red"']),
+
+    // Invalid property names should not cause exceptions.
+    makeColorTest("hellothere", "'red'", ["'red'"]),
+
+    // This requires better parsing than we currently have available.
+    // See bug 1158288.
+    // makeColorTest("filter",
+    //               "blur(1px) drop-shadow(0 0 0 blue) url(red.svg#blue)",
+    //               ["blur(1px) drop-shadow(0 0 0 ",
+    //                {name: "blue", value: "#00F"},
+    //                ") url(red.svg#blue)"])
+
+  ];
 
   let target = doc.querySelector("div");
   ok(target, "captain, we have the div");
-  target.appendChild(frag);
+
+  for (let test of tests) {
+    info(test.desc);
 
-  is(target.innerHTML,
-     '1px solid <span data-color="#F00"><span style="background-color:red" class="test-colorswatch"></span><span>#F00</span></span>',
-     "CSS property correctly parsed");
-
-  target.innerHTML = "";
+    let frag = parser.parseCssProperty(test.name, test.value, {
+      colorSwatchClass: COLOR_TEST_CLASS
+    });
 
-  frag = parser.parseCssProperty("background-image", "linear-gradient(to right, #F60 10%, rgba(0,0,0,1))", {
-    colorSwatchClass: "test-colorswatch",
-    colorClass: "test-color"
-  });
-  target.appendChild(frag);
-  is(target.innerHTML,
-     'linear-gradient(to right, <span data-color="#F60"><span style="background-color:#F60" class="test-colorswatch"></span><span class="test-color">#F60</span></span> 10%, ' +
-     '<span data-color="#000"><span style="background-color:rgba(0,0,0,1)" class="test-colorswatch"></span><span class="test-color">#000</span></span>)',
-     "Gradient CSS property correctly parsed");
+    target.appendChild(frag);
 
-  target.innerHTML = "";
+    is(target.innerHTML, test.expected,
+       "CSS property correctly parsed for " + test.name + ": " + test.value);
+
+    target.innerHTML = "";
+  }
 }
 
 function testParseCssVar(doc, parser) {
   let frag = parser.parseCssProperty("color", "var(--some-kind-of-green)", {
     colorSwatchClass: "test-colorswatch"
   });
 
   let target = doc.querySelector("div");
   ok(target, "captain, we have the div");
   target.appendChild(frag);
 
   is(target.innerHTML, "var(--some-kind-of-green)", "CSS property correctly parsed");
 
   target.innerHTML = "";
 }
-
-function testParseHTMLAttribute(doc, parser) {
-  let attrib = "color:red; font-size: 12px; background-image: " +
-               "url(chrome://branding/content/about-logo.png)";
-  let frag = parser.parseHTMLAttribute(attrib, {
-    urlClass: "theme-link",
-    colorClass: "theme-color"
-  });
-
-  let target = doc.querySelector("div");
-  ok(target, "captain, we have the div");
-  target.appendChild(frag);
-
-  let expected = 'color:<span data-color="#F00"><span class="theme-color">#F00</span></span>; font-size: 12px; ' +
-                 'background-image: url("<a href="chrome://branding/content/about-logo.png" ' +
-                 'class="theme-link" ' +
-                 'target="_blank">chrome://branding/content/about-logo.png</a>")';
-
-  is(target.innerHTML, expected, "HTML Attribute correctly parsed");
-  target.innerHTML = "";
-}
-
-function testParseNonCssHTMLAttribute(doc, parser) {
-  let attrib = "someclass background someotherclass red";
-  let frag = parser.parseHTMLAttribute(attrib);
-
-  let target = doc.querySelector("div");
-  ok(target, "captain, we have the div");
-  target.appendChild(frag);
-
-  let expected = 'someclass background someotherclass red';
-
-  is(target.innerHTML, expected, "Non-CSS HTML Attribute correctly parsed");
-  target.innerHTML = "";
-}
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.properties
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.properties
@@ -213,16 +213,20 @@ breakpointMenuItem.deleteAll=Remove all 
 # editor when the loading process has started but there is no file to display
 # yet.
 loadingText=Loading\u2026
 
 # LOCALIZATION NOTE (errorLoadingText): The text that is displayed in the debugger
 # viewer when there is an error loading a file
 errorLoadingText=Error loading source:\n
 
+# LOCALIZATION NOTE (errorLoadingText2): The text that is displayed in the debugger
+# viewer when there is an error loading a file
+errorLoadingText2=Error loading this URL: %S
+
 # LOCALIZATION NOTE (addWatchExpressionText): The text that is displayed in the
 # watch expressions list to add a new item.
 addWatchExpressionText=Add watch expression
 
 # LOCALIZATION NOTE (addWatchExpressionButton): The button that is displayed in the
 # variables view popup.
 addWatchExpressionButton=Watch
 
@@ -315,9 +319,10 @@ functionSearchSeparatorLabel=←
 # The substitution parameter is the URL of the last paused window that must be
 # resumed first.
 resumptionOrderPanelTitle=There are one or more paused debuggers. Please resume the most-recently paused debugger first at: %S
 
 variablesViewOptimizedOut=(optimized away)
 variablesViewUninitialized=(uninitialized)
 variablesViewMissingArgs=(unavailable)
 
-evalGroupLabel=Evaluated Sources
\ No newline at end of file
+evalGroupLabel=Evaluated Sources
+anonymousSourcesLabel=Anonymous Sources
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/browser/locales/en-US/chrome/browser/devtools/jsonview.properties
@@ -0,0 +1,46 @@
+# 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/.
+
+# LOCALIZATION NOTE These strings are used in the JSON View tool
+# that is used to inspect application/json document types loaded
+# in the browser.
+
+# LOCALIZATION NOTE The correct localization of this file might be to keep it
+# in English, or another language commonly spoken among web developers.
+# You want to make that choice consistent across the developer tools.
+# A good criteria is the language in which you'd find the best documentation
+# on web development on the web.
+
+# LOCALIZATION NOTE (jsonViewer.tab.JSON, jsonViewer.tab.RawData,
+# jsonViewer.tab.Headers): Label for a panel tab.
+jsonViewer.tab.JSON=JSON
+jsonViewer.tab.RawData=Raw Data
+jsonViewer.tab.Headers=Headers
+
+# LOCALIZATION NOTE (jsonViewer.responseHeaders, jsonViewer.requestHeaders):
+# Label for header groups within the 'Headers' panel.
+jsonViewer.responseHeaders=Response Headers
+jsonViewer.requestHeaders=Request Headers
+
+# LOCALIZATION NOTE (jsonViewer.Save): Label for save command
+jsonViewer.Save=Save
+
+# LOCALIZATION NOTE (jsonViewer.Copy): Label for clipboard copy command
+jsonViewer.Copy=Copy
+
+# LOCALIZATION NOTE (jsonViewer.PrettyPrint): Label for JSON
+# pretty print action button.
+jsonViewer.PrettyPrint=Pretty Print
+
+# LOCALIZATION NOTE (jsonViewer.reps.more): Label used in arrays
+# that have more items than displayed.
+jsonViewer.reps.more=more...
+
+# LOCALIZATION NOTE (jsonViewer.filterJSON): Label used in search box
+# at the top right cornder of the JSON Viewer.
+jsonViewer.filterJSON=Filter JSON
+
+# LOCALIZATION NOTE (jsonViewer.reps.reference): Label used for cycle
+# references in an array.
+jsonViewer.reps.reference=Cycle Reference
--- a/browser/locales/en-US/chrome/browser/loop/loop.properties
+++ b/browser/locales/en-US/chrome/browser/loop/loop.properties
@@ -7,24 +7,28 @@
 ## LOCALIZATION NOTE(clientShortname2): This should not be localized and
 ## should remain "Firefox Hello" for all locales.
 clientShortname2=Firefox Hello
 clientSuperShortname=Hello
 
 rooms_tab_button_tooltip=Conversations
 contacts_tab_button_tooltip=Contacts
 
-## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two):
+## LOCALIZATION_NOTE(sign_in_again_title_line_one, sign_in_again_title_line_two2):
 ## These are displayed together at the top of the panel when a user is needed to
-## sign-in again. The first "line_one" is slightly bigger font that "line_two",
-## hence the separation.
+## sign-in again. The emphesis is on the first line to get the user to sign-in again,
+## and this is displayed in slightly larger font. Please arrange as necessary for
+## your locale.
+## {{clientShortname2}} will be replaced by the brand name for either string.
 sign_in_again_title_line_one=Please sign in again
-sign_in_again_title_line_two=to continue using Firefox Hello
+sign_in_again_title_line_two2=to continue using {{clientShortname2}}
 sign_in_again_button=Sign In
-sign_in_again_use_as_guest_button=Use Hello as a Guest
+## LOCALIZATION_NOTE(sign_in_again_use_as_guest_button2): {{clientSuperShortname}}
+## will be replaced by the super short brandname.
+sign_in_again_use_as_guest_button2=Use {{clientSuperShortname}} as a Guest
 
 ## LOCALIZATION_NOTE(first_time_experience.title): clientShortname will be
 ## replaced by the brand name
 first_time_experience_title={{clientShortname}} — Join the conversation
 first_time_experience_button_label=Get Started
 
 invite_header_text=Invite someone to join you.
 
deleted file mode 100644
--- a/browser/locales/en-US/chrome/browser/syncProgress.dtd
+++ /dev/null
@@ -1,15 +0,0 @@
-<!-- This Source Code Form is subject to the terms of the Mozilla Public
-   - License, v. 2.0. If a copy of the MPL was not distributed with this
-   - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
-
-<!ENTITY % brandDTD
-    SYSTEM "chrome://branding/locale/brand.dtd">
-    %brandDTD;
-
-<!-- These strings are used in the sync progress upload page -->
-<!ENTITY syncProgress.pageTitle    "Your First Sync">
-<!ENTITY syncProgress.textBlurb    "Your data is now being encrypted and uploaded in the background. You can close this tab and continue using &brandShortName;.">
-<!ENTITY syncProgress.closeButton  "Close">
-<!ENTITY syncProgress.logoAltText  "&brandShortName; logo">
-<!ENTITY syncProgress.diffText     "&brandShortName; will now automatically sync in the background.  You can close this tab and continue using &brandShortName;.">
-
--- a/browser/locales/jar.mn
+++ b/browser/locales/jar.mn
@@ -15,17 +15,16 @@
     locale/browser/aboutHome.dtd                   (%chrome/browser/aboutHome.dtd)
     locale/browser/accounts.properties             (%chrome/browser/accounts.properties)
 #ifdef MOZ_SERVICES_HEALTHREPORT
     locale/browser/aboutHealthReport.dtd           (%chrome/browser/aboutHealthReport.dtd)
 #endif
     locale/browser/aboutSessionRestore.dtd         (%chrome/browser/aboutSessionRestore.dtd)
     locale/browser/aboutTabCrashed.dtd             (%chrome/browser/aboutTabCrashed.dtd)
 #ifdef MOZ_SERVICES_SYNC
-    locale/browser/syncProgress.dtd                (%chrome/browser/syncProgress.dtd)
     locale/browser/syncCustomize.dtd               (%chrome/browser/syncCustomize.dtd)
     locale/browser/aboutSyncTabs.dtd               (%chrome/browser/aboutSyncTabs.dtd)
 #endif
     locale/browser/browser.dtd                     (%chrome/browser/browser.dtd)
     locale/browser/baseMenuOverlay.dtd             (%chrome/browser/baseMenuOverlay.dtd)
     locale/browser/browser.properties              (%chrome/browser/browser.properties)
     locale/browser/customizableui/customizableWidgets.properties (%chrome/browser/customizableui/customizableWidgets.properties)
     locale/browser/devtools/animationinspector.dtd    (%chrome/browser/devtools/animationinspector.dtd)
@@ -41,16 +40,17 @@
     locale/browser/devtools/shadereditor.dtd          (%chrome/browser/devtools/shadereditor.dtd)
     locale/browser/devtools/shadereditor.properties   (%chrome/browser/devtools/shadereditor.properties)
     locale/browser/devtools/canvasdebugger.dtd        (%chrome/browser/devtools/canvasdebugger.dtd)
     locale/browser/devtools/canvasdebugger.properties (%chrome/browser/devtools/canvasdebugger.properties)
     locale/browser/devtools/webaudioeditor.dtd          (%chrome/browser/devtools/webaudioeditor.dtd)
     locale/browser/devtools/webaudioeditor.properties   (%chrome/browser/devtools/webaudioeditor.properties)
     locale/browser/devtools/webconsole.properties     (%chrome/browser/devtools/webconsole.properties)
     locale/browser/devtools/inspector.properties      (%chrome/browser/devtools/inspector.properties)
+    locale/browser/devtools/jsonview.properties       (%chrome/browser/devtools/jsonview.properties)
     locale/browser/devtools/tilt.properties           (%chrome/browser/devtools/tilt.properties)
     locale/browser/devtools/shared.properties         (%chrome/browser/devtools/shared.properties)
     locale/browser/devtools/scratchpad.properties     (%chrome/browser/devtools/scratchpad.properties)
     locale/browser/devtools/scratchpad.dtd            (%chrome/browser/devtools/scratchpad.dtd)
     locale/browser/devtools/storage.properties        (%chrome/browser/devtools/storage.properties)
     locale/browser/devtools/styleeditor.properties    (%chrome/browser/devtools/styleeditor.properties)
     locale/browser/devtools/styleeditor.dtd           (%chrome/browser/devtools/styleeditor.dtd)
     locale/browser/devtools/styleinspector.dtd        (%chrome/browser/devtools/styleinspector.dtd)
--- a/browser/themes/linux/jar.mn
+++ b/browser/themes/linux/jar.mn
@@ -418,17 +418,16 @@ browser.jar:
   skin/classic/browser/sync-mobileIcon.png
   skin/classic/browser/sync-notification-24.png
   skin/classic/browser/syncProgress-menuPanel.png
   skin/classic/browser/syncProgress-toolbar.png
   skin/classic/browser/syncProgress-toolbar-inverted.png
   skin/classic/browser/syncSetup.css
   skin/classic/browser/syncCommon.css
   skin/classic/browser/syncQuota.css
-  skin/classic/browser/syncProgress.css
   skin/classic/browser/syncProgress-horizontalbar.png
 #endif
   skin/classic/browser/notification-pluginNormal.png  (../shared/plugins/notification-pluginNormal.png)
   skin/classic/browser/notification-pluginAlert.png   (../shared/plugins/notification-pluginAlert.png)
   skin/classic/browser/notification-pluginBlocked.png (../shared/plugins/notification-pluginBlocked.png)
   skin/classic/browser/devtools/tooltip/arrow-horizontal-dark.png   (../shared/devtools/tooltip/arrow-horizontal-dark.png)
   skin/classic/browser/devtools/tooltip/arrow-horizontal-dark@2x.png   (../shared/devtools/tooltip/arrow-horizontal-dark@2x.png)
   skin/classic/browser/devtools/tooltip/arrow-vertical-dark.png   (../shared/devtools/tooltip/arrow-vertical-dark.png)
--- a/browser/themes/linux/searchbar.css
+++ b/browser/themes/linux/searchbar.css
@@ -114,28 +114,32 @@ searchbar[oneoffui] .search-go-button:-m
 }
 
 
 .search-panel-current-engine {
   border-top: none !important;
   -moz-box-align: center;
 }
 
-.search-panel-current-engine:not([showonlysettings]) {
+.search-panel-current-engine {
   border-bottom: 1px solid #ccc;
 }
 
 .search-panel-header {
   font-weight: normal;
   background-color: rgb(245, 245, 245);
   border-top: 1px solid #ccc;
   padding: 3px 5px;
   color: #666;
 }
 
+.search-panel-tree[collapsed=true] + .search-panel-header {
+  border-top: none;
+}
+
 .search-panel-current-input > label {
   margin: 0 0 !important;
 }
 
 .search-panel-input-value {
   color: black;
 }
 
deleted file mode 100644
--- a/browser/themes/linux/syncProgress.css
+++ /dev/null
@@ -1,46 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-@import url(chrome://global/skin/inContentUI.css);
-
-:root {
-  height: 100%;
-  width: 100%;
-  padding: 0;
-}
-
-body {
-  margin: 0;
-  padding: 0 2em;
-}
-
-#floatingBox {
-  margin: 4em auto;
-  max-width: 40em;
-  min-width: 23em;
-  padding: 1em 1.5em;
-  position: relative;
-  text-align: center;
-}
-
-#successLogo {
-  margin: 1em 2em;
-}
-
-#loadingText {
-  margin: 2em 6em;
-}
-
-#progressBar {
-  margin: 2em 10em;
-}
-
-#uploadProgressBar{
-  width: 100%;
-}
-
-#bottomRow {
-  margin-top: 2em;
-  padding: 0;
-  text-align: end;
-}
--- a/browser/themes/osx/jar.mn
+++ b/browser/themes/osx/jar.mn
@@ -549,17 +549,16 @@ browser.jar:
   skin/classic/browser/sync-desktopIcon.png
   skin/classic/browser/sync-horizontalbar.png
   skin/classic/browser/sync-horizontalbar@2x.png
   skin/classic/browser/sync-mobileIcon.png
   skin/classic/browser/sync-notification-24.png
   skin/classic/browser/syncSetup.css
   skin/classic/browser/syncCommon.css
   skin/classic/browser/syncQuota.css
-  skin/classic/browser/syncProgress.css
   skin/classic/browser/syncProgress-horizontalbar.png
   skin/classic/browser/syncProgress-horizontalbar@2x.png
   skin/classic/browser/syncProgress-menuPanel.png
   skin/classic/browser/syncProgress-menuPanel@2x.png
   skin/classic/browser/syncProgress-toolbar.png
   skin/classic/browser/syncProgress-toolbar@2x.png
   skin/classic/browser/syncProgress-toolbar-inverted.png
   skin/classic/browser/syncProgress-toolbar-inverted@2x.png
--- a/browser/themes/osx/searchbar.css
+++ b/browser/themes/osx/searchbar.css
@@ -137,30 +137,34 @@ searchbar[oneoffui] .search-go-button:-m
   }
 }
 
 .search-panel-current-engine {
   border-top: none !important;
   border-radius: 4px 4px 0 0;
 }
 
-.search-panel-current-engine:not([showonlysettings]) {
+.search-panel-current-engine {
   border-bottom: 1px solid #ccc;
 }
 
 .search-panel-header {
   font-size: 10px;
   font-weight: normal;
   background-color: rgb(245, 245, 245);
   border-top: 1px solid #ccc;
   margin: 0;
   padding: 3px 6px;
   color: #666;
 }
 
+.search-panel-tree[collapsed=true] + .search-panel-header {
+  border-top: none;
+}
+
 .search-panel-current-input > label {
   margin: 0 0 !important;
 }
 
 .search-panel-input-value {
   color: black;
 }
 
deleted file mode 100644
--- a/browser/themes/osx/syncProgress.css
+++ /dev/null
@@ -1,46 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-@import url(chrome://global/skin/inContentUI.css);
-
-:root {
-  height: 100%;
-  width: 100%;
-  padding: 0;
-}
-
-body {
-  margin: 0;
-  padding: 0 2em;
-}
-
-#floatingBox {
-  margin: 4em auto;
-  max-width: 40em;
-  min-width: 23em;
-  padding: 1em 1.5em;
-  position: relative;
-  text-align: center;
-}
-
-#successLogo {
-  margin: 1em 2em;
-}
-
-#loadingText {
-  margin: 2em 6em;
-}
-
-#progressBar {
-  margin: 2em 10em;
-}
-
-#uploadProgressBar{
-  width: 100%;
-}
-
-#bottomRow {
-  margin-top: 2em;
-  padding: 0;
-  text-align: end;
-}
--- a/browser/themes/windows/jar.mn
+++ b/browser/themes/windows/jar.mn
@@ -491,17 +491,16 @@ browser.jar:
         skin/classic/browser/sync-desktopIcon.png
         skin/classic/browser/sync-horizontalbar.png
         skin/classic/browser/sync-horizontalbar-XPVista7.png
         skin/classic/browser/sync-mobileIcon.png
         skin/classic/browser/sync-notification-24.png
         skin/classic/browser/syncSetup.css
         skin/classic/browser/syncCommon.css
         skin/classic/browser/syncQuota.css
-        skin/classic/browser/syncProgress.css
         skin/classic/browser/syncProgress-horizontalbar.png
         skin/classic/browser/syncProgress-horizontalbar-XPVista7.png
         skin/classic/browser/syncProgress-menuPanel.png
         skin/classic/browser/syncProgress-toolbar.png
         skin/classic/browser/syncProgress-toolbar@2x.png
         skin/classic/browser/syncProgress-toolbar-inverted.png
         skin/classic/browser/syncProgress-toolbar-inverted@2x.png
         skin/classic/browser/syncProgress-toolbar-XPVista7.png
--- a/browser/themes/windows/searchbar.css
+++ b/browser/themes/windows/searchbar.css
@@ -126,29 +126,33 @@ searchbar[oneoffui] .search-go-button:-m
 }
 
 
 .search-panel-current-engine {
   border-top: none !important;
   -moz-box-align: center;
 }
 
-.search-panel-current-engine:not([showonlysettings]) {
+.search-panel-current-engine {
   border-bottom: 1px solid #ccc;
 }
 
 .search-panel-header {
   font-weight: normal;
   background-color: rgb(245, 245, 245);
   border-top: 1px solid #ccc;
   margin: 0;
   padding: 3px 6px;
   color: #666;
 }
 
+.search-panel-tree[collapsed=true] + .search-panel-header {
+  border-top: none;
+}
+
 .search-panel-current-input > label {
   margin: 0 0 !important;
 }
 
 .search-panel-input-value {
   color: black;
 }
 
deleted file mode 100644
--- a/browser/themes/windows/syncProgress.css
+++ /dev/null
@@ -1,46 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-@import url(chrome://global/skin/inContentUI.css);
-
-:root {
-  height: 100%;
-  width: 100%;
-  padding: 0;
-}
-
-body {
-  margin: 0;
-  padding: 0 2em;
-}
-
-#floatingBox {
-  margin: 4em auto;
-  max-width: 40em;
-  min-width: 23em;
-  padding: 1em 1.5em;
-  position: relative;
-  text-align: center;
-}
-
-#successLogo {
-  margin: 1em 2em;
-}
-
-#loadingText {
-  margin: 2em 6em;
-}
-
-#progressBar {
-  margin: 2em 10em;
-}
-
-#uploadProgressBar{
-  width: 100%;
-}
-
-#bottomRow {
-  margin-top: 2em;
-  padding: 0;
-  text-align: end;
-}
--- a/config/milestone.txt
+++ b/config/milestone.txt
@@ -5,9 +5,9 @@
 #    x.x.x.x
 #    x.x.x+
 #
 # Referenced by milestone.py.
 # Hopefully I'll be able to automate replacement of *all*
 # hardcoded milestones in the tree from these two files.
 #--------------------------------------------------------
 
-40.0a1
+41.0a1
--- a/dom/bluetooth/BluetoothInterface.cpp
+++ b/dom/bluetooth/BluetoothInterface.cpp
@@ -133,31 +133,22 @@ BluetoothInterface::GetInstance()
   /* We pick a default backend from the available ones. The options are
    * ordered by preference. If a backend is supported but not available
    * on the current system, we pick the next one. The selected default
    * can be overriden manually by storing the respective string in the
    * system property 'ro.moz.bluetooth.backend'.
    */
 
   static const char* const sDefaultBackend[] = {
-#if MOZ_B2G_BT_API_V2
-#ifdef MOZ_B2G_BT_BLUEDROID
-    "bluedroid",
-#endif
-#ifdef MOZ_B2G_BT_DAEMON
-    "bluetoothd",
-#endif
-#else
 #ifdef MOZ_B2G_BT_DAEMON
     "bluetoothd",
 #endif
 #ifdef MOZ_B2G_BT_BLUEDROID
     "bluedroid",
 #endif
-#endif
     nullptr // no default backend; must be final element in array
   };
 
   const char* defaultBackend;
 
   for (size_t i = 0; i < MOZ_ARRAY_LENGTH(sDefaultBackend); ++i) {
 
     /* select current backend */
--- a/dom/bluetooth/BluetoothRilListener.cpp
+++ b/dom/bluetooth/BluetoothRilListener.cpp
@@ -198,26 +198,33 @@ nsresult
 TelephonyListener::HandleCallInfo(nsITelephonyCallInfo* aInfo, bool aSend)
 {
   BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
   NS_ENSURE_TRUE(hfp, NS_ERROR_FAILURE);
 
   uint32_t callIndex;
   uint16_t callState;
   nsAutoString number;
+  nsAutoString disconnectedReason;
   bool isOutgoing;
   bool isConference;
 
   aInfo->GetCallIndex(&callIndex);
   aInfo->GetCallState(&callState);
   aInfo->GetNumber(number);
+  aInfo->GetDisconnectedReason(disconnectedReason);
   aInfo->GetIsOutgoing(&isOutgoing);
   aInfo->GetIsConference(&isConference);
 
-  hfp->HandleCallStateChanged(callIndex, callState, EmptyString(), number,
+  // The disconnectedReason of a disconnected call must be nonempty no matter
+  // the call is disconnected for a normal reason or an error.
+  MOZ_ASSERT((callState != nsITelephonyService::CALL_STATE_DISCONNECTED ||
+              !disconnectedReason.IsEmpty()),
+             "disconnectedReason of an disconnected call must be nonempty.");
+  hfp->HandleCallStateChanged(callIndex, callState, disconnectedReason, number,
                               isOutgoing, isConference, aSend);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelephonyListener::CallStateChanged(uint32_t aLength,
                                     nsITelephonyCallInfo** aAllInfo)
 {
@@ -229,40 +236,16 @@ TelephonyListener::CallStateChanged(uint
 
 NS_IMETHODIMP
 TelephonyListener::EnumerateCallState(nsITelephonyCallInfo* aInfo)
 {
   return HandleCallInfo(aInfo, false);
 }
 
 NS_IMETHODIMP
-TelephonyListener::NotifyError(uint32_t aServiceId,
-                               int32_t aCallIndex,
-                               const nsAString& aError)
-{
-  BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
-  NS_ENSURE_TRUE(hfp, NS_ERROR_FAILURE);
-
-  if (aCallIndex > 0) {
-    // In order to not miss any related call state transition.
-    // It's possible that 3G network signal lost for unknown reason.
-    // If a call is released abnormally, NotifyError() will be called,
-    // instead of CallStateChanged(). We need to reset the call array state
-    // via setting CALL_STATE_DISCONNECTED
-    hfp->HandleCallStateChanged(aCallIndex,
-                                nsITelephonyService::CALL_STATE_DISCONNECTED,
-                                aError, EmptyString(), false, false, true);
-    BT_WARNING("Reset the call state due to call transition ends abnormally");
-  }
-
-  BT_WARNING(NS_ConvertUTF16toUTF8(aError).get());
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 TelephonyListener::ConferenceCallStateChanged(uint16_t aCallState)
 {
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelephonyListener::EnumerateCallStateComplete()
 {
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.cpp
@@ -718,16 +718,29 @@ BluetoothA2dpManager::HandleSinkProperty
       mDeviceAddress.Truncate();
       OnDisconnect(EmptyString());
       break;
     default:
       break;
   }
 }
 
+/*
+ * Reset connection state to DISCONNECTED to handle backend error. The state
+ * change triggers UI status bar update as ordinary bluetooth turn-off sequence.
+ */
+void
+BluetoothA2dpManager::HandleBackendError()
+{
+  if (mSinkState != SinkState::SINK_DISCONNECTED) {
+    ConnectionStateNotification(A2DP_CONNECTION_STATE_DISCONNECTED,
+      mDeviceAddress);
+  }
+}
+
 void
 BluetoothA2dpManager::NotifyConnectionStatusChanged()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   // Notify Gecko observers
   nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
   NS_ENSURE_TRUE_VOID(obs);
--- a/dom/bluetooth/bluedroid/BluetoothA2dpManager.h
+++ b/dom/bluetooth/bluedroid/BluetoothA2dpManager.h
@@ -58,16 +58,17 @@ public:
   void GetAlbum(nsAString& aAlbum);
   uint32_t GetDuration();
   ControlPlayStatus GetPlayStatus();
   uint32_t GetPosition();
   uint64_t GetMediaNumber();
   uint64_t GetTotalMediaNumber();
   void GetTitle(nsAString& aTitle);
   void GetArtist(nsAString& aArtist);
+  void HandleBackendError();
 
 protected:
   virtual ~BluetoothA2dpManager();
 
 private:
   class CleanupA2dpResultHandler;
   class CleanupA2dpResultHandlerRunnable;
   class CleanupAvrcpResultHandler;
--- a/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
+++ b/dom/bluetooth/bluedroid/BluetoothServiceBluedroid.cpp
@@ -2477,18 +2477,19 @@ BluetoothServiceBluedroid::AdapterProper
   }
 #endif
 }
 
 /**
  * RemoteDevicePropertiesNotification will be called
  *
  *   (1) automatically by Bluedroid when BT is turning on, or
- *   (2) as result of GetRemoteDeviceProperties, or
- *   (3) as result of GetRemoteServices.
+ *   (2) as result of remote device properties update during discovery, or
+ *   (3) as result of GetRemoteDeviceProperties, or
+ *   (4) as result of GetRemoteServices.
  */
 void
 BluetoothServiceBluedroid::RemoteDevicePropertiesNotification(
   BluetoothStatus aStatus, const nsAString& aBdAddr,
   int aNumProperties, const BluetoothProperty* aProperties)
 {
 #ifdef MOZ_B2G_BT_API_V2
   MOZ_ASSERT(NS_IsMainThread());
@@ -2649,18 +2650,28 @@ BluetoothServiceBluedroid::RemoteDeviceP
     }
   }
 
   // BlueDroid wouldn't notify the status of connection, therefore, query the
   // connection state and append to properties array
   BT_APPEND_NAMED_VALUE(props, "Connected", IsConnected(aBdAddr));
 
   if (sRequestedDeviceCountArray.IsEmpty()) {
-    // This is possible because the callback would be called after turning
-    // Bluetooth on.
+    /**
+     * This is possible when
+     *
+     *  (1) the callback is called after Bluetooth is turned on, or
+     *  (2) remote device properties get updated during discovery.
+     *
+     * For (2), fire 'devicefound' again to update device name.
+     * See bug 1076553 for more information.
+     */
+    DistributeSignal(BluetoothSignal(NS_LITERAL_STRING("DeviceFound"),
+                                     NS_LITERAL_STRING(KEY_ADAPTER),
+                                     BluetoothValue(props)));
     return;
   }
 
   // Use address as the index
   sRemoteDevicesPack.AppendElement(
     BluetoothNamedValue(nsString(aBdAddr), props));
 
   if (--sRequestedDeviceCountArray[0] == 0) {
@@ -3124,27 +3135,40 @@ BluetoothServiceBluedroid::EnergyInfoNot
   // FIXME: This will be implemented in the later patchset
 }
 #endif
 
 void
 BluetoothServiceBluedroid::BackendErrorNotification(bool aCrashed)
 {
   MOZ_ASSERT(NS_IsMainThread());
- // Recovery step 2 stop bluetooth
- if (aCrashed) {
-  BT_LOGR("Set aRestart = true");
+
+  if (!aCrashed) {
+    return;
+  }
+
+  /*
+   * Reset following profile manager states for unexpected backend crash.
+   * - HFP: connection state and audio state
+   * - A2DP: connection state
+   */
+  BluetoothHfpManager* hfp = BluetoothHfpManager::Get();
+  NS_ENSURE_TRUE_VOID(hfp);
+  hfp->HandleBackendError();
+  BluetoothA2dpManager* a2dp = BluetoothA2dpManager::Get();
+  NS_ENSURE_TRUE_VOID(a2dp);
+  a2dp->HandleBackendError();
+
   sIsRestart = true;
-  BT_LOGR("Reocvery step2: stop bluetooth");
+  BT_LOGR("Recovery step2: stop bluetooth");
 #ifdef MOZ_B2G_BT_API_V2
   StopBluetooth(false, nullptr);
 #else
   StopBluetooth(false);
 #endif
- }
 }
 
 void
 BluetoothServiceBluedroid::CompleteToggleBt(bool aEnabled)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (sIsRestart && !aEnabled && sIsFirstTimeToggleOffBt) {
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.cpp
@@ -1116,16 +1116,36 @@ BluetoothHfpManager::ToggleCalls()
   MOZ_ASSERT(mPhoneType == PhoneType::CDMA);
 
   // Toggle acitve and held calls
   mCdmaSecondCall.mState = (mCdmaSecondCall.IsActive()) ?
                              nsITelephonyService::CALL_STATE_HELD :
                              nsITelephonyService::CALL_STATE_CONNECTED;
 }
 
+/*
+ * Reset connection state and audio state to DISCONNECTED to handle backend
+ * error. The state change triggers UI status bar update as ordinary bluetooth
+ * turn-off sequence.
+ */
+void
+BluetoothHfpManager::HandleBackendError()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (mConnectionState != HFP_CONNECTION_STATE_DISCONNECTED) {
+    ConnectionStateNotification(HFP_CONNECTION_STATE_DISCONNECTED,
+      mDeviceAddress);
+  }
+
+  if (mAudioState != HFP_AUDIO_STATE_DISCONNECTED) {
+    AudioStateNotification(HFP_AUDIO_STATE_DISCONNECTED, mDeviceAddress);
+  }
+}
+
 class BluetoothHfpManager::ConnectAudioResultHandler final
   : public BluetoothHandsfreeResultHandler
 {
 public:
   void OnError(BluetoothStatus aStatus) override
   {
     BT_WARNING("BluetoothHandsfreeInterface::ConnectAudio failed: %d",
                (int)aStatus);
--- a/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
+++ b/dom/bluetooth/bluedroid/hfp/BluetoothHfpManager.h
@@ -104,16 +104,19 @@ public:
   void HandleVoiceConnectionChanged(uint32_t aClientId);
 
   // CDMA-specific functions
   void UpdateSecondNumber(const nsAString& aNumber);
   void AnswerWaitingCall();
   void IgnoreWaitingCall();
   void ToggleCalls();
 
+  // Handle unexpected backend crash
+  void HandleBackendError();
+
   //
   // Bluetooth notifications
   //
 
   void ConnectionStateNotification(BluetoothHandsfreeConnectionState aState,
                                    const nsAString& aBdAddress) override;
   void AudioStateNotification(BluetoothHandsfreeAudioState aState,
                               const nsAString& aBdAddress) override;
--- a/dom/bluetooth/bluetooth2/ipc/BluetoothMessageUtils.h
+++ b/dom/bluetooth/bluetooth2/ipc/BluetoothMessageUtils.h
@@ -116,22 +116,24 @@ template <>
 struct ParamTraits<mozilla::dom::bluetooth::BluetoothGattCharAttribute>
 {
   typedef mozilla::dom::bluetooth::BluetoothGattCharAttribute paramType;
 
   static void Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mId);
     WriteParam(aMsg, aParam.mProperties);
+    WriteParam(aMsg, aParam.mWriteType);
   }
 
   static bool Read(const Message* aMsg, void** aIter, paramType* aResult)
   {
     if (!ReadParam(aMsg, aIter, &(aResult->mId)) ||
-        !ReadParam(aMsg, aIter, &(aResult->mProperties))) {
+        !ReadParam(aMsg, aIter, &(aResult->mProperties)) ||
+        !ReadParam(aMsg, aIter, &(aResult->mWriteType))) {
       return false;
     }
 
     return true;
   }
 };
 } // namespace IPC
 
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -2938,149 +2938,8 @@ ContentChild::RecvInvokeDragSession(nsTA
             blobImpl->GetMozFullPathInternal(path, result);
             if (result.Failed()) {
               variant->SetAsISupports(blobImpl);
             } else {
               nsCOMPtr<nsIFile> file;
               NS_NewNativeLocalFile(NS_ConvertUTF16toUTF8(path), true, getter_AddRefs(file));
               variant->SetAsISupports(file);
             }
-          } else {
-            continue;
-          }
-          dataTransfer->SetDataWithPrincipal(NS_ConvertUTF8toUTF16(item.flavor()),
-                                             variant, i,
-                                             nsContentUtils::GetSystemPrincipal());
-        }
-      }
-      session->SetDataTransfer(dataTransfer);
-    }
-  }
-  return true;
-}
-
-bool
-ContentChild::RecvEndDragSession(const bool& aDoneDrag,
-                                 const bool& aUserCancelled)
-{
-  nsCOMPtr<nsIDragService> dragService =
-    do_GetService("@mozilla.org/widget/dragservice;1");
-  if (dragService) {
-    if (aUserCancelled) {
-      nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
-      if (dragSession) {
-        dragSession->UserCancelled();
-      }
-    }
-    dragService->EndDragSession(aDoneDrag);
-  }
-  return true;
-}
-
-} // namespace dom
-} // namespace mozilla
-
-extern "C" {
-
-#if defined(MOZ_NUWA_PROCESS)
-NS_EXPORT void
-GetProtoFdInfos(NuwaProtoFdInfo* aInfoList,
-                size_t aInfoListSize,
-                size_t* aInfoSize)
-{
-    size_t i = 0;
-
-    mozilla::dom::ContentChild* content =
-        mozilla::dom::ContentChild::GetSingleton();
-    aInfoList[i].protoId = content->GetProtocolId();
-    aInfoList[i].originFd =
-        content->GetTransport()->GetFileDescriptor();
-    i++;
-
-    IToplevelProtocol* actors[NUWA_TOPLEVEL_MAX];
-    size_t count = content->GetOpenedActorsUnsafe(actors, ArrayLength(actors));
-    for (size_t j = 0; j < count; j++) {
-        IToplevelProtocol* actor = actors[j];
-        if (i >= aInfoListSize) {
-            NS_RUNTIMEABORT("Too many top level protocols!");
-        }
-
-        aInfoList[i].protoId = actor->GetProtocolId();
-        aInfoList[i].originFd =
-            actor->GetTransport()->GetFileDescriptor();
-        i++;
-    }
-
-    if (i > NUWA_TOPLEVEL_MAX) {
-        NS_RUNTIMEABORT("Too many top level protocols!");
-    }
-    *aInfoSize = i;
-}
-
-class RunAddNewIPCProcess : public nsRunnable
-{
-public:
-    RunAddNewIPCProcess(pid_t aPid,
-                        nsTArray<mozilla::ipc::ProtocolFdMapping>& aMaps)
-        : mPid(aPid)
-    {
-        mMaps.SwapElements(aMaps);
-    }
-
-    NS_IMETHOD Run()
-    {
-        mozilla::dom::ContentChild::GetSingleton()->
-            SendAddNewProcess(mPid, mMaps);
-
-        MOZ_ASSERT(sNuwaForking);
-        sNuwaForking = false;
-
-        return NS_OK;
-    }
-
-private:
-    pid_t mPid;
-    nsTArray<mozilla::ipc::ProtocolFdMapping> mMaps;
-};
-
-/**
- * AddNewIPCProcess() is called by Nuwa process to tell the parent
- * process that a new process is created.
- *
- * In the newly created process, ResetContentChildTransport() is called to
- * reset fd for the IPC Channel and the session.
- */
-NS_EXPORT void
-AddNewIPCProcess(pid_t aPid, NuwaProtoFdInfo* aInfoList, size_t aInfoListSize)
-{
-    nsTArray<mozilla::ipc::ProtocolFdMapping> maps;
-
-    for (size_t i = 0; i < aInfoListSize; i++) {
-        int _fd = aInfoList[i].newFds[NUWA_NEWFD_PARENT];
-        mozilla::ipc::FileDescriptor fd(_fd);
-        mozilla::ipc::ProtocolFdMapping map(aInfoList[i].protoId, fd);
-        maps.AppendElement(map);
-    }
-
-    nsRefPtr<RunAddNewIPCProcess> runner = new RunAddNewIPCProcess(aPid, maps);
-    NS_DispatchToMainThread(runner);
-}
-
-NS_EXPORT void
-OnNuwaProcessReady()
-{
-    mozilla::dom::ContentChild* content =
-        mozilla::dom::ContentChild::GetSingleton();
-    content->SendNuwaReady();
-}
-
-NS_EXPORT void
-AfterNuwaFork()
-{
-    SetCurrentProcessPrivileges(base::PRIVILEGES_DEFAULT);
-#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
-    mozilla::SandboxEarlyInit(XRE_GetProcessType(), /* isNuwa: */ false);
-#endif
-}
-
-#endif // MOZ_NUWA_PROCESS
-
-}
--- a/dom/messages/SystemMessageInternal.js
+++ b/dom/messages/SystemMessageInternal.js
@@ -35,16 +35,17 @@ try {
   kMaxPendingMessages =
     Services.prefs.getIntPref("dom.messages.maxPendingMessages");
 } catch(e) {
   // getIntPref throws when the pref is not set.
   kMaxPendingMessages = 5;
 }
 
 const kMessages =["SystemMessageManager:GetPendingMessages",
+                  "SystemMessageManager:HasPendingMessages",
                   "SystemMessageManager:Register",
                   "SystemMessageManager:Unregister",
                   "SystemMessageManager:Message:Return:OK",
                   "SystemMessageManager:AskReadyToRegister",
                   "SystemMessageManager:HandleMessagesDone",
                   "SystemMessageManager:HandleMessageDone",
                   "child-process-shutdown"];
 
@@ -414,16 +415,17 @@ SystemMessageInternal.prototype = {
     let msg = aMessage.json;
 
     // To prevent the hacked child process from sending commands to parent
     // to manage system messages, we need to check its manifest URL.
     if (["SystemMessageManager:Register",
          // TODO: fix bug 988142 to re-enable.
          // "SystemMessageManager:Unregister",
          "SystemMessageManager:GetPendingMessages",
+         "SystemMessageManager:HasPendingMessages",
          "SystemMessageManager:Message:Return:OK",
          "SystemMessageManager:HandleMessagesDone",
          "SystemMessageManager:HandleMessageDone"].indexOf(aMessage.name) != -1) {
       if (!aMessage.target.assertContainApp(msg.manifestURL)) {
         debug("Got message from a child process containing illegal manifest URL.");
         return null;
       }
     }
@@ -514,16 +516,35 @@ SystemMessageInternal.prototype = {
                 .sendAsyncMessage("SystemMessageManager:GetPendingMessages:Return",
                                   { type: msg.type,
                                     manifestURL: msg.manifestURL,
                                     pageURL: msg.pageURL,
                                     msgQueue: pendingMessages });
         this._refreshCacheInternal(aMessage.target, msg.manifestURL);
         break;
       }
+      case "SystemMessageManager:HasPendingMessages":
+      {
+        debug("received SystemMessageManager:HasPendingMessages " + msg.type +
+              " for " + msg.pageURL + " @ " + msg.manifestURL);
+
+        // NB: Sync message SystemMessageManager:HasPendingMessages
+        // should only be used by in-process app. For out-of-process
+        // app, SystemMessageCache should be used.
+
+        // This is a sync call used to return if a page has pending messages.
+        // Find the right page to get its corresponding pending messages.
+        let page = this._findPage(msg.type, msg.pageURL, msg.manifestURL);
+        if (!page) {
+          return false;
+        }
+
+        return page.pendingMessages.length != 0;
+        break;
+      }
       case "SystemMessageManager:Message:Return:OK":
       {
         debug("received SystemMessageManager:Message:Return:OK " + msg.type +
           " for " + msg.pageURL + " @ " + msg.manifestURL);
 
         // We need to clean up the pending message since the app has already
         // received it, thus avoiding the re-lanunched app handling it again.
         let page = this._findPage(msg.type, msg.pageURL, msg.manifestURL);
--- a/dom/messages/SystemMessageManager.js
+++ b/dom/messages/SystemMessageManager.js
@@ -189,16 +189,24 @@ SystemMessageManager.prototype = {
     }
 
     // If we have a handler for this type, we can't have any pending message.
     if (aType in this._dispatchers) {
       return false;
     }
 
 
+    // Use SystemMessageManager directly when we are in the same process.
+    if (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT) {
+      return cpmm.sendSyncMessage("SystemMessageManager:HasPendingMessages",
+                                  { type: aType,
+                                    pageURL: this._pageURL,
+                                    manifestURL: this._manifestURL })[0];
+    }
+
     /*
      * NB: If the system message is fired after we received the cache
      *     and before we registered the pageURL we will get false
      *     negative however this is unlikely and will do no harm.
      */
     let cache = Cc["@mozilla.org/system-message-cache;1"]
                   .getService(Ci.nsISystemMessageCache);
 
--- a/dom/system/gonk/RadioInterfaceLayer.js
+++ b/dom/system/gonk/RadioInterfaceLayer.js
@@ -2829,18 +2829,21 @@ DataCall.prototype = {
       // Notify the DISCONNECTED event immediately after network interface is
       // removed from requestedNetworkIfaces, to make the DataCall, shared or
       // not, to have the same behavior.
       Services.tm.currentThread.dispatch(() => {
         // Do not notify if state changed while this event was being dispatched,
         // the state probably was notified already or need not to be notified.
         if (networkInterface.state == RIL.GECKO_NETWORK_STATE_DISCONNECTED) {
           networkInterface.notifyRILNetworkInterface();
+
           // Clear link info after notifying NetworkManager.
-          this.resetLinkInfo();
+          if (this.requestedNetworkIfaces.length === 0) {
+            this.resetLinkInfo();
+          }
         }
       }, Ci.nsIEventTarget.DISPATCH_NORMAL);
     }
 
     // Only deactivate data call if no more network interface needs this
     // DataCall and if state is CONNECTED, for other states, we simply remove
     // the network interface from requestedNetworkIfaces.
     if (this.requestedNetworkIfaces.length > 0 ||
--- a/dom/telephony/Telephony.cpp
+++ b/dom/telephony/Telephony.cpp
@@ -162,18 +162,16 @@ Telephony::IsValidServiceId(uint32_t aSe
   return aServiceId < GetNumServices();
 }
 
 // static
 bool
 Telephony::IsActiveState(uint16_t aCallState) {
   return aCallState == nsITelephonyService::CALL_STATE_DIALING ||
       aCallState == nsITelephonyService::CALL_STATE_ALERTING ||
-      aCallState == nsITelephonyService::CALL_STATE_HOLDING ||
-      aCallState == nsITelephonyService::CALL_STATE_DISCONNECTING ||
       aCallState == nsITelephonyService::CALL_STATE_CONNECTED;
 }
 
 uint32_t
 Telephony::GetServiceId(const Optional<uint32_t>& aServiceId,
                         bool aGetIfActiveCall)
 {
   if (aServiceId.WasPassed()) {
@@ -372,20 +370,24 @@ Telephony::HandleCallInfo(nsITelephonyCa
     call->UpdateSwitchable(isSwitchable);
     call->UpdateMergeable(isMergeable);
 
     nsAutoString number;
     aInfo->GetNumber(number);
     nsRefPtr<TelephonyCallId> id = call->Id();
     id->UpdateNumber(number);
 
+    nsAutoString disconnectedReason;
+    aInfo->GetDisconnectedReason(disconnectedReason);
+
     // State changed.
     if (call->CallState() != callState) {
       if (callState == nsITelephonyService::CALL_STATE_DISCONNECTED) {
-        call->ChangeStateInternal(callState, true);
+        call->UpdateDisconnectedReason(disconnectedReason);
+        call->ChangeState(nsITelephonyService::CALL_STATE_DISCONNECTED);
         return NS_OK;
       }
 
       // We don't fire the statechange event on a call in conference here.
       // Instead, the event will be fired later in
       // TelephonyCallGroup::ChangeState(). Thus the sequence of firing the
       // statechange events is guaranteed: first on TelephonyCallGroup then on
       // individual TelephonyCall objects.
@@ -685,36 +687,16 @@ Telephony::SupplementaryServiceNotificat
       return NS_ERROR_UNEXPECTED;
   }
 
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
 NS_IMETHODIMP
-Telephony::NotifyError(uint32_t aServiceId,
-                       int32_t aCallIndex,
-                       const nsAString& aError)
-{
-  nsRefPtr<TelephonyCall> callToNotify =
-    GetCallFromEverywhere(aServiceId, aCallIndex);
-  if (!callToNotify) {
-    NS_ERROR("Don't call me with a bad call index!");
-    return NS_ERROR_UNEXPECTED;
-  }
-
-  // Set the call state to 'disconnected' and remove it from the calls list.
-  callToNotify->UpdateDisconnectedReason(aError);
-  callToNotify->NotifyError(aError);
-  callToNotify->ChangeState(nsITelephonyService::CALL_STATE_DISCONNECTED);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 Telephony::NotifyCdmaCallWaiting(uint32_t aServiceId, const nsAString& aNumber,
                                  uint16_t aNumberPresentation,
                                  const nsAString& aName,
                                  uint16_t aNamePresentation)
 {
   MOZ_ASSERT(mCalls.Length() == 1);
 
   nsRefPtr<TelephonyCall> callToNotify = mCalls[0];
--- a/dom/telephony/TelephonyCall.cpp
+++ b/dom/telephony/TelephonyCall.cpp
@@ -82,33 +82,18 @@ TelephonyCall::WrapObject(JSContext* aCx
   return TelephonyCallBinding::Wrap(aCx, this, aGivenProto);
 }
 
 void
 TelephonyCall::ChangeStateInternal(uint16_t aCallState, bool aFireEvents)
 {
   nsRefPtr<TelephonyCall> kungFuDeathGrip(this);
 
-  // Update the internal state.
   mCallState = aCallState;
-
-  // Indicate whether the external state have been changed.
-  bool externalStateChanged = true;
   switch (aCallState) {
-    // These states are used internally to mark this call is currently being
-    // controlled, and we should block consecutive requests of the same type
-    // according to these states.
-    case nsITelephonyService::CALL_STATE_CONNECTING:
-    case nsITelephonyService::CALL_STATE_DISCONNECTING:
-    case nsITelephonyService::CALL_STATE_HOLDING:
-    case nsITelephonyService::CALL_STATE_RESUMING:
-      externalStateChanged = false;
-      break;
-    // These states will be translated into literal strings which are used to
-    // show the current status of this call.
     case nsITelephonyService::CALL_STATE_DIALING:
       mState.AssignLiteral("dialing");
       break;
     case nsITelephonyService::CALL_STATE_ALERTING:
       mState.AssignLiteral("alerting");
       break;
     case nsITelephonyService::CALL_STATE_CONNECTED:
       mState.AssignLiteral("connected");
@@ -129,27 +114,26 @@ TelephonyCall::ChangeStateInternal(uint1
   if (aCallState == nsITelephonyService::CALL_STATE_DISCONNECTED) {
     NS_ASSERTION(mLive, "Should be live!");
     mLive = false;
     if (mGroup) {
       mGroup->RemoveCall(this);
     } else {
       mTelephony->RemoveCall(this);
     }
-    UpdateDisconnectedReason(NS_LITERAL_STRING("NormalCallClearingError"));
   } else if (!mLive) {
     mLive = true;
     if (mGroup) {
       mGroup->AddCall(this);
     } else {
       mTelephony->AddCall(this);
     }
   }
 
-  if (aFireEvents && externalStateChanged) {
+  if (aFireEvents) {
     nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), this);
     if (NS_FAILED(rv)) {
       NS_WARNING("Failed to dispatch specific event!");
     }
 
     // This can change if the statechange handler called back here... Need to
     // figure out something smarter.
     if (mCallState == aCallState) {
@@ -206,26 +190,33 @@ TelephonyCall::NotifyError(const nsAStri
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch error event!");
   }
 }
 
 void
 TelephonyCall::UpdateDisconnectedReason(const nsAString& aDisconnectedReason)
 {
-  NS_ASSERTION(Substring(aDisconnectedReason, aDisconnectedReason.Length() - 5).EqualsLiteral("Error"),
+  NS_ASSERTION(Substring(aDisconnectedReason,
+                         aDisconnectedReason.Length() - 5).EqualsLiteral("Error"),
                "Disconnected reason should end with 'Error'");
 
-  if (mDisconnectedReason.IsNull()) {
-    // There is no 'Error' suffix in the corresponding enum. We should skip
-    // that part for comparison.
-    CONVERT_STRING_TO_NULLABLE_ENUM(
-        Substring(aDisconnectedReason, 0, aDisconnectedReason.Length() - 5),
-        TelephonyCallDisconnectedReason,
-        mDisconnectedReason);
+  if (!mDisconnectedReason.IsNull()) {
+    return;
+  }
+
+  // There is no 'Error' suffix in the corresponding enum. We should skip
+  // that part for comparison.
+  CONVERT_STRING_TO_NULLABLE_ENUM(
+      Substring(aDisconnectedReason, 0, aDisconnectedReason.Length() - 5),
+      TelephonyCallDisconnectedReason,
+      mDisconnectedReason);
+
+  if (!aDisconnectedReason.EqualsLiteral("NormalCallClearingError")) {
+    NotifyError(aDisconnectedReason);
   }
 }
 
 void
 TelephonyCall::ChangeGroup(TelephonyCallGroup* aGroup)
 {
   mGroup = aGroup;
 
@@ -293,43 +284,40 @@ TelephonyCall::Answer(ErrorResult& aRv)
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
 
   nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
   aRv = mTelephony->Service()->AnswerCall(mServiceId, mCallIndex, callback);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
-  ChangeStateInternal(nsITelephonyService::CALL_STATE_CONNECTING, false);
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 TelephonyCall::HangUp(ErrorResult& aRv)
 {
   nsRefPtr<Promise> promise = CreatePromise(aRv);
   if (!promise) {
     return nullptr;
   }
 
-  if (mCallState == nsITelephonyService::CALL_STATE_DISCONNECTING ||
-      mCallState == nsITelephonyService::CALL_STATE_DISCONNECTED) {
+  if (mCallState == nsITelephonyService::CALL_STATE_DISCONNECTED) {
     NS_WARNING(nsPrintfCString("HangUp on previously disconnected call"
                                " is rejected! (State: %u)", mCallState).get());
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
 
   nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
   aRv = mCallState == nsITelephonyService::CALL_STATE_INCOMING ?
     mTelephony->Service()->RejectCall(mServiceId, mCallIndex, callback) :
     mTelephony->Service()->HangUpCall(mServiceId, mCallIndex, callback);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
-  ChangeStateInternal(nsITelephonyService::CALL_STATE_DISCONNECTING, false);
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 TelephonyCall::Hold(ErrorResult& aRv)
 {
   nsRefPtr<Promise> promise = CreatePromise(aRv);
   if (!promise) {
@@ -361,17 +349,16 @@ TelephonyCall::Hold(ErrorResult& aRv)
 
   if (mSecondId) {
     // No state transition when we switch two numbers within one TelephonyCall
     // object. Otherwise, the state here will be inconsistent with the backend
     // RIL and will never be right.
     return promise.forget();
   }
 
-  ChangeStateInternal(nsITelephonyService::CALL_STATE_HOLDING, false);
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 TelephonyCall::Resume(ErrorResult& aRv)
 {
   nsRefPtr<Promise> promise = CreatePromise(aRv);
   if (!promise) {
@@ -396,11 +383,10 @@ TelephonyCall::Resume(ErrorResult& aRv)
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
 
   nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
   aRv = mTelephony->Service()->ResumeCall(mServiceId, mCallIndex, callback);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
 
-  ChangeStateInternal(nsITelephonyService::CALL_STATE_RESUMING, false);
   return promise.forget();
 }
--- a/dom/telephony/TelephonyCallGroup.cpp
+++ b/dom/telephony/TelephonyCallGroup.cpp
@@ -82,57 +82,43 @@ TelephonyCallGroup::NotifyError(const ns
 }
 
 void
 TelephonyCallGroup::ChangeState(uint16_t aCallState)
 {
   if (mCallState == aCallState) {
     return;
   }
-  // Update the internal state.
+
   mCallState = aCallState;
-
-  // Indicate whether the external state should be changed.
-  bool externalStateChanged = true;
   switch (aCallState) {
-    // These states are used internally to mark this CallGroup is currently
-    // being controlled, and we should block consecutive requests of the same
-    // type according to these states.
-    case nsITelephonyService::CALL_STATE_HOLDING:
-    case nsITelephonyService::CALL_STATE_RESUMING:
-      externalStateChanged = false;
-      break;
-    // These states will be translated into literal strings which are used to
-    // show the current status of this CallGroup.
     case nsITelephonyService::CALL_STATE_UNKNOWN:
       mState.AssignLiteral("");
       break;
     case nsITelephonyService::CALL_STATE_CONNECTED:
       mState.AssignLiteral("connected");
       break;
     case nsITelephonyService::CALL_STATE_HELD:
       mState.AssignLiteral("held");
       break;
     default:
       NS_NOTREACHED("Unknown state!");
   }
 
-  if (externalStateChanged) {
-    nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), nullptr);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to dispatch specific event!");
-    }
-    if (!mState.IsEmpty()) {
-      // This can change if the statechange handler called back here... Need to
-      // figure out something smarter.
-      if (mCallState == aCallState) {
-        rv = DispatchCallEvent(mState, nullptr);
-        if (NS_FAILED(rv)) {
-          NS_WARNING("Failed to dispatch specific event!");
-        }
+  nsresult rv = DispatchCallEvent(NS_LITERAL_STRING("statechange"), nullptr);
+  if (NS_FAILED(rv)) {
+    NS_WARNING("Failed to dispatch specific event!");
+  }
+  if (!mState.IsEmpty()) {
+    // This can change if the statechange handler called back here... Need to
+    // figure out something smarter.
+    if (mCallState == aCallState) {
+      rv = DispatchCallEvent(mState, nullptr);
+      if (NS_FAILED(rv)) {
+        NS_WARNING("Failed to dispatch specific event!");
       }
     }
   }
 
   for (uint32_t index = 0; index < mCalls.Length(); index++) {
     nsRefPtr<TelephonyCall> call = mCalls[index];
     call->ChangeState(aCallState);
 
@@ -343,17 +329,16 @@ TelephonyCallGroup::HangUp(ErrorResult& 
   if (!promise) {
     return nullptr;
   }
 
   nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
   aRv = mTelephony->Service()->HangUpConference(mCalls[0]->ServiceId(),
                                                 callback);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
-
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 TelephonyCallGroup::Hold(ErrorResult& aRv)
 {
   MOZ_ASSERT(!mCalls.IsEmpty());
 
@@ -367,18 +352,16 @@ TelephonyCallGroup::Hold(ErrorResult& aR
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
 
   nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
   aRv = mTelephony->Service()->HoldConference(mCalls[0]->ServiceId(),
                                               callback);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
-
-  ChangeState(nsITelephonyService::CALL_STATE_HOLDING);
   return promise.forget();
 }
 
 already_AddRefed<Promise>
 TelephonyCallGroup::Resume(ErrorResult& aRv)
 {
   MOZ_ASSERT(!mCalls.IsEmpty());
 
@@ -392,12 +375,10 @@ TelephonyCallGroup::Resume(ErrorResult& 
     promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
     return promise.forget();
   }
 
   nsCOMPtr<nsITelephonyCallback> callback = new TelephonyCallback(promise);
   aRv = mTelephony->Service()->ResumeConference(mCalls[0]->ServiceId(),
                                                 callback);
   NS_ENSURE_TRUE(!aRv.Failed(), nullptr);
-
-  ChangeState(nsITelephonyService::CALL_STATE_RESUMING);
   return promise.forget();
 }
--- a/dom/telephony/TelephonyCallInfo.cpp
+++ b/dom/telephony/TelephonyCallInfo.cpp
@@ -10,32 +10,38 @@ namespace mozilla {
 namespace dom {
 namespace telephony {
 
 NS_IMPL_ISUPPORTS(TelephonyCallInfo, nsITelephonyCallInfo)
 
 TelephonyCallInfo::TelephonyCallInfo(uint32_t aClientId,
                                      uint32_t aCallIndex,
                                      uint16_t aCallState,
+                                     const nsAString& aDisconnectedReason,
+
                                      const nsAString& aNumber,
                                      uint16_t aNumberPresentation,
                                      const nsAString& aName,
                                      uint16_t aNamePresentation,
+
                                      bool aIsOutgoing,
                                      bool aIsEmergency,
                                      bool aIsConference,
                                      bool aIsSwitchable,
                                      bool aIsMergeable)
   : mClientId(aClientId),
     mCallIndex(aCallIndex),
     mCallState(aCallState),
+    mDisconnectedReason(aDisconnectedReason),
+
     mNumber(aNumber),
     mNumberPresentation(aNumberPresentation),
     mName(aName),
     mNamePresentation(aNamePresentation),
+
     mIsOutgoing(aIsOutgoing),
     mIsEmergency(aIsEmergency),
     mIsConference(aIsConference),
     mIsSwitchable(aIsSwitchable),
     mIsMergeable(aIsMergeable)
 {
 }
 
@@ -56,16 +62,23 @@ TelephonyCallInfo::GetCallIndex(uint32_t
 NS_IMETHODIMP
 TelephonyCallInfo::GetCallState(uint16_t* aCallState)
 {
   *aCallState = mCallState;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+TelephonyCallInfo::GetDisconnectedReason(nsAString& aDisconnectedReason)
+{
+  aDisconnectedReason = mDisconnectedReason;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 TelephonyCallInfo::GetNumber(nsAString& aNumber)
 {
   aNumber = mNumber;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 TelephonyCallInfo::GetNumberPresentation(uint16_t* aNumberPresentation)
--- a/dom/telephony/TelephonyCallInfo.h
+++ b/dom/telephony/TelephonyCallInfo.h
@@ -16,36 +16,48 @@ namespace dom {
 namespace telephony {
 
 class TelephonyCallInfo final : public nsITelephonyCallInfo
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSITELEPHONYCALLINFO
 
-  TelephonyCallInfo(uint32_t aClientId, uint32_t aCallIndex,
-                    uint16_t aCallState, const nsAString& aNumber,
-                    uint16_t aNumberPresentation, const nsAString& aName,
-                    uint16_t aNamePresentation, bool aIsOutgoing,
-                    bool aIsEmergency, bool aIsConference,
-                    bool aIsSwitchable, bool aIsMergeable);
+  TelephonyCallInfo(uint32_t aClientId,
+                    uint32_t aCallIndex,
+                    uint16_t aCallState,
+                    const nsAString& aDisconnectedReason,
+
+                    const nsAString& aNumber,
+                    uint16_t aNumberPresentation,
+                    const nsAString& aName,
+                    uint16_t aNamePresentation,
+
+                    bool aIsOutgoing,
+                    bool aIsEmergency,
+                    bool aIsConference,
+                    bool aIsSwitchable,
+                    bool aIsMergeable);
 
 private:
   // Don't try to use the default constructor.
   TelephonyCallInfo() {}
 
   ~TelephonyCallInfo() {}
 
   uint32_t mClientId;
   uint32_t mCallIndex;
   uint16_t mCallState;
+  nsString mDisconnectedReason;
+
   nsString mNumber;
   uint16_t mNumberPresentation;
   nsString mName;
   uint16_t mNamePresentation;
+
   bool mIsOutgoing;
   bool mIsEmergency;
   bool mIsConference;
   bool mIsSwitchable;
   bool mIsMergeable;
 };
 
 } // namespace telephony
--- a/dom/telephony/gonk/TelephonyService.js
+++ b/dom/telephony/gonk/TelephonyService.js
@@ -125,20 +125,23 @@ MobileCallForwardingOptions.prototype = 
   timeSeconds: -1,
   serviceClass: nsIMobileConnection.ICC_SERVICE_CLASS_NONE
 };
 
 function TelephonyCallInfo(aCall) {
   this.clientId = aCall.clientId;
   this.callIndex = aCall.callIndex;
   this.callState = aCall.state;
+  this.disconnectedReason = aCall.disconnectedReason || "";
+
   this.number = aCall.number;
   this.numberPresentation = aCall.numberPresentation;
   this.name = aCall.name;
   this.namePresentation = aCall.namePresentation;
+
   this.isOutgoing = aCall.isOutgoing;
   this.isEmergency = aCall.isEmergency;
   this.isConference = aCall.isConference;
   this.isSwitchable = aCall.isSwitchable;
   this.isMergeable = aCall.isMergeable;
 }
 TelephonyCallInfo.prototype = {
   QueryInterface: XPCOMUtils.generateQI([Ci.nsITelephonyCallInfo]),
@@ -149,20 +152,23 @@ TelephonyCallInfo.prototype = {
     interfaces:       [Ci.nsITelephonyCallInfo]
   }),
 
   // nsITelephonyCallInfo
 
   clientId: 0,
   callIndex: 0,
   callState: nsITelephonyService.CALL_STATE_UNKNOWN,
+  disconnectedReason: "",
+
   number: "",
   numberPresentation: nsITelephonyService.CALL_PRESENTATION_ALLOWED,
   name: "",
   namePresentation: nsITelephonyService.CALL_PRESENTATION_ALLOWED,
+
   isOutgoing: true,
   isEmergency: false,
   isConference: false,
   isSwitchable: true,
   isMergeable: true
 };
 
 function Call(aClientId, aCallIndex) {
@@ -1439,20 +1445,16 @@ TelephonyService.prototype = {
     this._cdmaCallWaitingNumber = null;
   },
 
   /**
    * Disconnect calls by updating their states. Sometimes, it may cause other
    * calls being disconnected as well.
    *
    * @return Array a list of calls we need to fire callStateChange
-   *
-   * TODO: The list currently doesn't contain calls that we fire notifyError
-   * for them. However, after Bug 1147736, notifyError is replaced by
-   * callStateChanged and those calls should be included in the list.
    */
   _disconnectCalls: function(aClientId, aCalls,
                              aFailCause = RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) {
     if (DEBUG) debug("_disconnectCalls: " + JSON.stringify(aCalls));
 
     // Child cannot live without parent. Let's find all the calls that need to
     // be disconnected.
     let disconnectedCalls = aCalls.slice();
@@ -1466,32 +1468,26 @@ TelephonyService.prototype = {
 
     // Store unique value in the list.
     disconnectedCalls = [...Set(disconnectedCalls)];
 
     let callsForStateChanged = [];
 
     disconnectedCalls.forEach(call => {
       call.state = nsITelephonyService.CALL_STATE_DISCONNECTED;
-      call.failCause = aFailCause;
+      call.disconnectedReason = aFailCause;
 
       if (call.parentId) {
         let parentCall = this._currentCalls[aClientId][call.parentId];
         delete parentCall.childId;
       }
 
       this._notifyCallEnded(call);
 
-      if (call.hangUpLocal || !call.failCause ||
-          call.failCause === RIL.GECKO_CALL_ERROR_NORMAL_CALL_CLEARING) {
-        callsForStateChanged.push(call);
-      } else {
-        this._notifyAllListeners("notifyError",
-                                 [aClientId, call.callIndex, call.failCause]);
-      }
+      callsForStateChanged.push(call);
 
       delete this._currentCalls[aClientId][call.callIndex];
     });
 
     return callsForStateChanged;
   },
 
   /**
--- a/dom/telephony/ipc/PTelephony.ipdl
+++ b/dom/telephony/ipc/PTelephony.ipdl
@@ -120,18 +120,16 @@ union IPCTelephonyRequest
   SendTonesRequest;
 };
 
 sync protocol PTelephony {
   manager PContent;
   manages PTelephonyRequest;
 
 child:
-  NotifyCallError(uint32_t aClientId, int32_t aCallIndex, nsString aError);
-
   NotifyCallStateChanged(nsTelephonyCallInfo[] aAllInfo);
 
   NotifyCdmaCallWaiting(uint32_t aClientId, IPCCdmaWaitingCallData aData);
 
   NotifyConferenceCallStateChanged(uint16_t aCallState);
 
   NotifyConferenceError(nsString aName, nsString aMessage);
 
--- a/dom/telephony/ipc/TelephonyChild.cpp
+++ b/dom/telephony/ipc/TelephonyChild.cpp
@@ -43,27 +43,16 @@ TelephonyChild::AllocPTelephonyRequestCh
 bool
 TelephonyChild::DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor)
 {
   delete aActor;
   return true;
 }
 
 bool
-TelephonyChild::RecvNotifyCallError(const uint32_t& aClientId,
-                                    const int32_t& aCallIndex,
-                                    const nsString& aError)
-{
-  MOZ_ASSERT(mService);
-
-  mService->NotifyError(aClientId, aCallIndex, aError);
-  return true;
-}
-
-bool
 TelephonyChild::RecvNotifyCallStateChanged(nsTArray<nsITelephonyCallInfo*>&& aAllInfo)
 {
   uint32_t length = aAllInfo.Length();
   nsTArray<nsCOMPtr<nsITelephonyCallInfo>> results;
   for (uint32_t i = 0; i < length; ++i) {
     // Use dont_AddRef here because this instance has already been AddRef-ed in
     // TelephonyIPCSerializer.h
     nsCOMPtr<nsITelephonyCallInfo> info = dont_AddRef(aAllInfo[i]);
--- a/dom/telephony/ipc/TelephonyChild.h
+++ b/dom/telephony/ipc/TelephonyChild.h
@@ -30,20 +30,16 @@ protected:
 
   virtual PTelephonyRequestChild*
   AllocPTelephonyRequestChild(const IPCTelephonyRequest& aRequest) override;
 
   virtual bool
   DeallocPTelephonyRequestChild(PTelephonyRequestChild* aActor) override;
 
   virtual bool
-  RecvNotifyCallError(const uint32_t& aClientId, const int32_t& aCallIndex,
-                      const nsString& aError) override;
-
-  virtual bool
   RecvNotifyCallStateChanged(nsTArray<nsITelephonyCallInfo*>&& aAllInfo) override;
 
   virtual bool
   RecvNotifyCdmaCallWaiting(const uint32_t& aClientId,
                             const IPCCdmaWaitingCallData& aData) override;
 
   virtual bool
   RecvNotifyConferenceCallStateChanged(const uint16_t& aCallState) override;
--- a/dom/telephony/ipc/TelephonyIPCSerializer.h
+++ b/dom/telephony/ipc/TelephonyIPCSerializer.h
@@ -85,47 +85,64 @@ struct ParamTraits<nsITelephonyCallInfo*
     if (isNull) {
       *aResult = nullptr;
       return true;
     }
 
     uint32_t clientId;
     uint32_t callIndex;
     uint16_t callState;
+    nsString disconnectedReason;
+
     nsString number;
     uint16_t numberPresentation;
     nsString name;
     uint16_t namePresentation;
+
     bool isOutgoing;
     bool isEmergency;
     bool isConference;
     bool isSwitchable;
     bool isMergeable;
 
     // It's not important to us where it fails, but rather if it fails
     if (!(ReadParam(aMsg, aIter, &clientId) &&
           ReadParam(aMsg, aIter, &callIndex) &&
           ReadParam(aMsg, aIter, &callState) &&
+          ReadParam(aMsg, aIter, &disconnectedReason) &&
+
           ReadParam(aMsg, aIter, &number) &&
           ReadParam(aMsg, aIter, &numberPresentation) &&
           ReadParam(aMsg, aIter, &name) &&
           ReadParam(aMsg, aIter, &namePresentation) &&
+
           ReadParam(aMsg, aIter, &isOutgoing) &&
           ReadParam(aMsg, aIter, &isEmergency) &&
           ReadParam(aMsg, aIter, &isConference) &&
           ReadParam(aMsg, aIter, &isSwitchable) &&
           ReadParam(aMsg, aIter, &isMergeable))) {
       return false;
     }
 
     nsCOMPtr<nsITelephonyCallInfo> info =
-        new TelephonyCallInfo(clientId, callIndex, callState, number,
-                              numberPresentation, name, namePresentation,
-                              isOutgoing, isEmergency, isConference,
-                              isSwitchable, isMergeable);
+        new TelephonyCallInfo(clientId,
+                              callIndex,
+                              callState,
+                              disconnectedReason,
+
+                              number,
+                              numberPresentation,
+                              name,
+                              namePresentation,
+
+                              isOutgoing,
+                              isEmergency,
+                              isConference,
+                              isSwitchable,
+                              isMergeable);
 
     info.forget(aResult);
 
     return true;
   }
 };
 
 
--- a/dom/telephony/ipc/TelephonyIPCService.cpp
+++ b/dom/telephony/ipc/TelephonyIPCService.cpp
@@ -417,26 +417,16 @@ TelephonyIPCService::NotifyConferenceErr
 {
   for (uint32_t i = 0; i < mListeners.Length(); i++) {
     mListeners[i]->NotifyConferenceError(aName, aMessage);
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
-TelephonyIPCService::NotifyError(uint32_t aClientId, int32_t aCallIndex,
-                                  const nsAString& aError)
-{
-  for (uint32_t i = 0; i < mListeners.Length(); i++) {
-    mListeners[i]->NotifyError(aClientId, aCallIndex, aError);
-  }
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 TelephonyIPCService::SupplementaryServiceNotification(uint32_t aClientId,
                                                        int32_t aCallIndex,
                                                        uint16_t aNotification)
 {
   for (uint32_t i = 0; i < mListeners.Length(); i++) {
     mListeners[i]->SupplementaryServiceNotification(aClientId, aCallIndex,
                                                     aNotification);
   }
--- a/dom/telephony/ipc/TelephonyParent.cpp
+++ b/dom/telephony/ipc/TelephonyParent.cpp
@@ -327,27 +327,16 @@ TelephonyParent::NotifyConferenceError(c
 {
   NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
 
   return SendNotifyConferenceError(nsString(aName), nsString(aMessage)) ? NS_OK
                                                                         : NS_ERROR_FAILURE;
 }
 
 NS_IMETHODIMP
-TelephonyParent::NotifyError(uint32_t aClientId,
-                             int32_t aCallIndex,
-                             const nsAString& aError)
-{
-  NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
-
-  return SendNotifyCallError(aClientId, aCallIndex, nsString(aError))
-      ? NS_OK : NS_ERROR_FAILURE;
-}
-
-NS_IMETHODIMP
 TelephonyParent::SupplementaryServiceNotification(uint32_t aClientId,
                                                   int32_t aCallIndex,
                                                   uint16_t aNotification)
 {
   NS_ENSURE_TRUE(!mActorDestroyed, NS_ERROR_FAILURE);
 
   return SendNotifySupplementaryService(aClientId, aCallIndex, aNotification)
       ? NS_OK : NS_ERROR_FAILURE;
@@ -427,24 +416,16 @@ TelephonyRequestParent::NotifyCdmaCallWa
 NS_IMETHODIMP
 TelephonyRequestParent::NotifyConferenceError(const nsAString& aName,
                                               const nsAString& aMessage)
 {
   MOZ_CRASH("Not a TelephonyParent!");
 }
 
 NS_IMETHODIMP
-TelephonyRequestParent::NotifyError(uint32_t aClientId,
-                                    int32_t aCallIndex,
-                                    const nsAString& aError)
-{
-  MOZ_CRASH("Not a TelephonyParent!");
-}
-
-NS_IMETHODIMP
 TelephonyRequestParent::SupplementaryServiceNotification(uint32_t aClientId,
                                                          int32_t aCallIndex,
                                                          uint16_t aNotification)
 {
   MOZ_CRASH("Not a TelephonyParent!");
 }
 
 // nsITelephonyDialCallback
--- a/dom/telephony/nsIGonkTelephonyService.idl
+++ b/dom/telephony/nsIGonkTelephonyService.idl
@@ -5,17 +5,17 @@
 
 #include "nsITelephonyService.idl"
 
 %{C++
 #define GONK_TELEPHONY_SERVICE_CONTRACTID \
         "@mozilla.org/telephony/gonktelephonyservice;1"
 %}
 
-[scriptable, uuid(d287e11a-0a65-4456-b481-c63d62afdb5d)]
+[scriptable, uuid(8653d76b-6805-41d2-8ea5-3b14fb4e682d)]
 interface nsIGonkTelephonyService : nsITelephonyService
 {
   void notifyCallRing();
 
   void notifyCurrentCalls(in unsigned long clientId, in jsval calls);
 
   void notifyCdmaCallWaiting(in unsigned long clientId, in jsval waitingCall);
 
--- a/dom/telephony/nsITelephonyCallInfo.idl
+++ b/dom/telephony/nsITelephonyCallInfo.idl
@@ -1,15 +1,15 @@
 /* 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"
 
-[scriptable, uuid(3ea2d155-8ea2-42be-85d7-bd8ede8afc40)]
+[scriptable, uuid(e5e1be26-a3d4-49b3-8d9f-c1df5192b364)]
 interface nsITelephonyCallInfo : nsISupports
 {
   /**
    * Indicate the RIL client, 0 ~ (number of client - 1).
    */
   readonly attribute unsigned long clientId;
 
   /**
@@ -18,16 +18,24 @@ interface nsITelephonyCallInfo : nsISupp
   readonly attribute unsigned long callIndex;
 
   /**
    * One of the nsITelephonyService::CALL_STATE_* values.
    */
   readonly attribute unsigned short callState;
 
   /**
+   * The disconnectedReason of a call is defualt to an empty string when the
+   * call is "not disconnected", but once the call becomes "disconnected" the
+   * disconnectedReason should be a non-empty string no matter the call is
+   * disconnected for a noraml reason or an error.
+   */
+  readonly attribute DOMString disconnectedReason;
+
+  /**
    * Number of the other party.
    */
   readonly attribute DOMString number;
 
   /**
    * Presentation of the call number.
    * One of the nsITelephonyService::CALL_PRESENTATION_* values.
    */
--- a/dom/telephony/nsITelephonyService.idl
+++ b/dom/telephony/nsITelephonyService.idl
@@ -2,17 +2,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsISupports.idl"
 
 interface nsIMobileCallForwardingOptions;
 interface nsITelephonyCallInfo;
 
-[scriptable, uuid(37fb45bb-ae10-4cfd-b24e-d656a9787a0a)]
+[scriptable, uuid(80faf34e-286b-4487-bd55-135bd92668b9)]
 interface nsITelephonyListener : nsISupports
 {
   /**
    * Called when enumeration asked by nsITelephonyService::enumerateCalls
    * is completed.
    */
   void enumerateCallStateComplete();
 
@@ -50,30 +50,16 @@ interface nsITelephonyListener : nsISupp
    * @param notification
    *        One of the nsITelephonyService::NOTIFICATION_* values.
    */
   void supplementaryServiceNotification(in unsigned long clientId,
                                         in long callIndex,
                                         in unsigned short notification);
 
   /**
-   * Called when RIL error occurs.
-   *
-   * @param clientId
-            Indicate the RIL client, 0 ~ (number of client - 1).
-   * @param callIndex
-   *        Call identifier assigned by the RIL. -1 if no connection
-   * @param error
-   *        Error from RIL.
-   */
-  void notifyError(in unsigned long clientId,
-                   in long callIndex,
-                   in AString error);
-
-  /**
    * Called when a waiting call comes in CDMA networks.
    *
    * @param clientId
             Indicate the RIL client, 0 ~ (number of client - 1).
    * @param number
    *        Number of the other party.
    * @param numberPresentation
    *        Presentation of the call number.
@@ -170,30 +156,26 @@ interface nsITelephonyDialCallback : nsI
 #define TELEPHONY_SERVICE_CONTRACTID \
   "@mozilla.org/telephony/telephonyservice;1"
 %}
 
 /**
  * XPCOM component (in the content process) that provides the telephony
  * information.
  */
-[scriptable, uuid(2b7b3fe7-f1d9-48be-8fd6-aaf890fa73af)]
+[scriptable, uuid(75e4b5e3-6710-4156-bdaa-ba1081f390f4)]
 interface nsITelephonyService : nsISupports
 {
   const unsigned short CALL_STATE_UNKNOWN = 0;
   const unsigned short CALL_STATE_DIALING = 1;
   const unsigned short CALL_STATE_ALERTING = 2;
-  const unsigned short CALL_STATE_CONNECTING = 3;
-  const unsigned short CALL_STATE_CONNECTED = 4;
-  const unsigned short CALL_STATE_HOLDING = 5;
-  const unsigned short CALL_STATE_HELD = 6;
-  const unsigned short CALL_STATE_RESUMING = 7;
-  const unsigned short CALL_STATE_DISCONNECTING = 8;
-  const unsigned short CALL_STATE_DISCONNECTED = 9;
-  const unsigned short CALL_STATE_INCOMING = 10;
+  const unsigned short CALL_STATE_CONNECTED = 3;
+  const unsigned short CALL_STATE_HELD = 4;
+  const unsigned short CALL_STATE_DISCONNECTED = 5;
+  const unsigned short CALL_STATE_INCOMING = 6;
 
   const unsigned short NOTIFICATION_REMOTE_HELD = 0;
   const unsigned short NOTIFICATION_REMOTE_RESUMED = 1;
 
   const unsigned short CALL_PRESENTATION_ALLOWED = 0;
   const unsigned short CALL_PRESENTATION_RESTRICTED = 1;
   const unsigned short CALL_PRESENTATION_UNKNOWN = 2;
   const unsigned short CALL_PRESENTATION_PAYPHONE = 3;
--- a/dom/telephony/test/marionette/manifest.ini
+++ b/dom/telephony/test/marionette/manifest.ini
@@ -11,16 +11,17 @@ qemu = true
 [test_conference_remove_error.js]
 [test_conference_three_hangup_one.js]
 [test_conference_three_remove_one.js]
 [test_conference_two_calls.js]
 [test_conference_two_hangup_all.js]
 [test_conference_two_hangup_one.js]
 [test_conference_two_hold_resume.js]
 [test_conference_two_remove_one.js]
+[test_consecutive_hold.js]
 [test_crash_emulator.js]
 [test_dsds_connection_conflict.js]
 [test_dsds_default_service_id.js]
 [test_dsds_normal_call.js]
 [test_dtmf.js]
 [test_emergency.js]
 [test_emergency_label.js]
 [test_incall_mmi_call_hold.js]
new file mode 100644
--- /dev/null
+++ b/dom/telephony/test/marionette/test_consecutive_hold.js
@@ -0,0 +1,52 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/ */
+
+MARIONETTE_TIMEOUT = 90000;
+MARIONETTE_HEAD_JS = 'head.js';
+
+const inNumber = "5555552222";
+const inInfo = gInCallStrPool(inNumber);
+let inCall;
+
+startTest(function() {
+  Promise.resolve()
+
+    // Incoming
+    .then(() => gRemoteDial(inNumber))
+    .then(call => inCall = call)
+    .then(() => gCheckAll(null, [inCall], "", [], [inInfo.incoming]))
+    .then(() => is(inCall.disconnectedReason, null))
+
+    // Answer
+    .then(() => gAnswer(inCall))
+    .then(() => gCheckAll(inCall, [inCall], "", [], [inInfo.active]))
+    .then(() => is(inCall.disconnectedReason, null))
+
+    // Disable the Hold function of the emulator, then hold the active call,
+    // where the hold request will fail and the call will remain active.
+    .then(() => emulator.runCmd("gsm disable hold"))
+    .then(() => gHold(inCall))
+    .then(() => ok(false, "This hold request should be rejected."),
+          () => log("This hold request is rejected as expected."))
+    .then(() => gCheckAll(inCall, [inCall], "", [], [inInfo.active]))
+    .then(() => is(inCall.disconnectedReason, null))
+
+    // Enable the Hold function of the emulator, then hold the active call,
+    // where the hold request should succeed and the call should become held.
+    .then(() => emulator.runCmd("gsm enable hold"))
+    .then(() => gHold(inCall))
+    .then(() => log("This hold request is resolved as expected."),
+          () => ok(false, "This hold request should be resolved."))
+    .then(() => gCheckAll(null, [inCall], "", [], [inInfo.held]))
+    .then(() => is(inCall.disconnectedReason, null))
+
+    // Hang up the call
+    .then(() => gHangUp(inCall))
+    .then(() => gCheckAll(null, [], "", [], []))
+    .then(() => is(inCall.disconnectedReason, "NormalCallClearing"))
+
+    // Clean Up
+    .catch(error => ok(false, "Promise reject: " + error))
+    .then(() => emulator.runCmd("gsm enable hold"))
+    .then(finish);
+});
--- a/mobile/android/base/GeckoApp.java
+++ b/mobile/android/base/GeckoApp.java
@@ -15,17 +15,16 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Locale;
 import java.util.Map;
 import java.util.Set;
 
-import android.provider.Browser;
 import org.json.JSONArray;
 import org.json.JSONException;
 import org.json.JSONObject;
 import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.GeckoProfileDirectories.NoMozillaDirectoryException;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.gfx.BitmapUtils;
@@ -1431,28 +1430,28 @@ public abstract class GeckoApp
             Tabs.getInstance().loadUrl(AboutPages.HOME, flags);
         }
     }
 
     /**
      * Loads the initial tab at Fennec startup. This tab will load with the given
      * external URL. If that URL is invalid, about:home will be loaded.
      *
-     * @param url External URL to load.
-     * @param extraApplicationId Identifies the calling application; delivered with the URL
+     * @param url    External URL to load.
+     * @param intent External intent whose extras modify the request
+     * @param flags  Flags used to load the load
      */
-    protected void loadStartupTabWithExternalUrl(final String url, final String extraApplicationId,
-            final int flags) {
+    protected void loadStartupTab(final String url, final SafeIntent intent, final int flags) {
         // Invalid url
         if (url == null) {
             loadStartupTabWithAboutHome(flags);
             return;
         }
 
-        Tabs.getInstance().loadUrl(url, extraApplicationId, flags);
+        Tabs.getInstance().loadUrlWithIntentExtras(url, intent, flags);
     }
 
     private void initialize() {
         mInitialized = true;
 
         final SafeIntent intent = new SafeIntent(getIntent());
         final String action = intent.getAction();
 
@@ -1509,18 +1508,17 @@ public abstract class GeckoApp
         if (isExternalURL) {
             // Restore tabs before opening an external URL so that the new tab
             // is animated properly.
             Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
             int flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_USER_ENTERED | Tabs.LOADURL_EXTERNAL;
             if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
                 flags |= Tabs.LOADURL_PINNED;
             }
-            final String extraApplicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
-            loadStartupTabWithExternalUrl(passedUri, extraApplicationId, flags);
+            loadStartupTab(passedUri, intent, flags);
         } else {
             if (!mIsRestoringActivity) {
                 loadStartupTabWithAboutHome(Tabs.LOADURL_NEW_TAB);
             }
 
             Tabs.getInstance().notifyListeners(null, Tabs.TabEvents.RESTORED);
         }
 
@@ -1824,21 +1822,20 @@ public abstract class GeckoApp
                                                                             Tabs.LOADURL_USER_ENTERED |
                                                                             Tabs.LOADURL_EXTERNAL);
                                 }
                             }
                         }, "Tabs:TabsOpened");
 
                         TabQueueHelper.openQueuedUrls(GeckoApp.this, mProfile, TabQueueHelper.FILE_NAME, true);
                     } else {
-                        String uri = intent.getDataString();
-                        final String extraApplicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
-                        Tabs.getInstance().loadUrl(uri, extraApplicationId, Tabs.LOADURL_NEW_TAB |
-                                                                            Tabs.LOADURL_USER_ENTERED |
-                                                                            Tabs.LOADURL_EXTERNAL);
+                        final String url = intent.getDataString();
+                        Tabs.getInstance().loadUrlWithIntentExtras(url, intent, Tabs.LOADURL_NEW_TAB |
+                                Tabs.LOADURL_USER_ENTERED |
+                                Tabs.LOADURL_EXTERNAL);
                     }
                 }
             });
         } else if (ACTION_HOMESCREEN_SHORTCUT.equals(action)) {
             String uri = getURIFromIntent(intent);
             GeckoAppShell.sendEventToGecko(GeckoEvent.createBookmarkLoadEvent(uri));
         } else if (Intent.ACTION_SEARCH.equals(action)) {
             String uri = getURIFromIntent(intent);
--- a/mobile/android/base/Tabs.java
+++ b/mobile/android/base/Tabs.java
@@ -8,34 +8,38 @@ package org.mozilla.gecko;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
 import java.util.concurrent.CopyOnWriteArrayList;
 import java.util.concurrent.atomic.AtomicInteger;
 
 import org.json.JSONException;
 import org.json.JSONObject;
+
+import org.mozilla.gecko.AppConstants.Versions;
 import org.mozilla.gecko.db.BrowserDB;
 import org.mozilla.gecko.favicons.Favicons;
 import org.mozilla.gecko.fxa.FirefoxAccounts;
+import org.mozilla.gecko.mozglue.ContextUtils.SafeIntent;
 import org.mozilla.gecko.mozglue.JNITarget;
 import org.mozilla.gecko.mozglue.RobocopTarget;
 import org.mozilla.gecko.sync.setup.SyncAccounts;
 import org.mozilla.gecko.util.GeckoEventListener;
 import org.mozilla.gecko.util.ThreadUtils;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
 import android.accounts.OnAccountsUpdateListener;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.database.ContentObserver;
 import android.graphics.Color;
 import android.net.Uri;
 import android.os.Handler;
+import android.provider.Browser;
 import android.util.Log;
 
 public class Tabs implements GeckoEventListener {
     private static final String LOGTAG = "GeckoTabs";
 
     // mOrder and mTabs are always of the same cardinality, and contain the same values.
     private final CopyOnWriteArrayList<Tab> mOrder = new CopyOnWriteArrayList<Tab>();
 
@@ -808,38 +812,40 @@ public class Tabs implements GeckoEventL
      * @param flags flags used to load tab
      *
      * @return      the Tab if a new one was created; null otherwise
      */
     public Tab loadUrl(String url, int flags) {
         return loadUrl(url, null, -1, null, flags);
     }
 
-    public Tab loadUrl(final String url, final String applicationId, final int flags) {
-        return loadUrl(url, null, -1, applicationId, flags);
+    public Tab loadUrlWithIntentExtras(final String url, final SafeIntent intent, final int flags) {
+        // Note: we don't get the URL from the intent so the calling
+        // method has the opportunity to change the URL if applicable.
+        return loadUrl(url, null, -1, intent, flags);
     }
 
     public Tab loadUrl(final String url, final String searchEngine, final int parentId, final int flags) {
         return loadUrl(url, searchEngine, parentId, null, flags);
     }
 
     /**
      * Loads a tab with the given URL.
      *
-     * @param url           URL of page to load, or search term used if searchEngine is given
-     * @param searchEngine  if given, the search engine with this name is used
-     *                      to search for the url string; if null, the URL is loaded directly
-     * @param parentId      ID of this tab's parent, or -1 if it has no parent
-     * @param applicationId Identity of the calling application
-     * @param flags         flags used to load tab
+     * @param url          URL of page to load, or search term used if searchEngine is given
+     * @param searchEngine if given, the search engine with this name is used
+     *                     to search for the url string; if null, the URL is loaded directly
+     * @param parentId     ID of this tab's parent, or -1 if it has no parent
+     * @param intent       an intent whose extras are used to modify the request
+     * @param flags        flags used to load tab
      *
-     * @return              the Tab if a new one was created; null otherwise
+     * @return             the Tab if a new one was created; null otherwise
      */
     public Tab loadUrl(final String url, final String searchEngine, final int parentId,
-                   final String applicationId, final int flags) {
+                   final SafeIntent intent, final int flags) {
         JSONObject args = new JSONObject();
         Tab tabToSelect = null;
         boolean delayLoad = (flags & LOADURL_DELAY_LOAD) != 0;
 
         // delayLoad implies background tab
         boolean background = delayLoad || (flags & LOADURL_BACKGROUND) != 0;
 
         try {
@@ -852,21 +858,26 @@ public class Tabs implements GeckoEventL
             args.put("engine", searchEngine);
             args.put("parentId", parentId);
             args.put("userEntered", userEntered);
             args.put("isPrivate", isPrivate);
             args.put("pinned", (flags & LOADURL_PINNED) != 0);
             args.put("desktopMode", desktopMode);
 
             final boolean needsNewTab;
+            final String applicationId = (intent == null) ? null :
+                    intent.getStringExtra(Browser.EXTRA_APPLICATION_ID);
             if (applicationId == null) {
                 needsNewTab = (flags & LOADURL_NEW_TAB) != 0;
             } else {
+                // If you modify this code, be careful that intent != null.
+                final boolean extraCreateNewTab = (Versions.feature12Plus) ?
+                        intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false) : false;
                 final Tab applicationTab = getTabForApplicationId(applicationId);
-                if (applicationTab == null) {
+                if (applicationTab == null || extraCreateNewTab) {
                     needsNewTab = true;
                 } else {
                     needsNewTab = false;
                     delayLoad = false;
                     background = false;
 
                     tabToSelect = applicationTab;
                     final int tabToSelectId = tabToSelect.getId();
--- a/mobile/android/base/webapp/WebappImpl.java
+++ b/mobile/android/base/webapp/WebappImpl.java
@@ -181,28 +181,27 @@ public class WebappImpl extends GeckoApp
         // the URI from the intent data.  Otherwise, we should have been able
         // to get it from the APK resources.  So we should never get here.
         Log.wtf(LOGTAG, "Couldn't get URI from intent nor APK resources");
         return null;
     }
 
     @Override
     protected void loadStartupTabWithAboutHome(final int flags) {
-        loadStartupTabWithExternalUrl(null, null, flags);
+        loadStartupTab(null, null, flags);
     }
 
-    // Note: there is no support for applicationId in Webapps at
-    // the moment because I don't have time to debug/test.
+    // Note: there is no support for loading with intent extras in
+    // Webapps at the moment because I don't have time to debug/test.
     @Override
-    protected void loadStartupTabWithExternalUrl(final String uri, final String applicationId,
-            int flags) {
+    protected void loadStartupTab(final String uri, final SafeIntent unusedIntent, int flags) {
         // Load a tab so it's available for any code that assumes a tab
         // before the app tab itself is loaded in BrowserApp._loadWebapp.
         flags = Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_USER_ENTERED | Tabs.LOADURL_EXTERNAL;
-        super.loadStartupTabWithExternalUrl("about:blank", null, flags);
+        super.loadStartupTab("about:blank", null, flags);
     }
 
     private void showSplash() {
 
         // get the favicon dominant color, stored when the app was installed
         int dominantColor = Allocator.getInstance().getColor(getIndex());
 
         setBackgroundGradient(dominantColor);
--- a/mobile/android/chrome/content/Reader.js
+++ b/mobile/android/chrome/content/Reader.js
@@ -283,17 +283,17 @@ let Reader = {
       return article;
     }
 
     // Article hasn't been found in the cache, we need to
     // download the page and parse the article out of it.
     return yield ReaderMode.downloadAndParseDocument(url).catch(e => {
       Cu.reportError("Error downloading and parsing document: " + e);
       return null;
-    });;
+    });
   }),
 
   _getSavedArticle: function(browser) {
     return new Promise((resolve, reject) => {
       let mm = browser.messageManager;
       let listener = (message) => {
         mm.removeMessageListener("Reader:SavedArticleData", listener);
         resolve(message.data.article);
--- a/mobile/android/confvars.sh
+++ b/mobile/android/confvars.sh
@@ -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/.
 
 MOZ_APP_BASENAME=Fennec
 MOZ_APP_VENDOR=Mozilla
 
-MOZ_APP_VERSION=40.0a1
+MOZ_APP_VERSION=41.0a1
 MOZ_APP_UA_NAME=Firefox
 
 MOZ_BRANDING_DIRECTORY=mobile/android/branding/unofficial
 MOZ_OFFICIAL_BRANDING_DIRECTORY=mobile/android/branding/official
 # MOZ_APP_DISPLAYNAME is set by branding/configure.sh
 
 # We support Android SDK version 9 and up by default.
 # See the --enable-android-min-sdk and --enable-android-max-sdk arguments in configure.in.
--- a/mobile/android/themes/core/aboutPasswords.css
+++ b/mobile/android/themes/core/aboutPasswords.css
@@ -79,17 +79,17 @@
   background-size: 12px 12px;
   background-repeat: no-repeat;
   height: 32px;
   width: 32px;
   margin: 0 5px;
 }
 
 .icon {
-  background-image: url("resource://android/res/drawable-mdpi-v4/favicon.png");
+  background-image: url("resource://android/res/drawable-mdpi-v4/favicon_globe.png");
   background-position: center;
   background-size: 32px 32px;
   background-repeat: no-repeat;
   height: 32px;
   width: 32px;
   visibility: hidden;
 }
 
@@ -98,25 +98,25 @@
     background-image: url("resource://android/res/drawable-hdpi-v4/ab_search.png");
   }
 
   #filter-clear {
     background-image: url("resource://android/res/drawable-hdpi-v4/close_edit_mode_light.png");
   }
 
   .icon {
-    background-image: url("resource://android/res/drawable-hdpi-v4/favicon.png");
+    background-image: url("resource://android/res/drawable-hdpi-v4/favicon_globe.png");
   }
 }
 
 @media screen and (min-resolution: 2dppx) {
   #filter-button {
     background-image: url("resource://android/res/drawable-xhdpi-v4/ab_search.png");
   }
 
   #filter-clear {
     background-image: url("resource://android/res/drawable-hdpi-v4/close_edit_mode_light.png");
   }
 
   .icon {
-    background-image: url("resource://android/res/drawable-xhdpi-v4/favicon.png");
+    background-image: url("resource://android/res/drawable-xhdpi-v4/favicon_globe.png");
   }
 }
--- a/services/sync/Makefile.in
+++ b/services/sync/Makefile.in
@@ -1,14 +1,14 @@
 # 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/.
 
 # Definitions used by constants.js.
-weave_version := 1.42.0
+weave_version := 1.43.0
 weave_id      := {340c2bbc-ce74-4362-90b5-7c26312808ef}
 
 # Preprocess files.
 SYNC_PP := modules/constants.js
 SYNC_PP_FLAGS := \
  -Dweave_version=$(weave_version) \
  -Dweave_id='$(weave_id)'
 SYNC_PP_PATH = $(FINAL_TARGET)/modules/services-sync
--- a/toolkit/components/search/nsSearchSuggestions.js
+++ b/toolkit/components/search/nsSearchSuggestions.js
@@ -69,23 +69,16 @@ SuggestAutoComplete.prototype = {
       // "comments" column values for suggestions starts as empty strings
       let comments = new Array(results.remote.length).fill("", 1);
       comments[0] = this._suggestionLabel;
       // now put the history results above the suggestions
       finalResults = finalResults.concat(results.remote);
       finalComments = finalComments.concat(comments);
     }
 
-    // If no result, add the search term so that the panel of the new UI is shown anyway.
-    if (!finalResults.length &&
-        Services.prefs.getBoolPref("browser.search.showOneOffButtons")) {
-      finalResults.push(results.term);
-      finalComments.push("");
-    }
-
     // Notify the FE of our new results
     this.onResultsReady(results.term, finalResults, finalComments, results.formHistoryResult);
   },
 
   /**
    * Notifies the front end of new results.
    * @param searchString  the user's query string
    * @param results       an array of results to the search
--- a/toolkit/components/viewsource/content/viewPartialSource.js
+++ b/toolkit/components/viewsource/content/viewPartialSource.js
@@ -169,24 +169,24 @@ function viewPartialSourceForSelection(s
   // before drawing the selection.
   if (canDrawSelection) {
     window.document.getElementById("content").addEventListener("load", drawSelection, true);
   }
 
   // all our content is held by the data:URI and URIs are internally stored as utf-8 (see nsIURI.idl)
   var loadFlags = Components.interfaces.nsIWebNavigation.LOAD_FLAGS_NONE;
   var referrerPolicy = Components.interfaces.nsIHttpChannel.REFERRER_POLICY_DEFAULT;
-  getWebNavigation().loadURIWithOptions((isHTML ?
-                                         "view-source:data:text/html;charset=utf-8," :
-                                         "view-source:data:application/xml;charset=utf-8,")
-                                        + encodeURIComponent(tmpNode.innerHTML),
-                                        loadFlags,
-                                        null, referrerPolicy,  // referrer
-                                        null, null,  // postData, headers
-                                        Services.io.newURI(doc.baseURI, null, null));
+  ViewSourceChrome.webNav.loadURIWithOptions((isHTML ?
+                                              "view-source:data:text/html;charset=utf-8," :
+                                              "view-source:data:application/xml;charset=utf-8,")
+                                             + encodeURIComponent(tmpNode.innerHTML),
+                                             loadFlags,
+                                             null, referrerPolicy,  // referrer
+                                             null, null,  // postData, headers
+                                             Services.io.newURI(doc.baseURI, null, null));
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // helper to get a path like FIXptr, but with an array instead of the "tumbler" notation
 // see FIXptr: http://lists.w3.org/Archives/Public/www-xml-linking-comments/2001AprJun/att-0074/01-NOTE-FIXptr-20010425.htm
 function getPath(ancestor, node)
 {
   var n = node;
--- a/toolkit/components/viewsource/content/viewPartialSource.xul
+++ b/toolkit/components/viewsource/content/viewPartialSource.xul
@@ -45,19 +45,19 @@
   <command id="cmd_close" oncommand="window.close();"/>
   <commandset id="editMenuCommands"/>
   <command id="cmd_find"
            oncommand="document.getElementById('FindToolbar').onFindCommand();"/>
   <command id="cmd_findAgain"
            oncommand="document.getElementById('FindToolbar').onFindAgainCommand(false);"/>
   <command id="cmd_findPrevious"
            oncommand="document.getElementById('FindToolbar').onFindAgainCommand(true);"/>
-  <command id="cmd_goToLine" oncommand="ViewSourceGoToLine();" disabled="true"/>
-  <command id="cmd_highlightSyntax" oncommand="highlightSyntax();"/>
-  <command id="cmd_wrapLongLines" oncommand="wrapLongLines()"/>
+  <command id="cmd_goToLine" oncommand="ViewSourceChrome.promptAndGoToLine();" disabled="true"/>
+  <command id="cmd_highlightSyntax" oncommand="ViewSourceChrome.toggleSyntaxHighlighting();"/>
+  <command id="cmd_wrapLongLines" oncommand="ViewSourceChrome.toggleWrapping();"/>
   <command id="cmd_textZoomReduce" oncommand="ZoomManager.reduce();"/>
   <command id="cmd_textZoomEnlarge" oncommand="ZoomManager.enlarge();"/>
   <command id="cmd_textZoomReset" oncommand="ZoomManager.reset();"/>
 
   <keyset id="editMenuKeys"/>
   <keyset id="viewSourceKeys">
     <key id="key_savePage" key="&savePageCmd.commandkey;" modifiers="accel" command="cmd_savePage"/>
     <key id="key_print" key="&printCmd.commandkey;" modifiers="accel" command="cmd_print"/>
@@ -68,29 +68,28 @@
     <key id="key_textZoomEnlarge2" key="&textEnlarge.commandkey2;" command="cmd_textZoomEnlarge" modifiers="accel"/>
     <key id="key_textZoomEnlarge3" key="&textEnlarge.commandkey3;" command="cmd_textZoomEnlarge" modifiers="accel"/>
     <key id="key_textZoomReduce"  key="&textReduce.commandkey;" command="cmd_textZoomReduce" modifiers="accel"/>
     <key id="key_textZoomReduce2"  key="&textReduce.commandkey2;" command="cmd_textZoomReduce" modifiers="accel"/>
     <key id="key_textZoomReset" key="&textReset.commandkey;" command="cmd_textZoomReset" modifiers="accel"/>
     <key id="key_textZoomReset2" key="&textReset.commandkey2;" command="cmd_textZoomReset" modifiers="accel"/>
   </keyset>
 
-  <menupopup id="viewSourceContextMenu"
-             onpopupshowing="contextMenuShowing();">
+  <menupopup id="viewSourceContextMenu">
     <menuitem id="cMenu_findAgain"/>
     <menuseparator/>
     <menuitem id="cMenu_copy"/>
     <menuitem id="context-copyLink"
               label="&copyLinkCmd.label;"
               accesskey="&copyLinkCmd.accesskey;"
-              oncommand="contextMenuCopyLinkOrEmail();"/>
+              oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
     <menuitem id="context-copyEmail"
               label="&copyEmailCmd.label;"
               accesskey="&copyEmailCmd.accesskey;"
-              oncommand="contextMenuCopyLinkOrEmail();"/>
+              oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
     <menuseparator/>
     <menuitem id="cMenu_selectAll"/>
   </menupopup>
 
   <!-- Menu --> 
   <toolbox id="viewSource-toolbox">
     <menubar id="viewSource-main-menubar">
 
@@ -152,13 +151,13 @@
                     label="&menu_highlightSyntax.label;" accesskey="&menu_highlightSyntax.accesskey;"/>
         </menupopup>
       </menu>
     </menubar>  
   </toolbox>
 
   <vbox id="appcontent" flex="1">
     <browser id="content" type="content-primary" name="content" src="about:blank" flex="1"
-             disablehistory="true" context="viewSourceContextMenu"/>
+             disablehistory="true" context="viewSourceContextMenu" />
     <findbar id="FindToolbar" browserid="content"/>
   </vbox>
 
 </window>
new file mode 100644
--- /dev/null
+++ b/toolkit/components/viewsource/content/viewSource-content.js
@@ -0,0 +1,703 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+const { utils: Cu, interfaces: Ci, classes: Cc } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+XPCOMUtils.defineLazyModuleGetter(this, "BrowserUtils",
+  "resource://gre/modules/BrowserUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask",
+  "resource://gre/modules/DeferredTask.jsm");
+
+const BUNDLE_URL = "chrome://global/locale/viewSource.properties";
+
+let global = this;
+
+/**
+ * ViewSourceContent should be loaded in the <xul:browser> of the
+ * view source window, and initialized as soon as it has loaded.
+ */
+let ViewSourceContent = {
+  /**
+   * We'll act as an nsISelectionListener as well so that we can send
+   * updates to the view source window's status bar.
+   */
+  QueryInterface: XPCOMUtils.generateQI([Ci.nsISelectionListener]),
+
+  /**
+   * These are the messages that ViewSourceContent is prepared to listen
+   * for. If you need ViewSourceContent to handle more messages, add them
+   * here.
+   */
+  messages: [
+    "ViewSource:LoadSource",
+    "ViewSource:LoadSourceDeprecated",
+    "ViewSource:GoToLine",
+    "ViewSource:ToggleWrapping",
+    "ViewSource:ToggleSyntaxHighlighting",
+    "ViewSource:SetCharacterSet",
+  ],
+
+  /**
+   * ViewSourceContent is attached as an nsISelectionListener on pageshow,
+   * and removed on pagehide. When the initial about:blank is transitioned
+   * away from, a pagehide is fired without us having attached ourselves
+   * first. We use this boolean to keep track of whether or not we're
+   * attached, so we don't attempt to remove our listener when it's not
+   * yet there (which throws).
+   */
+  selectionListenerAttached: false,
+
+  /**
+   * This should be called as soon as this frame script has loaded.
+   */
+  init() {
+    this.messages.forEach((msgName) => {
+      addMessageListener(msgName, this);
+    });
+
+    addEventListener("pagehide", this, true);
+    addEventListener("pageshow", this, true);
+    addEventListener("click", this);
+    addEventListener("unload", this);
+    Services.els.addSystemEventListener(global, "contextmenu", this, false);
+  },
+
+  /**
+   * This should be called when the frame script is being unloaded,
+   * and the browser is tearing down.
+   */
+  uninit() {
+    this.messages.forEach((msgName) => {
+      removeMessageListener(msgName, this);
+    });
+
+    removeEventListener("pagehide", this, true);
+    removeEventListener("pageshow", this, true);
+    removeEventListener("click", this);
+    removeEventListener("unload", this);
+
+    Services.els.removeSystemEventListener(global, "contextmenu", this, false);
+
+    // Cancel any pending toolbar updates.
+    if (this.updateStatusTask) {
+      this.updateStatusTask.disarm();
+    }
+  },
+
+  /**
+   * Anything added to the messages array will get handled here, and should
+   * get dispatched to a specific function for the message name.
+   */
+  receiveMessage(msg) {
+    let data = msg.data;
+    let objects = msg.objects;
+    switch(msg.name) {
+      case "ViewSource:LoadSource":
+        this.viewSource(data.URL, data.outerWindowID, data.lineNumber,
+                        data.shouldWrap);
+        break;
+      case "ViewSource:LoadSourceDeprecated":
+        this.viewSourceDeprecated(data.URL, objects.pageDescriptor, data.lineNumber,
+                                  data.forcedCharSet);
+        break;
+      case "ViewSource:GoToLine":
+        this.goToLine(data.lineNumber);
+        break;
+      case "ViewSource:ToggleWrapping":
+        this.toggleWrapping();
+        break;
+      case "ViewSource:ToggleSyntaxHighlighting":
+        this.toggleSyntaxHighlighting();
+        break;
+      case "ViewSource:SetCharacterSet":
+        this.setCharacterSet(data.charset, data.doPageLoad);
+        break;
+    }
+  },
+
+  /**
+   * Any events should get handled here, and should get dispatched to
+   * a specific function for the event type.
+   */
+  handleEvent(event) {
+    switch(event.type) {
+      case "pagehide":
+        this.onPageHide(event);
+        break;
+      case "pageshow":
+        this.onPageShow(event);
+        break;
+      case "click":
+        this.onClick(event);
+        break;
+      case "unload":
+        this.uninit();
+        break;
+      case "contextmenu":
+        this.onContextMenu(event);
+        break;
+    }
+  },
+
+  /**
+   * A getter for the view source string bundle.
+   */
+  get bundle() {
+    delete this.bundle;
+    this.bundle = Services.strings.createBundle(BUNDLE_URL);
+    return this.bundle;
+  },
+
+  /**
+   * A shortcut to the nsISelectionController for the content.
+   */
+  get selectionController() {
+    return docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsISelectionDisplay)
+                   .QueryInterface(Ci.nsISelectionController);
+  },
+
+  /**
+   * Called when the parent sends a message to view some source code.
+   *
+   * @param URL (required)
+   *        The URL string of the source to be shown.
+   * @param outerWindowID (optional)
+   *        The outerWindowID of the content window that has hosted
+   *        the document, in case we want to retrieve it from the network
+   *        cache.
+   * @param lineNumber (optional)
+   *        The line number to focus as soon as the source has finished
+   *        loading.
+   */
+  viewSource(URL, outerWindowID, lineNumber) {
+    let pageDescriptor, forcedCharSet;
+
+    if (outerWindowID) {
+      let contentWindow = Services.wm.getOuterWindowWithId(outerWindowID);
+      let requestor = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor);
+
+      try {
+        let otherWebNav = requestor.getInterface(Ci.nsIWebNavigation);
+        pageDescriptor = otherWebNav.QueryInterface(Ci.nsIWebPageDescriptor)
+                                    .currentDescriptor;
+      } catch(e) {
+        // We couldn't get the page descriptor, so we'll probably end up re-retrieving
+        // this document off of the network.
+      }
+
+      let utils = requestor.getInterface(Ci.nsIDOMWindowUtils);
+      let doc = contentWindow.document;
+      let forcedCharSet = utils.docCharsetIsForced ? doc.characterSet
+                                                   : null;
+    }
+
+    this.loadSource(URL, pageDescriptor, lineNumber, forcedCharSet);
+  },
+
+  /**
+   * Called when the parent is using the deprecated API for viewSource.xul.
+   * This function will throw if it's called on a remote browser.
+   *
+   * @param URL (required)
+   *        The URL string of the source to be shown.
+   * @param pageDescriptor (optional)
+   *        The currentDescriptor off of an nsIWebPageDescriptor, in the
+   *        event that the caller wants to try to load the source out of
+   *        the network cache.
+   * @param lineNumber (optional)
+   *        The line number to focus as soon as the source has finished
+   *        loading.
+   * @param forcedCharSet (optional)
+   *        The document character set to use instead of the default one.
+   */
+  viewSourceDeprecated(URL, pageDescriptor, lineNumber, forcedCharSet) {
+    // This should not be called if this frame script is running
+    // in a content process!
+    if (Services.appinfo.processType != Services.appinfo.PROCESS_TYPE_DEFAULT) {
+      throw new Error("ViewSource deprecated API should not be used with " +
+                      "remote browsers.");
+    }
+
+    this.loadSource(URL, pageDescriptor, lineNumber, forcedCharSet);
+  },
+
+  /**
+   * Common utility function used by both the current and deprecated APIs
+   * for loading source.
+   *
+   * @param URL (required)
+   *        The URL string of the source to be shown.
+   * @param pageDescriptor (optional)
+   *        The currentDescriptor off of an nsIWebPageDescriptor, in the
+   *        event that the caller wants to try to load the source out of
+   *        the network cache.
+   * @param lineNumber (optional)
+   *        The line number to focus as soon as the source has finished
+   *        loading.
+   * @param forcedCharSet (optional)
+   *        The document character set to use instead of the default one.
+   */
+  loadSource(URL, pageDescriptor, lineNumber, forcedCharSet) {
+    const viewSrcURL = "view-source:" + URL;
+    let loadFromURL = false;
+
+    if (forcedCharSet) {
+      docShell.charset = forcedCharSet;
+    }
+
+    if (lineNumber) {
+      let doneLoading = (event) => {
+        this.goToLine(lineNumber);
+        removeEventListener("pageshow", doneLoading);
+      };
+
+      addEventListener("pageshow", doneLoading);
+    }
+
+    if (!pageDescriptor) {
+      this.loadSourceFromURL(viewSrcURL);
+      return;
+    }
+
+    try {
+      let pageLoader = docShell.QueryInterface(Ci.nsIWebPageDescriptor);
+      pageLoader.loadPage(pageDescriptor,
+                          Ci.nsIWebPageDescriptor.DISPLAY_AS_SOURCE);
+    } catch(e) {
+      // We were not able to load the source from the network cache.
+      this.loadSourceFromURL(viewSrcURL);
+      return;
+    }
+
+    let shEntrySource = pageDescriptor.QueryInterface(Ci.nsISHEntry);
+    let shEntry = Cc["@mozilla.org/browser/session-history-entry;1"]
+                    .createInstance(Ci.nsISHEntry);
+    shEntry.setURI(BrowserUtils.makeURI(viewSrcURL, null, null));
+    shEntry.setTitle(viewSrcURL);
+    shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
+    shEntry.cacheKey = shEntrySource.cacheKey;
+    docShell.QueryInterface(Ci.nsIWebNavigation)
+            .sessionHistory
+            .QueryInterface(Ci.nsISHistoryInternal)
+            .addEntry(shEntry, true);
+  },
+
+  /**
+   * Load some URL in the browser.
+   *
+   * @param URL
+   *        The URL string to load.
+   */
+  loadSourceFromURL(URL) {
+    let loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
+    let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
+    webNav.loadURI(URL, loadFlags, null, null, null);
+  },
+
+  /**
+   * This handler is specifically for click events bubbling up from
+   * error page content, which can show up if the user attempts to
+   * view the source of an attack page.
+   */
+  onClick(event) {
+    // Don't trust synthetic events
+    if (!event.isTrusted || event.target.localName != "button")
+      return;
+
+    let target = event.originalTarget;
+    let errorDoc = target.ownerDocument;
+
+    if (/^about:blocked/.test(errorDoc.documentURI)) {
+      // The event came from a button on a malware/phishing block page
+
+      if (target == errorDoc.getElementById("getMeOutButton")) {
+        // Instead of loading some safe page, just close the window
+        sendAsyncMessage("ViewSource:Close");
+      } else if (target == errorDoc.getElementById("reportButton")) {
+        // This is the "Why is this site blocked" button. We redirect
+        // to the generic page describing phishing/malware protection.
+        let URL = Services.urlFormatter.formatURLPref("app.support.baseURL");
+        sendAsyncMessage("ViewSource:OpenURL", { URL })
+      } else if (target == errorDoc.getElementById("ignoreWarningButton")) {
+        // Allow users to override and continue through to the site
+        docShell.QueryInterface(Ci.nsIWebNavigation)
+                .loadURIWithOptions(content.location.href,
+                                    Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
+                                    null, Ci.nsIHttpChannel.REFERRER_POLICY_DEFAULT,
+                                    null, null, null);
+      }
+    }
+  },
+
+  /**
+   * Handler for the pageshow event.
+   *
+   * @param event
+   *        The pageshow event being handled.
+   */
+  onPageShow(event) {
+    content.getSelection()
+           .QueryInterface(Ci.nsISelectionPrivate)
+           .addSelectionListener(this);
+    this.selectionListenerAttached = true;
+
+    content.focus();
+    sendAsyncMessage("ViewSource:SourceLoaded");
+  },
+
+  /**
+   * Handler for the pagehide event.
+   *
+   * @param event
+   *        The pagehide event being handled.
+   */
+  onPageHide(event) {
+    // The initial about:blank will fire pagehide before we
+    // ever set a selectionListener, so we have a boolean around
+    // to keep track of when the listener is attached.
+    if (this.selectionListenerAttached) {
+      content.getSelection()
+             .QueryInterface(Ci.nsISelectionPrivate)
+             .removeSelectionListener(this);
+      this.selectionListenerAttached = false;
+    }
+    sendAsyncMessage("ViewSource:SourceUnloaded");
+  },
+
+  onContextMenu(event) {
+    let addonInfo = {};
+    let subject = {
+      event: event,
+      addonInfo: addonInfo,
+    };
+
+    subject.wrappedJSObject = subject;
+    Services.obs.notifyObservers(subject, "content-contextmenu", null);
+
+    let node = event.target;
+
+    let result = {
+      isEmail: false,
+      isLink: false,
+      href: "",
+      // We have to pass these in the event that we're running in
+      // a remote browser, so that ViewSourceChrome knows where to
+      // open the context menu.
+      screenX: event.screenX,
+      screenY: event.screenY,
+    };
+
+    if (node && node.localName == "a") {
+      result.isLink = node.href.startsWith("view-source:");
+      result.isEmail = node.href.startsWith("mailto:");
+      result.href = node.href.substring(node.href.indexOf(":") + 1);
+    }
+
+    sendSyncMessage("ViewSource:ContextMenuOpening", result);
+  },
+
+  /**
+   * Attempts to go to a particular line in the source code being
+   * shown. If it succeeds in finding the line, it will fire a
+   * "ViewSource:GoToLine:Success" message, passing up an object
+   * with the lineNumber we just went to. If it cannot find the line,
+   * it will fire a "ViewSource:GoToLine:Failed" message.
+   *
+   * @param lineNumber
+   *        The line number to attempt to go to.
+   */
+  goToLine(lineNumber) {
+    let body = content.document.body;
+
+    // The source document is made up of a number of pre elements with
+    // id attributes in the format <pre id="line123">, meaning that
+    // the first line in the pre element is number 123.
+    // Do binary search to find the pre element containing the line.
+    // However, in the plain text case, we have only one pre without an
+    // attribute, so assume it begins on line 1.
+    let pre;
+    for (let lbound = 0, ubound = body.childNodes.length; ; ) {
+      let middle = (lbound + ubound) >> 1;
+      pre = body.childNodes[middle];
+
+      let firstLine = pre.id ? parseInt(pre.id.substring(4)) : 1;
+
+      if (lbound == ubound - 1) {
+        break;
+      }
+
+      if (lineNumber >= firstLine) {
+        lbound = middle;
+      } else {
+        ubound = middle;
+      }
+    }
+
+    let result = {};
+    let found = this.findLocation(pre, lineNumber, null, -1, false, result);
+
+    if (!found) {
+      sendAsyncMessage("ViewSource:GoToLine:Failed");
+      return;
+    }
+
+    let selection = content.getSelection();
+    selection.removeAllRanges();
+
+    // In our case, the range's startOffset is after "\n" on the previous line.
+    // Tune the selection at the beginning of the next line and do some tweaking
+    // to position the focusNode and the caret at the beginning of the line.
+    selection.QueryInterface(Ci.nsISelectionPrivate)
+      .interlinePosition = true;
+
+    selection.addRange(result.range);
+
+    if (!selection.isCollapsed) {
+      selection.collapseToEnd();
+
+      let offset = result.range.startOffset;
+      let node = result.range.startContainer;
+      if (offset < node.data.length) {
+        // The same text node spans across the "\n", just focus where we were.
+        selection.extend(node, offset);
+      }
+      else {
+        // There is another tag just after the "\n", hook there. We need
+        // to focus a safe point because there are edgy cases such as
+        // <span>...\n</span><span>...</span> vs.
+        // <span>...\n<span>...</span></span><span>...</span>
+        node = node.nextSibling ? node.nextSibling : node.parentNode.nextSibling;
+        selection.extend(node, 0);
+      }
+    }
+
+    let selCon = this.selectionController;
+    selCon.setDisplaySelection(Ci.nsISelectionController.SELECTION_ON);
+    selCon.setCaretVisibilityDuringSelection(true);
+
+    // Scroll the beginning of the line into view.
+    selCon.scrollSelectionIntoView(
+      Ci.nsISelectionController.SELECTION_NORMAL,
+      Ci.nsISelectionController.SELECTION_FOCUS_REGION,
+      true);
+
+    sendAsyncMessage("ViewSource:GoToLine:Success", { lineNumber });
+  },
+
+
+  /**
+   * Some old code from the original view source implementation. Original
+   * documentation follows:
+   *
+   * "Loops through the text lines in the pre element. The arguments are either
+   *  (pre, line) or (node, offset, interlinePosition). result is an out
+   *  argument. If (pre, line) are specified (and node == null), result.range is
+   *  a range spanning the specified line. If the (node, offset,
+   *  interlinePosition) are specified, result.line and result.col are the line
+   *  and column number of the specified offset in the specified node relative to
+   *  the whole file."
+   */
+  findLocation(pre, lineNumber, node, offset, interlinePosition, result) {
+    if (node && !pre) {
+      // Look upwards to find the current pre element.
+      for (pre = node;
+           pre.nodeName != "PRE";
+           pre = pre.parentNode);
+    }
+
+    // The source document is made up of a number of pre elements with
+    // id attributes in the format <pre id="line123">, meaning that
+    // the first line in the pre element is number 123.
+    // However, in the plain text case, there is only one <pre> without an id,
+    // so assume line 1.
+    let curLine = pre.id ? parseInt(pre.id.substring(4)) : 1;
+
+    // Walk through each of the text nodes and count newlines.
+    let treewalker = content.document
+        .createTreeWalker(pre, Ci.nsIDOMNodeFilter.SHOW_TEXT, null);
+
+    // The column number of the first character in the current text node.
+    let firstCol = 1;
+
+    let found = false;
+    for (let textNode = treewalker.firstChild();
+         textNode && !found;
+         textNode = treewalker.nextNode()) {
+
+      // \r is not a valid character in the DOM, so we only check for \n.
+      let lineArray = textNode.data.split(/\n/);
+      let lastLineInNode = curLine + lineArray.length - 1;
+
+      // Check if we can skip the text node without further inspection.
+      if (node ? (textNode != node) : (lastLineInNode < lineNumber)) {
+        if (lineArray.length > 1) {
+          firstCol = 1;
+        }
+        firstCol += lineArray[lineArray.length - 1].length;
+        curLine = lastLineInNode;
+        continue;
+      }
+
+      // curPos is the offset within the current text node of the first
+      // character in the current line.
+      for (var i = 0, curPos = 0;
+           i < lineArray.length;
+           curPos += lineArray[i++].length + 1) {
+
+        if (i > 0) {
+          curLine++;
+        }
+
+        if (node) {
+          if (offset >= curPos && offset <= curPos + lineArray[i].length) {
+            // If we are right after the \n of a line and interlinePosition is
+            // false, the caret looks as if it were at the end of the previous
+            // line, so we display that line and column instead.
+
+            if (i > 0 && offset == curPos && !interlinePosition) {
+              result.line = curLine - 1;
+              var prevPos = curPos - lineArray[i - 1].length;
+              result.col = (i == 1 ? firstCol : 1) + offset - prevPos;
+            } else {
+              result.line = curLine;
+              result.col = (i == 0 ? firstCol : 1) + offset - curPos;
+            }
+            found = true;
+
+            break;
+          }
+
+        } else {
+          if (curLine == lineNumber && !("range" in result)) {
+            result.range = content.document.createRange();
+            result.range.setStart(textNode, curPos);
+
+            // This will always be overridden later, except when we look for
+            // the very last line in the file (this is the only line that does
+            // not end with \n).
+            result.range.setEndAfter(pre.lastChild);
+
+          } else if (curLine == lineNumber + 1) {
+            result.range.setEnd(textNode, curPos - 1);
+            found = true;
+            break;
+          }
+        }
+      }
+    }
+
+    return found || ("range" in result);
+  },
+
+  /**
+   * Toggles the "wrap" class on the document body, which sets whether
+   * or not long lines are wrapped.
+   */
+  toggleWrapping() {
+    let body = content.document.body;
+    body.classList.toggle("wrap");
+  },
+
+  /**
+   * Called when the parent has changed the syntax highlighting pref.
+   */
+  toggleSyntaxHighlighting() {
+    // The parent process should have set the view_source.syntax_highlight
+    // pref to the desired value. The reload brings that setting into
+    // effect.
+    this.reload();
+  },
+
+  /**
+   * Called when the parent has changed the character set to view the
+   * source with.
+   *
+   * @param charset
+   *        The character set to use.
+   * @param doPageLoad
+   *        Whether or not we should reload the page ourselves with the
+   *        nsIWebPageDescriptor. Part of a workaround for bug 136322.
+   */
+  setCharacterSet(charset, doPageLoad) {
+    docShell.charset = charset;
+    if (doPageLoad) {
+      this.reload();
+    }
+  },
+
+  /**
+   * Reloads the content.
+   */
+  reload() {
+    let pageLoader = docShell.QueryInterface(Ci.nsIWebPageDescriptor);
+    try {
+      pageLoader.loadPage(pageLoader.currentDescriptor,
+                          Ci.nsIWebPageDescriptor.DISPLAY_NORMAL);
+    } catch(e) {
+      let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
+      webNav.reload(Ci.nsIWebNavigation.LOAD_FLAGS_NONE);
+    }
+  },
+
+  /**
+   * A reference to a DeferredTask that is armed every time the
+   * selection changes.
+   */
+  updateStatusTask: null,
+
+  /**
+   * Called once the DeferredTask fires. Sends a message up to the
+   * parent to update the status bar text.
+   */
+  updateStatus() {
+    let selection = content.getSelection();
+
+    if (!selection.focusNode) {
+      sendAsyncMessage("ViewSource:UpdateStatus", { label: "" });
+      return;
+    }
+    if (selection.focusNode.nodeType != Ci.nsIDOMNode.TEXT_NODE) {
+      return;
+    }
+
+    let selCon = this.selectionController;
+    selCon.setDisplaySelection(Ci.nsISelectionController.SELECTION_ON);
+    selCon.setCaretVisibilityDuringSelection(true);
+
+    let interlinePosition = selection.QueryInterface(Ci.nsISelectionPrivate)
+                                     .interlinePosition;
+
+    let result = {};
+    this.findLocation(null, -1,
+        selection.focusNode, selection.focusOffset, interlinePosition, result);
+
+    let label = this.bundle.formatStringFromName("statusBarLineCol",
+                                                 [result.line, result.col], 2);
+    sendAsyncMessage("ViewSource:UpdateStatus", { label });
+  },
+
+  /**
+   * nsISelectionListener
+   */
+
+  /**
+   * Gets called every time the selection is changed. Coalesces frequent
+   * changes, and calls updateStatus after 100ms of no selection change
+   * activity.
+   */
+  notifySelectionChanged(doc, sel, reason) {
+    if (!this.updateStatusTask) {
+      this.updateStatusTask = new DeferredTask(() => {
+        this.updateStatus();
+      }, 100);
+    }
+
+    this.updateStatusTask.arm();
+  },
+};
+ViewSourceContent.init();
--- a/toolkit/components/viewsource/content/viewSource.css
+++ b/toolkit/components/viewsource/content/viewSource.css
@@ -1,7 +1,11 @@
 /* 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/. */
 
 toolbar[printpreview="true"] {
   -moz-binding: url("chrome://global/content/printPreviewBindings.xml#printpreviewtoolbar");
 }
+
+browser[remote="true"] {
+  -moz-binding: url("chrome://global/content/bindings/remote-browser.xml#remote-browser");
+}
\ No newline at end of file
--- a/toolkit/components/viewsource/content/viewSource.js
+++ b/toolkit/components/viewsource/content/viewSource.js
@@ -1,703 +1,935 @@
 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-Components.utils.import("resource://gre/modules/Services.jsm");
-Components.utils.import("resource://gre/modules/CharsetMenu.jsm");
+const { utils: Cu, interfaces: Ci, classes: Cc } = Components;
+
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
-const Cc = Components.classes;
-const Ci = Components.interfaces;
-
-var gLastLineFound = '';
-var gGoToLine = 0;
+XPCOMUtils.defineLazyModuleGetter(this, "Services",
+  "resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "CharsetMenu",
+  "resource://gre/modules/CharsetMenu.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Deprecated",
+  "resource://gre/modules/Deprecated.jsm");
 
 [
   ["gBrowser",          "content"],
   ["gViewSourceBundle", "viewSourceBundle"],
   ["gContextMenu",      "viewSourceContextMenu"]
 ].forEach(function ([name, id]) {
   window.__defineGetter__(name, function () {
     var element = document.getElementById(id);
     if (!element)
       return null;
     delete window[name];
     return window[name] = element;
   });
 });
 
-// viewZoomOverlay.js uses this
-function getBrowser() {
-  return gBrowser;
-}
+/**
+ * ViewSourceChrome is the primary interface for interacting with
+ * the view source browser. It initializes itself on script load.
+ */
+let ViewSourceChrome = {
+  /**
+   * Holds the value of the last line found via the "Go to line"
+   * command, to pre-populate the prompt the next time it is
+   * opened.
+   */
+  lastLineFound: null,
+
+  /**
+   * The context menu, when opened from the content process, sends
+   * up a chunk of serialized data describing the items that the
+   * context menu is being opened on. This allows us to avoid using
+   * CPOWs.
+   */
+  contextMenuData: {},
 
-this.__defineGetter__("gPageLoader", function () {
-  var webnav = getWebNavigation();
-  if (!webnav)
-    return null;
-  delete this.gPageLoader;
-  return this.gPageLoader = webnav.QueryInterface(Ci.nsIWebPageDescriptor);
-});
+  /**
+   * These are the messages that ViewSourceChrome will listen for
+   * from the frame script it injects. Any message names added here
+   * will automatically have ViewSourceChrome listen for those messages,
+   * and remove the listeners on teardown.
+   */
+  messages: [
+    "ViewSource:SourceLoaded",
+    "ViewSource:SourceUnloaded",
+    "ViewSource:Close",
+    "ViewSource:OpenURL",
+    "ViewSource:GoToLine:Success",
+    "ViewSource:GoToLine:Failed",
+    "ViewSource:UpdateStatus",
+    "ViewSource:ContextMenuOpening",
+  ],
+
+  /**
+   * This should be called as soon as the script loads. When this function
+   * executes, we can assume the DOM content has not yet loaded.
+   */
+  init() {
+    // We use the window message manager so that if we switch remoteness of the
+    // browser (which we might do if we're attempting to load the document
+    // source out of the network cache), we automatically re-load the frame
+    // script.
+    let wMM = window.messageManager;
+    wMM.loadFrameScript("chrome://global/content/viewSource-content.js", true);
+    this.messages.forEach((msgName) => {
+      wMM.addMessageListener(msgName, this);
+    });
+
+    this.shouldWrap = Services.prefs.getBoolPref("view_source.wrap_long_lines");
+    this.shouldHighlight =
+      Services.prefs.getBoolPref("view_source.syntax_highlight");
 
-var gSelectionListener = {
-  timeout: 0,
-  attached: false,
-  notifySelectionChanged: function(doc, sel, reason)
-  {
-    // Coalesce notifications within 100ms intervals.
-    if (!this.timeout)
-      this.timeout = setTimeout(updateStatusBar, 100);
-  }
-}
+    addEventListener("load", this);
+    addEventListener("unload", this);
+    addEventListener("AppCommand", this, true);
+    addEventListener("MozSwipeGesture", this, true);
+  },
+
+  /**
+   * This should be called when the window is closing. This function should
+   * clean up event and message listeners.
+   */
+  uninit() {
+    let wMM = window.messageManager;
+    this.messages.forEach((msgName) => {
+      wMM.removeMessageListener(msgName, this);
+    });
+
+    // "load" event listener is removed in its handler, to
+    // ensure we only fire it once.
+    removeEventListener("unload", this);
+    removeEventListener("AppCommand", this, true);
+    removeEventListener("MozSwipeGesture", this, true);
+    gContextMenu.removeEventListener("popupshowing", this);
+    gContextMenu.removeEventListener("popuphidden", this);
+    Services.els.removeSystemEventListener(gBrowser, "dragover", this, true);
+    Services.els.removeSystemEventListener(gBrowser, "drop", this, true);
+  },
 
-function onLoadViewSource() 
-{
-  viewSource(window.arguments[0]);
-  document.commandDispatcher.focusedWindow = content;
-  gBrowser.droppedLinkHandler = function (event, url, name) {
-    viewSource(url)
-    event.preventDefault();
-  }
+  /**
+   * Anything added to the messages array will get handled here, and should
+   * get dispatched to a specific function for the message name.
+   */
+  receiveMessage(message) {
+    let data = message.data;
 
-  if (!isHistoryEnabled()) {
-    // Disable the BACK and FORWARD commands and hide the related menu items.
-    var viewSourceNavigation = document.getElementById("viewSourceNavigation");
-    viewSourceNavigation.setAttribute("disabled", "true");
-    viewSourceNavigation.setAttribute("hidden", "true");
-  }
-}
-
-function isHistoryEnabled() {
-  return !gBrowser.hasAttribute("disablehistory");
-}
+    switch(message.name) {
+      case "ViewSource:SourceLoaded":
+        this.onSourceLoaded();
+        break;
+      case "ViewSource:SourceUnloaded":
+        this.onSourceUnloaded();
+        break;
+      case "ViewSource:Close":
+        this.close();
+        break;
+      case "ViewSource:OpenURL":
+        this.openURL(data.URL);
+        break;
+      case "ViewSource:GoToLine:Failed":
+        this.onGoToLineFailed();
+        break;
+      case "ViewSource:GoToLine:Success":
+        this.onGoToLineSuccess(data.lineNumber);
+        break;
+      case "ViewSource:UpdateStatus":
+        this.updateStatus(data.label);
+        break;
+      case "ViewSource:ContextMenuOpening":
+        this.onContextMenuOpening(data.isLink, data.isEmail, data.href);
+        if (gBrowser.isRemoteBrowser) {
+          this.openContextMenu(data.screenX, data.screenY);
+        }
+        break;
+    }
+  },
 
-function getSelectionController() {
-  return gBrowser.docShell
-                 .QueryInterface(Ci.nsIInterfaceRequestor)
-                 .getInterface(Ci.nsISelectionDisplay)
-                 .QueryInterface(Ci.nsISelectionController);
-}
+  /**
+   * Any events should get handled here, and should get dispatched to
+   * a specific function for the event type.
+   */
+  handleEvent(event) {
+    switch(event.type) {
+      case "unload":
+        this.uninit();
+        break;
+      case "load":
+        this.onXULLoaded();
+        break;
+      case "AppCommand":
+        this.onAppCommand(event);
+        break;
+      case "MozSwipeGesture":
+        this.onSwipeGesture(event);
+        break;
+      case "popupshowing":
+        this.onContextMenuShowing(event);
+        break;
+      case "popuphidden":
+        this.onContextMenuHidden(event);
+        break;
+      case "dragover":
+        this.onDragOver(event);
+        break;
+      case "drop":
+        this.onDrop(event);
+        break;
+    }
+  },
 
-function viewSource(url)
-{
-  if (!url)
-    return; // throw Components.results.NS_ERROR_FAILURE;
-    
-  var viewSrcUrl = "view-source:" + url;
+  /**
+   * Getter that returns whether or not the view source browser
+   * has history enabled on it.
+   */
+  get historyEnabled() {
+    return !gBrowser.hasAttribute("disablehistory");
+  },
 
-  gBrowser.addEventListener("pagehide", onUnloadContent, true);
-  gBrowser.addEventListener("pageshow", onLoadContent, true);
-  gBrowser.addEventListener("click", onClickContent, false);
+  /**
+   * Getter for the message manager of the view source browser.
+   */
+  get mm() {
+    return gBrowser.messageManager;
+  },
 
-  var loadFromURL = true;
+  /**
+   * Getter for the nsIWebNavigation of the view source browser.
+   */
+  get webNav() {
+    return gBrowser.webNavigation;
+  },
+
+  /**
+   * Send the browser forward in its history.
+   */
+  goForward() {
+    gBrowser.goForward();
+  },
 
-  // Parse the 'arguments' supplied with the dialog.
-  //    arg[0] - URL string.
-  //    arg[1] - Charset value in the form 'charset=xxx'.
-  //    arg[2] - Page descriptor used to load content from the cache.
-  //    arg[3] - Line number to go to.
-  //    arg[4] - Whether charset was forced by the user
-
-  if ("arguments" in window) {
-    var arg;
+  /**
+   * Send the browser backward in its history.
+   */
+  goBack() {
+    gBrowser.goBack();
+  },
 
-    // Set the charset of the viewsource window...
-    var charset;
-    if (window.arguments.length >= 2) {
-      arg = window.arguments[1];
+  /**
+   * This should be called once when the DOM has finished loading. Here we
+   * set the state of various menu items, and add event listeners to
+   * DOM nodes.
+   *
+   * This is also the place where we handle any arguments that have been
+   * passed to viewSource.xul.
+   *
+   * Modern consumers should pass a single object argument to viewSource.xul:
+   *
+   *   URL (required):
+   *     A string URL for the page we'd like to view the source of.
+   *   browser:
+   *     The browser containing the document that we would like to view the
+   *     source of. This argument is optional if outerWindowID is not passed.
+   *   outerWindowID (optional):
+   *     The outerWindowID of the content window containing the document that
+   *     we want to view the source of. This is the only way of attempting to
+   *     load the source out of the network cache.
+   *   lineNumber (optional):
+   *     The line number to focus on once the source is loaded.
+   *
+   * The deprecated API has the opener pass in a number of arguments:
+   *
+   * arg[0] - URL string.
+   * arg[1] - Charset value string in the form 'charset=xxx'.
+   * arg[2] - Page descriptor from nsIWebPageDescriptor used to load content
+   *          from the cache.
+   * arg[3] - Line number to go to.
+   * arg[4] - Boolean for whether charset was forced by the user
+   */
+  onXULLoaded() {
+    // This handler should only ever run the first time the XUL is loaded.
+    removeEventListener("load", this);
 
-      try {
-        if (typeof(arg) == "string" && arg.indexOf('charset=') != -1) {
-          var arrayArgComponents = arg.split('=');
-          if (arrayArgComponents) {
-            // Remember the charset here so that it can be used below in case
-            // the document had a forced charset.
-            charset = arrayArgComponents[1];
-          }
-        }
-      } catch (ex) {
-        // Ignore the failure and keep processing arguments...
+    let wrapMenuItem = document.getElementById("menu_wrapLongLines");
+    if (this.shouldWrap) {
+      wrapMenuItem.setAttribute("checked", "true");
+    }
+
+    let highlightMenuItem = document.getElementById("menu_highlightSyntax");
+    if (this.shouldHighlight) {
+      highlightMenuItem.setAttribute("checked", "true");
+    }
+
+    gContextMenu.addEventListener("popupshowing", this);
+    gContextMenu.addEventListener("popuphidden", this);
+
+    Services.els.addSystemEventListener(gBrowser, "dragover", this, true);
+    Services.els.addSystemEventListener(gBrowser, "drop", this, true);
+
+    if (!this.historyEnabled) {
+      // Disable the BACK and FORWARD commands and hide the related menu items.
+      let viewSourceNavigation = document.getElementById("viewSourceNavigation");
+      if (viewSourceNavigation) {
+        viewSourceNavigation.setAttribute("disabled", "true");
+        viewSourceNavigation.setAttribute("hidden", "true");
       }
     }
-    // If the document had a forced charset, set it here also
-    if (window.arguments.length >= 5) {
-      arg = window.arguments[4];
+
+    // This will only work with non-remote browsers. See bug 1158377.
+    gBrowser.droppedLinkHandler = function (event, url, name) {
+      ViewSourceChrome.loadURL(url);
+      event.preventDefault();
+    };
+
+    // We require the first argument to do any loading of source.
+    // otherwise, we're done.
+    if (!window.arguments[0]) {
+      return;
+    }
 
-      try {
-        if (arg === true) {
-          gBrowser.docShell.charset = charset;
-        }
-      } catch (ex) {
-        // Ignore the failure and keep processing arguments...
+    if (typeof window.arguments[0] == "string") {
+      // We're using the deprecated API
+      return ViewSourceChrome._loadViewSourceDeprecated();
+    }
+
+    // We're using the modern API, which allows us to view the
+    // source of documents from out of process browsers.
+    let args = window.arguments[0];
+
+    if (!args.URL) {
+      throw new Error("Must supply a URL when opening view source.");
+    }
+
+    if (args.browser) {
+      // If we're dealing with a remote browser, then the browser
+      // for view source needs to be remote as well.
+      this.updateBrowserRemoteness(args.browser.isRemoteBrowser);
+    } else {
+      if (args.outerWindowID) {
+        throw new Error("Must supply the browser if passing the outerWindowID");
       }
     }
 
-    // Get any specified line to jump to.
-    if (window.arguments.length >= 4) {
-      arg = window.arguments[3];
-      gGoToLine = parseInt(arg);
+    this.mm.sendAsyncMessage("ViewSource:LoadSource", {
+      URL: args.URL,
+      outerWindowID: args.outerWindowID,
+      lineNumber: args.lineNumber,
+    });
+  },
+
+  /**
+   * This is the deprecated API for viewSource.xul, for old-timer consumers.
+   * This API might eventually go away.
+   */
+  _loadViewSourceDeprecated() {
+    Deprecated.warning("The arguments you're passing to viewSource.xul " +
+                       "are using an out-of-date API.",
+                       "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+    // Parse the 'arguments' supplied with the dialog.
+    //    arg[0] - URL string.
+    //    arg[1] - Charset value in the form 'charset=xxx'.
+    //    arg[2] - Page descriptor used to load content from the cache.
+    //    arg[3] - Line number to go to.
+    //    arg[4] - Whether charset was forced by the user
+
+    if (window.arguments[3] == "selection" ||
+        window.arguments[3] == "mathml") {
+      // viewPartialSource.js will take care of loading the content.
+      return;
+    }
+
+    if (window.arguments[2]) {
+      let pageDescriptor = window.arguments[2];
+      if (Cu.isCrossProcessWrapper(pageDescriptor)) {
+        throw new Error("Cannot pass a CPOW as the page descriptor to viewSource.xul.");
+      }
+    }
+
+    if (gBrowser.isRemoteBrowser) {
+      throw new Error("Deprecated view source API should not use a remote browser.");
+    }
+
+    let forcedCharSet;
+    if (window.arguments[4] && window.arguments[1].startsWith("charset=")) {
+      forcedCharSet = window.arguments[1].split("=")[1];
     }
 
-    // Use the page descriptor to load the content from the cache (if
-    // available).
-    if (window.arguments.length >= 3) {
-      arg = window.arguments[2];
+    gBrowser.messageManager.sendAsyncMessage("ViewSource:LoadSourceDeprecated", {
+      URL: window.arguments[0],
+      lineNumber: window.arguments[3],
+      forcedCharSet,
+    }, {
+      pageDescriptor: window.arguments[2],
+    });
+  },
 
-      try {
-        if (typeof(arg) == "object" && arg != null) {
-          // Load the page using the page descriptor rather than the URL.
-          // This allows the content to be fetched from the cache (if
-          // possible) rather than the network...
-          gPageLoader.loadPage(arg, gPageLoader.DISPLAY_AS_SOURCE);
-
-          // The content was successfully loaded.
-          loadFromURL = false;
+  /**
+   * Handler for the AppCommand event.
+   *
+   * @param event
+   *        The AppCommand event being handled.
+   */
+  onAppCommand(event) {
+    event.stopPropagation();
+    switch (event.command) {
+      case "Back":
+        this.goBack();
+        break;
+      case "Forward":
+        this.goForward();
+        break;
+    }
+  },
 
-          // Record the page load in the session history so <back> will work.
-          var shEntrySource = arg.QueryInterface(Ci.nsISHEntry);
-          var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"].createInstance(Ci.nsISHEntry);
-          shEntry.setURI(makeURI(viewSrcUrl, null, null));
-          shEntry.setTitle(viewSrcUrl);
-          shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory;
-          shEntry.cacheKey = shEntrySource.cacheKey;
-          gBrowser.sessionHistory
-                  .QueryInterface(Ci.nsISHistoryInternal)
-                  .addEntry(shEntry, true);
-        }
-      } catch(ex) {
-        // Ignore the failure.  The content will be loaded via the URL
-        // that was supplied in arg[0].
+  /**
+   * Handler for the MozSwipeGesture event.
+   *
+   * @param event
+   *        The MozSwipeGesture event being handled.
+   */
+  onSwipeGesture(event) {
+    event.stopPropagation();
+    switch (event.direction) {
+      case SimpleGestureEvent.DIRECTION_LEFT:
+        this.goBack();
+        break;
+      case SimpleGestureEvent.DIRECTION_RIGHT:
+        this.goForward();
+        break;
+      case SimpleGestureEvent.DIRECTION_UP:
+        goDoCommand("cmd_scrollTop");
+        break;
+      case SimpleGestureEvent.DIRECTION_DOWN:
+        goDoCommand("cmd_scrollBottom");
+        break;
+    }
+  },
+
+  /**
+   * Called as soon as the frame script reports that some source
+   * code has been loaded in the browser.
+   */
+  onSourceLoaded() {
+    document.getElementById("cmd_goToLine").removeAttribute("disabled");
+
+    if (this.historyEnabled) {
+      this.updateCommands();
+    }
+
+    gBrowser.focus();
+  },
+
+  /**
+   * Called as soon as the frame script reports that some source
+   * code has been unloaded from the browser.
+   */
+  onSourceUnloaded() {
+    // Disable "go to line" while reloading due to e.g. change of charset
+    // or toggling of syntax highlighting.
+    document.getElementById("cmd_goToLine").setAttribute("disabled", "true");
+  },
+
+  /**
+   * Called by clicks on a menu populated by CharsetMenu.jsm to
+   * change the selected character set.
+   *
+   * @param event
+   *        The click event on a character set menuitem.
+   */
+  onSetCharacterSet(event) {
+    if (event.target.hasAttribute("charset")) {
+      let charset = event.target.getAttribute("charset");
+
+      // If we don't have history enabled, we have to do a reload in order to
+      // show the character set change. See bug 136322.
+      this.mm.sendAsyncMessage("ViewSource:SetCharacterSet", {
+        charset: charset,
+        doPageLoad: this.historyEnabled,
+      });
+
+      if (this.historyEnabled) {
+        gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
       }
     }
-  }
-
-  if (loadFromURL) {
-    // Currently, an exception is thrown if the URL load fails...
-    var loadFlags = Ci.nsIWebNavigation.LOAD_FLAGS_NONE;
-    getWebNavigation().loadURI(viewSrcUrl, loadFlags, null, null, null);
-  }
-
-  // Check the view_source.wrap_long_lines pref and set the menuitem's checked
-  // attribute accordingly.
-  var wraplonglinesPrefValue = Services.prefs.getBoolPref("view_source.wrap_long_lines");
-
-  if (wraplonglinesPrefValue)
-    document.getElementById("menu_wrapLongLines").setAttribute("checked", "true");
+  },
 
-  document.getElementById("menu_highlightSyntax")
-          .setAttribute("checked",
-                        Services.prefs.getBoolPref("view_source.syntax_highlight"));
-
-  window.addEventListener("AppCommand", HandleAppCommandEvent, true);
-  window.addEventListener("MozSwipeGesture", HandleSwipeGesture, true);
-  window.content.focus();
-}
-
-function onLoadContent()
-{
-  // If the view source was opened with a "go to line" argument.
-  if (gGoToLine > 0) {
-    goToLine(gGoToLine);
-    gGoToLine = 0;
-  }
-  document.getElementById('cmd_goToLine').removeAttribute('disabled');
+  /**
+   * Called from the frame script when the context menu is about to
+   * open. This tells ViewSourceChrome things about the item that
+   * the context menu is being opened on. This should be called before
+   * the popupshowing event handler fires.
+   */
+  onContextMenuOpening(isLink, isEmail, href) {
+    this.contextMenuData = { isLink, isEmail, href, isOpen: true };
+  },
 
-  // Register a listener so that we can show the caret position on the status bar.
-  window.content.getSelection()
-   .QueryInterface(Ci.nsISelectionPrivate)
-   .addSelectionListener(gSelectionListener);
-  gSelectionListener.attached = true;
-
-  if (isHistoryEnabled())
-    UpdateBackForwardCommands();
-}
-
-function onUnloadContent()
-{
-  // Disable "go to line" while reloading due to e.g. change of charset
-  // or toggling of syntax highlighting.
-  document.getElementById('cmd_goToLine').setAttribute('disabled', 'true');
+  /**
+   * Event handler for the popupshowing event on the context menu.
+   * This handler is responsible for setting the state on various
+   * menu items in the context menu, and reads values that were sent
+   * up from the frame script and stashed into this.contextMenuData.
+   *
+   * @param event
+   *        The popupshowing event for the context menu.
+   */
+  onContextMenuShowing(event) {
+    let copyLinkMenuItem = document.getElementById("context-copyLink");
+    copyLinkMenuItem.hidden = !this.contextMenuData.isLink;
 
-  // If we're not just unloading the initial "about:blank" which doesn't have
-  // a selection listener, get rid of it so it doesn't try to fire after the
-  // window has gone away.
-  if (gSelectionListener.attached) {
-    window.content.getSelection().QueryInterface(Ci.nsISelectionPrivate)
-          .removeSelectionListener(gSelectionListener);
-    gSelectionListener.attached = false;
-  }
-}
-
-/**
- * Handle click events bubbling up from error page content
- */
-function onClickContent(event) {
-  // Don't trust synthetic events
-  if (!event.isTrusted || event.target.localName != "button")
-    return;
-
-  var target = event.originalTarget;
-  var errorDoc = target.ownerDocument;
-
-  if (/^about:blocked/.test(errorDoc.documentURI)) {
-    // The event came from a button on a malware/phishing block page
+    let copyEmailMenuItem = document.getElementById("context-copyEmail");
+    copyEmailMenuItem.hidden = !this.contextMenuData.isEmail;
+  },
 
-    if (target == errorDoc.getElementById('getMeOutButton')) {
-      // Instead of loading some safe page, just close the window
-      window.close();
-    } else if (target == errorDoc.getElementById('reportButton')) {
-      // This is the "Why is this site blocked" button. We redirect
-      // to the generic page describing phishing/malware protection.
-      let url = Services.urlFormatter.formatURLPref("app.support.baseURL");
-      openURL(url + "phishing-malware");
-    } else if (target == errorDoc.getElementById('ignoreWarningButton')) {
-      // Allow users to override and continue through to the site
-      gBrowser.loadURIWithFlags(content.location.href,
-                                Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CLASSIFIER,
-                                null, null, null);
+  /**
+   * Called when the user chooses the "Copy Link" or "Copy Email"
+   * menu items in the context menu. Copies the relevant selection
+   * into the system clipboard.
+   */
+  onContextMenuCopyLinkOrEmail() {
+    // It doesn't make any sense to call this if the context menu
+    // isn't open...
+    if (!this.contextMenuData.isOpen) {
+      return;
     }
-  }
-}
 
-function HandleAppCommandEvent(evt)
-{
-  evt.stopPropagation();
-  switch (evt.command) {
-    case "Back":
-      BrowserBack();
-      break;
-    case "Forward":
-      BrowserForward();
-      break;
-  }
-}
+    let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"]
+                      .getService(Ci.nsIClipboardHelper);
+    clipboard.copyString(this.contextMenuData.href, document);
+  },
 
-function HandleSwipeGesture(evt) {
-  evt.stopPropagation();
-  switch (evt.direction) {
-    case SimpleGestureEvent.DIRECTION_LEFT:
-      BrowserBack();
-      break;
-    case SimpleGestureEvent.DIRECTION_RIGHT:
-      BrowserForward();
-      break;
-    case SimpleGestureEvent.DIRECTION_UP:
-      goDoCommand("cmd_scrollTop");
-      break;
-    case SimpleGestureEvent.DIRECTION_DOWN:
-      goDoCommand("cmd_scrollBottom");
-      break;
-  }
-}
-
-function ViewSourceClose()
-{
-  window.close();
-}
+  /**
+   * Called when the context menu closes, and invalidates any data
+   * that the frame script might have sent up about what the context
+   * menu was opened on.
+   */
+  onContextMenuHidden(event) {
+    this.contextMenuData = {
+      isOpen: false,
+    };
+  },
 
-function ViewSourceReload()
-{
-  gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY |
-                           Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
-}
-
-// Strips the |view-source:| for internalSave()
-function ViewSourceSavePage()
-{
-  internalSave(window.content.location.href.replace(/^view-source:/i, ""),
-               null, null, null, null, null, "SaveLinkTitle",
-               null, null, window.content.document, null, gPageLoader);
-}
-
-var PrintPreviewListener = {
-  getPrintPreviewBrowser: function () {
-    var browser = document.getElementById("ppBrowser");
-    if (!browser) {
-      browser = document.createElement("browser");
-      browser.setAttribute("id", "ppBrowser");
-      browser.setAttribute("flex", "1");
-      browser.setAttribute("type", "content");
-      document.getElementById("appcontent").
-        insertBefore(browser, document.getElementById("FindToolbar"));
+  /**
+   * Called when the user drags something over the content browser.
+   */
+  onDragOver(event) {
+    // For drags that appear to be internal text (for example, tab drags),
+    // set the dropEffect to 'none'. This prevents the drop even if some
+    // other listener cancelled the event.
+    let types = event.dataTransfer.types;
+    if (types.contains("text/x-moz-text-internal") && !types.contains("text/plain")) {
+        event.dataTransfer.dropEffect = "none";
+        event.stopPropagation();
+        event.preventDefault();
     }
 
-    return browser;
-  },
-  getSourceBrowser: function () {
-    return gBrowser;
+    let linkHandler = Cc["@mozilla.org/content/dropped-link-handler;1"]
+                        .getService(Ci.nsIDroppedLinkHandler);
+
+    if (linkHandler.canDropLink(event, false)) {
+      event.preventDefault();
+    }
   },
-  getNavToolbox: function () {
-    return document.getElementById("appcontent");
+
+  /**
+   * Called twhen the user drops something onto the content browser.
+   */
+  onDrop(event) {
+    if (event.defaultPrevented)
+      return;
+
+    let name = { };
+    let linkHandler = Cc["@mozilla.org/content/dropped-link-handler;1"]
+                        .getService(Ci.nsIDroppedLinkHandler);
+    let uri;
+    try {
+      // Pass true to prevent the dropping of javascript:/data: URIs
+      uri = linkHandler.dropLink(event, name, true);
+    } catch (e) {
+      return;
+    }
+
+    if (uri) {
+      this.loadURL(uri);
+    }
   },
-  onEnter: function () {
-    var toolbox = document.getElementById("viewSource-toolbox");
-    toolbox.hidden = true;
-    gBrowser.collapsed = true;
+
+  /**
+   * For remote browsers, the contextmenu event is received in the
+   * content process, and a message is sent up from the frame script
+   * to ViewSourceChrome, but then it stops. The event does not bubble
+   * up to the point that the popup is opened in the parent process.
+   * ViewSourceChrome is responsible for opening up the context menu in
+   * that case. This is called when we receive the contextmenu message
+   * from the child, and we know that the browser is currently remote.
+   *
+   * @param screenX
+   *        The screenX coordinate to open the popup at.
+   * @param screenY
+   *        The screenY coordinate to open the popup at.
+   */
+  openContextMenu(screenX, screenY) {
+    gContextMenu.openPopupAtScreen(screenX, screenY, true);
   },
-  onExit: function () {
-    document.getElementById("ppBrowser").collapsed = true;
-    gBrowser.collapsed = false;
-    document.getElementById("viewSource-toolbox").hidden = false;
-  }
-}
+
+  /**
+   * Loads the source of a URL. This will probably end up hitting the
+   * network.
+   *
+   * @param URL
+   *        A URL string to be opened in the view source browser.
+   */
+  loadURL(URL) {
+    this.mm.sendAsyncMessage("ViewSource:LoadSource", { URL });
+  },
+
+  /**
+   * Updates any commands that are dependant on command broadcasters.
+   */
+  updateCommands() {
+    let backBroadcaster = document.getElementById("Browser:Back");
+    let forwardBroadcaster = document.getElementById("Browser:Forward");
 
-function getWebNavigation()
-{
-  try {
-    return gBrowser.webNavigation;
-  } catch (e) {
-    return null;
-  }
-}
+    if (this.webNav.canGoBack) {
+      backBroadcaster.removeAttribute("disabled");
+    } else {
+      backBroadcaster.setAttribute("disabled", "true");
+    }
+    if (this.webNav.canGoForward) {
+      forwardBroadcaster.removeAttribute("disabled");
+    } else {
+      forwardBroadcaster.setAttribute("disabled", "true");
+    }
+  },
 
-function ViewSourceGoToLine()
-{
-  var input = {value:gLastLineFound};
-  for (;;) {
-    var ok = Services.prompt.prompt(
+  /**
+   * Updates the status displayed in the status bar of the view source window.
+   *
+   * @param label
+   *        The string to be displayed in the statusbar-lin-col element.
+   */
+  updateStatus(label) {
+    let statusBarField = document.getElementById("statusbar-line-col");
+    if (statusBarField) {
+      statusBarField.label = label;
+    }
+  },
+
+  /**
+   * Opens the "Go to line" prompt for a user to hop to a particular line
+   * of the source code they're viewing. This will keep prompting until the
+   * user either cancels out of the prompt, or enters a valid line number.
+   */
+  promptAndGoToLine() {
+    let input = { value: this.lastLineFound };
+
+    let ok = Services.prompt.prompt(
         window,
         gViewSourceBundle.getString("goToLineTitle"),
         gViewSourceBundle.getString("goToLineText"),
         input,
         null,
         {value:0});
 
     if (!ok)
       return;
 
-    var line = parseInt(input.value, 10);
+    let line = parseInt(input.value, 10);
 
     if (!(line > 0)) {
       Services.prompt.alert(window,
                             gViewSourceBundle.getString("invalidInputTitle"),
                             gViewSourceBundle.getString("invalidInputText"));
+      this.promptAndGoToLine();
+    } else {
+      this.goToLine(line);
+    }
+  },
 
-      continue;
-    }
+  /**
+   * Go to a particular line of the source code. This act is asynchronous.
+   *
+   * @param lineNumber
+   *        The line number to try to go to to.
+   */
+  goToLine(lineNumber) {
+    this.mm.sendAsyncMessage("ViewSource:GoToLine", { lineNumber });
+  },
 
-    var found = goToLine(line);
+  /**
+   * Called when the frame script reports that a line was successfully gotten
+   * to.
+   *
+   * @param lineNumber
+   *        The line number that we successfully got to.
+   */
+  onGoToLineSuccess(lineNumber) {
+    // We'll pre-populate the "Go to line" prompt with this value the next
+    // time it comes up.
+    this.lastLineFound = lineNumber;
+    document.getElementById("statusbar-line-col").label =
+      gViewSourceBundle.getFormattedString("statusBarLineCol", [lineNumber, 1]);
+  },
 
-    if (found)
-      break;
-
+  /**
+   * Called when the frame script reports that we failed to go to a particular
+   * line. This informs the user that their selection was likely out of range,
+   * and then reprompts the user to try again.
+   */
+  onGoToLineFailed() {
     Services.prompt.alert(window,
                           gViewSourceBundle.getString("outOfRangeTitle"),
                           gViewSourceBundle.getString("outOfRangeText"));
+    this.promptAndGoToLine();
+  },
+
+  /**
+   * Reloads the browser, bypassing the network cache.
+   */
+  reload() {
+    gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_PROXY |
+                             Ci.nsIWebNavigation.LOAD_FLAGS_BYPASS_CACHE);
+  },
+
+  /**
+   * Closes the view source window.
+   */
+  close() {
+    window.close();
+  },
+
+  /**
+   * Called when the user clicks on the "Wrap Long Lines" menu item, and
+   * flips the user preference for wrapping long lines in the view source
+   * browser.
+   */
+  toggleWrapping() {
+    this.shouldWrap = !this.shouldWrap;
+    Services.prefs.setBoolPref("view_source.wrap_long_lines",
+                               this.shouldWrap);
+    this.mm.sendAsyncMessage("ViewSource:ToggleWrapping");
+  },
+
+  /**
+   * Called when the user clicks on the "Syntax Highlighting" menu item, and
+   * flips the user preference for wrapping long lines in the view source
+   * browser.
+   */
+  toggleSyntaxHighlighting() {
+    this.shouldHighlight = !this.shouldHighlight;
+    // We can't flip this value in the child, since prefs are read-only there.
+    // We flip it here, and then cause a reload in the child to make the change
+    // occur.
+    Services.prefs.setBoolPref("view_source.syntax_highlight",
+                               this.shouldHighlight);
+    this.mm.sendAsyncMessage("ViewSource:ToggleSyntaxHighlighting");
+  },
+
+  /**
+   * Updates the "remote" attribute of the view source browser. This
+   * will remove the browser from the DOM, and then re-add it in the
+   * same place it was taken from.
+   *
+   * @param shouldBeRemote
+   *        True if the browser should be made remote. If the browsers
+   *        remoteness already matches this value, this function does
+   *        nothing.
+   */
+  updateBrowserRemoteness(shouldBeRemote) {
+    if (gBrowser.isRemoteBrowser == shouldBeRemote) {
+      return;
+    }
+
+    let parentNode = gBrowser.parentNode;
+    let nextSibling = gBrowser.nextSibling;
+
+    gBrowser.remove();
+    if (shouldBeRemote) {
+      gBrowser.setAttribute("remote", "true");
+    } else {
+      gBrowser.removeAttribute("remote");
+    }
+    // If nextSibling was null, this will put the browser at
+    // the end of the list.
+    parentNode.insertBefore(gBrowser, nextSibling);
+
+    if (shouldBeRemote) {
+      // We're going to send a message down to the remote browser
+      // to load the source content - however, in order for the
+      // contentWindowAsCPOW and contentDocumentAsCPOW values on
+      // the remote browser to be set, we must set up the
+      // RemoteWebProgress, which is lazily loaded. We only need
+      // contentWindowAsCPOW for the printing support, and this
+      // should go away once bug 1146454 is fixed, since we can
+      // then just pass the outerWindowID of the gBrowser to
+      // PrintUtils.
+      gBrowser.webProgress;
+    }
+  },
+};
+
+ViewSourceChrome.init();
+
+/**
+ * PrintUtils uses this to make Print Preview work.
+ */
+let PrintPreviewListener = {
+  getPrintPreviewBrowser() {
+    let browser = document.getElementById("ppBrowser");
+    if (!browser) {
+      browser = document.createElement("browser");
+      browser.setAttribute("id", "ppBrowser");
+      browser.setAttribute("flex", "1");
+      browser.setAttribute("type", "content");
+
+      let findBar = document.getElementById("FindToolbar");
+      document.getElementById("appcontent")
+              .insertBefore(browser, findBar);
+    }
+
+    return browser;
+  },
+
+  getSourceBrowser() {
+    return gBrowser;
+  },
+
+  getNavToolbox() {
+    return document.getElementById("appcontent");
+  },
+
+  onEnter() {
+    let toolbox = document.getElementById("viewSource-toolbox");
+    toolbox.hidden = true;
+    gBrowser.collapsed = true;
+  },
+
+  onExit() {
+    document.getElementById("ppBrowser").collapsed = true;
+    gBrowser.collapsed = false;
+    document.getElementById("viewSource-toolbox").hidden = false;
+  },
+};
+
+// viewZoomOverlay.js uses this
+function getBrowser() {
+  return gBrowser;
+}
+
+this.__defineGetter__("gPageLoader", function () {
+  var webnav = ViewSourceChrome.webNav;
+  if (!webnav)
+    return null;
+  delete this.gPageLoader;
+  this.gPageLoader = (webnav instanceof Ci.nsIWebPageDescriptor) ? webnav
+                                                                 : null;
+  return this.gPageLoader;
+});
+
+// Strips the |view-source:| for internalSave()
+function ViewSourceSavePage()
+{
+  internalSave(gBrowser.currentURI.spec.replace(/^view-source:/i, ""),
+               null, null, null, null, null, "SaveLinkTitle",
+               null, null, gBrowser.contentDocumentAsCPOW, null,
+               gPageLoader);
+}
+
+// Below are old deprecated functions and variables left behind for
+// compatibility reasons. These will be removed soon via bug 1159293.
+
+this.__defineGetter__("gLastLineFound", function () {
+  Deprecated.warning("gLastLineFound is deprecated - please use " +
+                     "ViewSourceChrome.lastLineFound instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  return ViewSourceChrome.lastLineFound;
+});
+
+function onLoadViewSource() {
+  Deprecated.warning("onLoadViewSource() is deprecated - please use " +
+                     "ViewSourceChrome.onXULLoaded() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.onXULLoaded();
+}
+
+function isHistoryEnabled() {
+  Deprecated.warning("isHistoryEnabled() is deprecated - please use " +
+                     "ViewSourceChrome.historyEnabled instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  return ViewSourceChrome.historyEnabled;
+}
+
+function ViewSourceClose() {
+  Deprecated.warning("ViewSourceClose() is deprecated - please use " +
+                     "ViewSourceChrome.close() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.close();
+}
+
+function ViewSourceReload() {
+  Deprecated.warning("ViewSourceReload() is deprecated - please use " +
+                     "ViewSourceChrome.reload() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.reload();
+}
+
+function getWebNavigation()
+{
+  Deprecated.warning("getWebNavigation() is deprecated - please use " +
+                     "ViewSourceChrome.webNav instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  // The original implementation returned null if anything threw during
+  // the getting of the webNavigation.
+  try {
+    return ViewSourceChrome.webNav;
+  } catch (e) {
+    return null;
   }
 }
 
+function viewSource(url) {
+  Deprecated.warning("viewSource() is deprecated - please use " +
+                     "ViewSourceChrome.loadURL() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.loadURL(url);
+}
+
+function ViewSourceGoToLine()
+{
+  Deprecated.warning("ViewSourceGoToLine() is deprecated - please use " +
+                     "ViewSourceChrome.promptAndGoToLine() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.promptAndGoToLine();
+}
+
 function goToLine(line)
 {
-  var viewsource = window.content.document.body;
-
-  // The source document is made up of a number of pre elements with
-  // id attributes in the format <pre id="line123">, meaning that
-  // the first line in the pre element is number 123.
-  // Do binary search to find the pre element containing the line.
-  // However, in the plain text case, we have only one pre without an
-  // attribute, so assume it begins on line 1.
-
-  var pre;
-  for (var lbound = 0, ubound = viewsource.childNodes.length; ; ) {
-    var middle = (lbound + ubound) >> 1;
-    pre = viewsource.childNodes[middle];
-
-    var firstLine = pre.id ? parseInt(pre.id.substring(4)) : 1;
-
-    if (lbound == ubound - 1) {
-      break;
-    }
-
-    if (line >= firstLine) {
-      lbound = middle;
-    } else {
-      ubound = middle;
-    }
-  }
-
-  var result = {};
-  var found = findLocation(pre, line, null, -1, false, result);
-
-  if (!found) {
-    return false;
-  }
-
-  var selection = window.content.getSelection();
-  selection.removeAllRanges();
-
-  // In our case, the range's startOffset is after "\n" on the previous line.
-  // Tune the selection at the beginning of the next line and do some tweaking
-  // to position the focusNode and the caret at the beginning of the line.
-
-  selection.QueryInterface(Ci.nsISelectionPrivate)
-    .interlinePosition = true;
-
-  selection.addRange(result.range);
-
-  if (!selection.isCollapsed) {
-    selection.collapseToEnd();
-
-    var offset = result.range.startOffset;
-    var node = result.range.startContainer;
-    if (offset < node.data.length) {
-      // The same text node spans across the "\n", just focus where we were.
-      selection.extend(node, offset);
-    }
-    else {
-      // There is another tag just after the "\n", hook there. We need
-      // to focus a safe point because there are edgy cases such as
-      // <span>...\n</span><span>...</span> vs.
-      // <span>...\n<span>...</span></span><span>...</span>
-      node = node.nextSibling ? node.nextSibling : node.parentNode.nextSibling;
-      selection.extend(node, 0);
-    }
-  }
-
-  var selCon = getSelectionController();
-  selCon.setDisplaySelection(Ci.nsISelectionController.SELECTION_ON);
-  selCon.setCaretVisibilityDuringSelection(true);
-
-  // Scroll the beginning of the line into view.
-  selCon.scrollSelectionIntoView(
-    Ci.nsISelectionController.SELECTION_NORMAL,
-    Ci.nsISelectionController.SELECTION_FOCUS_REGION,
-    true);
-
-  gLastLineFound = line;
-
-  document.getElementById("statusbar-line-col").label =
-    gViewSourceBundle.getFormattedString("statusBarLineCol", [line, 1]);
-
-  return true;
-}
-
-function updateStatusBar()
-{
-  // Reset the coalesce flag.
-  gSelectionListener.timeout = 0;
-
-  var statusBarField = document.getElementById("statusbar-line-col");
-
-  var selection = window.content.getSelection();
-  if (!selection.focusNode) {
-    statusBarField.label = '';
-    return;
-  }
-  if (selection.focusNode.nodeType != Node.TEXT_NODE) {
-    return;
-  }
-
-  var selCon = getSelectionController();
-  selCon.setDisplaySelection(Ci.nsISelectionController.SELECTION_ON);
-  selCon.setCaretVisibilityDuringSelection(true);
-
-  var interlinePosition = selection.QueryInterface(Ci.nsISelectionPrivate)
-                                   .interlinePosition;
-
-  var result = {};
-  findLocation(null, -1, 
-      selection.focusNode, selection.focusOffset, interlinePosition, result);
-
-  statusBarField.label = gViewSourceBundle.getFormattedString(
-                           "statusBarLineCol", [result.line, result.col]);
-}
-
-// Loops through the text lines in the pre element. The arguments are either
-// (pre, line) or (node, offset, interlinePosition). result is an out
-// argument. If (pre, line) are specified (and node == null), result.range is
-// a range spanning the specified line. If the (node, offset,
-// interlinePosition) are specified, result.line and result.col are the line
-// and column number of the specified offset in the specified node relative to
-// the whole file.
-function findLocation(pre, line, node, offset, interlinePosition, result)
-{
-  if (node && !pre) {
-    // Look upwards to find the current pre element.
-    for (pre = node;
-         pre.nodeName != "PRE";
-         pre = pre.parentNode);
-  }
-
-  // The source document is made up of a number of pre elements with
-  // id attributes in the format <pre id="line123">, meaning that
-  // the first line in the pre element is number 123.
-  // However, in the plain text case, there is only one <pre> without an id,
-  // so assume line 1.
-  var curLine = pre.id ? parseInt(pre.id.substring(4)) : 1;
-
-  // Walk through each of the text nodes and count newlines.
-  var treewalker = window.content.document
-      .createTreeWalker(pre, NodeFilter.SHOW_TEXT, null);
-
-  // The column number of the first character in the current text node.
-  var firstCol = 1;
-
-  var found = false;
-  for (var textNode = treewalker.firstChild();
-       textNode && !found;
-       textNode = treewalker.nextNode()) {
-
-    // \r is not a valid character in the DOM, so we only check for \n.
-    var lineArray = textNode.data.split(/\n/);
-    var lastLineInNode = curLine + lineArray.length - 1;
-
-    // Check if we can skip the text node without further inspection.
-    if (node ? (textNode != node) : (lastLineInNode < line)) {
-      if (lineArray.length > 1) {
-        firstCol = 1;
-      }
-      firstCol += lineArray[lineArray.length - 1].length;
-      curLine = lastLineInNode;
-      continue;
-    }
-
-    // curPos is the offset within the current text node of the first
-    // character in the current line.
-    for (var i = 0, curPos = 0;
-         i < lineArray.length;
-         curPos += lineArray[i++].length + 1) {
-
-      if (i > 0) {
-        curLine++;
-      }
-
-      if (node) {
-        if (offset >= curPos && offset <= curPos + lineArray[i].length) {
-          // If we are right after the \n of a line and interlinePosition is
-          // false, the caret looks as if it were at the end of the previous
-          // line, so we display that line and column instead.
-
-          if (i > 0 && offset == curPos && !interlinePosition) {
-            result.line = curLine - 1;
-            var prevPos = curPos - lineArray[i - 1].length;
-            result.col = (i == 1 ? firstCol : 1) + offset - prevPos;
-          } else {
-            result.line = curLine;
-            result.col = (i == 0 ? firstCol : 1) + offset - curPos;
-          }
-          found = true;
-
-          break;
-        }
-
-      } else {
-        if (curLine == line && !("range" in result)) {
-          result.range = document.createRange();
-          result.range.setStart(textNode, curPos);
-
-          // This will always be overridden later, except when we look for
-          // the very last line in the file (this is the only line that does
-          // not end with \n).
-          result.range.setEndAfter(pre.lastChild);
-
-        } else if (curLine == line + 1) {
-          result.range.setEnd(textNode, curPos - 1);
-          found = true;
-          break;
-        }
-      }
-    }
-  }
-
-  return found || ("range" in result);
-}
-
-// Toggle long-line wrapping and sets the view_source.wrap_long_lines
-// pref to persist the last state.
-function wrapLongLines()
-{
-  var myWrap = window.content.document.body;
-  myWrap.classList.toggle("wrap");
-
-  // Since multiple viewsource windows are possible, another window could have
-  // affected the pref, so instead of determining the new pref value via the current
-  // pref value, we use myWrap.classList.
-  Services.prefs.setBoolPref("view_source.wrap_long_lines", myWrap.classList.contains("wrap"));
-}
-
-// Toggles syntax highlighting and sets the view_source.syntax_highlight
-// pref to persist the last state.
-function highlightSyntax()
-{
-  var highlightSyntaxMenu = document.getElementById("menu_highlightSyntax");
-  var highlightSyntax = (highlightSyntaxMenu.getAttribute("checked") == "true");
-  Services.prefs.setBoolPref("view_source.syntax_highlight", highlightSyntax);
-
-  gPageLoader.loadPage(gPageLoader.currentDescriptor, gPageLoader.DISPLAY_NORMAL);
-}
-
-// Reload after change to character encoding or autodetection
-//
-// Fix for bug 136322: this function overrides the function in
-// browser.js to call PageLoader.loadPage() instead of BrowserReloadWithFlags()
-function BrowserCharsetReload()
-{
-  if (isHistoryEnabled()) {
-    gPageLoader.loadPage(gPageLoader.currentDescriptor,
-                         gPageLoader.DISPLAY_NORMAL);
-  } else {
-    gBrowser.reloadWithFlags(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE);
-  }
-}
-
-function BrowserSetCharacterSet(aEvent)
-{
-  if (aEvent.target.hasAttribute("charset"))
-    gBrowser.docShell.charset = aEvent.target.getAttribute("charset");
-  BrowserCharsetReload();
+  Deprecated.warning("goToLine() is deprecated - please use " +
+                     "ViewSourceChrome.goToLine() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.goToLine(line);
 }
 
 function BrowserForward(aEvent) {
-  try {
-    gBrowser.goForward();
-  }
-  catch(ex) {
-  }
+  Deprecated.warning("BrowserForward() is deprecated - please use " +
+                     "ViewSourceChrome.goForward() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.goForward();
 }
 
 function BrowserBack(aEvent) {
-  try {
-    gBrowser.goBack();
-  }
-  catch(ex) {
-  }
+  Deprecated.warning("BrowserBack() is deprecated - please use " +
+                     "ViewSourceChrome.goBack() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.goBack();
 }
 
 function UpdateBackForwardCommands() {
-  var backBroadcaster = document.getElementById("Browser:Back");
-  var forwardBroadcaster = document.getElementById("Browser:Forward");
-
-  if (getWebNavigation().canGoBack)
-    backBroadcaster.removeAttribute("disabled");
-  else
-    backBroadcaster.setAttribute("disabled", "true");
-
-  if (getWebNavigation().canGoForward)
-    forwardBroadcaster.removeAttribute("disabled");
-  else
-    forwardBroadcaster.setAttribute("disabled", "true");
+  Deprecated.warning("UpdateBackForwardCommands() is deprecated - please use " +
+                     "ViewSourceChrome.updateCommands() instead.",
+                     "https://developer.mozilla.org/en-US/Add-ons/Code_snippets/ViewSource");
+  ViewSourceChrome.updateCommands();
 }
-
-function contextMenuShowing() {
-  var isLink = false;
-  var isEmail = false;
-  if (gContextMenu.triggerNode && gContextMenu.triggerNode.localName == 'a') {
-    if (gContextMenu.triggerNode.href.indexOf('view-source:') == 0)
-      isLink = true;
-    if (gContextMenu.triggerNode.href.indexOf('mailto:') == 0)
-      isEmail = true;
-  }
-  document.getElementById('context-copyLink').hidden = !isLink;
-  document.getElementById('context-copyEmail').hidden = !isEmail;
-}
-
-function contextMenuCopyLinkOrEmail() {
-  if (!gContextMenu.triggerNode)
-    return;
-
-  var href = gContextMenu.triggerNode.href;
-  var clipboard = Cc['@mozilla.org/widget/clipboardhelper;1'].
-                  getService(Ci.nsIClipboardHelper);
-  clipboard.copyString(href.substring(href.indexOf(':') + 1), document);
-}
--- a/toolkit/components/viewsource/content/viewSource.xul
+++ b/toolkit/components/viewsource/content/viewSource.xul
@@ -15,17 +15,16 @@
 <!ENTITY % sourceDTD SYSTEM "chrome://global/locale/viewSource.dtd" >
 %sourceDTD;
 <!ENTITY % charsetDTD SYSTEM "chrome://global/locale/charsetMenu.dtd" >
 %charsetDTD;
 ]>
 
 <window id="viewSource"
         xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-        onload="onLoadViewSource();"
         contenttitlesetting="true"
         title="&mainWindow.title;" 
         titlemodifier="&mainWindow.titlemodifier;" 
         titlepreface="&mainWindow.preface;"
         titlemenuseparator ="&mainWindow.titlemodifierseparator;"  
         windowtype="navigator:view-source"
         width="640" height="480"
         screenX="10" screenY="10"
@@ -35,41 +34,41 @@
   <script type="application/javascript" src="chrome://global/content/printUtils.js"/>
   <script type="application/javascript" src="chrome://global/content/viewSource.js"/>
   <script type="application/javascript" src="chrome://global/content/viewZoomOverlay.js"/>
   <script type="application/javascript" src="chrome://global/content/contentAreaUtils.js"/>
   
   <stringbundle id="viewSourceBundle" src="chrome://global/locale/viewSource.properties"/>
 
   <command id="cmd_savePage" oncommand="ViewSourceSavePage();"/>
-  <command id="cmd_print" oncommand="PrintUtils.print();"/>
+  <command id="cmd_print" oncommand="PrintUtils.print(gBrowser.contentWindowAsCPOW, gBrowser);"/>
   <command id="cmd_printpreview" oncommand="PrintUtils.printPreview(PrintPreviewListener);"/>
   <command id="cmd_pagesetup" oncommand="PrintUtils.showPageSetup();"/>
   <command id="cmd_close" oncommand="window.close();"/>
   <commandset id="editMenuCommands"/>
   <command id="cmd_find"
            oncommand="document.getElementById('FindToolbar').onFindCommand();"/>
   <command id="cmd_findAgain"
            oncommand="document.getElementById('FindToolbar').onFindAgainCommand(false);"/>
   <command id="cmd_findPrevious"
            oncommand="document.getElementById('FindToolbar').onFindAgainCommand(true);"/>
 #ifdef XP_MACOSX
   <command id="cmd_findSelection"
            oncommand="document.getElementById('FindToolbar').onFindSelectionCommand();"/>
 #endif
-  <command id="cmd_reload" oncommand="ViewSourceReload();"/>
-  <command id="cmd_goToLine" oncommand="ViewSourceGoToLine();" disabled="true"/>
-  <command id="cmd_highlightSyntax" oncommand="highlightSyntax();"/>
-  <command id="cmd_wrapLongLines" oncommand="wrapLongLines()"/>
+  <command id="cmd_reload" oncommand="ViewSourceChrome.reload();"/>
+  <command id="cmd_goToLine" oncommand="ViewSourceChrome.promptAndGoToLine();" disabled="true"/>
+  <command id="cmd_highlightSyntax" oncommand="ViewSourceChrome.toggleSyntaxHighlighting();"/>
+  <command id="cmd_wrapLongLines" oncommand="ViewSourceChrome.toggleWrapping();"/>
   <command id="cmd_textZoomReduce" oncommand="ZoomManager.reduce();"/>
   <command id="cmd_textZoomEnlarge" oncommand="ZoomManager.enlarge();"/>
   <command id="cmd_textZoomReset" oncommand="ZoomManager.reset();"/>
 
-  <command id="Browser:Back" oncommand="BrowserBack();" observes="viewSourceNavigation"/>
-  <command id="Browser:Forward" oncommand="BrowserForward();" observes="viewSourceNavigation"/>
+  <command id="Browser:Back" oncommand="ViewSourceChrome.goBack()" observes="viewSourceNavigation"/>
+  <command id="Browser:Forward" oncommand="ViewSourceChrome.goForward()" observes="viewSourceNavigation"/>
 
   <broadcaster id="viewSourceNavigation"/>
 
   <keyset id="editMenuKeys"/>
   <keyset id="viewSourceKeys">
     <key id="key_savePage" key="&savePageCmd.commandkey;" modifiers="accel" command="cmd_savePage"/>
     <key id="key_print" key="&printCmd.commandkey;" modifiers="accel" command="cmd_print"/>
     <key id="key_close" key="&closeCmd.commandkey;" modifiers="accel" command="cmd_close"/>
@@ -109,18 +108,17 @@
     <key id="goBackKb2" key="&goBackCmd.commandKey;" command="Browser:Back" modifiers="accel"/>
     <key id="goForwardKb2" key="&goForwardCmd.commandKey;" command="Browser:Forward" modifiers="accel"/>
 #endif
 
   </keyset>
   
   <tooltip id="aHTMLTooltip" page="true"/>
 
-  <menupopup id="viewSourceContextMenu"
-             onpopupshowing="contextMenuShowing();">
+  <menupopup id="viewSourceContextMenu">
     <menuitem id="context-back"
               label="&backCmd.label;"
               accesskey="&backCmd.accesskey;"
               command="Browser:Back"
               observes="viewSourceNavigation"/>
     <menuitem id="context-forward"
               label="&forwardCmd.label;"
               accesskey="&forwardCmd.accesskey;"
@@ -128,21 +126,21 @@
               observes="viewSourceNavigation"/>
     <menuseparator observes="viewSourceNavigation"/>
     <menuitem id="cMenu_findAgain"/>
     <menuseparator/>
     <menuitem id="cMenu_copy"/>
     <menuitem id="context-copyLink"
               label="&copyLinkCmd.label;"
               accesskey="&copyLinkCmd.accesskey;"
-              oncommand="contextMenuCopyLinkOrEmail();"/>
+              oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
     <menuitem id="context-copyEmail"
               label="&copyEmailCmd.label;"
               accesskey="&copyEmailCmd.accesskey;"
-              oncommand="contextMenuCopyLinkOrEmail();"/>
+              oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/>
     <menuseparator/>
     <menuitem id="cMenu_selectAll"/>
   </menupopup>
 
   <!-- Menu --> 
   <toolbox id="viewSource-toolbox">
     <menubar id="viewSource-main-menubar">
 
@@ -203,17 +201,17 @@
                         key="key_textZoomReset"/>
             </menupopup>
           </menu>
 
           <!-- Charset Menu -->
           <menu id="charsetMenu"
                 label="&charsetMenu2.label;"
                 accesskey="&charsetMenu2.accesskey;"
-                oncommand="BrowserSetCharacterSet(event);"
+                oncommand="ViewSourceChrome.onSetCharacterSet(event);"
                 onpopupshowing="CharsetMenu.build(event.target);"
                 onpopupshown="CharsetMenu.update(event.target, content.document.characterSet);">
             <menupopup/>
           </menu>
           <menuseparator/>
           <menuitem id="menu_wrapLongLines" type="checkbox" command="cmd_wrapLongLines"
                     label="&menu_wrapLongLines.title;" accesskey="&menu_wrapLongLines.accesskey;"/>
           <menuitem type="checkbox" id="menu_highlightSyntax" command="cmd_highlightSyntax"
@@ -221,17 +219,17 @@
         </menupopup>
       </menu>
     </menubar>  
   </toolbox>
 
   <vbox id="appcontent" flex="1">
 
     <browser id="content" type="content-primary" name="content" src="about:blank" flex="1"
-             context="viewSourceContextMenu" showcaret="true" tooltip="aHTMLTooltip"/>
+             context="viewSourceContextMenu" showcaret="true" tooltip="aHTMLTooltip" />
     <findbar id="FindToolbar" browserid="content"/>
   </vbox> 
 
   <statusbar id="status-bar" class="chromeclass-status">
     <statusbarpanel id="statusbar-line-col" label="" flex="1"/>
   </statusbar>
 
 </window>
--- a/toolkit/components/viewsource/content/viewSourceUtils.js
+++ b/toolkit/components/viewsource/content/viewSourceUtils.js
@@ -13,48 +13,83 @@
  */
 
 var gViewSourceUtils = {
 
   mnsIWebBrowserPersist: Components.interfaces.nsIWebBrowserPersist,
   mnsIWebProgress: Components.interfaces.nsIWebProgress,
   mnsIWebPageDescriptor: Components.interfaces.nsIWebPageDescriptor,
 
-  // Opens view source
-  viewSource: function(aURL, aPageDescriptor, aDocument, aLineNumber)
+  /**
+   * Opens the view source window.
+   *
+   * @param aArgsOrURL (required)
+   *        This is either an Object containing parameters, or a string
+   *        URL for the page we want to view the source of. In the latter
+   *        case we will be paying attention to the other parameters, as
+   *        we will be supporting the old API for this method.
+   *        If aArgsOrURL is an Object, the other parameters will be ignored.
+   *        aArgsOrURL as an Object can include the following properties:
+   *
+   *        URL (required):
+   *          A string URL for the page we'd like to view the source of.
+   *        browser (optional):
+   *          The browser containing the document that we would like to view the
+   *          source of. This is required if outerWindowID is passed.
+   *        outerWindowID (optional):
+   *          The outerWindowID of the content window containing the document that
+   *          we want to view the source of. Pass this if you want to attempt to
+   *          load the document source out of the network cache.
+   *        lineNumber (optional):
+   *          The line number to focus on once the source is loaded.
+   *
+   * @param aPageDescriptor (deprecated, optional)
+   *        Accepted for compatibility reasons, but is otherwise ignored.
+   * @param aDocument (deprecated, optional)
+   *        The content document we would like to view the source of. This
+   *        function will throw if aDocument is a CPOW.
+   * @param aLineNumber (deprecated, optional)
+   *        The line number to focus on once the source is loaded.
+   */
+  viewSource: function(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber)
   {
     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
                           .getService(Components.interfaces.nsIPrefBranch);
-    if (prefs.getBoolPref("view_source.editor.external"))
-      this.openInExternalEditor(aURL, aPageDescriptor, aDocument, aLineNumber);
-    else
-      this.openInInternalViewer(aURL, aPageDescriptor, aDocument, aLineNumber);
+    if (prefs.getBoolPref("view_source.editor.external")) {
+      this.openInExternalEditor(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber);
+    } else {
+      this._openInInternalViewer(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber);
+    }
   },
 
   // Opens the interval view source viewer
-  openInInternalViewer: function(aURL, aPageDescriptor, aDocument, aLineNumber)
+  _openInInternalViewer: function(aArgsOrURL, aPageDescriptor, aDocument, aLineNumber)
   {
     // try to open a view-source window while inheriting the charset (if any)
     var charset = null;
     var isForcedCharset = false;
     if (aDocument) {
+      if (Components.utils.isCrossProcessWrapper(aDocument)) {
+        throw new Error("View Source cannot accept a CPOW as a document.");
+      }
+
       charset = "charset=" + aDocument.characterSet;
-      try { 
+      try {
         isForcedCharset =
           aDocument.defaultView
                    .QueryInterface(Components.interfaces.nsIInterfaceRequestor)
                    .getInterface(Components.interfaces.nsIDOMWindowUtils)
                    .docCharsetIsForced;
       } catch (ex) {
       }
     }
     openDialog("chrome://global/content/viewSource.xul",
                "_blank",
                "all,dialog=no",
-               aURL, charset, aPageDescriptor, aLineNumber, isForcedCharset);
+               aArgsOrURL, charset, aPageDescriptor, aLineNumber, isForcedCharset);
   },
 
   buildEditorArgs: function(aPath, aLineNumber) {
     // Determine the command line arguments to pass to the editor.
     // We currently support a %LINE% placeholder which is set to the passed
     // line number (or to 0 if there's none)
     var editorArgs = [];
     var prefs = Components.classes["@mozilla.org/preferences-service;1"]
--- a/toolkit/components/viewsource/jar.mn
+++ b/toolkit/components/viewsource/jar.mn
@@ -4,8 +4,9 @@
 
 toolkit.jar:
   content/global/viewSource.css             (content/viewSource.css)
   content/global/viewSource.js              (content/viewSource.js)
 * content/global/viewSource.xul             (content/viewSource.xul)
   content/global/viewPartialSource.js       (content/viewPartialSource.js)
 * content/global/viewPartialSource.xul      (content/viewPartialSource.xul)
   content/global/viewSourceUtils.js         (content/viewSourceUtils.js)
+  content/global/viewSource-content.js      (content/viewSource-content.js)
\ No newline at end of file
--- a/toolkit/components/viewsource/test/browser/browser.ini
+++ b/toolkit/components/viewsource/test/browser/browser.ini
@@ -5,9 +5,8 @@ support-files = head.js
 [browser_bug464222.js]
 [browser_bug699356.js]
 [browser_bug713810.js]
 [browser_contextmenu.js]
 skip-if = e10s # occasional timeouts with dead CPOW in console
 [browser_gotoline.js]
 [browser_viewsourceprefs.js]
 skip-if = e10s # Bug ?????? - obscure failure (Syntax highlighting off - Got true, expected false)
-[browser_viewsourceprefs_nonhtml.js]
--- a/toolkit/components/viewsource/test/browser/browser_contextmenu.js
+++ b/toolkit/components/viewsource/test/browser/browser_contextmenu.js
@@ -55,17 +55,17 @@ function checkMenuItems(popupNode, copyL
   popupNode.scrollIntoView();
 
   let cachedEvent = null;
   let mouseFn = function(event) {
     cachedEvent = event;
   };
 
   gViewSourceWindow.gBrowser.contentWindow.addEventListener("mousedown", mouseFn, false);
-  EventUtils.synthesizeMouseAtCenter(popupNode, { button: 2 }, gViewSourceWindow.gBrowser.contentWindow);
+  EventUtils.synthesizeMouseAtCenter(popupNode, { type: "contextmenu", button: 2 }, gViewSourceWindow.gBrowser.contentWindow);
   gViewSourceWindow.gBrowser.contentWindow.removeEventListener("mousedown", mouseFn, false);
 
   gContextMenu.openPopup(popupNode, "after_start", 0, 0, false, false, cachedEvent);
 
   is(gCopyLinkMenuItem.hidden, !copyLinkExpected, "Copy link menuitem is " + (copyLinkExpected ? "not hidden" : "hidden"));
   is(gCopyEmailMenuItem.hidden, !copyEmailExpected, "Copy email menuitem is " + (copyEmailExpected ? "not hidden" : "hidden"));
 
   if (!copyLinkExpected && !copyEmailExpected) {
--- a/toolkit/components/viewsource/test/browser/browser_gotoline.js
+++ b/toolkit/components/viewsource/test/browser/browser_gotoline.js
@@ -1,27 +1,39 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-let content = "line 1\nline 2\nline 3";
-let runningPlainText = false;
+Cu.import("resource://testing-common/ContentTaskUtils.jsm", this);
 
-function test() {
-  waitForExplicitFinish();
+let content = "line 1\nline 2\nline 3";
 
-  testViewSourceWindow("data:text/html," + encodeURIComponent(content), checkViewSource, function() {
-    testViewSourceWindow("data:text/plain," + encodeURIComponent(content), checkViewSource, finish);
-  });
-}
+add_task(function*() {
+  // First test with text with the text/html mimetype.
+  let win = yield loadViewSourceWindow("data:text/html," + encodeURIComponent(content));
+  yield checkViewSource(win);
+  yield BrowserTestUtils.closeWindow(win);
 
-function checkViewSource(aWindow) {
+  win = yield loadViewSourceWindow("data:text/plain," + encodeURIComponent(content));
+  yield checkViewSource(win);
+  yield BrowserTestUtils.closeWindow(win);
+});
+
+let checkViewSource = Task.async(function* (aWindow) {
   is(aWindow.gBrowser.contentDocument.body.textContent, content, "Correct content loaded");
-
   let selection = aWindow.gBrowser.contentWindow.getSelection();
   let statusPanel = aWindow.document.getElementById("statusbar-line-col");
   is(statusPanel.getAttribute("label"), "", "Correct status bar text");
+
   for (let i = 1; i <= 3; i++) {
-    aWindow.goToLine(i);
-    is(selection.toString(), "line " + i, "Correct text selected");
-    is(statusPanel.getAttribute("label"), "Line " + i + ", Col 1", "Correct status bar text");
+    aWindow.ViewSourceChrome.goToLine(i);
+    let result = yield ContentTask.spawn(aWindow.gBrowser, i, function*(i) {
+      let selection = content.getSelection();
+      return (selection.toString() == "line " + i);
+    });
+
+    ok(result, "Correct text selected");
+
+    yield ContentTaskUtils.waitForCondition(() => {
+      return (statusPanel.getAttribute("label") == "Line " + i + ", Col 1");
+    }, "Correct status bar text");
   }
-}
+});
--- a/toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js
+++ b/toolkit/components/viewsource/test/browser/browser_viewsourceprefs.js
@@ -1,135 +1,134 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
-const source = "about:mozilla";
-let mWindow, wrapMenuItem, syntaxMenuItem;
+let plaintextURL = "data:text/plain,hello+world";
+let htmlURL = "about:mozilla";
 
-// Check the default values are set.
-function test() {
-  waitForExplicitFinish();
-
+add_task(function* setup() {
   registerCleanupFunction(function() {
     SpecialPowers.clearUserPref("view_source.tab_size");
     SpecialPowers.clearUserPref("view_source.wrap_long_lines");
     SpecialPowers.clearUserPref("view_source.syntax_highlight");
   });
-
-  openViewSourceWindow(source, function(aWindow) {
-    mWindow = aWindow;
-    wrapMenuItem = aWindow.document.getElementById('menu_wrapLongLines');
-    syntaxMenuItem = aWindow.document.getElementById('menu_highlightSyntax');
+});
 
-    // Strip checked="false" attributes, since we're not interested in them.
-    if (wrapMenuItem.getAttribute("checked") == "false")
-      wrapMenuItem.removeAttribute("checked");
-    if (syntaxMenuItem.getAttribute("checked") == "false")
-      syntaxMenuItem.removeAttribute("checked");
+add_task(function*() {
+  yield exercisePrefs(plaintextURL, false);
+  yield exercisePrefs(htmlURL, true);
+});
+
+let exercisePrefs = Task.async(function* (source, highlightable) {
+  let win = yield loadViewSourceWindow(source);
+  let wrapMenuItem = win.document.getElementById("menu_wrapLongLines");
+  let syntaxMenuItem = win.document.getElementById("menu_highlightSyntax");
 
-    is(wrapMenuItem.hasAttribute("checked"), false, "Wrap menu item not checked by default");
-    is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked by default");
-    checkStyle(aWindow, "-moz-tab-size", 4);
-    checkStyle(aWindow, "white-space", "pre");
+  // Strip checked="false" attributes, since we're not interested in them.
+  if (wrapMenuItem.getAttribute("checked") == "false") {
+    wrapMenuItem.removeAttribute("checked");
+  }
+  if (syntaxMenuItem.getAttribute("checked") == "false") {
+    syntaxMenuItem.removeAttribute("checked");
+  }
 
-    test1();
-  });
-}
+  // Test the default states of these menu items.
+  is(wrapMenuItem.hasAttribute("checked"), false,
+     "Wrap menu item not checked by default");
+  is(syntaxMenuItem.hasAttribute("checked"), true,
+     "Syntax menu item checked by default");
 
-// Check that the Wrap Long Lines menu item works.
-function test1() {
+  yield checkStyle(win, "-moz-tab-size", 4);
+  yield checkStyle(win, "white-space", "pre");
+
+  // Next, test that the Wrap Long Lines menu item works.
   simulateClick(wrapMenuItem);
-
   is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
   is(SpecialPowers.getBoolPref("view_source.wrap_long_lines"), true, "Wrap pref set");
-  checkStyle(mWindow, "white-space", "pre-wrap");
-  test2();
-}
 
-function test2() {
+  yield checkStyle(win, "white-space", "pre-wrap");
+
   simulateClick(wrapMenuItem);
-
   is(wrapMenuItem.hasAttribute("checked"), false, "Wrap menu item unchecked");
   is(SpecialPowers.getBoolPref("view_source.wrap_long_lines"), false, "Wrap pref set");
-  checkStyle(mWindow, "white-space", "pre");
-  test3();
-}
+  yield checkStyle(win, "white-space", "pre");
 
-// Check that the Syntax Highlighting menu item works.
-function test3() {
-  mWindow.gBrowser.addEventListener("pageshow", function test3Handler() {
-    mWindow.gBrowser.removeEventListener("pageshow", test3Handler, false);
-    is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
-    is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), false, "Syntax highlighting pref set");
-
-    checkHighlight(mWindow, false);
-    test4();
-  }, false);
-
+  // Check that the Syntax Highlighting menu item works.
+  let pageShowPromise = BrowserTestUtils.waitForEvent(win.gBrowser, "pageshow");
   simulateClick(syntaxMenuItem);
-}
+  yield pageShowPromise;
 
-function test4() {
-  mWindow.gBrowser.addEventListener("pageshow", function test4Handler() {
-    mWindow.gBrowser.removeEventListener("pageshow", test4Handler, false);
-    is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked");
-    is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), true, "Syntax highlighting pref set");
+  is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
+  is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), false, "Syntax highlighting pref set");
+  yield checkHighlight(win, false);
 
-    checkHighlight(mWindow, true);
-    closeViewSourceWindow(mWindow, test5);
-  }, false);
-
+  pageShowPromise = BrowserTestUtils.waitForEvent(win.gBrowser, "pageshow");
   simulateClick(syntaxMenuItem);
-}
+  yield pageShowPromise;
+
+  is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked");
+  is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), true, "Syntax highlighting pref set");
 
-// Open a new view-source window to check prefs are obeyed.
-function test5() {
+  yield checkHighlight(win, highlightable);
+  yield BrowserTestUtils.closeWindow(win);
+
+  // Open a new view-source window to check that the prefs are obeyed.
   SpecialPowers.setIntPref("view_source.tab_size", 2);
   SpecialPowers.setBoolPref("view_source.wrap_long_lines", true);
   SpecialPowers.setBoolPref("view_source.syntax_highlight", false);
 
-  executeSoon(function() {
-    openViewSourceWindow(source, function(aWindow) {
-      wrapMenuItem = aWindow.document.getElementById('menu_wrapLongLines');
-      syntaxMenuItem = aWindow.document.getElementById('menu_highlightSyntax');
+  win = yield loadViewSourceWindow(source);
+  wrapMenuItem = win.document.getElementById("menu_wrapLongLines");
+  syntaxMenuItem = win.document.getElementById("menu_highlightSyntax");
+
+  // Strip checked="false" attributes, since we're not interested in them.
+  if (wrapMenuItem.getAttribute("checked") == "false") {
+    wrapMenuItem.removeAttribute("checked");
+  }
+  if (syntaxMenuItem.getAttribute("checked") == "false") {
+    syntaxMenuItem.removeAttribute("checked");
+  }
 
-      // Strip checked="false" attributes, since we're not interested in them.
-      if (wrapMenuItem.getAttribute("checked") == "false")
-        wrapMenuItem.removeAttribute("checked");
-      if (syntaxMenuItem.getAttribute("checked") == "false")
-        syntaxMenuItem.removeAttribute("checked");
+  is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
+  is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
+  yield checkStyle(win, "-moz-tab-size", 2);
+  yield checkStyle(win, "white-space", "pre-wrap");
+  yield checkHighlight(win, false);
 
-      is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
-      is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
-      checkStyle(aWindow, "-moz-tab-size", 2);
-      checkStyle(aWindow, "white-space", "pre-wrap");
-      checkHighlight(aWindow, false);
-      closeViewSourceWindow(aWindow, finish);
-    });
-  });
-}
+  SpecialPowers.clearUserPref("view_source.tab_size");
+  SpecialPowers.clearUserPref("view_source.wrap_long_lines");
+  SpecialPowers.clearUserPref("view_source.syntax_highlight");
+
+  yield BrowserTestUtils.closeWindow(win);
+});
 
 // Simulate a menu item click, including toggling the checked state.
 // This saves us from opening the menu and trying to click on the item,
 // which doesn't work on Mac OS X.
 function simulateClick(aMenuItem) {
   if (aMenuItem.hasAttribute("checked"))
     aMenuItem.removeAttribute("checked");
   else
     aMenuItem.setAttribute("checked", "true");
 
   aMenuItem.click();
 }
 
-function checkStyle(aWindow, aStyleProperty, aExpectedValue) {
-  let gBrowser = aWindow.gBrowser;
-  let computedStyle = gBrowser.contentWindow.getComputedStyle(gBrowser.contentDocument.body, null);
-
-  is(computedStyle.getPropertyValue(aStyleProperty), aExpectedValue, "Correct value of " + aStyleProperty);
-}
+let checkStyle = Task.async(function* (win, styleProperty, expected) {
+  let browser = win.gBrowser;
+  let value = yield ContentTask.spawn(browser, styleProperty, function* (styleProperty) {
+    let style = content.getComputedStyle(content.document.body, null);
+    return style.getPropertyValue(styleProperty);
+  });
+  is(value, expected, "Correct value of " + styleProperty);
+});
 
-function checkHighlight(aWindow, aExpected) {
-  let spans = aWindow.gBrowser.contentDocument.getElementsByTagName("span");
-  is(Array.some(spans, function(aSpan) {
-    return aSpan.className != "";
-  }), aExpected, "Syntax highlighting " + (aExpected ? "on" : "off"));
-}
+let checkHighlight = Task.async(function* (win, expected) {
+  let browser = win.gBrowser;
+  let highlighted = yield ContentTask.spawn(browser, {}, function* () {
+    let spans = content.document.getElementsByTagName("span");
+    return Array.some(spans, (span) => {
+      return span.className != "";
+    });
+  });
+  is(highlighted, expected, "Syntax highlighting " + (expected ? "on" : "off"));
+});
deleted file mode 100644
--- a/toolkit/components/viewsource/test/browser/browser_viewsourceprefs_nonhtml.js
+++ /dev/null
@@ -1,135 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-let source = "data:text/plain,hello+world";
-let mWindow, wrapMenuItem, syntaxMenuItem;
-
-// Check the default values are set.
-function test() {
-  waitForExplicitFinish();
-
-  registerCleanupFunction(function() {
-    SpecialPowers.clearUserPref("view_source.tab_size");
-    SpecialPowers.clearUserPref("view_source.wrap_long_lines");
-    SpecialPowers.clearUserPref("view_source.syntax_highlight");
-  });
-
-  openViewSourceWindow(source, function(aWindow) {
-    mWindow = aWindow;
-    wrapMenuItem = aWindow.document.getElementById('menu_wrapLongLines');
-    syntaxMenuItem = aWindow.document.getElementById('menu_highlightSyntax');
-
-    // Strip checked="false" attributes, since we're not interested in them.
-    if (wrapMenuItem.getAttribute("checked") == "false")
-      wrapMenuItem.removeAttribute("checked");
-    if (syntaxMenuItem.getAttribute("checked") == "false")
-      syntaxMenuItem.removeAttribute("checked");
-
-    is(wrapMenuItem.hasAttribute("checked"), false, "Wrap menu item not checked by default");
-    is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked by default");
-    checkStyle(aWindow, "-moz-tab-size", 4);
-    checkStyle(aWindow, "white-space", "pre");
-
-    test1();
-  });
-}
-
-// Check that the Wrap Long Lines menu item works.
-function test1() {
-  simulateClick(wrapMenuItem);
-
-  is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
-  is(SpecialPowers.getBoolPref("view_source.wrap_long_lines"), true, "Wrap pref set");
-  checkStyle(mWindow, "white-space", "pre-wrap");
-  test2();
-}
-
-function test2() {
-  simulateClick(wrapMenuItem);
-
-  is(wrapMenuItem.hasAttribute("checked"), false, "Wrap menu item unchecked");
-  is(SpecialPowers.getBoolPref("view_source.wrap_long_lines"), false, "Wrap pref set");
-  checkStyle(mWindow, "white-space", "pre");
-  test3();
-}
-
-// Check that the Syntax Highlighting menu item works.
-function test3() {
-  mWindow.gBrowser.addEventListener("pageshow", function test3Handler() {
-    mWindow.gBrowser.removeEventListener("pageshow", test3Handler, false);
-    is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
-    is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), false, "Syntax highlighting pref set");
-
-    checkHighlight(mWindow, false);
-    test4();
-  }, false);
-
-  simulateClick(syntaxMenuItem);
-}
-
-function test4() {
-  mWindow.gBrowser.addEventListener("pageshow", function test4Handler() {
-    mWindow.gBrowser.removeEventListener("pageshow", test4Handler, false);
-    is(syntaxMenuItem.hasAttribute("checked"), true, "Syntax menu item checked");
-    is(SpecialPowers.getBoolPref("view_source.syntax_highlight"), true, "Syntax highlighting pref set");
-
-    checkHighlight(mWindow, false);
-    closeViewSourceWindow(mWindow, test5);
-  }, false);
-
-  simulateClick(syntaxMenuItem);
-}
-
-// Open a new view-source window to check prefs are obeyed.
-function test5() {
-  SpecialPowers.setIntPref("view_source.tab_size", 2);
-  SpecialPowers.setBoolPref("view_source.wrap_long_lines", true);
-  SpecialPowers.setBoolPref("view_source.syntax_highlight", false);
-
-  executeSoon(function() {
-    openViewSourceWindow(source, function(aWindow) {
-      wrapMenuItem = aWindow.document.getElementById('menu_wrapLongLines');
-      syntaxMenuItem = aWindow.document.getElementById('menu_highlightSyntax');
-
-      // Strip checked="false" attributes, since we're not interested in them.
-      if (wrapMenuItem.getAttribute("checked") == "false")
-        wrapMenuItem.removeAttribute("checked");
-      if (syntaxMenuItem.getAttribute("checked") == "false")
-        syntaxMenuItem.removeAttribute("checked");
-
-      is(wrapMenuItem.hasAttribute("checked"), true, "Wrap menu item checked");
-      is(syntaxMenuItem.hasAttribute("checked"), false, "Syntax menu item unchecked");
-      checkStyle(aWindow, "-moz-tab-size", 2);
-      checkStyle(aWindow, "white-space", "pre-wrap");
-      checkHighlight(aWindow, false);
-      closeViewSourceWindow(aWindow, finish);
-    });
-  });
-}
-
-// Simulate a menu item click, including toggling the checked state.
-// This saves us from opening the menu and trying to click on the item,
-// which doesn't work on Mac OS X.
-function simulateClick(aMenuItem) {
-  if (aMenuItem.hasAttribute("checked"))
-    aMenuItem.removeAttribute("checked");
-  else
-    aMenuItem.setAttribute("checked", "true");
-
-  aMenuItem.click();
-}
-
-function checkStyle(aWindow, aStyleProperty, aExpectedValue) {
-  let gBrowser = aWindow.gBrowser;
-  let computedStyle = gBrowser.contentWindow.getComputedStyle(gBrowser.contentDocument.body, null);
-
-  is(computedStyle.getPropertyValue(aStyleProperty), aExpectedValue, "Correct value of " + aStyleProperty);
-}
-
-function checkHighlight(aWindow, aExpected) {
-  let spans = aWindow.gBrowser.contentDocument.getElementsByTagName("span");
-  is(Array.some(spans, function(aSpan) {
-    return aSpan.className != "";
-  }), aExpected, "Syntax highlighting " + (aExpected ? "on" : "off"));
-}
--- a/toolkit/components/viewsource/test/browser/head.js
+++ b/toolkit/components/viewsource/test/browser/head.js
@@ -9,16 +9,22 @@ function openViewSourceWindow(aURI, aCal
     if (event.target.location == "view-source:" + aURI) {
       info("View source window opened: " + event.target.location);
       viewSourceWindow.removeEventListener("pageshow", pageShowHandler, false);
       aCallback(viewSourceWindow);
     }
   }, false);
 }
 
+function loadViewSourceWindow(URL) {
+  return new Promise((resolve) => {
+    openViewSourceWindow(URL, resolve);
+  })
+}
+
 function closeViewSourceWindow(aWindow, aCallback) {
   Services.wm.addListener({
     onCloseWindow: function() {
       Services.wm.removeListener(this);
       executeSoon(aCallback);
     }
   });
   aWindow.close();
--- a/toolkit/content/widgets/autocomplete.xml
+++ b/toolkit/content/widgets/autocomplete.xml
@@ -708,19 +708,19 @@
           if (!view)
             return;
           var rows = this.maxRows;
           if (!view.rowCount || (rows && view.rowCount < rows))
             rows = view.rowCount;
 
           var height = rows * bx.rowHeight;
 
-          if (height == 0)
+          if (height == 0) {
             this.tree.setAttribute("collapsed", "true");
-          else {
+          } else {
             if (this.tree.hasAttribute("collapsed"))
               this.tree.removeAttribute("collapsed");
 
             this.tree.setAttribute("height", height);
           }
           this.tree.setAttribute("hidescrollbar", view.rowCount <= rows);
         ]]>
         </body>
--- a/toolkit/devtools/discovery/tests/unit/xpcshell.ini
+++ b/toolkit/devtools/discovery/tests/unit/xpcshell.ini
@@ -1,7 +1,7 @@
 [DEFAULT]
 tags = devtools
 head =
 tail =
 
 [test_discovery.js]
-skip-if = toolkit == 'gonk' && debug # Debug doesn't like settings read
+skip-if = toolkit == 'gonk' # Disabled per Bug 1140913
--- a/toolkit/devtools/output-parser.js
+++ b/toolkit/devtools/output-parser.js
@@ -34,51 +34,29 @@ const REGEX_CSS_VAR = /^\bvar\(\s*--[-_a
  */
 const REGEX_ALL_COLORS = /^#[0-9a-fA-F]{3}\b|^#[0-9a-fA-F]{6}\b|^hsl\(.*?\)|^hsla\(.*?\)|^rgba?\(.*?\)|^[a-zA-Z-]+/;
 
 loader.lazyGetter(this, "DOMUtils", function () {
   return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
 });
 
 /**
- * This regular expression catches all css property names with their trailing
- * spaces and semicolon. This is used to ensure a value is valid for a property
- * name within style="" attributes.
- */
-loader.lazyGetter(this, "REGEX_ALL_CSS_PROPERTIES", function () {
-  let names = DOMUtils.getCSSPropertyNames();
-    let pattern = "^(";
-
-    for (let i = 0; i < names.length; i++) {
-      if (i > 0) {
-        pattern += "|";
-      }
-      pattern += names[i];
-    }
-    pattern += ")\\s*:\\s*";
-
-    return new RegExp(pattern);
-});
-
-/**
  * This module is used to process text for output by developer tools. This means
  * linking JS files with the debugger, CSS files with the style editor, JS
  * functions with the debugger, placing color swatches next to colors and
  * adding doorhanger previews where possible (images, angles, lengths,
  * border radius, cubic-bezier etc.).
  *
  * Usage:
  *   const {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
  *   const {OutputParser} = devtools.require("devtools/output-parser");
  *
  *   let parser = new OutputParser();
  *
  *   parser.parseCssProperty("color", "red"); // Returns document fragment.
- *   parser.parseHTMLAttribute("color:red; font-size: 12px;"); // Returns document
- *                                                             // fragment.
  */
 function OutputParser() {
   this.parsed = [];
   this.colorSwatches = new WeakMap();
   this._onSwatchMouseDown = this._onSwatchMouseDown.bind(this);
 }
 
 exports.OutputParser = OutputParser;
@@ -95,50 +73,32 @@ OutputParser.prototype = {
    *         Options object. For valid options and default values see
    *         _mergeOptions().
    * @return {DocumentFragment}
    *         A document fragment containing color swatches etc.
    */
   parseCssProperty: function(name, value, options={}) {
     options = this._mergeOptions(options);
 
-    // XXX: This is a quick fix that should stay until bug 977063 gets fixed.
-    // It avoids parsing "linear" as a timing-function in "linear-gradient(...)"
-    options.expectCubicBezier = ["transition", "transition-timing-function",
-      "animation", "animation-timing-function"].indexOf(name) !== -1;
-
+    options.expectCubicBezier =
+      safeCssPropertySupportsType(name, DOMUtils.TYPE_TIMING_FUNCTION);
     options.expectFilter = name === "filter";
+    options.supportsColor =
+      safeCssPropertySupportsType(name, DOMUtils.TYPE_COLOR) ||
+      safeCssPropertySupportsType(name, DOMUtils.TYPE_GRADIENT);
 
     if (this._cssPropertySupportsValue(name, value)) {
       return this._parse(value, options);
     }
     this._appendTextNode(value);
 
     return this._toDOM();
   },
 
   /**
-   * Parse a string.
-   *
-   * @param  {String} value
-   *         Text to parse.
-   * @param  {Object} [options]
-   *         Options object. For valid options and default values see
-   *         _mergeOptions().
-   * @return {DocumentFragment}
-   *         A document fragment. Colors will not be parsed.
-   */
-  parseHTMLAttribute: function(value, options={}) {
-    options.isHTMLAttribute = true;
-    options = this._mergeOptions(options);
-
-    return this._parse(value, options);
-  },
-
-  /**
    * Matches the beginning of the provided string to a css background-image url
    * and return both the whole url(...) match and the url itself.
    * This isn't handled via a regular expression to make sure we can match urls
    * that contain parenthesis easily
    */
   _matchBackgroundUrl: function(text) {
     let startToken = "url(";
     if (text.indexOf(startToken) !== 0) {
@@ -235,30 +195,17 @@ OutputParser.prototype = {
           let match = matched[0];
           text = this._trimMatchFromStart(text, match);
 
           this._appendCubicBezier(match, options);
           continue;
         }
       }
 
-      matched = text.match(REGEX_ALL_CSS_PROPERTIES);
-      if (matched) {
-        let [match] = matched;
-
-        text = this._trimMatchFromStart(text, match);
-        this._appendTextNode(match);
-
-        if (options.isHTMLAttribute) {
-          [text] = this._appendColorOnMatch(text, options);
-        }
-        continue;
-      }
-
-      if (!options.isHTMLAttribute) {
+      if (options.supportsColor) {
         let dirty;
 
         [text, dirty] = this._appendColorOnMatch(text, options);
 
         if (dirty) {
           continue;
         }
       }
@@ -583,43 +530,54 @@ OutputParser.prototype = {
    *           - defaultColorType: true // Convert colors to the default type
    *                                    // selected in the options panel.
    *           - colorSwatchClass: ""   // The class to use for color swatches.
    *           - colorClass: ""         // The class to use for the color value
    *                                    // that follows the swatch.
    *           - bezierSwatchClass: ""  // The class to use for bezier swatches.
    *           - bezierClass: ""        // The class to use for the bezier value
    *                                    // that follows the swatch.
-   *           - isHTMLAttribute: false // This property indicates whether we
-   *                                    // are parsing an HTML attribute value.
-   *                                    // When the value is passed in from an
-   *                                    // HTML attribute we need to check that
-   *                                    // any CSS property values are supported
-   *                                    // by the property name before
-   *                                    // processing the property value.
+   *           - supportsColor: false   // Does the CSS property support colors?
    *           - urlClass: ""           // The class to be used for url() links.
    *           - baseURI: ""            // A string or nsIURI used to resolve
    *                                    // relative links.
    * @return {Object}
    *         Overridden options object
    */
   _mergeOptions: function(overrides) {
     let defaults = {
       defaultColorType: true,
       colorSwatchClass: "",
       colorClass: "",
       bezierSwatchClass: "",
       bezierClass: "",
-      isHTMLAttribute: false,
+      supportsColor: false,
       urlClass: "",
       baseURI: ""
     };
 
     if (typeof overrides.baseURI === "string") {
       overrides.baseURI = Services.io.newURI(overrides.baseURI, null, null);
     }
 
     for (let item in overrides) {
       defaults[item] = overrides[item];
     }
     return defaults;
   }
 };
+
+/**
+ * A wrapper for DOMUtils.cssPropertySupportsType that ignores invalid
+ * properties.
+ *
+ * @param {String} name The property name.
+ * @param {number} type The type tested for support.
+ * @return {Boolean} Whether the property supports the type.
+ *        If the property is unknown, false is returned.
+ */
+function safeCssPropertySupportsType(name, type) {
+  try {
+    return DOMUtils.cssPropertySupportsType(name, type);
+  } catch(e) {
+    return false;
+  }
+}
--- a/toolkit/mozapps/extensions/internal/moz.build
+++ b/toolkit/mozapps/extensions/internal/moz.build
@@ -28,8 +28,12 @@ EXTRA_PP_JS_MODULES.addons += [
 
 # This is used in multiple places, so is defined here to avoid it getting
 # out of sync.
 DEFINES['MOZ_EXTENSIONS_DB_SCHEMA'] = 17
 
 # Additional debugging info is exposed in debug builds
 if CONFIG['MOZ_EM_DEBUG']:
     DEFINES['MOZ_EM_DEBUG'] = 1
+
+# Add-on signing cannot be preffed off in official beta, release or esr builds
+if CONFIG['MOZ_UPDATE_CHANNEL'] in ('beta', 'release', 'esr') and CONFIG['MOZ_OFFICIAL_BRANDING']:
+    DEFINES['MOZ_REQUIRE_SIGNING'] = 1
--- a/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
+++ b/toolkit/mozapps/update/tests/data/xpcshellUtilsAUS.js
@@ -1797,17 +1797,17 @@ function setupAppFiles() {
   let appFiles = [ { relPath  : FILE_APP_BIN,
                      inGreDir : false },
                    { relPath  : FILE_APPLICATION_INI,
                      inGreDir : true },
                    { relPath  : "dependentlibs.list",
                      inGreDir : true } ];
 
   // On Linux the updater.png must also be copied
-  if (IS_UNIX && !IS_MACOSX) {
+  if (IS_UNIX && !IS_MACOSX && !IS_TOOLKIT_GONK) {
     appFiles.push( { relPath  : "icons/updater.png",
                      inGreDir : true } );
   }
 
   // Read the dependent libs file leafnames from the dependentlibs.list file
   // into the array.
   let deplibsFile = gGREDirOrig.clone();
   deplibsFile.append("dependentlibs.list");
@@ -1884,24 +1884,26 @@ function copyFileToTestAppDir(aFileRelPa
       if (pathParts[i]) {
         srcFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : ""));
         destFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : ""));
       }
     }
     fileRelPath = fileRelPath + ".app";
   }
 
-  Assert.ok(srcFile.exists(), MSG_SHOULD_EXIST);
+  Assert.ok(srcFile.exists(),
+            MSG_SHOULD_EXIST + ", leafName: " + srcFile.leafName);
 
   // Symlink libraries. Note that the XUL library on Mac OS X doesn't have a
   // file extension and shouldSymlink will always be false on Windows.
   let shouldSymlink = (pathParts[pathParts.length - 1] == "XUL" ||
                        fileRelPath.substr(fileRelPath.length - 3) == ".so" ||
                        fileRelPath.substr(fileRelPath.length - 6) == ".dylib");
-  if (!shouldSymlink) {
+  // The tests don't support symlinks on gonk.
+  if (!shouldSymlink || IS_TOOLKIT_GONK) {
     if (!destFile.exists()) {
       try {
         srcFile.copyToFollowingLinks(destFile.parent, destFile.leafName);
       } catch (e) {
         // Just in case it is partially copied
         if (destFile.exists()) {
           try {
             destFile.remove(true);
@@ -3205,17 +3207,17 @@ function overrideUpdatePrompt(aCallback)
 }
 
 function UpdatePrompt(aCallback) {
   this._callback = aCallback;
 
   let fns = ["checkForUpdates", "showUpdateAvailable", "showUpdateDownloaded",
              "showUpdateError", "showUpdateHistory", "showUpdateInstalled"];
 
-  fns.forEach(function(aPromptFn) {
+  fns.forEach(function UP_fns(aPromptFn) {
     UpdatePrompt.prototype[aPromptFn] = function() {
       if (!this._callback) {
         return;
       }
 
       let callback = this._callback[aPromptFn];
       if (!callback) {
         return;
--- a/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marAppInUseStageSuccessComplete_unix.js
@@ -12,17 +12,18 @@ function run_test() {
   gTestFiles[gTestFiles.length - 1].compareContents = "FromComplete\n";
   gTestFiles[gTestFiles.length - 1].comparePerms = 0o644;
   gTestDirs = gTestDirsCompleteSuccess;
   setupUpdaterTest(FILE_COMPLETE_MAR);
 
   createUpdaterINI(false);
   setAppBundleModTime();
 
-  if (IS_UNIX) {
+  // The tests don't support symlinks on gonk.
+  if (IS_UNIX && !IS_TOOLKIT_GONK) {
     removeSymlink();
     createSymlink();
     do_register_cleanup(removeSymlink);
     gTestFiles.splice(gTestFiles.length - 3, 0,
     {
       description      : "Readable symlink",
       fileName         : "link",
       relPathDir       : DIR_RESOURCES,
@@ -82,17 +83,18 @@ function checkUpdateApplied() {
  */
 function finishCheckUpdateApplied() {
   checkAppBundleModTime();
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, false);
   setupHelperFinish();
 }
 
 function checkUpdate() {
-  if (IS_UNIX) {
+  // The tests don't support symlinks on gonk.
+  if (IS_UNIX && !IS_TOOLKIT_GONK) {
     checkSymlink();
   }
   checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
   standardInit();
   checkCallbackAppLog();
 }
 
 function runHelperProcess(args) {
--- a/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
+++ b/toolkit/mozapps/update/tests/unit_base_updater/marStageSuccessComplete.js
@@ -24,17 +24,18 @@ function run_test() {
     writeFile(testFile, "test\n");
   }
 
   createUpdaterINI(false);
   setAppBundleModTime();
 
   // Don't test symlinks on Mac OS X in this test since it tends to timeout.
   // It is tested on Mac OS X in marAppInUseStageSuccessComplete_unix.js
-  if (IS_UNIX && !IS_MACOSX) {
+  // The tests don't support symlinks on gonk.
+  if (IS_UNIX && !IS_MACOSX && !IS_TOOLKIT_GONK) {
     removeSymlink();
     createSymlink();
     do_register_cleanup(removeSymlink);
     gTestFiles.splice(gTestFiles.length - 3, 0,
     {
       description      : "Readable symlink",
       fileName         : "link",
       relPathDir       : DIR_RESOURCES,
@@ -82,17 +83,18 @@ function finishCheckUpdateApplied() {
   checkPostUpdateRunningFile(true);
 
   if (IS_MACOSX) {
     let distributionDir = getApplyDirFile(DIR_MACOS + "distribution", true);
     Assert.ok(!distributionDir.exists(), MSG_SHOULD_NOT_EXIST);
     checkUpdateLogContains("removing old distribution directory");
   }
 
-  if (IS_UNIX && !IS_MACOSX) {
+  // The tests don't support symlinks on gonk.
+  if (IS_UNIX && !IS_MACOSX && !IS_TOOLKIT_GONK) {
     checkSymlink();
   }
   checkAppBundleModTime();
   checkFilesAfterUpdateSuccess(getApplyDirFile, false, false);
   checkUpdateLogContents(LOG_COMPLETE_SUCCESS);
   standardInit();
   checkCallbackAppLog();
 }
--- a/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
+++ b/toolkit/mozapps/update/tests/unit_base_updater/xpcshell.ini
@@ -10,36 +10,38 @@
 tags = appupdate
 head = head_update.js
 tail =
 
 [marSuccessComplete.js]
 [marSuccessPartial.js]
 [marFailurePartial.js]
 [marStageSuccessComplete.js]
-skip-if = toolkit == 'gonk'
-reason = bug 820380
 [marStageSuccessPartial.js]
 [marVersionDowngrade.js]
 skip-if = os != 'win' && os != 'mac'
+reason = mar signing
 [marWrongChannel.js]
 skip-if = os != 'win' && os != 'mac'
+reason = mar signing
 [marStageFailurePartial.js]
 [marCallbackAppSuccessComplete_win.js]
 skip-if = os != 'win'
 [marCallbackAppSuccessPartial_win.js]
 skip-if = os != 'win'
 [marCallbackAppStageSuccessComplete_win.js]
 skip-if = os != 'win'
 [marCallbackAppStageSuccessPartial_win.js]
 skip-if = os != 'win'
 [marAppInUseSuccessComplete.js]
-skip-if = toolkit == 'gonk'
+skip-if = debug == true && toolkit == 'gonk'
+reason = bug 1163355
 [marAppInUseStageSuccessComplete_unix.js]
-skip-if = !(os == 'linux' || os == 'sunos' || os == 'mac')
+skip-if = os == 'win' || debug == true && toolkit == 'gonk'
+reason = bug 1163355
 [marAppInUseStageFailureComplete_win.js]
 skip-if = os != 'win'
 [marAppInUseFallbackStageFailureComplete_win.js]
 skip-if = os != 'win'
 [marFileLockedFailureComplete_win.js]
 skip-if = os != 'win'
 [marFileLockedFailurePartial_win.js]
 skip-if = os != 'win'
@@ -71,18 +73,18 @@ skip-if = os != 'win'
 [marFileInUseFallbackStageFailureComplete_win.js]
 skip-if = os != 'win'
 [marFileInUseFallbackStageFailurePartial_win.js]
 skip-if = os != 'win'
 [marRMRFDirFileInUseFallbackStageFailureComplete_win.js]
 skip-if = os != 'win'
 [marRMRFDirFileInUseFallbackStageFailurePartial_win.js]
 skip-if = os != 'win'
-reason = bug 820380
 [marAppApplyDirLockedStageFailure_win.js]
 skip-if = os != 'win'
 [marAppApplyUpdateAppBinInUseStageSuccess_win.js]
 skip-if = os != 'win'
 [marAppApplyUpdateSuccess.js]
-skip-if = toolkit == 'gonk'
-reason = bug 820380
+skip-if = debug == true && toolkit == 'gonk'
+reason = bug 1163354
 [marAppApplyUpdateStageSuccess.js]
-skip-if = toolkit == 'gonk'
+skip-if = debug == true && toolkit == 'gonk'
+reason = bug 1163354
--- a/toolkit/themes/linux/global/in-content/common.css
+++ b/toolkit/themes/linux/global/in-content/common.css
@@ -78,17 +78,16 @@ xul|*.radio-label-box {
   -moz-appearance: none;
 }
 
 xul|*.numberbox-input-box {
   -moz-appearance: none;
   border-width: 0;
 }
 
-html|a:-moz-focusring,
 xul|*.text-link:-moz-focusring,
 xul|*.inline-link:-moz-focusring {
   border: 1px dotted -moz-DialogText;
 }
 
 xul|spinbuttons {
   -moz-appearance: none;
 }
--- a/toolkit/themes/osx/global/in-content/common.css
+++ b/toolkit/themes/osx/global/in-content/common.css
@@ -71,17 +71,16 @@ xul|*.numberbox-input-box {
   border-width: 0;
 }
 
 xul|description {
   font-size: 1.25rem;
   line-height: 22px;
 }
 
-html|a:-moz-focusring,
 xul|*.text-link:-moz-focusring,
 xul|*.inline-link:-moz-focusring {
   color: #ff9500;
   text-decoration: underline;
   box-shadow: none;
 }
 
 xul|button:-moz-focusring,
--- a/toolkit/themes/shared/extensions/extensions.inc.css
+++ b/toolkit/themes/shared/extensions/extensions.inc.css
@@ -52,21 +52,16 @@
   .global-warning .warning-icon {
     background-color: #fff;
     box-shadow: 0 0 2px 5px #fff;
     border-radius: 10px;
   }
 }
 
 /*** global informations ***/
-#addons-page .global-info-container {
-  background-color: #f3f7fb;
-  border-top-right-radius: 2px;
-  border-top-left-radius: 2px;
-}
 
 /* Plugins aren't yet disabled by safemode (bug 342333),
    so don't show that warning when viewing plugins. */
 #addons-page[warning="safemode"] .view-pane[type="plugin"] .global-warning-container,
 #addons-page[warning="safemode"] #detail-view[loading="true"] .global-warning-container {
   background-color: inherit;
   background-image: none;
 }
@@ -348,20 +343,28 @@
 }
 
 
 /*** list ***/
 
 .list {
   -moz-appearance: none;
   margin: 0;
-  border-color: transparent;
+  border-width: 0 !important;
   background-color: transparent;
 }
 
+.list > scrollbox > .scrollbox-innerbox {
+  border: 1px dotted transparent;
+}
+
+.list:-moz-focusring > scrollbox > .scrollbox-innerbox {
+  border-color: #0095dd;
+}
+
 .addon {
   color: #444;
   border-bottom: 1px solid #c1c1c1;
   padding: 5px;
   background-origin: border-box;
 }
 
 .addon:not(:only-child):last-child {
--- a/toolkit/themes/windows/global/in-content/common.css
+++ b/toolkit/themes/windows/global/in-content/common.css
@@ -51,17 +51,16 @@ xul|radio {
   }
 }
 
 xul|*.radio-icon,
 xul|*.checkbox-icon {
   -moz-margin-end: 0;
 }
 
-html|a:-moz-focusring,
 xul|*.text-link:-moz-focusring,
 xul|*.inline-link:-moz-focusring {
   border: 1px dotted -moz-DialogText;
 }
 
 /* Don't draw a transparent border for the focusring because when page
    colors are disabled, the border is drawn in -moz-DialogText */
 xul|*.text-link:not(:-moz-focusring),
--- a/toolkit/webapps/MacNativeApp.js
+++ b/toolkit/webapps/MacNativeApp.js
@@ -289,29 +289,31 @@ NativeApp.prototype = {
    *
    * @param aMimeType     the icon mimetype
    * @param aImageStream  the stream for the image data
    * @param aDir          the directory where the icon should be stored
    */
   _processIcon: function(aMimeType, aIcon, aDir) {
     let deferred = Promise.defer();
 
-    let tmpIconPath = OS.Path.join(aDir, this.iconFile);
+    let finalIconPath = OS.Path.join(aDir, this.iconFile);
+    let tmpIconPath = OS.Path.join(OS.Constants.Path.tmpDir, "appicon.icns");
 
     function conversionDone(aSubject, aTopic) {
       if (aTopic != "process-finished") {
         deferred.reject("Failure converting icon, exit code: " + aSubject.exitValue);
         return;
       }
 
       // SIPS silently fails to convert the icon, so we need to verify if the
       // icon was successfully converted.
       OS.File.exists(tmpIconPath).then((aExists) => {
         if (aExists) {
-          deferred.resolve();
+          OS.File.move(tmpIconPath, finalIconPath).then(
+            deferred.resolve, err => deferred.reject(err));
         } else {
           deferred.reject("Failure converting icon, unrecognized image format");
         }
       });
     }
 
     let process = Cc["@mozilla.org/process/util;1"].
                   createInstance(Ci.nsIProcess);
--- a/toolkit/webapps/tests/chrome.ini
+++ b/toolkit/webapps/tests/chrome.ini
@@ -31,8 +31,9 @@ skip-if = asan
 skip-if = asan
 [test_hosted_icons.xul]
 [test_hosted_checkforupdates_from_webapp_runtime.xul]
 skip-if = asan
 [test_packaged_icons.xul]
 [test_packaged_checkforupdates_from_webapp_runtime.xul]
 skip-if = asan
 [test_webapp_runtime_executable_update.xul]
+[test_non_ascii_app_name.xul]
new file mode 100644
--- /dev/null
+++ b/toolkit/webapps/tests/test_non_ascii_app_name.xul
@@ -0,0 +1,158 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1152552
+-->
+<window title="Mozilla Bug 1152552"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript"
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="head.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=898647"
+     target="_blank">Mozilla Bug 898647</a>
+  </body>
+
+<script type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 1152552 **/
+
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NativeApp.jsm");
+Cu.import("resource://gre/modules/WebappOSUtils.jsm");
+
+let manifest = {
+  // Test unicode app names, the hyphen being \u2013
+  name: "Sample hosted app – \u2603 \u26a0",
+};
+
+let app = {
+  name: "Sample hosted app – \u2603 \u26a0",
+  manifestURL: "http://example.com/sample.manifest",
+  manifest: manifest,
+  origin: "http://example.com/",
+  categories: [],
+  installOrigin: "http://example.com/",
+  receipts: [],
+  installTime: Date.now(),
+};
+
+let testAppInfo = new TestAppInfo(app);
+
+let runTest = Task.async(function*() {
+  // Get to a clean state before the test
+  yield testAppInfo.cleanup();
+
+  SimpleTest.registerCleanupFunction(() => testAppInfo.cleanup());
+
+  setDryRunPref();
+
+  let nativeApp = new NativeApp(app, manifest, app.categories);
+  ok(nativeApp, "NativeApp object created");
+
+  info("Test update for an uninstalled application");
+  try {
+    yield nativeApp.prepareUpdate(app, manifest);
+    ok(false, "Didn't thrown");
+  } catch (ex) {
+    is(ex, "The application isn't installed", "Exception thrown");
+  }
+
+  testAppInfo.profileDir = nativeApp.createProfile();
+  ok(testAppInfo.profileDir && testAppInfo.profileDir.exists(), "Profile directory created");
+  ok((yield OS.File.exists(testAppInfo.profilesIni)), "profiles.ini file created");
+
+  // On Mac build servers, we don't have enough privileges to write to /Applications,
+  // so we install apps in a user-owned directory.
+  if (MAC) {
+    yield setMacRootInstallDir(OS.Path.join(OS.Constants.Path.homeDir, "Applications"));
+  }
+
+  // Install application
+  info("Test installation");
+  yield nativeApp.install(app, manifest);
+  while (!WebappOSUtils.isLaunchable(app)) {
+    yield wait(1000);
+  }
+  ok(true, "App launchable");
+  ok((yield checkFiles(testAppInfo.installedFiles)), "Files correctly written");
+  is(WebappOSUtils.getInstallPath(app), testAppInfo.installPath, "getInstallPath == installPath");
+
+  let stat = yield OS.File.stat(testAppInfo.installPath);
+  let installTime = stat.lastModificationDate;
+
+  // Wait one second, otherwise the last modification date is the same.
+  yield wait(1000);
+
+  // Reinstall application
+  info("Test reinstallation");
+  yield nativeApp.install(app, manifest);
+  while (!WebappOSUtils.isLaunchable(app)) {
+    yield wait(1000);
+  }
+  ok(true, "App launchable");
+  ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
+  ok((yield checkFiles(testAppInfo.tempUpdatedFiles)), "Files correctly written in the update subdirectory");
+
+  yield nativeApp.applyUpdate(app);
+  while (!WebappOSUtils.isLaunchable(app)) {
+    yield wait(1000);
+  }
+  ok(true, "App launchable");
+
+  if (MAC) {
+    stat = yield OS.File.stat(testAppInfo.webappINI);
+    is(stat.unixMode, 0o644, "Configuration file permissions correct");
+  } else if (LINUX) {
+    stat = yield OS.File.stat(testAppInfo.desktopINI);
+    is(stat.unixMode, 0o744, "Configuration file permissions correct");
+  }
+
+  ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
+  ok(!(yield OS.File.exists(OS.Path.join(testAppInfo.installPath, "update"))), "Update directory removed");
+  ok((yield checkDateHigherThan(testAppInfo.updatedFiles, installTime)), "Modification date higher");
+
+  stat = yield OS.File.stat(testAppInfo.installPath);
+  installTime = stat.lastModificationDate;
+
+  // Wait one second, otherwise the last modification date is the same.
+  yield wait(1000);
+
+  // Update application
+  info("Test update");
+  yield nativeApp.prepareUpdate(app, manifest);
+  while (!WebappOSUtils.isLaunchable(app)) {
+    yield wait(1000);
+  }
+  ok(true, "App launchable");
+  ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
+  ok((yield checkFiles(testAppInfo.tempUpdatedFiles)), "Files correctly written in the update subdirectory");
+
+  yield nativeApp.applyUpdate(app);
+  while (!WebappOSUtils.isLaunchable(app)) {
+    yield wait(1000);
+  }
+  ok(true, "App launchable");
+  ok((yield checkFiles(testAppInfo.installedFiles)), "Installation not corrupted");
+  ok(!(yield OS.File.exists(OS.Path.join(testAppInfo.installPath, "update"))), "Update directory removed");
+  ok((yield checkDateHigherThan(testAppInfo.updatedFiles, installTime)), "Modification date higher");
+
+  SimpleTest.finish();
+});
+
+runTest().catch((e) => {
+  ok(false, "Error during test: " + e);
+  SimpleTest.finish();
+});
+
+]]>
+</script>
+</window>
--- a/widget/gonk/nsAppShell.cpp
+++ b/widget/gonk/nsAppShell.cpp
@@ -44,16 +44,17 @@
 #include "mozilla/MouseEvents.h"
 #include "mozilla/Mutex.h"
 #include "mozilla/Services.h"
 #include "mozilla/TextEvents.h"
 #if ANDROID_VERSION >= 18
 #include "nativewindow/FakeSurfaceComposer.h"
 #endif
 #include "nsAppShell.h"
+#include "mozilla/DebugOnly.h"
 #include "mozilla/dom/Touch.h"
 #include "nsGkAtoms.h"
 #include "nsIObserverService.h"
 #include "nsIScreen.h"
 #include "nsScreenManagerGonk.h"
 #include "nsThreadUtils.h"
 #include "nsWindow.h"
 #include "OrientationObserver.h"
@@ -573,17 +574,17 @@ GeckoInputReaderPolicy::setDisplayInfo()
                   "Orientation enums not matched!");
     static_assert(nsIScreen::ROTATION_270_DEG ==
                   DISPLAY_ORIENTATION_270,
                   "Orientation enums not matched!");
 
     nsRefPtr<nsScreenGonk> screen = nsScreenManagerGonk::GetPrimaryScreen();
 
     uint32_t rotation = nsIScreen::ROTATION_0_DEG;
-    nsresult rv = screen->GetRotation(&rotation);
+    DebugOnly<nsresult> rv = screen->GetRotation(&rotation);
     MOZ_ASSERT(NS_SUCCEEDED(rv));
     nsIntRect screenBounds = screen->GetNaturalBounds();
 
     DisplayViewport viewport;
     viewport.displayId = 0;
     viewport.orientation = rotation;
     viewport.physicalRight = viewport.deviceWidth = screenBounds.width;
     viewport.physicalBottom = viewport.deviceHeight = screenBounds.height;
--- a/xpcom/components/Module.h
+++ b/xpcom/components/Module.h
@@ -17,17 +17,17 @@ namespace mozilla {
 /**
  * A module implements one or more XPCOM components. This structure is used
  * for both binary and script modules, but the registration members
  * (cids/contractids/categoryentries) are unused for modules which are loaded
  * via a module loader.
  */
 struct Module
 {
-  static const unsigned int kVersion = 40;
+  static const unsigned int kVersion = 41;
 
   struct CIDEntry;
 
   typedef already_AddRefed<nsIFactory> (*GetFactoryProcPtr)(
     const Module& module, const CIDEntry& entry);
 
   typedef nsresult (*ConstructorProcPtr)(nsISupports* aOuter,
                                          const nsIID& aIID,