author | Carsten "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 id | 28741 |
push user | kwierso@gmail.com |
push date | Tue, 12 May 2015 23:24:40 +0000 |
treeherder | mozilla-central@d476776d920d [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 41.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
|
--- 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/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="©LinkCmd.label;" accesskey="©LinkCmd.accesskey;" - oncommand="contextMenuCopyLinkOrEmail();"/> + oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/> <menuitem id="context-copyEmail" label="©EmailCmd.label;" accesskey="©EmailCmd.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="©LinkCmd.label;" accesskey="©LinkCmd.accesskey;" - oncommand="contextMenuCopyLinkOrEmail();"/> + oncommand="ViewSourceChrome.onContextMenuCopyLinkOrEmail();"/> <menuitem id="context-copyEmail" label="©EmailCmd.label;" accesskey="©EmailCmd.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,