зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound a=merge
This commit is contained in:
Коммит
4d9a345155
|
@ -827,8 +827,11 @@ pref("network.gonk.manage-offline-status", true);
|
|||
// On Firefox Mulet, we can't enable shared JSM scope
|
||||
// as it breaks most Firefox JSMs (see bug 961777)
|
||||
#ifndef MOZ_MULET
|
||||
// Break any JSMs or JS components that rely on shared scope
|
||||
#ifndef DEBUG
|
||||
pref("jsloader.reuseGlobal", true);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// Enable font inflation for browser tab content.
|
||||
pref("font.size.inflation.minTwips", 120);
|
||||
|
|
|
@ -486,6 +486,7 @@ let settingsToObserve = {
|
|||
resetToPref: true
|
||||
},
|
||||
'dom.mozApps.use_reviewer_certs': false,
|
||||
'dom.mozApps.signed_apps_installable_from': 'https://marketplace.firefox.com',
|
||||
'layers.draw-borders': false,
|
||||
'layers.draw-tile-borders': false,
|
||||
'layers.dump': false,
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
|
||||
|
@ -133,7 +133,7 @@
|
|||
<project name="platform/hardware/akm" path="hardware/akm" revision="6d3be412647b0eab0adff8a2768736cf4eb68039"/>
|
||||
<project groups="invensense" name="platform/hardware/invensense" path="hardware/invensense" revision="e6d9ab28b4f4e7684f6c07874ee819c9ea0002a2"/>
|
||||
<project name="platform/hardware/ril" path="hardware/ril" revision="865ce3b4a2ba0b3a31421ca671f4d6c5595f8690"/>
|
||||
<project name="kernel/common" path="kernel" revision="28aab3bd1139b6beea545f50dee8903c0634de84"/>
|
||||
<project name="kernel/common" path="kernel" revision="f365109310138f85bb91884b7dee60f6f0da042d"/>
|
||||
<project name="platform/system/core" path="system/core" revision="53d584d4a4b4316e4de9ee5f210d662f89b44e7e"/>
|
||||
<project name="u-boot" path="u-boot" revision="982c1fd67b89d5573317c1796cf5b0143de44e8a"/>
|
||||
<project name="vendor/sprd/gps" path="vendor/sprd/gps" revision="6974f8e771d4d8e910357a6739ab124768891e8f"/>
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
<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="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -134,7 +134,7 @@
|
|||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="d259117b4976decbe2f76eeed85218bf0109190f"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="9f28c4faea3b2f01db227b2467b08aeba96d9bec"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2a5dc67db7c9e6651c5fff855a6f69c1f2061ca7"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="cf9376f0f59ca72333dd24a54efe887d527da612"/>
|
||||
<project name="android-sdk" path="sdk" remote="b2g" revision="8b1365af38c9a653df97349ee53a3f5d64fd590a"/>
|
||||
<project name="darwinstreamingserver" path="system/darwinstreamingserver" remote="b2g" revision="cf85968c7f85e0ec36e72c87ceb4837a943b8af6"/>
|
||||
</manifest>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="f92a936f2aa97526d4593386754bdbf02db07a12"/>
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="6e47ff2790f5656b5b074407829ceecf3e6188c4"/>
|
||||
|
|
|
@ -19,13 +19,13 @@
|
|||
<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="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="cd88d860656c31c7da7bb310d6a160d0011b0961"/>
|
||||
<project name="platform_external_qemu" path="external/qemu" remote="b2g" revision="c058843242068d0df7c107e09da31b53d2e08fa6"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="dd924f92906085b831bf1cbbc7484d3c043d613c"/>
|
||||
<project name="platform/bionic" path="bionic" revision="c72b8f6359de7ed17c11ddc9dfdde3f615d188a9"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<project name="platform_build" path="build" remote="b2g" revision="3a2947df41a480de1457a6dcdbf46ad0af70d8e0">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -23,7 +23,7 @@
|
|||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<!-- Stock Android things -->
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/i686-linux-glibc2.7-4.6" revision="95bb5b66b3ec5769c3de8d3f25d681787418e7d2"/>
|
||||
<project groups="linux" name="platform/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" path="prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.7-4.6" revision="ebdad82e61c16772f6cd47e9f11936bf6ebe9aa0"/>
|
||||
|
@ -151,7 +151,7 @@
|
|||
<project name="platform/hardware/ril" path="hardware/ril" revision="12b1977cc704b35f2e9db2bb423fa405348bc2f3"/>
|
||||
<project name="platform/system/bluetooth" path="system/bluetooth" revision="985bf15264d865fe7b9c5b45f61c451cbaafa43d"/>
|
||||
<project name="platform/system/core" path="system/core" revision="350eac5403124dacb2a5fd9e28ac290a59fc3b8e"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2a5dc67db7c9e6651c5fff855a6f69c1f2061ca7"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="cf9376f0f59ca72333dd24a54efe887d527da612"/>
|
||||
<project name="platform/system/qcom" path="system/qcom" revision="63e3f6f176caad587d42bba4c16b66d953fb23c2"/>
|
||||
<project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="d8952a42771045fca73ec600e2b42a4c7129d723"/>
|
||||
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="7704e16da545f4207812e593743d6743e1afb9c5"/>
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -145,7 +145,7 @@
|
|||
<project name="platform/hardware/ril" path="hardware/ril" revision="c4e2ac95907a5519a0e09f01a0d8e27fec101af0"/>
|
||||
<project name="platform/system/bluetooth" path="system/bluetooth" revision="e1eb226fa3ad3874ea7b63c56a9dc7012d7ff3c2"/>
|
||||
<project name="platform/system/core" path="system/core" revision="adc485d8755af6a61641d197de7cfef667722580"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2a5dc67db7c9e6651c5fff855a6f69c1f2061ca7"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="cf9376f0f59ca72333dd24a54efe887d527da612"/>
|
||||
<project name="platform/system/qcom" path="system/qcom" revision="1cdab258b15258b7f9657da70e6f06ebd5a2fc25"/>
|
||||
<project name="platform/vendor/qcom/msm8610" path="device/qcom/msm8610" revision="4ae5df252123591d5b941191790e7abed1bce5a4"/>
|
||||
<project name="platform/vendor/qcom-opensource/wlan/prima" path="vendor/qcom/opensource/wlan/prima" revision="ce18b47b4a4f93a581d672bbd5cb6d12fe796ca9"/>
|
||||
|
|
|
@ -4,6 +4,6 @@
|
|||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "fb5a4aa15e266e9aa0e44281241ad081b528c75b",
|
||||
"revision": "7f097f40e32ecba580890ce1d3df2a493641bdec",
|
||||
"repo_path": "/integration/gaia-central"
|
||||
}
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
<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="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
<project name="platform/bionic" path="bionic" revision="d2eb6c7b6e1bc7643c17df2d9d9bcb1704d0b9ab"/>
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<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="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
|
|
@ -17,10 +17,10 @@
|
|||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<project name="valgrind" path="external/valgrind" remote="b2g" revision="daa61633c32b9606f58799a3186395fd2bbb8d8c"/>
|
||||
<project name="vex" path="external/VEX" remote="b2g" revision="47f031c320888fe9f3e656602588565b52d43010"/>
|
||||
<!-- Stock Android things -->
|
||||
|
@ -129,7 +129,7 @@
|
|||
<project name="device-mako" path="device/lge/mako" remote="b2g" revision="78d17f0c117f0c66dd55ee8d5c5dde8ccc93ecba"/>
|
||||
<project name="device/generic/armv7-a-neon" path="device/generic/armv7-a-neon" revision="3a9a17613cc685aa232432566ad6cc607eab4ec1"/>
|
||||
<project name="device/lge/mako-kernel" path="device/lge/mako-kernel" revision="d1729e53d71d711c8fde25eab8728ff2b9b4df0e"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="2a5dc67db7c9e6651c5fff855a6f69c1f2061ca7"/>
|
||||
<project name="platform_system_nfcd" path="system/nfcd" remote="b2g" revision="cf9376f0f59ca72333dd24a54efe887d527da612"/>
|
||||
<project name="platform/external/libnfc-nci" path="external/libnfc-nci" revision="7d33aaf740bbf6c7c6e9c34a92b371eda311b66b"/>
|
||||
<project name="platform/external/wpa_supplicant_8" path="external/wpa_supplicant_8" revision="0e56e450367cd802241b27164a2979188242b95f"/>
|
||||
<project name="platform/hardware/broadcom/wlan" path="hardware/broadcom/wlan" revision="0e1929fa3aa38bf9d40e9e953d619fab8164c82e"/>
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
<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="77ef35f5429bc3dfe9ca192b9aacc3c0bf8857de"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="0e280591881d44b80f456bc27e12d9114c218868"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="5883a99b6528ced9dafaed8d3ca2405fb285537e"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="562d357b72279a9e35d4af5aeecc8e1ffa2f44f1"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="0f7792c39ad26aedecf457117c21b16cc1aee879"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="4f9042d3a705307849a6f63961eaaaa2e1d85d77"/>
|
||||
<project name="gonk-patches" path="patches" remote="b2g" revision="223a2421006e8f5da33f516f6891c87cae86b0f6"/>
|
||||
<!-- Stock Android things -->
|
||||
<project name="platform/abi/cpp" path="abi/cpp" revision="6426040f1be4a844082c9769171ce7f5341a5528"/>
|
||||
|
|
|
@ -44,14 +44,14 @@ var gPluginHandler = {
|
|||
switch (msg.name) {
|
||||
case "PluginContent:ShowClickToPlayNotification":
|
||||
this.showClickToPlayNotification(msg.target, msg.data.plugins, msg.data.showNow,
|
||||
msg.principal, msg.data.host);
|
||||
msg.principal, msg.data.host, msg.data.location);
|
||||
break;
|
||||
case "PluginContent:RemoveNotification":
|
||||
this.removeNotification(msg.target, msg.data.name);
|
||||
break;
|
||||
case "PluginContent:UpdateHiddenPluginUI":
|
||||
this.updateHiddenPluginUI(msg.target, msg.data.haveInsecure, msg.data.actions,
|
||||
msg.principal, msg.data.host);
|
||||
msg.principal, msg.data.host, msg.data.location);
|
||||
break;
|
||||
case "PluginContent:HideNotificationBar":
|
||||
this.hideNotificationBar(msg.target, msg.data.name);
|
||||
|
@ -139,11 +139,6 @@ var gPluginHandler = {
|
|||
// list again
|
||||
this.options.primaryPlugin = null;
|
||||
}
|
||||
else if (event == "removed") {
|
||||
// Once the notification is removed, let the content script clear any
|
||||
// caches it may have populated.
|
||||
this.browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationRemoved");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -221,7 +216,8 @@ var gPluginHandler = {
|
|||
});
|
||||
},
|
||||
|
||||
showClickToPlayNotification: function (browser, plugins, showNow, principal, host) {
|
||||
showClickToPlayNotification: function (browser, plugins, showNow, principal,
|
||||
host, location) {
|
||||
// It is possible that we've received a message from the frame script to show
|
||||
// a click to play notification for a principal that no longer matches the one
|
||||
// that the browser's content now has assigned (ie, the browser has browsed away
|
||||
|
@ -231,6 +227,15 @@ var gPluginHandler = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Data URIs, when linked to from some page, inherit the principal of that
|
||||
// page. That means that we also need to compare the actual locations to
|
||||
// ensure we aren't getting a message from a Data URI that we're no longer
|
||||
// looking at.
|
||||
let receivedURI = BrowserUtils.makeURI(location);
|
||||
if (!browser.documentURI.equalsExceptRef(receivedURI)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let notification = PopupNotifications.getNotification("click-to-play-plugins", browser);
|
||||
|
||||
// If this is a new notification, create a pluginData map, otherwise append
|
||||
|
@ -305,7 +310,8 @@ var gPluginHandler = {
|
|||
notificationBox.removeNotification(notification, true);
|
||||
},
|
||||
|
||||
updateHiddenPluginUI: function (browser, haveInsecure, actions, principal, host) {
|
||||
updateHiddenPluginUI: function (browser, haveInsecure, actions, principal,
|
||||
host, location) {
|
||||
// It is possible that we've received a message from the frame script to show
|
||||
// the hidden plugin notification for a principal that no longer matches the one
|
||||
// that the browser's content now has assigned (ie, the browser has browsed away
|
||||
|
@ -315,6 +321,15 @@ var gPluginHandler = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Data URIs, when linked to from some page, inherit the principal of that
|
||||
// page. That means that we also need to compare the actual locations to
|
||||
// ensure we aren't getting a message from a Data URI that we're no longer
|
||||
// looking at.
|
||||
let receivedURI = BrowserUtils.makeURI(location);
|
||||
if (!browser.documentURI.equalsExceptRef(receivedURI)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set up the icon
|
||||
document.getElementById("plugins-notification-icon").classList.
|
||||
toggle("plugin-blocked", haveInsecure);
|
||||
|
|
|
@ -1025,15 +1025,27 @@ if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
|
|||
// Workaround bug 451997 by hardcoding heights for (potentially) wrapped items:
|
||||
_updateHeights: function(aContainer, aSetHeights) {
|
||||
// Make sure we don't get stuck not finding anything because of the XBL binding between
|
||||
// the popup and the radio/label elements:
|
||||
// the popup and the radio/label/description elements:
|
||||
let view = aContainer.ownerDocument.getElementById("PanelUI-panicView");
|
||||
let variableHeightItems = view.querySelectorAll("radio, label");
|
||||
let variableHeightItems = view.querySelectorAll("radio, label, description");
|
||||
let win = aContainer.ownerDocument.defaultView;
|
||||
for (let item of variableHeightItems) {
|
||||
if (aSetHeights) {
|
||||
item.style.height = win.getComputedStyle(item, null).getPropertyValue("height");
|
||||
let height = win.getComputedStyle(item, null).getPropertyValue("height");
|
||||
item.style.height = height;
|
||||
// In the main menu panel, need to set the height of the container of this
|
||||
// description because otherwise the text will overflow:
|
||||
if (item.id == "PanelUI-panic-mainDesc" &&
|
||||
view.getAttribute("current") == "true" &&
|
||||
// Ensure we don't make this less than the size of the icon:
|
||||
parseInt(height) > 32) {
|
||||
item.parentNode.style.minHeight = height;
|
||||
}
|
||||
} else {
|
||||
item.style.removeProperty("height");
|
||||
if (item.id == "PanelUI-panic-mainDesc") {
|
||||
item.parentNode.style.removeProperty("min-height");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1041,9 +1053,14 @@ if (Services.prefs.getBoolPref("privacy.panicButton.enabled")) {
|
|||
let view = aEvent.target;
|
||||
let forgetButton = view.querySelector("#PanelUI-panic-view-button");
|
||||
forgetButton.addEventListener("command", this);
|
||||
// When the popup starts showing, fix the label and radio heights
|
||||
// if we're in a standalone view (can't tell from here) - see updateHeights.
|
||||
view.ownerDocument.addEventListener("popupshowing", this);
|
||||
if (view.getAttribute("current") == "true") {
|
||||
// In the main menupanel, fix heights immediately:
|
||||
this._updateHeights(view, true);
|
||||
} else {
|
||||
// In a standalone panel, so fix the label and radio heights
|
||||
// when the popup starts showing.
|
||||
view.ownerDocument.addEventListener("popupshowing", this);
|
||||
}
|
||||
},
|
||||
onViewHiding: function(aEvent) {
|
||||
let view = aEvent.target;
|
||||
|
|
|
@ -186,7 +186,7 @@
|
|||
<vbox flex="1">
|
||||
<hbox id="PanelUI-panic-header">
|
||||
<image id="PanelUI-panic-timeframe-icon-small" alt=""/>
|
||||
<description value="&panicButton.view.mainTimeframeDesc;" id="PanelUI-panic-mainDesc"/>
|
||||
<description id="PanelUI-panic-mainDesc" flex="1">&panicButton.view.mainTimeframeDesc;</description>
|
||||
</hbox>
|
||||
<radiogroup id="PanelUI-panic-timeSpan" aria-labelledby="PanelUI-panic-mainDesc">
|
||||
<radio id="PanelUI-panic-5min" label="&panicButton.view.5min;" selected="true"
|
||||
|
@ -199,14 +199,14 @@
|
|||
</vbox>
|
||||
</hbox>
|
||||
<vbox id="PanelUI-panic-explanations">
|
||||
<label id="PanelUI-panic-actionlist-main-label" value="&panicButton.view.mainActionDesc;"/>
|
||||
<label id="PanelUI-panic-actionlist-main-label">&panicButton.view.mainActionDesc;</label>
|
||||
|
||||
<label id="PanelUI-panic-actionlist-cookies" class="PanelUI-panic-actionlist">&panicButton.view.deleteCookies;</label>
|
||||
<label id="PanelUI-panic-actionlist-history" class="PanelUI-panic-actionlist">&panicButton.view.deleteHistory;</label>
|
||||
<label id="PanelUI-panic-actionlist-windows" class="PanelUI-panic-actionlist">&panicButton.view.deleteTabsAndWindows;</label>
|
||||
<label id="PanelUI-panic-actionlist-newwindow" class="PanelUI-panic-actionlist">&panicButton.view.openNewWindow;</label>
|
||||
|
||||
<label id="PanelUI-panic-warning" value="&panicButton.view.undoWarning;"/>
|
||||
<label id="PanelUI-panic-warning">&panicButton.view.undoWarning;</label>
|
||||
</vbox>
|
||||
<button id="PanelUI-panic-view-button"
|
||||
label="&panicButton.view.forgetButton;"/>
|
||||
|
|
|
@ -66,6 +66,32 @@ skip-if = e10s # Bug ?????? - test uses promiseTabLoadEvent() which isn't e10s f
|
|||
[browser_943683_migration_test.js]
|
||||
[browser_944887_destroyWidget_should_destroy_in_palette.js]
|
||||
[browser_945739_showInPrivateBrowsing_customize_mode.js]
|
||||
[browser_947914_button_addons.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_copy.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_cut.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_find.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_history.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_newPrivateWindow.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_newWindow.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_paste.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_print.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_savePage.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_zoomIn.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_zoomOut.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947914_button_zoomReset.js]
|
||||
skip-if = os == "linux" # Intermittent failures
|
||||
[browser_947987_removable_default.js]
|
||||
[browser_948985_non_removable_defaultArea.js]
|
||||
[browser_952963_areaType_getter_no_area.js]
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let initialLocation = gBrowser.currentURI.spec;
|
||||
let newTab = null;
|
||||
|
||||
add_task(function() {
|
||||
info("Check addons button existence and functionality");
|
||||
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let addonsButton = document.getElementById("add-ons-button");
|
||||
ok(addonsButton, "Add-ons button exists in Panel Menu");
|
||||
addonsButton.click();
|
||||
|
||||
newTab = gBrowser.selectedTab;
|
||||
yield waitForCondition(function() gBrowser.currentURI &&
|
||||
gBrowser.currentURI.spec == "about:addons");
|
||||
|
||||
let addonsPage = gBrowser.selectedBrowser.contentWindow.document.
|
||||
getElementById("addons-page");
|
||||
ok(addonsPage, "Add-ons page was opened");
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
gBrowser.addTab(initialLocation);
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
info("Tabs were restored");
|
||||
});
|
|
@ -0,0 +1,64 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let initialLocation = gBrowser.currentURI.spec;
|
||||
let globalClipboard;
|
||||
|
||||
add_task(function() {
|
||||
info("Check copy button existence and functionality");
|
||||
|
||||
let testText = "copy text test";
|
||||
|
||||
gURLBar.focus();
|
||||
info("The URL bar was focused");
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let copyButton = document.getElementById("copy-button");
|
||||
ok(copyButton, "Copy button exists in Panel Menu");
|
||||
is(copyButton.getAttribute("disabled"), "true", "Copy button is initially disabled");
|
||||
|
||||
// copy text from URL bar
|
||||
gURLBar.value = testText;
|
||||
gURLBar.focus();
|
||||
gURLBar.select();
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
ok(!copyButton.hasAttribute("disabled"), "Copy button gets enabled");
|
||||
|
||||
copyButton.click();
|
||||
is(gURLBar.value, testText, "Selected text is unaltered when clicking copy");
|
||||
|
||||
// check that the text was added to the clipboard
|
||||
let clipboard = Services.clipboard;
|
||||
let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
||||
globalClipboard = clipboard.kGlobalClipboard;
|
||||
|
||||
transferable.init(null);
|
||||
transferable.addDataFlavor("text/unicode");
|
||||
clipboard.getData(transferable, globalClipboard);
|
||||
let str = {}, strLength = {};
|
||||
transferable.getTransferData("text/unicode", str, strLength);
|
||||
let clipboardValue = "";
|
||||
|
||||
if (str.value) {
|
||||
str.value.QueryInterface(Ci.nsISupportsString);
|
||||
clipboardValue = str.value.data;
|
||||
}
|
||||
is(clipboardValue, testText, "Data was copied to the clipboard.");
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
// clear the clipboard
|
||||
Services.clipboard.emptyClipboard(globalClipboard);
|
||||
info("Clipboard was cleared");
|
||||
|
||||
// restore the tab as it was at the begining of the test
|
||||
gBrowser.addTab(initialLocation);
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
info("Tabs were restored");
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let initialLocation = gBrowser.currentURI.spec;
|
||||
let globalClipboard;
|
||||
|
||||
add_task(function() {
|
||||
info("Check cut button existence and functionality");
|
||||
|
||||
let testText = "cut text test";
|
||||
|
||||
gURLBar.focus();
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let cutButton = document.getElementById("cut-button");
|
||||
ok(cutButton, "Cut button exists in Panel Menu");
|
||||
ok(cutButton.getAttribute("disabled"), "Cut button is disabled");
|
||||
|
||||
// cut text from URL bar
|
||||
gURLBar.value = testText;
|
||||
gURLBar.focus();
|
||||
gURLBar.select();
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
ok(!cutButton.hasAttribute("disabled"), "Cut button gets enabled");
|
||||
cutButton.click();
|
||||
is(gURLBar.value, "", "Selected text is removed from source when clicking on cut");
|
||||
|
||||
// check that the text was added to the clipboard
|
||||
let clipboard = Services.clipboard;
|
||||
let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
||||
globalClipboard = clipboard.kGlobalClipboard;
|
||||
|
||||
transferable.init(null);
|
||||
transferable.addDataFlavor("text/unicode");
|
||||
clipboard.getData(transferable, globalClipboard);
|
||||
let str = {}, strLength = {};
|
||||
transferable.getTransferData("text/unicode", str, strLength);
|
||||
let clipboardValue = "";
|
||||
|
||||
if (str.value) {
|
||||
str.value.QueryInterface(Ci.nsISupportsString);
|
||||
clipboardValue = str.value.data;
|
||||
}
|
||||
is(clipboardValue, testText, "Data was copied to the clipboard.");
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
// clear the clipboard
|
||||
Services.clipboard.emptyClipboard(globalClipboard);
|
||||
info("Clipboard was cleared");
|
||||
|
||||
// restore the tab as it was at the begining of the test
|
||||
gBrowser.addTab(initialLocation);
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
info("Tabs were restored");
|
||||
});
|
|
@ -0,0 +1,22 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(function() {
|
||||
info("Check find button existence and functionality");
|
||||
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let findButton = document.getElementById("find-button");
|
||||
ok(findButton, "Find button exists in Panel Menu");
|
||||
|
||||
findButton.click();
|
||||
ok(!gFindBar.hasAttribute("hidden"), "Findbar opened successfully");
|
||||
|
||||
// close find bar
|
||||
gFindBar.close();
|
||||
info("Findbar was closed");
|
||||
});
|
|
@ -0,0 +1,24 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(function() {
|
||||
info("Check history button existence and functionality");
|
||||
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let historyButton = document.getElementById("history-panelmenu");
|
||||
ok(historyButton, "History button appears in Panel Menu");
|
||||
|
||||
historyButton.click();
|
||||
let historyPanel = document.getElementById("PanelUI-history");
|
||||
ok(historyPanel.getAttribute("current"), "History Panel is in view");
|
||||
|
||||
let panelHiddenPromise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
yield panelHiddenPromise
|
||||
info("Menu panel was closed");
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(function() {
|
||||
info("Check private browsing button existence and functionality");
|
||||
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let windowWasHandled = false;
|
||||
let privateWindow = null;
|
||||
|
||||
let observerWindowOpened = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "domwindowopened") {
|
||||
privateWindow = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
|
||||
privateWindow.addEventListener("load", function newWindowHandler() {
|
||||
privateWindow.removeEventListener("load", newWindowHandler, false);
|
||||
is(privateWindow.location.href, "chrome://browser/content/browser.xul",
|
||||
"A new browser window was opened");
|
||||
ok(PrivateBrowsingUtils.isWindowPrivate(privateWindow), "Window is private");
|
||||
windowWasHandled = true;
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Services.ww.registerNotification(observerWindowOpened);
|
||||
|
||||
let privateBrowsingButton = document.getElementById("privatebrowsing-button");
|
||||
ok(privateBrowsingButton, "Private browsing button exists in Panel Menu");
|
||||
privateBrowsingButton.click();
|
||||
|
||||
try {
|
||||
yield waitForCondition(() => windowWasHandled);
|
||||
yield promiseWindowClosed(privateWindow);
|
||||
info("The new private window was closed");
|
||||
}
|
||||
catch(e) {
|
||||
ok(false, "The new private browser window was not properly handled");
|
||||
}
|
||||
finally {
|
||||
Services.ww.unregisterNotification(observerWindowOpened);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(function() {
|
||||
info("Check new window button existence and functionality");
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let windowWasHandled = false;
|
||||
let newWindow = null;
|
||||
|
||||
let observerWindowOpened = {
|
||||
observe: function(aSubject, aTopic, aData) {
|
||||
if (aTopic == "domwindowopened") {
|
||||
newWindow = aSubject.QueryInterface(Components.interfaces.nsIDOMWindow);
|
||||
newWindow.addEventListener("load", function newWindowHandler() {
|
||||
newWindow.removeEventListener("load", newWindowHandler, false);
|
||||
is(newWindow.location.href, "chrome://browser/content/browser.xul",
|
||||
"A new browser window was opened");
|
||||
ok(!PrivateBrowsingUtils.isWindowPrivate(newWindow), "Window is not private");
|
||||
windowWasHandled = true;
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Services.ww.registerNotification(observerWindowOpened);
|
||||
|
||||
let newWindowButton = document.getElementById("new-window-button");
|
||||
ok(newWindowButton, "New Window button exists in Panel Menu");
|
||||
newWindowButton.click();
|
||||
|
||||
try {
|
||||
yield waitForCondition(() => windowWasHandled);
|
||||
yield promiseWindowClosed(newWindow);
|
||||
info("The new window was closed");
|
||||
}
|
||||
catch(e) {
|
||||
ok(false, "The new browser window was not properly handled");
|
||||
}
|
||||
finally {
|
||||
Services.ww.unregisterNotification(observerWindowOpened);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,47 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let initialLocation = gBrowser.currentURI.spec;
|
||||
let globalClipboard;
|
||||
|
||||
add_task(function() {
|
||||
info("Check paste button existence and functionality");
|
||||
|
||||
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||
globalClipboard = Services.clipboard.kGlobalClipboard;
|
||||
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let pasteButton = document.getElementById("paste-button");
|
||||
ok(pasteButton, "Paste button exists in Panel Menu");
|
||||
|
||||
// add text to clipboard
|
||||
let text = "Sample text for testing";
|
||||
clipboard.copyString(text);
|
||||
|
||||
// test paste button by pasting text to URL bar
|
||||
gURLBar.focus();
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
ok(!pasteButton.hasAttribute("disabled"), "Paste button is enabled");
|
||||
pasteButton.click();
|
||||
|
||||
is(gURLBar.value, text, "Text pasted successfully");
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
// clear the clipboard
|
||||
Services.clipboard.emptyClipboard(globalClipboard);
|
||||
info("Clipboard was cleared");
|
||||
|
||||
// restore the tab as it was at the begining of the test
|
||||
gBrowser.addTab(initialLocation);
|
||||
gBrowser.removeTab(gBrowser.selectedTab);
|
||||
info("Tabs were restored");
|
||||
});
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
const isOSX = (Services.appinfo.OS === "Darwin");
|
||||
|
||||
add_task(function() {
|
||||
info("Check print button existence and functionality");
|
||||
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
yield waitForCondition(() => document.getElementById("print-button") != null);
|
||||
|
||||
let printButton = document.getElementById("print-button");
|
||||
ok(printButton, "Print button exists in Panel Menu");
|
||||
|
||||
if (isOSX) {
|
||||
let panelHiddenPromise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
yield panelHiddenPromise;
|
||||
info("Menu panel was closed");
|
||||
}
|
||||
else {
|
||||
printButton.click();
|
||||
yield waitForCondition(() => gInPrintPreviewMode);
|
||||
|
||||
ok(gInPrintPreviewMode, "Entered print preview mode");
|
||||
}
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
// close print preview
|
||||
if (gInPrintPreviewMode) {
|
||||
PrintUtils.exitPrintPreview();
|
||||
yield waitForCondition(() => !window.gInPrintPreviewMode);
|
||||
info("Exited print preview")
|
||||
}
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(function() {
|
||||
info("Check save page button existence");
|
||||
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let savePageButton = document.getElementById("save-page-button");
|
||||
ok(savePageButton, "Save Page button exists in Panel Menu");
|
||||
|
||||
let panelHiddenPromise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
yield panelHiddenPromise;
|
||||
info("Menu panel was closed");
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let initialPageZoom = ZoomManager.zoom;
|
||||
|
||||
add_task(function() {
|
||||
info("Check zoom in button existence and functionality");
|
||||
|
||||
is(initialPageZoom, 1, "Initial zoom factor should be 1");
|
||||
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let zoomInButton = document.getElementById("zoom-in-button");
|
||||
ok(zoomInButton, "Zoom in button exists in Panel Menu");
|
||||
|
||||
zoomInButton.click();
|
||||
let pageZoomLevel = parseInt(ZoomManager.zoom * 100);
|
||||
let zoomResetButton = document.getElementById("zoom-reset-button");
|
||||
let expectedZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
|
||||
ok(pageZoomLevel > 100 && pageZoomLevel == expectedZoomLevel, "Page zoomed in correctly");
|
||||
|
||||
// close the Panel
|
||||
let panelHiddenPromise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
yield panelHiddenPromise;
|
||||
info("Menu panel was closed");
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
// reset zoom level
|
||||
ZoomManager.zoom = initialPageZoom;
|
||||
info("Zoom level was restored");
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let initialPageZoom = ZoomManager.zoom;
|
||||
|
||||
add_task(function() {
|
||||
info("Check zoom out button existence and functionality");
|
||||
|
||||
is(initialPageZoom, 1, "Initial zoom factor should be 1");
|
||||
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let zoomOutButton = document.getElementById("zoom-out-button");
|
||||
ok(zoomOutButton, "Zoom out button exists in Panel Menu");
|
||||
|
||||
zoomOutButton.click();
|
||||
let pageZoomLevel = Math.round(ZoomManager.zoom * 100);
|
||||
|
||||
let zoomResetButton = document.getElementById("zoom-reset-button");
|
||||
let expectedZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
|
||||
ok(pageZoomLevel < 100 && pageZoomLevel == expectedZoomLevel, "Page zoomed out correctly");
|
||||
|
||||
// close the panel
|
||||
let panelHiddenPromise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
yield panelHiddenPromise;
|
||||
info("Menu panel was closed");
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
// reset zoom level
|
||||
ZoomManager.zoom = initialPageZoom;
|
||||
info("Zoom level was restored");
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
let initialPageZoom = ZoomManager.zoom;
|
||||
|
||||
add_task(function() {
|
||||
info("Check zoom reset button existence and functionality");
|
||||
|
||||
is(initialPageZoom, 1, "Page zoom reset correctly");
|
||||
ZoomManager.zoom = 0.5;
|
||||
yield PanelUI.show();
|
||||
info("Menu panel was opened");
|
||||
|
||||
let zoomResetButton = document.getElementById("zoom-reset-button");
|
||||
ok(zoomResetButton, "Zoom reset button exists in Panel Menu");
|
||||
|
||||
zoomResetButton.click();
|
||||
let pageZoomLevel = Math.floor(ZoomManager.zoom * 100);
|
||||
let expectedZoomLevel = 100;
|
||||
let buttonZoomLevel = parseInt(zoomResetButton.getAttribute("label"), 10);
|
||||
is(pageZoomLevel, expectedZoomLevel, "Page zoom reset correctly");
|
||||
is(pageZoomLevel, buttonZoomLevel, "Button displays the correct zoom level");
|
||||
|
||||
// close the panel
|
||||
let panelHiddenPromise = promisePanelHidden(window);
|
||||
PanelUI.hide();
|
||||
yield panelHiddenPromise;
|
||||
info("Menu panel was closed");
|
||||
});
|
||||
|
||||
add_task(function asyncCleanup() {
|
||||
// reset zoom level
|
||||
ZoomManager.zoom = initialPageZoom;
|
||||
info("Zoom level was restored");
|
||||
});
|
|
@ -17,15 +17,123 @@ loop.contacts = (function(_, mozL10n) {
|
|||
// Number of contacts to add to the list at the same time.
|
||||
const CONTACTS_CHUNK_SIZE = 100;
|
||||
|
||||
const ContactDetail = React.createClass({displayName: 'ContactDetail',
|
||||
const ContactDropdown = React.createClass({displayName: 'ContactDropdown',
|
||||
propTypes: {
|
||||
handleContactClick: React.PropTypes.func,
|
||||
handleAction: React.PropTypes.func.isRequired,
|
||||
canEdit: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
return {
|
||||
openDirUp: false,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function () {
|
||||
// This method is called once when the dropdown menu is added to the DOM
|
||||
// inside the contact item. If the menu extends outside of the visible
|
||||
// area of the scrollable list, it is re-rendered in different direction.
|
||||
|
||||
let menuNode = this.getDOMNode();
|
||||
let menuNodeRect = menuNode.getBoundingClientRect();
|
||||
|
||||
let listNode = document.getElementsByClassName("contact-list")[0];
|
||||
let listNodeRect = listNode.getBoundingClientRect();
|
||||
|
||||
if (menuNodeRect.top + menuNodeRect.height >=
|
||||
listNodeRect.top + listNodeRect.height) {
|
||||
this.setState({
|
||||
openDirUp: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onItemClick: function(event) {
|
||||
this.props.handleAction(event.currentTarget.dataset.action);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
|
||||
let blockAction = this.props.blocked ? "unblock" : "block";
|
||||
let blockLabel = this.props.blocked ? "unblock_contact_menu_button"
|
||||
: "block_contact_menu_button";
|
||||
|
||||
return (
|
||||
React.DOM.ul({className: cx({ "dropdown-menu": true,
|
||||
"dropdown-menu-up": this.state.openDirUp })},
|
||||
React.DOM.li({className: cx({ "dropdown-menu-item": true,
|
||||
"disabled": true }),
|
||||
onClick: this.onItemClick, 'data-action': "video-call"},
|
||||
React.DOM.i({className: "icon icon-video-call"}),
|
||||
mozL10n.get("video_call_menu_button")
|
||||
),
|
||||
React.DOM.li({className: cx({ "dropdown-menu-item": true,
|
||||
"disabled": true }),
|
||||
onClick: this.onItemClick, 'data-action': "audio-call"},
|
||||
React.DOM.i({className: "icon icon-audio-call"}),
|
||||
mozL10n.get("audio_call_menu_button")
|
||||
),
|
||||
React.DOM.li({className: cx({ "dropdown-menu-item": true,
|
||||
"disabled": !this.props.canEdit }),
|
||||
onClick: this.onItemClick, 'data-action': "edit"},
|
||||
React.DOM.i({className: "icon icon-edit"}),
|
||||
mozL10n.get("edit_contact_menu_button")
|
||||
),
|
||||
React.DOM.li({className: "dropdown-menu-item",
|
||||
onClick: this.onItemClick, 'data-action': blockAction},
|
||||
React.DOM.i({className: "icon icon-" + blockAction}),
|
||||
mozL10n.get(blockLabel)
|
||||
),
|
||||
React.DOM.li({className: cx({ "dropdown-menu-item": true,
|
||||
"disabled": !this.props.canEdit }),
|
||||
onClick: this.onItemClick, 'data-action': "remove"},
|
||||
React.DOM.i({className: "icon icon-remove"}),
|
||||
mozL10n.get("remove_contact_menu_button")
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const ContactDetail = React.createClass({displayName: 'ContactDetail',
|
||||
getInitialState: function() {
|
||||
return {
|
||||
showMenu: false,
|
||||
};
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
handleContactAction: React.PropTypes.func,
|
||||
contact: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
handleContactClick: function() {
|
||||
if (this.props.handleContactClick) {
|
||||
this.props.handleContactClick(this.props.key);
|
||||
_onBodyClick: function() {
|
||||
// Hide the menu after other click handlers have been invoked.
|
||||
setTimeout(this.hideDropdownMenu, 10);
|
||||
},
|
||||
|
||||
showDropdownMenu: function() {
|
||||
document.body.addEventListener("click", this._onBodyClick);
|
||||
this.setState({showMenu: true});
|
||||
},
|
||||
|
||||
hideDropdownMenu: function() {
|
||||
document.body.removeEventListener("click", this._onBodyClick);
|
||||
// Since this call may be deferred, we need to guard it, for example in
|
||||
// case the contact was removed in the meantime.
|
||||
if (this.isMounted()) {
|
||||
this.setState({showMenu: false});
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
document.body.removeEventListener("click", this._onBodyClick);
|
||||
},
|
||||
|
||||
handleAction: function(actionName) {
|
||||
if (this.props.handleContactAction) {
|
||||
this.props.handleContactAction(this.props.contact, actionName);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -56,6 +164,12 @@ loop.contacts = (function(_, mozL10n) {
|
|||
return email;
|
||||
},
|
||||
|
||||
canEdit: function() {
|
||||
// We cannot modify imported contacts. For the moment, the check for
|
||||
// determining whether the contact is imported is based on its category.
|
||||
return this.props.contact.category[0] != "google";
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let names = this.getContactNames();
|
||||
let email = this.getPreferredEmail();
|
||||
|
@ -66,7 +180,7 @@ loop.contacts = (function(_, mozL10n) {
|
|||
});
|
||||
|
||||
return (
|
||||
React.DOM.li({onClick: this.handleContactClick, className: contactCSSClass},
|
||||
React.DOM.li({className: contactCSSClass, onMouseLeave: this.hideDropdownMenu},
|
||||
React.DOM.div({className: "avatar"},
|
||||
React.DOM.img({src: navigator.mozLoop.getUserAvatar(email.value)})
|
||||
),
|
||||
|
@ -78,9 +192,17 @@ loop.contacts = (function(_, mozL10n) {
|
|||
React.DOM.div({className: "email"}, email.value)
|
||||
),
|
||||
React.DOM.div({className: "icons"},
|
||||
React.DOM.i({className: "icon icon-video"}),
|
||||
React.DOM.i({className: "icon icon-caret-down"})
|
||||
)
|
||||
React.DOM.i({className: "icon icon-video",
|
||||
onClick: this.handleAction.bind(null, "video-call")}),
|
||||
React.DOM.i({className: "icon icon-caret-down",
|
||||
onClick: this.showDropdownMenu})
|
||||
),
|
||||
this.state.showMenu
|
||||
? ContactDropdown({handleAction: this.handleAction,
|
||||
canEdit: this.canEdit(),
|
||||
blocked: this.props.contact.blocked})
|
||||
: null
|
||||
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -158,6 +280,27 @@ loop.contacts = (function(_, mozL10n) {
|
|||
this.props.startForm("contacts_add");
|
||||
},
|
||||
|
||||
handleContactAction: function(contact, actionName) {
|
||||
switch (actionName) {
|
||||
case "edit":
|
||||
this.props.startForm("contacts_edit", contact);
|
||||
break;
|
||||
case "remove":
|
||||
case "block":
|
||||
case "unblock":
|
||||
// Invoke the API named like the action.
|
||||
navigator.mozLoop.contacts[actionName](contact._guid, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.error("Unrecognized action: " + actionName);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
sortContacts: function(contact1, contact2) {
|
||||
let comp = contact1.name[0].localeCompare(contact2.name[0]);
|
||||
if (comp !== 0) {
|
||||
|
@ -170,17 +313,17 @@ loop.contacts = (function(_, mozL10n) {
|
|||
|
||||
render: function() {
|
||||
let viewForItem = item => {
|
||||
return ContactDetail({key: item._guid, contact: item})
|
||||
return ContactDetail({key: item._guid, contact: item,
|
||||
handleContactAction: this.handleContactAction})
|
||||
};
|
||||
|
||||
let shownContacts = _.groupBy(this.state.contacts, function(contact) {
|
||||
return contact.blocked ? "blocked" : "available";
|
||||
});
|
||||
|
||||
// Buttons are temporarily hidden using "style".
|
||||
return (
|
||||
React.DOM.div(null,
|
||||
React.DOM.div({className: "content-area", style: {display: "none"}},
|
||||
React.DOM.div({className: "content-area"},
|
||||
ButtonGroup(null,
|
||||
Button({caption: mozL10n.get("import_contacts_button"),
|
||||
disabled: true,
|
||||
|
@ -223,7 +366,11 @@ loop.contacts = (function(_, mozL10n) {
|
|||
|
||||
initForm: function(contact) {
|
||||
let state = this.getInitialState();
|
||||
state.contact = contact || null;
|
||||
if (contact) {
|
||||
state.contact = contact;
|
||||
state.name = contact.name[0];
|
||||
state.email = contact.email[0].value;
|
||||
}
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
|
@ -244,6 +391,13 @@ loop.contacts = (function(_, mozL10n) {
|
|||
|
||||
switch (this.props.mode) {
|
||||
case "edit":
|
||||
this.state.contact.name[0] = this.state.name.trim();
|
||||
this.state.contact.email[0].value = this.state.email.trim();
|
||||
contactsAPI.update(this.state.contact, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
contact: null,
|
||||
});
|
||||
|
@ -275,7 +429,9 @@ loop.contacts = (function(_, mozL10n) {
|
|||
let cx = React.addons.classSet;
|
||||
return (
|
||||
React.DOM.div({className: "content-area contact-form"},
|
||||
React.DOM.header(null, mozL10n.get("add_contact_button")),
|
||||
React.DOM.header(null, this.props.mode == "add"
|
||||
? mozL10n.get("add_contact_button")
|
||||
: mozL10n.get("edit_contact_title")),
|
||||
React.DOM.label(null, mozL10n.get("edit_contact_name_label")),
|
||||
React.DOM.input({ref: "name", required: true, pattern: "\\s*\\S.*",
|
||||
className: cx({pristine: this.state.pristine}),
|
||||
|
@ -289,7 +445,9 @@ loop.contacts = (function(_, mozL10n) {
|
|||
caption: mozL10n.get("cancel_button"),
|
||||
onClick: this.handleCancelButtonClick}),
|
||||
Button({additionalClass: "button-accept",
|
||||
caption: mozL10n.get("add_contact_button"),
|
||||
caption: this.props.mode == "add"
|
||||
? mozL10n.get("add_contact_button")
|
||||
: mozL10n.get("edit_contact_done_button"),
|
||||
onClick: this.handleAcceptButtonClick})
|
||||
)
|
||||
)
|
||||
|
|
|
@ -17,15 +17,123 @@ loop.contacts = (function(_, mozL10n) {
|
|||
// Number of contacts to add to the list at the same time.
|
||||
const CONTACTS_CHUNK_SIZE = 100;
|
||||
|
||||
const ContactDetail = React.createClass({
|
||||
const ContactDropdown = React.createClass({
|
||||
propTypes: {
|
||||
handleContactClick: React.PropTypes.func,
|
||||
handleAction: React.PropTypes.func.isRequired,
|
||||
canEdit: React.PropTypes.bool
|
||||
},
|
||||
|
||||
getInitialState: function () {
|
||||
return {
|
||||
openDirUp: false,
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function () {
|
||||
// This method is called once when the dropdown menu is added to the DOM
|
||||
// inside the contact item. If the menu extends outside of the visible
|
||||
// area of the scrollable list, it is re-rendered in different direction.
|
||||
|
||||
let menuNode = this.getDOMNode();
|
||||
let menuNodeRect = menuNode.getBoundingClientRect();
|
||||
|
||||
let listNode = document.getElementsByClassName("contact-list")[0];
|
||||
let listNodeRect = listNode.getBoundingClientRect();
|
||||
|
||||
if (menuNodeRect.top + menuNodeRect.height >=
|
||||
listNodeRect.top + listNodeRect.height) {
|
||||
this.setState({
|
||||
openDirUp: true,
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
onItemClick: function(event) {
|
||||
this.props.handleAction(event.currentTarget.dataset.action);
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var cx = React.addons.classSet;
|
||||
|
||||
let blockAction = this.props.blocked ? "unblock" : "block";
|
||||
let blockLabel = this.props.blocked ? "unblock_contact_menu_button"
|
||||
: "block_contact_menu_button";
|
||||
|
||||
return (
|
||||
<ul className={cx({ "dropdown-menu": true,
|
||||
"dropdown-menu-up": this.state.openDirUp })}>
|
||||
<li className={cx({ "dropdown-menu-item": true,
|
||||
"disabled": true })}
|
||||
onClick={this.onItemClick} data-action="video-call">
|
||||
<i className="icon icon-video-call" />
|
||||
{mozL10n.get("video_call_menu_button")}
|
||||
</li>
|
||||
<li className={cx({ "dropdown-menu-item": true,
|
||||
"disabled": true })}
|
||||
onClick={this.onItemClick} data-action="audio-call">
|
||||
<i className="icon icon-audio-call" />
|
||||
{mozL10n.get("audio_call_menu_button")}
|
||||
</li>
|
||||
<li className={cx({ "dropdown-menu-item": true,
|
||||
"disabled": !this.props.canEdit })}
|
||||
onClick={this.onItemClick} data-action="edit">
|
||||
<i className="icon icon-edit" />
|
||||
{mozL10n.get("edit_contact_menu_button")}
|
||||
</li>
|
||||
<li className="dropdown-menu-item"
|
||||
onClick={this.onItemClick} data-action={blockAction}>
|
||||
<i className={"icon icon-" + blockAction} />
|
||||
{mozL10n.get(blockLabel)}
|
||||
</li>
|
||||
<li className={cx({ "dropdown-menu-item": true,
|
||||
"disabled": !this.props.canEdit })}
|
||||
onClick={this.onItemClick} data-action="remove">
|
||||
<i className="icon icon-remove" />
|
||||
{mozL10n.get("remove_contact_menu_button")}
|
||||
</li>
|
||||
</ul>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
const ContactDetail = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
showMenu: false,
|
||||
};
|
||||
},
|
||||
|
||||
propTypes: {
|
||||
handleContactAction: React.PropTypes.func,
|
||||
contact: React.PropTypes.object.isRequired
|
||||
},
|
||||
|
||||
handleContactClick: function() {
|
||||
if (this.props.handleContactClick) {
|
||||
this.props.handleContactClick(this.props.key);
|
||||
_onBodyClick: function() {
|
||||
// Hide the menu after other click handlers have been invoked.
|
||||
setTimeout(this.hideDropdownMenu, 10);
|
||||
},
|
||||
|
||||
showDropdownMenu: function() {
|
||||
document.body.addEventListener("click", this._onBodyClick);
|
||||
this.setState({showMenu: true});
|
||||
},
|
||||
|
||||
hideDropdownMenu: function() {
|
||||
document.body.removeEventListener("click", this._onBodyClick);
|
||||
// Since this call may be deferred, we need to guard it, for example in
|
||||
// case the contact was removed in the meantime.
|
||||
if (this.isMounted()) {
|
||||
this.setState({showMenu: false});
|
||||
}
|
||||
},
|
||||
|
||||
componentWillUnmount: function() {
|
||||
document.body.removeEventListener("click", this._onBodyClick);
|
||||
},
|
||||
|
||||
handleAction: function(actionName) {
|
||||
if (this.props.handleContactAction) {
|
||||
this.props.handleContactAction(this.props.contact, actionName);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -56,6 +164,12 @@ loop.contacts = (function(_, mozL10n) {
|
|||
return email;
|
||||
},
|
||||
|
||||
canEdit: function() {
|
||||
// We cannot modify imported contacts. For the moment, the check for
|
||||
// determining whether the contact is imported is based on its category.
|
||||
return this.props.contact.category[0] != "google";
|
||||
},
|
||||
|
||||
render: function() {
|
||||
let names = this.getContactNames();
|
||||
let email = this.getPreferredEmail();
|
||||
|
@ -66,7 +180,7 @@ loop.contacts = (function(_, mozL10n) {
|
|||
});
|
||||
|
||||
return (
|
||||
<li onClick={this.handleContactClick} className={contactCSSClass}>
|
||||
<li className={contactCSSClass} onMouseLeave={this.hideDropdownMenu}>
|
||||
<div className="avatar">
|
||||
<img src={navigator.mozLoop.getUserAvatar(email.value)} />
|
||||
</div>
|
||||
|
@ -78,9 +192,17 @@ loop.contacts = (function(_, mozL10n) {
|
|||
<div className="email">{email.value}</div>
|
||||
</div>
|
||||
<div className="icons">
|
||||
<i className="icon icon-video" />
|
||||
<i className="icon icon-caret-down" />
|
||||
<i className="icon icon-video"
|
||||
onClick={this.handleAction.bind(null, "video-call")} />
|
||||
<i className="icon icon-caret-down"
|
||||
onClick={this.showDropdownMenu} />
|
||||
</div>
|
||||
{this.state.showMenu
|
||||
? <ContactDropdown handleAction={this.handleAction}
|
||||
canEdit={this.canEdit()}
|
||||
blocked={this.props.contact.blocked} />
|
||||
: null
|
||||
}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
@ -158,6 +280,27 @@ loop.contacts = (function(_, mozL10n) {
|
|||
this.props.startForm("contacts_add");
|
||||
},
|
||||
|
||||
handleContactAction: function(contact, actionName) {
|
||||
switch (actionName) {
|
||||
case "edit":
|
||||
this.props.startForm("contacts_edit", contact);
|
||||
break;
|
||||
case "remove":
|
||||
case "block":
|
||||
case "unblock":
|
||||
// Invoke the API named like the action.
|
||||
navigator.mozLoop.contacts[actionName](contact._guid, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
break;
|
||||
default:
|
||||
console.error("Unrecognized action: " + actionName);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
sortContacts: function(contact1, contact2) {
|
||||
let comp = contact1.name[0].localeCompare(contact2.name[0]);
|
||||
if (comp !== 0) {
|
||||
|
@ -170,17 +313,17 @@ loop.contacts = (function(_, mozL10n) {
|
|||
|
||||
render: function() {
|
||||
let viewForItem = item => {
|
||||
return <ContactDetail key={item._guid} contact={item} />
|
||||
return <ContactDetail key={item._guid} contact={item}
|
||||
handleContactAction={this.handleContactAction} />
|
||||
};
|
||||
|
||||
let shownContacts = _.groupBy(this.state.contacts, function(contact) {
|
||||
return contact.blocked ? "blocked" : "available";
|
||||
});
|
||||
|
||||
// Buttons are temporarily hidden using "style".
|
||||
return (
|
||||
<div>
|
||||
<div className="content-area" style={{display: "none"}}>
|
||||
<div className="content-area">
|
||||
<ButtonGroup>
|
||||
<Button caption={mozL10n.get("import_contacts_button")}
|
||||
disabled
|
||||
|
@ -223,7 +366,11 @@ loop.contacts = (function(_, mozL10n) {
|
|||
|
||||
initForm: function(contact) {
|
||||
let state = this.getInitialState();
|
||||
state.contact = contact || null;
|
||||
if (contact) {
|
||||
state.contact = contact;
|
||||
state.name = contact.name[0];
|
||||
state.email = contact.email[0].value;
|
||||
}
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
|
@ -244,6 +391,13 @@ loop.contacts = (function(_, mozL10n) {
|
|||
|
||||
switch (this.props.mode) {
|
||||
case "edit":
|
||||
this.state.contact.name[0] = this.state.name.trim();
|
||||
this.state.contact.email[0].value = this.state.email.trim();
|
||||
contactsAPI.update(this.state.contact, err => {
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
contact: null,
|
||||
});
|
||||
|
@ -275,7 +429,9 @@ loop.contacts = (function(_, mozL10n) {
|
|||
let cx = React.addons.classSet;
|
||||
return (
|
||||
<div className="content-area contact-form">
|
||||
<header>{mozL10n.get("add_contact_button")}</header>
|
||||
<header>{this.props.mode == "add"
|
||||
? mozL10n.get("add_contact_button")
|
||||
: mozL10n.get("edit_contact_title")}</header>
|
||||
<label>{mozL10n.get("edit_contact_name_label")}</label>
|
||||
<input ref="name" required pattern="\s*\S.*"
|
||||
className={cx({pristine: this.state.pristine})}
|
||||
|
@ -289,7 +445,9 @@ loop.contacts = (function(_, mozL10n) {
|
|||
caption={mozL10n.get("cancel_button")}
|
||||
onClick={this.handleCancelButtonClick} />
|
||||
<Button additionalClass="button-accept"
|
||||
caption={mozL10n.get("add_contact_button")}
|
||||
caption={this.props.mode == "add"
|
||||
? mozL10n.get("add_contact_button")
|
||||
: mozL10n.get("edit_contact_done_button")}
|
||||
onClick={this.handleAcceptButtonClick} />
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
border-top: 1px solid #ccc;
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
/* Show six contacts and scroll for the rest */
|
||||
max-height: 305px;
|
||||
/* We need enough space to show the context menu of the first contact. */
|
||||
min-height: 204px;
|
||||
/* Show six contacts and scroll for the rest. */
|
||||
max-height: 306px;
|
||||
}
|
||||
|
||||
.contact,
|
||||
|
@ -44,7 +46,7 @@
|
|||
|
||||
.contact:hover > .icons {
|
||||
display: block;
|
||||
z-index: 1000;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.contact > .avatar {
|
||||
|
@ -146,6 +148,52 @@
|
|||
height: 16px;
|
||||
}
|
||||
|
||||
.contact > .dropdown-menu {
|
||||
z-index: 2;
|
||||
top: 10px;
|
||||
bottom: auto;
|
||||
right: 3em;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.contact > .dropdown-menu-up {
|
||||
bottom: 10px;
|
||||
top: auto;
|
||||
}
|
||||
|
||||
.contact > .dropdown-menu > .dropdown-menu-item > .icon {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 10px;
|
||||
background-position: center left;
|
||||
background-size: 10px 10px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.contact > .dropdown-menu > .dropdown-menu-item > .icon-audio-call {
|
||||
background-image: url("../img/icons-16x16.svg#audio");
|
||||
}
|
||||
|
||||
.contact > .dropdown-menu > .dropdown-menu-item > .icon-video-call {
|
||||
background-image: url("../img/icons-16x16.svg#video");
|
||||
}
|
||||
|
||||
.contact > .dropdown-menu > .dropdown-menu-item > .icon-edit {
|
||||
background-image: url("../img/icons-16x16.svg#contacts");
|
||||
}
|
||||
|
||||
.contact > .dropdown-menu > .dropdown-menu-item > .icon-block {
|
||||
background-image: url("../img/icons-16x16.svg#block");
|
||||
}
|
||||
|
||||
.contact > .dropdown-menu > .dropdown-menu-item > .icon-unblock {
|
||||
background-image: url("../img/icons-16x16.svg#unblock");
|
||||
}
|
||||
|
||||
.contact > .dropdown-menu > .dropdown-menu-item > .icon-remove {
|
||||
background-image: url("../img/icons-16x16.svg#delete");
|
||||
}
|
||||
|
||||
.contact-form > .button-group {
|
||||
margin-top: 14px;
|
||||
}
|
||||
|
|
|
@ -38,12 +38,11 @@ PluginContent.prototype = {
|
|||
global.addEventListener("PluginOutdated", this, true);
|
||||
global.addEventListener("PluginInstantiated", this, true);
|
||||
global.addEventListener("PluginRemoved", this, true);
|
||||
global.addEventListener("pagehide", this, true);
|
||||
global.addEventListener("pageshow", this, true);
|
||||
global.addEventListener("unload", this);
|
||||
|
||||
global.addEventListener("pageshow", (event) => this.onPageShow(event), true);
|
||||
|
||||
global.addMessageListener("BrowserPlugins:ActivatePlugins", this);
|
||||
global.addMessageListener("BrowserPlugins:NotificationRemoved", this);
|
||||
global.addMessageListener("BrowserPlugins:NotificationShown", this);
|
||||
global.addMessageListener("BrowserPlugins:ContextMenuCommand", this);
|
||||
},
|
||||
|
@ -58,9 +57,6 @@ PluginContent.prototype = {
|
|||
case "BrowserPlugins:ActivatePlugins":
|
||||
this.activatePlugins(msg.data.pluginInfo, msg.data.newState);
|
||||
break;
|
||||
case "BrowserPlugins:NotificationRemoved":
|
||||
this.clearPluginDataCache();
|
||||
break;
|
||||
case "BrowserPlugins:NotificationShown":
|
||||
setTimeout(() => this.updateNotificationUI(), 0);
|
||||
break;
|
||||
|
@ -79,7 +75,7 @@ PluginContent.prototype = {
|
|||
|
||||
onPageShow: function (event) {
|
||||
// Ignore events that aren't from the main document.
|
||||
if (this.global.content && event.target != this.global.content.document) {
|
||||
if (!this.content || event.target != this.content.document) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -91,6 +87,15 @@ PluginContent.prototype = {
|
|||
}
|
||||
},
|
||||
|
||||
onPageHide: function (event) {
|
||||
// Ignore events that aren't from the main document.
|
||||
if (!this.content || event.target != this.content.document) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearPluginDataCache();
|
||||
},
|
||||
|
||||
getPluginUI: function (plugin, anonid) {
|
||||
return plugin.ownerDocument.
|
||||
getAnonymousElementByAttribute(plugin, "anonid", anonid);
|
||||
|
@ -284,6 +289,16 @@ PluginContent.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
if (eventType == "pagehide") {
|
||||
this.onPageHide(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventType == "pageshow") {
|
||||
this.onPageShow(event);
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventType == "PluginRemoved") {
|
||||
this.updateNotificationUI(event.target);
|
||||
return;
|
||||
|
@ -647,7 +662,7 @@ PluginContent.prototype = {
|
|||
// If plugin is null, that means the user has navigated back to a page with
|
||||
// plugins, and we need to collect all the plugins.
|
||||
if (plugin === null) {
|
||||
let contentWindow = this.global.content;
|
||||
let contentWindow = this.content;
|
||||
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
// cwu.plugins may contain non-plugin <object>s, filter them out
|
||||
|
@ -664,8 +679,9 @@ PluginContent.prototype = {
|
|||
|
||||
let pluginData = this.pluginData;
|
||||
|
||||
let principal = this.global.content.document.nodePrincipal;
|
||||
let principal = this.content.document.nodePrincipal;
|
||||
let principalHost = this._getHostFromPrincipal(principal);
|
||||
let location = this.content.document.location.href;
|
||||
|
||||
for (let p of plugins) {
|
||||
let pluginInfo = this._getPluginInfo(p);
|
||||
|
@ -695,6 +711,7 @@ PluginContent.prototype = {
|
|||
plugins: [... this.pluginData.values()],
|
||||
showNow: showNow,
|
||||
host: principalHost,
|
||||
location: location,
|
||||
}, null, principal);
|
||||
},
|
||||
|
||||
|
@ -710,16 +727,13 @@ PluginContent.prototype = {
|
|||
* to the current top-level document.
|
||||
*/
|
||||
updateNotificationUI: function (document) {
|
||||
let principal;
|
||||
document = document || this.content.document;
|
||||
|
||||
if (document) {
|
||||
// We're only interested in the top-level document, since that's
|
||||
// the one that provides the Principal that we send back to the
|
||||
// parent.
|
||||
principal = document.defaultView.top.document.nodePrincipal;
|
||||
} else {
|
||||
principal = this.content.document.nodePrincipal;
|
||||
}
|
||||
// We're only interested in the top-level document, since that's
|
||||
// the one that provides the Principal that we send back to the
|
||||
// parent.
|
||||
let principal = document.defaultView.top.document.nodePrincipal;
|
||||
let location = document.location.href;
|
||||
|
||||
// Make a copy of the actions from the last popup notification.
|
||||
let haveInsecure = false;
|
||||
|
@ -780,6 +794,7 @@ PluginContent.prototype = {
|
|||
haveInsecure: haveInsecure,
|
||||
actions: [... actions.values()],
|
||||
host: this._getHostFromPrincipal(principal),
|
||||
location: location,
|
||||
}, null, principal);
|
||||
},
|
||||
|
||||
|
|
|
@ -1468,14 +1468,16 @@ notification[value="translation"] menulist > .menulist-dropmarker {
|
|||
}
|
||||
|
||||
.ac-result-type-keyword,
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
|
||||
richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.ac-result-type-keyword[selected="true"],
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
|
||||
richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
|
||||
}
|
||||
|
||||
|
@ -1505,14 +1507,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
|||
padding: 0 3px;
|
||||
}
|
||||
|
||||
richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon");
|
||||
}
|
||||
|
||||
richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted");
|
||||
}
|
||||
|
||||
.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
|
||||
color: GrayText;
|
||||
}
|
||||
|
|
|
@ -161,6 +161,19 @@ toolbarseparator {
|
|||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Always draw a border on Yosemite to ensure the border is well-defined there
|
||||
* (the default border is too light). */
|
||||
@media (-moz-mac-yosemite-theme) {
|
||||
#main-window[tabsintitlebar] #TabsToolbar:not([collapsed="true"]) + #nav-bar:not(:-moz-lwtheme) {
|
||||
border-top: 1px solid hsla(0,0%,0%,.2);
|
||||
background-clip: padding-box;
|
||||
margin-top: -@tabToolbarNavbarOverlap@;
|
||||
/* Position the toolbar above the bottom of background tabs */
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#nav-bar-customization-target {
|
||||
padding: 4px;
|
||||
}
|
||||
|
@ -2212,14 +2225,16 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
|||
}
|
||||
|
||||
.ac-result-type-keyword,
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
|
||||
richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.ac-result-type-keyword[selected="true"],
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
|
||||
richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
|
||||
}
|
||||
|
||||
|
@ -2256,14 +2271,6 @@ richlistitem[type~="action"][actiontype="switchtab"][selected="true"] > .ac-url-
|
|||
-moz-image-region: rect(11px, 16px, 22px, 0);
|
||||
}
|
||||
|
||||
richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon");
|
||||
}
|
||||
|
||||
richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted");
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
.ac-result-type-bookmark {
|
||||
list-style-image: url("chrome://browser/skin/places/star-icons@2x.png");
|
||||
|
|
|
@ -1394,7 +1394,8 @@ richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-
|
|||
}
|
||||
|
||||
.ac-result-type-keyword,
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage) {
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage),
|
||||
richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon);
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
|
@ -1404,7 +1405,8 @@ richlistitem[selected="true"][current="true"] > .ac-title-box > .ac-result-type-
|
|||
@media not all and (-moz-windows-default-theme) {
|
||||
%endif
|
||||
.ac-result-type-keyword[selected="true"],
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected) {
|
||||
.autocomplete-treebody::-moz-tree-image(keyword, treecolAutoCompleteImage, selected),
|
||||
richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url(chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted);
|
||||
}
|
||||
%ifdef WINDOWS_AERO
|
||||
|
@ -1463,20 +1465,6 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
|||
}
|
||||
%endif
|
||||
|
||||
richlistitem[type~="action"][actiontype="searchengine"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon");
|
||||
}
|
||||
|
||||
%ifdef WINDOWS_AERO
|
||||
@media not all and (-moz-windows-default-theme) {
|
||||
%endif
|
||||
richlistitem[type~="action"][actiontype="searchengine"][selected="true"] > .ac-title-box > .ac-site-icon {
|
||||
list-style-image: url("chrome://global/skin/icons/autocomplete-search.svg#search-icon-inverted");
|
||||
}
|
||||
%ifdef WINDOWS_AERO
|
||||
}
|
||||
%endif
|
||||
|
||||
.autocomplete-treebody::-moz-tree-cell-text(treecolAutoCompleteComment) {
|
||||
color: GrayText;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "nsRadioInterfaceLayer.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/SettingChangeNotificationBinding.h"
|
||||
|
||||
#define MOZSETTINGS_CHANGED_ID "mozsettings-changed"
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "nsIObserverService.h"
|
||||
#include "nsISettingsService.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/SettingChangeNotificationBinding.h"
|
||||
|
||||
#ifdef MOZ_B2G_RIL
|
||||
|
|
|
@ -50,7 +50,6 @@ const NFCCONTENTHELPER_CID =
|
|||
const NFC_IPC_MSG_NAMES = [
|
||||
"NFC:ReadNDEFResponse",
|
||||
"NFC:WriteNDEFResponse",
|
||||
"NFC:GetDetailsNDEFResponse",
|
||||
"NFC:MakeReadOnlyNDEFResponse",
|
||||
"NFC:ConnectResponse",
|
||||
"NFC:CloseResponse",
|
||||
|
@ -64,17 +63,6 @@ XPCOMUtils.defineLazyServiceGetter(this, "cpmm",
|
|||
"@mozilla.org/childprocessmessagemanager;1",
|
||||
"nsISyncMessageSender");
|
||||
|
||||
function GetDetailsNDEFResponse(details) {
|
||||
this.canBeMadeReadOnly = details.canBeMadeReadOnly;
|
||||
this.isReadOnly = details.isReadOnly;
|
||||
this.maxSupportedLength = details.maxSupportedLength;
|
||||
}
|
||||
GetDetailsNDEFResponse.prototype = {
|
||||
__exposedProps__ : {canBeMadeReadOnly: 'r',
|
||||
isReadOnly: 'r',
|
||||
maxSupportedLength: 'r'}
|
||||
};
|
||||
|
||||
function NfcContentHelper() {
|
||||
this.initDOMRequestHelper(/* aWindow */ null, NFC_IPC_MSG_NAMES);
|
||||
Services.obs.addObserver(this, "xpcom-shutdown", false);
|
||||
|
@ -127,22 +115,6 @@ NfcContentHelper.prototype = {
|
|||
},
|
||||
|
||||
// NFCTag interface
|
||||
getDetailsNDEF: function getDetailsNDEF(window, sessionToken) {
|
||||
if (window == null) {
|
||||
throw Components.Exception("Can't get window object",
|
||||
Cr.NS_ERROR_UNEXPECTED);
|
||||
}
|
||||
let request = Services.DOMRequest.createRequest(window);
|
||||
let requestId = btoa(this.getRequestId(request));
|
||||
this._requestMap[requestId] = window;
|
||||
|
||||
cpmm.sendAsyncMessage("NFC:GetDetailsNDEF", {
|
||||
requestId: requestId,
|
||||
sessionToken: sessionToken
|
||||
});
|
||||
return request;
|
||||
},
|
||||
|
||||
readNDEF: function readNDEF(window, sessionToken) {
|
||||
if (window == null) {
|
||||
throw Components.Exception("Can't get window object",
|
||||
|
@ -395,9 +367,6 @@ NfcContentHelper.prototype = {
|
|||
case "NFC:ReadNDEFResponse":
|
||||
this.handleReadNDEFResponse(result);
|
||||
break;
|
||||
case "NFC:GetDetailsNDEFResponse":
|
||||
this.handleGetDetailsNDEFResponse(result);
|
||||
break;
|
||||
case "NFC:CheckP2PRegistrationResponse":
|
||||
this.handleCheckP2PRegistrationResponse(result);
|
||||
break;
|
||||
|
@ -452,16 +421,6 @@ NfcContentHelper.prototype = {
|
|||
this.fireRequestSuccess(requestId, ndefMsg);
|
||||
},
|
||||
|
||||
handleGetDetailsNDEFResponse: function handleGetDetailsNDEFResponse(result) {
|
||||
if (result.errorMsg) {
|
||||
this.fireRequestError(atob(result.requestId), result.errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
let requestId = atob(result.requestId);
|
||||
this.fireRequestSuccess(requestId, new GetDetailsNDEFResponse(result));
|
||||
},
|
||||
|
||||
handleCheckP2PRegistrationResponse: function handleCheckP2PRegistrationResponse(result) {
|
||||
// Privilaged status API. Always fire success to avoid using exposed props.
|
||||
// The receiver must check the boolean mapped status code to handle.
|
||||
|
|
|
@ -57,7 +57,6 @@ const NFC_IPC_MSG_NAMES = [
|
|||
|
||||
const NFC_IPC_READ_PERM_MSG_NAMES = [
|
||||
"NFC:ReadNDEF",
|
||||
"NFC:GetDetailsNDEF",
|
||||
"NFC:Connect",
|
||||
"NFC:Close",
|
||||
];
|
||||
|
@ -263,7 +262,7 @@ XPCOMUtils.defineLazyGetter(this, "gMessageManager", function () {
|
|||
if (message.name == "child-process-shutdown") {
|
||||
this.removePeerTarget(message.target);
|
||||
this.nfc.removeTarget(message.target);
|
||||
this.removeEventTarget(msg.target);
|
||||
this.removeEventTarget(message.target);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -538,7 +537,6 @@ Nfc.prototype = {
|
|||
break;
|
||||
case "ConnectResponse": // Fall through.
|
||||
case "CloseResponse":
|
||||
case "GetDetailsNDEFResponse":
|
||||
case "ReadNDEFResponse":
|
||||
case "MakeReadOnlyNDEFResponse":
|
||||
case "WriteNDEFResponse":
|
||||
|
@ -608,9 +606,6 @@ Nfc.prototype = {
|
|||
this.setConfig({powerLevel: NFC.NFC_POWER_LEVEL_DISABLED,
|
||||
requestId: message.data.requestId});
|
||||
break;
|
||||
case "NFC:GetDetailsNDEF":
|
||||
this.sendToNfcService("getDetailsNDEF", message.data);
|
||||
break;
|
||||
case "NFC:ReadNDEF":
|
||||
this.sendToNfcService("readNDEF", message.data);
|
||||
break;
|
||||
|
|
|
@ -8,13 +8,12 @@
|
|||
namespace mozilla {
|
||||
|
||||
#define NFCD_MAJOR_VERSION 1
|
||||
#define NFCD_MINOR_VERSION 9
|
||||
#define NFCD_MINOR_VERSION 10
|
||||
|
||||
enum NfcRequest {
|
||||
ConfigReq = 0,
|
||||
ConnectReq,
|
||||
CloseReq,
|
||||
GetDetailsNDEFReq,
|
||||
ReadNDEFReq,
|
||||
WriteNDEFReq,
|
||||
MakeReadOnlyNDEFReq,
|
||||
|
@ -23,7 +22,6 @@ enum NfcRequest {
|
|||
enum NfcResponse {
|
||||
GeneralRsp = 1000,
|
||||
ConfigRsp,
|
||||
GetDetailsNDEFRsp,
|
||||
ReadNDEFRsp,
|
||||
};
|
||||
|
||||
|
|
|
@ -17,7 +17,6 @@ using namespace mozilla;
|
|||
using namespace mozilla::dom;
|
||||
|
||||
static const char* kConfigRequest = "config";
|
||||
static const char* kGetDetailsNDEF = "getDetailsNDEF";
|
||||
static const char* kReadNDEFRequest = "readNDEF";
|
||||
static const char* kWriteNDEFRequest = "writeNDEF";
|
||||
static const char* kMakeReadOnlyNDEFRequest = "makeReadOnlyNDEF";
|
||||
|
@ -25,7 +24,6 @@ static const char* kConnectRequest = "connect";
|
|||
static const char* kCloseRequest = "close";
|
||||
|
||||
static const char* kConfigResponse = "ConfigResponse";
|
||||
static const char* kGetDetailsNDEFResponse = "GetDetailsNDEFResponse";
|
||||
static const char* kReadNDEFResponse = "ReadNDEFResponse";
|
||||
static const char* kWriteNDEFResponse = "WriteNDEFResponse";
|
||||
static const char* kMakeReadOnlyNDEFResponse = "MakeReadOnlyNDEFResponse";
|
||||
|
@ -46,8 +44,6 @@ NfcMessageHandler::Marshall(Parcel& aParcel, const CommandOptions& aOptions)
|
|||
|
||||
if (!strcmp(type, kConfigRequest)) {
|
||||
result = ConfigRequest(aParcel, aOptions);
|
||||
} else if (!strcmp(type, kGetDetailsNDEF)) {
|
||||
result = GetDetailsNDEFRequest(aParcel, aOptions);
|
||||
} else if (!strcmp(type, kReadNDEFRequest)) {
|
||||
result = ReadNDEFRequest(aParcel, aOptions);
|
||||
} else if (!strcmp(type, kWriteNDEFRequest)) {
|
||||
|
@ -83,9 +79,6 @@ NfcMessageHandler::Unmarshall(const Parcel& aParcel, EventOptions& aOptions)
|
|||
case NfcResponse::ConfigRsp:
|
||||
result = ConfigResponse(aParcel, aOptions);
|
||||
break;
|
||||
case NfcResponse::GetDetailsNDEFRsp:
|
||||
result = GetDetailsNDEFResponse(aParcel, aOptions);
|
||||
break;
|
||||
case NfcResponse::ReadNDEFRsp:
|
||||
result = ReadNDEFResponse(aParcel, aOptions);
|
||||
break;
|
||||
|
@ -171,35 +164,6 @@ NfcMessageHandler::ConfigResponse(const Parcel& aParcel, EventOptions& aOptions)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
NfcMessageHandler::GetDetailsNDEFRequest(Parcel& aParcel, const CommandOptions& aOptions)
|
||||
{
|
||||
aParcel.writeInt32(NfcRequest::GetDetailsNDEFReq);
|
||||
aParcel.writeInt32(aOptions.mSessionId);
|
||||
mRequestIdQueue.AppendElement(aOptions.mRequestId);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
NfcMessageHandler::GetDetailsNDEFResponse(const Parcel& aParcel, EventOptions& aOptions)
|
||||
{
|
||||
aOptions.mType = NS_ConvertUTF8toUTF16(kGetDetailsNDEFResponse);
|
||||
aOptions.mStatus = aParcel.readInt32();
|
||||
aOptions.mSessionId = aParcel.readInt32();
|
||||
|
||||
if (aOptions.mStatus == NfcErrorCode::Success) {
|
||||
int readOnly = aParcel.readInt32();
|
||||
aOptions.mIsReadOnly = readOnly & 0xff;
|
||||
aOptions.mCanBeMadeReadOnly = (readOnly >> 8) & 0xff;
|
||||
aOptions.mMaxSupportedLength = aParcel.readInt32();
|
||||
}
|
||||
|
||||
NS_ENSURE_TRUE(!mRequestIdQueue.IsEmpty(), false);
|
||||
aOptions.mRequestId = mRequestIdQueue[0];
|
||||
mRequestIdQueue.RemoveElementAt(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
NfcMessageHandler::ReadNDEFRequest(Parcel& aParcel, const CommandOptions& aOptions)
|
||||
{
|
||||
|
|
|
@ -27,8 +27,6 @@ private:
|
|||
bool GeneralResponse(const android::Parcel& aParcel, EventOptions& aOptions);
|
||||
bool ConfigRequest(android::Parcel& aParcel, const CommandOptions& options);
|
||||
bool ConfigResponse(const android::Parcel& aParcel, EventOptions& aOptions);
|
||||
bool GetDetailsNDEFRequest(android::Parcel& aParcel, const CommandOptions& options);
|
||||
bool GetDetailsNDEFResponse(const android::Parcel& aParcel, EventOptions& aOptions);
|
||||
bool ReadNDEFRequest(android::Parcel& aParcel, const CommandOptions& options);
|
||||
bool ReadNDEFResponse(const android::Parcel& aParcel, EventOptions& aOptions);
|
||||
bool WriteNDEFRequest(android::Parcel& aParcel, const CommandOptions& options);
|
||||
|
|
|
@ -27,7 +27,7 @@ interface nsINfcDOMEventTarget : nsISupports
|
|||
void notifyPeerLost(in DOMString sessionToken);
|
||||
};
|
||||
|
||||
[scriptable, uuid(7eaf4c31-e1d1-422e-aa55-181f4eb156b0)]
|
||||
[scriptable, uuid(d3f1bdc1-048f-44a8-abe2-bc386edce40b)]
|
||||
interface nsINfcContentHelper : nsISupports
|
||||
{
|
||||
const long NFC_EVENT_PEER_READY = 0x01;
|
||||
|
@ -35,7 +35,6 @@ interface nsINfcContentHelper : nsISupports
|
|||
|
||||
boolean checkSessionToken(in DOMString sessionToken);
|
||||
|
||||
nsIDOMDOMRequest getDetailsNDEF(in nsIDOMWindow window, in DOMString sessionToken);
|
||||
nsIDOMDOMRequest readNDEF(in nsIDOMWindow window, in DOMString sessionToken);
|
||||
nsIDOMDOMRequest writeNDEF(in nsIDOMWindow window, in nsIVariant records, in DOMString sessionToken);
|
||||
nsIDOMDOMRequest makeReadOnlyNDEF(in nsIDOMWindow window, in DOMString sessionToken);
|
||||
|
|
|
@ -14,6 +14,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
|||
|
||||
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.importGlobalProperties(["indexedDB"]);
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "appsService",
|
||||
"@mozilla.org/AppsService;1",
|
||||
|
|
|
@ -2650,6 +2650,7 @@ bool AsyncPanZoomController::IsCurrentlyCheckerboarding() const {
|
|||
|
||||
CSSPoint currentScrollOffset = mFrameMetrics.GetScrollOffset() + mTestAsyncScrollOffset;
|
||||
CSSRect painted = mLastContentPaintMetrics.mDisplayPort + mLastContentPaintMetrics.GetScrollOffset();
|
||||
painted.Inflate(CSSMargin::FromAppUnits(nsMargin(1, 1, 1, 1))); // fuzz for rounding error
|
||||
CSSRect visible = CSSRect(currentScrollOffset, mFrameMetrics.CalculateCompositedSizeInCssPixels());
|
||||
return !painted.Contains(visible);
|
||||
}
|
||||
|
|
|
@ -389,7 +389,8 @@ mozJSComponentLoader::ReallyInit()
|
|||
// XXXkhuey B2G child processes have some sort of preferences race that
|
||||
// results in getting the wrong value.
|
||||
// But we don't want that on Firefox Mulet as it break most Firefox JSMs...
|
||||
#if defined(MOZ_B2G) && !defined(MOZ_MULET)
|
||||
// Also disable on debug builds to break js components that rely on this.
|
||||
#if defined(MOZ_B2G) && !defined(MOZ_MULET) && !defined(DEBUG)
|
||||
mReuseLoaderGlobal = true;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -139,6 +139,13 @@ public class GeckoAppShell
|
|||
private static final Queue<GeckoEvent> PENDING_EVENTS = new ConcurrentLinkedQueue<GeckoEvent>();
|
||||
private static final Map<String, String> ALERT_COOKIES = new ConcurrentHashMap<String, String>();
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static final List<String> UNKNOWN_MIME_TYPES = new ArrayList<String>(3) {{
|
||||
add("application/octet-stream"); // This will be used as a default mime type for unknown files
|
||||
add("application/unknown");
|
||||
add("application/octet-stream"); // Github uses this for APK files
|
||||
}};
|
||||
|
||||
private static volatile boolean locationHighAccuracyEnabled;
|
||||
|
||||
// Accessed by NotificationHelper. This should be encapsulated.
|
||||
|
@ -1795,24 +1802,41 @@ public class GeckoAppShell
|
|||
|
||||
@WrapElementForJNI
|
||||
public static void scanMedia(final String aFile, String aMimeType) {
|
||||
String mimeType = aMimeType;
|
||||
if (UNKNOWN_MIME_TYPES.contains(mimeType)) {
|
||||
// If this is a generic undefined mimetype, erase it so that we can try to determine
|
||||
// one from the file extension below.
|
||||
mimeType = "";
|
||||
}
|
||||
|
||||
// If the platform didn't give us a mimetype, try to guess one from the filename
|
||||
if (TextUtils.isEmpty(aMimeType)) {
|
||||
if (TextUtils.isEmpty(mimeType)) {
|
||||
int extPosition = aFile.lastIndexOf(".");
|
||||
if (extPosition > 0 && extPosition < aFile.length() - 1) {
|
||||
aMimeType = getMimeTypeFromExtension(aFile.substring(extPosition+1));
|
||||
mimeType = getMimeTypeFromExtension(aFile.substring(extPosition+1));
|
||||
}
|
||||
}
|
||||
|
||||
// addCompletedDownload will throw if it received any null parameters. Use aMimeType or a default
|
||||
// if we still don't have one.
|
||||
if (TextUtils.isEmpty(mimeType)) {
|
||||
if (TextUtils.isEmpty(aMimeType)) {
|
||||
mimeType = UNKNOWN_MIME_TYPES.get(0);
|
||||
} else {
|
||||
mimeType = aMimeType;
|
||||
}
|
||||
}
|
||||
|
||||
final File f = new File(aFile);
|
||||
if (AppConstants.ANDROID_DOWNLOADS_INTEGRATION) {
|
||||
final File f = new File(aFile);
|
||||
final DownloadManager dm = (DownloadManager) getContext().getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
dm.addCompletedDownload(f.getName(),
|
||||
f.getName(),
|
||||
!TextUtils.isEmpty(aMimeType),
|
||||
true, // Media scanner should scan this
|
||||
aMimeType,
|
||||
f.getAbsolutePath(),
|
||||
f.length(),
|
||||
false);
|
||||
Math.max(0, f.length()),
|
||||
false); // Don't show a notification.
|
||||
} else {
|
||||
Context context = getContext();
|
||||
GeckoMediaScannerClient.startScan(context, aFile, aMimeType);
|
||||
|
|
|
@ -37,7 +37,7 @@ import android.util.Log;
|
|||
public class GeckoNetworkManager extends BroadcastReceiver implements NativeEventListener {
|
||||
private static final String LOGTAG = "GeckoNetworkManager";
|
||||
|
||||
static private GeckoNetworkManager sInstance = new GeckoNetworkManager();
|
||||
static private GeckoNetworkManager sInstance;
|
||||
|
||||
public static void destroy() {
|
||||
if (sInstance != null) {
|
||||
|
@ -90,6 +90,10 @@ public class GeckoNetworkManager extends BroadcastReceiver implements NativeEven
|
|||
private volatile Context mApplicationContext;
|
||||
|
||||
public static GeckoNetworkManager getInstance() {
|
||||
if (sInstance == null) {
|
||||
sInstance = new GeckoNetworkManager();
|
||||
}
|
||||
|
||||
return sInstance;
|
||||
}
|
||||
|
||||
|
|
|
@ -9,11 +9,18 @@ import java.util.Set;
|
|||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
|
||||
import java.lang.StringBuilder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.mozilla.gecko.mozglue.RobocopTarget;
|
||||
import org.mozilla.gecko.AppConstants.Versions;
|
||||
import org.mozilla.gecko.mozglue.generatorannotations.WrapElementForJNI;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
|
@ -22,7 +29,34 @@ import android.util.Log;
|
|||
public class RestrictedProfiles {
|
||||
private static final String LOGTAG = "GeckoRestrictedProfiles";
|
||||
|
||||
// These constants should be in sync with the ones from toolkit/components/parentalcontrols/nsIParentalControlServices.java
|
||||
private static Boolean inGuest = null;
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static final List<String> BANNED_SCHEMES = new ArrayList<String>() {{
|
||||
add("file");
|
||||
add("chrome");
|
||||
add("resource");
|
||||
add("jar");
|
||||
add("wyciwyg");
|
||||
}};
|
||||
|
||||
private static boolean getInGuest() {
|
||||
if (inGuest == null) {
|
||||
inGuest = GeckoAppShell.getGeckoInterface().getProfile().inGuestMode();
|
||||
}
|
||||
|
||||
return inGuest;
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
private static final List<String> BANNED_URLS = new ArrayList<String>() {{
|
||||
add("about:config");
|
||||
}};
|
||||
|
||||
/* This is a list of things we can restrict you from doing. Some of these are reflected in Android UserManager constants.
|
||||
* Others are specific to us.
|
||||
* These constants should be in sync with the ones from toolkit/components/parentalcontrols/nsIParentalControlServices.idl
|
||||
*/
|
||||
public static enum Restriction {
|
||||
DISALLOW_DOWNLOADS(1, "no_download_files"),
|
||||
DISALLOW_INSTALL_EXTENSIONS(2, "no_install_extensions"),
|
||||
|
@ -43,10 +77,10 @@ public class RestrictedProfiles {
|
|||
}
|
||||
}
|
||||
|
||||
private static String geckoActionToRestriction(int action) {
|
||||
private static Restriction geckoActionToRestriction(int action) {
|
||||
for (Restriction rest : Restriction.values()) {
|
||||
if (rest.id == action) {
|
||||
return rest.name;
|
||||
return rest;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -59,10 +93,42 @@ public class RestrictedProfiles {
|
|||
return mgr.getUserRestrictions();
|
||||
}
|
||||
|
||||
private static boolean canLoadUrl(final String url) {
|
||||
// Null urls are always allowed
|
||||
if (url == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
// If we're not in guest mode, and the system restriction isn't in place, everything is allowed.
|
||||
if (!getInGuest() &&
|
||||
!getRestrictions().getBoolean(Restriction.DISALLOW_BROWSE_FILES.name, false)) {
|
||||
return true;
|
||||
}
|
||||
} catch(IllegalArgumentException ex) {
|
||||
Log.i(LOGTAG, "Invalid action", ex);
|
||||
}
|
||||
|
||||
final Uri u = Uri.parse(url);
|
||||
final String scheme = u.getScheme();
|
||||
if (BANNED_SCHEMES.contains(scheme)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (String banned : BANNED_URLS) {
|
||||
if (url.startsWith(banned)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: The UserManager should support blacklisting urls by the device owner.
|
||||
return true;
|
||||
}
|
||||
|
||||
@WrapElementForJNI
|
||||
public static boolean isUserRestricted() {
|
||||
// Guest mode is supported in all Android versions
|
||||
if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
|
||||
if (getInGuest()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -79,8 +145,19 @@ public class RestrictedProfiles {
|
|||
|
||||
@WrapElementForJNI
|
||||
public static boolean isAllowed(int action, String url) {
|
||||
final Restriction restriction;
|
||||
try {
|
||||
restriction = geckoActionToRestriction(action);
|
||||
} catch(IllegalArgumentException ex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Restriction.DISALLOW_BROWSE_FILES == restriction) {
|
||||
return canLoadUrl(url);
|
||||
}
|
||||
|
||||
// ALl actions are blocked in Guest mode
|
||||
if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
|
||||
if (getInGuest()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -90,8 +167,7 @@ public class RestrictedProfiles {
|
|||
|
||||
try {
|
||||
// NOTE: Restrictions hold the opposite intention, so we need to flip it
|
||||
final String restriction = geckoActionToRestriction(action);
|
||||
return !getRestrictions().getBoolean(restriction, false);
|
||||
return !getRestrictions().getBoolean(restriction.name, false);
|
||||
} catch(IllegalArgumentException ex) {
|
||||
Log.i(LOGTAG, "Invalid action", ex);
|
||||
}
|
||||
|
@ -102,7 +178,7 @@ public class RestrictedProfiles {
|
|||
@WrapElementForJNI
|
||||
public static String getUserRestrictions() {
|
||||
// Guest mode is supported in all Android versions
|
||||
if (GeckoAppShell.getGeckoInterface().getProfile().inGuestMode()) {
|
||||
if (getInGuest()) {
|
||||
StringBuilder builder = new StringBuilder("{ ");
|
||||
|
||||
for (Restriction restriction : Restriction.values()) {
|
||||
|
|
|
@ -32,7 +32,7 @@ public class testAppMenuPathways extends UITest {
|
|||
message.put("baseDomain", "webmfiles.org");
|
||||
message.put("type", "Content:LocationChange");
|
||||
message.put("sameDocument", false);
|
||||
message.put("userSearch", "");
|
||||
message.put("userRequested", "");
|
||||
message.put("uri", getAbsoluteIpUrl("/big-buck-bunny_trailer.webm"));
|
||||
message.put("tabID", 0);
|
||||
} catch (Exception ex) {
|
||||
|
|
|
@ -4218,16 +4218,12 @@ Tab.prototype = {
|
|||
} catch (ex) { }
|
||||
|
||||
// In restricted profiles, we refuse to let you open any file urls.
|
||||
if (!ParentalControls.isAllowed(ParentalControls.VISIT_FILE_URLS)) {
|
||||
let bannedSchemes = ["file", "chrome", "resource", "jar", "wyciwyg"];
|
||||
if (!ParentalControls.isAllowed(ParentalControls.VISIT_FILE_URLS, fixedURI)) {
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
|
||||
if (bannedSchemes.indexOf(fixedURI.scheme) > -1) {
|
||||
aRequest.cancel(Cr.NS_BINDING_ABORTED);
|
||||
|
||||
aRequest = this.browser.docShell.displayLoadError(Cr.NS_ERROR_UNKNOWN_PROTOCOL, fixedURI, null);
|
||||
if (aRequest) {
|
||||
fixedURI = aRequest.URI;
|
||||
}
|
||||
aRequest = this.browser.docShell.displayLoadError(Cr.NS_ERROR_UNKNOWN_PROTOCOL, fixedURI, null);
|
||||
if (aRequest) {
|
||||
fixedURI = aRequest.URI;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -116,6 +116,7 @@ nsAutoCompleteController::SetInput(nsIAutoCompleteInput *aInput)
|
|||
|
||||
// Reset all search state members to default values
|
||||
mSearchString = newValue;
|
||||
mPlaceholderCompletionString.Truncate();
|
||||
mDefaultIndexCompleted = false;
|
||||
mBackspaced = false;
|
||||
mSearchStatus = nsIAutoCompleteController::STATUS_NONE;
|
||||
|
@ -230,8 +231,10 @@ nsAutoCompleteController::HandleText()
|
|||
// We need to throw away previous results so we don't try to search through them again
|
||||
ClearResults();
|
||||
mBackspaced = true;
|
||||
} else
|
||||
mPlaceholderCompletionString.Truncate();
|
||||
} else {
|
||||
mBackspaced = false;
|
||||
}
|
||||
|
||||
mSearchString = newValue;
|
||||
|
||||
|
@ -1111,6 +1114,46 @@ nsAutoCompleteController::StopSearch()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsAutoCompleteController::MaybeCompletePlaceholder()
|
||||
{
|
||||
MOZ_ASSERT(mInput);
|
||||
|
||||
if (!mInput) { // or mInput depending on what you choose
|
||||
MOZ_ASSERT_UNREACHABLE("Input should always be valid at this point");
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t selectionStart;
|
||||
mInput->GetSelectionStart(&selectionStart);
|
||||
int32_t selectionEnd;
|
||||
mInput->GetSelectionEnd(&selectionEnd);
|
||||
|
||||
// Check if the current input should be completed with the placeholder string
|
||||
// from the last completion until the actual search results come back.
|
||||
// The new input string needs to be compatible with the last completed string.
|
||||
// E.g. if the new value is "fob", but the last completion was "foobar",
|
||||
// then the last completion is incompatible.
|
||||
// If the search string is the same as the last completion value, then don't
|
||||
// complete the value again (this prevents completion to happen e.g. if the
|
||||
// cursor is moved and StartSeaches() is invoked).
|
||||
// In addition, the selection must be at the end of the current input to
|
||||
// trigger the placeholder completion.
|
||||
bool usePlaceholderCompletion =
|
||||
!mPlaceholderCompletionString.IsEmpty() &&
|
||||
mPlaceholderCompletionString.Length() > mSearchString.Length() &&
|
||||
selectionEnd == selectionStart &&
|
||||
selectionEnd == (int32_t)mSearchString.Length() &&
|
||||
StringBeginsWith(mPlaceholderCompletionString, mSearchString,
|
||||
nsCaseInsensitiveStringComparator());
|
||||
|
||||
if (usePlaceholderCompletion) {
|
||||
CompleteValue(mPlaceholderCompletionString);
|
||||
} else {
|
||||
mPlaceholderCompletionString.Truncate();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsAutoCompleteController::StartSearches()
|
||||
{
|
||||
|
@ -1120,6 +1163,10 @@ nsAutoCompleteController::StartSearches()
|
|||
if (mTimer || !mInput)
|
||||
return NS_OK;
|
||||
|
||||
// Check if the current input should be completed with the placeholder string
|
||||
// from the last completion until the actual search results come back.
|
||||
MaybeCompletePlaceholder();
|
||||
|
||||
nsCOMPtr<nsIAutoCompleteInput> input(mInput);
|
||||
|
||||
// Get the timeout for delayed searches.
|
||||
|
@ -1457,10 +1504,18 @@ nsAutoCompleteController::CompleteDefaultIndex(int32_t aResultIndex)
|
|||
int32_t selectionEnd;
|
||||
input->GetSelectionEnd(&selectionEnd);
|
||||
|
||||
bool isPlaceholderSelected =
|
||||
selectionEnd == (int32_t)mPlaceholderCompletionString.Length() &&
|
||||
selectionStart == (int32_t)mSearchString.Length() &&
|
||||
StringBeginsWith(mPlaceholderCompletionString,
|
||||
mSearchString, nsCaseInsensitiveStringComparator());
|
||||
|
||||
// Don't try to automatically complete to the first result if there's already
|
||||
// a selection or the cursor isn't at the end of the input
|
||||
if (selectionEnd != selectionStart ||
|
||||
selectionEnd != (int32_t)mSearchString.Length())
|
||||
// a selection or the cursor isn't at the end of the input. In case the
|
||||
// selection is from the current placeholder completion value, then still
|
||||
// automatically complete.
|
||||
if (!isPlaceholderSelected && (selectionEnd != selectionStart ||
|
||||
selectionEnd != (int32_t)mSearchString.Length()))
|
||||
return NS_OK;
|
||||
|
||||
bool shouldComplete;
|
||||
|
@ -1603,6 +1658,7 @@ nsAutoCompleteController::CompleteValue(nsString &aValue)
|
|||
// aValue is empty (we were asked to clear mInput), or mSearchString
|
||||
// matches the beginning of aValue. In either case we can simply
|
||||
// autocomplete to aValue.
|
||||
mPlaceholderCompletionString = aValue;
|
||||
input->SetTextValue(aValue);
|
||||
} else {
|
||||
nsresult rv;
|
||||
|
@ -1623,9 +1679,9 @@ nsAutoCompleteController::CompleteValue(nsString &aValue)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
input->SetTextValue(mSearchString +
|
||||
Substring(aValue, mSearchStringLength + findIndex,
|
||||
endSelect));
|
||||
mPlaceholderCompletionString = mSearchString +
|
||||
Substring(aValue, mSearchStringLength + findIndex, endSelect);
|
||||
input->SetTextValue(mPlaceholderCompletionString);
|
||||
|
||||
endSelect -= findIndex; // We're skipping this many characters of aValue.
|
||||
} else {
|
||||
|
@ -1635,6 +1691,9 @@ nsAutoCompleteController::CompleteValue(nsString &aValue)
|
|||
input->SetTextValue(mSearchString + NS_LITERAL_STRING(" >> ") + aValue);
|
||||
|
||||
endSelect = mSearchString.Length() + 4 + aValue.Length();
|
||||
|
||||
// Reset the last search completion.
|
||||
mPlaceholderCompletionString.Truncate();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -48,6 +48,7 @@ protected:
|
|||
nsresult StartSearches();
|
||||
void AfterSearches();
|
||||
nsresult ClearSearchTimer();
|
||||
void MaybeCompletePlaceholder();
|
||||
|
||||
nsresult ProcessResult(int32_t aSearchIndex, nsIAutoCompleteResult *aResult);
|
||||
nsresult PostSearchCleanup();
|
||||
|
@ -134,6 +135,7 @@ protected:
|
|||
nsCOMPtr<nsITreeBoxObject> mTree;
|
||||
|
||||
nsString mSearchString;
|
||||
nsString mPlaceholderCompletionString;
|
||||
bool mDefaultIndexCompleted;
|
||||
bool mBackspaced;
|
||||
bool mPopupClosedByCompositionStart;
|
||||
|
|
|
@ -957,6 +957,18 @@ this.PlacesDBUtils = {
|
|||
|
||||
{ histogram: "PLACES_ANNOS_PAGES_SIZE_KB",
|
||||
query: "SELECT SUM(LENGTH(content))/1024 FROM moz_annos" },
|
||||
|
||||
{ histogram: "PLACES_MAINTENANCE_DAYSFROMLAST",
|
||||
callback: function () {
|
||||
try {
|
||||
let lastMaintenance = Services.prefs.getIntPref("places.database.lastMaintenance");
|
||||
let nowSeconds = parseInt(Date.now() / 1000);
|
||||
return parseInt((nowSeconds - lastMaintenance) / 86400);
|
||||
} catch (ex) {
|
||||
return 60;
|
||||
}
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
let params = {
|
||||
|
|
|
@ -1921,6 +1921,7 @@ XPCOMUtils.defineLazyGetter(this, "gAsyncDBConnPromised", () => {
|
|||
catch(ex) {
|
||||
// It's too late to block shutdown, just close the connection.
|
||||
return conn.close();
|
||||
throw (ex);
|
||||
}
|
||||
return Promise.resolve();
|
||||
}).then(null, Cu.reportError);
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
||||
|
||||
const TOPIC_SHUTDOWN = "places-shutdown";
|
||||
const TOPIC_PREFCHANGED = "nsPref:changed";
|
||||
|
||||
const DEFAULT_BEHAVIOR = 0;
|
||||
|
@ -629,7 +628,9 @@ Search.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* Used to cancel this search, will stop providing results.
|
||||
* Cancels this search.
|
||||
* After invoking this method, we won't run any more searches or heuristics,
|
||||
* and no new matches may be added to the current result.
|
||||
*/
|
||||
cancel: function () {
|
||||
if (this._sleepTimer)
|
||||
|
@ -980,6 +981,10 @@ Search.prototype = {
|
|||
break;
|
||||
}
|
||||
this._addMatch(match);
|
||||
// If the search has been canceled by the user or by _addMatch reaching the
|
||||
// maximum number of results, we can stop the underlying Sqlite query.
|
||||
if (!this.pending)
|
||||
throw StopIteration;
|
||||
},
|
||||
|
||||
_maybeRestyleSearchMatch: function (match) {
|
||||
|
@ -1041,22 +1046,19 @@ Search.prototype = {
|
|||
match.comment,
|
||||
match.icon || PlacesUtils.favicons.defaultFavicon.spec,
|
||||
match.style,
|
||||
match.finalCompleteValue);
|
||||
match.finalCompleteValue || "");
|
||||
notifyResults = true;
|
||||
}
|
||||
|
||||
if (this._result.matchCount == 6)
|
||||
TelemetryStopwatch.finish(TELEMETRY_6_FIRST_RESULTS);
|
||||
|
||||
if (this._result.matchCount == Prefs.maxRichResults || !this.pending) {
|
||||
if (this._result.matchCount == Prefs.maxRichResults) {
|
||||
// We have enough results, so stop running our search.
|
||||
// We don't need to notify results in this case, cause the main promise
|
||||
// chain will do that for us when finishSearch is invoked.
|
||||
this.cancel();
|
||||
// This tells Sqlite.jsm to stop providing us results and cancel the
|
||||
// underlying query.
|
||||
throw StopIteration;
|
||||
}
|
||||
|
||||
if (notifyResults) {
|
||||
} else if (notifyResults) {
|
||||
// Notify about results if we've gotten them.
|
||||
this.notifyResults(true);
|
||||
}
|
||||
|
@ -1460,19 +1462,9 @@ Search.prototype = {
|
|||
//// component @mozilla.org/autocomplete/search;1?name=unifiedcomplete
|
||||
|
||||
function UnifiedComplete() {
|
||||
Services.obs.addObserver(this, TOPIC_SHUTDOWN, true);
|
||||
}
|
||||
|
||||
UnifiedComplete.prototype = {
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsIObserver
|
||||
|
||||
observe: function (subject, topic, data) {
|
||||
if (topic === TOPIC_SHUTDOWN) {
|
||||
this.ensureShutdown();
|
||||
}
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Database handling
|
||||
|
||||
|
@ -1497,6 +1489,18 @@ UnifiedComplete.prototype = {
|
|||
readOnly: true
|
||||
});
|
||||
|
||||
try {
|
||||
Sqlite.shutdown.addBlocker("Places UnifiedComplete.js clone closing",
|
||||
Task.async(function* () {
|
||||
SwitchToTabStorage.shutdown();
|
||||
yield conn.close();
|
||||
}));
|
||||
} catch (ex) {
|
||||
// It's too late to block shutdown, just close the connection.
|
||||
yield conn.close();
|
||||
throw ex;
|
||||
}
|
||||
|
||||
// Autocomplete often fallbacks to a table scan due to lack of text
|
||||
// indices. A larger cache helps reducing IO and improving performance.
|
||||
// The value used here is larger than the default Storage value defined
|
||||
|
@ -1512,20 +1516,6 @@ UnifiedComplete.prototype = {
|
|||
return this._promiseDatabase;
|
||||
},
|
||||
|
||||
/**
|
||||
* Used to stop running queries and close the database handle.
|
||||
*/
|
||||
ensureShutdown: function () {
|
||||
if (this._promiseDatabase) {
|
||||
Task.spawn(function* () {
|
||||
let conn = yield this.getDatabaseHandle();
|
||||
SwitchToTabStorage.shutdown();
|
||||
yield conn.close()
|
||||
}.bind(this)).then(null, Cu.reportError);
|
||||
this._promiseDatabase = null;
|
||||
}
|
||||
},
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIPlacesAutoComplete
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ let histograms = {
|
|||
PLACES_ANNOS_PAGES_COUNT: function (val) do_check_eq(val, 1),
|
||||
PLACES_ANNOS_PAGES_SIZE_KB: function (val) do_check_eq(val, 1),
|
||||
PLACES_FRECENCY_CALC_TIME_MS: function (val) do_check_true(val >= 0),
|
||||
PLACES_MAINTENANCE_DAYSFROMLAST: function (val) do_check_true(val >= 0),
|
||||
}
|
||||
|
||||
function run_test()
|
||||
|
|
|
@ -3408,6 +3408,14 @@
|
|||
"extended_statistics_ok": true,
|
||||
"description": "PLACES: Time to calculate frecency of a page (ms)"
|
||||
},
|
||||
"PLACES_MAINTENANCE_DAYSFROMLAST": {
|
||||
"expires_in_version" : "never",
|
||||
"kind": "exponential",
|
||||
"low": 7,
|
||||
"high": 60,
|
||||
"n_buckets" : 10,
|
||||
"description": "PLACES: Days from last maintenance"
|
||||
},
|
||||
"UPDATER_BACKGROUND_CHECK_CODE_EXTERNAL": {
|
||||
"expires_in_version": "default",
|
||||
"kind": "enumerated",
|
||||
|
|
|
@ -55,6 +55,7 @@ support-files =
|
|||
[test_autocomplete_delayOnPaste.xul]
|
||||
[test_autocomplete_with_composition_on_input.html]
|
||||
[test_autocomplete_with_composition_on_textbox.xul]
|
||||
[test_autocomplete_placehold_last_complete.xul]
|
||||
[test_browser_drop.xul]
|
||||
skip-if = buildapp == 'mulet'
|
||||
[test_bug253481.xul]
|
||||
|
|
|
@ -0,0 +1,309 @@
|
|||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
|
||||
<window title="Autocomplete Widget Test"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
onload="runTest();">
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
|
||||
<script type="application/javascript"
|
||||
src="chrome://global/content/globalOverlay.js"/>
|
||||
|
||||
<textbox id="autocomplete"
|
||||
type="autocomplete"
|
||||
completedefaultindex="true"
|
||||
timeout="0"
|
||||
autocompletesearch="simple"/>
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
<![CDATA[
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
function autoCompleteSimpleResult(aString, searchId) {
|
||||
this.searchString = aString;
|
||||
this.searchResult = Components.interfaces.nsIAutoCompleteResult.RESULT_SUCCESS;
|
||||
this.matchCount = 1;
|
||||
if (aString.startsWith('ret')) {
|
||||
this._param = autoCompleteSimpleResult.retireCompletion;
|
||||
} else {
|
||||
this._param = "Result";
|
||||
}
|
||||
this._searchId = searchId;
|
||||
}
|
||||
autoCompleteSimpleResult.retireCompletion = "Retire";
|
||||
autoCompleteSimpleResult.prototype = {
|
||||
_param: "",
|
||||
searchString: null,
|
||||
searchResult: Components.interfaces.nsIAutoCompleteResult.RESULT_FAILURE,
|
||||
defaultIndex: 0,
|
||||
errorDescription: null,
|
||||
matchCount: 0,
|
||||
getValueAt: function() { return this._param; },
|
||||
getCommentAt: function() { return null; },
|
||||
getStyleAt: function() { return null; },
|
||||
getImageAt: function() { return null; },
|
||||
getLabelAt: function() { return null; },
|
||||
removeValueAt: function() {}
|
||||
};
|
||||
|
||||
var searchCounter = 0;
|
||||
|
||||
// A basic autocomplete implementation that returns one result.
|
||||
let autoCompleteSimple = {
|
||||
classID: Components.ID("0a2afbdb-f30e-47d1-9cb1-0cd160240aca"),
|
||||
contractID: "@mozilla.org/autocomplete/search;1?name=simple",
|
||||
searchAsync: false,
|
||||
pendingSearch: null,
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Components.interfaces.nsIFactory,
|
||||
Components.interfaces.nsIAutoCompleteSearch
|
||||
]),
|
||||
createInstance: function (outer, iid) {
|
||||
return this.QueryInterface(iid);
|
||||
},
|
||||
|
||||
registerFactory: function () {
|
||||
let registrar =
|
||||
Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
|
||||
registrar.registerFactory(this.classID, "Test Simple Autocomplete",
|
||||
this.contractID, this);
|
||||
},
|
||||
unregisterFactory: function () {
|
||||
let registrar =
|
||||
Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
|
||||
registrar.unregisterFactory(this.classID, this);
|
||||
},
|
||||
|
||||
startSearch: function (aString, aParam, aResult, aListener) {
|
||||
let result = new autoCompleteSimpleResult(aString);
|
||||
|
||||
if (this.searchAsync) {
|
||||
// Simulate an async search by using a timeout before invoking the
|
||||
// |onSearchResult| callback.
|
||||
// Store the searchTimeout such that it can be canceled if stopSearch is called.
|
||||
this.pendingSearch = setTimeout(() => {
|
||||
this.pendingSearch = null;
|
||||
|
||||
aListener.onSearchResult(this, result);
|
||||
|
||||
// Move to the next step in the async test.
|
||||
asyncTest.next();
|
||||
}, 0);
|
||||
} else {
|
||||
aListener.onSearchResult(this, result);
|
||||
}
|
||||
},
|
||||
stopSearch: function () {
|
||||
clearTimeout(this.pendingSearch);
|
||||
}
|
||||
};
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// XPFE AutoComplete needs to register early.
|
||||
autoCompleteSimple.registerFactory();
|
||||
|
||||
let gACTimer;
|
||||
let gAutoComplete;
|
||||
let asyncTest;
|
||||
|
||||
let searchCompleteTimeoutId = null;
|
||||
|
||||
function finishTest() {
|
||||
// Unregister the factory so that we don't get in the way of other tests
|
||||
autoCompleteSimple.unregisterFactory();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
function runTest() {
|
||||
gAutoComplete = $("autocomplete");
|
||||
gAutoComplete.focus();
|
||||
|
||||
// Return the search results synchronous, which also makes the completion
|
||||
// happen synchronous.
|
||||
autoCompleteSimple.searchAsync = false;
|
||||
|
||||
synthesizeKey("r", {});
|
||||
is(gAutoComplete.value, "result", "Value should be autocompleted immediately");
|
||||
|
||||
synthesizeKey("e", {});
|
||||
is(gAutoComplete.value, "result", "Value should be autocompleted immediately");
|
||||
|
||||
synthesizeKey("VK_DELETE", {});
|
||||
is(gAutoComplete.value, "re", "Deletion should not complete value");
|
||||
|
||||
synthesizeKey("VK_BACK_SPACE", {});
|
||||
is(gAutoComplete.value, "r", "Backspace should not complete value");
|
||||
|
||||
synthesizeKey("VK_LEFT", {});
|
||||
is(gAutoComplete.value, "r", "Value should stay same when navigating with cursor");
|
||||
|
||||
runAsyncTest();
|
||||
}
|
||||
|
||||
function* asyncTestGenerator() {
|
||||
synthesizeKey("r", {});
|
||||
synthesizeKey("e", {});
|
||||
is(gAutoComplete.value, "re", "Value should not be autocompleted immediately");
|
||||
|
||||
// Calling |yield undefined| makes this generator function wait until
|
||||
// |asyncTest.next();| is called. This happens from within the
|
||||
// |autoCompleteSimple.startSearch()| function once the simulated async
|
||||
// search has finished.
|
||||
// Therefore, the effect of the |yield undefined;| here (and the ones) below
|
||||
// is to wait until the async search result comes back.
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "result", "Value should be autocompleted");
|
||||
|
||||
// Test if typing the `s` character completes directly based on the last
|
||||
// completion
|
||||
synthesizeKey("s", {});
|
||||
is(gAutoComplete.value, "result", "Value should be completed immediately");
|
||||
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "result", "Value should be autocompleted to same value");
|
||||
synthesizeKey("VK_DELETE", {});
|
||||
is(gAutoComplete.value, "res", "Deletion should not complete value");
|
||||
|
||||
// No |yield undefined| needed here as no completion is triggered by the deletion.
|
||||
|
||||
is(gAutoComplete.value, "res", "Still no complete value after deletion");
|
||||
|
||||
synthesizeKey("VK_BACK_SPACE", {});
|
||||
is(gAutoComplete.value, "re", "Backspace should not complete value");
|
||||
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "re", "Value after search due to backspace should stay the same"); (3)
|
||||
|
||||
// Typing a character that is not like the previous match. In this case, the
|
||||
// completion cannot happen directly and therefore the value will be completed
|
||||
// only after the search has finished.
|
||||
synthesizeKey("t", {});
|
||||
is(gAutoComplete.value, "ret", "Value should not be autocompleted immediately");
|
||||
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "retire", "Value should be autocompleted");
|
||||
|
||||
synthesizeKey("i", {});
|
||||
is(gAutoComplete.value, "retire", "Value should be autocompleted immediately");
|
||||
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "retire", "Value should be autocompleted to the same value");
|
||||
|
||||
// Setup the scene to test how the completion behaves once the placeholder
|
||||
// completion and the result from the search do not agree with each other.
|
||||
gAutoComplete.value = 'r';
|
||||
// Need to type two characters as the input was reset and the autocomplete
|
||||
// controller things, ther user hit the backspace button, in which case
|
||||
// no completion is performed. But as a completion is desired, another
|
||||
// character `t` is typed afterwards.
|
||||
synthesizeKey("e", {});
|
||||
yield undefined;
|
||||
synthesizeKey("t", {});
|
||||
is(gAutoComplete.value, "ret", "Value should not be autocompleted");
|
||||
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "retire", "Value should be autocompleted");
|
||||
|
||||
// The placeholder string is now set to "retire". Changing the completion
|
||||
// string to "retirement" and see what the completion will turn out like.
|
||||
autoCompleteSimpleResult.retireCompletion = "Retirement";
|
||||
synthesizeKey("i", {});
|
||||
is(gAutoComplete.value, "retire", "Value should be autocompleted based on placeholder");
|
||||
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "retirement", "Value should be autocompleted based on search result");
|
||||
|
||||
// Change the search result to `Retire` again and see if the new result is
|
||||
// complited.
|
||||
autoCompleteSimpleResult.retireCompletion = "Retire";
|
||||
synthesizeKey("r", {});
|
||||
is(gAutoComplete.value, "retirement", "Value should be autocompleted based on placeholder");
|
||||
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "retire", "Value should be autocompleted based on search result");
|
||||
|
||||
// Complete the value
|
||||
gAutoComplete.value = 're';
|
||||
synthesizeKey("t", {});
|
||||
yield undefined;
|
||||
synthesizeKey("i", {});
|
||||
is(gAutoComplete.value, "reti", "Value should not be autocompleted");
|
||||
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "retire", "Value should be autocompleted");
|
||||
|
||||
// Remove the selected text "re" (1) and the "et" (2). Afterwards, add it again (3).
|
||||
// This should not cause the completion to kick in.
|
||||
synthesizeKey("VK_DELETE", {}); // (1)
|
||||
|
||||
is(gAutoComplete.value, "reti", "Value should not complete after deletion");
|
||||
|
||||
gAutoComplete.selectionStart = 1;
|
||||
gAutoComplete.selectionEnd = 3;
|
||||
synthesizeKey("VK_DELETE", {}); // (2)
|
||||
|
||||
is(gAutoComplete.value, "ri", "Value should stay unchanged after removing character in the middle");
|
||||
|
||||
yield undefined;
|
||||
|
||||
synthesizeKey("e", {}); // (3.1)
|
||||
is(gAutoComplete.value, "rei", "Inserting a character in the middle should not complete the value");
|
||||
|
||||
yield undefined;
|
||||
|
||||
synthesizeKey("t", {}); // (3.2)
|
||||
is(gAutoComplete.value, "reti", "Inserting a character in the middle should not complete the value");
|
||||
|
||||
yield undefined;
|
||||
|
||||
// Adding a new character at the end should not cause the completion to happen again
|
||||
// as the completion failed before.
|
||||
gAutoComplete.selectionStart = 4;
|
||||
gAutoComplete.selectionEnd = 4;
|
||||
synthesizeKey("r", {});
|
||||
is(gAutoComplete.value, "retir", "Value should not be autocompleted immediately");
|
||||
|
||||
yield undefined;
|
||||
|
||||
is(gAutoComplete.value, "retire", "Value should be autocompleted");
|
||||
|
||||
finishTest();
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
function runAsyncTest() {
|
||||
gAutoComplete.value = '';
|
||||
autoCompleteSimple.searchAsync = true;
|
||||
|
||||
asyncTest = asyncTestGenerator();
|
||||
asyncTest.next();
|
||||
}
|
||||
]]>
|
||||
</script>
|
||||
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<p id="display">
|
||||
</p>
|
||||
<div id="content" style="display: none">
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
|
||||
</window>
|
|
@ -200,7 +200,7 @@ void
|
|||
LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId,
|
||||
SourceEventType aSourceEventType)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(IsInitialized() && aSourceEventId);
|
||||
NS_ENSURE_TRUE_VOID(IsInitialized());
|
||||
|
||||
// Log format:
|
||||
// [0 taskId dispatchTime sourceEventId sourceEventType parentTaskId]
|
||||
|
@ -209,7 +209,7 @@ LogDispatch(uint64_t aTaskId, uint64_t aParentTaskId, uint64_t aSourceEventId,
|
|||
void
|
||||
LogBegin(uint64_t aTaskId, uint64_t aSourceEventId)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(IsInitialized() && aSourceEventId);
|
||||
NS_ENSURE_TRUE_VOID(IsInitialized());
|
||||
|
||||
// Log format:
|
||||
// [1 taskId beginTime processId threadId]
|
||||
|
@ -218,7 +218,7 @@ LogBegin(uint64_t aTaskId, uint64_t aSourceEventId)
|
|||
void
|
||||
LogEnd(uint64_t aTaskId, uint64_t aSourceEventId)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(IsInitialized() && aSourceEventId);
|
||||
NS_ENSURE_TRUE_VOID(IsInitialized());
|
||||
|
||||
// Log format:
|
||||
// [2 taskId endTime]
|
||||
|
@ -227,7 +227,7 @@ LogEnd(uint64_t aTaskId, uint64_t aSourceEventId)
|
|||
void
|
||||
LogVirtualTablePtr(uint64_t aTaskId, uint64_t aSourceEventId, int* aVptr)
|
||||
{
|
||||
NS_ENSURE_TRUE_VOID(IsInitialized() && aSourceEventId);
|
||||
NS_ENSURE_TRUE_VOID(IsInitialized());
|
||||
|
||||
// Log format:
|
||||
// [4 taskId address]
|
||||
|
|
|
@ -349,8 +349,13 @@ IsExpired(const MultiTouchInput& aTouch)
|
|||
void
|
||||
GeckoTouchDispatcher::DispatchTouchEvent(MultiTouchInput& aMultiTouch)
|
||||
{
|
||||
if (aMultiTouch.mType == MultiTouchInput::MULTITOUCH_START &&
|
||||
if ((aMultiTouch.mType == MultiTouchInput::MULTITOUCH_END ||
|
||||
aMultiTouch.mType == MultiTouchInput::MULTITOUCH_CANCEL) &&
|
||||
aMultiTouch.mTouches.Length() == 1) {
|
||||
MutexAutoLock lock(mTouchQueueLock);
|
||||
mTouchMoveEvents.clear();
|
||||
} else if (aMultiTouch.mType == MultiTouchInput::MULTITOUCH_START &&
|
||||
aMultiTouch.mTouches.Length() == 1) {
|
||||
mTouchEventsFiltered = IsExpired(aMultiTouch);
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче